Compare commits

..

175 Commits

Author SHA1 Message Date
Alexander Chemeris
33f03a0bb9 bump version to 0.1.10~2 2016-04-30 01:59:06 +03:00
Kirill Zakharenko
7a0615288d bump version to 0.1.10~1 2016-04-30 00:40:23 +03:00
Kirill Zakharenko
a216b5f87a debian: compile for atom arch with SSE3 optimizations 2016-04-30 00:40:23 +03:00
Alexander Chemeris
33d3b71e36 transceiver: WIP: Set default max delay to 2 samples.
Default value of 0 may be too harsh, especially given random Rx/Tx delay
in 1 SPS receive mode.
2016-04-30 00:40:23 +03:00
Alexander Chemeris
c9633bae32 transceiver: Add an option to emulate a RACH delay in random filler mode. 2016-04-30 00:40:23 +03:00
Alexander Chemeris
bbaa249e6d sigProcLib: Change number of head bits in detectRACHBurst() from 4 to 8.
To match GSM 05.02 Access Burst definition.
2016-04-30 00:40:23 +03:00
Alexander Chemeris
f0cd4bd1db transceiver: Log channel number in DEBUG output of demoded bursts. 2016-04-30 00:40:23 +03:00
Alexander Chemeris
c88385c69d makefile: Fix build from an external path.
When you build from an external path, compiler can't find convert.h
include, because it was specified relative to the current directory.
Change this to specify the include dit relative to the Makefile
location.
2016-04-29 14:05:59 +03:00
Tom Tsou
047956259b EDGE: Fix demodulation slicer input
EDGE 8-PSK soft slicer was receiving input from the output of the
downsampler. Equalization and derotation were missing causing the
soft symbol output to be invalid.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-04-26 21:20:43 -07:00
Tom Tsou
d2b070369d uhd: Correct timing alignment in 8-PSK and GMSK downlink bursts
Delay the EDGE downlink bursts by one symbol in order to match GMSK
pulse shaping group delay. The difference in group delay arises from
the dual pulse filter combination of the GMSK Laurent represenation
whereas 8-PSK uses a single pulse linear filter.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-04-26 19:31:14 -07:00
Alexander Chemeris
9664c3a6e7 transceiver: Do not pass transceiver state struct to function where it's not used. 2016-04-26 12:03:20 -07:00
Alexander Chemeris
1ab5e7f7bc osmo-trx: Output Rx SPS as a part of configuration output. 2016-04-26 12:02:51 -07:00
Alexander Chemeris
5efe05021a transceiver: Add an option to generate random Access Bursts. 2016-04-20 13:46:02 -07:00
Alexander Chemeris
78d1fc9a13 transceiver: Properly handle MAXDLY.
Previously MAXDLY value was applied to Normal Bursts, which was nice
when working with sloppy test equipment like CMD57, but useless for
real world usage. At the same time documentation and de facto usage
of MAXDLY in OsmoBTS and OpenBTS assumed that it actually applies to
Access Bursts (RACH). So this patch changes osmo-rx behavior to apply
MAXDLY to RACH bursts and introduces a new command MAXDLYNB for the
old behavior.
2016-04-20 13:45:00 -07:00
Alexander Chemeris
a8cf208616 Common: Make sure gLogEarly() log to the same facilities as the normal log. 2016-04-20 12:33:41 -07:00
Alexander Chemeris
f84232d30a Common: Get rid of a compilation warning.
debugLogEarly was replaced to an empty space and arguments of the function
became operators, grouped together by ():
Configuration.cpp: In member function 'bool ConfigurationTable::defines(const string&)':
Configuration.cpp:272:28: warning: left operand of comma operator has no effect [-Wunused-value]
   debugLogEarly(LOG_ALERT, "configuration parameter %s not found", key.c_str());
                            ^

This fix removes debugLogEarly together with its arguments.
2016-04-20 12:26:57 -07:00
Tom Tsou
9bd649ec73 EDGE: Fix USRP B210 device support
Commit 871b8782 "EDGE: Add support for UmTRX" disabled B210 support
using EDGE. Add B210 explicitly to the timing offset table to avoid
this issue.

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

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

  $ ./osmo-trx -e -r 7

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

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

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

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

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

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

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

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

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

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

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-10-21 17:13:24 -07:00
Tom Tsou
f147b17447 sigproc: Make convolution and convert input buffers immutable
For good practice, use const specifier when applicable.

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

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

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

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

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

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

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

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

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

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

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

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

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

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:17:59 -07:00
Alexander Chemeris
dbe26abcb9 Transceiver: Print noise level for each burst in debug mode.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:17:56 -07:00
Alexander Chemeris
e8905a03a5 osmo-trx: Add a command line option for the dBFS to dBm offset.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:14:33 -07:00
Alexander Chemeris
909ffbfd23 Common: Use a scoped lock in the Logger to avoid deadlock on thread cancel.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:14:23 -07:00
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
Thomas Tsou
187225cf33 Transceiver52M: Fix SSE convolution shuffle register
An errant shuffle register value used in complex-complex convolution
causes distorted correlation peak-to-average values for certain TSC
values. The error effect varies for different TSC sequences with the
most noticeable effect of degraded detection on TSC 1 and no effect on
TSC 7.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2014-05-08 13:57:36 -04:00
Thomas Tsou
ccb73e15f3 Transceiver52M: Fix retransmissions when filler table is enabled
Commit 15d743efaf "Disable filler table
retransmissions by default" made OpenBTS style filler table behavior
optional. When enabled, dummy bursts were automatically loaded into the
filler table, but the table was not updated and only filler busts were
retransmitted.

Enable the restransmit state flag when the filler table option is
specified. Only preload filler table and enable retransmissions on
channel zero.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2014-04-15 17:47:48 -04:00
Thomas Tsou
a5c83aeb56 Transceiver52M: Add E1XX USRP device id and timings
Device specific timing settings for the E100 and E110 were missing from
the Tx/Rx offset table. Add E1XX identifier and offsets to the device
list and offset table respectively.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2014-04-09 23:14:31 -04:00
Thomas Tsou
c283916e41 Transceiver52M: Allow startup delay for stream alignment
UHD requires a small amount of time to align multiple streams at
startup. Delay the startup by 100 ms relative to the queried device time
(actual delay inclusive of control latencies will be less).

The following error is only relevant to dual-channel UHD devices (e.g.
Fairwaves UmTRX and Ettus B210).

UHD Error:
    The receive packet handler failed to time-align packets.
    1002 received packets were processed by the handler.
    However, a timestamp match could not be determined.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2014-03-27 13:57:18 -04:00
Thomas Tsou
cecfb2e8f9 Transceiver52M: Set UHD rates before creating streamers
Recent versions of UHD require setting the sample rate before creating
streamers otherwise the following exception occurs.

Boost_105300; UHD_003.007.000-0-g7fef199d

terminate called after throwing an instance of
'boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::math::rounding_error> >'
  what():  Error in function boost::math::round<d>(d): Value -nan can not be represented in the target integer type.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2014-03-27 13:49:51 -04:00
Thomas Tsou
8e17df7374 Transceivert52M: Add option for baseband frequency offset
Allow command line setting of the DSP frequency in UHD. All channels
will be tuned with the same offset. Dual-channel tuning with the B210,
which uses a single LO, will override the command line offset value
and set the DSP frequency automatically.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2014-03-06 15:52:49 -05:00
Thomas Tsou
3ebc772dc2 Transceiver52M: Enable B210 dual channel support
The main difference between existing UmTRX dual channel is the
single LO on B210 transmit and receive front-ends vs. independent
tuning paths. In order to support dual-ARFCN frequencies, baseband
offset conversion must be applied by tuning the FPGA CORDIC for
each channel. For B210, the following tuning order is applied.

1. If the new frequency of channel A is within the baseband range
   of channel B, then retune both channels with the RF centered
   and equal valued positive and negative baseband shifts.

2. If the new frequency of channel A is not with the baseband range
   of channel B, then retune channel A directly (without manual
   applied offset). Channel B will no longer be tuned to the
   previous frequency.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2014-03-03 23:17:57 -05:00
Thomas Tsou
18d3b833bc Transceiver52M: Disable initial device time reset
With dual-channels on B210, we lose the ability to reset both
channels to a synchronized state. Instead, let the timestamp
clock start with an arbitary value, which is the first
timestamp received from the device, instead of a near-zero
value. This approach also makes integration for device, in
general, with free-running timestamp clocks.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2014-03-03 23:16:59 -05:00
Thomas Tsou
e788239fba Transceiver52M: Split B200 and B210 identifiers
Differentiate between the two in order to provide enumeration
for dual-channel support.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2014-03-03 18:47:36 -05:00
Thomas Tsou
635f1bf2af Transceiver52M: Set B2XX clock frequency to 26 MHz
Change from the original USRP1 rate of 52 MHz. On B2XX we can use
26 MHz, which is closer to the default 32 MHz of the device.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2014-02-13 14:35:45 -05:00
Thomas Tsou
15d743efaf Transceiver52M: Disable filler table retransmissions by default
Burst selection at a particular time works in the following order
of priority.

1. Slot is disabled with channel combination set to NONE (default)
1. Burst exists in priority queue for the current time.
2. Filler table entry is used

This patch sets default behaviour to force all filler table entries
to zero and disallows filler table changes. This effectively means
that only bursts received from upper layers will be transmitted and
nothing will be automatically transmitted in the absence or delay
of incoming burts at a particular time.

New Command line option "Enable C0 filler table" allows reverting
to previous idle burst generation and retransmission behaviour on
TRX0. Retransmission cannot be enabled on non-C0 channels.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2014-01-26 21:52:46 -05:00
Thomas Tsou
af506441b3 Transceiver52M: Add missing scaling vector resize
Downlink scaling factors, which are stored in a vector for multiple
channels, was not being sized correctly.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-18 01:36:58 -05:00
Thomas Tsou
4de70be9a6 Transceiver52M: Remove database configuration file requirement
We don't require any parameters stored in the configuration table,
so don't bother with the existence of the persistent database file.
This also removes an unnecessary step during initial setup since
relevant parameters can be configured from the command line.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-17 18:57:45 -05:00
Thomas Tsou
fb827d04ba Transceiver52M: Ignore channel estimation if we are not equalizing
Equalization is currently disabled by default. As such, we don't need to
run channel estimates or even track the update state, which would
otherwise be allocating/decallocating the channel state vector at
regular intervals.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-16 16:16:36 -05:00
Thomas Tsou
2c79110969 Transceiver52M: UHD: Check running status before stopping stream
On startup errors we get a segfault if we stop and shutdown. This
is because we try to send a stop stream command to the device before
it has been created. Setup a check for running status before
attempting to stop the physical device.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-16 01:44:07 -05:00
Thomas Tsou
85b179d125 Transceiver52M: Create new osmo-trx executable
Create new main executable with full command line option parsing
of relevant parameters. Database configuration table still exists
(and must exist because of the global gConfig object), but can
be bypassed with command line options.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-16 01:44:07 -05:00
Thomas Tsou
2e622ff131 Transceiver52M: Output device and operating mode to stdout
Very useful user information at startup.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-16 01:43:46 -05:00
Thomas Tsou
3f32ab5afa Transceiver52M: Enable all warnings and resolve
Mainly basic signed vs unsigned comparisons and intializer ordering.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
8c33679fa5 Transceiver52M: Add virtual destructor for radio device
Empty destructor removes compile warning.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
477b77c558 Transceiver52M: Remove unused code
This includes unknown and unused variables, functions, and
non-relevant documentation.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
d3fccea05f Transceiver52M: Allow only channel zero to set TSC value
We support one TSC value per each transceiver object. Only channel
zero can set this value. Other channels can attempt to set the TSC
value, but will error if the TSC does not match the existing value.
In either case, non-zero channels do not manipulate the gloabl TSC
setting.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
cb269a32dd Transceiver52M: Use independent power scaling varables for each channel
Simply vectorize the existing power state variable.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
1882099d15 Transceiver52M: Set const qualifier on appropriate radio vector methods
Pointer accessor and noise average methods for radio and noise vectors
respectively.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
a0179e37f8 Transceiver52M: Use independent noise vectors for each channel
Each ARFCN channel may be independently configureted and possibly on
separate hardware, so don't share a single vector for noise estimate
calculations. Allow a non-pointer based iterator so we can get away
with using the default copy constructor.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
ef25dba4e7 Transceiver52M: Ignore detected bursts at the noise floor
The transceiver has the ability to detect bursts below the noise floor,
but little hope in successful decoding, so don't even try. We still use
the detected burst to differentiate against noise vs actual data.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
2d0c00bd3d Transceiver52M: Check time slot validity of incoming bursts
In errant cases, GSM core may send bursts with invalid slot values,
which is allowed by the GSM::Time object. If we find a burst like this
coming into the transceiver, then drop it immediately.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
e90a42becc Transceiver52M: Add dual channel diversity receiver option
This patch add support for dual channel diversity on the receive
path. This allows two antennas two shared antennas to be used for
each ARFCN handling channel in the receiver. This configuration
may improvde performance in multi-path fading environments,
however, noise andpotential interference levels are increased due
to the higher bandwidth used.

The receive path is oversampled by a factor of four for a rate
of 1.083333 Msps. If the receive paths are tuned within a
maximum channel spacing (currently set at 600 kHz), then both
ARFCN frequencies are processed by each channel of the receiver.
Otherwise, the frequency shifted diversity path is disabled and
standard non-diversity operation takes place.

Diversity processing is handled by selecting the path with the
higheset energy level and discarding the burst on the second
path. Selection occurs on a burst-by-burst basis.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
30421a7e25 Transceiver52M: Refactor receive path outer burst handling
Separate the large pullRadioVector() call, which forms the central
portion of the receive path burst processing. Break out RACH, normal
burst, and demodulation into separate methods. This makes the burst
handling from the FIFO read to soft bit output somewhat more
manageable.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
34bbef754f Transceiver52M: sigproc: Wrap internal phase on frequency shift
The call into table lookup will loop on values outside of the
table range. With continuously increasing phase, this leads
to an eventual permanent hard spin. Wrap the phase value to
prevent that from happening.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
2c1f85a10c Transceiver52M: UHD: Add string descriptors to device-offset pairs
As we add more channel combintions including but not limited to
new devices, signal processing schemes, and diversity, we'll
need to handle more special cases. Add string descriptions for
just a bit more sanity.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
a2fe91a688 Transceiver52M: Add vectorized radio burst capability
This patch allows multiple signalVectors to be stored within
a single radioVector object. The motivation is to provide
a facility for diversity and/or MIMO burst handling. When
no channel value is specified, single channel bevhaviour
is maintained.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
e1ce92599a Transceiver52M: Rearrange socket port assignemnts
Style change for clarity only.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
b075dd2f73 Transceiver52M: Dynamically allocate correlation vectors
Stack allocating the correlation output generates a call to the copy
constructor of an zero valued vector. We can avoid this extra copy
constructor with a pointer reference and dynamic allocation.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
94edaaeee6 Transceiver52M: Allow separate in/out vectors for delay and decimation
Allow non-in-place use of the delay setting. Internally, the delay call
creates a new vector and copies the contents back into the original.
Instead, provide the option to return the computed output vector
directly and remove an an extra copy in the process.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
0e0e1f4363 Transceiver52M: Setup sinc() call directly with table lookup
On Beagle Board the call into the sinc() function is generating a lot of
load on the peak interpolation. Simplify the sinc() function with a
dedicated table lookup. Eventually, this table may be removed in favour
of using a precomputed filterbank for fractional delay determination.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
d0f3ca3e94 Transceiver52M: Preallocate head room for burst correlation
Set a transceiver high level length value that specifies the largest
number of complex or real filter taps that we will encounter. This
allows preallocation of head room and prevents an extra allocation and
copy on every incoming receive burst.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
f8bc7c351f Transceiver52M: UHD: Continue on receive and send timeouts
With testing on current UHD releases, currently 003.005.xxx series,
timeout errors on both receive and transmit are recoverable on network
and USB based devices. Remove the fatal error conditions.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
a4cf48cf8b Transceiver52M: Set priority on downlink socket thread
Clock indications passed up to GSM core originate on the transciever
downlink side. Set priority to keep the flow of clock updates
consistent.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
7940335586 Transceiver52M: Default to 1 sample-per-symbol low powered devices
This includes ARM Cortex A8 and A15 powered device such as Beagle
Board, Gumstix driven E100 USRP, and Arndale board. Set the reduced
SPS value automatically for the user.

For x86, if we don't support SSE3, then the architecture is
probably ancient and not with using. Drop the sampling down anyways
to at least make an attempt. Non floating point SIMD devices (e.g.
Raspberry Pi) also fall in this category

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
f79c4d06ff Transceiver52M: Precompute fractional delay filters
Preallocate and compute a bank of fractional sample delay filters.
The number of filters to allocate is specified by the DELAYFILTS
preprocessor definition with a default value of 64. The filters
themselves are sinc pulse generated with 20 taps and Blackman-harris
windowed .

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
20eb6d64fd Transceiver52M: Separate signalVector into it's own file
Break out the signalVector object and clean up the interface in the
process.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
e0fa2bfd93 Transceiver52M: Remove extra copy in receive drive path
Currently the code allocations a signalVector and then copies
into a radioVector. This is unnecessary because the latter is
a derived class making the first allocation unnecessary.
Modify the radioVector constructor to allow direct use in the
case above.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
6f4906e375 Transceiver52M: Dynamically allocate convolution input vectors
This prevents the use of a copy constructor in the downlink
modulator and prevents a secondary memory allocation during
the convolution. Avoid both cases by dynamically allocating
with preloaded head room. The latter provides enough memory
before the first sample in the burst to cover the length
of the filter taps.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:35:07 -05:00
Thomas Tsou
0a3dc4c210 Transceiver52M: Add NEON complex-complex multiply
Complex-complex block multiples are used for phase rotation of
bursts. Optimization targeted from perf profiling.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:34:59 -05:00
Thomas Tsou
acc22fa3ff Transceiver52M: Use USRP1 type window for B2xx devices
B2xx is a USB based device so use the USRP1 based adaptive flow
control window for transmit bursts. This adds additional stability
primarily on ARM platforms.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:32:40 -05:00
Thomas Tsou
7553aa973f Transceiver52M: Set variable thread priority levels
The transceiver and underlying device drivers are threaded. use
the following priority levels.

0.50 - UHD driver internal threads
0.45 - Receive device drive thread
0.44 - Transmit device drive thread
0.43 - UHD asynchronous update thread (error reporting)
0.42 - Receive burst processing thread(s)

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:32:40 -05:00
Thomas Tsou
7e4e536b1b Transceiver52M: Add ARM NEON support
Similar to the existing Intel SSE cases, add support for NEON vector
floating point SIMD processing. In this case, use ARM assembly
directly as the NEON intrinsics do not generate preferential code
output.

Currently support NEON vectorized convolution and floating point
integer conversions.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 23:32:35 -05:00
Thomas Tsou
204a9f135a Transceiver52M: Add multi channel transceiver support
This patch primarily addresses devices with multiple RF front end
support. Currently device support is limited to UmTRX.

Vectorize transceiver variables to allow multiple asynchronous
threads on the upper layer with single downlink and uplink threads
driving the UHD I/O interface synchronously.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-15 14:45:20 -05:00
Thomas Tsou
0169b310de Transceiver52M: Remove unnecessary UHD clock setting call
There is no need to create this method. Just call the UHD interface
directly.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-08 11:33:31 -05:00
Thomas Tsou
66e2dd2543 Transceiver52M: Remove unused files and utilities
USRPping and sigProcLibTest are in an unmaintained state,
while the intended functionality remains unknown. Stored
filter taps are also unused and should also be removed.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-08 11:33:31 -05:00
Thomas Tsou
f078273a8a Transceiver52M: Separate transceiver per-slot state information
Collect the slot information into an indpendent state object. This
will allow us to easily create multiple instances of internal state
variables without having to replicate the transceiver object itself.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-08 11:33:31 -05:00
Thomas Tsou
d647ec5dc1 Transceiver52M: Delay socket allocation to heap
For multiple transceiver connections, it is inappropriate to
allocate all sockets in the transceiver constructor due to not
knowing how many connections are avaialble in advance and for
error checking purposes. Instead, store the base socket address
port combination and setup the sockets in the initialization
call.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-08 11:33:31 -05:00
Thomas Tsou
c289d7a409 Transceiver52M: Remove transmit logging option
The current status and operability of this compile option is
unknown. Remove due to lack of use, demand, and maintenance.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-08 11:33:31 -05:00
Thomas Tsou
035bee5c32 build: Remove subversion references
We do not use subversion. We do not need to refer to subversion
reference numbers.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-08 11:18:46 -05:00
Thomas Tsou
cf910dcdda Transceiver52M: Reset overrun and underrun indicators
Underruns are only explicitly set on the downlink side. Overruns
are logged but unused. In either case, reset indicators to false
to avoid sending false state information.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-07 23:07:49 -05:00
Thomas Tsou
69762fd7c6 Transceiver52M: Fix SSE preprocessor definition
Using non-SSE4.1 enabled architecture would cause undefined
reference to 'convert_si16_ps' call.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-07 22:57:21 -05:00
Thomas Tsou
fffd987f22 build: Set UHD driver as default configuration
Currently the default configuration is to not build the full
transceiver, which is pointless. Set the UHD driver, which
includes either Ettus or Fairwaves variants, as the default.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-04 10:23:24 -08:00
Thomas Tsou
17bbb9b755 Transceiver52M: Separate architecture specific files
Move x86 specific files into their own directory as this
area is about to get crowded with the addition of ARM
support.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-11-04 09:15:55 -08:00
Thomas Tsou
a1a3ab4bab Transceiver52M: Update RSSI calculation
Use the same measurement method for RSSI as the noise level. Previous
method was to use the peak correlation amplitude relative to the
expected value. This created two very different amplitude approaches
between the noise measurement and RSSI measurement, which would
throw off the upper layer MS power control loop.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:18 -04:00
Thomas Tsou
fa3a787ccb Transceiver52M: Update noise measurement calculation
Previous removal of the energy detector requirement broke
the noise level calculation loop. The previous adaptive
approach was finicky - noticably at high gain levels. Since
we no longer use the energy threshold for primary burst gating,
we can return to a simpler world.

In the new approach, we compute a running average of energy
levels and track them with a noise vector. A timeslot that
passes the correlator threshold is a valid burst. These are
not used in the noise calculation. Everything else is
considered noise and used to compute the noise level with
respect to full scale input level, which for almost all
supported devices is 2^15.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:18 -04:00
Thomas Tsou
010fff783b Transceiver52M: Move reference select from compile time to database
Enabling the external reference on UHD devices through the configure
time switch is awkward. Use a database variable "TRX.Reference" with
'0' or '1' value for internal and external references respectively.
Use internal reference is no entry is defined.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:18 -04:00
Thomas Tsou
61b4a6ad9f Transceiver52M: Delay UHD messaging registration until after start
We want to push UHD logs to the OpenBTS logging system, but most
device errors occur at startup, so keep the output on stdout until
after device initialization. That way obvious errors are easily
viewable before seeing the useless TRX timeout message.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:18 -04:00
Thomas Tsou
de1648ca6b Transceiver52M: Deallocate high level resources on shutdown
This primarily addresses the error case at initialization.
In the event that the transceiver fails to start, we should
be able cleanly shutdown and release while providing a useful
reason for exiting.

After the radio is started and threads launched, there
are no thread state variables or shutdown messaging between
threads, and the transceiver cannot be consistently
shutdown. This issue remains to be solved.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:18 -04:00
Thomas Tsou
3952d80d05 Transceiver52M: Reduce and place bounds checking on I/O buffers
Previous send and receive buffers at the radio interface were
arbitrarily set to a sufficient size. For normal (non-resampling)
devices, use a block (chunk) size of 625 samples. For 64 or 100
MHz resampling devices, use 4 times the reduced resampling
numerator or denominator and provide bounds checking where
appropriate.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:18 -04:00
Thomas Tsou
fe269fe31d Transceiver52M: Add 64 MHz resampling option with B100
Move B100 to the resampling interface with default
clocking. This temporarily resolves undetermined
FPGA clocking issues. This also provides extensible
support for multiple clocking rates and resampling
ratios.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:18 -04:00
Thomas Tsou
c064124429 Transceiver52M: Remove support for ancient libusrp versions
Current functionality with these old versions is questionable.
There is no reason to use any version of GNU Radio / libusrp older
than 3.3. Version 3.4.2 is the only recommended version for USRP1
users.

Non-USRP1 users must use UHD driver from Ettus Research.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:17 -04:00
Thomas Tsou
69d14c9a14 Transceiver52M: Add B210 support
Identical to B200 support, but explicitly check for the device type
name.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:17 -04:00
Thomas Tsou
c1f7c42a33 Transceiver52M: Setup dual sample rate transceiver
This patch applies oversampling, when selected with 4 sps,
to the downlink only, while running the receiver with
minimal sampling at 1 sps. These split sample rates allow
us to run a highly accurate downlink signal with minimal
distortion, while keeping receive path channel filtering
on the FPGA.

Without this patch, we oversample the receive path and
require a steep receive filter to get similar adjacent
channel suppression as the FPGA halfband / CIC filter
combination, which comes with a high computational cost.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:17 -04:00
Thomas Tsou
2c282f5e12 Transceiver52M: Generate delay filter with SSE memory alignment
This requires an additional memcpy() on the signal vector
constructor, but allows the interpolation filter to use
SSE optimzationed convolution.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:17 -04:00
Thomas Tsou
92c16df875 Transceiver52M: Separate main transmit and receive drive threads
This patch primarily addresses observed repeated overrun
conditions in embedded environments - namely ARM.

The heartbeat of the transceiver is derived from the receive
sample stream, which drives the main GSM clock. Detach the
transmit thread from the receive loop to avoid interfering with
the receive I/O, which is sensitive to overrun conditions if
pull process is interrupted.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:17 -04:00
Thomas Tsou
d5a80c3dc6 Transceiver52M: Disable equalization
Unsupported at 4 sps, and performance benefits remain
to be proven at 1 sps. Disable until further testing.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:17 -04:00
Thomas Tsou
dafb33700e Transceiver52M: Reduce RACH and TSC correlation windows
Start the correlation search window at 4 symbols before
the expected correlation peak. End the search at 10
symbols and 4 + maximum expected delay for RACH and TSC
bursts respectively.

This change lowers receive side cpu utilization while
maintaining reasonable timing jitter and accuracy tolerance.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:17 -04:00
Thomas Tsou
0e44ab360e Transceiver52M: Narrow resampling filter bandwidth
This patch only applies to resampling use at 4 samples-per-symbol.
By extention that means only USRP2 / N2xx devices are affected.
At 4 samples-per-symbol we restrict output bandwidth to roughly
roughly 700 MHz, which combined with the 2 pulse Laurent
approximation yields < 0.5 degrees of RMS phase error at the
resampler output.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:17 -04:00
Thomas Tsou
092f757ec2 Transceiver52M: Add B200 support
Set master clock rate to 52 MHz for B200. Also, we want to avoid
floating point comparison errors on clock rate settings, but we
expect to be able really set the rates we specify. Set the
offset limit to 1 Hz. If we can't set our rates with that level
of precision, then something is wrong.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:17 -04:00
Thomas Tsou
a57bc8a3b9 Transceiver52M: Setup dual Laurent pulse shaping filter
Provides substantially improved transmit phase error
performance when enabled. Requires use of 4 samples
per symbol, and is enabled by default when set.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:17 -04:00
Thomas Tsou
84c60f6679 Transceiver52M: Check that sample rates are sane before using
If there is an error in the sample rate determination, noted
by a negative return sample rate value, error directly and
don't try to set the device rate.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:17 -04:00
Thomas Tsou
865bca42d6 Transceiver52M: Refactor RACH and normal burst detection
Both RACH and normal bursts are detected with the same approach of
midamble correlation combined with peak-to-average ratio. The
difference is the midamble placements and lengths. Thus, there is
no reason to have independent implementations.

This patch creates a common call burstDetect(), while leaving the
correlation window indexing in the original calls.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:17 -04:00
Thomas Tsou
c88d8d53f8 Transceiver52M: Add UmTRX support
Requires Fairwaves UHD driver.

  https://github.com/chemeris/UHD-Fairwaves.git

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:17 -04:00
Thomas Tsou
9ccd9f2c3c Transceiver52M: Add 4 samples-per-symbol Laurent pulse shape
When 4 samples-per-symbol operation is selected, replace the
existing pulse approximation, which becomes inaccurate with
non-unit oversampling, with the primary pulse, C0, from the
Laurent linear pulse approximation.

Pierre Laurent, "Exact and Approximate Construction of Digital Phase
  Modulations by Superposition of Amplitude Modulated Pulses", IEEE
  Transactions of Communications, Vol. 34, No. 2, Feb 1986.

Octave pulse generation code for the first three pulses of the
linear approximation are included.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:17 -04:00
Thomas Tsou
8181b0104a Transceiver52M: Disable energy detector
The adaptive energy threshold gating suffers a near-far problem
at certain gain levels. This is due to exponential threshold
raising, but linear decreases. A large signal level followed by
a period low signal level causes (comparatively) weak signals to
go undetected. Additionally, the algorithm performs differently
at multiple RF gain levels.

This patch switches solely to correlation based gating for burst
detection. The main computational load with this approach is
sub-sample width peak interpolation, which we disable for intial
detection and run after threshold passing.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:17 -04:00
Thomas Tsou
9471d7635a Transceiver52M: Add SSE floating point / integer conversion
Convertions are performed in multiples of 4 or 8. All loads are
considered unaligned.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:17 -04:00
Thomas Tsou
03e6ecf977 Transceiver52M: Replace resampler with SSE enabled implementation
Replace the polyphase filter and resampler with a separate
implementation using SSE enabled convolution. The USRP2 (including
derived devices N200, N210) are the only supported devices that
require sample rate conversion, so set the default resampling
parameters for the 100 MHz FPGA clock. This changes the previous
resampling ratios.

  270.833 kHz -> 400 kHz      (65 / 96)
  270.833 kHz -> 390.625 kHz  (52 / 75)

The new resampling factor uses a USRP resampling factor of 256
instead of 250. On the device, this allows two halfband filters to
be used rather than one. The end result is reduced distortial and
aliasing effecits from CIC filter rolloff.

B100 and USRP1 will no be supported at 400 ksps with these changes.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:17 -04:00
Thomas Tsou
3eaae80c90 Transceiver52M: Replace convolve and related calls with SSE implementation
This large patch replaced the convolve() call with an SSE vector
enabled version. The lower C and SSE intrinsic based code operates
on fixed and aligned vectors for the filter taps. The storage format
of interleaved I/Q for both complex and real vectors is maintained.

SSE filter tap values must:

  1. Start 16-byte aligned
  2. Number with a multiple of 4 between 4 and 20 for real taps
  3. Number with a multiple of 4 for complex taps

Non-compliant values will fall back to non-SSE usage. Fixed length
iterators mean that head and tail cases may require reallocation of
the input vector, which is automatically handled by the upper C++
interface.

Other calls are affected by these changes and adjusted or rewritten
accordingly. The underlying algorithms, however, are unchanged.

  generateGSMPulse()
  analyzeTrafficBurst()
  detectRACHBurst()

Intel SSE configuration is automatically detected and configured at
build time with Autoconf macros.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:17 -04:00
Thomas Tsou
e57004d0c3 Transceiver52M: Generate RACH correlation sequence at initialization
There is no temporal dependency on when the RACH sequence is generated,
so there is no need for transceiver to create it in response to a
command from GSM core. If we power on the transceiver, we will need
the RACH sequence, so just allocate it during initialization.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:17 -04:00
Thomas Tsou
6aa1f18f41 Transceiver52M: Remove logging from signal processing core
The only logging outputs in the the signal processing library
are debug lines that generate copious amounts of output while
providing little useful information to the user. The relevant
information (time-of-arrival, channel gains, etc.) can and
should be logged from transceiver instance itself.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:17 -04:00
Thomas Tsou
e5dcfc4f80 Transceiver52M: Add destructors to correlation seqeunce objects
Add destructor calls so we can avoid the nested vector deallocations.
Also remove the unnecessary pointer NULL checks prior to destruction.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:17 -04:00
Thomas Tsou
83e0689e76 Transceiver52M: Make GSM pulse filter internal to implementation
There is no reason expose the pulse shaping filter outside of the
signal processing calls. The main transceiver object makes no use
of the filter and there's no reason to pass it around.

Initialize the pulse shape with the signal processing library, and
maintain an internal static member like many of the other library
variables. Similarly destroy the object when the library is closed.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:10:04 -04:00
Thomas Tsou
d24cc2cd96 Transceiver52M: Rename samples-per-symbol variable names
Because repeatedly typing mSamplesPerSymbol is giving me
carpal tunnel syndrome. Replace with the much shorter,
easier to type, and just as clear name of 'sps'.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:04:15 -04:00
Thomas Tsou
ddd6defb43 Transceiver52M: Verify global config sanity before using
The configuration table is instantiated as a global variable with
no means to check constructor status. This means various types
of database failure conditions (e.g. file existence, permissions,
etc.) are not reported. This patch performs a small number of
checks to make sure that the configuration is sane.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:04:13 -04:00
Thomas Tsou
96794cb746 Transceiver52M: Remove unused test code from main
The commented out test code is not maintained and behaviour is
unknown. Remove for clarity.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:03:41 -04:00
Thomas Tsou
7e06806ff0 Transceiver52M: Use exception blocks for rate changes
UHD will throw if something goes awry in these sensitive sections,
so we should catch and shutdown gracefully. There is no recovery
if we can't set rates.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:03:41 -04:00
Thomas Tsou
cb69f08410 Transceiver52M: Set resampling option automatically based on device
Remove the built time resampling selection and link both options.
Move the normal push/pullBuffer() calls back to the base class and
overload them in the inherited resampling class.

USRP2/N2xx devices are the only devices that require resampling so
return that resampling is necessary on the device open(), which is
the point at which the device type will be known.

The GSM transceiver only operates at a whole number multiple of
the GSM rate and doesn't care about the actual device rate and
if resampling is used. Therefore GSM specific portion of the
transceiver should only need to submit the samples-per-symbol
value to the device interface.

Then, the device should be able to determine the appropriate
sample rate (400 ksps or 270.833 ksps) and if resampling is
appropriate.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:03:41 -04:00
Thomas Tsou
312e387630 Transceiver52M: Remove and rename oversampling variables
The transceiver only uses a single integer oversampling value,
which is more simply referred to as samples-per-symbol.

mRadioOversampling --> mSPS
mTransceiverOversampling (removed)

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:03:41 -04:00
Thomas Tsou
f2293b8cfa Transceiver52M: Remove periodic alignment update from UHD build
Periodic timing alignment should never be required for UHD devices,
though the mechanism was used as a fallback mechanism should UHD
not properly recover after an underrun - as may occur in old
003.003.000 based revisions. This issue is not a concern in more
recent UHD releases and deprecates this code for legacy USRP1
use only.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:03:41 -04:00
Thomas Tsou
e3e8814948 Transceiver52M: Add device offset correction table
Previously, two timing correction values were used for UHD devices
depending on the sample rate of 270.833e3 or 400e3 for native GSM or
resampled device rate respectively. The correction values compensate
for residual timing effects due to analog component delays, filters
lag times, and general fudge factors. These values are device
specific and over-generalized by the two value configuration.

This patch adds the following struct to store these correction
values by device type and sample rate - through samples-per-symbol.

struct uhd_dev_offset {
        enum uhd_dev_type type;
        int sps;
        double offset;
};

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:03:41 -04:00
Thomas Tsou
02d88d1380 Transceiver52M: Add UHD device type checking
UHD device type was previously detected, but only categorized in
terms of bus type, USB or Ethernet, and sample rate capability.
With the number of supported device increasing, we can no longer
easily group devices since we need to handle more and more
device-specific peculiarities. Some of these factors are managed
internally by the UHD driver, but other factors (e.g. timing
offsets) are specific to a single device.

Start by maintaining an enumerated list of relevant device types
that we can use for applying device specific operations. Also
rename the USB/Ethernet grouping to transmit window type because
that's what it is.

enum uhd_dev_type {
        USRP1,
        USRP2,
        B100,
        NUM_USRP_TYPES,
};

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:03:41 -04:00
Thomas Tsou
b4cb4e23c0 Transceiver52M: Update to UHD streamer interface
This patch is long overdue and can now be merged after better understanding
of timestamp stability issues. UHD tick / timespec conversions were
generally used with the streamer interface, though these calls are actually
independent change sets. The combination would lead to internal rounding
errors and a timing drift most notably on B100 running at GSM symbol
rate multiples. There are no known issues, however, with the streamer code
itself.

The aforementioned issue was discovered in test code only, which was never
merged to mainline.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-10-18 13:03:41 -04:00
79 changed files with 8324 additions and 4572 deletions

View File

@@ -1,6 +1,5 @@
/*
* Copyright 2008, 2011 Free Software Foundation, Inc.
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -42,7 +41,6 @@
//@{
// UNUSED in osmo-trx
/** Pointer FIFO for interthread operations. */
// (pat) The elements in the queue are type T*, and
// the Fifo class implements the underlying queue.
@@ -100,7 +98,7 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue {
{
ScopedLock lock(mLock);
T* retVal = (T*)mQ.get();
if (retVal==NULL) {
while (retVal==NULL) {
mWriteSignal.wait(mLock);
retVal = (T*)mQ.get();
}
@@ -157,7 +155,6 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue {
}
};
// UNUSED in osmo-trx
// (pat) Identical to above but with the threading problem fixed.
template <class T, class Fifo=PointerFIFO> class InterthreadQueue2 {
@@ -279,7 +276,6 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue2 {
// UNUSED in osmo-trx
/** Pointer FIFO for interthread operations. */
template <class T> class InterthreadQueueWithWait {
@@ -384,7 +380,7 @@ template <class T> class InterthreadQueueWithWait {
// UNUSED in osmo-trx
/** Thread-safe map of pointers to class D, keyed by class K. */
template <class K, class D > class InterthreadMap {
@@ -648,7 +644,7 @@ template <class T, class C = std::vector<T*>, class Cmp = PointerCompare<T> > cl
// UNUSED in osmo-trx
class Semaphore {
private:

View File

@@ -1,6 +1,5 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
*
*
* This software is distributed under the terms of the GNU Affero Public License.
@@ -28,91 +27,66 @@
#include "Threads.h"
#include "Interthread.h"
#include "Configuration.h"
#include <iostream>
using namespace std;
ConfigurationTable gConfig;
InterthreadQueue<int> gQ;
InterthreadMap<int,int> gMap;
class QueueWriter : public Thread
void* qWriter(void*)
{
public:
QueueWriter() : Thread("QueueWriter") {}
protected:
virtual void runThread()
{
int *p;
for (int i=0; i<20; i++) {
p = new int;
*p = i;
COUT("queue write " << *p);
gQ.write(p);
msleep(1);
}
int *p;
for (int i=0; i<20; i++) {
p = new int;
*p = -1;
*p = i;
COUT("queue write " << *p);
gQ.write(p);
if (random()%2) sleep(1);
}
};
p = new int;
*p = -1;
gQ.write(p);
return NULL;
}
class QueueReader : public Thread
void* qReader(void*)
{
public:
QueueReader() : Thread("QueueReader") {}
protected:
virtual void runThread()
{
bool done = false;
while (!done) {
int *p = gQ.read();
COUT("queue read " << *p);
if (*p<0) done=true;
delete p;
}
bool done = false;
while (!done) {
int *p = gQ.read();
COUT("queue read " << *p);
if (*p<0) done=true;
delete p;
}
};
return NULL;
}
class MapWriter : public Thread
void* mapWriter(void*)
{
public:
MapWriter() : Thread("MapWriter") {}
protected:
virtual void runThread()
{
int *p;
for (int i=0; i<20; i++) {
p = new int;
*p = i;
COUT("map write " << *p);
gMap.write(i,p);
msleep(1);
}
int *p;
for (int i=0; i<20; i++) {
p = new int;
*p = i;
COUT("map write " << *p);
gMap.write(i,p);
if (random()%2) sleep(1);
}
};
return NULL;
}
class MapReader : public Thread
void* mapReader(void*)
{
public:
MapReader() : Thread("MapReader") {}
protected:
virtual void runThread()
{
for (int i=0; i<20; i++) {
int *p = gMap.read(i);
COUT("map read " << *p);
// InterthreadMap will delete the pointers
}
for (int i=0; i<20; i++) {
int *p = gMap.read(i);
COUT("map read " << *p);
// InterthreadMap will delete the pointers
// delete p;
}
};
return NULL;
}
@@ -121,25 +95,20 @@ protected:
int main(int argc, char *argv[])
{
COUT("TEST 1: InterthreadQueue")
QueueReader qReaderThread;
QueueWriter qWriterThread;
qReaderThread.startThread();
qWriterThread.startThread();
// stopThread() will wait for a thread to stop for 5 seconds, which
// is more than enough for this test to finish.
qReaderThread.stopThread();
qWriterThread.stopThread();
Thread qReaderThread;
qReaderThread.start(qReader,NULL);
Thread mapReaderThread;
mapReaderThread.start(mapReader,NULL);
COUT("TEST 2: InterthreadMap")
MapReader mapReaderThread;
mapReaderThread.startThread();
MapWriter mapWriterThread;
mapWriterThread.startThread();
// stopThread() will wait for a thread to stop for 5 seconds, which
// is more than enough for this test to finish.
mapReaderThread.stopThread();
mapWriterThread.stopThread();
Thread qWriterThread;
qWriterThread.start(qWriter,NULL);
Thread mapWriterThread;
mapWriterThread.start(mapWriter,NULL);
qReaderThread.join();
qWriterThread.join();
mapReaderThread.join();
mapWriterThread.join();
}

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

@@ -44,7 +44,6 @@ libcommon_la_SOURCES = \
Utils.cpp
noinst_PROGRAMS = \
ThreadsTest \
BitVectorTest \
InterthreadTest \
SocketsTest \
@@ -81,16 +80,12 @@ URLEncodeTest_LDADD = libcommon.la
BitVectorTest_SOURCES = BitVectorTest.cpp
BitVectorTest_LDADD = libcommon.la $(SQLITE_LA)
ThreadsTest_SOURCES = ThreadsTest.cpp
ThreadsTest_LDADD = libcommon.la $(SQLITE_LA)
ThreadsTest_LDFLAGS = -lpthread
InterthreadTest_SOURCES = InterthreadTest.cpp
InterthreadTest_LDADD = libcommon.la $(SQLITE_LA)
InterthreadTest_LDADD = libcommon.la
InterthreadTest_LDFLAGS = -lpthread
SocketsTest_SOURCES = SocketsTest.cpp
SocketsTest_LDADD = libcommon.la $(SQLITE_LA)
SocketsTest_LDADD = libcommon.la
SocketsTest_LDFLAGS = -lpthread
TimevalTest_SOURCES = TimevalTest.cpp

View File

@@ -1,6 +1,5 @@
/*
* Copyright 2008, 2010 Free Software Foundation, Inc.
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
*
*
* This software is distributed under the terms of the GNU Affero Public License.
@@ -130,11 +129,6 @@ void DatagramSocket::close()
::close(mSocketFD);
}
void DatagramSocket::shutdown()
{
::shutdown(mSocketFD, SHUT_RDWR);
}
DatagramSocket::~DatagramSocket()
{

View File

@@ -1,6 +1,5 @@
/*
* Copyright 2008, 2010 Free Software Foundation, Inc.
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -135,11 +134,6 @@ public:
/** Close the socket. */
void close();
/** Shutdown the socket without destroying the descriptor
* Use this to interrupt blocking read()
*/
void shutdown();
};

View File

@@ -1,6 +1,5 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
*
*
* This software is distributed under the terms of the GNU Affero Public License.
@@ -29,73 +28,59 @@
#include "Sockets.h"
#include "Threads.h"
#include "Configuration.h"
#include "Timeval.h"
#include <stdio.h>
#include <stdlib.h>
ConfigurationTable gConfig;
static const int gNumToSend = 10;
class TestReaderIP : public Thread
void *testReaderIP(void *)
{
public:
TestReaderIP() : Thread("TestReaderIP") {}
protected:
virtual void runThread()
{
UDPSocket readSocket(5934, "localhost", 5061);
readSocket.nonblocking();
int rc = 0;
while (rc<gNumToSend) {
char buf[MAX_UDP_LENGTH];
int count = readSocket.read(buf);
if (count>0) {
COUT("IP read: " << buf);
rc++;
} else {
COUT("IP sleeping...");
sleep(2);
}
UDPSocket readSocket(5934, "localhost", 5061);
readSocket.nonblocking();
int rc = 0;
while (rc<gNumToSend) {
char buf[MAX_UDP_LENGTH];
int count = readSocket.read(buf);
if (count>0) {
COUT("read: " << buf);
rc++;
} else {
sleep(2);
}
}
};
return NULL;
}
class TestReaderUnix : public Thread
void *testReaderUnix(void *)
{
public:
TestReaderUnix() : Thread("TestReaderUnix") {}
protected:
virtual void runThread()
{
UDDSocket readSocket("testDestination");
readSocket.nonblocking();
int rc = 0;
while (rc<gNumToSend) {
char buf[MAX_UDP_LENGTH];
int count = readSocket.read(buf);
if (count>0) {
COUT("UNIX read: " << buf);
rc++;
} else {
COUT("UNIX sleeping...");
sleep(2);
}
UDDSocket readSocket("testDestination");
readSocket.nonblocking();
int rc = 0;
while (rc<gNumToSend) {
char buf[MAX_UDP_LENGTH];
int count = readSocket.read(buf);
if (count>0) {
COUT("read: " << buf);
rc++;
} else {
sleep(2);
}
}
};
return NULL;
}
int main(int argc, char * argv[] )
{
TestReaderIP readerThreadIP;
TestReaderUnix readerThreadUnix;
readerThreadIP.startThread();
readerThreadUnix.startThread();
Thread readerThreadIP;
readerThreadIP.start(testReaderIP,NULL);
Thread readerThreadUnix;
readerThreadUnix.start(testReaderUnix,NULL);
UDPSocket socket1(5061, "127.0.0.1",5934);
UDDSocket socket1U("testSource","testDestination");
@@ -107,10 +92,12 @@ int main(int argc, char * argv[] )
for (int i=0; i<gNumToSend; i++) {
socket1.write("Hello IP land");
socket1U.write("Hello Unix domain");
msleep(1);
socket1U.write("Hello Unix domain");
sleep(1);
}
readerThreadIP.join();
readerThreadUnix.join();
}
// vim: ts=4 sw=4

View File

@@ -1,6 +1,5 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
*
*
* This software is distributed under the terms of the GNU Affero Public License.
@@ -30,26 +29,11 @@
#include "Threads.h"
#include "Timeval.h"
#include "Logger.h"
#include <pthread.h>
#include <sys/types.h>
#include <errno.h> // for ETIMEDOUT
#include <sys/syscall.h> // for SYS_gettid
#include <sys/prctl.h> // Linux specific, for prctl(PR_SET_NAME)
// Make sure we get MCL_CURRENT and MCL_FUTURE (for mlockall) on OS X 10.3
#define _P1003_1B_VISIBLE
#include <sys/mman.h>
#undef _P1003_1B_VISIBLE
using namespace std;
#define POSIX_OK 0
#define POSIX_NO_WAIT 0
#define POSIX_WAIT_FOREVER (-1)
static inline int gettid() {return syscall(SYS_gettid);}
Mutex gStreamLock; ///< Global lock to control access to cout and cerr.
@@ -111,235 +95,27 @@ Mutex::~Mutex()
/** Block for the signal up to the cancellation timeout. */
int Signal::wait(Mutex& wMutex, unsigned timeout) const
void Signal::wait(Mutex& wMutex, unsigned timeout) const
{
Timeval then(timeout);
struct timespec waitTime = then.timespec();
return pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime);
pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime);
}
Thread::Thread(const string &name, size_t stackSize)
: mThreadId((pthread_t)0)
, mThreadName(name)
, mStackSize(stackSize)
, mThreadState(THREAD_STATE_IDLE)
, mThreadData(NULL)
void Thread::start(void *(*task)(void*), void *arg)
{
assert(mThread==((pthread_t)0));
bool res;
// (pat) Moved initialization to constructor to avoid crash in destructor.
//res = pthread_attr_init(&mAttrib);
//assert(!res);
res = pthread_attr_setstacksize(&mAttrib, mStackSize);
assert(!res);
res = pthread_create(&mThread, &mAttrib, task, arg);
assert(!res);
}
Thread::~Thread()
{
stopThread();
}
void *Thread::threadAdaptor(void *data)
{
Thread *pThread = (Thread*)data;
// If we ever receive a thread cancel request, it means that the Thread
// object is in the process of being destroyed. To avoid the situation
// where a thread attempts to run after its containing Thread object has
// been freed, we set the thread up so that the cancel takes effect
// immediately (as opposed to waiting until the next thread cancellation
// point).
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
// =====================================================================
// Synchronize with the start() in the parent thread.
{
// 1. Lock synchronization mutex.
ScopedLock lock(pThread->mThreadStartupMutex);
// 2. Btw, set the thread name, while we're inside the mutex.
// FIXME: This works on Linux with glibc >= 2.12. Under *BSD and MacOS X
// this function has different arguments.
// pthread_setname_np(pThread->mThreadId, pThread->mThreadName.c_str());
// FIXME: For some reason the previous call doesn't work on my Ubuntu 12.04,
// so we use this one which works.
prctl(PR_SET_NAME, pThread->mThreadName.c_str());
// 3. Signal that we've started.
pThread->mThreadStartStopEvent.signal();
// 4. Wait until start() finishes its initialization.
//
// The actual thread is created and started with pthread_create(), then
// start() does its housekeeping and sets mThreadState=THREAD_STATE_RUNNING.
// If we allow Thread::run() to start before this initialization completes,
// callers might think (among other things) that the thread is not started
// while it's actually started.
pThread->mThreadInitializedEvent.wait(pThread->mThreadStartupMutex);
}
// Synchronization with the parent thread is finished.
// =====================================================================
// Log Thread ID for debugging purposes
LOG(INFO) << "Thread started: " << pThread->mThreadName
<< " with lwp=" << gettid() << ", pid=" << getpid();
// Keep all memory locked into physical mem, to guarantee realtime-behaviour
int res = mlockall(MCL_CURRENT|MCL_FUTURE);
if (res != POSIX_OK) {
LOG(WARNING) << "Failed to lock memory for thread: " << pThread->mThreadName;
}
// Run the actual code
pThread->runThread();
// Huh, we're done. Signal to a (potentially) waiting stop()'s.
{
ScopedLock lock(pThread->mThreadStateMutex);
pThread->mThreadState = THREAD_STATE_IDLE;
pThread->mThreadStartStopEvent.broadcast();
}
return NULL;
}
Thread::ReturnStatus Thread::startThread(void *data)
{
pthread_attr_t attrib;
// timeval threadStartTime;
// timespec threadStartTimeout;
bool res;
// Lock startup synchronization mutex. It will be used in conjunction with
// mThreadInitializedEvent and mThreadStartStopEvent conditional variables.
ScopedLock lock(mThreadStartupMutex);
{
ScopedLock lock(mThreadStateMutex);
if (mThreadState != THREAD_STATE_IDLE)
return ALREADY_STARTED;
mThreadState = THREAD_STATE_STARTING;
}
// Save thread data pointer
mThreadData = data;
LOG(DEBUG) << "Starting thread " << mThreadName << " (" << this << ")";
// construct thread attribute
res = pthread_attr_init(&attrib);
if (res != POSIX_OK) {
LOG(ALERT) << "pthread_attr_init failed, returned " << res
<< " in " << mThreadName << " (" << this << ")";
}
// Set the thread stack size
res = pthread_attr_setstacksize(&attrib, mStackSize);
if (res != POSIX_OK)
{
LOG(ALERT) << "pthread_attr_setstacksize failed, returned " << res
<< " in " << mThreadName << " (" << this << ")";
}
// Create the thread detached
res = pthread_attr_setdetachstate(&attrib, PTHREAD_CREATE_DETACHED);
if (res != POSIX_OK)
{
LOG(ALERT) << "pthread_attr_setdetachstate failed, returned " << res
<< " in " << mThreadName << " (" << this << ")";
}
// =====================================================================
// Start the thread and synchronize with it
// Start the thread!
res = pthread_create(&mThreadId, &attrib, threadAdaptor, (void *)this);
// Attributes are no longer needed.
pthread_attr_destroy(&attrib);
if (res != POSIX_OK)
{
LOG(ALERT) << "pthread_create failed, returned " << res
<< " in " << mThreadName << " (" << this << ")";
return PTHREAD_ERROR;
}
// Wait for the thread to startup.
res = mThreadStartStopEvent.wait(mThreadStartupMutex, THREAD_STARTUP_TIMEOUT*1000);
// If the thread does not start in THREAD_STARTUP_TIMEOUT seconds,
// then something is terribly wrong here.
if (res == ETIMEDOUT)
{
LOG(ALERT) << "thread " << mThreadName << " (" << this << ") hasn't started up in "
<< THREAD_STARTUP_TIMEOUT << " seconds. Bailing out.";
return RETURN_TIMEOUT;
}
// We're done with the initialization.
ackThreadStart();
// ToDo: Add other initialization here, e.g. adding this thread to a list of all threads.
// Startup initialization finished. Signal this to started thread, so
// it could go on.
mThreadInitializedEvent.signal();
return RETURN_OK;
}
Thread::ReturnStatus Thread::stopThread()
{
int res;
LOG(DEBUG) << "Stopping thread " << mThreadName << " (" << this << ")";
while (1) {
ScopedLock lock(mThreadStateMutex);
switch (mThreadState) {
case THREAD_STATE_IDLE:
// Nothing to do.
return RETURN_OK;
case THREAD_STATE_STARTING:
// Something is wrong in thi world.
assert(mThreadState != THREAD_STATE_STARTING);
LOG(ALERT) << "Trying to stop thread " << mThreadName
<< " (" << this << ") while it's trying to start.";
return WRONG_STATE;
case THREAD_STATE_RUNNING:
// Request shudown
mThreadState = THREAD_STATE_STOPPING;
// no "break" here to fall through to the next case
case THREAD_STATE_STOPPING:
// Wait for the thread to stop.
LOG(DEBUG) << "Waiting for thread " << mThreadName << " (" << this << ") to stop.";
res = mThreadStartStopEvent.wait(mThreadStateMutex, THREAD_STOP_TIMEOUT*1000);
LOG(DEBUG) << "Thread " << mThreadName << " (" << this << ") signalled stop "
<< "with res=" << res << " and mThreadState=" << mThreadState;
// If the thread does not stop in THREAD_STOP_TIMEOUT seconds,
// return error. It may be waiting for something.
if (res == ETIMEDOUT)
{
LOG(ALERT) << "thread " << mThreadName << " (" << this << ") hasn't stopped in "
<< THREAD_STARTUP_TIMEOUT << " seconds. Bailing out.";
return RETURN_TIMEOUT;
}
// Conditional variable could return in case of a signal, so we should
// double check that the thread has indeed stopped.
if (mThreadState == THREAD_STATE_IDLE)
return RETURN_OK;
else
// Try again...
break;
}
}
// We should never reach this line
assert(false);
return RETURN_OK;
}
// vim: ts=4 sw=4

View File

@@ -1,6 +1,5 @@
/*
* Copyright 2008, 2011 Free Software Foundation, Inc.
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -122,14 +121,14 @@ class Signal {
Block for the signal up to the cancellation timeout.
Under Linux, spurious returns are possible.
*/
int wait(Mutex& wMutex, unsigned timeout) const;
void wait(Mutex& wMutex, unsigned timeout) const;
/**
Block for the signal.
Under Linux, spurious returns are possible.
*/
int wait(Mutex& wMutex) const
{ return pthread_cond_wait(&mSignal,&wMutex.mMutex); }
void wait(Mutex& wMutex) const
{ pthread_cond_wait(&mSignal,&wMutex.mMutex); }
void signal() { pthread_cond_signal(&mSignal); }
@@ -138,105 +137,54 @@ class Signal {
};
#define START_THREAD(thread,function,argument) \
thread.start((void *(*)(void*))function, (void*)argument);
/** A C++ wrapper for pthread threads. */
class Thread {
public:
private:
typedef void *(*Adaptor)(void*);
enum ReturnStatus {
RETURN_OK = 0,
ALREADY_STARTED,
ALREADY_IDLE,
PTHREAD_ERROR,
WRONG_STATE,
RETURN_TIMEOUT
};
enum ThreadState {
THREAD_STATE_IDLE, ///< Thread is not started. On start() => STARTING
THREAD_STATE_STARTING, ///< Thread is about to start. When actually started => RUNNING
THREAD_STATE_RUNNING, ///< Thread is active. On stop() => STOPPING
THREAD_STATE_STOPPING ///< Thread is about to stop. When actually stopped => IDLE
};
enum {
THREAD_STARTUP_TIMEOUT=5, ///< Time to wait for thread startup (in seconds).
THREAD_STOP_TIMEOUT=5 ///< Time to wait for thread stop (in seconds).
};
pthread_t mThread;
pthread_attr_t mAttrib;
// FIXME -- Can this be reduced now?
size_t mStackSize;
/** Create a thread in a non-running state. */
Thread(const std::string &name, size_t stackSize = (65536*4));
public:
/** Destroy the Thread. */
virtual ~Thread();
/** Create a thread in a non-running state. */
Thread(size_t wStackSize = (65536*4)):mThread((pthread_t)0) {
pthread_attr_init(&mAttrib); // (pat) moved this here.
mStackSize=wStackSize;
}
/** Start the thread. */
ReturnStatus startThread(void *data=NULL);
/**
Destroy the Thread.
It should be stopped and joined.
*/
// (pat) If the Thread is destroyed without being started, then mAttrib is undefined. Oops.
~Thread() { pthread_attr_destroy(&mAttrib); }
/** Stop the thread. */
ReturnStatus stopThread();
ThreadState getThreadState() const
{
ScopedLock lock(mThreadStateMutex);
return mThreadState;
}
/** Start the thread on a task. */
void start(void *(*task)(void*), void *arg);
bool isThreadRunning() const
{
ScopedLock lock(mThreadStateMutex);
return mThreadState == THREAD_STATE_RUNNING;
}
void requestThreadStop()
{
ScopedLock lock(mThreadStateMutex);
if (mThreadState == THREAD_STATE_RUNNING)
mThreadState = THREAD_STATE_STOPPING;
}
bool isThreadStopping() const
{
ScopedLock lock(mThreadStateMutex);
return mThreadState == THREAD_STATE_STOPPING;
}
/** Join a thread that will stop on its own. */
void join() {
if (mThread) {
int s = pthread_join(mThread, NULL);
assert(!s);
}
}
const std::string &getThreadName() const {return mThreadName;}
protected:
pthread_t mThreadId; ///< OS id of the thread.
const std::string mThreadName; ///< Name of the thread.
size_t mStackSize; ///< Requested stack size for the thread.
ThreadState mThreadState; ///< The current state of the thread.
mutable Mutex mThreadStateMutex; ///< Mutex to protect ThreadState variable
void *mThreadData; ///< Data to be passed to the thread loop.
Mutex mThreadStartupMutex; ///< Mutex, used with the next two conditional
///< variables to synchronize thread startup.
Signal mThreadInitializedEvent; ///< Conditional variable, signaling
///< that this thread object initialization is completed
///< and the thread could go on.
Signal mThreadStartStopEvent; ///< Conditional variable, signaling
///< that the thread is started and start() method could
///< return to caller.
/** Function with the actual thread loop.
* Override this function in child classes to do real work.
*/
virtual void runThread() =0;
// Static funciton which actually starts the run() method.
static void *threadAdaptor(void *data);
void ackThreadStart() {
ScopedLock lock(mThreadStateMutex);
assert(mThreadState == THREAD_STATE_STARTING);
mThreadState = THREAD_STATE_RUNNING;
}
void ackThreadStop() {
ScopedLock lock(mThreadStateMutex);
assert(mThreadState == THREAD_STATE_STOPPING);
mThreadState = THREAD_STATE_IDLE;
}
/** Send cancelation to thread */
void cancel() { pthread_cancel(mThread); }
};
#endif
// vim: ts=4 sw=4

View File

@@ -1,94 +0,0 @@
/*
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
*
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Threads.h"
#include "Timeval.h"
#include "Configuration.h"
#include <iostream>
ConfigurationTable gConfig;
class SimpleThreadTest : public Thread
{
public:
SimpleThreadTest() : Thread("SimpleThreadTest") {}
void runThread()
{
COUT(getThreadName() << ": Started thread");
while (isThreadRunning()) {
COUT(getThreadName() << ": Sleeping...");
msleep(50);
}
COUT(getThreadName() << ": Stopped thread");
}
};
void testSimpleStartStop()
{
SimpleThreadTest simpleThreadTest;
COUT("Main: Starting thread " << simpleThreadTest.getThreadName());
simpleThreadTest.startThread();
COUT("Main: Started thread " << simpleThreadTest.getThreadName());
msleep(30);
COUT("Main: Stopping thread " << simpleThreadTest.getThreadName());
simpleThreadTest.stopThread();
COUT("Main: Stopped thread " << simpleThreadTest.getThreadName());
}
void testDoubleRequestStop()
{
SimpleThreadTest simpleThreadTest;
COUT("Main: Starting thread " << simpleThreadTest.getThreadName());
simpleThreadTest.startThread();
COUT("Main: Started thread " << simpleThreadTest.getThreadName());
msleep(30);
COUT("Main: Requesting stop for thread " << simpleThreadTest.getThreadName());
simpleThreadTest.requestThreadStop();
msleep(30);
COUT("Main: Requesting stop for thread " << simpleThreadTest.getThreadName());
simpleThreadTest.requestThreadStop();
msleep(30);
COUT("Main: Stopping thread " << simpleThreadTest.getThreadName());
simpleThreadTest.stopThread();
COUT("Main: Stopped thread " << simpleThreadTest.getThreadName());
}
int main(int argc, char *argv[])
{
std::cout<< std::endl << "Simple start/stop test" << std::endl << std::endl ;
testSimpleStartStop();
std::cout << std::endl << "Double requestThreadStop() test" << std::endl << std::endl ;
testDoubleRequestStop();
}
// vim: ts=4 sw=4

View File

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

View File

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

View File

@@ -20,6 +20,7 @@
include $(top_srcdir)/Makefile.common
ACLOCAL_AMFLAGS = -I config
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USB_INCLUDES) $(WITH_INCLUDES)
AM_CXXFLAGS = -Wall -pthread -ldl
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread -ldl

View File

@@ -18,11 +18,15 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
top_srcdir = $(abs_top_srcdir)
top_builddir = $(abs_top_builddir)
COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs
GSM_INCLUDEDIR = $(top_srcdir)/GSM
SQLITE_INCLUDEDIR = $(top_srcdir)/sqlite3
STD_DEFINES_AND_INCLUDES = \
$(SVNDEV) \
-I$(COMMON_INCLUDEDIR) \
-I$(GSM_INCLUDEDIR) \
-I$(SQLITE_INCLUDEDIR)

260
README
View File

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

View File

@@ -1,268 +0,0 @@
/*
* Copyright 2008, 2009, 2010, 2012 Free Software Foundation, Inc.
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
*
* This software is distributed under the terms of the GNU Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include "DriveLoop.h"
#include <Logger.h>
using namespace GSM;
DriveLoop::DriveLoop(int wBasePort, const char *TRXAddress,
RadioInterface *wRadioInterface,
int wChanM, int wC0, int wSamplesPerSymbol,
GSM::Time wTransmitLatency)
: Thread("DriveLoop")
, mClockSocket(wBasePort, TRXAddress, wBasePort + 100)
, mC0(wC0)
{
mChanM = wChanM;
mSamplesPerSymbol = wSamplesPerSymbol;
mRadioInterface = wRadioInterface;
mStartTime = (random() % gHyperframe, 0);
mTransmitDeadlineClock = mStartTime;
mLatencyUpdateTime = mStartTime;
mTransmitLatency = wTransmitLatency;
mLastClockUpdateTime = mStartTime;
mRadioInterface->getClock()->set(mStartTime);
// generate pulse and setup up signal processing library
gsmPulse = generateGSMPulse(2, mSamplesPerSymbol);
LOG(DEBUG) << "gsmPulse: " << *gsmPulse;
sigProcLibSetup(mSamplesPerSymbol);
txFullScale = mRadioInterface->fullScaleInputValue();
// initialize filler tables with dummy bursts on C0, empty bursts otherwise
for (int i = 0; i < 8; i++) {
signalVector* modBurst = modulateBurst(gDummyBurst, *gsmPulse,
8 + (i % 4 == 0), mSamplesPerSymbol);
scaleVector(*modBurst, txFullScale);
for (int j = 0; j < 102; j++) {
for (int n = 0; n < mChanM; n++) {
if (n == mC0)
fillerTable[n][j][i] = new signalVector(*modBurst);
else
fillerTable[n][j][i] = new signalVector(modBurst->size());
}
}
delete modBurst;
for (int n = 0; n < mChanM; n++) {
fillerModulus[n][i] = 26;
mChanType[n][i] = NONE;
}
}
}
DriveLoop::~DriveLoop()
{
stopThread();
delete gsmPulse;
sigProcLibDestroy();
}
void DriveLoop::pushRadioVector(GSM::Time &nowTime)
{
int i;
radioVector *staleBurst;
radioVector *next;
for (i = 0; i < mChanM; i++) {
// dump stale bursts, if any
while (staleBurst = mTransmitPriorityQueue[i].getStaleBurst(nowTime)) {
// Even if the burst is stale, put it in the fillter table.
// (It might be an idle pattern.)
LOG(NOTICE) << "dumping STALE burst in TRX->USRP interface";
}
int TN = nowTime.TN();
int modFN = nowTime.FN() % fillerModulus[i][nowTime.TN()];
mTxBursts[i] = fillerTable[i][modFN][TN];
mIsFiller[i] = true;
mIsZero[i] = (mChanType[i][TN] == NONE);
// if queue contains data at the desired timestamp, stick it into FIFO
if (next = (radioVector*) mTransmitPriorityQueue[i].getCurrentBurst(nowTime)) {
LOG(DEBUG) << "transmitFIFO: wrote burst " << next << " at time: " << nowTime;
mTxBursts[i] = next;
mIsFiller[i] = false;
mIsZero[i] = false;
}
}
mRadioInterface->driveTransmitRadio(mTxBursts, mIsZero);
for (i = 0; i < mChanM; i++) {
if (!mIsFiller[i])
delete mTxBursts[i];
}
}
void DriveLoop::setModulus(int channel, int timeslot)
{
switch (mChanType[channel][timeslot]) {
case NONE:
case I:
case II:
case III:
case FILL:
fillerModulus[channel][timeslot] = 26;
break;
case IV:
case VI:
case V:
fillerModulus[channel][timeslot] = 51;
break;
//case V:
case VII:
fillerModulus[channel][timeslot] = 102;
break;
default:
break;
}
}
DriveLoop::CorrType DriveLoop::expectedCorrType(int channel, GSM::Time currTime)
{
unsigned burstTN = currTime.TN();
unsigned burstFN = currTime.FN();
switch (mChanType[channel][burstTN]) {
case NONE:
return OFF;
break;
case FILL:
return IDLE;
break;
case I:
return TSC;
/*if (burstFN % 26 == 25)
return IDLE;
else
return TSC;*/
break;
case II:
if (burstFN % 2 == 1)
return IDLE;
else
return TSC;
break;
case III:
return TSC;
break;
case IV:
case VI:
return RACH;
break;
case V: {
int mod51 = burstFN % 51;
if ((mod51 <= 36) && (mod51 >= 14))
return RACH;
else if ((mod51 == 4) || (mod51 == 5))
return RACH;
else if ((mod51 == 45) || (mod51 == 46))
return RACH;
else
return TSC;
break;
}
case VII:
if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
return IDLE;
else
return TSC;
break;
case LOOPBACK:
if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
return IDLE;
else
return TSC;
break;
default:
return OFF;
break;
}
}
void DriveLoop::driveReceiveFIFO()
{
SoftVector *rxBurst = NULL;
int RSSI;
int TOA; // in 1/256 of a symbol
GSM::Time burstTime;
mRadioInterface->driveReceiveRadio();
}
/*
* Features a carefully controlled latency mechanism, to
* assure that transmit packets arrive at the radio/USRP
* before they need to be transmitted.
*
* Deadline clock indicates the burst that needs to be
* pushed into the FIFO right NOW. If transmit queue does
* not have a burst, stick in filler data.
*/
void DriveLoop::driveTransmitFIFO()
{
int i;
RadioClock *radioClock = (mRadioInterface->getClock());
while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
pushRadioVector(mTransmitDeadlineClock);
mTransmitDeadlineClock.incTN();
}
// FIXME -- This should not be a hard spin.
// But any delay here causes us to throw omni_thread_fatal.
//else radioClock->wait();
}
void DriveLoop::writeClockInterface()
{
char command[50];
// FIXME -- This should be adaptive.
sprintf(command,"IND CLOCK %llu",
(unsigned long long) (mTransmitDeadlineClock.FN() + 2));
LOG(INFO) << "ClockInterface: sending " << command;
mClockSocket.write(command,strlen(command)+1);
mLastClockUpdateTime = mTransmitDeadlineClock;
}
void DriveLoop::runThread()
{
setPriority();
while (isThreadRunning()) {
driveReceiveFIFO();
driveTransmitFIFO();
}
}

View File

@@ -1,188 +0,0 @@
/*
* Copyright 2008, 2012 Free Software Foundation, Inc.
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
*
* This software is distributed under the terms of the GNU Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
Compilation switches
TRANSMIT_LOGGING write every burst on the given slot to a log
*/
#ifndef _DRIVELOOP_H_
#define _DRIVELOOP_H_
#include "radioInterface.h"
#include "Interthread.h"
#include "GSMCommon.h"
#include "Sockets.h"
#include <sys/types.h>
#include <sys/socket.h>
/** Define this to be the slot number to be logged. */
//#define TRANSMIT_LOGGING 1
/** The Transceiver class, responsible for physical layer of basestation */
class DriveLoop : public Thread {
private:
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
UDPSocket mClockSocket; ///< socket for writing clock updates to GSM core
VectorQueue mTransmitPriorityQueue[CHAN_MAX]; ///< priority queue of transmit bursts received from GSM core
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
GSM::Time mStartTime; ///< random start time of the radio clock
RadioInterface *mRadioInterface; ///< associated radioInterface object
double txFullScale; ///< full scale input to radio
double rxFullScale; ///< full scale output to radio
/** Number of channels supported by the channelizer */
int mChanM;
/** unmodulate a modulated burst */
#ifdef TRANSMIT_LOGGING
void unModulateVector(signalVector wVector);
#endif
/** 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);
signalVector *gsmPulse; ///< the GSM shaping pulse for modulation
int mSamplesPerSymbol; ///< number of samples per GSM symbol
int fillerModulus[CHAN_MAX][8]; ///< modulus values of all timeslots, in frames
signalVector *fillerTable[CHAN_MAX][102][8]; ///< table of modulated filler waveforms for all timeslots
/** Channelizer path for primary ARFCN */
int mC0;
signalVector *mTxBursts[CHAN_MAX];
bool mIsFiller[CHAN_MAX];
bool mIsZero[CHAN_MAX];
public:
/** Transceiver constructor
@param wBasePort base port number of UDP sockets
@param TRXAddress IP address of the TRX manager, as a string
@param wSamplesPerSymbol number of samples per GSM symbol
@param wTransmitLatency initial setting of transmit latency
@param radioInterface associated radioInterface object
*/
DriveLoop(int wBasePort, const char *TRXAddress,
RadioInterface *wRadioInterface,
int wChanM = 1, int wC0 = 0,
int wSamplesPerSymbol = SAMPSPERSYM,
GSM::Time wTransmitLatency = GSM::Time(3, 0));
/** Destructor */
~DriveLoop();
VectorQueue *priorityQueue(int m) { return &mTransmitPriorityQueue[m]; }
/** 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;
/** Codes for channel combinations */
typedef enum {
FILL, ///< Channel is transmitted, but unused
I, ///< TCH/FS
II, ///< TCH/HS, idle every other slot
III, ///< TCH/HS
IV, ///< FCCH+SCH+CCCH+BCCH, uplink RACH
V, ///< FCCH+SCH+CCCH+BCCH+SDCCH/4+SACCH/4, uplink RACH+SDCCH/4
VI, ///< CCCH+BCCH, uplink RACH
VII, ///< SDCCH/8 + SACCH/8
NONE, ///< Channel is inactive, default
LOOPBACK ///< similar go VII, used in loopback testing
} ChannelCombination;
/** Set modulus for specific timeslot */
void setModulus(int channel, int timeslot);
/** return the expected burst type for the specified timestamp */
CorrType expectedCorrType(int channel, GSM::Time currTime);
void setTimeslot(int m, int timeslot, ChannelCombination comb)
{
mChanType[m][timeslot] = comb;
}
GSM::Time getStartTime() { return mStartTime; }
GSM::Time getLastClockUpdate() { return mLastClockUpdateTime; }
GSM::Time getDeadlineClock() { return mTransmitDeadlineClock; }
/** send messages over the clock socket */
void writeClockInterface(void);
private:
ChannelCombination mChanType[CHAN_MAX][8]; ///< channel types for all timeslots
protected:
/** drive reception and demodulation of GSM bursts */
void driveReceiveFIFO();
/** drive transmission of GSM bursts */
void driveTransmitFIFO();
/** drive handling of control messages from GSM core */
void driveControl();
/**
drive modulation and sorting of GSM bursts from GSM core
@return true if a burst was transferred successfully
*/
bool driveTransmitPriorityQueue();
virtual void runThread();
void reset();
/** set priority on current thread */
void setPriority() { mRadioInterface->setPriority(); }
};
/** FIFO thread loop */
void *RadioDriveLoopAdapter(DriveLoop *);
#endif /* _DRIVELOOP_H_ */

View File

@@ -1,146 +0,0 @@
/*
* Copyright 2008, 2009 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
Compilation Flags
SWLOOPBACK compile for software loopback testing
*/
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include "Threads.h"
#include "DummyLoad.h"
#include <Logger.h>
using namespace std;
int DummyLoad::loadBurst(short *wDummyBurst, int len) {
dummyBurst = wDummyBurst;
dummyBurstSz = len;
}
DummyLoad::DummyLoad (double _desiredSampleRate)
{
LOG(INFO) << "creating USRP device...";
sampleRate = _desiredSampleRate;
}
void DummyLoad::updateTime(void) {
gettimeofday(&currTime,NULL);
double timeElapsed = (currTime.tv_sec - startTime.tv_sec)*1.0e6 +
(currTime.tv_usec - startTime.tv_usec);
currstamp = (TIMESTAMP) floor(timeElapsed/(1.0e6/sampleRate));
}
bool DummyLoad::make(bool wSkipRx)
{
samplesRead = 0;
samplesWritten = 0;
return true;
}
bool DummyLoad::start()
{
LOG(INFO) << "starting USRP...";
underrun = false;
gettimeofday(&startTime,NULL);
dummyBurstCursor = 0;
return true;
}
bool DummyLoad::stop()
{
return true;
}
// NOTE: Assumes sequential reads
int DummyLoad::readSamples(short *buf, int len, bool *overrun,
TIMESTAMP timestamp,
bool *wUnderrun,
unsigned *RSSI)
{
updateTime();
underrunLock.lock();
*wUnderrun = underrun;
underrunLock.unlock();
if (currstamp+len < timestamp) {
usleep(100);
return 0;
}
else if (currstamp < timestamp) {
usleep(100);
return 0;
}
else if (timestamp+len < currstamp) {
memcpy(buf,dummyBurst+dummyBurstCursor*2,sizeof(short)*2*(dummyBurstSz-dummyBurstCursor));
int retVal = dummyBurstSz-dummyBurstCursor;
dummyBurstCursor = 0;
return retVal;
}
else if (timestamp + len > currstamp) {
int amount = timestamp + len - currstamp;
if (amount < dummyBurstSz-dummyBurstCursor) {
memcpy(buf,dummyBurst+dummyBurstCursor*2,sizeof(short)*2*amount);
dummyBurstCursor += amount;
return amount;
}
else {
memcpy(buf,dummyBurst+dummyBurstCursor*2,sizeof(short)*2*(dummyBurstSz-dummyBurstCursor));
int retVal = dummyBurstSz-dummyBurstCursor;
dummyBurstCursor = 0;
return retVal;
}
}
return 0;
}
int DummyLoad::writeSamples(short *buf, int len, bool *wUnderrun,
unsigned long long timestamp,
bool isControl)
{
updateTime();
underrunLock.lock();
underrun |= (currstamp+len < timestamp);
underrunLock.unlock();
return len;
}
bool DummyLoad::updateAlignment(TIMESTAMP timestamp)
{
return true;
}
bool DummyLoad::setTxFreq(double wFreq) { return true;};
bool DummyLoad::setRxFreq(double wFreq) { return true;};

View File

@@ -1,132 +0,0 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "radioDevice.h"
#include <sys/time.h>
#include <math.h>
#include <string>
#include <iostream>
/** A class to handle a USRP rev 4, with a two RFX900 daughterboards */
class DummyLoad: public RadioDevice {
private:
double sampleRate; ///< the desired sampling rate
unsigned long long samplesRead; ///< number of samples read from USRP
unsigned long long samplesWritten; ///< number of samples sent to USRP
Mutex underrunLock;
struct timeval startTime, currTime;
TIMESTAMP currstamp;
short *dummyBurst;
int dummyBurstSz;
int dummyBurstCursor;
bool underrun;
void updateTime(void);
public:
/** Object constructor */
DummyLoad (double _desiredSampleRate);
int loadBurst(short *wDummyBurst, int len);
/** Instantiate the USRP */
bool make(bool skipRx = false);
/** Start the USRP */
bool start();
/** Stop the USRP */
bool stop();
/**
Read samples from the USRP.
@param buf preallocated buf to contain read result
@param len number of samples desired
@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
@param timestamp The timestamp of the first samples to be read
@param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough
@param RSSI The received signal strength of the read result
@return The number of samples actually read
*/
int readSamples(short *buf, int len, bool *overrun,
TIMESTAMP timestamp = 0xffffffff,
bool *underrun = NULL,
unsigned *RSSI = NULL);
/**
Write samples to the USRP.
@param buf Contains the data to be written.
@param len number of samples to write.
@param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough
@param timestamp The timestamp of the first sample of the data buffer.
@param isControl Set if data is a control packet, e.g. a ping command
@return The number of samples actually written
*/
int writeSamples(short *buf, int len, bool *underrun,
TIMESTAMP timestamp = 0xffffffff,
bool isControl = false);
/** Update the alignment between the read and write timestamps */
bool updateAlignment(TIMESTAMP timestamp);
/** Set the transmitter frequency */
bool setTxFreq(double wFreq);
/** Set the receiver frequency */
bool setRxFreq(double wFreq);
/** Returns the starting write Timestamp*/
TIMESTAMP initialWriteTimestamp(void) { return 20000;}
/** Returns the starting read Timestamp*/
TIMESTAMP initialReadTimestamp(void) { return 20000;}
/** returns the full-scale transmit amplitude **/
double fullScaleInputValue() {return 13500.0;}
/** returns the full-scale receive amplitude **/
double fullScaleOutputValue() {return 9450.0;}
/** Return internal status values */
inline double getTxFreq() { return 0;}
inline double getRxFreq() { return 0;}
inline double getSampleRate() {return sampleRate;}
inline double numberRead() { return samplesRead; }
inline double numberWritten() { return samplesWritten;}
};

View File

@@ -21,20 +21,23 @@
include $(top_srcdir)/Makefile.common
#UHD wins if both are defined
if UHD
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(UHD_CFLAGS)
else
if USRP1
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USRP_CFLAGS)
else
#we should never be here, as this doesn't build if one of the above
#doesn't exist
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
endif
endif
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/common
AM_CXXFLAGS = -ldl -lpthread
SUBDIRS = arm x86
if ARCH_ARM
ARCH_LA = arm/libarch.la
else
ARCH_LA = x86/libarch.la
endif
if USRP1
AM_CPPFLAGS += $(USRP_CFLAGS)
else
AM_CPPFLAGS += $(UHD_CFLAGS)
endif
rev2dir = $(datadir)/usrp/rev2
rev4dir = $(datadir)/usrp/rev4
@@ -52,16 +55,16 @@ COMMON_SOURCES = \
radioVector.cpp \
radioClock.cpp \
sigProcLib.cpp \
DriveLoop.cpp \
Transceiver.cpp \
DummyLoad.cpp
signalVector.cpp \
Transceiver.cpp
libtransceiver_la_SOURCES = \
$(COMMON_SOURCES)
$(COMMON_SOURCES) \
Resampler.cpp \
radioInterfaceResamp.cpp \
radioInterfaceDiversity.cpp
noinst_PROGRAMS = \
transceiver \
sigProcLibTest
bin_PROGRAMS = osmo-trx
noinst_HEADERS = \
Complex.h \
@@ -70,45 +73,26 @@ noinst_HEADERS = \
radioClock.h \
radioDevice.h \
sigProcLib.h \
signalVector.h \
Transceiver.h \
USRPDevice.h \
DummyLoad.h \
rcvLPF_651.h \
sendLPF_961.h
Resampler.h \
common/convolve.h \
common/convert.h \
common/scale.h \
common/mult.h
transceiver_SOURCES = multiTRX.cpp
transceiver_LDADD = \
osmo_trx_SOURCES = osmo-trx.cpp
osmo_trx_LDADD = \
libtransceiver.la \
$(ARCH_LA) \
$(GSM_LA) \
$(COMMON_LA) $(SQLITE_LA)
sigProcLibTest_SOURCES = sigProcLibTest.cpp
sigProcLibTest_LDADD = \
libtransceiver.la \
$(GSM_LA) \
$(COMMON_LA) $(SQLITE_LA)
#uhd wins
if UHD
libtransceiver_la_SOURCES += UHDDevice.cpp
transceiver_LDADD += $(UHD_LIBS)
sigProcLibTest_LDADD += $(UHD_LIBS)
else
if USRP1
if USRP1
libtransceiver_la_SOURCES += USRPDevice.cpp
transceiver_LDADD += $(USRP_LIBS)
sigProcLibTest_LDADD += $(USRP_LIBS)
osmo_trx_LDADD += $(USRP_LIBS)
else
#we should never be here, as one of the above mustbe defined for us to build
libtransceiver_la_SOURCES += UHDDevice.cpp
osmo_trx_LDADD += $(UHD_LIBS)
endif
endif
MOSTLYCLEANFILES +=
#radioInterface.cpp
#ComplexTest.cpp
#sigProcLibTest.cpp
#sweepGenerator.cpp
#testRadio.cpp

View File

@@ -1,15 +0,0 @@
Basic model:
Have channel H = {h_0, h_1, ..., h_{K-1}}.
Have received sequence Y = {y_0, ..., y_{K+N}}.
Have transmitted sequence X = {x_0, ..., x_{N-1}}.
Denote state S_n = {x_n, x_{n-1}, ..., x_{n-L}}.
Define a bag as an unordered collection with two operations, add and take.
We have three bags:
S: a bag of survivors.
F: a bag of available data structures.
At time n, start with a non-empty bag S of survivors from time n-1.
Take a member out of S, and create all possible branches and their corresponding metrics. If metric ratio is above T, discard branch. Otherwise, check branch against entry in pruning table P. If branch metric is smaller than the existing entry's metric in P, then replace entry with branch. Otherwise, discard branch.
Once all possible branches of S have been created and pruned, S should be empty.Empty pruning table back into S, thus P is now empty. Repeat.

View File

@@ -0,0 +1,252 @@
/*
* Rational Sample Rate Conversion
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <malloc.h>
#include <iostream>
#include "Resampler.h"
extern "C" {
#include "convolve.h"
}
#ifndef M_PI
#define M_PI 3.14159265358979323846264338327f
#endif
#define MAX_OUTPUT_LEN 4096
static float sinc(float x)
{
if (x == 0.0)
return 0.9999999999;
return sin(M_PI * x) / (M_PI * x);
}
bool Resampler::initFilters(float bw)
{
size_t proto_len = p * filt_len;
float *proto, val, cutoff;
float sum = 0.0f, scale = 0.0f;
float midpt = (float) (proto_len - 1.0) / 2.0;
/*
* Allocate partition filters and the temporary prototype filter
* according to numerator of the rational rate. Coefficients are
* real only and must be 16-byte memory aligned for SSE usage.
*/
proto = new float[proto_len];
if (!proto)
return false;
partitions = (float **) malloc(sizeof(float *) * p);
if (!partitions) {
free(proto);
return false;
}
for (size_t i = 0; i < p; i++) {
partitions[i] = (float *)
memalign(16, filt_len * 2 * sizeof(float));
}
/*
* Generate the prototype filter with a Blackman-harris window.
* Scale coefficients with DC filter gain set to unity divided
* by the number of filter partitions.
*/
float a0 = 0.35875;
float a1 = 0.48829;
float a2 = 0.14128;
float a3 = 0.01168;
if (p > q)
cutoff = (float) p;
else
cutoff = (float) q;
for (size_t i = 0; i < proto_len; i++) {
proto[i] = sinc(((float) i - midpt) / cutoff * bw);
proto[i] *= a0 -
a1 * cos(2 * M_PI * i / (proto_len - 1)) +
a2 * cos(4 * M_PI * i / (proto_len - 1)) -
a3 * cos(6 * M_PI * i / (proto_len - 1));
sum += proto[i];
}
scale = p / sum;
/* Populate filter partitions from the prototype filter */
for (size_t i = 0; i < filt_len; i++) {
for (size_t n = 0; n < p; n++) {
partitions[n][2 * i + 0] = proto[i * p + n] * scale;
partitions[n][2 * i + 1] = 0.0f;
}
}
/* For convolution, we store the filter taps in reverse */
for (size_t n = 0; n < p; n++) {
for (size_t i = 0; i < filt_len / 2; i++) {
val = partitions[n][2 * i];
partitions[n][2 * i] = partitions[n][2 * (filt_len - 1 - i)];
partitions[n][2 * (filt_len - 1 - i)] = val;
}
}
delete proto;
return true;
}
void Resampler::releaseFilters()
{
if (partitions) {
for (size_t i = 0; i < p; i++)
free(partitions[i]);
}
free(partitions);
partitions = NULL;
}
static bool check_vec_len(int in_len, int out_len, int p, int q)
{
if (in_len % q) {
std::cerr << "Invalid input length " << in_len
<< " is not multiple of " << q << std::endl;
return false;
}
if (out_len % p) {
std::cerr << "Invalid output length " << out_len
<< " is not multiple of " << p << std::endl;
return false;
}
if ((in_len / q) != (out_len / p)) {
std::cerr << "Input/output block length mismatch" << std::endl;
std::cerr << "P = " << p << ", Q = " << q << std::endl;
std::cerr << "Input len: " << in_len << std::endl;
std::cerr << "Output len: " << out_len << std::endl;
return false;
}
if (out_len > MAX_OUTPUT_LEN) {
std::cerr << "Block length of " << out_len
<< " exceeds max of " << MAX_OUTPUT_LEN << std::endl;
return false;
}
return true;
}
void Resampler::computePath()
{
for (int i = 0; i < MAX_OUTPUT_LEN; i++) {
in_index[i] = (q * i) / p;
out_path[i] = (q * i) % p;
}
}
int Resampler::rotate(float *in, size_t in_len, float *out, size_t out_len)
{
int n, path;
int hist_len = filt_len - 1;
if (!check_vec_len(in_len, out_len, p, q))
return -1;
if (history_on) {
memcpy(&in[-2 * hist_len],
history, hist_len * 2 * sizeof(float));
} else {
memset(&in[-2 * hist_len], 0,
hist_len * 2 * sizeof(float));
}
/* Generate output from precomputed input/output paths */
for (size_t i = 0; i < out_len; i++) {
n = in_index[i];
path = out_path[i];
convolve_real(in, in_len,
partitions[path], filt_len,
&out[2 * i], out_len - i,
n, 1, 1, 0);
}
/* Save history */
if (history_on) {
memcpy(history, &in[2 * (in_len - hist_len)],
hist_len * 2 * sizeof(float));
}
return out_len;
}
bool Resampler::init(float bw)
{
size_t hist_len = filt_len - 1;
/* Filterbank filter internals */
if (initFilters(bw) < 0)
return false;
/* History buffer */
history = new float[2 * hist_len];
memset(history, 0, 2 * hist_len * sizeof(float));
/* Precompute filterbank paths */
in_index = new size_t[MAX_OUTPUT_LEN];
out_path = new size_t[MAX_OUTPUT_LEN];
computePath();
return true;
}
size_t Resampler::len()
{
return filt_len;
}
void Resampler::enableHistory(bool on)
{
history_on = on;
}
Resampler::Resampler(size_t p, size_t q, size_t filt_len)
: in_index(NULL), out_path(NULL), partitions(NULL),
history(NULL), history_on(true)
{
this->p = p;
this->q = q;
this->filt_len = filt_len;
}
Resampler::~Resampler()
{
releaseFilters();
delete history;
delete in_index;
delete out_path;
}

View File

@@ -0,0 +1,83 @@
/*
* Rational Sample Rate Conversion
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _RESAMPLER_H_
#define _RESAMPLER_H_
class Resampler {
public:
/* Constructor for rational sample rate conversion
* @param p numerator of resampling ratio
* @param q denominator of resampling ratio
* @param filt_len length of each polyphase subfilter
*/
Resampler(size_t p, size_t q, size_t filt_len = 16);
~Resampler();
/* Initilize resampler filterbank.
* @param bw bandwidth factor on filter generation (pre-window)
* @return false on error, zero otherwise
*
* Automatic setting is to compute the filter to prevent aliasing with
* a Blackman-Harris window. Adjustment is made through a bandwith
* factor to shift the cutoff and/or the constituent filter lengths.
* Calculation of specific rolloff factors or 3-dB cutoff points is
* left as an excersize for the reader.
*/
bool init(float bw = 1.0f);
/* Rotate "commutator" and drive samples through filterbank
* @param in continuous buffer of input complex float values
* @param in_len input buffer length
* @param out continuous buffer of output complex float values
* @param out_len output buffer length
* @return number of samples outputted, negative on error
*
* Input and output vector lengths must of be equal multiples of the
* rational conversion rate denominator and numerator respectively.
*/
int rotate(float *in, size_t in_len, float *out, size_t out_len);
/* Get filter length
* @return number of taps in each filter partition
*/
size_t len();
/*
* Enable/disable history
*/
void enableHistory(bool on);
private:
size_t p;
size_t q;
size_t filt_len;
size_t *in_index;
size_t *out_path;
float **partitions;
float *history;
bool history_on;
bool initFilters(float bw);
void releaseFilters();
void computePath();
};
#endif /* _RESAMPLER_H_ */

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,5 @@
/*
* Copyright 2008, 2012 Free Software Foundation, Inc.
* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
* Copyright 2008 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Public License.
* See the COPYING file in the main directory for details.
@@ -23,14 +22,6 @@
*/
/*
Compilation switches
TRANSMIT_LOGGING write every burst on the given slot to a log
*/
#include "DriveLoop.h"
#include "radioInterface.h"
#include "Interthread.h"
#include "GSMCommon.h"
@@ -39,165 +30,265 @@
#include <sys/types.h>
#include <sys/socket.h>
/** Define this to be the slot number to be logged. */
//#define TRANSMIT_LOGGING 1
class Transceiver;
/** FIFO thread loop */
class FIFOServiceLoopThread : public Thread {
public:
FIFOServiceLoopThread() : Thread("FIFOServiceLoopThread") {}
Thread::ReturnStatus shutdown();
/** Channel descriptor for transceiver object and channel number pair */
struct TransceiverChannel {
TransceiverChannel(Transceiver *trx, int num)
{
this->trx = trx;
this->num = num;
}
protected:
virtual void runThread();
~TransceiverChannel()
{
}
Transceiver *trx;
size_t num;
};
/** control message handler thread loop */
class ControlServiceLoopThread : public Thread {
public:
ControlServiceLoopThread() : Thread("ControlServiceLoopThread") {}
Thread::ReturnStatus shutdown();
/** Internal transceiver state variables */
struct TransceiverState {
TransceiverState();
~TransceiverState();
protected:
virtual void runThread();
};
/* Initialize a multiframe slot in the filler table */
bool init(int filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay);
/** transmit queueing thread loop */
class TransmitPriorityQueueServiceLoopThread : public Thread {
public:
TransmitPriorityQueueServiceLoopThread() : Thread("TransmitPriorityQueueServiceLoopThread") {}
Thread::ReturnStatus shutdown();
int chanType[8];
protected:
virtual void runThread();
/* 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 {
public:
/** Transceiver constructor
@param wBasePort base port number of UDP sockets
@param TRXAddress IP address of the TRX manager, as a string
@param wSPS number of samples per GSM symbol
@param wTransmitLatency initial setting of transmit latency
@param radioInterface associated radioInterface object
*/
Transceiver(int wBasePort,
const char *TRXAddress,
size_t tx_sps, size_t rx_sps, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface,
double wRssiOffset);
/** Destructor */
~Transceiver();
/** Start the control loop */
bool init(int filler, size_t rtsc, unsigned rach_delay);
/** attach the radioInterface receive FIFO */
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
{
if (chan >= mReceiveFIFO.size())
return false;
mReceiveFIFO[chan] = wFIFO;
return true;
}
/** accessor for number of channels */
size_t numChans() const { return mChans; };
/** Codes for channel combinations */
typedef enum {
FILL, ///< Channel is transmitted, but unused
I, ///< TCH/FS
II, ///< TCH/HS, idle every other slot
III, ///< TCH/HS
IV, ///< FCCH+SCH+CCCH+BCCH, uplink RACH
V, ///< FCCH+SCH+CCCH+BCCH+SDCCH/4+SACCH/4, uplink RACH+SDCCH/4
VI, ///< CCCH+BCCH, uplink RACH
VII, ///< SDCCH/8 + SACCH/8
VIII, ///< TCH/F + FACCH/F + SACCH/M
IX, ///< TCH/F + SACCH/M
X, ///< TCH/FD + SACCH/MD
XI, ///< PBCCH+PCCCH+PDTCH+PACCH+PTCCH
XII, ///< PCCCH+PDTCH+PACCH+PTCCH
XIII, ///< PDTCH+PACCH+PTCCH
NONE, ///< Channel is inactive, default
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
EDGE, ///< timeslot should contain an EDGE burst
IDLE ///< timeslot is an idle (or dummy) burst
} CorrType;
enum FillerType {
FILLER_DUMMY,
FILLER_ZERO,
FILLER_NORM_RAND,
FILLER_EDGE_RAND,
FILLER_ACCESS_RAND,
};
private:
DriveLoop *mDriveLoop;
int mBasePort;
std::string mAddr;
int mBasePort; ///< Base port address for all our ports
std::string mTRXAddress; ///< Address of the BTS TRX control interface
UDPSocket mDataSocket; ///< socket for writing to/reading from GSM core
UDPSocket mControlSocket; ///< socket for writing/reading control commands from GSM core
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
VectorQueue *mTransmitPriorityQueue; ///< priority queue of transmit bursts received from GSM core
VectorFIFO* mReceiveFIFO; ///< radioInterface FIFO of receive bursts
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
friend class FIFOServiceLoopThread;
FIFOServiceLoopThread mFIFOServiceLoop; ///< thread to push/pull bursts into transmit/receive FIFO
friend class ControlServiceLoopThread;
ControlServiceLoopThread mControlServiceLoop; ///< thread to process control messages from GSM core
friend class TransmitPriorityQueueServiceLoopThread;
TransmitPriorityQueueServiceLoopThread mTransmitPriorityQueueServiceLoop;///< thread to process transmit bursts from GSM core
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
int mChannel; ///< channelizer attach number between 0 and 'M-1'
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
/** unmodulate a modulated burst */
#ifdef TRANSMIT_LOGGING
void unModulateVector(signalVector wVector);
#endif
double rssiOffset; ///< RSSI to dBm conversion offset
/** modulate and add a burst to the transmit queue */
void addRadioVector(BitVector &burst,
int RSSI,
GSM::Time &wTime);
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);
/** Pull and demodulate a burst from the receive FIFO */
SoftVector *pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
double &timingOffset, double &noise,
size_t chan = 0);
/** Set modulus for specific timeslot */
void setModulus(size_t timeslot, size_t chan);
/** return the expected burst type for the specified timestamp */
CorrType expectedCorrType(GSM::Time currTime, size_t chan);
/** send messages over the clock socket */
void writeClockInterface(void);
void pullFIFO(void); ///< blocking call on receive FIFO
/** Detectbursts */
int detectBurst(signalVector &burst,
complex &amp, float &toa, CorrType type);
signalVector *gsmPulse; ///< the GSM shaping pulse for modulation
/** Demodulate burst and output soft bits */
SoftVector *demodulate(signalVector &burst,
complex amp, float toa, CorrType type);
int mSamplesPerSymbol; ///< number of samples per GSM symbol
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 mRunning; ///< flag to indicate control loop is running
bool mPrimary; ///< flag to indicate C0 channel
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
double mFreqOffset; ///< RF frequency offset
int mPower; ///< the transmit power in dB
double mEnergyThreshold; ///< threshold to determine if received data is potentially a GSM burst
GSM::Time prevFalseDetectionTime; ///< last timestamp of a false energy detection
unsigned mMaxExpectedDelay; ///< maximum expected time-of-arrival offset in GSM symbols
unsigned mTSC; ///< the midamble sequence code
unsigned mMaxExpectedDelayAB; ///< maximum expected time-of-arrival offset in GSM symbols for Access Bursts (RACH)
unsigned mMaxExpectedDelayNB; ///< maximum expected time-of-arrival offset in GSM symbols for Normal Bursts
unsigned mWriteBurstToDiskMask; ///< debug: bitmask to indicate which timeslots to dump to disk
GSM::Time channelEstimateTime[8]; ///< last timestamp of each timeslot's channel estimate
signalVector *channelResponse[8]; ///< most recent channel estimate of all timeslots
float SNRestimate[8]; ///< most recent SNR estimate of all timeslots
signalVector *DFEForward[8]; ///< most recent DFE feedforward filter of all timeslots
signalVector *DFEFeedback[8]; ///< most recent DFE feedback filter of all timeslots
float chanRespOffset[8]; ///< most recent timing offset, e.g. TOA, of all timeslots
complex chanRespAmplitude[8]; ///< most recent channel amplitude of all timeslots
std::vector<TransceiverState> mStates;
static int mTSC; ///< the midamble sequence code
/** Start and stop I/O threads through the control socket API */
bool start();
void stop();
public:
/** Transceiver constructor
@param wBasePort base port number of UDP sockets
@param TRXAddress IP address of the TRX manager, as a string
@param wSamplesPerSymbol number of samples per GSM symbol
@param wTransmitLatency initial setting of transmit latency
@param radioInterface associated radioInterface object
*/
Transceiver(int wBasePort, const char *TRXAddress,
DriveLoop *wDriveLoop, RadioInterface *wRadioInterface,
int wSamplesPerSymbol = SAMPSPERSYM,
int wChannel = 0, bool wPrimary = true);
/** Destructor */
~Transceiver();
/** start the Transceiver */
void start();
/** shutdown (teardown threads) the Transceiver */
void shutdown();
/** Protect destructor accessable stop call */
Mutex mLock;
protected:
/** drive lower receive I/O and burst generation */
void driveReceiveRadio();
/** drive reception and demodulation of GSM bursts */
void driveReceiveFIFO();
/** drive demodulation of GSM bursts */
void driveReceiveFIFO(size_t chan);
/** drive transmission of GSM bursts */
void driveTransmitFIFO();
void driveTxFIFO();
/** drive handling of control messages from GSM core */
void driveControl();
void driveControl(size_t chan);
/**
drive modulation and sorting of GSM bursts from GSM core
@return true if a burst was transferred successfully
*/
bool driveTransmitPriorityQueue();
bool driveTxPriorityQueue(size_t chan);
friend void *RxUpperLoopAdapter(TransceiverChannel *);
friend void *TxUpperLoopAdapter(TransceiverChannel *);
friend void *RxLowerLoopAdapter(Transceiver *);
friend void *TxLowerLoopAdapter(Transceiver *);
friend void *ControlServiceLoopAdapter(TransceiverChannel *);
void reset();
/** return transceiver on/off status */
bool on() { return mOn; }
/** return control loop operational status */
bool running() { return mRunning; }
/** return the drive loop pointer */
DriveLoop *getDriveLoop() { return mDriveLoop; }
/** set priority on current thread */
void setPriority() { mRadioInterface->setPriority(); }
void setPriority(float prio = 0.5) { mRadioInterface->setPriority(prio); }
void logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm,
double rssi, double noise, double toa);
};
void *RxUpperLoopAdapter(TransceiverChannel *);
/** Main drive threads */
void *RxLowerLoopAdapter(Transceiver *);
void *TxLowerLoopAdapter(Transceiver *);
/** control message handler thread loop */
void *ControlServiceLoopAdapter(TransceiverChannel *);
/** transmit queueing thread loop */
void *TxUpperLoopAdapter(TransceiverChannel *);

File diff suppressed because it is too large Load Diff

View File

@@ -59,14 +59,28 @@ const dboardConfigType dboardConfig = TXA_RXB;
const double USRPDevice::masterClockRate = 52.0e6;
USRPDevice::USRPDevice(int sps, bool skipRx)
: skipRx(skipRx)
USRPDevice::USRPDevice(size_t sps)
{
LOG(INFO) << "creating USRP device...";
this->sps = sps;
decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) sps));
actualSampleRate = masterClockRate/decimRate;
rxGain = 0;
/*
* Undetermined delay b/w ping response timestamp and true
* receive timestamp. Values are empirically measured. With
* split sample rate Tx/Rx - 4/1 sps we need to need to
* compensate for advance rather than delay.
*/
if (sps == 1)
pingOffset = 272;
else if (sps == 4)
pingOffset = 269 - 7500;
else
pingOffset = 0;
#ifdef SWLOOPBACK
samplePeriod = 1.0e6/actualSampleRate;
loopbackBufferSize = 0;
@@ -75,7 +89,7 @@ USRPDevice::USRPDevice(int sps, bool skipRx)
#endif
}
int USRPDevice::open(const std::string &)
int USRPDevice::open(const std::string &, bool, bool)
{
writeLock.unlock();
@@ -86,14 +100,13 @@ int USRPDevice::open(const std::string &)
m_uRx.reset();
if (!skipRx) {
try {
m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make(0,decimRate,1,-1,
usrp_standard_rx::FPGA_MODE_NORMAL,
1024,16*8,rbf));
#ifdef HAVE_LIBUSRP_3_2
m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make(
0, decimRate * sps, 1, -1,
usrp_standard_rx::FPGA_MODE_NORMAL,
1024, 16 * 8, rbf));
m_uRx->set_fpga_master_clock_freq(masterClockRate);
#endif
}
catch(...) {
LOG(ALERT) << "make failed on Rx";
m_uRx.reset();
@@ -110,13 +123,12 @@ int USRPDevice::open(const std::string &)
}
try {
m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make(0,decimRate*2,1,-1,
1024,16*8,rbf));
#ifdef HAVE_LIBUSRP_3_2
m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make(
0, decimRate * 2, 1, -1,
1024, 16 * 8, rbf));
m_uTx->set_fpga_master_clock_freq(masterClockRate);
#endif
}
catch(...) {
LOG(ALERT) << "make failed on Tx";
m_uTx.reset();
@@ -253,49 +265,66 @@ double USRPDevice::minRxGain()
return m_dbRx->gain_min();
}
double USRPDevice::setTxGain(double dB) {
writeLock.lock();
if (dB > maxTxGain()) dB = maxTxGain();
if (dB < minTxGain()) dB = minTxGain();
double USRPDevice::setTxGain(double dB, size_t chan)
{
if (chan) {
LOG(ALERT) << "Invalid channel " << chan;
return 0.0;
}
LOG(NOTICE) << "Setting TX gain to " << dB << " dB.";
writeLock.lock();
if (dB > maxTxGain())
dB = maxTxGain();
if (dB < minTxGain())
dB = minTxGain();
if (!m_dbTx->set_gain(dB))
LOG(ERR) << "Error setting TX gain";
LOG(NOTICE) << "Setting TX gain to " << dB << " dB.";
writeLock.unlock();
return dB;
if (!m_dbTx->set_gain(dB))
LOG(ERR) << "Error setting TX gain";
writeLock.unlock();
return dB;
}
double USRPDevice::setRxGain(double dB) {
double USRPDevice::setRxGain(double dB, size_t chan)
{
if (chan) {
LOG(ALERT) << "Invalid channel " << chan;
return 0.0;
}
writeLock.lock();
if (dB > maxRxGain()) dB = maxRxGain();
if (dB < minRxGain()) dB = minRxGain();
LOG(NOTICE) << "Setting RX gain to " << dB << " dB.";
dB = 47.0;
if (!m_dbRx->set_gain(dB))
LOG(ERR) << "Error setting RX gain";
writeLock.unlock();
return dB;
writeLock.lock();
if (dB > maxRxGain())
dB = maxRxGain();
if (dB < minRxGain())
dB = minRxGain();
LOG(NOTICE) << "Setting RX gain to " << dB << " dB.";
if (!m_dbRx->set_gain(dB))
LOG(ERR) << "Error setting RX gain";
writeLock.unlock();
return dB;
}
// NOTE: Assumes sequential reads
int USRPDevice::readSamples(short *buf, int len, bool *overrun,
TIMESTAMP timestamp,
bool *underrun,
unsigned *RSSI)
int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
{
#ifndef SWLOOPBACK
if (!m_uRx) return 0;
if (!m_uRx)
return 0;
short *buf = bufs[0];
timestamp += timestampOffset;
if (timestamp + len < timeStart) {
@@ -341,7 +370,7 @@ int USRPDevice::readSamples(short *buf, int len, bool *overrun,
uint32_t word2 = usrp_to_host_u32(tmpBuf[2]);
if ((word2 >> 16) == ((0x01 << 8) | 0x02)) {
timestamp -= timestampOffset;
timestampOffset = pktTimestamp - pingTimestamp + PINGOFFSET;
timestampOffset = pktTimestamp - pingTimestamp + pingOffset;
LOG(DEBUG) << "updating timestamp offset to: " << timestampOffset;
timestamp += timestampOffset;
isAligned = true;
@@ -437,17 +466,20 @@ int USRPDevice::readSamples(short *buf, int len, bool *overrun,
#endif
}
int USRPDevice::writeSamples(short *buf, int len, bool *underrun,
unsigned long long timestamp,
bool isControl)
int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
bool *underrun, unsigned long long timestamp,
bool isControl)
{
writeLock.lock();
#ifndef SWLOOPBACK
if (!m_uTx) return 0;
if (!m_uTx)
return 0;
short *buf = bufs[0];
static uint32_t outData[128*20];
for (int i = 0; i < len*2; i++) {
buf[i] = host_to_usrp_short(buf[i]);
}
@@ -499,7 +531,9 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
uint32_t *wordPtr = (uint32_t *) data;
*wordPtr = host_to_usrp_u32(*wordPtr);
bool tmpUnderrun;
if (writeSamples((short *) data,1,&tmpUnderrun,timestamp & 0x0ffffffffll,true)) {
std::vector<short *> buf(1, data);
if (writeSamples(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) {
pingTimestamp = timestamp;
return true;
}
@@ -510,10 +544,15 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
}
#ifndef SWLOOPBACK
bool USRPDevice::setTxFreq(double wFreq)
bool USRPDevice::setTxFreq(double wFreq, size_t chan)
{
usrp_tune_result result;
if (chan) {
LOG(ALERT) << "Invalid channel " << chan;
return false;
}
if (m_uTx->tune(txSubdevSpec.side, m_dbTx, wFreq, &result)) {
LOG(INFO) << "set TX: " << wFreq << std::endl
<< " baseband freq: " << result.baseband_freq << std::endl
@@ -530,10 +569,15 @@ bool USRPDevice::setTxFreq(double wFreq)
}
}
bool USRPDevice::setRxFreq(double wFreq)
bool USRPDevice::setRxFreq(double wFreq, size_t chan)
{
usrp_tune_result result;
if (chan) {
LOG(ALERT) << "Invalid channel " << chan;
return false;
}
if (m_uRx->tune(0, m_dbRx, wFreq, &result)) {
LOG(INFO) << "set RX: " << wFreq << std::endl
<< " baseband freq: " << result.baseband_freq << std::endl
@@ -556,7 +600,8 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;};
bool USRPDevice::setRxFreq(double wFreq) { return true;};
#endif
RadioDevice *RadioDevice::make(int sps, bool skipRx)
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
size_t chans, bool diversity, double)
{
return new USRPDevice(sps, skipRx);
return new USRPDevice(tx_sps);
}

View File

@@ -21,29 +21,17 @@
#include "radioDevice.h"
#ifdef HAVE_LIBUSRP_3_3 // [
# include <usrp/usrp_standard.h>
# include <usrp/usrp_bytesex.h>
# include <usrp/usrp_prims.h>
#else // HAVE_LIBUSRP_3_3 ][
# include "usrp_standard.h"
# include "usrp_bytesex.h"
# include "usrp_prims.h"
#endif // !HAVE_LIBUSRP_3_3 ]
#include <usrp/usrp_standard.h>
#include <usrp/usrp_bytesex.h>
#include <usrp/usrp_prims.h>
#include <sys/time.h>
#include <math.h>
#include <string>
#include <iostream>
/** Define types which are not defined in libusrp-3.1 */
#ifndef HAVE_LIBUSRP_3_2
#include <boost/shared_ptr.hpp>
typedef boost::shared_ptr<usrp_standard_tx> usrp_standard_tx_sptr;
typedef boost::shared_ptr<usrp_standard_rx> usrp_standard_rx_sptr;
#endif // HAVE_LIBUSRP_3_2
/** A class to handle a USRP rev 4, with a two RFX900 daughterboards */
class USRPDevice: public RadioDevice {
@@ -60,6 +48,7 @@ private:
usrp_subdev_spec rxSubdevSpec;
usrp_subdev_spec txSubdevSpec;
int sps;
double actualSampleRate; ///< the actual USRP sampling rate
unsigned int decimRate; ///< the USRP decimation rate
@@ -87,7 +76,8 @@ private:
TIMESTAMP timestampOffset; ///< timestamp offset b/w Tx and Rx blocks
TIMESTAMP latestWriteTimestamp; ///< timestamp of most recent ping command
TIMESTAMP pingTimestamp; ///< timestamp of most recent ping response
static const TIMESTAMP PINGOFFSET = 272; ///< undetermined delay b/w ping response timestamp and true receive timestamp
long long pingOffset;
unsigned long hi32Timestamp;
unsigned long lastPktTimestamp;
@@ -103,19 +93,13 @@ private:
bool firstRead;
#endif
/** Set the transmission frequency */
bool tx_setFreq(double freq, double *actual_freq);
/** Set the receiver frequency */
bool rx_setFreq(double freq, double *actual_freq);
public:
/** Object constructor */
USRPDevice(int sps, bool skipRx);
USRPDevice(size_t sps);
/** Instantiate the USRP */
int open(const std::string &);
int open(const std::string &, bool, bool);
/** Start the USRP */
bool start();
@@ -124,7 +108,7 @@ private:
bool stop();
/** Set priority not supported */
void setPriority() { return; }
void setPriority(float prio = 0.5) { }
enum TxWindowType getWindowType() { return TX_WINDOW_USRP1; }
@@ -138,10 +122,9 @@ private:
@param RSSI The received signal strength of the read result
@return The number of samples actually read
*/
int readSamples(short *buf, int len, bool *overrun,
TIMESTAMP timestamp = 0xffffffff,
bool *underrun = NULL,
unsigned *RSSI = NULL);
int readSamples(std::vector<short *> &buf, int len, bool *overrun,
TIMESTAMP timestamp = 0xffffffff, bool *underrun = NULL,
unsigned *RSSI = NULL);
/**
Write samples to the USRP.
@param buf Contains the data to be written.
@@ -151,18 +134,17 @@ private:
@param isControl Set if data is a control packet, e.g. a ping command
@return The number of samples actually written
*/
int writeSamples(short *buf, int len, bool *underrun,
TIMESTAMP timestamp = 0xffffffff,
bool isControl = false);
int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
TIMESTAMP timestamp = 0xffffffff, bool isControl = false);
/** Update the alignment between the read and write timestamps */
bool updateAlignment(TIMESTAMP timestamp);
/** Set the transmitter frequency */
bool setTxFreq(double wFreq);
bool setTxFreq(double wFreq, size_t chan = 0);
/** Set the receiver frequency */
bool setRxFreq(double wFreq);
bool setRxFreq(double wFreq, size_t chan = 0);
/** Returns the starting write Timestamp*/
TIMESTAMP initialWriteTimestamp(void) { return 20000;}
@@ -177,10 +159,10 @@ private:
double fullScaleOutputValue() {return 9450.0;}
/** sets the receive chan gain, returns the gain setting **/
double setRxGain(double dB);
double setRxGain(double dB, size_t chan = 0);
/** get the current receive gain */
double getRxGain(void) {return rxGain;}
double getRxGain(size_t chan = 0) { return rxGain; }
/** return maximum Rx Gain **/
double maxRxGain(void);
@@ -189,7 +171,7 @@ private:
double minRxGain(void);
/** sets the transmit chan gain, returns the gain setting **/
double setTxGain(double dB);
double setTxGain(double dB, size_t chan = 0);
/** return maximum Tx Gain **/
double maxTxGain(void);
@@ -197,13 +179,12 @@ private:
/** return minimum Rx Gain **/
double minTxGain(void);
/** Return internal status values */
inline double getTxFreq() { return 0;}
inline double getRxFreq() { return 0;}
inline double getSampleRate() {return actualSampleRate;}
inline double getTxFreq(size_t chan = 0) { return 0; }
inline double getRxFreq(size_t chan = 0) { return 0; }
inline double getSampleRate() { return actualSampleRate; }
inline double numberRead() { return samplesRead; }
inline double numberWritten() { return samplesWritten;}
inline double numberWritten() { return samplesWritten; }
};

View File

@@ -1,95 +0,0 @@
/*
* Copyright 2008, 2009 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <stdio.h>
#include <Logger.h>
#include <Configuration.h>
#include "radioDevice.h"
ConfigurationTable gConfig;
using namespace std;
int main(int argc, char *argv[]) {
// Configure logger.
if (argc>1) gLogInit(argv[1]);
else gLogInit("DEBUG");
//if (argc>2) gSetLogFile(argv[2]);
RadioDevice *usrp = RadioDevice::make(52.0e6/192.0, 1);
usrp->open("");
TIMESTAMP timestamp;
usrp->setTxFreq(825.4e6);
usrp->setRxFreq(825.4e6);
usrp->start();
usrp->setRxGain(57);
LOG(INFO) << "Looping...";
bool underrun;
short data[]={0x00,0x02};
usrp->updateAlignment(20000);
usrp->updateAlignment(21000);
int numpkts = 1;
short data2[512*2*numpkts];
for (int i = 0; i < 512*numpkts; i++) {
data2[i<<1] = 10000;//4096*cos(2*3.14159*(i % 126)/126);
data2[(i<<1) + 1] = 10000;//4096*sin(2*3.14159*(i % 126)/126);
}
for (int i = 0; i < 1; i++)
usrp->writeSamples((short*) data2,512*numpkts,&underrun,102000+i*1000);
timestamp = 19000;
double sum = 0.0;
unsigned long num = 0;
while (1) {
short readBuf[512*2];
int rd = usrp->readSamples(readBuf,512,&underrun,timestamp);
if (rd) {
LOG(INFO) << "rcvd. data@:" << timestamp;
for (int i = 0; i < 512; i++) {
uint32_t *wordPtr = (uint32_t *) &readBuf[2*i];
printf ("%llu: %d %d\n", timestamp+i,readBuf[2*i],readBuf[2*i+1]);
sum += (readBuf[2*i+1]*readBuf[2*i+1] + readBuf[2*i]*readBuf[2*i]);
num++;
//if (num % 10000 == 0) printf("avg pwr: %f\n",sum/num);
}
timestamp += rd;
//usrp->writeSamples((short*) data2,512*numpkts,&underrun,timestamp+1000);
}
}
}

View File

@@ -0,0 +1,23 @@
if ARCH_ARM
if ARCH_ARM_A15
ARCH_FLAGS = -mfpu=neon-vfpv4
else
ARCH_FLAGS = -mfpu=neon
endif
AM_CFLAGS = -Wall $(ARCH_FLAGS) -std=gnu99 -I../common
AM_CCASFLAGS = $(ARCH_FLAGS)
noinst_LTLIBRARIES = libarch.la
libarch_la_SOURCES = \
../common/convolve_base.c \
convert.c \
convert_neon.S \
convolve.c \
convolve_neon.S \
scale.c \
scale_neon.S \
mult.c \
mult_neon.S
endif

View File

@@ -0,0 +1,96 @@
/*
* NEON type conversions
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
#include <string.h>
#include "convert.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
void neon_convert_ps_si16_4n(short *, const float *, const float *, int);
void neon_convert_si16_ps_4n(float *, const short *, int);
#ifndef HAVE_NEON
static void convert_si16_ps(float *out, const short *in, int len)
{
for (int i = 0; i < len; i++)
out[i] = in[i];
}
static void convert_ps_si16(short *out, const float *in, float scale, int len)
{
for (int i = 0; i < len; i++)
out[i] = in[i] * scale;
}
#else
/* 4*N 16-bit signed integer conversion with remainder */
static void neon_convert_si16_ps(float *out,
const short *in,
int len)
{
int start = len / 4 * 4;
neon_convert_si16_ps_4n(out, in, len >> 2);
for (int i = 0; i < len % 4; i++)
out[start + i] = (float) in[start + i];
}
/* 4*N 16-bit signed integer conversion with remainder */
static void neon_convert_ps_si16(short *out,
const float *in,
const float *scale,
int len)
{
int start = len / 4 * 4;
neon_convert_ps_si16_4n(out, in, scale, len >> 2);
for (int i = 0; i < len % 4; i++)
out[start + i] = (short) (in[start + i] * (*scale));
}
#endif
void convert_float_short(short *out, const float *in, float scale, int len)
{
#ifdef HAVE_NEON
float q[4] = { scale, scale, scale, scale };
if (len % 4)
neon_convert_ps_si16(out, in, q, len);
else
neon_convert_ps_si16_4n(out, in, q, len >> 2);
#else
convert_ps_si16(out, in, scale, len);
#endif
}
void convert_short_float(float *out, const short *in, int len)
{
#ifdef HAVE_NEON
if (len % 4)
neon_convert_si16_ps(out, in, len);
else
neon_convert_si16_ps_4n(out, in, len >> 2);
#else
convert_si16_ps(out, in, len);
#endif
}

View File

@@ -0,0 +1,51 @@
/*
* NEON type conversions
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
.syntax unified
.text
.align 2
.global neon_convert_ps_si16_4n
.type neon_convert_ps_si16_4n, %function
neon_convert_ps_si16_4n:
vld1.32 {q1}, [r2]
.loop_fltint:
vld1.64 {d0-d1}, [r1]!
vmul.f32 q0, q1
vcvt.s32.f32 q2, q0
vqmovn.s32 d0, q2
vst1.64 {d0}, [r0]!
subs r3, #1
bne .loop_fltint
bx lr
.size neon_convert_ps_si16_4n, .-neon_convert_ps_si16_4n
.text
.align 2
.global neon_convert_si16_ps_4n
.type neon_convert_si16_ps_4n, %function
neon_convert_si16_ps_4n:
.loop_intflt:
vld1.64 {d0}, [r1]!
vmovl.s16 q1, d0
vcvt.f32.s32 q0, q1
vst1.64 {q0}, [r0]!
subs r2, #1
bne .loop_intflt
bx lr
.size neon_convert_si16_ps_4n, .-neon_convert_si16_ps_4n
.section .note.GNU-stack,"",%progbits

View File

@@ -0,0 +1,139 @@
/*
* NEON Convolution
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
#include <string.h>
#include <stdio.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/* Forward declarations from base implementation */
int _base_convolve_real(float *x, int x_len,
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,
float *y, int y_len,
int start, int len,
int step, int offset);
int bounds_check(int x_len, int h_len, int y_len,
int start, int len, int step);
#ifdef HAVE_NEON
/* Calls into NEON assembler */
void neon_conv_real4(float *x, float *h, float *y, int len);
void neon_conv_real8(float *x, float *h, float *y, int len);
void neon_conv_real12(float *x, float *h, float *y, int len);
void neon_conv_real16(float *x, float *h, float *y, int len);
void neon_conv_real20(float *x, float *h, float *y, int len);
void mac_cx_neon4(float *x, float *h, float *y, int len);
/* Complex-complex convolution */
static void neon_conv_cmplx_4n(float *x, float *h, float *y, int h_len, int len)
{
for (int i = 0; i < len; i++)
mac_cx_neon4(&x[2 * i], h, &y[2 * i], h_len >> 2);
}
#endif
/* API: Aligned complex-real */
int convolve_real(float *x, int x_len,
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;
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
return -1;
memset(y, 0, len * 2 * sizeof(float));
#ifdef HAVE_NEON
if (step <= 4) {
switch (h_len) {
case 4:
conv_func = neon_conv_real4;
break;
case 8:
conv_func = neon_conv_real8;
break;
case 12:
conv_func = neon_conv_real12;
break;
case 16:
conv_func = neon_conv_real16;
break;
case 20:
conv_func = neon_conv_real20;
break;
}
}
#endif
if (conv_func) {
conv_func(&x[2 * (-(h_len - 1) + start)],
h, y, len);
} else {
_base_convolve_real(x, x_len,
h, h_len,
y, y_len,
start, len, step, offset);
}
return len;
}
/* API: Aligned complex-complex */
int convolve_complex(float *x, int x_len,
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;
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
return -1;
memset(y, 0, len * 2 * sizeof(float));
#ifdef HAVE_NEON
if (step <= 4 && !(h_len % 4))
conv_func = neon_conv_cmplx_4n;
#endif
if (conv_func) {
conv_func(&x[2 * (-(h_len - 1) + start)],
h, y, h_len, len);
} else {
_base_convolve_complex(x, x_len,
h, h_len,
y, y_len,
start, len, step, offset);
}
return len;
}

View File

@@ -0,0 +1,277 @@
/*
* NEON Convolution
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
.syntax unified
.text
.align 2
.global neon_conv_real4
.type neon_conv_real4, %function
neon_conv_real4:
push {r4, lr}
vpush {q4-q7}
vld2.32 {q0-q1}, [r1]
ldr r4, =8
.neon_conv_loop4:
vld2.32 {q2-q3}, [r0], r4
vmul.f32 q4, q2, q0
vmul.f32 q5, q3, q0
vpadd.f32 d12, d8, d9
vpadd.f32 d13, d10, d11
vpadd.f32 d14, d12, d13
vst1.64 {d14}, [r2]!
subs r3, r3, #1
bne .neon_conv_loop4
vpop {q4-q7}
pop {r4, pc}
.size neon_conv_real4, .-neon_conv_real4
.align 2
.p2align 4,,15
.global neon_conv_real8
.type neon_conv_real8, %function
neon_conv_real8:
push {r4-r5, lr}
vpush {q4-q7}
vld2.32 {q0-q1}, [r1]!
vld2.32 {q2-q3}, [r1]
add r4, r0, #32
ldr r5, =8
.neon_conv_loop8:
vld2.32 {q4-q5}, [r0], r5
vld2.32 {q6-q7}, [r4], r5
vmul.f32 q8, q4, q0
vmul.f32 q9, q5, q0
vmul.f32 q10, q6, q2
vmul.f32 q11, q7, q2
vadd.f32 q12, q8, q10
vadd.f32 q13, q9, q11
vpadd.f32 d22, d24, d25
vpadd.f32 d23, d26, d27
vpadd.f32 d24, d22, d23
vst1.64 {d24}, [r2]!
subs r3, r3, #1
bne .neon_conv_loop8
vpop {q4-q7}
pop {r4-r5, pc}
.size neon_conv_real8, .-neon_conv_real8
.align 2
.global neon_conv_real12
.type neon_conv_real12, %function
neon_conv_real12:
push {r4-r6, lr}
vpush {q4-q7}
vld2.32 {q0-q1}, [r1]!
vld2.32 {q2-q3}, [r1]!
vld2.32 {q4-q5}, [r1]!
add r4, r0, #32
add r5, r0, #64
ldr r6, =8
.neon_conv_loop12:
vld2.32 {q6-q7}, [r0], r6
vld2.32 {q8-q9}, [r4], r6
vld2.32 {q10-q11}, [r5], r6
#ifdef HAVE_NEON_FMA
vfma.f32 q1, q6, q0
vfma.f32 q3, q7, q0
vfma.f32 q1, q8, q2
vfma.f32 q3, q9, q2
vfma.f32 q1, q10, q4
vfma.f32 q3, q11, q4
#else
vmul.f32 q12, q6, q0
vmul.f32 q13, q7, q0
vmul.f32 q14, q8, q2
vmul.f32 q15, q9, q2
vmul.f32 q1, q10, q4
vmul.f32 q3, q11, q4
vadd.f32 q5, q12, q14
vadd.f32 q6, q13, q15
vadd.f32 q1, q5, q1
vadd.f32 q3, q6, q3
#endif
vpadd.f32 d2, d2, d3
vpadd.f32 d3, d6, d7
vpadd.f32 d6, d2, d3
vst1.64 {d6}, [r2]!
subs r3, r3, #1
bne .neon_conv_loop12
vpop {q4-q7}
pop {r4-r6, pc}
.size neon_conv_real12, .-neon_conv_real12
.align 2
.global neon_conv_real16
.type neon_conv_real16, %function
neon_conv_real16:
push {r4-r7, lr}
vpush {q4-q7}
vld2.32 {q0-q1}, [r1]!
vld2.32 {q2-q3}, [r1]!
vld2.32 {q4-q5}, [r1]!
vld2.32 {q6-q7}, [r1]
add r4, r0, #32
add r5, r0, #64
add r6, r0, #96
ldr r7, =8
.neon_conv_loop16:
vld2.32 {q8-q9}, [r0], r7
vld2.32 {q10-q11}, [r4], r7
vld2.32 {q12-q13}, [r5], r7
vld2.32 {q14-q15}, [r6], r7
#ifdef HAVE_NEON_FMA
vmul.f32 q1, q8, q0
vmul.f32 q3, q9, q0
vfma.f32 q1, q10, q2
vfma.f32 q3, q11, q2
vfma.f32 q1, q12, q4
vfma.f32 q3, q13, q4
vfma.f32 q1, q14, q6
vfma.f32 q3, q15, q6
#else
vmul.f32 q1, q8, q0
vmul.f32 q3, q9, q0
vmul.f32 q5, q10, q2
vmul.f32 q7, q11, q2
vmul.f32 q8, q12, q4
vmul.f32 q9, q13, q4
vmul.f32 q10, q14, q6
vmul.f32 q11, q15, q6
vadd.f32 q1, q1, q5
vadd.f32 q3, q3, q7
vadd.f32 q5, q8, q10
vadd.f32 q7, q9, q11
vadd.f32 q1, q1, q5
vadd.f32 q3, q3, q7
#endif
vpadd.f32 d2, d2, d3
vpadd.f32 d3, d6, d7
vpadd.f32 d6, d2, d3
vst1.64 {d6}, [r2]!
subs r3, r3, #1
bne .neon_conv_loop16
vpop {q4-q7}
pop {r4-r7, pc}
.size neon_conv_real16, .-neon_conv_real16
.align 2
.global neon_conv_real20
.type neon_conv_real20, %function
neon_conv_real20:
push {r4-r8, lr}
vpush {q4-q7}
vld2.32 {q0-q1}, [r1]!
vld2.32 {q2-q3}, [r1]!
vld2.32 {q4-q5}, [r1]!
vld2.32 {q6-q7}, [r1]!
vld2.32 {q8-q9}, [r1]
add r4, r0, #32
add r5, r0, #64
add r6, r0, #96
add r7, r0, #128
ldr r8, =8
.neon_conv_loop20:
vld2.32 {q10-q11}, [r0], r8
vld2.32 {q12-q13}, [r4], r8
vld2.32 {q14-q15}, [r5], r8
#ifdef HAVE_NEON_FMA
vmul.f32 q1, q10, q0
vfma.f32 q1, q12, q2
vfma.f32 q1, q14, q4
vmul.f32 q3, q11, q0
vfma.f32 q3, q13, q2
vfma.f32 q3, q15, q4
vld2.32 {q12-q13}, [r6], r8
vld2.32 {q14-q15}, [r7], r8
vfma.f32 q1, q12, q6
vfma.f32 q3, q13, q6
vfma.f32 q1, q14, q8
vfma.f32 q3, q15, q8
#else
vmul.f32 q1, q10, q0
vmul.f32 q3, q12, q2
vmul.f32 q5, q14, q4
vmul.f32 q7, q11, q0
vmul.f32 q9, q13, q2
vmul.f32 q10, q15, q4
vadd.f32 q1, q1, q3
vadd.f32 q3, q7, q9
vadd.f32 q9, q1, q5
vadd.f32 q10, q3, q10
vld2.32 {q12-q13}, [r6], r8
vld2.32 {q14-q15}, [r7], r8
vmul.f32 q1, q12, q6
vmul.f32 q3, q13, q6
vmul.f32 q5, q14, q8
vmul.f32 q7, q15, q8
vadd.f32 q12, q1, q9
vadd.f32 q14, q3, q10
vadd.f32 q1, q12, q5
vadd.f32 q3, q14, q7
#endif
vpadd.f32 d2, d2, d3
vpadd.f32 d3, d6, d7
vpadd.f32 d6, d2, d3
vst1.64 {d6}, [r2]!
subs r3, r3, #1
bne .neon_conv_loop20
vpop {q4-q7}
pop {r4-r8, pc}
.size neon_conv_real20, .-neon_conv_real20
.align 2
.global mac_cx_neon4
.type mac_cx_neon4, %function
mac_cx_neon4:
push {r4, lr}
ldr r4, =32
veor q14, q14
veor q15, q15
.neon_conv_loop_mac4:
vld2.32 {q0-q1}, [r0], r4
vld2.32 {q2-q3}, [r1]!
vmul.f32 q10, q0, q2
vmul.f32 q11, q1, q3
vmul.f32 q12, q0, q3
vmul.f32 q13, q2, q1
vsub.f32 q8, q10, q11
vadd.f32 q9, q12, q13
vadd.f32 q14, q8
vadd.f32 q15, q9
subs r3, #1
bne .neon_conv_loop_mac4
vld1.64 d0, [r2]
vpadd.f32 d28, d28, d29
vpadd.f32 d30, d30, d31
vpadd.f32 d1, d28, d30
vadd.f32 d1, d0
vst1.64 d1, [r2]
pop {r4, pc}
.size mac_cx_neon4, .-mac_cx_neon4
.section .note.GNU-stack,"",%progbits

56
Transceiver52M/arm/mult.c Normal file
View File

@@ -0,0 +1,56 @@
/*
* NEON scaling
* Copyright (C) 2012,2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
#include <string.h>
#include <mult.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
void neon_cmplx_mul_4n(float *, float *, float *, int);
static void cmplx_mul_ps(float *out, float *a, float *b, int len)
{
float ai, aq, bi, bq;
for (int i = 0; i < len; i++) {
ai = a[2 * i + 0];
aq = a[2 * i + 1];
bi = b[2 * i + 0];
bq = b[2 * i + 1];
out[2 * i + 0] = ai * bi - aq * bq;
out[2 * i + 1] = ai * bq + aq * bi;
}
}
void mul_complex(float *out, float *a, float *b, int len)
{
#ifdef HAVE_NEON
if (len % 4)
cmplx_mul_ps(out, a, b, len);
else
neon_cmplx_mul_4n(out, a, b, len >> 2);
#else
cmplx_mul_ps(out, a, b, len);
#endif
}

View File

@@ -0,0 +1,42 @@
/*
* NEON complex multiplication
* Copyright (C) 2012,2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
.syntax unified
.text
.align 2
.global neon_cmplx_mul_4n
.type neon_cmplx_mul_4n, %function
neon_cmplx_mul_4n:
vpush {q4-q7}
.loop_mul:
vld2.32 {q0-q1}, [r1]!
vld2.32 {q2-q3}, [r2]!
vmul.f32 q4, q0, q2
vmul.f32 q5, q1, q3
vmul.f32 q6, q0, q3
vmul.f32 q7, q2, q1
vsub.f32 q8, q4, q5
vadd.f32 q9, q6, q7
vst2.32 {q8-q9}, [r0]!
subs r3, #1
bne .loop_mul
vpop {q4-q7}
bx lr
.size neon_cmplx_mul_4n, .-neon_cmplx_mul_4n
.section .note.GNU-stack,"",%progbits

View File

@@ -0,0 +1,56 @@
/*
* NEON scaling
* Copyright (C) 2012,2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
#include <string.h>
#include <scale.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
void neon_scale_4n(float *, float *, float *, int);
static void scale_ps(float *out, float *in, float *scale, int len)
{
float ai, aq, bi, bq;
bi = scale[0];
bq = scale[1];
for (int i = 0; i < len; i++) {
ai = in[2 * i + 0];
aq = in[2 * i + 1];
out[2 * i + 0] = ai * bi - aq * bq;
out[2 * i + 1] = ai * bq + aq * bi;
}
}
void scale_complex(float *out, float *in, float* scale, int len)
{
#ifdef HAVE_NEON
if (len % 4)
scale_ps(out, in, scale, len);
else
neon_scale_4n(in, scale, out, len >> 2);
#else
scale_ps(out, in, scale, len);
#endif
}

View File

@@ -0,0 +1,50 @@
/*
* ARM NEON Scaling
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
.syntax unified
.text
.align 2
.global neon_scale_4n
.type neon_scale_4n, %function
neon_scale_4n:
push {r4, lr}
ldr r4, =32
vld1.64 d0, [r1]
vmov.32 s4, s1
vmov.32 s1, s0
vmov.64 d1, d0
vmov.32 s5, s4
vmov.64 d3, d2
.loop_mul_const:
vld2.32 {q2-q3}, [r0], r4
vmul.f32 q8, q0, q2
vmul.f32 q9, q1, q3
vmul.f32 q10, q0, q3
vmul.f32 q11, q1, q2
vsub.f32 q8, q8, q9
vadd.f32 q9, q10, q11
vst2.32 {q8-q9}, [r2]!
subs r3, #1
bne .loop_mul_const
pop {r4, pc}
.size neon_scale_4n, .-neon_scale_4n
.section .note.GNU-stack,"",%progbits

View File

@@ -0,0 +1,7 @@
#ifndef _CONVERT_H_
#define _CONVERT_H_
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

@@ -0,0 +1,30 @@
#ifndef _CONVOLVE_H_
#define _CONVOLVE_H_
void *convolve_h_alloc(int num);
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(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(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(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);
#endif /* _CONVOLVE_H_ */

View File

@@ -0,0 +1,156 @@
/*
* Convolution
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
#include <string.h>
#include <stdio.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/* Base multiply and accumulate complex-real */
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(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(const float *x, const float *h, float *y,
int len, int step, int offset)
{
for (int i = offset; i < len; i += step)
mac_real(&x[2 * i], &h[2 * i], y);
}
/* Base vector complex-complex multiply and accumulate */
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)
mac_cmplx(&x[2 * i], &h[2 * i], y);
}
/* Base complex-real convolution */
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)
{
for (int i = 0; i < len; i++) {
mac_real_vec_n(&x[2 * (i - (h_len - 1) + start)],
h,
&y[2 * i], h_len,
step, offset);
}
return len;
}
/* Base complex-complex convolution */
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)
{
for (int i = 0; i < len; i++) {
mac_cmplx_vec_n(&x[2 * (i - (h_len - 1) + start)],
h,
&y[2 * i],
h_len, step, offset);
}
return len;
}
/* Buffer validity checks */
int bounds_check(int x_len, int h_len, int y_len,
int start, int len, int step)
{
if ((x_len < 1) || (h_len < 1) ||
(y_len < 1) || (len < 1) || (step < 1)) {
fprintf(stderr, "Convolve: Invalid input\n");
return -1;
}
if ((start + len > x_len) || (len > y_len) || (x_len < h_len)) {
fprintf(stderr, "Convolve: Boundary exception\n");
fprintf(stderr, "start: %i, len: %i, x: %i, h: %i, y: %i\n",
start, len, x_len, h_len, y_len);
return -1;
}
return 0;
}
/* API: Non-aligned (no SSE) complex-real */
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)
{
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
return -1;
memset(y, 0, len * 2 * sizeof(float));
return _base_convolve_real(x, x_len,
h, h_len,
y, y_len,
start, len, step, offset);
}
/* API: Non-aligned (no SSE) complex-complex */
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)
{
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
return -1;
memset(y, 0, len * 2 * sizeof(float));
return _base_convolve_complex(x, x_len,
h, h_len,
y, y_len,
start, len, step, offset);
}
/* Aligned filter tap allocation */
void *convolve_h_alloc(int len)
{
#ifdef HAVE_SSE3
return memalign(16, len * 2 * sizeof(float));
#else
return malloc(len * 2 * sizeof(float));
#endif
}

View File

@@ -0,0 +1,6 @@
#ifndef _MULT_H_
#define _MULT_H_
void mul_complex(float *out, float *a, float *b, int len);
#endif /* _MULT_H_ */

View File

@@ -0,0 +1,6 @@
#ifndef _SCALE_H_
#define _SCALE_H_
void scale_complex(float *out, float *in, float *scale, int len);
#endif /* _SCALE_H_ */

83
Transceiver52M/laurent.m Normal file
View File

@@ -0,0 +1,83 @@
%
% Laurent decomposition of GMSK signals
% Generates C0, C1, and C2 pulse shapes
%
% Pierre Laurent, "Exact and Approximate Construction of Digital Phase
% Modulations by Superposition of Amplitude Modulated Pulses", IEEE
% Transactions of Communications, Vol. 34, No. 2, Feb 1986.
%
% Author: Thomas Tsou <tom@tsou.cc>
%
% Modulation parameters
oversamp = 16;
L = 3;
f = 270.83333e3;
T = 1/f;
h = 0.5;
BT = 0.30;
B = BT / T;
% Generate sampling points for L symbol periods
t = -(L*T/2):T/oversamp:(L*T/2);
t = t(1:end-1) + (T/oversamp/2);
% Generate Gaussian pulse
g = qfunc(2*pi*B*(t - T/2)/(log(2)^.5)) - qfunc(2*pi*B*(t + T/2)/(log(2)^.5));
g = g / sum(g) * pi/2;
g = [0 g];
% Integrate phase
q = 0;
for i = 1:size(g,2);
q(i) = sum(g(1:i));
end
% Compute two sided "generalized phase pulse" function
s = 0;
for i = 1:size(g,2);
s(i) = sin(q(i)) / sin(pi*h);
end
for i = (size(g,2) + 1):(2 * size(g,2) - 1);
s(i) = sin(pi*h - q(i - (size(g,2) - 1))) / sin(pi*h);
end
% Compute C0 pulse: valid for all L values
c0 = s(1:end-(oversamp*(L-1)));
for i = 1:L-1;
c0 = c0 .* s((1 + i*oversamp):end-(oversamp*(L - 1 - i)));
end
% Compute C1 pulse: valid for L = 3 only!
% C1 = S0 * S4 * S2
c1 = s(1:end-(oversamp*(4)));
c1 = c1 .* s((1 + 4*oversamp):end-(oversamp*(4 - 1 - 3)));
c1 = c1 .* s((1 + 2*oversamp):end-(oversamp*(4 - 1 - 1)));
% Compute C2 pulse: valid for L = 3 only!
% C2 = S0 * S1 * S5
c2 = s(1:end-(oversamp*(5)));
c2 = c2 .* s((1 + 1*oversamp):end-(oversamp*(5 - 1 - 0)));
c2 = c2 .* s((1 + 5*oversamp):end-(oversamp*(5 - 1 - 4)));
% Plot C0, C1, C2 Laurent pulse series
figure(1);
hold off;
plot((0:size(c0,2)-1)/oversamp - 2,c0, 'b');
hold on;
plot((0:size(c1,2)-1)/oversamp - 2,c1, 'r');
plot((0:size(c2,2)-1)/oversamp - 2,c2, 'g');
% Generate OpenBTS pulse
numSamples = size(c0,2);
centerPoint = (numSamples - 1)/2;
i = ((0:numSamples) - centerPoint) / oversamp;
xP = .96*exp(-1.1380*i.^2 - 0.527*i.^4);
xP = xP / max(xP) * max(c0);
% Plot C0 pulse compared to OpenBTS pulse
figure(2);
hold off;
plot((0:size(c0,2)-1)/oversamp, c0, 'b');
hold on;
plot((0:size(xP,2)-1)/oversamp, xP, 'r');

View File

@@ -1,120 +0,0 @@
/*
* Copyright 2012 Thomas Tsou <ttsou@vt.edu>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* See the COPYING file in the main directory for details.
*/
#include <time.h>
#include <signal.h>
#include <GSMCommon.h>
#include <Logger.h>
#include <Configuration.h>
#include "Transceiver.h"
#include "radioDevice.h"
ConfigurationTable gConfig("/etc/OpenBTS/OpenBTS.db");
volatile bool gbShutdown = false;
int Transceiver::mTSC = -1;
static void sigHandler(int signum)
{
LOG(NOTICE) << "Received shutdown signal";
gbShutdown = true;
}
static int setupSignals()
{
struct sigaction action;
action.sa_handler = sigHandler;
sigemptyset(&action.sa_mask);
action.sa_flags = 0;
if (sigaction(SIGINT, &action, NULL) < 0)
return -1;
if (sigaction(SIGTERM, &action, NULL) < 0)
return -1;
return 0;
}
int main(int argc, char *argv[])
{
int numARFCN = 1;
if (argc > 1) {
numARFCN = atoi(argv[1]);
if (numARFCN > CHAN_MAX) {
LOG(ALERT) << numARFCN << " channels not supported "
<< " with with current build";
exit(-1);
}
}
gLogInit("transceiver", gConfig.getStr("Log.Level").c_str(), LOG_LOCAL7);
srandom(time(NULL));
if (setupSignals() < 0) {
LOG(ERR) << "Failed to setup signal handlers, exiting...";
exit(-1);
}
RadioDevice *device = RadioDevice::make(SAMPSPERSYM);
int radioType = device->open("");
if (radioType < 0) {
LOG(ALERT) << "Failed to open device, exiting...";
return EXIT_FAILURE;
}
RadioInterface *radio;
switch (radioType) {
case RadioDevice::NORMAL:
radio = new RadioInterface(device, numARFCN);
break;
case RadioDevice::RESAMP:
default:
LOG(ALERT) << "Unsupported configuration";
return EXIT_FAILURE;
}
DriveLoop *drive = new DriveLoop(5700, "127.0.0.1", radio, numARFCN, 0);
Transceiver *trx[CHAN_MAX];
bool primary = true;
for (int i = 0; i < numARFCN; i++) {
trx[i] = new Transceiver(5700 + 2 * i, "127.0.0.1",
drive, radio, SAMPSPERSYM,
i, primary);
trx[i]->start();
primary = false;
}
while (!gbShutdown)
sleep(1);
LOG(NOTICE) << "Shutting down transceivers...";
for (int i = 0; i < numARFCN; i++)
trx[i]->shutdown();
for (int i = 0; i < numARFCN; i++)
delete trx[i];
delete drive;
delete radio;
delete device;
}

495
Transceiver52M/osmo-trx.cpp Normal file
View File

@@ -0,0 +1,495 @@
/*
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "Transceiver.h"
#include "radioDevice.h"
#include <time.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
#include <GSMCommon.h>
#include <Logger.h>
#include <Configuration.h>
/* Samples-per-symbol for downlink path
* 4 - Uses precision modulator (more computation, less distortion)
* 1 - Uses minimized modulator (less computation, more distortion)
*
* Other values are invalid. Receive path (uplink) is always
* downsampled to 1 sps. Default to 4 sps for all cases except for
* ARM and non-SIMD enabled architectures.
*/
#if defined(HAVE_NEON) || !defined(HAVE_SSE3)
#define DEFAULT_TX_SPS 1
#else
#define DEFAULT_TX_SPS 4
#endif
/*
* Samples-per-symbol for uplink (receiver) path
* Do not modify this value. EDGE configures 4 sps automatically on
* B200/B210 devices only. Use of 4 sps on the receive path for other
* configurations is not supported.
*/
#define DEFAULT_RX_SPS 1
/* Default configuration parameters
* Note that these values are only used if the particular key does not
* exist in the configuration database. IP port and address values will
* typically be overwritten by the OpenBTS.db values. Other values will
* not be in the database by default.
*/
#define DEFAULT_TRX_PORT 5700
#define DEFAULT_TRX_IP "127.0.0.1"
#define DEFAULT_EXTREF false
#define DEFAULT_DIVERSITY false
#define DEFAULT_CHANS 1
struct trx_config {
std::string log_level;
std::string addr;
std::string dev_args;
unsigned port;
unsigned tx_sps;
unsigned rx_sps;
unsigned chans;
unsigned rtsc;
unsigned rach_delay;
bool extref;
Transceiver::FillerType filler;
bool diversity;
double offset;
double rssi_offset;
bool swap_channels;
bool edge;
};
ConfigurationTable gConfig;
volatile bool gshutdown = false;
/* Run sanity check on configuration table
* The global table constructor cannot provide notification in the
* event of failure. Make sure that we can access the database,
* write to it, and that it contains the bare minimum required keys.
*/
bool testConfig()
{
int val = 9999;
std::string test = "asldfkjsaldkf";
const char *key = "Log.Level";
/* Attempt to query */
try {
gConfig.getStr(key);
} catch (...) {
std::cerr << std::endl;
std::cerr << "Config: Failed query required key " << key
<< std::endl;
return false;
}
/* Attempt to set a test value in the global config */
if (!gConfig.set(test, val)) {
std::cerr << std::endl;
std::cerr << "Config: Failed to set test key" << std::endl;
return false;
} else {
gConfig.remove(test);
}
return true;
}
/* Setup configuration values
* Don't query the existence of the Log.Level because it's a
* mandatory value. That is, if it doesn't exist, the configuration
* table will crash or will have already crashed. Everything else we
* can survive without and use default values if the database entries
* are empty.
*/
bool trx_setup_config(struct trx_config *config)
{
std::string refstr, fillstr, divstr, edgestr;
if (!testConfig())
return false;
if (config->log_level == "")
config->log_level = gConfig.getStr("Log.Level");
if (!config->port) {
if (gConfig.defines("TRX.Port"))
config->port = gConfig.getNum("TRX.Port");
else
config->port = DEFAULT_TRX_PORT;
}
if (config->addr == "") {
if (gConfig.defines("TRX.IP"))
config->addr = gConfig.getStr("TRX.IP");
else
config->addr = DEFAULT_TRX_IP;
}
if (!config->extref) {
if (gConfig.defines("TRX.Reference"))
config->extref = gConfig.getNum("TRX.Reference");
else
config->extref = DEFAULT_EXTREF;
}
if (!config->diversity) {
if (gConfig.defines("TRX.Diversity"))
config->diversity = gConfig.getNum("TRX.Diversity");
else
config->diversity = DEFAULT_DIVERSITY;
}
/* Diversity only supported on 2 channels */
if (config->diversity)
config->chans = 2;
edgestr = config->edge ? "Enabled" : "Disabled";
refstr = config->extref ? "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_NORM_RAND:
fillstr = "Normal busrts with random payload";
break;
case Transceiver::FILLER_EDGE_RAND:
fillstr = "EDGE busrts with random payload";
break;
case Transceiver::FILLER_ACCESS_RAND:
fillstr = "Access busrts with random payload";
break;
}
std::ostringstream ost("");
ost << "Config Settings" << std::endl;
ost << " Log Level............... " << config->log_level << std::endl;
ost << " Device args............. " << config->dev_args << std::endl;
ost << " TRX Base Port........... " << config->port << std::endl;
ost << " TRX Address............. " << config->addr << std::endl;
ost << " Channels................ " << config->chans << std::endl;
ost << " Tx Samples-per-Symbol... " << config->tx_sps << std::endl;
ost << " Rx Samples-per-Symbol... " << config->rx_sps << std::endl;
ost << " EDGE support............ " << edgestr << std::endl;
ost << " External Reference...... " << refstr << std::endl;
ost << " C0 Filler Table......... " << fillstr << std::endl;
ost << " Diversity............... " << divstr << std::endl;
ost << " Tuning offset........... " << config->offset << std::endl;
ost << " RSSI to dBm offset...... " << config->rssi_offset << std::endl;
ost << " Swap channels........... " << config->swap_channels << std::endl;
std::cout << ost << std::endl;
return true;
}
/* Create radio interface
* The interface consists of sample rate changes, frequency shifts,
* channel multiplexing, and other conversions. The transceiver core
* accepts input vectors sampled at multiples of the GSM symbol rate.
* The radio interface connects the main transceiver with the device
* object, which may be operating some other rate.
*/
RadioInterface *makeRadioInterface(struct trx_config *config,
RadioDevice *usrp, int type)
{
RadioInterface *radio = NULL;
if ((config->rx_sps != 1) && (type != RadioDevice::NORMAL)) {
LOG(ALERT) << "Unsupported radio interface configuration";
}
switch (type) {
case RadioDevice::NORMAL:
radio = new RadioInterface(usrp, config->tx_sps,
config->rx_sps, config->chans);
break;
case RadioDevice::RESAMP_64M:
case RadioDevice::RESAMP_100M:
radio = new RadioInterfaceResamp(usrp, config->tx_sps,
config->chans);
break;
case RadioDevice::DIVERSITY:
radio = new RadioInterfaceDiversity(usrp, config->tx_sps,
config->chans);
break;
default:
LOG(ALERT) << "Unsupported radio interface configuration";
return NULL;
}
if (!radio->init(type)) {
LOG(ALERT) << "Failed to initialize radio interface";
return NULL;
}
return radio;
}
/* Create transceiver core
* The multi-threaded modem core operates at multiples of the GSM rate of
* 270.8333 ksps and consists of GSM specific modulation, demodulation,
* and decoding schemes. Also included are the socket interfaces for
* connecting to the upper layer stack.
*/
Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio)
{
Transceiver *trx;
VectorFIFO *fifo;
trx = new Transceiver(config->port, config->addr.c_str(),
config->tx_sps, config->rx_sps, config->chans,
GSM::Time(3,0), radio, config->rssi_offset);
if (!trx->init(config->filler, config->rtsc, config->rach_delay)) {
LOG(ALERT) << "Failed to initialize transceiver";
delete trx;
return NULL;
}
for (size_t i = 0; i < config->chans; i++) {
fifo = radio->receiveFIFO(i);
if (fifo && trx->receiveFIFO(fifo, i))
continue;
LOG(ALERT) << "Could not attach FIFO to channel " << i;
delete trx;
return NULL;
}
return trx;
}
static void sig_handler(int signo)
{
fprintf(stdout, "Received shutdown signal");
gshutdown = true;
}
static void setup_signal_handlers()
{
if (signal(SIGINT, sig_handler) == SIG_ERR) {
fprintf(stderr, "Failed to install SIGINT signal handler\n");
exit(EXIT_FAILURE);
}
if (signal(SIGTERM, sig_handler) == SIG_ERR) {
fprintf(stderr, "Couldn't install SIGTERM signal handler\n");
exit( EXIT_FAILURE);
}
}
static void print_help()
{
fprintf(stdout, "Options:\n"
" -h This text\n"
" -a UHD device args\n"
" -l Logging level (%s)\n"
" -i IP address of GSM core\n"
" -p Base port number\n"
" -e Enable EDGE receiver\n"
" -d Enable dual channel diversity receiver\n"
" -x Enable external 10 MHz reference\n"
" -s Samples-per-symbol (1 or 4)\n"
" -c Number of ARFCN channels (default=1)\n"
" -f Enable C0 filler table\n"
" -o Set baseband frequency offset (default=auto)\n"
" -r Random Normal Burst test mode with TSC\n"
" -A Random Access Burst test mode with delay\n"
" -R RSSI to dBm offset in dB (default=0)\n"
" -S Swap channels (UmTRX only)\n",
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
}
static void handle_options(int argc, char **argv, struct trx_config *config)
{
int option;
config->port = 0;
config->tx_sps = DEFAULT_TX_SPS;
config->rx_sps = DEFAULT_RX_SPS;
config->chans = DEFAULT_CHANS;
config->rtsc = 0;
config->rach_delay = 0;
config->extref = false;
config->filler = Transceiver::FILLER_ZERO;
config->diversity = false;
config->offset = 0.0;
config->rssi_offset = 0.0;
config->swap_channels = false;
config->edge = false;
while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:r:A:R:Se")) != -1) {
switch (option) {
case 'h':
print_help();
exit(0);
break;
case 'a':
config->dev_args = optarg;
break;
case 'l':
config->log_level = optarg;
break;
case 'i':
config->addr = optarg;
break;
case 'p':
config->port = atoi(optarg);
break;
case 'c':
config->chans = atoi(optarg);
break;
case 'd':
config->diversity = true;
break;
case 'x':
config->extref = true;
break;
case 'f':
config->filler = Transceiver::FILLER_DUMMY;
break;
case 'o':
config->offset = atof(optarg);
break;
case 's':
config->tx_sps = atoi(optarg);
break;
case 'r':
config->rtsc = atoi(optarg);
config->filler = Transceiver::FILLER_NORM_RAND;
break;
case 'A':
config->rach_delay = atoi(optarg);
config->filler = Transceiver::FILLER_ACCESS_RAND;
break;
case 'R':
config->rssi_offset = atof(optarg);
break;
case 'S':
config->swap_channels = true;
break;
case 'e':
config->edge = true;
config->rx_sps = 4;
break;
default:
print_help();
exit(0);
}
}
if (config->edge && (config->filler == Transceiver::FILLER_NORM_RAND))
config->filler = Transceiver::FILLER_EDGE_RAND;
if ((config->tx_sps != 1) && (config->tx_sps != 4)) {
printf("Unsupported samples-per-symbol %i\n\n", config->tx_sps);
print_help();
exit(0);
}
if (config->edge && (config->tx_sps != 4)) {
printf("EDGE only supported at 4 samples per symbol\n\n");
print_help();
exit(0);
}
if (config->rtsc > 7) {
printf("Invalid training sequence %i\n\n", config->rtsc);
print_help();
exit(0);
}
if (config->rach_delay > 68) {
printf("RACH delay is too big %i\n\n", config->rach_delay);
print_help();
exit(0);
}
}
int main(int argc, char *argv[])
{
int type, chans;
RadioDevice *usrp;
RadioInterface *radio = NULL;
Transceiver *trx = NULL;
struct trx_config config;
handle_options(argc, argv, &config);
setup_signal_handlers();
/* Check database sanity */
if (!trx_setup_config(&config)) {
std::cerr << "Config: Database failure - exiting" << std::endl;
return EXIT_FAILURE;
}
gLogInit("transceiver", config.log_level.c_str(), LOG_LOCAL7);
srandom(time(NULL));
/* Create the low level device object */
usrp = RadioDevice::make(config.tx_sps, config.rx_sps, config.chans,
config.diversity, config.offset);
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;
}
/* Setup the appropriate device interface */
radio = makeRadioInterface(&config, usrp, type);
if (!radio)
goto shutdown;
/* Create the transceiver core */
trx = makeTransceiver(&config, radio);
if (!trx)
goto shutdown;
chans = trx->numChans();
std::cout << "-- Transceiver active with "
<< chans << " channel(s)" << std::endl;
while (!gshutdown)
sleep(1);
shutdown:
std::cout << "Shutting down transceiver..." << std::endl;
delete trx;
delete radio;
delete usrp;
return 0;
}

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

@@ -16,6 +16,7 @@
#define __RADIO_DEVICE_H__
#include <string>
#include <vector>
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -34,14 +35,15 @@ class RadioDevice {
enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED };
/* Radio interface types */
enum RadioInterfaceType { NORMAL, RESAMP };
enum RadioInterfaceType { NORMAL, RESAMP_64M, RESAMP_100M, DIVERSITY };
static RadioDevice *make(int sps, bool skipRx = false);
virtual ~RadioDevice() {};
static RadioDevice *make(size_t tx_sps, size_t rx_sps = 1, size_t chans = 1,
bool diversity = false, double offset = 0.0);
/** Initialize the USRP */
virtual int open(const std::string &args)=0;
virtual int open(const std::string &args = "", bool extref = false, bool swap_channels = false)=0;
virtual ~RadioDevice() { }
/** Start the USRP */
virtual bool start()=0;
@@ -53,7 +55,7 @@ class RadioDevice {
virtual enum TxWindowType getWindowType()=0;
/** Enable thread priority */
virtual void setPriority()=0;
virtual void setPriority(float prio = 0.5) = 0;
/**
Read samples from the radio.
@@ -65,9 +67,9 @@ class RadioDevice {
@param RSSI The received signal strength of the read result
@return The number of samples actually read
*/
virtual int readSamples(short **buf, int chans, int len, TIMESTAMP timestamp,
bool *overrun = NULL, bool *underrun = NULL,
unsigned *RSSI = NULL)=0;
virtual int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
TIMESTAMP timestamp = 0xffffffff, bool *underrun = 0,
unsigned *RSSI = 0) = 0;
/**
Write samples to the radio.
@param buf Contains the data to be written.
@@ -77,17 +79,17 @@ class RadioDevice {
@param isControl Set if data is a control packet, e.g. a ping command
@return The number of samples actually written
*/
virtual int writeSamples(short **buf, int chans, int len, TIMESTAMP timestamp,
bool *underrun = NULL, bool isControl = false)=0;
virtual int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
TIMESTAMP timestamp, bool isControl = false) = 0;
/** Update the alignment between the read and write timestamps */
virtual bool updateAlignment(TIMESTAMP timestamp)=0;
/** Set the transmitter frequency */
virtual bool setTxFreq(double wFreq, int chan = 0)=0;
virtual bool setTxFreq(double wFreq, size_t chan = 0) = 0;
/** Set the receiver frequency */
virtual bool setRxFreq(double wFreq, int chan = 0)=0;
virtual bool setRxFreq(double wFreq, size_t chan = 0) = 0;
/** Returns the starting write Timestamp*/
virtual TIMESTAMP initialWriteTimestamp(void)=0;
@@ -102,10 +104,10 @@ class RadioDevice {
virtual double fullScaleOutputValue()=0;
/** sets the receive chan gain, returns the gain setting **/
virtual double setRxGain(double dB, int chan = 0)=0;
virtual double setRxGain(double dB, size_t chan = 0) = 0;
/** gets the current receive gain **/
virtual double getRxGain(int chan = 0)=0;
virtual double getRxGain(size_t chan = 0) = 0;
/** return maximum Rx Gain **/
virtual double maxRxGain(void) = 0;
@@ -114,7 +116,7 @@ class RadioDevice {
virtual double minRxGain(void) = 0;
/** sets the transmit chan gain, returns the gain setting **/
virtual double setTxGain(double dB, int chan = 0)=0;
virtual double setTxGain(double dB, size_t chan = 0) = 0;
/** return maximum Tx Gain **/
virtual double maxTxGain(void) = 0;
@@ -122,18 +124,13 @@ class RadioDevice {
/** return minimum Tx Gain **/
virtual double minTxGain(void) = 0;
/** set and return antennas selection **/
virtual void setTxAntenna(std::string &name) = 0;
virtual void setRxAntenna(std::string &name) = 0;
virtual std::string getRxAntenna() = 0;
virtual std::string getTxAntenna() = 0;
/** Return internal status values */
virtual double getTxFreq(int chan = 0)=0;
virtual double getRxFreq(int chan = 0)=0;
virtual double getTxFreq(size_t chan = 0) = 0;
virtual double getRxFreq(size_t chan = 0) = 0;
virtual double getSampleRate()=0;
virtual double numberRead()=0;
virtual double numberWritten()=0;
};
#endif

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2008, 2009, 2012 Free Software Foundation, Inc.
* Copyright 2008, 2009 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
@@ -23,57 +23,79 @@
*/
#include "radioInterface.h"
#include "Resampler.h"
#include <Logger.h>
bool started = false;
/* Device side buffers */
static short *rx_buf[CHAN_MAX];
static short *tx_buf[CHAN_MAX];
/* Complex float to short conversion */
static void floatToShort(short *out, float *in, int num)
{
for (int i = 0; i < num; i++) {
out[2 * i + 0] = (short) in[2 * i + 0];
out[2 * i + 1] = (short) in[2 * i + 1];
}
extern "C" {
#include "convert.h"
}
/* Complex short to float conversion */
static void shortToFloat(float *out, short *in, int num)
{
for (int i = 0; i < num; i++) {
out[2 * i + 0] = (float) in[2 * i + 0];
out[2 * i + 1] = (float) in[2 * i + 1];
}
}
#define CHUNK 625
#define NUMCHUNKS 4
RadioInterface::RadioInterface(RadioDevice *wRadio,
int wChanM,
int wSPS,
int wReceiveOffset,
GSM::Time wStartTime)
: mChanM(wChanM), underrun(false), sendCursor(0), rcvCursor(0), mOn(false),
mRadio(wRadio), receiveOffset(wReceiveOffset), samplesPerSymbol(wSPS),
powerScaling(1.0), loadTest(false)
RadioInterface::RadioInterface(RadioDevice *wRadio, size_t tx_sps,
size_t rx_sps, size_t chans, size_t diversity,
int wReceiveOffset, GSM::Time wStartTime)
: mRadio(wRadio), mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans),
mMIMO(diversity), sendCursor(0), recvCursor(0), underrun(false),
overrun(false), receiveOffset(wReceiveOffset), mOn(false)
{
mClock.set(wStartTime);
}
RadioInterface::~RadioInterface(void)
{
if (mOn) {
mRadio->stop();
close();
close();
}
for (int i = 0; i < mChanM; i++) {
if (rcvBuffer[i] != NULL)
delete rcvBuffer[i];
if (sendBuffer[i] != NULL)
delete sendBuffer[i];
}
bool RadioInterface::init(int type)
{
if ((type != RadioDevice::NORMAL) || (mMIMO > 1) || !mChans) {
LOG(ALERT) << "Invalid configuration";
return false;
}
close();
sendBuffer.resize(mChans);
recvBuffer.resize(mChans);
convertSendBuffer.resize(mChans);
convertRecvBuffer.resize(mChans);
mReceiveFIFO.resize(mChans);
powerScaling.resize(mChans);
for (size_t i = 0; i < mChans; i++) {
sendBuffer[i] = new signalVector(CHUNK * mSPSTx);
recvBuffer[i] = new signalVector(NUMCHUNKS * CHUNK * mSPSRx);
convertSendBuffer[i] = new short[sendBuffer[i]->size() * 2];
convertRecvBuffer[i] = new short[recvBuffer[i]->size() * 2];
}
sendCursor = 0;
recvCursor = 0;
return true;
}
void RadioInterface::close()
{
for (size_t i = 0; i < sendBuffer.size(); i++)
delete sendBuffer[i];
for (size_t i = 0; i < recvBuffer.size(); i++)
delete recvBuffer[i];
for (size_t i = 0; i < convertSendBuffer.size(); i++)
delete convertSendBuffer[i];
for (size_t i = 0; i < convertRecvBuffer.size(); i++)
delete convertRecvBuffer[i];
sendBuffer.resize(0);
recvBuffer.resize(0);
convertSendBuffer.resize(0);
convertRecvBuffer.resize(0);
}
double RadioInterface::fullScaleInputValue(void) {
@@ -84,111 +106,119 @@ double RadioInterface::fullScaleOutputValue(void) {
return mRadio->fullScaleOutputValue();
}
void RadioInterface::setPowerAttenuation(double atten, int chan)
int RadioInterface::setPowerAttenuation(int atten, size_t chan)
{
double rfGain, digAtten;
rfGain = mRadio->setTxGain(mRadio->maxTxGain() - atten, chan);
digAtten = atten - mRadio->maxTxGain() + rfGain;
if (chan >= mChans) {
LOG(ALERT) << "Invalid channel requested";
return -1;
}
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 = 1.0;
powerScaling[chan] = 1.0;
else
powerScaling = 1.0/sqrt(pow(10, (digAtten/10.0)));
powerScaling[chan] = 1.0 / sqrt(pow(10, digAtten / 10.0));
return atten;
}
int RadioInterface::radioifyVector(signalVector &wVector,
float *retVector,
float scale,
bool zero)
{
int i;
signalVector::iterator itr = wVector.begin();
if (zero) {
memset(retVector, 0, wVector.size() * 2 * sizeof(float));
return wVector.size();
}
for (i = 0; i < wVector.size(); i++) {
retVector[2 * i + 0] = itr->real() * scale;
retVector[2 * i + 1] = itr->imag() * scale;
itr++;
}
memcpy(retVector, wVector.begin(), wVector.size() * 2 * sizeof(float));
return wVector.size();
}
int RadioInterface::unRadioifyVector(float *floatVector, int offset,
signalVector &newVector)
int RadioInterface::unRadioifyVector(float *floatVector,
signalVector& newVector)
{
int i;
signalVector::iterator itr = newVector.begin();
for (i = 0; i < newVector.size(); i++) {
*itr++ = Complex<float>(floatVector[offset + 2 * i + 0],
floatVector[offset + 2 * i + 1]);
if (newVector.size() > recvCursor) {
LOG(ALERT) << "Insufficient number of samples in receive buffer";
return -1;
}
for (size_t i = 0; i < newVector.size(); i++) {
*itr++ = Complex<float>(floatVector[2 * i + 0],
floatVector[2 * i + 1]);
}
return newVector.size();
}
bool RadioInterface::tuneTx(double freq, int chan)
bool RadioInterface::tuneTx(double freq, size_t chan)
{
return mRadio->setTxFreq(freq, chan);
}
bool RadioInterface::tuneRx(double freq, int chan)
bool RadioInterface::tuneRx(double freq, size_t chan)
{
return mRadio->setRxFreq(freq, chan);
}
bool RadioInterface::start()
{
int i;
if (mOn)
return true;
LOG(INFO) << "Starting radio device";
#ifdef USRP1
mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter,
(void*)this);
#endif
if (!mRadio->start())
return false;
mOn = true;
#ifdef USRP1
mAlignRadioServiceLoopThread = new Thread(32768);
mAlignRadioServiceLoopThread->start((void * (*)(void*))AlignRadioServiceLoopAdapter,
(void*)this);
#endif
recvCursor = 0;
sendCursor = 0;
writeTimestamp = mRadio->initialWriteTimestamp();
readTimestamp = mRadio->initialReadTimestamp();
for (i = 0; i < mChanM; i++) {
sendBuffer[i] = new float[8*2*INCHUNK];
rcvBuffer[i] = new float[8*2*OUTCHUNK];
}
/* Init I/O specific variables if applicable */
init();
mRadio->start();
LOG(DEBUG) << "Radio started";
mRadio->updateAlignment(writeTimestamp-10000);
mRadio->updateAlignment(writeTimestamp-10000);
mRadio->updateAlignment(writeTimestamp-10000);
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)
if (!mOn || !mRadio->stop())
return false;
mOn = false;
mRadio->stop();
return true;
}
#ifdef USRP1
void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
{
while (radioInterface->on()) {
while (1) {
radioInterface->alignRadio();
pthread_testcancel();
}
@@ -201,89 +231,111 @@ void RadioInterface::alignRadio() {
}
#endif
void RadioInterface::driveTransmitRadio(signalVector **radioBurst, bool *zeroBurst)
void RadioInterface::driveTransmitRadio(std::vector<signalVector *> &bursts,
std::vector<bool> &zeros)
{
int i;
if (!mOn)
return;
for (i = 0; i < mChanM; i++) {
radioifyVector(*radioBurst[i], sendBuffer[i] + 2 * sendCursor,
powerScaling, zeroBurst[i]);
for (size_t i = 0; i < mChans; i++) {
radioifyVector(*bursts[i],
(float *) (sendBuffer[i]->begin() + sendCursor), zeros[i]);
}
/*
* All bursts should be the same size since all transceivers are
* tied with a single clock in the radio interface.
*/
sendCursor += radioBurst[0]->size();
sendCursor += bursts[0]->size();
pushBuffer();
}
static inline void shiftRxBuffers(float **buf, int offset, int len, int chanM)
bool RadioInterface::driveReceiveRadio()
{
for (int i = 0; i < chanM; i++)
memmove(buf[i], buf[i] + offset, sizeof(float) * len);
}
radioVector *burst = NULL;
void RadioInterface::loadVectors(unsigned tN, int samplesPerBurst,
int idx, GSM::Time rxClock)
{
int i;
for (i = 0; i < mChanM; i++) {
signalVector rxVector(samplesPerBurst);
unRadioifyVector(rcvBuffer[i], idx * 2, rxVector);
radioVector *rxBurst = new radioVector(rxVector, rxClock);
mReceiveFIFO[i].write(rxBurst);
}
}
void RadioInterface::driveReceiveRadio()
{
if (!mOn)
return;
if (mReceiveFIFO[0].size() > 8)
return;
return false;
pullBuffer();
GSM::Time rcvClock = mClock.get();
rcvClock.decTN(receiveOffset);
unsigned tN = rcvClock.TN();
int rcvSz = rcvCursor;
int recvSz = recvCursor;
int readSz = 0;
const int symbolsPerSlot = gSlotLen + 8;
int samplesPerBurst = (symbolsPerSlot + (tN % 4 == 0)) * samplesPerSymbol;
int burstSize;
// while there's enough data in receive buffer, form received
// GSM bursts and pass up to Transceiver
// Using the 157-156-156-156 symbols per timeslot format.
while (rcvSz >= samplesPerBurst) {
if (rcvClock.FN() >= 0) {
loadVectors(tN, samplesPerBurst, readSz, rcvClock);
if (mSPSRx == 4)
burstSize = 625;
else
burstSize = symbolsPerSlot + (tN % 4 == 0);
/*
* Pre-allocate head room for the largest correlation size
* so we can later avoid a re-allocation and copy
* */
size_t head = GSM::gRACHSynchSequence.size();
/*
* Form receive bursts and pass up to transceiver. Use repeating
* pattern of 157-156-156-156 symbols per timeslot
*/
while (recvSz > burstSize) {
for (size_t i = 0; i < mChans; i++) {
burst = new radioVector(rcvClock, burstSize, head, mMIMO);
for (size_t n = 0; n < mMIMO; n++) {
unRadioifyVector((float *)
(recvBuffer[mMIMO * i + n]->begin() + readSz),
*burst->getVector(n));
}
if (mReceiveFIFO[i].size() < 32)
mReceiveFIFO[i].write(burst);
else
delete burst;
}
mClock.incTN();
rcvClock.incTN();
readSz += samplesPerBurst;
rcvSz -= samplesPerBurst;
readSz += burstSize;
recvSz -= burstSize;
tN = rcvClock.TN();
samplesPerBurst = (symbolsPerSlot + (tN % 4 == 0)) * samplesPerSymbol;
if (mSPSRx != 4)
burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
}
if (readSz > 0) {
rcvCursor -= readSz;
shiftRxBuffers(rcvBuffer, 2 * readSz, 2 * rcvCursor, mChanM);
for (size_t i = 0; i < recvBuffer.size(); i++) {
memmove(recvBuffer[i]->begin(),
recvBuffer[i]->begin() + readSz,
(recvCursor - readSz) * 2 * sizeof(float));
}
recvCursor -= readSz;
}
return true;
}
double RadioInterface::setRxGain(double dB, int chan)
bool RadioInterface::isUnderrun()
{
bool retVal = underrun;
underrun = false;
return retVal;
}
VectorFIFO* RadioInterface::receiveFIFO(size_t chan)
{
if (chan >= mReceiveFIFO.size())
return NULL;
return &mReceiveFIFO[chan];
}
double RadioInterface::setRxGain(double dB, size_t chan)
{
if (mRadio)
return mRadio->setRxGain(dB, chan);
@@ -291,7 +343,7 @@ double RadioInterface::setRxGain(double dB, int chan)
return -1;
}
double RadioInterface::getRxGain(int chan)
double RadioInterface::getRxGain(size_t chan)
{
if (mRadio)
return mRadio->getRxGain(chan);
@@ -299,56 +351,60 @@ double RadioInterface::getRxGain(int chan)
return -1;
}
bool RadioInterface::init()
{
for (int i = 0; i < CHAN_MAX; i++) {
rx_buf[i] = new short[2 * OUTCHUNK];
tx_buf[i] = new short[4 * 2 * INCHUNK];
}
}
void RadioInterface::close()
{
for (int i = 0; i < CHAN_MAX; i++) {
delete rx_buf[i];
delete tx_buf[i];
}
}
/* Receive a timestamped chunk from the device */
/* Receive a timestamped chunk from the device */
void RadioInterface::pullBuffer()
{
bool local_underrun;
int num_recv;
float *output;
/* Read samples. Fail if we don't get what we want. */
int num_rd = mRadio->readSamples(rx_buf, mChanM, OUTCHUNK, readTimestamp);
LOG(DEBUG) << "Rx read " << num_rd << " samples from device";
assert(num_rd == OUTCHUNK);
underrun |= local_underrun;
readTimestamp += (TIMESTAMP) num_rd;
for (int i = 0; i < mChanM; i++)
shortToFloat(rcvBuffer[i] + 2 * rcvCursor, rx_buf[i], num_rd);
rcvCursor += num_rd;
}
/* Send timestamped chunk to the device with arbitrary size */
void RadioInterface::pushBuffer()
{
if (sendCursor < INCHUNK)
if (recvCursor > recvBuffer[0]->size() - CHUNK)
return;
for (int i = 0; i < mChanM; i++)
floatToShort(tx_buf[i], sendBuffer[i], sendCursor);
/* Outer buffer access size is fixed */
num_recv = mRadio->readSamples(convertRecvBuffer,
CHUNK,
&overrun,
readTimestamp,
&local_underrun);
if (num_recv != CHUNK) {
LOG(ALERT) << "Receive error " << num_recv;
return;
}
/* Write samples. Fail if we don't get what we want. */
int num_smpls = mRadio->writeSamples(tx_buf, mChanM, sendCursor,
writeTimestamp, &underrun);
assert(num_smpls == sendCursor);
for (size_t i = 0; i < mChans; i++) {
output = (float *) (recvBuffer[i]->begin() + recvCursor);
convert_short_float(output, convertRecvBuffer[i], 2 * num_recv);
}
writeTimestamp += (TIMESTAMP) num_smpls;
underrun |= local_underrun;
readTimestamp += num_recv;
recvCursor += num_recv;
}
/* Send timestamped chunk to the device with arbitrary size */
void RadioInterface::pushBuffer()
{
int num_sent;
if (sendCursor < CHUNK)
return;
if (sendCursor > sendBuffer[0]->size())
LOG(ALERT) << "Send buffer overflow";
for (size_t i = 0; i < mChans; i++) {
convert_float_short(convertSendBuffer[i],
(float *) sendBuffer[i]->begin(),
powerScaling[i], 2 * sendCursor);
}
/* Send the all samples in the send buffer */
num_sent = mRadio->writeSamples(convertSendBuffer,
sendCursor,
&underrun,
writeTimestamp);
writeTimestamp += num_sent;
sendCursor = 0;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2008, 2012 Free Software Foundation, Inc.
* Copyright 2008 Free Software Foundation, Inc.
*
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
*
@@ -12,8 +12,7 @@
*/
#ifndef _RADIOINTEFACE_H_
#define _RADIOINTEFACE_H_
#include "sigProcLib.h"
#include "GSMCommon.h"
@@ -21,13 +20,7 @@
#include "radioDevice.h"
#include "radioVector.h"
#include "radioClock.h"
/** samples per GSM symbol */
#define SAMPSPERSYM 1
#define INCHUNK (625)
#define OUTCHUNK (625)
#define CHAN_MAX 2
#include "Resampler.h"
static const unsigned gSlotLen = 148; ///< number of symbols per slot, not counting guard periods
@@ -36,18 +29,25 @@ class RadioInterface {
protected:
int mChanM; ///< channelizer width
Thread mAlignRadioServiceLoopThread; ///< thread that synchronizes transmit and receive sections
VectorFIFO mReceiveFIFO[CHAN_MAX]; ///< FIFO that holds receive bursts
std::vector<VectorFIFO> mReceiveFIFO; ///< FIFO that holds receive bursts
RadioDevice *mRadio; ///< the USRP object
float *sendBuffer[CHAN_MAX];
size_t mSPSTx;
size_t mSPSRx;
size_t mChans;
size_t mMIMO;
std::vector<signalVector *> sendBuffer;
std::vector<signalVector *> recvBuffer;
unsigned sendCursor;
unsigned recvCursor;
float *rcvBuffer[CHAN_MAX];
unsigned rcvCursor;
std::vector<short *> convertRecvBuffer;
std::vector<short *> convertSendBuffer;
std::vector<float> powerScaling;
bool underrun; ///< indicates writes to USRP are too slow
bool overrun; ///< indicates reads from USRP are too slow
TIMESTAMP writeTimestamp; ///< sample timestamp of next packet written to USRP
@@ -55,29 +55,19 @@ protected:
RadioClock mClock; ///< the basestation clock!
int samplesPerSymbol; ///< samples per GSM symbol
int receiveOffset; ///< offset b/w transmit and receive GSM timestamps, in timeslots
bool mOn; ///< indicates radio is on
double powerScaling;
bool loadTest;
int mNumARFCNs;
signalVector *finalVec, *finalVec9;
private:
/** initialize I/O internals */
bool init();
/** format samples to USRP */
int radioifyVector(signalVector &wVector,
float *floatVector,
float scale,
bool zero);
/** format samples from USRP */
int unRadioifyVector(float *floatVector, int offset, signalVector &wVector);
int unRadioifyVector(float *floatVector, signalVector &wVector);
/** push GSM bursts into the transmit buffer */
virtual void pushBuffer(void);
@@ -85,59 +75,54 @@ private:
/** pull GSM bursts from the receive buffer */
virtual void pullBuffer(void);
/** load receive vectors into FIFO's */
void loadVectors(unsigned tN, int samplesPerBurst, int index, GSM::Time rxClock);
public:
/** start the interface */
bool start();
bool stop();
bool started() { return mOn; };
/** shutdown interface */
void close();
/** intialization */
virtual bool init(int type);
virtual void close();
/** constructor */
RadioInterface(RadioDevice* wRadio,
int wChanM = 1,
int wSPS = SAMPSPERSYM,
int receiveOffset = 3,
GSM::Time wStartTime = GSM::Time(0, 0));
RadioInterface(RadioDevice* wRadio = NULL,
size_t tx_sps = 4, size_t rx_sps = 1,
size_t chans = 1, size_t diversity = 1,
int receiveOffset = 3, GSM::Time wStartTime = GSM::Time(0));
/** destructor */
~RadioInterface();
virtual ~RadioInterface();
void setSamplesPerSymbol(int wSamplesPerSymbol) {if (!mOn) samplesPerSymbol = wSamplesPerSymbol;}
/** check for underrun, resets underrun value */
bool isUnderrun();
int getSamplesPerSymbol() { return samplesPerSymbol;}
/** return the receive FIFO */
VectorFIFO* receiveFIFO(int num) { return &mReceiveFIFO[num];}
VectorFIFO* receiveFIFO(size_t chan = 0);
/** return the basestation clock */
RadioClock* getClock(void) { return &mClock;};
/** set transmit frequency */
bool tuneTx(double freq, int chan = 0);
bool tuneTx(double freq, size_t chan = 0);
/** set receive frequency */
bool tuneRx(double freq, int chan = 0);
virtual bool tuneRx(double freq, size_t chan = 0);
/** set receive gain */
double setRxGain(double dB, int chan = 0);
double setRxGain(double dB, size_t chan = 0);
/** get receive gain */
double getRxGain(int chan = 0);
double getRxGain(size_t chan = 0);
/** drive transmission of GSM bursts */
void driveTransmitRadio(signalVector **radioBurst, bool *zeroBurst);
void driveTransmitRadio(std::vector<signalVector *> &bursts,
std::vector<bool> &zeros);
/** drive reception of GSM bursts */
void driveReceiveRadio();
bool driveReceiveRadio();
void setPowerAttenuation(double atten, int chan = 0);
int setPowerAttenuation(int atten, size_t chan = 0);
/** returns the full-scale transmit amplitude **/
double fullScaleInputValue();
@@ -146,10 +131,66 @@ public:
double fullScaleOutputValue();
/** set thread priority on current thread */
void setPriority() { mRadio->setPriority(); }
void setPriority(float prio = 0.5) { mRadio->setPriority(prio); }
/** get transport window type of attached device */
enum RadioDevice::TxWindowType getWindowType() { return mRadio->getWindowType(); }
#if USRP1
protected:
/** drive synchronization of Tx/Rx of USRP */
void alignRadio();
friend void *AlignRadioServiceLoopAdapter(RadioInterface*);
#endif
};
#endif /* _RADIOINTEFACE_H_ */
#if USRP1
/** synchronization thread loop */
void *AlignRadioServiceLoopAdapter(RadioInterface*);
#endif
class RadioInterfaceResamp : public RadioInterface {
private:
signalVector *innerSendBuffer;
signalVector *outerSendBuffer;
signalVector *innerRecvBuffer;
signalVector *outerRecvBuffer;
void pushBuffer();
void pullBuffer();
public:
RadioInterfaceResamp(RadioDevice* wRadio, size_t wSPS = 4, size_t chans = 1);
~RadioInterfaceResamp();
bool init(int type);
void close();
};
class RadioInterfaceDiversity : public RadioInterface {
public:
RadioInterfaceDiversity(RadioDevice* wRadio,
size_t sps = 4, size_t chans = 2);
~RadioInterfaceDiversity();
bool init(int type);
void close();
bool tuneRx(double freq, size_t chan);
private:
std::vector<Resampler *> dnsamplers;
std::vector<float> phases;
signalVector *outerRecvBuffer;
bool mDiversity;
double mFreqSpacing;
bool setupDiversityChannels();
void pullBuffer();
};

View File

@@ -0,0 +1,248 @@
/*
* SSE Convolution
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <radioInterface.h>
#include <Logger.h>
#include "Resampler.h"
extern "C" {
#include "convert.h"
}
/* Resampling parameters for 64 MHz clocking */
#define RESAMP_64M_INRATE 20
#define RESAMP_64M_OUTRATE 80
/* Downlink block size */
#define CHUNK 625
/* Universal resampling parameters */
#define NUMCHUNKS 48
/*
* Resampling filter bandwidth scaling factor
* This narrows the filter cutoff relative to the output bandwidth
* of the polyphase resampler. At 4 samples-per-symbol using the
* 2 pulse Laurent GMSK approximation gives us below 0.5 degrees
* RMS phase error at the resampler output.
*/
#define RESAMP_TX4_FILTER 0.45
static size_t resamp_inrate = 0;
static size_t resamp_inchunk = 0;
static size_t resamp_outrate = 0;
static size_t resamp_outchunk = 0;
RadioInterfaceDiversity::RadioInterfaceDiversity(RadioDevice *wRadio,
size_t sps, size_t chans)
: RadioInterface(wRadio, sps, chans, 2), outerRecvBuffer(NULL),
mDiversity(false), mFreqSpacing(0.0)
{
}
RadioInterfaceDiversity::~RadioInterfaceDiversity()
{
close();
}
void RadioInterfaceDiversity::close()
{
delete outerRecvBuffer;
outerRecvBuffer = NULL;
for (size_t i = 0; i < dnsamplers.size(); i++) {
delete dnsamplers[i];
dnsamplers[i] = NULL;
}
if (recvBuffer.size())
recvBuffer[0] = NULL;
RadioInterface::close();
}
bool RadioInterfaceDiversity::setupDiversityChannels()
{
size_t inner_rx_len;
/* Inner and outer rates */
resamp_inrate = RESAMP_64M_INRATE;
resamp_outrate = RESAMP_64M_OUTRATE;
resamp_inchunk = resamp_inrate * 4;
resamp_outchunk = resamp_outrate * 4;
/* Buffer lengths */
inner_rx_len = NUMCHUNKS * resamp_inchunk;
/* Inside buffer must hold at least 2 bursts */
if (inner_rx_len < 157 * mSPSRx * 2) {
LOG(ALERT) << "Invalid inner buffer size " << inner_rx_len;
return false;
}
/* One Receive buffer and downsampler per diversity channel */
for (size_t i = 0; i < mMIMO * mChans; i++) {
dnsamplers[i] = new Resampler(resamp_inrate, resamp_outrate);
if (!dnsamplers[i]->init()) {
LOG(ALERT) << "Rx resampler failed to initialize";
return false;
}
recvBuffer[i] = new signalVector(inner_rx_len);
}
return true;
}
/* Initialize I/O specific objects */
bool RadioInterfaceDiversity::init(int type)
{
int tx_len, outer_rx_len;
if ((mMIMO != 2) || (mChans != 2)) {
LOG(ALERT) << "Unsupported channel configuration " << mChans;
return false;
}
/* Resize for channel combination */
sendBuffer.resize(mChans);
recvBuffer.resize(mChans * mMIMO);
convertSendBuffer.resize(mChans);
convertRecvBuffer.resize(mChans);
mReceiveFIFO.resize(mChans);
dnsamplers.resize(mChans * mMIMO);
phases.resize(mChans);
if (!setupDiversityChannels())
return false;
tx_len = CHUNK * mSPSTx;
outer_rx_len = resamp_outchunk;
for (size_t i = 0; i < mChans; i++) {
/* Full rate float and integer outer receive buffers */
convertRecvBuffer[i] = new short[outer_rx_len * 2];
/* Send buffers (not-resampled) */
sendBuffer[i] = new signalVector(tx_len);
convertSendBuffer[i] = new short[tx_len * 2];
}
outerRecvBuffer = new signalVector(outer_rx_len, dnsamplers[0]->len());
return true;
}
bool RadioInterfaceDiversity::tuneRx(double freq, size_t chan)
{
double f0, f1;
if (chan > 1)
return false;
if (!mRadio->setRxFreq(freq, chan))
return false;
f0 = mRadio->getRxFreq(0);
f1 = mRadio->getRxFreq(1);
mFreqSpacing = f1 - f0;
if (abs(mFreqSpacing) <= 600e3)
mDiversity = true;
else
mDiversity = false;
return true;
}
/* Receive a timestamped chunk from the device */
void RadioInterfaceDiversity::pullBuffer()
{
bool local_underrun;
int rc, num, path0, path1;
signalVector *shift, *base;
float *in, *out, rate = -mFreqSpacing * 2.0 * M_PI / 1.08333333e6;
if (recvCursor > recvBuffer[0]->size() - resamp_inchunk)
return;
/* Outer buffer access size is fixed */
num = mRadio->readSamples(convertRecvBuffer,
resamp_outchunk,
&overrun,
readTimestamp,
&local_underrun);
if ((size_t) num != resamp_outchunk) {
LOG(ALERT) << "Receive error " << num;
return;
}
for (size_t i = 0; i < mChans; i++) {
convert_short_float((float *) outerRecvBuffer->begin(),
convertRecvBuffer[i], 2 * resamp_outchunk);
if (!i) {
path0 = 0;
path1 = 2;
} else {
path0 = 3;
path1 = 1;
}
/* Diversity path 1 */
base = outerRecvBuffer;
in = (float *) base->begin();
out = (float *) (recvBuffer[path0]->begin() + recvCursor);
rc = dnsamplers[2 * i + 0]->rotate(in, resamp_outchunk,
out, resamp_inchunk);
if (rc < 0) {
LOG(ALERT) << "Sample rate downsampling error";
}
/* Enable path 2 if Nyquist bandwidth is sufficient */
if (!mDiversity)
continue;
/* Diversity path 2 */
shift = new signalVector(base->size(), base->getStart());
in = (float *) shift->begin();
out = (float *) (recvBuffer[path1]->begin() + recvCursor);
rate = i ? -rate : rate;
if (!frequencyShift(shift, base, rate, phases[i], &phases[i])) {
LOG(ALERT) << "Frequency shift failed";
}
rc = dnsamplers[2 * i + 1]->rotate(in, resamp_outchunk,
out, resamp_inchunk);
if (rc < 0) {
LOG(ALERT) << "Sample rate downsampling error";
}
delete shift;
}
underrun |= local_underrun;
readTimestamp += (TIMESTAMP) resamp_outchunk;
recvCursor += resamp_inchunk;
}

View File

@@ -0,0 +1,260 @@
/*
* Radio device interface with sample rate conversion
* Written by Thomas Tsou <tom@tsou.cc>
*
* Copyright 2011, 2012, 2013 Free Software Foundation, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* See the COPYING file in the main directory for details.
*/
#include <radioInterface.h>
#include <Logger.h>
#include "Resampler.h"
extern "C" {
#include "convert.h"
}
/* Resampling parameters for 64 MHz clocking */
#define RESAMP_64M_INRATE 65
#define RESAMP_64M_OUTRATE 96
/* Resampling parameters for 100 MHz clocking */
#define RESAMP_100M_INRATE 52
#define RESAMP_100M_OUTRATE 75
/* Universal resampling parameters */
#define NUMCHUNKS 24
/*
* Resampling filter bandwidth scaling factor
* This narrows the filter cutoff relative to the output bandwidth
* of the polyphase resampler. At 4 samples-per-symbol using the
* 2 pulse Laurent GMSK approximation gives us below 0.5 degrees
* RMS phase error at the resampler output.
*/
#define RESAMP_TX4_FILTER 0.45
static Resampler *upsampler = NULL;
static Resampler *dnsampler = NULL;
static size_t resamp_inrate = 0;
static size_t resamp_inchunk = 0;
static size_t resamp_outrate = 0;
static size_t resamp_outchunk = 0;
RadioInterfaceResamp::RadioInterfaceResamp(RadioDevice *wRadio,
size_t sps, size_t chans)
: RadioInterface(wRadio, sps, chans),
innerSendBuffer(NULL), outerSendBuffer(NULL),
innerRecvBuffer(NULL), outerRecvBuffer(NULL)
{
}
RadioInterfaceResamp::~RadioInterfaceResamp()
{
close();
}
void RadioInterfaceResamp::close()
{
delete innerSendBuffer;
delete outerSendBuffer;
delete innerRecvBuffer;
delete outerRecvBuffer;
delete upsampler;
delete dnsampler;
innerSendBuffer = NULL;
outerSendBuffer = NULL;
innerRecvBuffer = NULL;
outerRecvBuffer = NULL;
upsampler = NULL;
dnsampler = NULL;
if (sendBuffer.size())
sendBuffer[0] = NULL;
if (recvBuffer.size())
recvBuffer[0] = NULL;
RadioInterface::close();
}
/* Initialize I/O specific objects */
bool RadioInterfaceResamp::init(int type)
{
float cutoff = 1.0f;
if (mChans != 1) {
LOG(ALERT) << "Unsupported channel configuration " << mChans;
return false;
}
close();
sendBuffer.resize(1);
recvBuffer.resize(1);
convertSendBuffer.resize(1);
convertRecvBuffer.resize(1);
mReceiveFIFO.resize(1);
powerScaling.resize(1);
switch (type) {
case RadioDevice::RESAMP_64M:
resamp_inrate = RESAMP_64M_INRATE;
resamp_outrate = RESAMP_64M_OUTRATE;
break;
case RadioDevice::RESAMP_100M:
resamp_inrate = RESAMP_100M_INRATE;
resamp_outrate = RESAMP_100M_OUTRATE;
break;
case RadioDevice::NORMAL:
default:
LOG(ALERT) << "Invalid device configuration";
return false;
}
resamp_inchunk = resamp_inrate * 4;
resamp_outchunk = resamp_outrate * 4;
if (resamp_inchunk * NUMCHUNKS < 157 * mSPSTx * 2) {
LOG(ALERT) << "Invalid inner chunk size " << resamp_inchunk;
return false;
}
if (mSPSTx == 4)
cutoff = RESAMP_TX4_FILTER;
dnsampler = new Resampler(resamp_inrate, resamp_outrate);
if (!dnsampler->init()) {
LOG(ALERT) << "Rx resampler failed to initialize";
return false;
}
upsampler = new Resampler(resamp_outrate, resamp_inrate);
if (!upsampler->init(cutoff)) {
LOG(ALERT) << "Tx resampler failed to initialize";
return false;
}
/*
* Allocate high and low rate buffers. The high rate receive
* buffer and low rate transmit vectors feed into the resampler
* and requires headroom equivalent to the filter length. Low
* rate buffers are allocated in the main radio interface code.
*/
innerSendBuffer =
new signalVector(NUMCHUNKS * resamp_inchunk, upsampler->len());
outerSendBuffer =
new signalVector(NUMCHUNKS * resamp_outchunk);
outerRecvBuffer =
new signalVector(resamp_outchunk, dnsampler->len());
innerRecvBuffer =
new signalVector(NUMCHUNKS * resamp_inchunk / mSPSTx);
convertSendBuffer[0] = new short[outerSendBuffer->size() * 2];
convertRecvBuffer[0] = new short[outerRecvBuffer->size() * 2];
sendBuffer[0] = innerSendBuffer;
recvBuffer[0] = innerRecvBuffer;
return true;
}
/* Receive a timestamped chunk from the device */
void RadioInterfaceResamp::pullBuffer()
{
bool local_underrun;
int rc, num_recv;
if (recvCursor > innerRecvBuffer->size() - resamp_inchunk)
return;
/* Outer buffer access size is fixed */
num_recv = mRadio->readSamples(convertRecvBuffer,
resamp_outchunk,
&overrun,
readTimestamp,
&local_underrun);
if (num_recv != (int) resamp_outchunk) {
LOG(ALERT) << "Receive error " << num_recv;
return;
}
convert_short_float((float *) outerRecvBuffer->begin(),
convertRecvBuffer[0], 2 * resamp_outchunk);
underrun |= local_underrun;
readTimestamp += (TIMESTAMP) resamp_outchunk;
/* Write to the end of the inner receive buffer */
rc = dnsampler->rotate((float *) outerRecvBuffer->begin(),
resamp_outchunk,
(float *) (innerRecvBuffer->begin() + recvCursor),
resamp_inchunk);
if (rc < 0) {
LOG(ALERT) << "Sample rate upsampling error";
}
recvCursor += resamp_inchunk;
}
/* Send a timestamped chunk to the device */
void RadioInterfaceResamp::pushBuffer()
{
int rc, chunks, num_sent;
int inner_len, outer_len;
if (sendCursor < resamp_inchunk)
return;
if (sendCursor > innerSendBuffer->size())
LOG(ALERT) << "Send buffer overflow";
chunks = sendCursor / resamp_inchunk;
inner_len = chunks * resamp_inchunk;
outer_len = chunks * resamp_outchunk;
/* Always send from the beginning of the buffer */
rc = upsampler->rotate((float *) innerSendBuffer->begin(), inner_len,
(float *) outerSendBuffer->begin(), outer_len);
if (rc < 0) {
LOG(ALERT) << "Sample rate downsampling error";
}
convert_float_short(convertSendBuffer[0],
(float *) outerSendBuffer->begin(),
powerScaling[0], 2 * outer_len);
num_sent = mRadio->writeSamples(convertSendBuffer,
outer_len,
&underrun,
writeTimestamp);
if (num_sent != outer_len) {
LOG(ALERT) << "Transmit error " << num_sent;
}
/* Shift remaining samples to beginning of buffer */
memmove(innerSendBuffer->begin(),
innerSendBuffer->begin() + inner_len,
(sendCursor - inner_len) * 2 * sizeof(float));
writeTimestamp += outer_len;
sendCursor -= inner_len;
assert(sendCursor >= 0);
}

View File

@@ -2,7 +2,7 @@
* Written by Thomas Tsou <ttsou@vt.edu>
* Based on code by Harvind S Samra <hssamra@kestrelsp.com>
*
* Copyright 2011, 2012 Free Software Foundation, Inc.
* Copyright 2011 Free Software Foundation, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -21,9 +21,24 @@
#include "radioVector.h"
radioVector::radioVector(const signalVector& wVector, GSM::Time& wTime)
: signalVector(wVector), mTime(wTime)
radioVector::radioVector(GSM::Time &time, size_t size,
size_t start, size_t chans)
: vectors(chans), mTime(time)
{
for (size_t i = 0; i < vectors.size(); i++)
vectors[i] = new signalVector(size, start);
}
radioVector::radioVector(GSM::Time& wTime, signalVector *vector)
: vectors(1), mTime(wTime)
{
vectors[0] = vector;
}
radioVector::~radioVector()
{
for (size_t i = 0; i < vectors.size(); i++)
delete vectors[i];
}
GSM::Time radioVector::getTime() const
@@ -41,6 +56,52 @@ bool radioVector::operator>(const radioVector& other) const
return mTime > other.mTime;
}
signalVector *radioVector::getVector(size_t chan) const
{
if (chan >= vectors.size())
return NULL;
return vectors[chan];
}
bool radioVector::setVector(signalVector *vector, size_t chan)
{
if (chan >= vectors.size())
return false;
vectors[chan] = vector;
return true;
}
noiseVector::noiseVector(size_t size)
: std::vector<float>(size), itr(0)
{
}
float noiseVector::avg() const
{
float val = 0.0;
for (size_t i = 0; i < size(); i++)
val += (*this)[i];
return val / (float) size();
}
bool noiseVector::insert(float val)
{
if (!size())
return false;
if (itr >= this->size())
itr = 0;
(*this)[itr++] = val;
return true;
}
GSM::Time VectorQueue::nextTime() const
{
GSM::Time retVal;

View File

@@ -2,7 +2,7 @@
* Written by Thomas Tsou <ttsou@vt.edu>
* Based on code by Harvind S Samra <hssamra@kestrelsp.com>
*
* Copyright 2011, 2012 Free Software Foundation, Inc.
* Copyright 2011 Free Software Foundation, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -26,20 +26,38 @@
#include "GSMCommon.h"
#include "Interthread.h"
class radioVector : public signalVector {
class radioVector {
public:
radioVector(const signalVector& wVector, GSM::Time& wTime);
radioVector(GSM::Time& wTime, size_t size = 0,
size_t start = 0, size_t chans = 1);
radioVector(GSM::Time& wTime, signalVector *vector);
~radioVector();
GSM::Time getTime() const;
void setTime(const GSM::Time& wTime);
bool operator>(const radioVector& other) const;
signalVector *getVector(size_t chan = 0) const;
bool setVector(signalVector *vector, size_t chan = 0);
size_t chans() const { return vectors.size(); }
private:
std::vector<signalVector *> vectors;
GSM::Time mTime;
};
class VectorFIFO : public InterthreadQueue<radioVector> {
class noiseVector : std::vector<float> {
public:
noiseVector(size_t size = 0);
bool insert(float val);
float avg() const;
private:
size_t itr;
};
class VectorFIFO : public InterthreadQueue<radioVector> { };
class VectorQueue : public InterthreadPriorityQueue<radioVector> {
public:
GSM::Time nextTime() const;

File diff suppressed because one or more lines are too long

View File

@@ -1,164 +0,0 @@
/*
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
* Copyright 2010 Kestrel Signal Processing, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Transceiver.h"
#include "radioDevice.h"
#include "DummyLoad.h"
#include <time.h>
#include <signal.h>
#include <GSMCommon.h>
#include <Logger.h>
#include <Configuration.h>
using namespace std;
ConfigurationTable gConfig("/etc/OpenBTS/OpenBTS.db");
volatile bool gbShutdown = false;
static void ctrlCHandler(int signo)
{
cout << "Received shutdown signal" << endl;;
gbShutdown = true;
}
int main(int argc, char *argv[])
{
std::string deviceArgs;
std::string txAntenna, rxAntenna;
if (argc == 3)
{
deviceArgs = std::string(argv[2]);
}
else
{
deviceArgs = "";
}
if ( signal( SIGINT, ctrlCHandler ) == SIG_ERR )
{
cerr << "Couldn't install signal handler for SIGINT" << endl;
exit(1);
}
if ( signal( SIGTERM, ctrlCHandler ) == SIG_ERR )
{
cerr << "Couldn't install signal handler for SIGTERM" << endl;
exit(1);
}
// Configure logger.
gLogInit("transceiver",gConfig.getStr("Log.Level").c_str(),LOG_LOCAL7);
int numARFCN=1;
LOG(NOTICE) << "starting transceiver with " << numARFCN << " ARFCNs (argc=" << argc << ")";
srandom(time(NULL));
RadioDevice *usrp = RadioDevice::make(SAMPSPERSYM);
int radioType = usrp->open(deviceArgs);
if (radioType < 0) {
LOG(ALERT) << "Transceiver exiting..." << std::endl;
return EXIT_FAILURE;
}
if (gConfig.defines("GSM.Radio.TxAntenna"))
txAntenna = gConfig.getStr("GSM.Radio.TxAntenna").c_str();
if (gConfig.defines("GSM.Radio.RxAntenna"))
rxAntenna = gConfig.getStr("GSM.Radio.RxAntenna").c_str();
if (txAntenna != "")
usrp->setTxAntenna(txAntenna);
if (rxAntenna != "")
usrp->setRxAntenna(rxAntenna);
LOG(INFO) << "transceiver using transmit antenna " << usrp->getRxAntenna();
LOG(INFO) << "transceiver using receive antenna " << usrp->getTxAntenna();
RadioInterface* radio;
switch (radioType) {
case RadioDevice::NORMAL:
radio = new RadioInterface(usrp, 3, SAMPSPERSYM, false);
break;
case RadioDevice::RESAMP:
default:
LOG(ALERT) << "Unsupported configuration";
return EXIT_FAILURE;
}
int port = gConfig.getNum("TRX.Port");
const char *addr = gConfig.getStr("TRX.IP").c_str();
DriveLoop *drive = new DriveLoop(SAMPSPERSYM,GSM::Time(3,0),radio);
Transceiver *trx = new Transceiver(port, addr, SAMPSPERSYM, radio, drive, 0);
radio->activateChan(0);
/*
signalVector *gsmPulse = generateGSMPulse(2,1);
BitVector normalBurstSeg = "0000101010100111110010101010010110101110011000111001101010000";
BitVector normalBurst(BitVector(normalBurstSeg,gTrainingSequence[0]),normalBurstSeg);
signalVector *modBurst = modulateBurst(normalBurst,*gsmPulse,8,1);
signalVector *modBurst9 = modulateBurst(normalBurst,*gsmPulse,9,1);
signalVector *interpolationFilter = createLPF(0.6/mOversamplingRate,6*mOversamplingRate,1);
signalVector totalBurst1(*modBurst,*modBurst9);
signalVector totalBurst2(*modBurst,*modBurst);
signalVector totalBurst(totalBurst1,totalBurst2);
scaleVector(totalBurst,usrp->fullScaleInputValue());
double beaconFreq = -1.0*(numARFCN-1)*200e3;
signalVector finalVec(625*mOversamplingRate);
for (int j = 0; j < numARFCN; j++) {
signalVector *frequencyShifter = new signalVector(625*mOversamplingRate);
frequencyShifter->fill(1.0);
frequencyShift(frequencyShifter,frequencyShifter,2.0*M_PI*(beaconFreq+j*400e3)/(1625.0e3/6.0*mOversamplingRate));
signalVector *interpVec = polyphaseResampleVector(totalBurst,mOversamplingRate,1,interpolationFilter);
multVector(*interpVec,*frequencyShifter);
addVector(finalVec,*interpVec);
}
signalVector::iterator itr = finalVec.begin();
short finalVecShort[2*finalVec.size()];
short *shortItr = finalVecShort;
while (itr < finalVec.end()) {
*shortItr++ = (short) (itr->real());
*shortItr++ = (short) (itr->imag());
itr++;
}
usrp->loadBurst(finalVecShort,finalVec.size());
*/
trx->start();
while(!gbShutdown) { sleep(1); }//i++; if (i==60) break;}
cout << "Shutting down transceiver..." << endl;
trx->shutdown();
delete trx;
delete drive;
delete radio;
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -18,73 +18,27 @@
#include "Vector.h"
#include "Complex.h"
#include "BitVector.h"
#include "signalVector.h"
/** Indicated signalVector symmetry */
enum Symmetry {
NONE = 0,
ABSSYM = 1
};
/* Burst lengths */
#define NORMAL_BURST_NBITS 148
#define EDGE_BURST_NBITS 444
#define EDGE_BURST_NSYMS (EDGE_BURST_NBITS / 3)
/** Convolution type indicator */
enum ConvType {
FULL_SPAN = 0,
OVERLAP_ONLY = 1,
START_ONLY = 2,
WITH_TAIL = 3,
NO_DELAY = 4,
CUSTOM = 5,
UNDEFINED = 255
START_ONLY,
NO_DELAY,
CUSTOM,
UNDEFINED,
};
/** the core data structure of the Transceiver */
class signalVector: public Vector<complex>
{
private:
Symmetry symmetry; ///< the symmetry of the vector
bool realOnly; ///< true if vector is real-valued, not complex-valued
public:
/** Constructors */
signalVector(int dSize=0, Symmetry wSymmetry = NONE):
Vector<complex>(dSize),
realOnly(false)
{
symmetry = wSymmetry;
};
signalVector(complex* wData, size_t start,
size_t span, Symmetry wSymmetry = NONE):
Vector<complex>(NULL,wData+start,wData+start+span),
realOnly(false)
{
symmetry = wSymmetry;
};
signalVector(const signalVector &vec1, const signalVector &vec2):
Vector<complex>(vec1,vec2),
realOnly(false)
{
symmetry = vec1.symmetry;
};
signalVector(const signalVector &wVector):
Vector<complex>(wVector.size()),
realOnly(false)
{
wVector.copyTo(*this);
symmetry = wVector.getSymmetry();
};
/** symmetry operators */
Symmetry getSymmetry() const { return symmetry;};
void setSymmetry(Symmetry wSymmetry) { symmetry = wSymmetry;};
/** real-valued operators */
bool isRealOnly() const { return realOnly;};
void isRealOnly(bool wOnly) { realOnly = wOnly;};
enum signalError {
SIGERR_NONE,
SIGERR_BOUNDS,
SIGERR_CLIP,
SIGERR_UNSUPPORTED,
SIGERR_INTERNAL,
};
/** Convert a linear number to a dB value */
@@ -100,7 +54,7 @@ float vectorNorm2(const signalVector &x);
float vectorPower(const signalVector &x);
/** Setup the signal processing library */
void sigProcLibSetup(int samplesPerSymbol);
bool sigProcLibSetup();
/** Destroy the signal processing library */
void sigProcLibDestroy(void);
@@ -110,23 +64,12 @@ void sigProcLibDestroy(void);
@param a,b The vectors to be convolved.
@param c, A preallocated vector to hold the convolution result.
@param spanType The type/span of the convolution.
@return The convolution result.
@return The convolution result or NULL on error.
*/
signalVector* convolve(const signalVector *a,
const signalVector *b,
signalVector *c,
ConvType spanType,
unsigned startIx = 0,
unsigned len = 0);
/**
Generate the GSM pulse.
@param samplesPerSymbol The number of samples per GSM symbol.
@param symbolLength The size of the pulse.
@return The GSM pulse.
*/
signalVector* generateGSMPulse(int samplesPerSymbol,
int symbolLength);
signalVector *convolve(const signalVector *a, const signalVector *b,
signalVector *c, ConvType spanType,
size_t start = 0, size_t len = 0,
size_t step = 1, int offset = 0);
/**
Frequency shift a vector.
@@ -163,16 +106,33 @@ bool vectorSlicer(signalVector *x);
/** GMSK modulate a GSM burst of bits */
signalVector *modulateBurst(const BitVector &wBurst,
const signalVector &gsmPulse,
int guardPeriodLength,
int samplesPerSymbol);
int sps, bool emptyPulse = false);
/** 8-PSK modulate a burst of bits */
signalVector *modulateEdgeBurst(const BitVector &bits,
int sps, bool emptyPulse = false);
/** Generate a EDGE burst with random payload - 4 SPS (625 samples) only */
signalVector *generateEdgeBurst(int tsc);
/** Generate an empty burst - 4 or 1 SPS */
signalVector *generateEmptyBurst(int sps, int tn);
/** Generate a normal GSM burst with random payload - 4 or 1 SPS */
signalVector *genRandNormalBurst(int tsc, int sps, int tn);
/** Generate an access GSM burst with random payload - 4 or 1 SPS */
signalVector *genRandAccessBurst(int delay, int sps, int tn);
/** Generate a dummy GSM burst - 4 or 1 SPS */
signalVector *generateDummyBurst(int sps, int tn);
/** Sinc function */
float sinc(float x);
/** Delay a vector */
void delayVector(signalVector &wBurst,
float delay);
signalVector *delayVector(signalVector *in, signalVector *out, float delay);
/** Add two vectors in-place */
bool addVector(signalVector &x,
@@ -215,33 +175,6 @@ complex peakDetect(const signalVector &rxBurst,
void scaleVector(signalVector &x,
complex scale);
/**
Add a constant offset to a vecotr.
@param x The vector of interest.
@param offset The offset.
*/
void offsetVector(signalVector &x,
complex offset);
/**
Generate a modulated GSM midamble, stored within the library.
@param gsmPulse The GSM pulse used for modulation.
@param samplesPerSymbol The number of samples per GSM symbol.
@param TSC The training sequence [0..7]
@return Success.
*/
bool generateMidamble(signalVector &gsmPulse,
int samplesPerSymbol,
int TSC);
/**
Generate a modulated RACH sequence, stored within the library.
@param gsmPulse The GSM pulse used for modulation.
@param samplesPerSymbol The number of samples per GSM symbol.
@return Success.
*/
bool generateRACHSequence(signalVector &gsmPulse,
int samplesPerSymbol);
/**
Energy detector, checks to see if received burst energy is above a threshold.
@param rxBurst The received GSM burst of interest.
@@ -259,128 +192,97 @@ bool energyDetect(signalVector &rxBurst,
RACH correlator/detector.
@param rxBurst The received GSM burst of interest.
@param detectThreshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
@param samplesPerSymbol The number of samples per GSM symbol.
@param sps The number of samples per GSM symbol.
@param amplitude The estimated amplitude of received RACH burst.
@param TOA The estimate time-of-arrival of received RACH burst.
@return True if burst SNR is larger that the detectThreshold value.
@param maxTOA The maximum expected time-of-arrival
@return positive if threshold value is reached, negative on error, zero otherwise
*/
bool detectRACHBurst(signalVector &rxBurst,
float detectThreshold,
int samplesPerSymbol,
complex *amplitude,
float* TOA);
int detectRACHBurst(signalVector &rxBurst,
float detectThreshold,
int sps,
complex &amplitude,
float &TOA,
unsigned maxTOA);
/**
Normal burst correlator, detector, channel estimator.
@param rxBurst The received GSM burst of interest.
@param detectThreshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
@param samplesPerSymbol The number of samples per GSM symbol.
@param sps The number of samples per GSM symbol.
@param amplitude The estimated amplitude of received TSC burst.
@param TOA The estimate time-of-arrival of received TSC burst.
@param maxTOA The maximum expected time-of-arrival
@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 True if burst SNR is larger that the detectThreshold value.
@return positive if threshold value is reached, negative on error, zero otherwise
*/
bool analyzeTrafficBurst(signalVector &rxBurst,
unsigned TSC,
float detectThreshold,
int samplesPerSymbol,
complex *amplitude,
float *TOA,
unsigned maxTOA,
bool requestChannel = false,
signalVector** channelResponse = NULL,
float *channelResponseOffset = NULL);
int analyzeTrafficBurst(signalVector &rxBurst,
unsigned TSC,
float detectThreshold,
int sps,
complex &amplitude,
float &TOA,
unsigned maxTOA);
/**
EDGE burst detector
@param burst The received GSM burst of interest
@param detectThreshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
@param sps The number of samples per GSM symbol.
@param amplitude The estimated amplitude of received TSC burst.
@param TOA The estimate time-of-arrival of received TSC burst.
@param maxTOA The maximum expected time-of-arrival
@return positive if threshold value is reached, negative on error, zero otherwise
*/
int detectEdgeBurst(signalVector &burst,
unsigned TSC,
float detectThreshold,
int sps,
complex &amplitude,
float &TOA,
unsigned maxTOA);
/**
Downsample 4 SPS to 1 SPS using a polyphase filterbank
@param burst Input burst of at least 624 symbols
@return Decimated signal vector of 156 symbols
*/
signalVector *downsampleBurst(signalVector &burst);
/**
Decimate a vector.
@param wVector The vector of interest.
@param decimationFactor The amount of decimation, i.e. the decimation factor.
@param factor Decimation factor.
@return The decimated signal vector.
*/
signalVector *decimateVector(signalVector &wVector,
int decimationFactor);
signalVector *decimateVector(signalVector &wVector, size_t factor);
/**
Demodulates a received burst using a soft-slicer.
@param rxBurst The burst to be demodulated.
@param gsmPulse The GSM pulse.
@param samplesPerSymbol The number of samples per GSM symbol.
@param sps The number of samples per GSM symbol.
@param channel The amplitude estimate of the received burst.
@param TOA The time-of-arrival of the received burst.
@return The demodulated bit sequence.
*/
SoftVector *demodulateBurst(signalVector &rxBurst,
const signalVector &gsmPulse,
int samplesPerSymbol,
complex channel,
float TOA);
SoftVector *demodulateBurst(signalVector &rxBurst, int sps,
complex channel, float TOA);
/**
Creates a simple Kaiser-windowed low-pass FIR filter.
@param cutoffFreq The digital 3dB bandwidth of the filter.
@param filterLen The number of taps in the filter.
@param gainDC The DC gain of the filter.
@return The desired LPF
Demodulate 8-PSK EDGE burst with soft symbol ooutput
@param rxBurst The burst to be demodulated.
@param sps The number of samples per GSM symbol.
@param channel The amplitude estimate of the received burst.
@param TOA The time-of-arrival of the received burst.
@return The demodulated bit sequence.
*/
signalVector *createLPF(float cutoffFreq,
int filterLen,
float gainDC = 1.0);
/**
Change sampling rate of a vector via polyphase resampling.
@param wVector The vector to be resampled.
@param P The numerator, i.e. the amount of upsampling.
@param Q The denominator, i.e. the amount of downsampling.
@param LPF An optional low-pass filter used in the resampling process.
@return A vector resampled at P/Q of the original sampling rate.
*/
signalVector *polyphaseResampleVector(signalVector &wVector,
int P, int Q,
signalVector *LPF);
/**
Change the sampling rate of a vector via linear interpolation.
@param wVector The vector to be resampled.
@param expFactor Ratio of new sampling rate/original sampling rate.
@param endPoint ???
@return A vector resampled a expFactor*original sampling rate.
*/
signalVector *resampleVector(signalVector &wVector,
float expFactor,
complex endPoint);
/**
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 samplesPerSymbol 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 samplesPerSymbol,
signalVector &w,
signalVector &b);
SoftVector *demodEdgeBurst(signalVector &rxBurst, int sps,
complex channel, float TOA);
#endif /* SIGPROCLIB_H */

View File

@@ -1,170 +0,0 @@
/*
* Copyright 2011 Free Software Foundation, Inc.
* Copyright 2008, 2010 Kestrel Signal Processing, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*
Contributors:
Harvind S. Samra, hssamra@kestrelsp.com
*/
#include "sigProcLib.h"
//#include "radioInterface.h"
#include <Logger.h>
#include <Configuration.h>
#include <GSMCommon.h>
using namespace std;
using namespace GSM;
ConfigurationTable gConfig;
int main(int argc, char **argv) {
gLogInit("sigProcLibTest","DEBUG");
int samplesPerSymbol = 1;
int TSC = 2;
sigProcLibSetup(samplesPerSymbol);
signalVector *gsmPulse = generateGSMPulse(2,samplesPerSymbol);
cout << *gsmPulse << endl;
BitVector RACHBurstStart = "01010101";
BitVector RACHBurstRest = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
BitVector RACHBurst(BitVector(RACHBurstStart,gRACHSynchSequence),RACHBurstRest);
signalVector *RACHSeq = modulateBurst(RACHBurst,
*gsmPulse,
9,
samplesPerSymbol);
generateRACHSequence(*gsmPulse,samplesPerSymbol);
complex a; float t;
detectRACHBurst(*RACHSeq, 5, samplesPerSymbol,&a,&t);
//cout << *RACHSeq << endl;
//signalVector *autocorr = correlate(RACHSeq,RACHSeq,NULL,NO_DELAY);
//cout << *autocorr;
//exit(1);
/*signalVector x(6500);
x.fill(1.0);
frequencyShift(&x,&x,0.48*M_PI);
signalVector *y = polyphaseResampleVector(x,96,65,NULL);
cout << *y << endl;
exit(1);*/
//CommSig normalBurstSeg = "0000000000000000000000000000000000000000000000000000000000000";
BitVector normalBurstSeg = "0000101010100111110010101010010110101110011000111001101010000";
BitVector normalBurst(BitVector(normalBurstSeg,gTrainingSequence[TSC]),normalBurstSeg);
generateMidamble(*gsmPulse,samplesPerSymbol,TSC);
signalVector *modBurst = modulateBurst(normalBurst,*gsmPulse,
0,samplesPerSymbol);
//delayVector(*rsVector2,6.932);
complex ampl = 1;
float TOA = 0;
//modBurst = rsVector2;
//delayVector(*modBurst,0.8);
/*
signalVector channelResponse(4);
signalVector::iterator c=channelResponse.begin();
*c = (complex) 9000.0; c++;
*c = (complex) 0.4*9000.0; c++; c++;
*c = (complex) -1.2*0;
signalVector *guhBurst = convolve(modBurst,&channelResponse,NULL,NO_DELAY);
delete modBurst; modBurst = guhBurst;
*/
signalVector *chanResp;
/*
double noisePwr = 0.001/sqrtf(2);
signalVector *noise = gaussianNoise(modBurst->size(),noisePwr);
*/
float chanRespOffset;
analyzeTrafficBurst(*modBurst,TSC,8.0,samplesPerSymbol,&ampl,&TOA,1,true,&chanResp,&chanRespOffset);
//addVector(*modBurst,*noise);
cout << "ampl:" << ampl << endl;
cout << "TOA: " << TOA << endl;
//cout << "chanResp: " << *chanResp << endl;
SoftVector *demodBurst = demodulateBurst(*modBurst,*gsmPulse,samplesPerSymbol,(complex) ampl, TOA);
cout << *demodBurst << endl;
/*
COUT("chanResp: " << *chanResp);
signalVector *w,*b;
designDFE(*chanResp,1.0/noisePwr,7,&w,&b);
COUT("w: " << *w);
COUT("b: " << *b);
SoftSig *DFEBurst = equalizeBurst(*modBurst,TOA-chanRespOffset,samplesPerSymbol,*w,*b);
COUT("DFEBurst: " << *DFEBurst);
delete gsmPulse;
delete RACHSeq;
delete modBurst;
delete sendLPF;
delete rcvLPF;
delete rsVector;
//delete rsVector2;
delete autocorr;
delete chanResp;
delete noise;
delete demodBurst;
delete w;
delete b;
delete DFEBurst;
*/
sigProcLibDestroy();
}

View File

@@ -0,0 +1,81 @@
#include "signalVector.h"
signalVector::signalVector(size_t size)
: Vector<complex>(size),
real(false), aligned(false), symmetry(NONE)
{
}
signalVector::signalVector(size_t size, size_t start)
: Vector<complex>(size + start),
real(false), aligned(false), symmetry(NONE)
{
mStart = mData + start;
}
signalVector::signalVector(complex *data, size_t start, size_t span)
: Vector<complex>(NULL, data + start, data + start + span),
real(false), aligned(false), symmetry(NONE)
{
}
signalVector::signalVector(const signalVector &vector)
: Vector<complex>(vector.size() + vector.getStart()), aligned(false)
{
mStart = mData + vector.getStart();
vector.copyTo(*this);
symmetry = vector.getSymmetry();
real = vector.isReal();
};
signalVector::signalVector(const signalVector &vector,
size_t start, size_t tail)
: Vector<complex>(start + vector.size() + tail), aligned(false)
{
mStart = mData + start;
vector.copyTo(*this);
symmetry = vector.getSymmetry();
real = vector.isReal();
};
void signalVector::operator=(const signalVector& vector)
{
resize(vector.size() + vector.getStart());
memcpy(mData, vector.mData, bytes());
mStart = mData + vector.getStart();
}
size_t signalVector::getStart() const
{
return mStart - mData;
}
Symmetry signalVector::getSymmetry() const
{
return symmetry;
}
void signalVector::setSymmetry(Symmetry symmetry)
{
this->symmetry = symmetry;
}
bool signalVector::isReal() const
{
return real;
}
void signalVector::isReal(bool wOnly)
{
real = wOnly;
}
bool signalVector::isAligned() const
{
return aligned;
}
void signalVector::setAligned(bool aligned)
{
this->aligned = aligned;
}

View File

@@ -0,0 +1,51 @@
#ifndef _SIGNALVECTOR_H_
#define _SIGNALVECTOR_H_
#include <Vector.h>
#include <Complex.h>
/** Vector symmetry */
enum Symmetry {
NONE = 0,
ABSSYM = 1
};
class signalVector: public Vector<complex> {
public:
/** Default constructor */
signalVector(size_t size = 0);
/** Construct with head room */
signalVector(size_t size, size_t start);
/** Construct from existing buffer data (buffer not managed) */
signalVector(complex *data, size_t start, size_t span);
/** Construct by from existing vector */
signalVector(const signalVector &vector);
/** Construct by from existing vector and append head-tail room */
signalVector(const signalVector &vector, size_t start, size_t tail = 0);
/** Override base assignment operator to include start offsets */
void operator=(const signalVector& vector);
/** Return head room */
size_t getStart() const;
Symmetry getSymmetry() const;
void setSymmetry(Symmetry symmetry);
bool isReal() const;
void isReal(bool real);
bool isAligned() const;
void setAligned(bool aligned);
private:
bool real;
bool aligned;
Symmetry symmetry;
};
#endif /* _SIGNALVECTOR_H_ */

View File

@@ -0,0 +1,10 @@
if !ARCH_ARM
AM_CFLAGS = -Wall -std=gnu99 -march=native -I${srcdir}/../common
noinst_LTLIBRARIES = libarch.la
libarch_la_SOURCES = \
../common/convolve_base.c \
convert.c \
convolve.c
endif

View File

@@ -0,0 +1,201 @@
/*
* SSE type conversions
* Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
#include <string.h>
#include "convert.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_SSE3
#include <xmmintrin.h>
#include <emmintrin.h>
#ifdef HAVE_SSE4_1
#include <smmintrin.h>
/* 16*N 16-bit signed integer converted to single precision floats */
static void _sse_convert_si16_ps_16n(float *restrict out,
const short *restrict in,
int len)
{
__m128i m0, m1, m2, m3, m4, m5;
__m128 m6, m7, m8, m9;
for (int i = 0; i < len / 16; i++) {
/* Load (unaligned) packed floats */
m0 = _mm_loadu_si128((__m128i *) &in[16 * i + 0]);
m1 = _mm_loadu_si128((__m128i *) &in[16 * i + 8]);
/* Unpack */
m2 = _mm_cvtepi16_epi32(m0);
m4 = _mm_cvtepi16_epi32(m1);
m0 = _mm_shuffle_epi32(m0, _MM_SHUFFLE(1, 0, 3, 2));
m1 = _mm_shuffle_epi32(m1, _MM_SHUFFLE(1, 0, 3, 2));
m3 = _mm_cvtepi16_epi32(m0);
m5 = _mm_cvtepi16_epi32(m1);
/* Convert */
m6 = _mm_cvtepi32_ps(m2);
m7 = _mm_cvtepi32_ps(m3);
m8 = _mm_cvtepi32_ps(m4);
m9 = _mm_cvtepi32_ps(m5);
/* Store */
_mm_storeu_ps(&out[16 * i + 0], m6);
_mm_storeu_ps(&out[16 * i + 4], m7);
_mm_storeu_ps(&out[16 * i + 8], m8);
_mm_storeu_ps(&out[16 * i + 12], m9);
}
}
/* 16*N 16-bit signed integer conversion with remainder */
static void _sse_convert_si16_ps(float *restrict out,
const short *restrict in,
int len)
{
int start = len / 16 * 16;
_sse_convert_si16_ps_16n(out, in, len);
for (int i = 0; i < len % 16; i++)
out[start + i] = in[start + i];
}
#endif /* HAVE_SSE4_1 */
/* 8*N single precision floats scaled and converted to 16-bit signed integer */
static void _sse_convert_scale_ps_si16_8n(short *restrict out,
const float *restrict in,
float scale, int len)
{
__m128 m0, m1, m2;
__m128i m4, m5;
for (int i = 0; i < len / 8; i++) {
/* Load (unaligned) packed floats */
m0 = _mm_loadu_ps(&in[8 * i + 0]);
m1 = _mm_loadu_ps(&in[8 * i + 4]);
m2 = _mm_load1_ps(&scale);
/* Scale */
m0 = _mm_mul_ps(m0, m2);
m1 = _mm_mul_ps(m1, m2);
/* Convert */
m4 = _mm_cvtps_epi32(m0);
m5 = _mm_cvtps_epi32(m1);
/* Pack and store */
m5 = _mm_packs_epi32(m4, m5);
_mm_storeu_si128((__m128i *) &out[8 * i], m5);
}
}
/* 8*N single precision floats scaled and converted with remainder */
static void _sse_convert_scale_ps_si16(short *restrict out,
const float *restrict in,
float scale, int len)
{
int start = len / 8 * 8;
_sse_convert_scale_ps_si16_8n(out, in, scale, len);
for (int i = 0; i < len % 8; i++)
out[start + i] = in[start + i] * scale;
}
/* 16*N single precision floats scaled and converted to 16-bit signed integer */
static void _sse_convert_scale_ps_si16_16n(short *restrict out,
const float *restrict in,
float scale, int len)
{
__m128 m0, m1, m2, m3, m4;
__m128i m5, m6, m7, m8;
for (int i = 0; i < len / 16; i++) {
/* Load (unaligned) packed floats */
m0 = _mm_loadu_ps(&in[16 * i + 0]);
m1 = _mm_loadu_ps(&in[16 * i + 4]);
m2 = _mm_loadu_ps(&in[16 * i + 8]);
m3 = _mm_loadu_ps(&in[16 * i + 12]);
m4 = _mm_load1_ps(&scale);
/* Scale */
m0 = _mm_mul_ps(m0, m4);
m1 = _mm_mul_ps(m1, m4);
m2 = _mm_mul_ps(m2, m4);
m3 = _mm_mul_ps(m3, m4);
/* Convert */
m5 = _mm_cvtps_epi32(m0);
m6 = _mm_cvtps_epi32(m1);
m7 = _mm_cvtps_epi32(m2);
m8 = _mm_cvtps_epi32(m3);
/* Pack and store */
m5 = _mm_packs_epi32(m5, m6);
m7 = _mm_packs_epi32(m7, m8);
_mm_storeu_si128((__m128i *) &out[16 * i + 0], m5);
_mm_storeu_si128((__m128i *) &out[16 * i + 8], m7);
}
}
#else /* HAVE_SSE3 */
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;
}
#endif
#ifndef HAVE_SSE4_1
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, const float *in, float scale, int len)
{
#ifdef HAVE_SSE3
if (!(len % 16))
_sse_convert_scale_ps_si16_16n(out, in, scale, len);
else if (!(len % 8))
_sse_convert_scale_ps_si16_8n(out, in, scale, len);
else
_sse_convert_scale_ps_si16(out, in, scale, len);
#else
convert_scale_ps_si16(out, in, scale, len);
#endif
}
void convert_short_float(float *out, const short *in, int len)
{
#ifdef HAVE_SSE4_1
if (!(len % 16))
_sse_convert_si16_ps_16n(out, in, len);
else
_sse_convert_si16_ps(out, in, len);
#else
convert_si16_ps(out, in, len);
#endif
}

View File

@@ -0,0 +1,613 @@
/*
* SSE Convolution
* Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
#include <string.h>
#include <stdio.h>
#include "convolve.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
/* Forward declarations from base implementation */
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(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 bounds_check(int x_len, int h_len, int y_len,
int start, int len, int step);
#ifdef HAVE_SSE3
#include <xmmintrin.h>
#include <pmmintrin.h>
/* 4-tap SSE complex-real convolution */
static void sse_conv_real4(const float *restrict x,
const float *restrict h,
float *restrict y,
int len)
{
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
/* Load (aligned) filter taps */
m0 = _mm_load_ps(&h[0]);
m1 = _mm_load_ps(&h[4]);
m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
for (int i = 0; i < len; i++) {
/* Load (unaligned) input data */
m0 = _mm_loadu_ps(&x[2 * i + 0]);
m1 = _mm_loadu_ps(&x[2 * i + 4]);
m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
m3 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
/* Quad multiply */
m4 = _mm_mul_ps(m2, m7);
m5 = _mm_mul_ps(m3, m7);
/* Sum and store */
m6 = _mm_hadd_ps(m4, m5);
m0 = _mm_hadd_ps(m6, m6);
_mm_store_ss(&y[2 * i + 0], m0);
m0 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(0, 3, 2, 1));
_mm_store_ss(&y[2 * i + 1], m0);
}
}
/* 8-tap SSE complex-real convolution */
static void sse_conv_real8(const float *restrict x,
const float *restrict h,
float *restrict y,
int len)
{
__m128 m0, m1, m2, m3, m4, m5, m6, m7, m8, m9;
/* Load (aligned) filter taps */
m0 = _mm_load_ps(&h[0]);
m1 = _mm_load_ps(&h[4]);
m2 = _mm_load_ps(&h[8]);
m3 = _mm_load_ps(&h[12]);
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
m5 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
for (int i = 0; i < len; i++) {
/* Load (unaligned) input data */
m0 = _mm_loadu_ps(&x[2 * i + 0]);
m1 = _mm_loadu_ps(&x[2 * i + 4]);
m2 = _mm_loadu_ps(&x[2 * i + 8]);
m3 = _mm_loadu_ps(&x[2 * i + 12]);
m6 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
m8 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
m9 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
/* Quad multiply */
m6 = _mm_mul_ps(m6, m4);
m7 = _mm_mul_ps(m7, m4);
m8 = _mm_mul_ps(m8, m5);
m9 = _mm_mul_ps(m9, m5);
/* Sum and store */
m6 = _mm_add_ps(m6, m8);
m7 = _mm_add_ps(m7, m9);
m6 = _mm_hadd_ps(m6, m7);
m6 = _mm_hadd_ps(m6, m6);
_mm_store_ss(&y[2 * i + 0], m6);
m6 = _mm_shuffle_ps(m6, m6, _MM_SHUFFLE(0, 3, 2, 1));
_mm_store_ss(&y[2 * i + 1], m6);
}
}
/* 12-tap SSE complex-real convolution */
static void sse_conv_real12(const float *restrict x,
const float *restrict h,
float *restrict y,
int len)
{
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
__m128 m8, m9, m10, m11, m12, m13, m14;
/* Load (aligned) filter taps */
m0 = _mm_load_ps(&h[0]);
m1 = _mm_load_ps(&h[4]);
m2 = _mm_load_ps(&h[8]);
m3 = _mm_load_ps(&h[12]);
m4 = _mm_load_ps(&h[16]);
m5 = _mm_load_ps(&h[20]);
m12 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
m13 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
m14 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
for (int i = 0; i < len; i++) {
/* Load (unaligned) input data */
m0 = _mm_loadu_ps(&x[2 * i + 0]);
m1 = _mm_loadu_ps(&x[2 * i + 4]);
m2 = _mm_loadu_ps(&x[2 * i + 8]);
m3 = _mm_loadu_ps(&x[2 * i + 12]);
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
m0 = _mm_loadu_ps(&x[2 * i + 16]);
m1 = _mm_loadu_ps(&x[2 * i + 20]);
m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
/* Quad multiply */
m0 = _mm_mul_ps(m4, m12);
m1 = _mm_mul_ps(m5, m12);
m2 = _mm_mul_ps(m6, m13);
m3 = _mm_mul_ps(m7, m13);
m4 = _mm_mul_ps(m8, m14);
m5 = _mm_mul_ps(m9, m14);
/* Sum and store */
m8 = _mm_add_ps(m0, m2);
m9 = _mm_add_ps(m1, m3);
m10 = _mm_add_ps(m8, m4);
m11 = _mm_add_ps(m9, m5);
m2 = _mm_hadd_ps(m10, m11);
m3 = _mm_hadd_ps(m2, m2);
_mm_store_ss(&y[2 * i + 0], m3);
m3 = _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(0, 3, 2, 1));
_mm_store_ss(&y[2 * i + 1], m3);
}
}
/* 16-tap SSE complex-real convolution */
static void sse_conv_real16(const float *restrict x,
const float *restrict h,
float *restrict y,
int len)
{
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
__m128 m8, m9, m10, m11, m12, m13, m14, m15;
/* Load (aligned) filter taps */
m0 = _mm_load_ps(&h[0]);
m1 = _mm_load_ps(&h[4]);
m2 = _mm_load_ps(&h[8]);
m3 = _mm_load_ps(&h[12]);
m4 = _mm_load_ps(&h[16]);
m5 = _mm_load_ps(&h[20]);
m6 = _mm_load_ps(&h[24]);
m7 = _mm_load_ps(&h[28]);
m12 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
m13 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
m14 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
m15 = _mm_shuffle_ps(m6, m7, _MM_SHUFFLE(0, 2, 0, 2));
for (int i = 0; i < len; i++) {
/* Load (unaligned) input data */
m0 = _mm_loadu_ps(&x[2 * i + 0]);
m1 = _mm_loadu_ps(&x[2 * i + 4]);
m2 = _mm_loadu_ps(&x[2 * i + 8]);
m3 = _mm_loadu_ps(&x[2 * i + 12]);
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
m0 = _mm_loadu_ps(&x[2 * i + 16]);
m1 = _mm_loadu_ps(&x[2 * i + 20]);
m2 = _mm_loadu_ps(&x[2 * i + 24]);
m3 = _mm_loadu_ps(&x[2 * i + 28]);
m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
m10 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
m11 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
/* Quad multiply */
m0 = _mm_mul_ps(m4, m12);
m1 = _mm_mul_ps(m5, m12);
m2 = _mm_mul_ps(m6, m13);
m3 = _mm_mul_ps(m7, m13);
m4 = _mm_mul_ps(m8, m14);
m5 = _mm_mul_ps(m9, m14);
m6 = _mm_mul_ps(m10, m15);
m7 = _mm_mul_ps(m11, m15);
/* Sum and store */
m8 = _mm_add_ps(m0, m2);
m9 = _mm_add_ps(m1, m3);
m10 = _mm_add_ps(m4, m6);
m11 = _mm_add_ps(m5, m7);
m0 = _mm_add_ps(m8, m10);
m1 = _mm_add_ps(m9, m11);
m2 = _mm_hadd_ps(m0, m1);
m3 = _mm_hadd_ps(m2, m2);
_mm_store_ss(&y[2 * i + 0], m3);
m3 = _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(0, 3, 2, 1));
_mm_store_ss(&y[2 * i + 1], m3);
}
}
/* 20-tap SSE complex-real convolution */
static void sse_conv_real20(const float *restrict x,
const float *restrict h,
float *restrict y,
int len)
{
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
__m128 m8, m9, m11, m12, m13, m14, m15;
/* Load (aligned) filter taps */
m0 = _mm_load_ps(&h[0]);
m1 = _mm_load_ps(&h[4]);
m2 = _mm_load_ps(&h[8]);
m3 = _mm_load_ps(&h[12]);
m4 = _mm_load_ps(&h[16]);
m5 = _mm_load_ps(&h[20]);
m6 = _mm_load_ps(&h[24]);
m7 = _mm_load_ps(&h[28]);
m8 = _mm_load_ps(&h[32]);
m9 = _mm_load_ps(&h[36]);
m11 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
m12 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
m13 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
m14 = _mm_shuffle_ps(m6, m7, _MM_SHUFFLE(0, 2, 0, 2));
m15 = _mm_shuffle_ps(m8, m9, _MM_SHUFFLE(0, 2, 0, 2));
for (int i = 0; i < len; i++) {
/* Multiply-accumulate first 12 taps */
m0 = _mm_loadu_ps(&x[2 * i + 0]);
m1 = _mm_loadu_ps(&x[2 * i + 4]);
m2 = _mm_loadu_ps(&x[2 * i + 8]);
m3 = _mm_loadu_ps(&x[2 * i + 12]);
m4 = _mm_loadu_ps(&x[2 * i + 16]);
m5 = _mm_loadu_ps(&x[2 * i + 20]);
m6 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
m7 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
m8 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
m9 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
m0 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(0, 2, 0, 2));
m1 = _mm_shuffle_ps(m4, m5, _MM_SHUFFLE(1, 3, 1, 3));
m2 = _mm_mul_ps(m6, m11);
m3 = _mm_mul_ps(m7, m11);
m4 = _mm_mul_ps(m8, m12);
m5 = _mm_mul_ps(m9, m12);
m6 = _mm_mul_ps(m0, m13);
m7 = _mm_mul_ps(m1, m13);
m0 = _mm_add_ps(m2, m4);
m1 = _mm_add_ps(m3, m5);
m8 = _mm_add_ps(m0, m6);
m9 = _mm_add_ps(m1, m7);
/* Multiply-accumulate last 8 taps */
m0 = _mm_loadu_ps(&x[2 * i + 24]);
m1 = _mm_loadu_ps(&x[2 * i + 28]);
m2 = _mm_loadu_ps(&x[2 * i + 32]);
m3 = _mm_loadu_ps(&x[2 * i + 36]);
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
m0 = _mm_mul_ps(m4, m14);
m1 = _mm_mul_ps(m5, m14);
m2 = _mm_mul_ps(m6, m15);
m3 = _mm_mul_ps(m7, m15);
m4 = _mm_add_ps(m0, m2);
m5 = _mm_add_ps(m1, m3);
/* Final sum and store */
m0 = _mm_add_ps(m8, m4);
m1 = _mm_add_ps(m9, m5);
m2 = _mm_hadd_ps(m0, m1);
m3 = _mm_hadd_ps(m2, m2);
_mm_store_ss(&y[2 * i + 0], m3);
m3 = _mm_shuffle_ps(m3, m3, _MM_SHUFFLE(0, 3, 2, 1));
_mm_store_ss(&y[2 * i + 1], m3);
}
}
/* 4*N-tap SSE complex-real convolution */
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;
for (int i = 0; i < len; i++) {
/* Zero */
m6 = _mm_setzero_ps();
m7 = _mm_setzero_ps();
for (int n = 0; n < h_len / 4; n++) {
/* Load (aligned) filter taps */
m0 = _mm_load_ps(&h[8 * n + 0]);
m1 = _mm_load_ps(&h[8 * n + 4]);
m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
/* Load (unaligned) input data */
m0 = _mm_loadu_ps(&x[2 * i + 8 * n + 0]);
m1 = _mm_loadu_ps(&x[2 * i + 8 * n + 4]);
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
/* Quad multiply */
m0 = _mm_mul_ps(m2, m4);
m1 = _mm_mul_ps(m2, m5);
/* Accumulate */
m6 = _mm_add_ps(m6, m0);
m7 = _mm_add_ps(m7, m1);
}
m0 = _mm_hadd_ps(m6, m7);
m0 = _mm_hadd_ps(m0, m0);
_mm_store_ss(&y[2 * i + 0], m0);
m0 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(0, 3, 2, 1));
_mm_store_ss(&y[2 * i + 1], m0);
}
}
/* 4*N-tap SSE complex-complex convolution */
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;
for (int i = 0; i < len; i++) {
/* Zero */
m6 = _mm_setzero_ps();
m7 = _mm_setzero_ps();
for (int n = 0; n < h_len / 4; n++) {
/* Load (aligned) filter taps */
m0 = _mm_load_ps(&h[8 * n + 0]);
m1 = _mm_load_ps(&h[8 * n + 4]);
m2 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
m3 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
/* Load (unaligned) input data */
m0 = _mm_loadu_ps(&x[2 * i + 8 * n + 0]);
m1 = _mm_loadu_ps(&x[2 * i + 8 * n + 4]);
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
/* Quad multiply */
m0 = _mm_mul_ps(m2, m4);
m1 = _mm_mul_ps(m3, m5);
m2 = _mm_mul_ps(m2, m5);
m3 = _mm_mul_ps(m3, m4);
/* Sum */
m0 = _mm_sub_ps(m0, m1);
m2 = _mm_add_ps(m2, m3);
/* Accumulate */
m6 = _mm_add_ps(m6, m0);
m7 = _mm_add_ps(m7, m2);
}
m0 = _mm_hadd_ps(m6, m7);
m0 = _mm_hadd_ps(m0, m0);
_mm_store_ss(&y[2 * i + 0], m0);
m0 = _mm_shuffle_ps(m0, m0, _MM_SHUFFLE(0, 3, 2, 1));
_mm_store_ss(&y[2 * i + 1], m0);
}
}
/* 8*N-tap SSE complex-complex convolution */
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;
for (int i = 0; i < len; i++) {
/* Zero */
m12 = _mm_setzero_ps();
m13 = _mm_setzero_ps();
m14 = _mm_setzero_ps();
m15 = _mm_setzero_ps();
for (int n = 0; n < h_len / 8; n++) {
/* Load (aligned) filter taps */
m0 = _mm_load_ps(&h[16 * n + 0]);
m1 = _mm_load_ps(&h[16 * n + 4]);
m2 = _mm_load_ps(&h[16 * n + 8]);
m3 = _mm_load_ps(&h[16 * n + 12]);
m4 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
m5 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
m6 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
m7 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
/* Load (unaligned) input data */
m0 = _mm_loadu_ps(&x[2 * i + 16 * n + 0]);
m1 = _mm_loadu_ps(&x[2 * i + 16 * n + 4]);
m2 = _mm_loadu_ps(&x[2 * i + 16 * n + 8]);
m3 = _mm_loadu_ps(&x[2 * i + 16 * n + 12]);
m8 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(0, 2, 0, 2));
m9 = _mm_shuffle_ps(m0, m1, _MM_SHUFFLE(1, 3, 1, 3));
m10 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(0, 2, 0, 2));
m11 = _mm_shuffle_ps(m2, m3, _MM_SHUFFLE(1, 3, 1, 3));
/* Quad multiply */
m0 = _mm_mul_ps(m4, m8);
m1 = _mm_mul_ps(m5, m9);
m2 = _mm_mul_ps(m6, m10);
m3 = _mm_mul_ps(m7, m11);
m4 = _mm_mul_ps(m4, m9);
m5 = _mm_mul_ps(m5, m8);
m6 = _mm_mul_ps(m6, m11);
m7 = _mm_mul_ps(m7, m10);
/* Sum */
m0 = _mm_sub_ps(m0, m1);
m2 = _mm_sub_ps(m2, m3);
m4 = _mm_add_ps(m4, m5);
m6 = _mm_add_ps(m6, m7);
/* Accumulate */
m12 = _mm_add_ps(m12, m0);
m13 = _mm_add_ps(m13, m2);
m14 = _mm_add_ps(m14, m4);
m15 = _mm_add_ps(m15, m6);
}
m0 = _mm_add_ps(m12, m13);
m1 = _mm_add_ps(m14, m15);
m2 = _mm_hadd_ps(m0, m1);
m2 = _mm_hadd_ps(m2, m2);
_mm_store_ss(&y[2 * i + 0], m2);
m2 = _mm_shuffle_ps(m2, m2, _MM_SHUFFLE(0, 3, 2, 1));
_mm_store_ss(&y[2 * i + 1], m2);
}
}
#endif
/* API: Aligned complex-real */
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)(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;
memset(y, 0, len * 2 * sizeof(float));
#ifdef HAVE_SSE3
if (step <= 4) {
switch (h_len) {
case 4:
conv_func = sse_conv_real4;
break;
case 8:
conv_func = sse_conv_real8;
break;
case 12:
conv_func = sse_conv_real12;
break;
case 16:
conv_func = sse_conv_real16;
break;
case 20:
conv_func = sse_conv_real20;
break;
default:
if (!(h_len % 4))
conv_func_n = sse_conv_real4n;
}
}
#endif
if (conv_func) {
conv_func(&x[2 * (-(h_len - 1) + start)],
h, y, len);
} else if (conv_func_n) {
conv_func_n(&x[2 * (-(h_len - 1) + start)],
h, y, h_len, len);
} else {
_base_convolve_real(x, x_len,
h, h_len,
y, y_len,
start, len, step, offset);
}
return len;
}
/* API: Aligned complex-complex */
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)(const float *, const float *,
float *, int, int) = NULL;
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
return -1;
memset(y, 0, len * 2 * sizeof(float));
#ifdef HAVE_SSE3
if (step <= 4) {
if (!(h_len % 8))
conv_func = sse_conv_cmplx_8n;
else if (!(h_len % 4))
conv_func = sse_conv_cmplx_4n;
}
#endif
if (conv_func) {
conv_func(&x[2 * (-(h_len - 1) + start)],
h, y, h_len, len);
} else {
_base_convolve_complex(x, x_len,
h, h_len,
y, y_len,
start, len, step, offset);
}
return len;
}

View File

@@ -0,0 +1,72 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS])
#
# DESCRIPTION
#
# Check whether the given FLAG works with the current language's compiler
# or gives an error. (Warnings, however, are ignored)
#
# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on
# success/failure.
#
# If EXTRA-FLAGS is defined, it is added to the current language's default
# flags (e.g. CFLAGS) when the check is done. The check is thus made with
# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to
# force the compiler to issue an error when a bad flag is given.
#
# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this
# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG.
#
# LICENSE
#
# Copyright (c) 2008 Guido U. Draheim <guidod@gmx.de>
# Copyright (c) 2011 Maarten Bosmans <mkbosmans@gmail.com>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 2
AC_DEFUN([AX_CHECK_COMPILE_FLAG],
[AC_PREREQ(2.59)dnl for _AC_LANG_PREFIX
AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl
AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [
ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS
_AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1"
AC_COMPILE_IFELSE([AC_LANG_PROGRAM()],
[AS_VAR_SET(CACHEVAR,[yes])],
[AS_VAR_SET(CACHEVAR,[no])])
_AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags])
AS_IF([test x"AS_VAR_GET(CACHEVAR)" = xyes],
[m4_default([$2], :)],
[m4_default([$3], :)])
AS_VAR_POPDEF([CACHEVAR])dnl
])dnl AX_CHECK_COMPILE_FLAGS

221
config/ax_ext.m4 Normal file
View File

@@ -0,0 +1,221 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_ext.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_EXT
#
# DESCRIPTION
#
# Find supported SIMD extensions by requesting cpuid. When an SIMD
# extension is found, the -m"simdextensionname" is added to SIMD_FLAGS if
# compiler supports it. For example, if "sse2" is available, then "-msse2"
# is added to SIMD_FLAGS.
#
# This macro calls:
#
# AC_SUBST(SIMD_FLAGS)
#
# And defines:
#
# HAVE_MMX / HAVE_SSE / HAVE_SSE2 / HAVE_SSE3 / HAVE_SSSE3 / HAVE_SSE4.1 / HAVE_SSE4.2 / HAVE_AVX
#
# LICENSE
#
# Copyright (c) 2007 Christophe Tournayre <turn3r@users.sourceforge.net>
# Copyright (c) 2013 Michael Petch <mpetch@capp-sysware.com>
#
# Copying and distribution of this file, with or without modification, are
# permitted in any medium without royalty provided the copyright notice
# and this notice are preserved. This file is offered as-is, without any
# warranty.
#serial 12
AC_DEFUN([AX_EXT],
[
AC_REQUIRE([AC_CANONICAL_HOST])
case $host_cpu in
i[[3456]]86*|x86_64*|amd64*)
AC_REQUIRE([AX_GCC_X86_CPUID])
AC_REQUIRE([AX_GCC_X86_AVX_XGETBV])
AX_GCC_X86_CPUID(0x00000001)
ecx=`echo $ax_cv_gcc_x86_cpuid_0x00000001 | cut -d ":" -f 3`
edx=`echo $ax_cv_gcc_x86_cpuid_0x00000001 | cut -d ":" -f 4`
AC_CACHE_CHECK([whether mmx is supported], [ax_cv_have_mmx_ext],
[
ax_cv_have_mmx_ext=no
if test "$((0x$edx>>23&0x01))" = 1; then
ax_cv_have_mmx_ext=yes
fi
])
AC_CACHE_CHECK([whether sse is supported], [ax_cv_have_sse_ext],
[
ax_cv_have_sse_ext=no
if test "$((0x$edx>>25&0x01))" = 1; then
ax_cv_have_sse_ext=yes
fi
])
AC_CACHE_CHECK([whether sse2 is supported], [ax_cv_have_sse2_ext],
[
ax_cv_have_sse2_ext=no
if test "$((0x$edx>>26&0x01))" = 1; then
ax_cv_have_sse2_ext=yes
fi
])
AC_CACHE_CHECK([whether sse3 is supported], [ax_cv_have_sse3_ext],
[
ax_cv_have_sse3_ext=no
if test "$((0x$ecx&0x01))" = 1; then
ax_cv_have_sse3_ext=yes
fi
])
AC_CACHE_CHECK([whether ssse3 is supported], [ax_cv_have_ssse3_ext],
[
ax_cv_have_ssse3_ext=no
if test "$((0x$ecx>>9&0x01))" = 1; then
ax_cv_have_ssse3_ext=yes
fi
])
AC_CACHE_CHECK([whether sse4.1 is supported], [ax_cv_have_sse41_ext],
[
ax_cv_have_sse41_ext=no
if test "$((0x$ecx>>19&0x01))" = 1; then
ax_cv_have_sse41_ext=yes
fi
])
AC_CACHE_CHECK([whether sse4.2 is supported], [ax_cv_have_sse42_ext],
[
ax_cv_have_sse42_ext=no
if test "$((0x$ecx>>20&0x01))" = 1; then
ax_cv_have_sse42_ext=yes
fi
])
AC_CACHE_CHECK([whether avx is supported by processor], [ax_cv_have_avx_cpu_ext],
[
ax_cv_have_avx_cpu_ext=no
if test "$((0x$ecx>>28&0x01))" = 1; then
ax_cv_have_avx_cpu_ext=yes
fi
])
if test x"$ax_cv_have_avx_cpu_ext" = x"yes"; then
AX_GCC_X86_AVX_XGETBV(0x00000000)
xgetbv_eax="0"
if test x"$ax_cv_gcc_x86_avx_xgetbv_0x00000000" != x"unknown"; then
xgetbv_eax=`echo $ax_cv_gcc_x86_avx_xgetbv_0x00000000 | cut -d ":" -f 1`
fi
AC_CACHE_CHECK([whether avx is supported by operating system], [ax_cv_have_avx_ext],
[
ax_cv_have_avx_ext=no
if test "$((0x$ecx>>27&0x01))" = 1; then
if test "$((0x$xgetbv_eax&0x6))" = 6; then
ax_cv_have_avx_ext=yes
fi
fi
])
if test x"$ax_cv_have_avx_ext" = x"no"; then
AC_MSG_WARN([Your processor supports AVX, but your operating system doesn't])
fi
fi
if test "$ax_cv_have_mmx_ext" = yes; then
AX_CHECK_COMPILE_FLAG(-mmmx, ax_cv_support_mmx_ext=yes, [])
if test x"$ax_cv_support_mmx_ext" = x"yes"; then
SIMD_FLAGS="$SIMD_FLAGS -mmmx"
AC_DEFINE(HAVE_MMX,,[Support mmx instructions])
else
AC_MSG_WARN([Your processor supports mmx instructions but not your compiler, can you try another compiler?])
fi
fi
if test "$ax_cv_have_sse_ext" = yes; then
AX_CHECK_COMPILE_FLAG(-msse, ax_cv_support_sse_ext=yes, [])
if test x"$ax_cv_support_sse_ext" = x"yes"; then
SIMD_FLAGS="$SIMD_FLAGS -msse"
AC_DEFINE(HAVE_SSE,,[Support SSE (Streaming SIMD Extensions) instructions])
else
AC_MSG_WARN([Your processor supports sse instructions but not your compiler, can you try another compiler?])
fi
fi
if test "$ax_cv_have_sse2_ext" = yes; then
AX_CHECK_COMPILE_FLAG(-msse2, ax_cv_support_sse2_ext=yes, [])
if test x"$ax_cv_support_sse2_ext" = x"yes"; then
SIMD_FLAGS="$SIMD_FLAGS -msse2"
AC_DEFINE(HAVE_SSE2,,[Support SSE2 (Streaming SIMD Extensions 2) instructions])
else
AC_MSG_WARN([Your processor supports sse2 instructions but not your compiler, can you try another compiler?])
fi
fi
if test "$ax_cv_have_sse3_ext" = yes; then
AX_CHECK_COMPILE_FLAG(-msse3, ax_cv_support_sse3_ext=yes, [])
if test x"$ax_cv_support_sse3_ext" = x"yes"; then
SIMD_FLAGS="$SIMD_FLAGS -msse3"
AC_DEFINE(HAVE_SSE3,,[Support SSE3 (Streaming SIMD Extensions 3) instructions])
else
AC_MSG_WARN([Your processor supports sse3 instructions but not your compiler, can you try another compiler?])
fi
fi
if test "$ax_cv_have_ssse3_ext" = yes; then
AX_CHECK_COMPILE_FLAG(-mssse3, ax_cv_support_ssse3_ext=yes, [])
if test x"$ax_cv_support_ssse3_ext" = x"yes"; then
SIMD_FLAGS="$SIMD_FLAGS -mssse3"
AC_DEFINE(HAVE_SSSE3,,[Support SSSE3 (Supplemental Streaming SIMD Extensions 3) instructions])
else
AC_MSG_WARN([Your processor supports ssse3 instructions but not your compiler, can you try another compiler?])
fi
fi
if test "$ax_cv_have_sse41_ext" = yes; then
AX_CHECK_COMPILE_FLAG(-msse4.1, ax_cv_support_sse41_ext=yes, [])
if test x"$ax_cv_support_sse41_ext" = x"yes"; then
SIMD_FLAGS="$SIMD_FLAGS -msse4.1"
AC_DEFINE(HAVE_SSE4_1,,[Support SSSE4.1 (Streaming SIMD Extensions 4.1) instructions])
else
AC_MSG_WARN([Your processor supports sse4.1 instructions but not your compiler, can you try another compiler?])
fi
fi
if test "$ax_cv_have_sse42_ext" = yes; then
AX_CHECK_COMPILE_FLAG(-msse4.2, ax_cv_support_sse42_ext=yes, [])
if test x"$ax_cv_support_sse42_ext" = x"yes"; then
SIMD_FLAGS="$SIMD_FLAGS -msse4.2"
AC_DEFINE(HAVE_SSE4_2,,[Support SSSE4.2 (Streaming SIMD Extensions 4.2) instructions])
else
AC_MSG_WARN([Your processor supports sse4.2 instructions but not your compiler, can you try another compiler?])
fi
fi
if test "$ax_cv_have_avx_ext" = yes; then
AX_CHECK_COMPILE_FLAG(-mavx, ax_cv_support_avx_ext=yes, [])
if test x"$ax_cv_support_avx_ext" = x"yes"; then
SIMD_FLAGS="$SIMD_FLAGS -mavx"
AC_DEFINE(HAVE_AVX,,[Support AVX (Advanced Vector Extensions) instructions])
else
AC_MSG_WARN([Your processor supports avx instructions but not your compiler, can you try another compiler?])
fi
fi
;;
esac
AC_SUBST(SIMD_FLAGS)
])

View File

@@ -0,0 +1,79 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_gcc_x86_avx_xgetbv.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_GCC_X86_AVX_XGETBV
#
# DESCRIPTION
#
# On later x86 processors with AVX SIMD support, with gcc or a compiler
# that has a compatible syntax for inline assembly instructions, run a
# small program that executes the xgetbv instruction with input OP. This
# can be used to detect if the OS supports AVX instruction usage.
#
# On output, the values of the eax and edx registers are stored as
# hexadecimal strings as "eax:edx" in the cache variable
# ax_cv_gcc_x86_avx_xgetbv.
#
# If the xgetbv instruction fails (because you are running a
# cross-compiler, or because you are not using gcc, or because you are on
# a processor that doesn't have this instruction),
# ax_cv_gcc_x86_avx_xgetbv_OP is set to the string "unknown".
#
# This macro mainly exists to be used in AX_EXT.
#
# LICENSE
#
# Copyright (c) 2013 Michael Petch <mpetch@capp-sysware.com>
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 1
AC_DEFUN([AX_GCC_X86_AVX_XGETBV],
[AC_REQUIRE([AC_PROG_CC])
AC_LANG_PUSH([C])
AC_CACHE_CHECK(for x86-AVX xgetbv $1 output, ax_cv_gcc_x86_avx_xgetbv_$1,
[AC_RUN_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>], [
int op = $1, eax, edx;
FILE *f;
/* Opcodes for xgetbv */
__asm__(".byte 0x0f, 0x01, 0xd0"
: "=a" (eax), "=d" (edx)
: "c" (op));
f = fopen("conftest_xgetbv", "w"); if (!f) return 1;
fprintf(f, "%x:%x\n", eax, edx);
fclose(f);
return 0;
])],
[ax_cv_gcc_x86_avx_xgetbv_$1=`cat conftest_xgetbv`; rm -f conftest_xgetbv],
[ax_cv_gcc_x86_avx_xgetbv_$1=unknown; rm -f conftest_xgetbv],
[ax_cv_gcc_x86_avx_xgetbv_$1=unknown])])
AC_LANG_POP([C])
])

View File

@@ -0,0 +1,79 @@
# ===========================================================================
# http://www.gnu.org/software/autoconf-archive/ax_gcc_x86_cpuid.html
# ===========================================================================
#
# SYNOPSIS
#
# AX_GCC_X86_CPUID(OP)
#
# DESCRIPTION
#
# On Pentium and later x86 processors, with gcc or a compiler that has a
# compatible syntax for inline assembly instructions, run a small program
# that executes the cpuid instruction with input OP. This can be used to
# detect the CPU type.
#
# On output, the values of the eax, ebx, ecx, and edx registers are stored
# as hexadecimal strings as "eax:ebx:ecx:edx" in the cache variable
# ax_cv_gcc_x86_cpuid_OP.
#
# If the cpuid instruction fails (because you are running a
# cross-compiler, or because you are not using gcc, or because you are on
# a processor that doesn't have this instruction), ax_cv_gcc_x86_cpuid_OP
# is set to the string "unknown".
#
# This macro mainly exists to be used in AX_GCC_ARCHFLAG.
#
# LICENSE
#
# Copyright (c) 2008 Steven G. Johnson <stevenj@alum.mit.edu>
# Copyright (c) 2008 Matteo Frigo
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
# As a special exception, the respective Autoconf Macro's copyright owner
# gives unlimited permission to copy, distribute and modify the configure
# scripts that are the output of Autoconf when processing the Macro. You
# need not follow the terms of the GNU General Public License when using
# or distributing such scripts, even though portions of the text of the
# Macro appear in them. The GNU General Public License (GPL) does govern
# all other use of the material that constitutes the Autoconf Macro.
#
# This special exception to the GPL applies to versions of the Autoconf
# Macro released by the Autoconf Archive. When you make and distribute a
# modified version of the Autoconf Macro, you may extend this special
# exception to the GPL to apply to your modified version as well.
#serial 7
AC_DEFUN([AX_GCC_X86_CPUID],
[AC_REQUIRE([AC_PROG_CC])
AC_LANG_PUSH([C])
AC_CACHE_CHECK(for x86 cpuid $1 output, ax_cv_gcc_x86_cpuid_$1,
[AC_RUN_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>], [
int op = $1, eax, ebx, ecx, edx;
FILE *f;
__asm__("cpuid"
: "=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
: "a" (op));
f = fopen("conftest_cpuid", "w"); if (!f) return 1;
fprintf(f, "%x:%x:%x:%x\n", eax, ebx, ecx, edx);
fclose(f);
return 0;
])],
[ax_cv_gcc_x86_cpuid_$1=`cat conftest_cpuid`; rm -f conftest_cpuid],
[ax_cv_gcc_x86_cpuid_$1=unknown; rm -f conftest_cpuid],
[ax_cv_gcc_x86_cpuid_$1=unknown])])
AC_LANG_POP([C])
])

View File

@@ -22,13 +22,14 @@ AC_INIT(openbts,P2.8TRUNK)
AC_PREREQ(2.57)
AC_CONFIG_SRCDIR([Transceiver52M/Makefile.am])
AC_CONFIG_AUX_DIR([.])
AC_CONFIG_MACRO_DIR([config])
AM_CONFIG_HEADER(config.h)
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])])
@@ -62,53 +63,60 @@ AC_ARG_WITH(usrp1, [
[enable USRP1 gnuradio based transceiver])
])
AC_ARG_WITH(uhd, [
AS_HELP_STRING([--with-uhd],
[enable UHD based transceiver])
])
AC_ARG_WITH(singledb, [
AS_HELP_STRING([--with-singledb],
[enable single daughterboard use on USRP1])
])
AC_ARG_WITH(extref, [
AS_HELP_STRING([--with-extref],
[enable external reference on UHD devices])
AC_ARG_WITH(neon, [
AS_HELP_STRING([--with-neon],
[enable ARM NEON support])
])
AC_ARG_WITH(neon-vfpv4, [
AS_HELP_STRING([--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)
])
AS_IF([test "x$with_neon_vfpv4" = "xyes"], [
AC_DEFINE(HAVE_NEON, 1, Support ARM NEON)
AC_DEFINE(HAVE_NEON_FMA, 1, Support ARM NEON with FMA)
])
AS_IF([test "x$with_usrp1" = "xyes"], [
# Defines USRP_CFLAGS, USRP_INCLUDEDIR, and USRP_LIBS
PKG_CHECK_MODULES(USRP, usrp > 3.1)
# Check whether we have libusrp >= 3.2
PKG_CHECK_EXISTS(usrp >= 3.2, libusrp_3_2=yes, libusrp_3_2=no)
if test "x$libusrp_3_2" = "xyes";then
AC_DEFINE(HAVE_LIBUSRP_3_2, 1, Define to 1 if you have libusrp >= 3.2)
fi
# Check whether we have libusrp >= 3.3
PKG_CHECK_EXISTS(usrp >= 3.3, libusrp_3_3=yes, libusrp_3_3=no)
if test "x$libusrp_3_3" = "xyes";then
AC_DEFINE(HAVE_LIBUSRP_3_3, 1, Define to 1 if you have libusrp >= 3.3)
fi
PKG_CHECK_MODULES(USRP, usrp >= 3.3)
])
AS_IF([test "x$with_uhd" = "xyes"],[
PKG_CHECK_MODULES(UHD, uhd >= 003.004.000)
AC_DEFINE(USE_UHD, 1, Define to 1 if using UHD)
])
AS_IF([test "x$with_extref" = "xyes"], [
AC_DEFINE(EXTREF, 1, Define to 1 for external reference)
AS_IF([test "x$with_usrp1" != "xyes"],[
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"], [
AC_DEFINE(SINGLEDB, 1, Define to 1 for single daughterboard)
])
AM_CONDITIONAL(UHD, [test "x$with_uhd" = "xyes"])
AM_CONDITIONAL(USRP1, [test "x$with_usrp1" = "xyes"])
# Find and define supported SIMD extensions
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"])
AM_CONDITIONAL(ARCH_ARM_A15, [test "x$with_neon_vfpv4" = "xyes"])
# Defines LIBUSB_TRANSFER_CANCELLED, LIBUSB_TRANSFER_COMPLETED, LIBUSB_SUCCESS, LIBUSB_ERROR_*
PKG_CHECK_MODULES(LIBUSB, libusb-1.0)
dnl Output files
@@ -117,7 +125,9 @@ AC_CONFIG_FILES([\
CommonLibs/Makefile \
GSM/Makefile \
Transceiver52M/Makefile \
Transceiver52M/arm/Makefile \
Transceiver52M/x86/Makefile \
sqlite3/Makefile \
])
])
AC_OUTPUT

26
debian/changelog vendored Normal file
View File

@@ -0,0 +1,26 @@
osmo-trx (0.1.10~2) trusty; urgency=medium
* c88385c makefile: Fix build from an external path.
* 0479562 EDGE: Fix demodulation slicer input
* d2b0703 uhd: Correct timing alignment in 8-PSK and GMSK downlink bursts
-- Alexander Chemeris <Alexander.Chemeris@gmail.com> Sat, 30 Apr 2016 01:57:45 +0300
osmo-trx (0.1.10~1) trusty; urgency=medium
* some EDGE support in master
* fairwaves/rach-filler branch
-- Kirill Zakharenko <earwin@gmail.com> Sun, 27 Mar 2016 19:37:39 +0100
osmo-trx (0.1.9) trusty; urgency=medium
* Ask Ivan, really
-- Kirill Zakharenko <earwin@gmail.com> Thu, 16 Jul 2015 12:13:46 +0000
osmo-trx (0.1.8) precise; urgency=low
* Initial release (Closes: #nnnn) <nnnn is the bug number of your ITP
-- Ivan Klyuchnikov <Ivan.Kluchnikov@fairwaves.ru> Sun, 9 Mar 2014 14:10:10 +0400

1
debian/compat vendored Normal file
View File

@@ -0,0 +1 @@
9

24
debian/control vendored Normal file
View File

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

25
debian/copyright vendored Normal file
View File

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

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

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

15
debian/rules vendored Executable file
View File

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

1
debian/source/format vendored Normal file
View File

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

3
utils/clockdump.sh Executable file
View File

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