Compare commits

..

57 Commits

Author SHA1 Message Date
Alexander Chemeris
0832c0c34b Transceiver52M: HACK: Exit transceiver on POWEROFF command.
This hack allows OsmoBTS to correctly restart itself with different parameters.
We assume that transceiver is run as a service and will be restarted on exit.
2013-07-16 07:36:17 +04:00
Alexander Chemeris
9cddd4aa5d Transceiver52M: Start and stop all threads on POWERON/POWEROFF.
This change reveals some issue with the UHD start/stop code. Specifically,
we get a failed assert in RadioInterface::pullBuffer() (num_rd == OUTCHUNK
with num_rd=0) with the POWERON/POWEROFF/POWERON sequence.
2013-07-16 07:36:17 +04:00
Alexander Chemeris
f1c5b4f765 Transceiver52M: Introduce usage counting for DriveLoop and RadioInterface.
The reason is to allow them to be started from any TRX. I.e. they are started
when the first TRX starts and stopped when the last TRX stops.
2013-07-16 07:36:17 +04:00
Alexander Chemeris
19ae715e71 Transceiver52M: fixup for c5da660: We don't need to check for NULL with delete operator. 2013-07-16 07:33:57 +04:00
Alexander Chemeris
c4038ef54c CommonLibs: Adding a new ThreadsTest testsuite.
It's very basic at this moment. We should add a stress-test for thread
start/stop at least.
2013-07-14 23:11:54 +04:00
Alexander Chemeris
604f65e69f CommonLibs: Port InterthreadTest and SocketsTest to the new Thread API. 2013-07-14 23:11:54 +04:00
Alexander Chemeris
129ad76b15 Transceiver52M: Delay is no longer needed at the transceiver exit, threads should shut down cleanly. 2013-07-14 23:11:54 +04:00
Alexander Chemeris
de202e3435 Transceiver52M: Port all threads to the new Thread interface.
Also note that we introduced shutdown() method for Transceiver threads which
implement proper shutdown of threads when they are in blocking read state.
This involves using shutdown() on sockets and pushing NULL to queues.

With this change we should be able to start/stop transceiver channels at
arbitrary moments.
2013-07-14 23:11:54 +04:00
Alexander Chemeris
e279124660 CommonLibs: Rewrite Threads class to be predictable during start/stop.
Current implementation strictly synchronize thread startup and shutdown
to make sure a thread is actually started on startThread() exit and
that it's stopped on stopThread() exit (or an error is returned).

I also changed the Thread class to be used as a parent class for
an actual thread. This way we could provide much better logical
separation between variables used by different threads. Which will
(hopefully) reduce number of issues with inter-thread communications.
2013-07-14 23:11:54 +04:00
Alexander Chemeris
302a9198df CommonLibs: Signal::wait() should return the value which pthread_cond_timedwait() returns. 2013-07-14 23:11:54 +04:00
Alexander Chemeris
d4a8c1360e CommonLibs: Add shutdown() method to the DatagramSocket class.
This method is useful to abort a blocking operations on a socket during shutdown.
2013-07-14 23:11:54 +04:00
Alexander Chemeris
69b6a6dfcd CommonLibs: Allow NULLs to be retrieved from InterthreadQueue.
We need a way to stop InterthreadQueue blocking read to be able to shutdown
a thread. The easiest way to do that is to push NULL to the queue, but the
original implementation will just ignore that and continue blocking. After
the change the blocking read() will exit with NULL result which is perfectly
fine with us.

Ideally we should change all methods of InterthreadQueue to return a status
value to indicate normal exits, timeouts, etc. Right now the only way to
indicate an error is returning NULL, which could be a valid operation.
2013-07-14 23:11:54 +04:00
Alexander Chemeris
b864694652 CommonLibs: DEBUG: Mark interthread classes which are not used in OsmoTRX. 2013-07-14 23:11:54 +04:00
Alexander Chemeris
8eaa40dd6c Transceiver52M: Remove unused thread mAlignRadioServiceLoopThread; 2013-07-14 23:11:54 +04:00
Alexander Chemeris
dbd27a60b6 CommonLibs: Fix compile time warnings. 2013-07-14 14:59:00 +04:00
Alexander Chemeris
c5da6607b4 Transceiver52M: Fix crash in uhd_device destructor due to deleting statically allocated memory. 2013-07-14 14:59:00 +04:00
Thomas Tsou
42ade041d7 Transceiver52M: Setup independent gain and tune elements for dual channel 2013-07-14 13:37:03 +04:00
Thomas Tsou
5e18001bb0 Transceiver52M: Disable TSC check on slot setting 2013-07-14 13:36:55 +04:00
Thomas Tsou
621e52ab4a Transceiver52M: Disable dynamic filler table setting and fix deallocation 2013-07-14 13:36:42 +04:00
Alexander Chemeris
f86aa2c923 Transceiver52M: Fix build in the new repository. 2013-06-24 01:57:31 +04:00
Thomas Tsou
48f8fb34aa Transceiver52M: Setup UmTRX dual carrier support 2013-06-24 01:51:03 +04:00
Thomas Tsou
40c3d0a6d9 transceiver: mcbts: remove unused call in drive loop
Inside the drive loop addRadioVector() is duplicate call that was
not removed from the previous separation of the main loop and
transceiver instances.

Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2013-06-24 01:51:03 +04:00
Thomas Tsou
41c6657938 multi-arfcn: refactor to match upstream GSM core
This patch aligns the multicarrier (MC) USRP code with
released GSM core changes that accommodate the MC RAD1.
Primary changes are:

     1. Runtime setting of number of channelizer paths
     2. Matching channelizer path to ARFCN mapping of GSM core
     3. Use a single clock update socket on the drive loop
     4. Match transceiver data and control socket ports

Setting of channelizer paths (or width) was previously fixed
at compile time. In either case, channelizer width is limited
by the sample rate of the device and channel spacing of the
maximally decimated filterbank. Available settings are 1, 5,
and 10 channels, which accommodate any number of ARFCN's in
between. Also add the frequency offsets to handle the effective
shift in setting RF frequency.

Previous assumption was to place C0 at the center frequency,
but RAD1 assumes C0 at the leftmost carrier, so adjust
accordingly.

The rest is general consolidation to mostly match the RAD1
interaction with GSM core. There is some loss of flexibility to
run, say, multiple independent instances of OpenBTS through a
single bank of channelized transceivers. But, the better
compatibility and reduction in code is the appropriate tradeoff.

Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2013-06-24 01:51:02 +04:00
Thomas Tsou
cd576c9636 uhd: fix local overflow handling of buffer reads
This patches fixes the hypothetical bug in the read out of the
intermediate sample buffer after a local overflow condition.

Local overflows - occurring in the intermediate storage buffer
and not the UHD transport - should never occur; the existence
of a local overflow indicates a significant error elsewhere in
the system. For example, if timestamps or timing offsets are
ridiculously off, such errors may occur.

Nonetheless, handle errors anyways by taking the modulo value
of the calculated read index to stay within the buffer and avoid
seg faulting.

Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2013-06-24 01:51:02 +04:00
Thomas Tsou
3eeda4841d multi-arfcn, trx: create transceivers based on command line arg
Move from the hard coded case of 3 transceiver instances to a
command line determined value. 'M' potential channels will be
compiled into the build depending on preprocessor selections
in radioParams.h. The command line argument must be less M.

Channels are selected starting from 0, which is centered on the
RF tuning frequency. Subsequent channels are selected by shifting
outward from 0 (center) in a left before right pattern. Default
channel spacing is 400 kHz. The ordering for supported cases of
1, 5, and 10 path channelizers is as follows.

CHAN_M = 1

    { 0 }

CHAN_M = 5

    { 0, 1, 4, 2, 3 }

CHAN_M = 10

    { 0, 1, 9, 2, 8, 3, 7, 4, 6, 5}

Note: Channel 5 for the 10 channel case sits on the Nyquist
frequency and is therefor unusable.

Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2013-06-24 01:51:02 +04:00
Thomas Tsou
5d64491f9b transceiver, uhd: dynamically allocate async event thread
Similar to the previous commit titled,

"multi-arfcn, trx: allocate threads on heap and fix thread release"

there is the potential for a segfault on exit if the event thread
is never started. As before, address the issue by initializing
the Thread pointer with NULL and later allocating the object
immediately prior to use.

On stop or exit, allow the thread to exit by checking a condition
variable. If device is stopped or never started, the same variable
can be checked for state, which avoids attempts to deallocate an
empty pointer.

If there is a better method to shutdown / deallocate using the
OpenBTS thread library, please let me know.

Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2013-06-24 01:51:02 +04:00
Thomas Tsou
b5c450dfdf multi-arfcn, trx: attach FIFO's internally in transceiver
The original split-transceiver abstraction did not maintain
internal instances of the radio interface or drive loop.
The FIFO's were attached through external calls. The control
loop, however, made such an approach overly difficult, so
the transceiver now maintains pointers to the aforementioned
objects. In doing so, we no longer need external attachment
calls to setup the FIFO's.

Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2013-06-24 01:51:02 +04:00
Thomas Tsou
afb04f8b63 multi-arfcn, trx: fix infinite energy threshold bug
This fixes a bug where the energy threshold may reach infinity.

The transceiver energy detection threshold increase is
dependent on elapsed frames and the previous false detection
time. If we assume a (0,0) start time with the actual start
time - randomly determined - it's possible to get very
large elapsed frame counts at start. Once the threshold hits
'inf' further calculations are impossible and transceiver
is locked out from use.

Use the actual start time for initializing variables so
we avoid this scenario.

Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2013-06-24 01:51:02 +04:00
Thomas Tsou
5f13377b83 multi-arfcn, trx: handle thread exiting on shutdown
Previous approach was to allow stack unwinding to take care
shutdown and thread ending, which was unpredictable and
occasionally segfault. Attempt to shutdown more gracefully.

There are thread cancellation points in the transceiver code
using pthread_testcancel(), but the thread abstraction library
does not allow direct access to the pthread variables. This
prevents thread shutdown through pthread_cancel(). To get
around this, use boolean status values in the receive socket
service loops and main drive loop.

The socket read calls will block indefinitly, so shutdown
may cause the socket implementation to throw a SocketError
exception. Use of timeout values with reads does not seem to
work correctly or reliably, so catch the exception and ignore
if it occurs on shutdown.

The following error may appear as the socket is shutdown while
the Transceiver is blocking on read().

  DatagramSocket::read() failed: Bad file descriptor

So be it; the API doesn't allow us to do any more.

Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2013-06-24 01:51:02 +04:00
Thomas Tsou
a6ca73ca67 multi-arfcn, trx: allocate threads on heap and fix thread release
The underlying pthread of the Thread object isn't created until
Thread::start(). If the Thread object is contructed, but not
started, then the destructor will fail with a variety of
unpredictable errors such as the following or double free() in
certain cases.

Program received signal SIGSEGV, Segmentation fault.
__GI___libc_free (mem=0x3811abed3e946178) at malloc.c:2972
2972      if (chunk_is_mmapped(p))

If the Thread object is stack allocated, but start() isn't called,
destructor is guaranteed to run and will fail. The previous
approach was to dynamically allocate threads, but not free them,
thus avoiding memory errors, but creating memory leaks.

To get around this limitation, dynamically allocate Thread objects
and initialize with NULL. Then allocate immediately prior to start
such that pthread allocation is tied to the Thread object
constructor. Deallocation can check that the Thread pointer is
valid through NULL or other tracking methods.

Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2013-06-24 01:51:02 +04:00
Thomas Tsou
5a37840dfa multi-arfcn, trx: remove unused reset() call in drive loop
This call is a remnant of the Transceiver / DriveLoop split. The
empty call is never used.

Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2013-06-24 01:51:02 +04:00
Thomas Tsou
ca5d35cce8 multi-arfcn, trx: remove unused attach call()
At one point an attach() call was used to connect
multiple transceivers to the radio interface. The
current approach is to pass the radio interface to
the transceiver instances through the constructor.
Remove the unused and deprecated call.

Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2013-06-24 01:51:02 +04:00
Thomas Tsou
507e6d4e12 multi-arfcn, trx: only deallocate radio resouces if started
Certain notable variables - sample buffers - are not
allocated until start(), which causes a segfault if the
transceiver is shutdown without the radio starting. Check
that the radio is 'started' before releasing in the
destructor.

Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2013-06-24 01:51:02 +04:00
Thomas Tsou
1f330a9801 transceiver: define virtual destructor for base device
Lack of an explicitly defined virtual destructor was
causing the empty default interface destructor to be
called, which created a memory leak at shutdown.

Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2013-06-24 01:51:02 +04:00
Thomas Tsou
7c6f58af7a multi-arfcn, trx: add and modify transceiver main for new interfaces
Add a transceiver main() for multi-arfcn use and modify single
channel transceiver for use with updated interfaces.

Setup multiTRX with 3 channels for default case.

Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2013-06-24 01:51:02 +04:00
Thomas Tsou
996f426c16 multi-arfcn, trx: split transceiver to handle multiple channels
This patch separates the 'Transceiver' into a multi-channel
I/O component and single channel component. The latter may
may have multiple instances. The receive FIFO is converted to
a thread-safe queue.

The 'TransceiverIO' continuously drives the receive and transmit
loops. In this process, bursts are driven into thread-safe FIFO's
and read from the priority queues. Filler bursts are inserted if
no transmit data is available.

Each 'Transceiver' instance attaches to the I/O object and creates
its own threads and sockets, which include blocking on the receive
FIFO for the attached channel. Each instance also handles its own
control loop and clock indications.

Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2013-06-24 01:50:59 +04:00
Thomas Tsou
711e6afddf multi-arfcn, trx: modify radio interface for multi-channel use
The radio interface needs to feed the device I/O buffers
synchronously in order to drive the channelizer. Asynchronous
channel access occurs opposite the radio interface through
a bank of thread-safe FIFO's or priority queue's on receive
and transmit sides respectively.

Setup 'M' channels and allow only a subset to be active at a
given time. When a channel is unused, there is no need to
feed the particular receive FIFO or pull data from the
channel priority queue.

Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2013-06-24 01:46:34 +04:00
Thomas Tsou
222688d3dc multi-arfcn, trx: add header wrappers for radio interface
Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2013-06-24 01:46:34 +04:00
Thomas Tsou
20bc24d367 Transceiver52M: Add UmTRX support 2013-06-24 01:46:34 +04:00
Thomas Tsou
59796e1e3e Transceiver52M: Explicitly check for USRP2 device type
Before, we assumed that non-B100 device was implicitly a USRP2
(inclusive of N200/N210). Make the check explicit so that any
unknown device will causes an error and exit.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-06-24 01:46:34 +04:00
Thomas Tsou
19a506dffa 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-06-24 01:46:34 +04:00
Thomas Tsou
fbd6e1c985 Transceiver52M: Allow tolerance in UHD sample rate selection
We're performance floating point comparisons so allow a
10 Hz offset when UHD does not return an exact sample rate;

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-06-24 01:46:34 +04:00
Thomas Tsou
03669856b7 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.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-06-24 01:46:34 +04:00
Thomas Tsou
5d0e392b21 Transceiver52M: Set sample rate from within the radio device
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-06-24 01:46:34 +04:00
Thomas Tsou
801ce60d4a 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-06-24 01:46:34 +04:00
Thomas Tsou
ed64e799bc 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-06-24 01:46:34 +04:00
Thomas Tsou
d1bcab2731 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-06-24 01:46:34 +04:00
Thomas Tsou
00493f1a41 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-06-24 01:46:33 +04:00
Thomas Tsou
d565556b4b Transceiver52M: Setup test case for second UmTRX channel
Feed the second channel with the same data buffer as channel one.
The two channel send maintains the same UHD interface, so we use
the same metadata for both channels. Hard code the second channel
RF frequency as an offset relative to first channel for now.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-06-24 01:46:33 +04:00
Thomas Tsou
2b48784c61 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-06-24 01:46:33 +04:00
Thomas Tsou
635e34239c umtrx: set timing offset and clocking frequency
Measured offset and set to zero based on Nokia 3120 handset.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-06-24 01:46:33 +04:00
Alexander Chemeris
9e5e208b6e Transceier52M: 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.
2013-06-24 01:46:33 +04:00
Alexander Chemeris
8f47387777 Transceiver52M: Check for correctly set TSC before setting timeslot types. 2013-06-24 01:46:33 +04:00
Ivan Kluchnikov
c707f42396 Modified fillerTable usage in pushRadioVector function.
Now we put to fillerTable only frames of BEACON channels, all others frames in fillerTable are dummy bursts.
2013-06-24 01:46:33 +04:00
Thomas Tsou
00ed1441a1 umtrx: flush any possible garbage bursts at start
In certain cases (higher sample-per-symbol counts), there is
some sensitivity to either timeouts or bad metadata on the
first packet. The first packet sets the transceiver clock, so
this is essential. As a workaround, drop the first 50 packets
to guarantee that we get a packet with a valid timestamp

Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2013-06-24 01:46:10 +04:00
Thomas Tsou
8d804a4cd8 transceiver: workaround for transmit testing with no clock reset
Non-functional clock reset causes huge initial timing offset
between expected and received timestamps. Receive an initial
packet to 'set' the expected starting timestamp value for
both transmit and receive.

Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2013-06-24 01:46:10 +04:00
Thomas Tsou
82ede3e810 Transceiver52M: add antenna selection from configuration
Set optional transmit and receive antennas from database configuration
file. Use default antenna values on empty string or if option does not
exist.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
2013-06-24 01:45:04 +04:00
66 changed files with 4207 additions and 6834 deletions

View File

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

View File

@@ -1,5 +1,6 @@
/*
* 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.
@@ -41,6 +42,7 @@
//@{
// 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.
@@ -98,7 +100,7 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue {
{
ScopedLock lock(mLock);
T* retVal = (T*)mQ.get();
while (retVal==NULL) {
if (retVal==NULL) {
mWriteSignal.wait(mLock);
retVal = (T*)mQ.get();
}
@@ -155,6 +157,7 @@ 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 {
@@ -276,6 +279,7 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue2 {
// UNUSED in osmo-trx
/** Pointer FIFO for interthread operations. */
template <class T> class InterthreadQueueWithWait {
@@ -380,7 +384,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 {
@@ -644,7 +648,7 @@ template <class T, class C = std::vector<T*>, class Cmp = PointerCompare<T> > cl
// UNUSED in osmo-trx
class Semaphore {
private:

View File

@@ -1,5 +1,6 @@
/*
* 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.
@@ -27,66 +28,91 @@
#include "Threads.h"
#include "Interthread.h"
#include "Configuration.h"
#include <iostream>
using namespace std;
ConfigurationTable gConfig;
InterthreadQueue<int> gQ;
InterthreadMap<int,int> gMap;
void* qWriter(void*)
class QueueWriter : public Thread
{
int *p;
for (int i=0; i<20; i++) {
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);
}
p = new int;
*p = i;
COUT("queue write " << *p);
*p = -1;
gQ.write(p);
if (random()%2) sleep(1);
}
p = new int;
*p = -1;
gQ.write(p);
return NULL;
}
};
void* qReader(void*)
class QueueReader : public Thread
{
bool done = false;
while (!done) {
int *p = gQ.read();
COUT("queue read " << *p);
if (*p<0) done=true;
delete p;
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;
}
}
return NULL;
}
};
void* mapWriter(void*)
class MapWriter : public Thread
{
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;
}
public:
MapWriter() : Thread("MapWriter") {}
void* mapReader(void*)
{
for (int i=0; i<20; i++) {
int *p = gMap.read(i);
COUT("map read " << *p);
// InterthreadMap will delete the pointers
// delete p;
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);
}
}
return NULL;
}
};
class MapReader : public Thread
{
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
}
}
};
@@ -95,20 +121,25 @@ void* mapReader(void*)
int main(int argc, char *argv[])
{
Thread qReaderThread;
qReaderThread.start(qReader,NULL);
Thread mapReaderThread;
mapReaderThread.start(mapReader,NULL);
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 qWriterThread;
qWriterThread.start(qWriter,NULL);
Thread mapWriterThread;
mapWriterThread.start(mapWriter,NULL);
qReaderThread.join();
qWriterThread.join();
mapReaderThread.join();
mapWriterThread.join();
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();
}

View File

@@ -44,6 +44,7 @@ libcommon_la_SOURCES = \
Utils.cpp
noinst_PROGRAMS = \
ThreadsTest \
BitVectorTest \
InterthreadTest \
SocketsTest \
@@ -80,12 +81,16 @@ 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
InterthreadTest_LDADD = libcommon.la $(SQLITE_LA)
InterthreadTest_LDFLAGS = -lpthread
SocketsTest_SOURCES = SocketsTest.cpp
SocketsTest_LDADD = libcommon.la
SocketsTest_LDADD = libcommon.la $(SQLITE_LA)
SocketsTest_LDFLAGS = -lpthread
TimevalTest_SOURCES = TimevalTest.cpp

View File

@@ -1,5 +1,6 @@
/*
* 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.
@@ -129,6 +130,11 @@ void DatagramSocket::close()
::close(mSocketFD);
}
void DatagramSocket::shutdown()
{
::shutdown(mSocketFD, SHUT_RDWR);
}
DatagramSocket::~DatagramSocket()
{

View File

@@ -1,5 +1,6 @@
/*
* 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.
@@ -134,6 +135,11 @@ public:
/** Close the socket. */
void close();
/** Shutdown the socket without destroying the descriptor
* Use this to interrupt blocking read()
*/
void shutdown();
};

View File

@@ -1,5 +1,6 @@
/*
* 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,59 +29,73 @@
#include "Sockets.h"
#include "Threads.h"
#include <stdio.h>
#include "Configuration.h"
#include "Timeval.h"
#include <stdlib.h>
ConfigurationTable gConfig;
static const int gNumToSend = 10;
void *testReaderIP(void *)
class TestReaderIP : public Thread
{
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);
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);
}
}
}
return NULL;
}
};
void *testReaderUnix(void *)
class TestReaderUnix : public Thread
{
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);
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);
}
}
}
return NULL;
}
};
int main(int argc, char * argv[] )
{
Thread readerThreadIP;
readerThreadIP.start(testReaderIP,NULL);
Thread readerThreadUnix;
readerThreadUnix.start(testReaderUnix,NULL);
TestReaderIP readerThreadIP;
TestReaderUnix readerThreadUnix;
readerThreadIP.startThread();
readerThreadUnix.startThread();
UDPSocket socket1(5061, "127.0.0.1",5934);
UDDSocket socket1U("testSource","testDestination");
@@ -92,12 +107,10 @@ int main(int argc, char * argv[] )
for (int i=0; i<gNumToSend; i++) {
socket1.write("Hello IP land");
socket1U.write("Hello Unix domain");
sleep(1);
socket1U.write("Hello Unix domain");
msleep(1);
}
readerThreadIP.join();
readerThreadUnix.join();
}
// vim: ts=4 sw=4

View File

@@ -1,5 +1,6 @@
/*
* 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,11 +30,26 @@
#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.
@@ -95,27 +111,235 @@ Mutex::~Mutex()
/** Block for the signal up to the cancellation timeout. */
void Signal::wait(Mutex& wMutex, unsigned timeout) const
int Signal::wait(Mutex& wMutex, unsigned timeout) const
{
Timeval then(timeout);
struct timespec waitTime = then.timespec();
pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime);
return pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime);
}
void Thread::start(void *(*task)(void*), void *arg)
Thread::Thread(const string &name, size_t stackSize)
: mThreadId((pthread_t)0)
, mThreadName(name)
, mStackSize(stackSize)
, mThreadState(THREAD_STATE_IDLE)
, mThreadData(NULL)
{
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,5 +1,6 @@
/*
* 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.
@@ -121,14 +122,14 @@ class Signal {
Block for the signal up to the cancellation timeout.
Under Linux, spurious returns are possible.
*/
void wait(Mutex& wMutex, unsigned timeout) const;
int wait(Mutex& wMutex, unsigned timeout) const;
/**
Block for the signal.
Under Linux, spurious returns are possible.
*/
void wait(Mutex& wMutex) const
{ pthread_cond_wait(&mSignal,&wMutex.mMutex); }
int wait(Mutex& wMutex) const
{ return pthread_cond_wait(&mSignal,&wMutex.mMutex); }
void signal() { pthread_cond_signal(&mSignal); }
@@ -137,47 +138,105 @@ class Signal {
};
#define START_THREAD(thread,function,argument) \
thread.start((void *(*)(void*))function, (void*)argument);
/** A C++ wrapper for pthread threads. */
class Thread {
private:
public:
pthread_t mThread;
pthread_attr_t mAttrib;
// FIXME -- Can this be reduced now?
size_t mStackSize;
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).
};
public:
/** Create a thread in a non-running state. */
Thread(const std::string &name, size_t stackSize = (65536*4));
/** 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;
}
/** Destroy the Thread. */
virtual ~Thread();
/**
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); }
/** Start the thread. */
ReturnStatus startThread(void *data=NULL);
/** Stop the thread. */
ReturnStatus stopThread();
/** Start the thread on a task. */
void start(void *(*task)(void*), void *arg);
ThreadState getThreadState() const
{
ScopedLock lock(mThreadStateMutex);
return mThreadState;
}
/** Join a thread that will stop on its own. */
void join() { int s = pthread_join(mThread,NULL); assert(!s); mThread = 0; }
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;
}
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;
}
};
#endif
// vim: ts=4 sw=4

View File

@@ -0,0 +1,94 @@
/*
* 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

@@ -20,7 +20,6 @@
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,6 +18,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
#hack to get around symlink svn:externals in git -kurtis
top_srcdir = $(abs_top_srcdir)
top_builddir = $(abs_top_builddir)
@@ -25,6 +26,8 @@ COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs
GSM_INCLUDEDIR = $(top_srcdir)/GSM
SQLITE_INCLUDEDIR = $(top_srcdir)/sqlite3
SVNDEV = -D'SVN_REV="$(shell svnversion -n $(top_builddir))"'
STD_DEFINES_AND_INCLUDES = \
$(SVNDEV) \
-I$(COMMON_INCLUDEDIR) \

View File

@@ -0,0 +1,295 @@
/*
* 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")
, mUseCount(0)
, 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();
}
bool DriveLoop::start()
{
// Use count must not be negative
assert(mUseCount>=0);
mUseCount++;
if (mUseCount>1)
return false;
startThread();
return true;
}
bool DriveLoop::stop()
{
// Use count must not be negative or zero
assert(mUseCount>0);
mUseCount--;
if (mUseCount>0)
return false;
stopThread();
return true;
}
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();
}
}

195
Transceiver52M/DriveLoop.h Normal file
View File

@@ -0,0 +1,195 @@
/*
* 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 : private Thread {
private:
int mUseCount; ///< Use counter
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();
/** Increase usage counter and start the thread if not started yet */
bool start();
/** Decrease usage counter and stop the thread if no users left */
bool stop();
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

@@ -0,0 +1,146 @@
/*
* 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;};

132
Transceiver52M/DummyLoad.h Normal file
View File

@@ -0,0 +1,132 @@
/*
* 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,23 +21,20 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I./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_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
@@ -55,16 +52,16 @@ COMMON_SOURCES = \
radioVector.cpp \
radioClock.cpp \
sigProcLib.cpp \
signalVector.cpp \
Transceiver.cpp
DriveLoop.cpp \
Transceiver.cpp \
DummyLoad.cpp
libtransceiver_la_SOURCES = \
$(COMMON_SOURCES) \
Resampler.cpp \
radioInterfaceResamp.cpp \
radioInterfaceDiversity.cpp
$(COMMON_SOURCES)
bin_PROGRAMS = osmo-trx
noinst_PROGRAMS = \
transceiver \
sigProcLibTest
noinst_HEADERS = \
Complex.h \
@@ -73,26 +70,45 @@ noinst_HEADERS = \
radioClock.h \
radioDevice.h \
sigProcLib.h \
signalVector.h \
Transceiver.h \
USRPDevice.h \
Resampler.h \
common/convolve.h \
common/convert.h \
common/scale.h \
common/mult.h
DummyLoad.h \
rcvLPF_651.h \
sendLPF_961.h
osmo_trx_SOURCES = osmo-trx.cpp
osmo_trx_LDADD = \
transceiver_SOURCES = multiTRX.cpp
transceiver_LDADD = \
libtransceiver.la \
$(ARCH_LA) \
$(GSM_LA) \
$(COMMON_LA) $(SQLITE_LA)
if USRP1
libtransceiver_la_SOURCES += USRPDevice.cpp
osmo_trx_LDADD += $(USRP_LIBS)
else
sigProcLibTest_SOURCES = sigProcLibTest.cpp
sigProcLibTest_LDADD = \
libtransceiver.la \
$(GSM_LA) \
$(COMMON_LA) $(SQLITE_LA)
#uhd wins
if UHD
libtransceiver_la_SOURCES += UHDDevice.cpp
osmo_trx_LDADD += $(UHD_LIBS)
transceiver_LDADD += $(UHD_LIBS)
sigProcLibTest_LDADD += $(UHD_LIBS)
else
if USRP1
libtransceiver_la_SOURCES += USRPDevice.cpp
transceiver_LDADD += $(USRP_LIBS)
sigProcLibTest_LDADD += $(USRP_LIBS)
else
#we should never be here, as one of the above mustbe defined for us to build
endif
endif
MOSTLYCLEANFILES +=
#radioInterface.cpp
#ComplexTest.cpp
#sigProcLibTest.cpp
#sweepGenerator.cpp
#testRadio.cpp

View File

@@ -0,0 +1,15 @@
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

@@ -1,239 +0,0 @@
/*
* 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;
/* Insert history */
memcpy(&in[-2 * hist_len], history, 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 */
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;
}
Resampler::Resampler(size_t p, size_t q, size_t filt_len)
: in_index(NULL), out_path(NULL), partitions(NULL), history(NULL)
{
this->p = p;
this->q = q;
this->filt_len = filt_len;
}
Resampler::~Resampler()
{
releaseFilters();
delete history;
delete in_index;
delete out_path;
}

View File

@@ -1,77 +0,0 @@
/*
* 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();
private:
size_t p;
size_t q;
size_t filt_len;
size_t *in_index;
size_t *out_path;
float **partitions;
float *history;
bool initFilters(float bw);
void releaseFilters();
void computePath();
};
#endif /* _RESAMPLER_H_ */

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
* 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.
@@ -22,6 +23,14 @@
*/
/*
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"
@@ -30,248 +39,165 @@
#include <sys/types.h>
#include <sys/socket.h>
class Transceiver;
/** Define this to be the slot number to be logged. */
//#define TRANSMIT_LOGGING 1
/** Channel descriptor for transceiver object and channel number pair */
struct TransceiverChannel {
TransceiverChannel(Transceiver *trx, int num)
{
this->trx = trx;
this->num = num;
}
/** FIFO thread loop */
class FIFOServiceLoopThread : public Thread {
public:
FIFOServiceLoopThread() : Thread("FIFOServiceLoopThread") {}
Thread::ReturnStatus shutdown();
~TransceiverChannel()
{
}
Transceiver *trx;
size_t num;
protected:
virtual void runThread();
};
/** Internal transceiver state variables */
struct TransceiverState {
TransceiverState();
~TransceiverState();
/** control message handler thread loop */
class ControlServiceLoopThread : public Thread {
public:
ControlServiceLoopThread() : Thread("ControlServiceLoopThread") {}
Thread::ReturnStatus shutdown();
/* Initialize a multiframe slot in the filler table */
void init(size_t slot, signalVector *burst, bool fill);
protected:
virtual void runThread();
};
int chanType[8];
/** transmit queueing thread loop */
class TransmitPriorityQueueServiceLoopThread : public Thread {
public:
TransmitPriorityQueueServiceLoopThread() : Thread("TransmitPriorityQueueServiceLoopThread") {}
Thread::ReturnStatus shutdown();
/* 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;
protected:
virtual void runThread();
};
/** The Transceiver class, responsible for physical layer of basestation */
class Transceiver {
private:
int mBasePort;
std::string mAddr;
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
DriveLoop *mDriveLoop;
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
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<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
VectorQueue *mTransmitPriorityQueue; ///< priority queue of transmit bursts received from GSM core
VectorFIFO* mReceiveFIFO; ///< radioInterface FIFO of receive bursts
std::vector<Thread *> mRxServiceLoopThreads; ///< thread to pull bursts into receive FIFO
Thread *mRxLowerLoopThread; ///< thread to pull bursts into receive FIFO
Thread *mTxLowerLoopThread; ///< thread to push bursts into transmit FIFO
std::vector<Thread *> mControlServiceLoopThreads; ///< thread to process control messages from GSM core
std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core
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
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
int mChannel; ///< channelizer attach number between 0 and 'M-1'
RadioInterface *mRadioInterface; ///< associated radioInterface object
double txFullScale; ///< full scale input to radio
double rxFullScale; ///< full scale output to radio
/** Codes for burst types of received bursts*/
typedef enum {
OFF, ///< timeslot is off
TSC, ///< timeslot should contain a normal burst
RACH, ///< timeslot should contain an access burst
IDLE ///< timeslot is an idle (or dummy) burst
} CorrType;
/** unmodulate a modulated burst */
#ifdef TRANSMIT_LOGGING
void unModulateVector(signalVector wVector);
#endif
/** modulate and add a burst to the transmit queue */
void addRadioVector(size_t chan, BitVector &bits,
int RSSI, GSM::Time &wTime);
/** Update filler table */
void updateFillerTable(size_t chan, radioVector *burst);
void addRadioVector(BitVector &burst,
int RSSI,
GSM::Time &wTime);
/** Push modulated burst into transmit FIFO corresponding to a particular timestamp */
void pushRadioVector(GSM::Time &nowTime);
/** Pull and demodulate a burst from the receive FIFO */
SoftVector *pullRadioVector(GSM::Time &wTime, int &RSSI,
int &timingOffset, size_t chan = 0);
/** Set modulus for specific timeslot */
void setModulus(size_t timeslot, size_t chan);
/** return the expected burst type for the specified timestamp */
CorrType expectedCorrType(GSM::Time currTime, size_t chan);
/** Pull and demodulate a burst from the receive FIFO */
SoftVector *pullRadioVector(GSM::Time &wTime,
int &RSSI,
int &timingOffset);
/** send messages over the clock socket */
void writeClockInterface(void);
/** Detect RACH bursts */
bool detectRACH(TransceiverState *state,
signalVector &burst,
complex &amp, float &toa);
void pullFIFO(void); ///< blocking call on receive FIFO
/** Detect normal bursts */
bool detectTSC(TransceiverState *state,
signalVector &burst,
complex &amp, float &toa, GSM::Time &time);
signalVector *gsmPulse; ///< the GSM shaping pulse for modulation
/** Demodulat burst and output soft bits */
SoftVector *demodulate(TransceiverState *state,
signalVector &burst, complex amp,
float toa, size_t tn, bool equalize);
int mSPSTx; ///< number of samples per Tx symbol
int mSPSRx; ///< number of samples per Rx symbol
size_t mChans;
int mSamplesPerSymbol; ///< number of samples per GSM symbol
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
double mTxFreq; ///< the transmit frequency
double mRxFreq; ///< the receive frequency
double mFreqOffset; ///< RF frequency offset
int mPower; ///< the transmit power in dB
unsigned mTSC; ///< the midamble sequence code
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
std::vector<TransceiverState> mStates;
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
static int mTSC; ///< the midamble sequence code
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 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,
size_t wSPS, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface);
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();
bool init(bool filler);
/** 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;
/** shutdown (teardown threads) the Transceiver */
void shutdown();
protected:
/** drive lower receive I/O and burst generation */
void driveReceiveRadio();
/** drive demodulation of GSM bursts */
void driveReceiveFIFO(size_t chan);
/** drive reception and demodulation of GSM bursts */
void driveReceiveFIFO();
/** drive transmission of GSM bursts */
void driveTxFIFO();
void driveTransmitFIFO();
/** drive handling of control messages from GSM core */
void driveControl(size_t chan);
void driveControl();
/**
drive modulation and sorting of GSM bursts from GSM core
@return true if a burst was transferred successfully
*/
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 *);
bool driveTransmitPriorityQueue();
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(float prio = 0.5) { mRadioInterface->setPriority(prio); }
void setPriority() { mRadioInterface->setPriority(); }
};
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,28 +59,14 @@ const dboardConfigType dboardConfig = TXA_RXB;
const double USRPDevice::masterClockRate = 52.0e6;
USRPDevice::USRPDevice(size_t sps, size_t, bool)
USRPDevice::USRPDevice(int sps, bool skipRx)
: skipRx(skipRx)
{
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;
@@ -89,7 +75,7 @@ USRPDevice::USRPDevice(size_t sps, size_t, bool)
#endif
}
int USRPDevice::open(const std::string &, bool)
int USRPDevice::open(const std::string &)
{
writeLock.unlock();
@@ -100,13 +86,14 @@ int USRPDevice::open(const std::string &, bool)
m_uRx.reset();
if (!skipRx) {
try {
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 = 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->set_fpga_master_clock_freq(masterClockRate);
#endif
}
catch(...) {
LOG(ALERT) << "make failed on Rx";
m_uRx.reset();
@@ -123,12 +110,13 @@ int USRPDevice::open(const std::string &, bool)
}
try {
m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make(
0, decimRate * 2, 1, -1,
1024, 16 * 8, rbf));
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->set_fpga_master_clock_freq(masterClockRate);
#endif
}
catch(...) {
LOG(ALERT) << "make failed on Tx";
m_uTx.reset();
@@ -265,66 +253,49 @@ double USRPDevice::minRxGain()
return m_dbRx->gain_min();
}
double USRPDevice::setTxGain(double dB, size_t chan)
{
if (chan) {
LOG(ALERT) << "Invalid channel " << chan;
return 0.0;
}
double USRPDevice::setTxGain(double dB) {
writeLock.lock();
if (dB > maxTxGain()) dB = maxTxGain();
if (dB < minTxGain()) dB = minTxGain();
writeLock.lock();
if (dB > maxTxGain())
dB = maxTxGain();
if (dB < minTxGain())
dB = minTxGain();
LOG(NOTICE) << "Setting TX gain to " << dB << " dB.";
LOG(NOTICE) << "Setting TX gain to " << dB << " dB.";
if (!m_dbTx->set_gain(dB))
LOG(ERR) << "Error setting TX gain";
if (!m_dbTx->set_gain(dB))
LOG(ERR) << "Error setting TX gain";
writeLock.unlock();
return dB;
writeLock.unlock();
return dB;
}
double USRPDevice::setRxGain(double dB, size_t chan)
{
if (chan) {
LOG(ALERT) << "Invalid channel " << chan;
return 0.0;
}
double USRPDevice::setRxGain(double dB) {
dB = 47.0;
writeLock.lock();
if (dB > maxRxGain()) dB = maxRxGain();
if (dB < minRxGain()) dB = minRxGain();
LOG(NOTICE) << "Setting RX gain to " << dB << " 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;
if (!m_dbRx->set_gain(dB))
LOG(ERR) << "Error setting RX gain";
writeLock.unlock();
return dB;
}
// NOTE: Assumes sequential reads
int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
int USRPDevice::readSamples(short *buf, int len, bool *overrun,
TIMESTAMP timestamp,
bool *underrun,
unsigned *RSSI)
{
#ifndef SWLOOPBACK
if (!m_uRx)
return 0;
short *buf = bufs[0];
if (!m_uRx) return 0;
timestamp += timestampOffset;
if (timestamp + len < timeStart) {
@@ -370,7 +341,7 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, 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;
@@ -466,20 +437,17 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
#endif
}
int USRPDevice::writeSamples(std::vector<short *> &bufs, int len,
bool *underrun, unsigned long long timestamp,
bool isControl)
int USRPDevice::writeSamples(short *buf, int len, bool *underrun,
unsigned long long timestamp,
bool isControl)
{
writeLock.lock();
#ifndef SWLOOPBACK
if (!m_uTx)
return 0;
short *buf = bufs[0];
if (!m_uTx) return 0;
static uint32_t outData[128*20];
for (int i = 0; i < len*2; i++) {
buf[i] = host_to_usrp_short(buf[i]);
}
@@ -531,9 +499,7 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
uint32_t *wordPtr = (uint32_t *) data;
*wordPtr = host_to_usrp_u32(*wordPtr);
bool tmpUnderrun;
std::vector<short *> buf(1, data);
if (writeSamples(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) {
if (writeSamples((short *) data,1,&tmpUnderrun,timestamp & 0x0ffffffffll,true)) {
pingTimestamp = timestamp;
return true;
}
@@ -544,15 +510,10 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
}
#ifndef SWLOOPBACK
bool USRPDevice::setTxFreq(double wFreq, size_t chan)
bool USRPDevice::setTxFreq(double wFreq)
{
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
@@ -569,15 +530,10 @@ bool USRPDevice::setTxFreq(double wFreq, size_t chan)
}
}
bool USRPDevice::setRxFreq(double wFreq, size_t chan)
bool USRPDevice::setRxFreq(double wFreq)
{
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
@@ -600,7 +556,7 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;};
bool USRPDevice::setRxFreq(double wFreq) { return true;};
#endif
RadioDevice *RadioDevice::make(size_t sps, size_t chans, bool diversity)
RadioDevice *RadioDevice::make(int sps, bool skipRx)
{
return new USRPDevice(sps, chans, diversity);
return new USRPDevice(sps, skipRx);
}

View File

@@ -21,17 +21,29 @@
#include "radioDevice.h"
#include <usrp/usrp_standard.h>
#include <usrp/usrp_bytesex.h>
#include <usrp/usrp_prims.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 <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 {
@@ -48,7 +60,6 @@ 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
@@ -76,8 +87,7 @@ 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
long long pingOffset;
static const TIMESTAMP PINGOFFSET = 272; ///< undetermined delay b/w ping response timestamp and true receive timestamp
unsigned long hi32Timestamp;
unsigned long lastPktTimestamp;
@@ -93,13 +103,19 @@ 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(size_t sps, size_t chans = 1, bool diversity = false);
USRPDevice(int sps, bool skipRx);
/** Instantiate the USRP */
int open(const std::string &, bool);
int open(const std::string &);
/** Start the USRP */
bool start();
@@ -108,7 +124,7 @@ private:
bool stop();
/** Set priority not supported */
void setPriority(float prio = 0.5) { }
void setPriority() { return; }
enum TxWindowType getWindowType() { return TX_WINDOW_USRP1; }
@@ -122,9 +138,10 @@ private:
@param RSSI The received signal strength of the read result
@return The number of samples actually read
*/
int readSamples(std::vector<short *> &buf, int len, bool *overrun,
TIMESTAMP timestamp = 0xffffffff, bool *underrun = NULL,
unsigned *RSSI = NULL);
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.
@@ -134,17 +151,18 @@ private:
@param isControl Set if data is a control packet, e.g. a ping command
@return The number of samples actually written
*/
int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
TIMESTAMP timestamp = 0xffffffff, bool isControl = false);
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, size_t chan = 0);
bool setTxFreq(double wFreq);
/** Set the receiver frequency */
bool setRxFreq(double wFreq, size_t chan = 0);
bool setRxFreq(double wFreq);
/** Returns the starting write Timestamp*/
TIMESTAMP initialWriteTimestamp(void) { return 20000;}
@@ -159,10 +177,10 @@ private:
double fullScaleOutputValue() {return 9450.0;}
/** sets the receive chan gain, returns the gain setting **/
double setRxGain(double dB, size_t chan = 0);
double setRxGain(double dB);
/** get the current receive gain */
double getRxGain(size_t chan = 0) { return rxGain; }
double getRxGain(void) {return rxGain;}
/** return maximum Rx Gain **/
double maxRxGain(void);
@@ -171,7 +189,7 @@ private:
double minRxGain(void);
/** sets the transmit chan gain, returns the gain setting **/
double setTxGain(double dB, size_t chan = 0);
double setTxGain(double dB);
/** return maximum Tx Gain **/
double maxTxGain(void);
@@ -179,12 +197,13 @@ private:
/** return minimum Rx Gain **/
double minTxGain(void);
/** Return internal status values */
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 getTxFreq() { return 0;}
inline double getRxFreq() { return 0;}
inline double getSampleRate() {return actualSampleRate;}
inline double numberRead() { return samplesRead; }
inline double numberWritten() { return samplesWritten; }
inline double numberWritten() { return samplesWritten;}
};

View File

@@ -0,0 +1,95 @@
/*
* 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

@@ -1,23 +0,0 @@
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

@@ -1,96 +0,0 @@
/*
* 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 *, float *, float *, int);
void neon_convert_si16_ps_4n(float *, short *, int);
#ifndef HAVE_NEON
static void convert_si16_ps(float *out, short *in, int len)
{
for (int i = 0; i < len; i++)
out[i] = in[i];
}
static void convert_ps_si16(short *out, float *in, float scale, int len)
{
for (int i = 0; i < len; i++)
out[i] = in[i] * scale;
}
#else
/* 4*N 16-bit signed integer conversion with remainder */
static void neon_convert_si16_ps(float *restrict out,
short *restrict in,
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 *restrict out,
float *restrict in,
float *restrict 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, 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, 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

@@ -1,51 +0,0 @@
/*
* 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

@@ -1,139 +0,0 @@
/*
* 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

@@ -1,277 +0,0 @@
/*
* 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

View File

@@ -1,56 +0,0 @@
/*
* 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

@@ -1,42 +0,0 @@
/*
* 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

@@ -1,56 +0,0 @@
/*
* 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

@@ -1,50 +0,0 @@
/*
* 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

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

View File

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

View File

@@ -1,156 +0,0 @@
/*
* 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(float *x, float *h, float *y)
{
y[0] += x[0] * h[0];
y[1] += x[1] * h[0];
}
/* Base multiply and accumulate complex-complex */
static void mac_cmplx(float *x, float *h, float *y)
{
y[0] += x[0] * h[0] - x[1] * h[1];
y[1] += x[0] * h[1] + x[1] * h[0];
}
/* Base vector complex-complex multiply and accumulate */
static void mac_real_vec_n(float *x, float *h, float *y,
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(float *x, 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(float *x, int x_len,
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(float *x, int x_len,
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(float *x, int x_len,
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(float *x, int x_len,
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

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

View File

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

View File

@@ -1,83 +0,0 @@
%
% 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');

120
Transceiver52M/multiTRX.cpp Normal file
View File

@@ -0,0 +1,120 @@
/*
* 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;
}

View File

@@ -1,411 +0,0 @@
/*
* 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_SPS 1
#else
#define DEFAULT_SPS 4
#endif
/* 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 sps;
unsigned chans;
bool extref;
bool filler;
bool diversity;
double offset;
};
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;
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;
}
if (!config->sps)
config->sps = DEFAULT_SPS;
if (!config->chans)
config->chans = DEFAULT_CHANS;
/* Diversity only supported on 2 channels */
if (config->diversity)
config->chans = 2;
refstr = config->extref ? "Enabled" : "Disabled";
fillstr = config->filler ? "Enabled" : "Disabled";
divstr = config->diversity ? "Enabled" : "Disabled";
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 << " Samples-per-Symbol...... " << config->sps << 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;
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;
switch (type) {
case RadioDevice::NORMAL:
radio = new RadioInterface(usrp, config->sps, config->chans);
break;
case RadioDevice::RESAMP_64M:
case RadioDevice::RESAMP_100M:
radio = new RadioInterfaceResamp(usrp,
config->sps, config->chans);
break;
case RadioDevice::DIVERSITY:
radio = new RadioInterfaceDiversity(usrp,
config->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->sps,
config->chans, GSM::Time(3,0), radio);
if (!trx->init(config->filler)) {
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"
" -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",
"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->sps = 0;
config->chans = 0;
config->extref = false;
config->filler = false;
config->diversity = false;
config->offset = 0.0;
while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:")) != -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 = true;
break;
case 'o':
config->offset = atof(optarg);
break;
case 's':
config->sps = atoi(optarg);
if ((config->sps != 1) && (config->sps != 4)) {
printf("Unsupported samples-per-symbol\n\n");
print_help();
exit(0);
}
break;
default:
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.sps, config.chans,
config.diversity, config.offset);
type = usrp->open(config.dev_args, config.extref);
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;
trx->start();
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

@@ -16,7 +16,6 @@
#define __RADIO_DEVICE_H__
#include <string>
#include <vector>
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -35,15 +34,14 @@ class RadioDevice {
enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED };
/* Radio interface types */
enum RadioInterfaceType { NORMAL, RESAMP_64M, RESAMP_100M, DIVERSITY };
enum RadioInterfaceType { NORMAL, RESAMP };
static RadioDevice *make(size_t sps, size_t chans = 1,
bool diversity = false, double offset = 0.0);
static RadioDevice *make(int sps, bool skipRx = false);
virtual ~RadioDevice() {};
/** Initialize the USRP */
virtual int open(const std::string &args = "", bool extref = false)=0;
virtual ~RadioDevice() { }
virtual int open(const std::string &args)=0;
/** Start the USRP */
virtual bool start()=0;
@@ -55,7 +53,7 @@ class RadioDevice {
virtual enum TxWindowType getWindowType()=0;
/** Enable thread priority */
virtual void setPriority(float prio = 0.5) = 0;
virtual void setPriority()=0;
/**
Read samples from the radio.
@@ -67,9 +65,9 @@ class RadioDevice {
@param RSSI The received signal strength of the read result
@return The number of samples actually read
*/
virtual int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
TIMESTAMP timestamp = 0xffffffff, bool *underrun = 0,
unsigned *RSSI = 0) = 0;
virtual int readSamples(short **buf, int chans, int len, TIMESTAMP timestamp,
bool *overrun = NULL, bool *underrun = NULL,
unsigned *RSSI = NULL)=0;
/**
Write samples to the radio.
@param buf Contains the data to be written.
@@ -79,17 +77,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(std::vector<short *> &bufs, int len, bool *underrun,
TIMESTAMP timestamp, bool isControl = false) = 0;
virtual int writeSamples(short **buf, int chans, int len, TIMESTAMP timestamp,
bool *underrun = NULL, 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, size_t chan = 0) = 0;
virtual bool setTxFreq(double wFreq, int chan = 0)=0;
/** Set the receiver frequency */
virtual bool setRxFreq(double wFreq, size_t chan = 0) = 0;
virtual bool setRxFreq(double wFreq, int chan = 0)=0;
/** Returns the starting write Timestamp*/
virtual TIMESTAMP initialWriteTimestamp(void)=0;
@@ -104,10 +102,10 @@ class RadioDevice {
virtual double fullScaleOutputValue()=0;
/** sets the receive chan gain, returns the gain setting **/
virtual double setRxGain(double dB, size_t chan = 0) = 0;
virtual double setRxGain(double dB, int chan = 0)=0;
/** gets the current receive gain **/
virtual double getRxGain(size_t chan = 0) = 0;
virtual double getRxGain(int chan = 0)=0;
/** return maximum Rx Gain **/
virtual double maxRxGain(void) = 0;
@@ -116,7 +114,7 @@ class RadioDevice {
virtual double minRxGain(void) = 0;
/** sets the transmit chan gain, returns the gain setting **/
virtual double setTxGain(double dB, size_t chan = 0) = 0;
virtual double setTxGain(double dB, int chan = 0)=0;
/** return maximum Tx Gain **/
virtual double maxTxGain(void) = 0;
@@ -124,13 +122,18 @@ 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(size_t chan = 0) = 0;
virtual double getRxFreq(size_t chan = 0) = 0;
virtual double getTxFreq(int chan = 0)=0;
virtual double getRxFreq(int 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 Free Software Foundation, Inc.
* Copyright 2008, 2009, 2012 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,79 +23,58 @@
*/
#include "radioInterface.h"
#include "Resampler.h"
#include <Logger.h>
extern "C" {
#include "convert.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];
}
}
#define CHUNK 625
#define NUMCHUNKS 4
/* 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];
}
}
RadioInterface::RadioInterface(RadioDevice *wRadio,
size_t sps, size_t chans, size_t diversity,
int wReceiveOffset, GSM::Time wStartTime)
: mRadio(wRadio), mSPSTx(sps), mSPSRx(1), mChans(chans), mMIMO(diversity),
sendCursor(0), recvCursor(0), underrun(false), overrun(false),
receiveOffset(wReceiveOffset), mOn(false)
int wChanM,
int wSPS,
int wReceiveOffset,
GSM::Time wStartTime)
: mChanM(wChanM), underrun(false), sendCursor(0), rcvCursor(0), mOn(false),
mUseCount(0),
mRadio(wRadio), receiveOffset(wReceiveOffset), samplesPerSymbol(wSPS),
powerScaling(1.0), loadTest(false)
{
mClock.set(wStartTime);
}
RadioInterface::~RadioInterface(void)
{
close();
}
if (mOn) {
mRadio->stop();
close();
bool RadioInterface::init(int type)
{
if ((type != RadioDevice::NORMAL) || (mMIMO > 1) || !mChans) {
LOG(ALERT) << "Invalid configuration";
return false;
for (int i = 0; i < mChanM; i++) {
if (rcvBuffer[i] != NULL)
delete rcvBuffer[i];
if (sendBuffer[i] != NULL)
delete sendBuffer[i];
}
}
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) {
@@ -107,89 +86,121 @@ double RadioInterface::fullScaleOutputValue(void) {
}
void RadioInterface::setPowerAttenuation(double atten, size_t chan)
void RadioInterface::setPowerAttenuation(double atten, int chan)
{
double rfGain, digAtten;
if (chan >= mChans) {
LOG(ALERT) << "Invalid channel requested";
return;
}
rfGain = mRadio->setTxGain(mRadio->maxTxGain() - atten, chan);
digAtten = atten - mRadio->maxTxGain() + rfGain;
if (digAtten < 1.0)
powerScaling[chan] = 1.0;
powerScaling = 1.0;
else
powerScaling[chan] = 1.0 / sqrt(pow(10, digAtten / 10.0));
powerScaling = 1.0/sqrt(pow(10, (digAtten/10.0)));
}
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();
}
memcpy(retVector, wVector.begin(), wVector.size() * 2 * sizeof(float));
for (i = 0; i < wVector.size(); i++) {
retVector[2 * i + 0] = itr->real() * scale;
retVector[2 * i + 1] = itr->imag() * scale;
itr++;
}
return wVector.size();
}
int RadioInterface::unRadioifyVector(float *floatVector,
signalVector& newVector)
int RadioInterface::unRadioifyVector(float *floatVector, int offset,
signalVector &newVector)
{
int i;
signalVector::iterator itr = newVector.begin();
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]);
for (i = 0; i < newVector.size(); i++) {
*itr++ = Complex<float>(floatVector[offset + 2 * i + 0],
floatVector[offset + 2 * i + 1]);
}
return newVector.size();
}
bool RadioInterface::tuneTx(double freq, size_t chan)
bool RadioInterface::tuneTx(double freq, int chan)
{
return mRadio->setTxFreq(freq, chan);
}
bool RadioInterface::tuneRx(double freq, size_t chan)
bool RadioInterface::tuneRx(double freq, int chan)
{
return mRadio->setRxFreq(freq, chan);
}
void RadioInterface::start()
bool RadioInterface::start()
{
LOG(INFO) << "Starting radio";
#ifdef USRP1
mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter,
(void*)this);
#endif
mRadio->start();
writeTimestamp = mRadio->initialWriteTimestamp();
readTimestamp = mRadio->initialReadTimestamp();
// Use count must not be negative
assert(mUseCount>=0);
// Being on while mUseCount is 0 is a wrong condition
assert(!(mOn && mUseCount==0));
mRadio->updateAlignment(writeTimestamp-10000);
mRadio->updateAlignment(writeTimestamp-10000);
mUseCount++;
if (mOn || mUseCount>1)
return false;
mOn = true;
LOG(INFO) << "Radio started";
#ifdef USRP1
mAlignRadioServiceLoopThread = new Thread(32768);
mAlignRadioServiceLoopThread->start((void * (*)(void*))AlignRadioServiceLoopAdapter,
(void*)this);
#endif
writeTimestamp = mRadio->initialWriteTimestamp();
readTimestamp = mRadio->initialReadTimestamp();
for (int 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);
return true;
}
bool RadioInterface::stop()
{
// Use count must not be negative or zero
assert(mUseCount>0);
// Must be on while stopping
assert(mOn);
mUseCount--;
if (mUseCount>0)
return false;
mOn = false;
mRadio->stop();
return true;
}
#ifdef USRP1
void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
{
while (1) {
while (radioInterface->on()) {
radioInterface->alignRadio();
pthread_testcancel();
}
@@ -202,105 +213,89 @@ void RadioInterface::alignRadio() {
}
#endif
void RadioInterface::driveTransmitRadio(std::vector<signalVector *> &bursts,
std::vector<bool> &zeros)
void RadioInterface::driveTransmitRadio(signalVector **radioBurst, bool *zeroBurst)
{
int i;
if (!mOn)
return;
for (size_t i = 0; i < mChans; i++) {
radioifyVector(*bursts[i],
(float *) (sendBuffer[i]->begin() + sendCursor), zeros[i]);
for (i = 0; i < mChanM; i++) {
radioifyVector(*radioBurst[i], sendBuffer[i] + 2 * sendCursor,
powerScaling, zeroBurst[i]);
}
sendCursor += bursts[0]->size();
/*
* All bursts should be the same size since all transceivers are
* tied with a single clock in the radio interface.
*/
sendCursor += radioBurst[0]->size();
pushBuffer();
}
bool RadioInterface::driveReceiveRadio()
static inline void shiftRxBuffers(float **buf, int offset, int len, int chanM)
{
radioVector *burst = NULL;
for (int i = 0; i < chanM; i++)
memmove(buf[i], buf[i] + offset, sizeof(float) * len);
}
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 false;
return;
if (mReceiveFIFO[0].size() > 8)
return;
pullBuffer();
GSM::Time rcvClock = mClock.get();
rcvClock.decTN(receiveOffset);
unsigned tN = rcvClock.TN();
int recvSz = recvCursor;
int rcvSz = rcvCursor;
int readSz = 0;
const int symbolsPerSlot = gSlotLen + 8;
int burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
int samplesPerBurst = (symbolsPerSlot + (tN % 4 == 0)) * samplesPerSymbol;
/*
* 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;
// 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);
}
mClock.incTN();
rcvClock.incTN();
readSz += burstSize;
recvSz -= burstSize;
readSz += samplesPerBurst;
rcvSz -= samplesPerBurst;
tN = rcvClock.TN();
burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
samplesPerBurst = (symbolsPerSlot + (tN % 4 == 0)) * samplesPerSymbol;
}
if (readSz > 0) {
for (size_t i = 0; i < recvBuffer.size(); i++) {
memmove(recvBuffer[i]->begin(),
recvBuffer[i]->begin() + readSz,
(recvCursor - readSz) * 2 * sizeof(float));
}
recvCursor -= readSz;
rcvCursor -= readSz;
shiftRxBuffers(rcvBuffer, 2 * readSz, 2 * rcvCursor, mChanM);
}
return true;
}
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)
double RadioInterface::setRxGain(double dB, int chan)
{
if (mRadio)
return mRadio->setRxGain(dB, chan);
@@ -308,7 +303,7 @@ double RadioInterface::setRxGain(double dB, size_t chan)
return -1;
}
double RadioInterface::getRxGain(size_t chan)
double RadioInterface::getRxGain(int chan)
{
if (mRadio)
return mRadio->getRxGain(chan);
@@ -316,60 +311,56 @@ double RadioInterface::getRxGain(size_t chan)
return -1;
}
/* Receive a timestamped chunk from the device */
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 */
void RadioInterface::pullBuffer()
{
bool local_underrun;
int num_recv;
float *output;
if (recvCursor > recvBuffer[0]->size() - CHUNK)
return;
/* Read samples. Fail if we don't get what we want. */
int num_rd = mRadio->readSamples(rx_buf, mChanM, OUTCHUNK, readTimestamp);
/* 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;
}
for (size_t i = 0; i < mChans; i++) {
output = (float *) (recvBuffer[i]->begin() + recvCursor);
convert_short_float(output, convertRecvBuffer[i], 2 * num_recv);
}
LOG(DEBUG) << "Rx read " << num_rd << " samples from device";
assert(num_rd == OUTCHUNK);
underrun |= local_underrun;
readTimestamp += (TIMESTAMP) num_rd;
readTimestamp += num_recv;
recvCursor += num_recv;
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 */
/* Send timestamped chunk to the device with arbitrary size */
void RadioInterface::pushBuffer()
{
int num_sent;
if (sendCursor < CHUNK)
if (sendCursor < INCHUNK)
return;
if (sendCursor > sendBuffer[0]->size())
LOG(ALERT) << "Send buffer overflow";
for (int i = 0; i < mChanM; i++)
floatToShort(tx_buf[i], sendBuffer[i], sendCursor);
for (size_t i = 0; i < mChans; i++) {
convert_float_short(convertSendBuffer[i],
(float *) sendBuffer[i]->begin(),
powerScaling[i], 2 * sendCursor);
}
/* 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);
/* Send the all samples in the send buffer */
num_sent = mRadio->writeSamples(convertSendBuffer,
sendCursor,
&underrun,
writeTimestamp);
writeTimestamp += num_sent;
writeTimestamp += (TIMESTAMP) num_smpls;
sendCursor = 0;
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2008 Free Software Foundation, Inc.
* Copyright 2008, 2012 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,7 +12,8 @@
*/
#ifndef _RADIOINTEFACE_H_
#define _RADIOINTEFACE_H_
#include "sigProcLib.h"
#include "GSMCommon.h"
@@ -20,7 +21,13 @@
#include "radioDevice.h"
#include "radioVector.h"
#include "radioClock.h"
#include "Resampler.h"
/** samples per GSM symbol */
#define SAMPSPERSYM 1
#define INCHUNK (625)
#define OUTCHUNK (625)
#define CHAN_MAX 2
static const unsigned gSlotLen = 148; ///< number of symbols per slot, not counting guard periods
@@ -29,25 +36,18 @@ class RadioInterface {
protected:
Thread mAlignRadioServiceLoopThread; ///< thread that synchronizes transmit and receive sections
int mChanM; ///< channelizer width
std::vector<VectorFIFO> mReceiveFIFO; ///< FIFO that holds receive bursts
VectorFIFO mReceiveFIFO[CHAN_MAX]; ///< FIFO that holds receive bursts
RadioDevice *mRadio; ///< the USRP object
size_t mSPSTx;
size_t mSPSRx;
size_t mChans;
size_t mMIMO;
std::vector<signalVector *> sendBuffer;
std::vector<signalVector *> recvBuffer;
float *sendBuffer[CHAN_MAX];
unsigned sendCursor;
unsigned recvCursor;
std::vector<short *> convertRecvBuffer;
std::vector<short *> convertSendBuffer;
std::vector<float> powerScaling;
float *rcvBuffer[CHAN_MAX];
unsigned rcvCursor;
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,19 +55,30 @@ 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
int mUseCount; ///< Use counter
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, signalVector &wVector);
int unRadioifyVector(float *floatVector, int offset, signalVector &wVector);
/** push GSM bursts into the transmit buffer */
virtual void pushBuffer(void);
@@ -75,52 +86,60 @@ 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 */
void start();
/** Increase usage counter and start the interface if not started yet */
bool start();
/** Decrease usage counter and stop the interface if no users left */
bool stop();
/** intialization */
virtual bool init(int type);
virtual void close();
bool started() { return mOn; };
/** shutdown interface */
void close();
/** constructor */
RadioInterface(RadioDevice* wRadio = NULL,
size_t sps = 4, size_t chans = 1, size_t diversity = 1,
int receiveOffset = 3, GSM::Time wStartTime = GSM::Time(0));
RadioInterface(RadioDevice* wRadio,
int wChanM = 1,
int wSPS = SAMPSPERSYM,
int receiveOffset = 3,
GSM::Time wStartTime = GSM::Time(0, 0));
/** destructor */
virtual ~RadioInterface();
~RadioInterface();
/** check for underrun, resets underrun value */
bool isUnderrun();
void setSamplesPerSymbol(int wSamplesPerSymbol) {if (!mOn) samplesPerSymbol = wSamplesPerSymbol;}
int getSamplesPerSymbol() { return samplesPerSymbol;}
/** return the receive FIFO */
VectorFIFO* receiveFIFO(size_t chan = 0);
VectorFIFO* receiveFIFO(int num) { return &mReceiveFIFO[num];}
/** return the basestation clock */
RadioClock* getClock(void) { return &mClock;};
/** set transmit frequency */
bool tuneTx(double freq, size_t chan = 0);
bool tuneTx(double freq, int chan = 0);
/** set receive frequency */
virtual bool tuneRx(double freq, size_t chan = 0);
bool tuneRx(double freq, int chan = 0);
/** set receive gain */
double setRxGain(double dB, size_t chan = 0);
double setRxGain(double dB, int chan = 0);
/** get receive gain */
double getRxGain(size_t chan = 0);
double getRxGain(int chan = 0);
/** drive transmission of GSM bursts */
void driveTransmitRadio(std::vector<signalVector *> &bursts,
std::vector<bool> &zeros);
void driveTransmitRadio(signalVector **radioBurst, bool *zeroBurst);
/** drive reception of GSM bursts */
bool driveReceiveRadio();
void driveReceiveRadio();
void setPowerAttenuation(double atten, size_t chan = 0);
void setPowerAttenuation(double atten, int chan = 0);
/** returns the full-scale transmit amplitude **/
double fullScaleInputValue();
@@ -129,66 +148,10 @@ public:
double fullScaleOutputValue();
/** set thread priority on current thread */
void setPriority(float prio = 0.5) { mRadio->setPriority(prio); }
void setPriority() { mRadio->setPriority(); }
/** 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
};
#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();
};
#endif /* _RADIOINTEFACE_H_ */

View File

@@ -1,248 +0,0 @@
/*
* 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

@@ -1,260 +0,0 @@
/*
* 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 Free Software Foundation, Inc.
* Copyright 2011, 2012 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,24 +21,9 @@
#include "radioVector.h"
radioVector::radioVector(GSM::Time &time, size_t size,
size_t start, size_t chans)
: vectors(chans), mTime(time)
radioVector::radioVector(const signalVector& wVector, GSM::Time& wTime)
: signalVector(wVector), mTime(wTime)
{
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
@@ -56,52 +41,6 @@ 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 Free Software Foundation, Inc.
* Copyright 2011, 2012 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,38 +26,20 @@
#include "GSMCommon.h"
#include "Interthread.h"
class radioVector {
class radioVector : public signalVector {
public:
radioVector(GSM::Time& wTime, size_t size = 0,
size_t start = 0, size_t chans = 1);
radioVector(GSM::Time& wTime, signalVector *vector);
~radioVector();
radioVector(const signalVector& wVector, GSM::Time& wTime);
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 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 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

@@ -0,0 +1,164 @@
/*
* 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,14 +18,73 @@
#include "Vector.h"
#include "Complex.h"
#include "BitVector.h"
#include "signalVector.h"
/** Indicated signalVector symmetry */
enum Symmetry {
NONE = 0,
ABSSYM = 1
};
/** Convolution type indicator */
enum ConvType {
START_ONLY,
NO_DELAY,
CUSTOM,
UNDEFINED,
FULL_SPAN = 0,
OVERLAP_ONLY = 1,
START_ONLY = 2,
WITH_TAIL = 3,
NO_DELAY = 4,
CUSTOM = 5,
UNDEFINED = 255
};
/** 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;};
};
/** Convert a linear number to a dB value */
@@ -41,7 +100,7 @@ float vectorNorm2(const signalVector &x);
float vectorPower(const signalVector &x);
/** Setup the signal processing library */
bool sigProcLibSetup(int sps);
void sigProcLibSetup(int samplesPerSymbol);
/** Destroy the signal processing library */
void sigProcLibDestroy(void);
@@ -51,12 +110,23 @@ 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 or NULL on error.
@return The convolution result.
*/
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);
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);
/**
Frequency shift a vector.
@@ -93,14 +163,16 @@ bool vectorSlicer(signalVector *x);
/** GMSK modulate a GSM burst of bits */
signalVector *modulateBurst(const BitVector &wBurst,
const signalVector &gsmPulse,
int guardPeriodLength,
int sps, bool emptyPulse = false);
int samplesPerSymbol);
/** Sinc function */
float sinc(float x);
/** Delay a vector */
signalVector *delayVector(signalVector *in, signalVector *out, float delay);
void delayVector(signalVector &wBurst,
float delay);
/** Add two vectors in-place */
bool addVector(signalVector &x,
@@ -143,21 +215,32 @@ 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 sps The number of samples per GSM symbol.
@param samplesPerSymbol The number of samples per GSM symbol.
@param TSC The training sequence [0..7]
@return Success.
*/
bool generateMidamble(int sps, int tsc);
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 sps The number of samples per GSM symbol.
@param samplesPerSymbol The number of samples per GSM symbol.
@return Success.
*/
bool generateRACHSequence(int sps);
bool generateRACHSequence(signalVector &gsmPulse,
int samplesPerSymbol);
/**
Energy detector, checks to see if received burst energy is above a threshold.
@@ -176,61 +259,99 @@ 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 sps The number of samples per GSM symbol.
@param samplesPerSymbol 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 positive if threshold value is reached, negative on error, zero otherwise
@return True if burst SNR is larger that the detectThreshold value.
*/
int detectRACHBurst(signalVector &rxBurst,
float detectThreshold,
int sps,
complex *amplitude,
float* TOA);
bool detectRACHBurst(signalVector &rxBurst,
float detectThreshold,
int samplesPerSymbol,
complex *amplitude,
float* TOA);
/**
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 sps The number of samples per GSM symbol.
@param samplesPerSymbol 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 positive if threshold value is reached, negative on error, zero otherwise
@return True if burst SNR is larger that the detectThreshold value.
*/
int analyzeTrafficBurst(signalVector &rxBurst,
unsigned TSC,
float detectThreshold,
int sps,
complex *amplitude,
float *TOA,
unsigned maxTOA,
bool requestChannel = false,
signalVector** channelResponse = NULL,
float *channelResponseOffset = NULL);
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);
/**
Decimate a vector.
@param wVector The vector of interest.
@param factor Decimation factor.
@param decimationFactor The amount of decimation, i.e. the decimation factor.
@return The decimated signal vector.
*/
signalVector *decimateVector(signalVector &wVector, size_t factor);
signalVector *decimateVector(signalVector &wVector,
int decimationFactor);
/**
Demodulates a received burst using a soft-slicer.
@param rxBurst The burst to be demodulated.
@param gsmPulse The GSM pulse.
@param sps The number of samples per GSM symbol.
@param samplesPerSymbol 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, int sps,
complex channel, float TOA);
SoftVector *demodulateBurst(signalVector &rxBurst,
const signalVector &gsmPulse,
int samplesPerSymbol,
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
*/
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.
@@ -251,14 +372,14 @@ bool designDFE(signalVector &channelResponse,
Equalize/demodulate a received burst via a decision-feedback equalizer.
@param rxBurst The received burst to be demodulated.
@param TOA The time-of-arrival of the received burst.
@param sps The number of samples per GSM symbol.
@param 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 sps,
int samplesPerSymbol,
signalVector &w,
signalVector &b);

View File

@@ -0,0 +1,170 @@
/*
* 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

@@ -1,81 +0,0 @@
#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

@@ -1,51 +0,0 @@
#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

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

View File

@@ -1,200 +0,0 @@
/*
* 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,
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,
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,
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,
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,
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, 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, short *in, int len)
{
for (int i = 0; i < len; i++)
out[i] = in[i];
}
#endif
void convert_float_short(short *out, float *in, float scale, int len)
{
#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, 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

@@ -1,601 +0,0 @@
/*
* 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(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_SSE3
#include <xmmintrin.h>
#include <pmmintrin.h>
/* 4-tap SSE complex-real convolution */
static void sse_conv_real4(float *restrict x,
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(float *restrict x,
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(float *restrict x,
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(float *restrict x,
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(float *restrict x,
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(float *x, 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(float *x, 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(float *x, 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(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;
void (*conv_func_n)(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_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(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_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

@@ -1,72 +0,0 @@
# ===========================================================================
# 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

View File

@@ -1,221 +0,0 @@
# ===========================================================================
# 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

@@ -1,79 +0,0 @@
# ===========================================================================
# 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

@@ -1,79 +0,0 @@
# ===========================================================================
# 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,7 +22,6 @@ 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
@@ -63,50 +62,53 @@ 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(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])
])
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)
AC_ARG_WITH(extref, [
AS_HELP_STRING([--with-extref],
[enable external reference on UHD devices])
])
AS_IF([test "x$with_usrp1" = "xyes"], [
PKG_CHECK_MODULES(USRP, usrp >= 3.3)
# 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
])
AS_IF([test "x$with_usrp1" != "xyes"],[
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_singledb" = "xyes"], [
AC_DEFINE(SINGLEDB, 1, Define to 1 for single daughterboard)
])
# Find and define supported SIMD extensions
AX_EXT
AM_CONDITIONAL(UHD, [test "x$with_uhd" = "xyes"])
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
@@ -115,9 +117,7 @@ AC_CONFIG_FILES([\
CommonLibs/Makefile \
GSM/Makefile \
Transceiver52M/Makefile \
Transceiver52M/arm/Makefile \
Transceiver52M/x86/Makefile \
sqlite3/Makefile \
])
])
AC_OUTPUT