Compare commits

..

1 Commits

Author SHA1 Message Date
Ivan Kluchnikov
3605872e43 Transceiver52M: UHD: Exit on receive more than 100 timeout errors
Receiving timeout is not a fatal error and should be recoverable on network devices.
But for some reasons these errors are not always recoverable, in this case osmo-trx should be restarted.
2014-06-30 20:36:51 +04:00
60 changed files with 1497 additions and 4301 deletions

45
.gitignore vendored
View File

@@ -1,45 +0,0 @@
# build results
*.o
*.lo
*.la
Transceiver52M/osmo-trx
# tests
CommonLibs/BitVectorTest
CommonLibs/ConfigurationTest
CommonLibs/F16Test
CommonLibs/InterthreadTest
CommonLibs/LogTest
CommonLibs/RegexpTest
CommonLibs/SocketsTest
CommonLibs/TimevalTest
CommonLibs/URLEncodeTest
CommonLibs/VectorTest
# automake/autoconf
*.in
.deps
.libs
.dirstamp
*~
Makefile
config.log
config.status
config.h
config.guess
config.sub
config/*
configure
compile
aclocal.m4
autom4te.cache
depcomp
install-sh
libtool
ltmain.sh
missing
stamp-h1
INSTALL
# vim
*.sw?

View File

@@ -1,3 +0,0 @@
[gerrit]
host=gerrit.osmocom.org
project=osmo-trx

View File

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

View File

@@ -38,14 +38,6 @@
using namespace std;
// Switches to enable/disable logging targets
// MUST BE DEFINED BEFORE gConfig FOR gLogEarly() TO WORK CORRECTLY
bool gLogToConsole = true;
bool gLogToSyslog = false;
FILE *gLogToFile = NULL;
Mutex gLogToLock;
// Reference to a global config table, used all over the system.
extern ConfigurationTable gConfig;
@@ -75,6 +67,9 @@ const char *levelNames[] = {
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
};
int numLevels = 8;
bool gLogToConsole = 0;
FILE *gLogToFile = NULL;
Mutex gLogToLock;
int levelStringToInt(const string& name)
@@ -197,20 +192,18 @@ Log::~Log()
if (mDummyInit) return;
// Anything at or above LOG_CRIT is an "alarm".
// Save alarms in the local list and echo them to stderr.
if (mPriority <= LOG_ERR) {
if (mPriority <= LOG_CRIT) {
if (sLoggerInited) addAlarm(mStream.str().c_str());
cerr << mStream.str() << endl;
}
// Current logging level was already checked by the macro. So just log.
// Log to syslog
if (gLogToSyslog) {
syslog(mPriority, "%s", mStream.str().c_str());
}
// Log to file and console
// Current logging level was already checked by the macro.
// So just log.
syslog(mPriority, "%s", mStream.str().c_str());
// pat added for easy debugging.
if (gLogToConsole||gLogToFile) {
int mlen = mStream.str().size();
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
ScopedLock lock(gLogToLock);
gLogToLock.lock();
if (gLogToConsole) {
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
// so just use std::cout.
@@ -222,6 +215,7 @@ Log::~Log()
if (neednl) {fputc('\n',gLogToFile);}
fflush(gLogToFile);
}
gLogToLock.unlock();
}
}
@@ -249,9 +243,10 @@ void gLogInit(const char* name, const char* level, int facility)
gConfig.set("Log.Level",level);
}
// Pat added, tired of the syslog facility.
// Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file:
string str = gConfig.getStr("Log.File");
if (gLogToFile==NULL && str.length() && 0==strncmp(gCmdName,"Open",4)) {
if (gLogToFile==0 && str.length() && 0==strncmp(gCmdName,"Open",4)) {
const char *fn = str.c_str();
if (fn && *fn && strlen(fn)>3) { // strlen because a garbage char is getting in sometimes.
gLogToFile = fopen(fn,"w"); // New log file each time we start.
@@ -273,32 +268,9 @@ void gLogInit(const char* name, const char* level, int facility)
void gLogEarly(int level, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
if (gLogToSyslog) {
va_list args_copy;
va_copy(args_copy, args);
vsyslog(level | LOG_USER, fmt, args_copy);
va_end(args_copy);
}
if (gLogToConsole) {
va_list args_copy;
va_copy(args_copy, args);
vprintf(fmt, args_copy);
printf("\n");
va_end(args_copy);
}
if (gLogToFile) {
va_list args_copy;
va_copy(args_copy, args);
vfprintf(gLogToFile, fmt, args_copy);
fprintf(gLogToFile, "\n");
va_end(args_copy);
}
vsyslog(level | LOG_USER, fmt, args);
va_end(args);
}

View File

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

View File

@@ -187,20 +187,24 @@ int DatagramSocket::send(const struct sockaddr* dest, const char * message)
return send(dest,message,length);
}
int DatagramSocket::read(char* buffer, size_t length)
{
socklen_t addr_len = sizeof(mSource);
int rd_length = recvfrom(mSocketFD, (void *) buffer, length, 0,
(struct sockaddr*) &mSource, &addr_len);
if ((rd_length==-1) && (errno!=EAGAIN)) {
int DatagramSocket::read(char* buffer)
{
socklen_t temp_len = sizeof(mSource);
int length = recvfrom(mSocketFD, (void*)buffer, MAX_UDP_LENGTH, 0,
(struct sockaddr*)&mSource,&temp_len);
if ((length==-1) && (errno!=EAGAIN)) {
perror("DatagramSocket::read() failed");
throw SocketError();
}
return rd_length;
return length;
}
int DatagramSocket::read(char* buffer, size_t length, unsigned timeout)
int DatagramSocket::read(char* buffer, unsigned timeout)
{
fd_set fds;
FD_ZERO(&fds);
@@ -214,7 +218,7 @@ int DatagramSocket::read(char* buffer, size_t length, unsigned timeout)
throw SocketError();
}
if (sel==0) return -1;
if (FD_ISSET(mSocketFD,&fds)) return read(buffer, length);
if (FD_ISSET(mSocketFD,&fds)) return read(buffer);
return -1;
}
@@ -265,7 +269,7 @@ void UDPSocket::open(unsigned short localPort)
size_t length = sizeof(address);
bzero(&address,length);
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(localPort);
if (bind(mSocketFD,(struct sockaddr*)&address,length)<0) {
perror("bind() failed");

View File

@@ -108,7 +108,7 @@ public:
@param buffer A char[MAX_UDP_LENGTH] procured by the caller.
@return The number of bytes received or -1 on non-blocking pass.
*/
int read(char* buffer, size_t length);
int read(char* buffer);
/**
Receive a packet with a timeout.
@@ -116,7 +116,7 @@ public:
@param maximum wait time in milliseconds
@return The number of bytes received or -1 on timeout.
*/
int read(char* buffer, size_t length, unsigned timeout);
int read(char* buffer, unsigned timeout);
/** Send a packet to a given destination, other than the default. */

View File

@@ -42,7 +42,7 @@ void *testReaderIP(void *)
int rc = 0;
while (rc<gNumToSend) {
char buf[MAX_UDP_LENGTH];
int count = readSocket.read(buf, MAX_UDP_LENGTH);
int count = readSocket.read(buf);
if (count>0) {
COUT("read: " << buf);
rc++;
@@ -62,7 +62,7 @@ void *testReaderUnix(void *)
int rc = 0;
while (rc<gNumToSend) {
char buf[MAX_UDP_LENGTH];
int count = readSocket.read(buf, MAX_UDP_LENGTH);
int count = readSocket.read(buf);
if (count>0) {
COUT("read: " << buf);
rc++;

View File

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

View File

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

View File

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

0
INSTALL Normal file
View File

260
README
View File

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

View File

@@ -1,108 +0,0 @@
/*
* Polyphase channelizer
*
* Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
* Copyright (C) 2015 Ettus Research LLC
*
* 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 <stdlib.h>
#include <math.h>
#include <assert.h>
#include <string.h>
#include <cstdio>
#include "Logger.h"
#include "Channelizer.h"
extern "C" {
#include "common/fft.h"
#include "common/convolve.h"
}
static void deinterleave(const float *in, size_t ilen,
float **out, size_t olen, size_t m)
{
size_t i, n;
for (i = 0; i < olen; i++) {
for (n = 0; n < m; n++) {
out[m - 1 - n][2 * i + 0] = in[2 * (i * m + n) + 0];
out[m - 1 - n][2 * i + 1] = in[2 * (i * m + n) + 1];
}
}
}
size_t Channelizer::inputLen() const
{
return blockLen * m;
}
size_t Channelizer::outputLen() const
{
return blockLen;
}
float *Channelizer::outputBuffer(size_t chan) const
{
if (chan >= m)
return NULL;
return hInputs[chan];
}
/*
* Implementation based on material found in:
*
* "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
* Prentice Hall, 2006."
*/
bool Channelizer::rotate(const float *in, size_t len)
{
size_t hSize = 2 * hLen * sizeof(float);
if (!checkLen(blockLen, len))
return false;
deinterleave(in, len, hInputs, blockLen, m);
/*
* Convolve through filterbank while applying and saving sample history
*/
for (size_t i = 0; i < m; i++) {
memcpy(&hInputs[i][2 * -hLen], hist[i], hSize);
memcpy(hist[i], &hInputs[i][2 * (blockLen - hLen)], hSize);
convolve_real(hInputs[i], blockLen,
subFilters[i], hLen,
hOutputs[i], blockLen,
0, blockLen, 1, 0);
}
cxvec_fft(fftHandle);
return true;
}
/* Setup channelizer paramaters */
Channelizer::Channelizer(size_t m, size_t blockLen, size_t hLen)
: ChannelizerBase(m, blockLen, hLen)
{
}
Channelizer::~Channelizer()
{
}

View File

@@ -1,34 +0,0 @@
#ifndef _CHANNELIZER_RX_H_
#define _CHANNELIZER_RX_H_
#include "ChannelizerBase.h"
class Channelizer : public ChannelizerBase {
public:
/** Constructor for channelizing filter bank
@param m number of physical channels
@param blockLen number of samples per output of each iteration
@param hLen number of taps in each constituent filter path
*/
Channelizer(size_t m, size_t blockLen, size_t hLen = 16);
~Channelizer();
/* Return required input and output buffer lengths */
size_t inputLen() const;
size_t outputLen() const;
/** Rotate "input commutator" and drive samples through filterbank
@param in complex input vector
@param iLen number of samples in buffer (must match block length)
@return false on error and true otherwise
*/
bool rotate(const float *in, size_t iLen);
/** Get buffer for an output path
@param chan channel number of filterbank
@return NULL on error and pointer to buffer otherwise
*/
float *outputBuffer(size_t chan) const;
};
#endif /* _CHANNELIZER_RX_H_ */

View File

@@ -1,251 +0,0 @@
/*
* Polyphase channelizer
*
* Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
* Copyright (C) 2015 Ettus Research LLC
*
* 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 <malloc.h>
#include <math.h>
#include <assert.h>
#include <string.h>
#include <cstdio>
#include "Logger.h"
#include "ChannelizerBase.h"
extern "C" {
#include "common/fft.h"
}
static float sinc(float x)
{
if (x == 0.0f)
return 0.999999999999f;
return sin(M_PI * x) / (M_PI * x);
}
/*
* There are more efficient reversal algorithms, but we only reverse at
* initialization so we don't care.
*/
static void reverse(float *buf, size_t len)
{
float tmp[2 * len];
memcpy(tmp, buf, 2 * len * sizeof(float));
for (size_t i = 0; i < len; i++) {
buf[2 * i + 0] = tmp[2 * (len - 1 - i) + 0];
buf[2 * i + 1] = tmp[2 * (len - 1 - i) + 1];
}
}
/*
* Create polyphase filterbank
*
* Implementation based material found in,
*
* "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
* Prentice Hall, 2006."
*/
bool ChannelizerBase::initFilters()
{
size_t protoLen = m * hLen;
float *proto;
float sum = 0.0f, scale = 0.0f;
float midpt = (float) (protoLen - 1.0) / 2.0;
/*
* Allocate 'M' partition filters and the temporary prototype
* filter. Coefficients are real only and must be 16-byte memory
* aligned for SSE usage.
*/
proto = new float[protoLen];
if (!proto)
return false;
subFilters = (float **) malloc(sizeof(float *) * m);
if (!subFilters) {
delete[] proto;
return false;
}
for (size_t i = 0; i < m; i++) {
subFilters[i] = (float *)
memalign(16, hLen * 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 channels.
*/
float a0 = 0.35875;
float a1 = 0.48829;
float a2 = 0.14128;
float a3 = 0.01168;
for (size_t i = 0; i < protoLen; i++) {
proto[i] = sinc(((float) i - midpt) / (float) m);
proto[i] *= a0 -
a1 * cos(2 * M_PI * i / (protoLen - 1)) +
a2 * cos(4 * M_PI * i / (protoLen - 1)) -
a3 * cos(6 * M_PI * i / (protoLen - 1));
sum += proto[i];
}
scale = (float) m / sum;
/*
* Populate partition filters and reverse the coefficients per
* convolution requirements.
*/
for (size_t i = 0; i < hLen; i++) {
for (size_t n = 0; n < m; n++) {
subFilters[n][2 * i + 0] = proto[i * m + n] * scale;
subFilters[n][2 * i + 1] = 0.0f;
}
}
for (size_t i = 0; i < m; i++)
reverse(subFilters[i], hLen);
delete[] proto;
return true;
}
bool ChannelizerBase::initFFT()
{
size_t size;
if (fftInput || fftOutput || fftHandle)
return false;
size = blockLen * m * 2 * sizeof(float);
fftInput = (float *) fft_malloc(size);
memset(fftInput, 0, size);
size = (blockLen + hLen) * m * 2 * sizeof(float);
fftOutput = (float *) fft_malloc(size);
memset(fftOutput, 0, size);
if (!fftInput | !fftOutput) {
LOG(ALERT) << "Memory allocation error";
return false;
}
fftHandle = init_fft(0, m, blockLen, blockLen + hLen,
fftInput, fftOutput, hLen);
return true;
}
bool ChannelizerBase::mapBuffers()
{
if (!fftHandle) {
LOG(ALERT) << "FFT buffers not initialized";
return false;
}
hInputs = (float **) malloc(sizeof(float *) * m);
hOutputs = (float **) malloc(sizeof(float *) * m);
if (!hInputs | !hOutputs)
return false;
for (size_t i = 0; i < m; i++) {
hInputs[i] = &fftOutput[2 * (i * (blockLen + hLen) + hLen)];
hOutputs[i] = &fftInput[2 * (i * blockLen)];
}
return true;
}
/*
* Setup filterbank internals
*/
bool ChannelizerBase::init()
{
/*
* Filterbank coefficients, fft plan, history, and output sample
* rate conversion blocks
*/
if (!initFilters()) {
LOG(ALERT) << "Failed to initialize channelizing filter";
return false;
}
hist = (float **) malloc(sizeof(float *) * m);
for (size_t i = 0; i < m; i++) {
hist[i] = new float[2 * hLen];
memset(hist[i], 0, 2 * hLen * sizeof(float));
}
if (!initFFT()) {
LOG(ALERT) << "Failed to initialize FFT";
return false;
}
mapBuffers();
return true;
}
/* Check vector length validity */
bool ChannelizerBase::checkLen(size_t innerLen, size_t outerLen)
{
if (outerLen != innerLen * m) {
LOG(ALERT) << "Invalid outer length " << innerLen
<< " is not multiple of " << blockLen;
return false;
}
if (innerLen != blockLen) {
LOG(ALERT) << "Invalid inner length " << outerLen
<< " does not equal " << blockLen;
return false;
}
return true;
}
/*
* Setup channelizer paramaters
*/
ChannelizerBase::ChannelizerBase(size_t m, size_t blockLen, size_t hLen)
: fftInput(NULL), fftOutput(NULL), fftHandle(NULL)
{
this->m = m;
this->hLen = hLen;
this->blockLen = blockLen;
}
ChannelizerBase::~ChannelizerBase()
{
free_fft(fftHandle);
for (size_t i = 0; i < m; i++) {
free(subFilters[i]);
delete hist[i];
}
fft_free(fftInput);
fft_free(fftOutput);
free(hInputs);
free(hOutputs);
free(hist);
}

View File

@@ -1,39 +0,0 @@
#ifndef _CHANNELIZER_BASE_H_
#define _CHANNELIZER_BASE_H_
class ChannelizerBase {
protected:
ChannelizerBase(size_t m, size_t blockLen, size_t hLen);
~ChannelizerBase();
/* Channelizer parameters */
size_t m;
size_t hLen;
size_t blockLen;
/* Channelizer filterbank sub-filters */
float **subFilters;
/* Input/Output buffers */
float **hInputs, **hOutputs, **hist;
float *fftInput, *fftOutput;
/* Pointer to opaque FFT instance */
struct fft_hdl *fftHandle;
/* Initializer internals */
bool initFilters();
bool initFFT();
void releaseFilters();
/* Map overlapped FFT and filter I/O buffers */
bool mapBuffers();
/* Buffer length validity checking */
bool checkLen(size_t innerLen, size_t outerLen);
public:
/* Initilize channelizer/synthesis filter internals */
bool init();
};
#endif /* _CHANNELIZER_BASE_H_ */

View File

@@ -21,7 +21,7 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I./common
AM_CXXFLAGS = -ldl -lpthread
SUBDIRS = arm x86
@@ -54,20 +54,14 @@ COMMON_SOURCES = \
radioInterface.cpp \
radioVector.cpp \
radioClock.cpp \
radioBuffer.cpp \
sigProcLib.cpp \
signalVector.cpp \
Transceiver.cpp \
ChannelizerBase.cpp \
Channelizer.cpp \
Synthesis.cpp \
common/fft.c
Transceiver.cpp
libtransceiver_la_SOURCES = \
$(COMMON_SOURCES) \
Resampler.cpp \
radioInterfaceResamp.cpp \
radioInterfaceMulti.cpp \
radioInterfaceDiversity.cpp
bin_PROGRAMS = osmo-trx
@@ -78,20 +72,15 @@ noinst_HEADERS = \
radioVector.h \
radioClock.h \
radioDevice.h \
radioBuffer.h \
sigProcLib.h \
signalVector.h \
Transceiver.h \
USRPDevice.h \
Resampler.h \
ChannelizerBase.h \
Channelizer.h \
Synthesis.h \
common/convolve.h \
common/convert.h \
common/scale.h \
common/mult.h \
common/fft.h
common/mult.h
osmo_trx_SOURCES = osmo-trx.cpp
osmo_trx_LDADD = \
@@ -105,5 +94,5 @@ libtransceiver_la_SOURCES += USRPDevice.cpp
osmo_trx_LDADD += $(USRP_LIBS)
else
libtransceiver_la_SOURCES += UHDDevice.cpp
osmo_trx_LDADD += $(UHD_LIBS) $(FFTWF_LIBS)
osmo_trx_LDADD += $(UHD_LIBS)
endif

View File

@@ -61,7 +61,7 @@ bool Resampler::initFilters(float bw)
partitions = (float **) malloc(sizeof(float *) * p);
if (!partitions) {
delete[] proto;
free(proto);
return false;
}
@@ -167,12 +167,16 @@ void Resampler::computePath()
}
}
int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len)
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;
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++) {
@@ -185,15 +189,25 @@ int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len
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];
@@ -208,7 +222,7 @@ size_t Resampler::len()
}
Resampler::Resampler(size_t p, size_t q, size_t filt_len)
: in_index(NULL), out_path(NULL), partitions(NULL)
: in_index(NULL), out_path(NULL), partitions(NULL), history(NULL)
{
this->p = p;
this->q = q;
@@ -219,6 +233,7 @@ Resampler::~Resampler()
{
releaseFilters();
delete history;
delete in_index;
delete out_path;
}

View File

@@ -52,7 +52,7 @@ public:
* Input and output vector lengths must of be equal multiples of the
* rational conversion rate denominator and numerator respectively.
*/
int rotate(const float *in, size_t in_len, float *out, size_t out_len);
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
@@ -67,6 +67,7 @@ private:
size_t *out_path;
float **partitions;
float *history;
bool initFilters(float bw);
void releaseFilters();

View File

@@ -1,121 +0,0 @@
/*
* Polyphase synthesis filter
*
* Copyright (C) 2012-2014 Tom Tsou <tom@tsou.cc>
* Copyright (C) 2015 Ettus Research LLC
*
* 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 <stdlib.h>
#include <math.h>
#include <assert.h>
#include <string.h>
#include <cstdio>
#include "Logger.h"
#include "Synthesis.h"
extern "C" {
#include "common/fft.h"
#include "common/convolve.h"
}
static void interleave(float **in, size_t ilen,
float *out, size_t m)
{
size_t i, n;
for (i = 0; i < ilen; i++) {
for (n = 0; n < m; n++) {
out[2 * (i * m + n) + 0] = in[n][2 * i + 0];
out[2 * (i * m + n) + 1] = in[n][2 * i + 1];
}
}
}
size_t Synthesis::inputLen() const
{
return blockLen;
}
size_t Synthesis::outputLen() const
{
return blockLen * m;
}
float *Synthesis::inputBuffer(size_t chan) const
{
if (chan >= m)
return NULL;
return hOutputs[chan];
}
bool Synthesis::resetBuffer(size_t chan)
{
if (chan >= m)
return false;
memset(hOutputs[chan], 0, blockLen * 2 * sizeof(float));
return true;
}
/*
* Implementation based on material found in:
*
* "harris, fred, Multirate Signal Processing, Upper Saddle River, NJ,
* Prentice Hall, 2006."
*/
bool Synthesis::rotate(float *out, size_t len)
{
size_t hSize = 2 * hLen * sizeof(float);
if (!checkLen(blockLen, len)) {
std::cout << "Length fail" << std::endl;
exit(1);
return false;
}
cxvec_fft(fftHandle);
/*
* Convolve through filterbank while applying and saving sample history
*/
for (size_t i = 0; i < m; i++) {
memcpy(&hInputs[i][2 * -hLen], hist[i], hSize);
memcpy(hist[i], &hInputs[i][2 * (blockLen - hLen)], hSize);
convolve_real(hInputs[i], blockLen,
subFilters[i], hLen,
hOutputs[i], blockLen,
0, blockLen, 1, 0);
}
/* Interleave into output vector */
interleave(hOutputs, blockLen, out, m);
return true;
}
Synthesis::Synthesis(size_t m, size_t blockLen, size_t hLen)
: ChannelizerBase(m, blockLen, hLen)
{
}
Synthesis::~Synthesis()
{
}

View File

@@ -1,35 +0,0 @@
#ifndef _SYNTHESIS_H_
#define _SYNTHESIS_H_
#include "ChannelizerBase.h"
class Synthesis : public ChannelizerBase {
public:
/** Constructor for synthesis filterbank
@param m number of physical channels
@param blockLen number of samples per output of each iteration
@param hLen number of taps in each constituent filter path
*/
Synthesis(size_t m, size_t blockLen, size_t hLen = 16);
~Synthesis();
/* Return required input and output buffer lengths */
size_t inputLen() const;
size_t outputLen() const;
/** Rotate "output commutator" and drive samples through filterbank
@param out complex output vector
@param oLen number of samples in buffer (must match block length * m)
@return false on error and true otherwise
*/
bool rotate(float *out, size_t oLen);
/** Get buffer for an input path
@param chan channel number of filterbank
@return NULL on error and pointer to buffer otherwise
*/
float *inputBuffer(size_t chan) const;
bool resetBuffer(size_t chan);
};
#endif /* _SYNTHESIS_H_ */

View File

@@ -22,8 +22,6 @@
*/
#include <stdio.h>
#include <iomanip> // std::setprecision
#include <fstream>
#include "Transceiver.h"
#include <Logger.h>
@@ -44,17 +42,8 @@ using namespace GSM;
/* Number of running values use in noise average */
#define NOISE_CNT 20
/*
* Burst detection threshold
*
* Decision threshold value for burst gating on peak-to-average value of
* correlated synchronization sequences. Lower values pass more bursts up
* to upper layers but will increase the false detection rate.
*/
#define BURST_THRESH 4.0
TransceiverState::TransceiverState()
: mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT), mPower(0.0)
: mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT)
{
for (int i = 0; i < 8; i++) {
chanType[i] = Transceiver::NONE;
@@ -80,113 +69,75 @@ TransceiverState::~TransceiverState()
}
}
bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
void TransceiverState::init(size_t slot, signalVector *burst, bool fill)
{
signalVector *burst;
signalVector *filler;
if ((sps != 1) && (sps != 4))
return false;
for (int i = 0; i < 102; i++) {
if (fill)
filler = new signalVector(*burst);
else
filler = new signalVector(burst->size());
for (size_t n = 0; n < 8; n++) {
for (size_t i = 0; i < 102; i++) {
switch (filler) {
case Transceiver::FILLER_DUMMY:
burst = generateDummyBurst(sps, n);
break;
case Transceiver::FILLER_NORM_RAND:
burst = genRandNormalBurst(rtsc, sps, n);
break;
case Transceiver::FILLER_EDGE_RAND:
burst = generateEdgeBurst(rtsc);
break;
case Transceiver::FILLER_ACCESS_RAND:
burst = genRandAccessBurst(rach_delay, sps, n);
break;
case Transceiver::FILLER_ZERO:
default:
burst = generateEmptyBurst(sps, n);
}
scaleVector(*burst, scale);
fillerTable[i][n] = burst;
}
if ((filler == Transceiver::FILLER_NORM_RAND) ||
(filler == Transceiver::FILLER_EDGE_RAND)) {
chanType[n] = Transceiver::TSC;
}
fillerTable[i][slot] = filler;
}
return false;
}
Transceiver::Transceiver(int wBasePort,
const char *wTRXAddress,
size_t tx_sps, size_t rx_sps, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface,
double wRssiOffset)
: mBasePort(wBasePort), mAddr(wTRXAddress),
mClockSocket(wBasePort, wTRXAddress, mBasePort + 100),
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
rssiOffset(wRssiOffset),
mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false),
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(2*rx_sps), mMaxExpectedDelayNB(2*rx_sps),
mWriteBurstToDiskMask(0)
const char *TRXAddress,
size_t wSPS, size_t wChans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface)
: mBasePort(wBasePort), mAddr(TRXAddress),
mTransmitLatency(wTransmitLatency), mClockSocket(NULL),
mRadioInterface(wRadioInterface), mSPSTx(wSPS), mSPSRx(1), mChans(wChans),
mOn(false), mTxFreq(0.0), mRxFreq(0.0), mPower(-10), mMaxExpectedDelay(0)
{
GSM::Time startTime(random() % gHyperframe,0);
mRxLowerLoopThread = new Thread(32768);
mTxLowerLoopThread = new Thread(32768);
mTransmitDeadlineClock = startTime;
mLastClockUpdateTime = startTime;
mLatencyUpdateTime = startTime;
mRadioInterface->getClock()->set(startTime);
txFullScale = mRadioInterface->fullScaleInputValue();
rxFullScale = mRadioInterface->fullScaleOutputValue();
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++)
mHandover[i][j] = false;
}
}
Transceiver::~Transceiver()
{
stop();
sigProcLibDestroy();
for (size_t i = 0; i < mChans; i++) {
mControlServiceLoopThreads[i]->cancel();
mControlServiceLoopThreads[i]->join();
delete mControlServiceLoopThreads[i];
delete mClockSocket;
for (size_t i = 0; i < mChans; i++) {
mTxPriorityQueues[i].clear();
delete mCtrlSockets[i];
delete mDataSockets[i];
}
}
/*
* Initialize transceiver
*
* Start or restart the control loop. Any further control is handled through the
* socket API. Randomize the central radio clock set the downlink burst
* counters. Note that the clock will not update until the radio starts, but we
* are still expected to report clock indications through control channel
* activity.
*/
bool Transceiver::init(int filler, size_t rtsc, unsigned rach_delay, bool edge)
bool Transceiver::init(bool filler)
{
int d_srcport, d_dstport, c_srcport, c_dstport;
signalVector *burst;
if (!mChans) {
LOG(ALERT) << "No channels assigned";
return false;
}
if (!sigProcLibSetup()) {
if (!sigProcLibSetup(mSPSTx)) {
LOG(ALERT) << "Failed to initialize signal processing library";
return false;
}
mEdge = edge;
mDataSockets.resize(mChans);
mCtrlSockets.resize(mChans);
mControlServiceLoopThreads.resize(mChans);
mTxPriorityQueueServiceLoopThreads.resize(mChans);
mRxServiceLoopThreads.resize(mChans);
@@ -196,10 +147,11 @@ bool Transceiver::init(int filler, size_t rtsc, unsigned rach_delay, bool edge)
mStates.resize(mChans);
/* Filler table retransmissions - support only on channel 0 */
if (filler == FILLER_DUMMY)
if (filler)
mStates[0].mRetrans = true;
/* Setup sockets */
mClockSocket = new UDPSocket(mBasePort, mAddr.c_str(), mBasePort + 100);
for (size_t i = 0; i < mChans; i++) {
c_srcport = mBasePort + 2 * i + 1;
c_dstport = mBasePort + 2 * i + 101;
@@ -210,129 +162,22 @@ bool Transceiver::init(int filler, size_t rtsc, unsigned rach_delay, bool edge)
mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport);
}
/* Randomize the central clock */
GSM::Time startTime(random() % gHyperframe, 0);
mRadioInterface->getClock()->set(startTime);
mTransmitDeadlineClock = startTime;
mLastClockUpdateTime = startTime;
mLatencyUpdateTime = startTime;
/* Start control threads */
for (size_t i = 0; i < mChans; i++) {
TransceiverChannel *chan = new TransceiverChannel(this, i);
mControlServiceLoopThreads[i] = new Thread(32768);
mControlServiceLoopThreads[i]->start((void * (*)(void*))
ControlServiceLoopAdapter, (void*) chan);
if (i && filler == FILLER_DUMMY)
filler = FILLER_ZERO;
mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
}
return true;
}
/*
* Start the transceiver
*
* Submit command(s) to the radio device to commence streaming samples and
* launch threads to handle sample I/O. Re-synchronize the transmit burst
* counters to the central radio clock here as well.
*/
bool Transceiver::start()
{
ScopedLock lock(mLock);
if (mOn) {
LOG(ERR) << "Transceiver already running";
return true;
}
LOG(NOTICE) << "Starting the transceiver";
GSM::Time time = mRadioInterface->getClock()->get();
mTransmitDeadlineClock = time;
mLastClockUpdateTime = time;
mLatencyUpdateTime = time;
if (!mRadioInterface->start()) {
LOG(ALERT) << "Device failed to start";
return false;
}
/* Device is running - launch I/O threads */
mRxLowerLoopThread = new Thread(32768);
mTxLowerLoopThread = new Thread(32768);
mTxLowerLoopThread->start((void * (*)(void*))
TxLowerLoopAdapter,(void*) this);
mRxLowerLoopThread->start((void * (*)(void*))
RxLowerLoopAdapter,(void*) this);
/* Launch uplink and downlink burst processing threads */
for (size_t i = 0; i < mChans; i++) {
TransceiverChannel *chan = new TransceiverChannel(this, i);
mRxServiceLoopThreads[i] = new Thread(32768);
mRxServiceLoopThreads[i]->start((void * (*)(void*))
RxUpperLoopAdapter, (void*) chan);
chan = new TransceiverChannel(this, i);
mTxPriorityQueueServiceLoopThreads[i] = new Thread(32768);
mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
TxUpperLoopAdapter, (void*) chan);
mRxServiceLoopThreads[i] = new Thread(32768);
for (size_t n = 0; n < 8; n++) {
burst = modulateBurst(gDummyBurst, 8 + (n % 4 == 0), mSPSTx);
scaleVector(*burst, txFullScale);
mStates[i].init(n, burst, filler && !i);
delete burst;
}
}
writeClockInterface();
mOn = true;
return true;
}
/*
* Stop the transceiver
*
* Perform stopping by disabling receive streaming and issuing cancellation
* requests to running threads. Most threads will timeout and terminate once
* device is disabled, but the transmit loop may block waiting on the central
* UMTS clock. Explicitly signal the clock to make sure that the transmit loop
* makes it to the thread cancellation point.
*/
void Transceiver::stop()
{
ScopedLock lock(mLock);
if (!mOn)
return;
LOG(NOTICE) << "Stopping the transceiver";
mTxLowerLoopThread->cancel();
mRxLowerLoopThread->cancel();
for (size_t i = 0; i < mChans; i++) {
mRxServiceLoopThreads[i]->cancel();
mTxPriorityQueueServiceLoopThreads[i]->cancel();
}
LOG(INFO) << "Stopping the device";
mRadioInterface->stop();
for (size_t i = 0; i < mChans; i++) {
mRxServiceLoopThreads[i]->join();
mTxPriorityQueueServiceLoopThreads[i]->join();
delete mRxServiceLoopThreads[i];
delete mTxPriorityQueueServiceLoopThreads[i];
mTxPriorityQueues[i].clear();
}
mTxLowerLoopThread->join();
mRxLowerLoopThread->join();
delete mTxLowerLoopThread;
delete mRxLowerLoopThread;
mOn = false;
LOG(NOTICE) << "Transceiver stopped";
}
void Transceiver::addRadioVector(size_t chan, BitVector &bits,
int RSSI, GSM::Time &wTime)
{
@@ -349,12 +194,7 @@ void Transceiver::addRadioVector(size_t chan, BitVector &bits,
return;
}
/* Use the number of bits as the EDGE burst indicator */
if (bits.size() == EDGE_BURST_NBITS)
burst = modulateEdgeBurst(bits, mSPSTx);
else
burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
radio_burst = new radioVector(wTime, burst);
@@ -455,15 +295,9 @@ void Transceiver::setModulus(size_t timeslot, size_t chan)
Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
size_t chan)
{
static int tchh_subslot[26] = { 0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,1,0,1,0,1,0,1,0,1,1 };
static int sdcch4_subslot[102] = { 3,3,3,3,0,0,2,2,2,2,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,2,2,2,2,
3,3,3,3,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,2,2,2,2 };
static int sdcch8_subslot[102] = { 5,5,5,5,6,6,6,6,7,7,7,7,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,0,0,0,0,
1,1,1,1,2,2,2,2,3,3,3,3,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,4,4,4,4 };
TransceiverState *state = &mStates[chan];
unsigned burstTN = currTime.TN();
unsigned burstFN = currTime.FN();
int subch;
switch (state->chanType[burstTN]) {
case NONE:
@@ -473,25 +307,16 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
return IDLE;
break;
case I:
// TODO: Are we expecting RACH on an IDLE frame?
/* if (burstFN % 26 == 25)
return IDLE;*/
if (mHandover[burstTN][0])
return RACH;
return TSC;
/*if (burstFN % 26 == 25)
return IDLE;
else
return TSC;*/
break;
case II:
subch = tchh_subslot[burstFN % 26];
if (subch == 1)
return IDLE;
if (mHandover[burstTN][0])
return RACH;
return TSC;
break;
case III:
subch = tchh_subslot[burstFN % 26];
if (mHandover[burstTN][subch])
return RACH;
return TSC;
break;
case IV:
@@ -506,8 +331,6 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
return RACH;
else if ((mod51 == 45) || (mod51 == 46))
return RACH;
else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
return RACH;
else
return TSC;
break;
@@ -515,8 +338,6 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
case VII:
if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
return IDLE;
else if (mHandover[burstTN][sdcch8_subslot[burstFN % 102]])
return RACH;
else
return TSC;
break;
@@ -542,76 +363,104 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
}
}
int Transceiver::detectBurst(signalVector &burst,
complex &amp, float &toa, CorrType type)
/*
* Detect RACH synchronization sequence within a burst. No equalization
* is used or available on the RACH channel.
*/
bool Transceiver::detectRACH(TransceiverState *state,
signalVector &burst,
complex &amp, float &toa)
{
int rc = 0;
float threshold = 6.0;
switch (type) {
case EDGE:
rc = detectEdgeBurst(burst, mTSC, BURST_THRESH, mSPSRx,
amp, toa, mMaxExpectedDelayNB);
if (rc > 0)
break;
else
type = TSC;
case TSC:
rc = analyzeTrafficBurst(burst, mTSC, BURST_THRESH, mSPSRx,
amp, toa, mMaxExpectedDelayNB);
break;
case RACH:
rc = detectRACHBurst(burst, BURST_THRESH, mSPSRx, amp, toa,
mMaxExpectedDelayAB);
break;
default:
LOG(ERR) << "Invalid correlation type";
}
if (rc > 0)
return type;
return rc;
return detectRACHBurst(burst, threshold, mSPSRx, &amp, &toa);
}
/*
* Demodulate GMSK by direct rotation and soft slicing.
* Detect normal burst training sequence midamble. Update equalization
* state information and channel estimate if necessary. Equalization
* is currently disabled.
*/
SoftVector *Transceiver::demodulate(signalVector &burst, complex amp,
float toa, CorrType type)
bool Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
complex &amp, float &toa, GSM::Time &time)
{
if (type == EDGE)
return demodEdgeBurst(burst, mSPSRx, amp, toa);
int tn = time.TN();
float chanOffset, threshold = 5.0;
bool noise, needDFE = false, estimateChan = false;
double elapsed = time - state->chanEstimateTime[tn];
signalVector *chanResp;
return demodulateBurst(burst, mSPSRx, amp, toa);
/* Check equalization update state */
if (needDFE && ((elapsed > 50) || (!state->chanResponse[tn]))) {
delete state->DFEForward[tn];
delete state->DFEFeedback[tn];
state->DFEForward[tn] = NULL;
state->DFEFeedback[tn] = NULL;
estimateChan = true;
}
/* Detect normal burst midambles */
if (!analyzeTrafficBurst(burst, mTSC, threshold, mSPSRx, &amp,
&toa, mMaxExpectedDelay, estimateChan,
&chanResp, &chanOffset)) {
return false;
}
noise = state->mNoiseLev;
state->SNRestimate[tn] = amp.norm2() / (noise * noise + 1.0);
/* Set equalizer if unabled */
if (needDFE && estimateChan) {
state->chanResponse[tn] = chanResp;
state->chanRespOffset[tn] = chanOffset;
state->chanRespAmplitude[tn] = amp;
scaleVector(*chanResp, complex(1.0, 0.0) / amp);
designDFE(*chanResp, state->SNRestimate[tn],
7, &state->DFEForward[tn], &state->DFEFeedback[tn]);
state->chanEstimateTime[tn] = time;
}
return true;;
}
void writeToFile(radioVector *radio_burst, size_t chan)
/*
* Demodulate GMSK burst using equalization if requested. Otherwise
* demodulate by direct rotation and soft slicing.
*/
SoftVector *Transceiver::demodulate(TransceiverState *state,
signalVector &burst, complex amp,
float toa, size_t tn, bool equalize)
{
GSM::Time time = radio_burst->getTime();
std::ostringstream fname;
fname << chan << "_" << time.FN() << "_" << time.TN() << ".fc";
std::ofstream outfile (fname.str().c_str(), std::ofstream::binary);
outfile.write((char*)radio_burst->getVector()->begin(), radio_burst->getVector()->size() * 2 * sizeof(float));
outfile.close();
if (equalize) {
scaleVector(burst, complex(1.0, 0.0) / amp);
return equalizeBurst(burst,
toa - state->chanRespOffset[tn],
mSPSRx,
*state->DFEForward[tn],
*state->DFEFeedback[tn]);
}
return demodulateBurst(burst, mSPSRx, amp, toa);
}
/*
* Pull bursts from the FIFO and handle according to the slot
* and burst correlation type. Equalzation is currently disabled.
*/
SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
double &timingOffset, double &noise,
size_t chan)
SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
int &timingOffset, size_t chan)
{
int rc;
bool success, equalize = false;
complex amp;
float toa, pow, max = -1.0, avg = 0.0;
int max_i = -1;
signalVector *burst;
SoftVector *bits = NULL;
TransceiverState *state = &mStates[chan];
isRssiValid = false;
/* Blocking FIFO read */
radioVector *radio_burst = mReceiveFIFO[chan]->read();
@@ -622,19 +471,7 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &i
GSM::Time time = radio_burst->getTime();
CorrType type = expectedCorrType(time, chan);
/* Enable 8-PSK burst detection if EDGE is enabled */
if (mEdge && (type == TSC))
type = EDGE;
/* Debug: dump bursts to disk */
/* bits 0-7 - chan 0 timeslots
* bits 8-15 - chan 1 timeslots */
if (mWriteBurstToDiskMask & ((1<<time.TN()) << (8*chan)))
writeToFile(radio_burst, chan);
/* No processing if the timeslot is off.
* Not even power level or noise calculation. */
if (type == OFF) {
if ((type == OFF) || (type == IDLE)) {
delete radio_burst;
return NULL;
}
@@ -658,50 +495,47 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &i
/* Average noise on diversity paths and update global levels */
burst = radio_burst->getVector(max_i);
avg = sqrt(avg / radio_burst->chans());
wTime = time;
RSSI = 20.0 * log10(rxFullScale / avg);
/* RSSI estimation are valid */
isRssiValid = true;
if (type == IDLE) {
/* Update noise levels */
state->mNoises.insert(avg);
state->mNoiseLev = state->mNoises.avg();
noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
delete radio_burst;
return NULL;
} else {
/* Do not update noise levels */
noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
}
state->mNoiseLev = state->mNoises.avg();
/* Detect normal or RACH bursts */
rc = detectBurst(*burst, amp, toa, type);
if (rc > 0) {
type = (CorrType) rc;
} else if (rc <= 0) {
if (rc == -SIGERR_CLIP) {
LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
} else if (rc != SIGERR_NONE) {
LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
}
if (type == TSC)
success = detectTSC(state, *burst, amp, toa, time);
else
success = detectRACH(state, *burst, amp, toa);
if (!success) {
state->mNoises.insert(avg);
delete radio_burst;
return NULL;
}
timingOffset = toa;
/* Demodulate and set output info */
if (equalize && (type != TSC))
equalize = false;
bits = demodulate(*burst, amp, toa, type);
if (avg - state->mNoiseLev > 0.0)
bits = demodulate(state, *burst, amp, toa, time.TN(), equalize);
wTime = time;
RSSI = (int) floor(20.0 * log10(rxFullScale / avg));
timingOffset = (int) round(toa * 256.0 / mSPSRx);
delete radio_burst;
return bits;
}
void Transceiver::start()
{
TransceiverChannel *chan;
for (size_t i = 0; i < mControlServiceLoopThreads.size(); i++) {
chan = new TransceiverChannel(this, i);
mControlServiceLoopThreads[i]->start((void * (*)(void*))
ControlServiceLoopAdapter, (void*) chan);
}
}
void Transceiver::reset()
{
for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
@@ -718,7 +552,7 @@ void Transceiver::driveControl(size_t chan)
int msgLen = -1;
buffer[0] = '\0';
msgLen = mCtrlSockets[chan]->read(buffer, sizeof(buffer));
msgLen = mCtrlSockets[chan]->read(buffer);
if (msgLen < 1) {
return;
@@ -740,46 +574,48 @@ void Transceiver::driveControl(size_t chan)
LOG(INFO) << "command is " << buffer;
if (strcmp(command,"POWEROFF")==0) {
stop();
sprintf(response,"RSP POWEROFF 0");
// turn off transmitter/demod
sprintf(response,"RSP POWEROFF 0");
}
else if (strcmp(command,"POWERON")==0) {
if (!start()) {
// turn on transmitter/demod
if (!mTxFreq || !mRxFreq)
sprintf(response,"RSP POWERON 1");
} else {
else {
sprintf(response,"RSP POWERON 0");
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++)
mHandover[i][j] = false;
if (!chan && !mOn) {
// Prepare for thread start
mPower = -20;
mRadioInterface->start();
// Start radio interface threads.
mTxLowerLoopThread->start((void * (*)(void*))
TxLowerLoopAdapter,(void*) this);
mRxLowerLoopThread->start((void * (*)(void*))
RxLowerLoopAdapter,(void*) this);
for (size_t i = 0; i < mChans; i++) {
TransceiverChannel *chan = new TransceiverChannel(this, i);
mRxServiceLoopThreads[i]->start((void * (*)(void*))
RxUpperLoopAdapter, (void*) chan);
chan = new TransceiverChannel(this, i);
mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
TxUpperLoopAdapter, (void*) chan);
}
writeClockInterface();
mOn = true;
}
}
}
else if (strcmp(command,"HANDOVER")==0){
int ts=0,ss=0;
sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&ts,&ss);
mHandover[ts][ss] = true;
sprintf(response,"RSP HANDOVER 0 %d %d",ts,ss);
}
else if (strcmp(command,"NOHANDOVER")==0){
int ts=0,ss=0;
sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&ts,&ss);
mHandover[ts][ss] = false;
sprintf(response,"RSP NOHANDOVER 0 %d %d",ts,ss);
}
else if (strcmp(command,"SETMAXDLY")==0) {
//set expected maximum time-of-arrival
int maxDelay;
sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay);
mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
mMaxExpectedDelay = maxDelay; // 1 GSM symbol is approx. 1 km
sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
}
else if (strcmp(command,"SETMAXDLYNB")==0) {
//set expected maximum time-of-arrival
int maxDelay;
sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay);
mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
}
else if (strcmp(command,"SETRXGAIN")==0) {
//set expected maximum time-of-arrival
int newGain;
@@ -798,19 +634,28 @@ void Transceiver::driveControl(size_t chan)
}
}
else if (!strcmp(command, "SETPOWER")) {
int power;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &power);
power = mRadioInterface->setPowerAttenuation(power, chan);
mStates[chan].mPower = power;
sprintf(response, "RSP SETPOWER 0 %d", power);
// set output power in dB
int dbPwr;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &dbPwr);
if (!mOn)
sprintf(response, "RSP SETPOWER 1 %d", dbPwr);
else {
mPower = dbPwr;
mRadioInterface->setPowerAttenuation(mPower, chan);
sprintf(response, "RSP SETPOWER 0 %d", dbPwr);
}
}
else if (!strcmp(command,"ADJPOWER")) {
int power, step;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &step);
power = mStates[chan].mPower + step;
power = mRadioInterface->setPowerAttenuation(power, chan);
mStates[chan].mPower = power;
sprintf(response, "RSP ADJPOWER 0 %d", power);
// adjust power in dB steps
int dbStep;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &dbStep);
if (!mOn)
sprintf(response, "RSP ADJPOWER 1 %d", mPower);
else {
mPower += dbStep;
mRadioInterface->setPowerAttenuation(mPower, chan);
sprintf(response, "RSP ADJPOWER 0 %d", mPower);
}
}
else if (strcmp(command,"RXTUNE")==0) {
// tune receiver
@@ -840,16 +685,18 @@ void Transceiver::driveControl(size_t chan)
// set TSC
unsigned TSC;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &TSC);
if ((TSC < 0) || (TSC > 7))
if (mOn)
sprintf(response, "RSP SETTSC 1 %d", TSC);
else if (chan && (TSC != mTSC))
sprintf(response, "RSP SETTSC 1 %d", TSC);
else {
LOG(NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
mTSC = TSC;
generateMidamble(mSPSRx, TSC);
sprintf(response,"RSP SETTSC 0 %d", TSC);
}
}
else if (strcmp(command,"SETSLOT")==0) {
// set slot type
// set TSC
int corrCode;
int timeslot;
sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&timeslot,&corrCode);
@@ -863,17 +710,8 @@ void Transceiver::driveControl(size_t chan)
sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
}
else if (strcmp(command,"_SETBURSTTODISKMASK")==0) {
// debug command! may change or disapear without notice
// set a mask which bursts to dump to disk
int mask;
sscanf(buffer,"%3s %s %d",cmdcheck,command,&mask);
mWriteBurstToDiskMask = mask;
sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
}
else {
LOG(WARNING) << "bogus command " << command << " on control interface.";
sprintf(response,"RSP ERR 1");
}
mCtrlSockets[chan]->write(response, strlen(response) + 1);
@@ -881,20 +719,12 @@ void Transceiver::driveControl(size_t chan)
bool Transceiver::driveTxPriorityQueue(size_t chan)
{
int burstLen;
char buffer[EDGE_BURST_NBITS + 50];
char buffer[gSlotLen+50];
// check data socket
size_t msgLen = mDataSockets[chan]->read(buffer, sizeof(buffer));
size_t msgLen = mDataSockets[chan]->read(buffer);
if (msgLen == gSlotLen + 1 + 4 + 1) {
burstLen = gSlotLen;
} else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) {
if (mSPSTx != 4)
return false;
burstLen = EDGE_BURST_NBITS;
} else {
if (msgLen!=gSlotLen+1+4+1) {
LOG(ERR) << "badly formatted packet on GSM->TRX interface";
return false;
}
@@ -904,10 +734,19 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
for (int i = 0; i < 4; i++)
frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
// periodically update GSM core clock
LOG(DEBUG) << "mTransmitDeadlineClock " << mTransmitDeadlineClock
<< " mLastClockUpdateTime " << mLastClockUpdateTime;
if (!chan) {
if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
writeClockInterface();
}
LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
int RSSI = (int) buffer[5];
BitVector newBurst(burstLen);
static BitVector newBurst(gSlotLen);
BitVector::iterator itr = newBurst.begin();
char *bufferItr = buffer+6;
while (itr < newBurst.end())
@@ -924,71 +763,44 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
void Transceiver::driveReceiveRadio()
{
if (!mRadioInterface->driveReceiveRadio()) {
if (!mRadioInterface->driveReceiveRadio())
usleep(100000);
} else {
if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
writeClockInterface();
}
}
void Transceiver::logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm,
double rssi, double noise, double toa)
{
LOG(DEBUG) << std::fixed << std::right
<< " chan: " << chan
<< " time: " << time
<< " RSSI: " << std::setw(5) << std::setprecision(1) << rssi
<< "dBFS/" << std::setw(6) << -dbm << "dBm"
<< " noise: " << std::setw(5) << std::setprecision(1) << noise
<< "dBFS/" << std::setw(6) << -(noise + rssiOffset) << "dBm"
<< " TOA: " << std::setw(5) << std::setprecision(2) << toa
<< " bits: " << *burst;
}
void Transceiver::driveReceiveFIFO(size_t chan)
{
SoftVector *rxBurst = NULL;
double RSSI; // in dBFS
double dBm; // in dBm
double TOA; // in symbols
int TOAint; // in 1/256 symbols
double noise; // noise level in dBFS
int RSSI;
int TOA; // in 1/256 of a symbol
GSM::Time burstTime;
bool isRssiValid; // are RSSI, noise and burstTime valid
unsigned nbits = gSlotLen;
rxBurst = pullRadioVector(burstTime, RSSI, isRssiValid, TOA, noise, chan);
if (!rxBurst)
return;
rxBurst = pullRadioVector(burstTime, RSSI, TOA, chan);
/*
* EDGE demodulator returns 444 (148 * 3) bits
*/
if (rxBurst->size() == gSlotLen * 3)
nbits = gSlotLen * 3;
if (rxBurst) {
dBm = RSSI + rssiOffset;
logRxBurst(chan, rxBurst, burstTime, dBm, RSSI, noise, TOA);
LOG(DEBUG) << "burst parameters: "
<< " time: " << burstTime
<< " RSSI: " << RSSI
<< " TOA: " << TOA
<< " bits: " << *rxBurst;
char burstString[gSlotLen+10];
burstString[0] = burstTime.TN();
for (int i = 0; i < 4; i++)
burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
burstString[5] = RSSI;
burstString[6] = (TOA >> 8) & 0x0ff;
burstString[7] = TOA & 0x0ff;
SoftVector::iterator burstItr = rxBurst->begin();
TOAint = (int) (TOA * 256.0 + 0.5); // round to closest integer
for (unsigned int i = 0; i < gSlotLen; i++) {
burstString[8+i] =(char) round((*burstItr++)*255.0);
}
burstString[gSlotLen+9] = '\0';
delete rxBurst;
char burstString[nbits + 10];
burstString[0] = burstTime.TN();
for (int i = 0; i < 4; i++)
burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
burstString[5] = (int)dBm;
burstString[6] = (TOAint >> 8) & 0x0ff;
burstString[7] = TOAint & 0x0ff;
SoftVector::iterator burstItr = rxBurst->begin();
for (unsigned i = 0; i < nbits; i++)
burstString[8 + i] = (char) round((*burstItr++) * 255.0);
burstString[nbits + 9] = '\0';
delete rxBurst;
mDataSockets[chan]->write(burstString, nbits + 10);
mDataSockets[chan]->write(burstString,gSlotLen+10);
}
}
void Transceiver::driveTxFIFO()
@@ -1053,7 +865,7 @@ void Transceiver::writeClockInterface()
LOG(INFO) << "ClockInterface: sending " << command;
mClockSocket.write(command, strlen(command) + 1);
mClockSocket->write(command, strlen(command) + 1);
mLastClockUpdateTime = mTransmitDeadlineClock;
@@ -1121,7 +933,15 @@ void *TxUpperLoopAdapter(TransceiverChannel *chan)
trx->setPriority(0.40);
while (1) {
trx->driveTxPriorityQueue(num);
bool stale = false;
// Flush the UDP packets until a successful transfer.
while (!trx->driveTxPriorityQueue(num)) {
stale = true;
}
if (!num && stale) {
// If a packet was stale, remind the GSM stack of the clock.
trx->writeClockInterface();
}
pthread_testcancel();
}
return NULL;

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -59,7 +59,7 @@ const dboardConfigType dboardConfig = TXA_RXB;
const double USRPDevice::masterClockRate = 52.0e6;
USRPDevice::USRPDevice(size_t sps)
USRPDevice::USRPDevice(size_t sps, size_t, bool)
{
LOG(INFO) << "creating USRP device...";
@@ -89,7 +89,7 @@ USRPDevice::USRPDevice(size_t sps)
#endif
}
int USRPDevice::open(const std::string &, int, bool)
int USRPDevice::open(const std::string &, bool)
{
writeLock.unlock();
@@ -600,8 +600,7 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;};
bool USRPDevice::setRxFreq(double wFreq) { return true;};
#endif
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
size_t chans, bool diversity, double)
RadioDevice *RadioDevice::make(size_t sps, size_t chans, bool diversity)
{
return new USRPDevice(tx_sps);
return new USRPDevice(sps, chans, diversity);
}

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
#ifndef _CONVERT_H_
#define _CONVERT_H_
void convert_float_short(short *out, const float *in, float scale, int len);
void convert_short_float(float *out, const short *in, int len);
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

@@ -3,26 +3,26 @@
void *convolve_h_alloc(int num);
int convolve_real(const float *x, int x_len,
const float *h, int h_len,
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(const float *x, int x_len,
const float *h, int h_len,
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(const float *x, int x_len,
const float *h, int h_len,
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(const float *x, int x_len,
const float *h, int h_len,
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);

View File

@@ -26,21 +26,21 @@
#endif
/* Base multiply and accumulate complex-real */
static void mac_real(const float *x, const float *h, float *y)
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(const float *x, const float *h, float *y)
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(const float *x, const float *h, float *y,
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)
@@ -48,7 +48,7 @@ static void mac_real_vec_n(const float *x, const float *h, float *y,
}
/* Base vector complex-complex multiply and accumulate */
static void mac_cmplx_vec_n(const float *x, const float *h, float *y,
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)
@@ -56,8 +56,8 @@ static void mac_cmplx_vec_n(const float *x, const float *h, float *y,
}
/* Base complex-real convolution */
int _base_convolve_real(const float *x, int x_len,
const float *h, int h_len,
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)
@@ -73,8 +73,8 @@ int _base_convolve_real(const float *x, int x_len,
}
/* Base complex-complex convolution */
int _base_convolve_complex(const float *x, int x_len,
const float *h, int h_len,
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)
@@ -110,8 +110,8 @@ int bounds_check(int x_len, int h_len, int y_len,
}
/* API: Non-aligned (no SSE) complex-real */
int base_convolve_real(const float *x, int x_len,
const float *h, int h_len,
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)
@@ -128,8 +128,8 @@ int base_convolve_real(const float *x, int x_len,
}
/* API: Non-aligned (no SSE) complex-complex */
int base_convolve_complex(const float *x, int x_len,
const float *h, int h_len,
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)

View File

@@ -1,112 +0,0 @@
/*
* Fast Fourier transform
*
* Copyright (C) 2012 Tom Tsou <tom@tsou.cc>
*
* 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 <stdlib.h>
#include <string.h>
#include <assert.h>
#include <fftw3.h>
#include "fft.h"
struct fft_hdl {
float *fft_in;
float *fft_out;
int len;
fftwf_plan fft_plan;
};
/*! \brief Initialize FFT backend
* \param[in] reverse FFT direction
* \param[in] m FFT length
* \param[in] istride input stride count
* \param[in] ostride output stride count
* \param[in] in input buffer (FFTW aligned)
* \param[in] out output buffer (FFTW aligned)
* \param[in] ooffset initial offset into output buffer
*
* If the reverse is non-NULL, then an inverse FFT will be used. This is a
* wrapper for advanced non-contiguous FFTW usage. See FFTW documentation for
* further details.
*
* http://www.fftw.org/doc/Advanced-Complex-DFTs.html
*
* It is currently unknown how the offset of the output buffer affects FFTW
* memory alignment.
*/
struct fft_hdl *init_fft(int reverse, int m, int istride, int ostride,
float *in, float *out, int ooffset)
{
int rank = 1;
int n[] = { m };
int howmany = istride;
int idist = 1;
int odist = 1;
int *inembed = n;
int *onembed = n;
fftwf_complex *obuffer, *ibuffer;
struct fft_hdl *hdl = (struct fft_hdl *) malloc(sizeof(struct fft_hdl));
if (!hdl)
return NULL;
int direction = FFTW_FORWARD;
if (reverse)
direction = FFTW_BACKWARD;
ibuffer = (fftwf_complex *) in;
obuffer = (fftwf_complex *) out + ooffset;
hdl->fft_in = in;
hdl->fft_out = out;
hdl->fft_plan = fftwf_plan_many_dft(rank, n, howmany,
ibuffer, inembed, istride, idist,
obuffer, onembed, ostride, odist,
direction, FFTW_MEASURE);
return hdl;
}
void *fft_malloc(size_t size)
{
return fftwf_malloc(size);
}
void fft_free(void *ptr)
{
free(ptr);
}
/*! \brief Free FFT backend resources
*/
void free_fft(struct fft_hdl *hdl)
{
fftwf_destroy_plan(hdl->fft_plan);
free(hdl);
}
/*! \brief Run multiple DFT operations with the initialized plan
* \param[in] hdl handle to an intitialized fft struct
*
* Input and output buffers are configured with init_fft().
*/
int cxvec_fft(struct fft_hdl *hdl)
{
fftwf_execute(hdl->fft_plan);
return 0;
}

View File

@@ -1,13 +0,0 @@
#ifndef _FFT_H_
#define _FFT_H_
struct fft_hdl;
struct fft_hdl *init_fft(int reverse, int m, int istride, int ostride,
float *in, float *out, int ooffset);
void *fft_malloc(size_t size);
void fft_free(void *ptr);
void free_fft(struct fft_hdl *hdl);
int cxvec_fft(struct fft_hdl *hdl);
#endif /* _FFT_H_ */

View File

@@ -37,17 +37,14 @@
* 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.
* downsampled to 1 sps. Default to 4 sps for all cases except for
* ARM and non-SIMD enabled architectures.
*/
#define DEFAULT_TX_SPS 4
/*
* Samples-per-symbol for uplink (receiver) path
* Do not modify this value. EDGE configures 4 sps automatically on
* B200/B210 devices only. Use of 4 sps on the receive path for other
* configurations is not supported.
*/
#define DEFAULT_RX_SPS 1
#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
@@ -66,20 +63,12 @@ struct trx_config {
std::string addr;
std::string dev_args;
unsigned port;
unsigned tx_sps;
unsigned rx_sps;
unsigned sps;
unsigned chans;
unsigned rtsc;
unsigned rach_delay;
bool extref;
bool gpsref;
Transceiver::FillerType filler;
bool filler;
bool diversity;
bool mcbts;
double offset;
double rssi_offset;
bool swap_channels;
bool edge;
};
ConfigurationTable gConfig;
@@ -129,7 +118,7 @@ bool testConfig()
*/
bool trx_setup_config(struct trx_config *config)
{
std::string refstr, fillstr, divstr, mcstr, edgestr;
std::string refstr, fillstr, divstr;
if (!testConfig())
return false;
@@ -165,42 +154,19 @@ bool trx_setup_config(struct trx_config *config)
config->diversity = DEFAULT_DIVERSITY;
}
if (!config->sps)
config->sps = DEFAULT_SPS;
if (!config->chans)
config->chans = DEFAULT_CHANS;
if (config->mcbts && ((config->chans < 0) || (config->chans > 5))) {
std::cout << "Unsupported number of channels" << std::endl;
return false;
}
/* Diversity only supported on 2 channels */
if (config->diversity)
config->chans = 2;
edgestr = config->edge ? "Enabled" : "Disabled";
refstr = config->extref ? "Enabled" : "Disabled";
fillstr = config->filler ? "Enabled" : "Disabled";
divstr = config->diversity ? "Enabled" : "Disabled";
mcstr = config->mcbts ? "Enabled" : "Disabled";
if (config->extref)
refstr = "External";
else if (config->gpsref)
refstr = "GPS";
else
refstr = "Internal";
switch (config->filler) {
case Transceiver::FILLER_DUMMY:
fillstr = "Dummy bursts";
break;
case Transceiver::FILLER_ZERO:
fillstr = "Disabled";
break;
case Transceiver::FILLER_NORM_RAND:
fillstr = "Normal busrts with random payload";
break;
case Transceiver::FILLER_EDGE_RAND:
fillstr = "EDGE busrts with random payload";
break;
case Transceiver::FILLER_ACCESS_RAND:
fillstr = "Access busrts with random payload";
break;
}
std::ostringstream ost("");
ost << "Config Settings" << std::endl;
@@ -209,16 +175,11 @@ bool trx_setup_config(struct trx_config *config)
ost << " TRX Base Port........... " << config->port << std::endl;
ost << " TRX Address............. " << config->addr << std::endl;
ost << " Channels................ " << config->chans << std::endl;
ost << " Tx Samples-per-Symbol... " << config->tx_sps << std::endl;
ost << " Rx Samples-per-Symbol... " << config->rx_sps << std::endl;
ost << " EDGE support............ " << edgestr << std::endl;
ost << " Reference............... " << refstr << std::endl;
ost << " Samples-per-Symbol...... " << config->sps << std::endl;
ost << " External Reference...... " << refstr << std::endl;
ost << " C0 Filler Table......... " << fillstr << std::endl;
ost << " Multi-Carrier........... " << mcstr << std::endl;
ost << " Diversity............... " << divstr << std::endl;
ost << " Tuning offset........... " << config->offset << std::endl;
ost << " RSSI to dBm offset...... " << config->rssi_offset << std::endl;
ost << " Swap channels........... " << config->swap_channels << std::endl;
std::cout << ost << std::endl;
return true;
@@ -238,23 +199,16 @@ RadioInterface *makeRadioInterface(struct trx_config *config,
switch (type) {
case RadioDevice::NORMAL:
radio = new RadioInterface(usrp, config->tx_sps,
config->rx_sps, config->chans);
radio = new RadioInterface(usrp, config->sps, config->chans);
break;
case RadioDevice::RESAMP_64M:
case RadioDevice::RESAMP_100M:
radio = new RadioInterfaceResamp(usrp, config->tx_sps,
config->rx_sps);
radio = new RadioInterfaceResamp(usrp,
config->sps, config->chans);
break;
case RadioDevice::DIVERSITY:
radio = new RadioInterfaceDiversity(usrp, config->tx_sps,
config->chans);
break;
case RadioDevice::MULTI_ARFCN:
radio = new RadioInterfaceMulti(usrp, config->tx_sps,
config->rx_sps, config->chans);
radio = new RadioInterfaceDiversity(usrp,
config->sps, config->chans);
break;
default:
LOG(ALERT) << "Unsupported radio interface configuration";
@@ -280,11 +234,9 @@ Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio)
Transceiver *trx;
VectorFIFO *fifo;
trx = new Transceiver(config->port, config->addr.c_str(),
config->tx_sps, config->rx_sps, config->chans,
GSM::Time(3,0), radio, config->rssi_offset);
if (!trx->init(config->filler, config->rtsc,
config->rach_delay, config->edge)) {
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;
@@ -329,20 +281,12 @@ static void print_help()
" -l Logging level (%s)\n"
" -i IP address of GSM core\n"
" -p Base port number\n"
" -e Enable EDGE receiver\n"
" -d Enable dual channel diversity receiver (deprecated)\n"
" -m Enable multi-ARFCN transceiver (default=disabled)\n"
" -d Enable dual channel diversity receiver\n"
" -x Enable external 10 MHz reference\n"
" -g Enable GPSDO reference\n"
" -s Tx samples-per-symbol (1 or 4)\n"
" -b Rx samples-per-symbol (1 or 4)\n"
" -s Samples-per-symbol (1 or 4)\n"
" -c Number of ARFCN channels (default=1)\n"
" -f Enable C0 filler table\n"
" -o Set baseband frequency offset (default=auto)\n"
" -r Random Normal Burst test mode with TSC\n"
" -A Random Access Burst test mode with delay\n"
" -R RSSI to dBm offset in dB (default=0)\n"
" -S Swap channels (UmTRX only)\n",
" -o Set baseband frequency offset (default=auto)\n",
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
}
@@ -351,22 +295,14 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
int option;
config->port = 0;
config->tx_sps = DEFAULT_TX_SPS;
config->rx_sps = DEFAULT_RX_SPS;
config->chans = DEFAULT_CHANS;
config->rtsc = 0;
config->rach_delay = 0;
config->sps = 0;
config->chans = 0;
config->extref = false;
config->gpsref = false;
config->filler = Transceiver::FILLER_ZERO;
config->mcbts = false;
config->filler = false;
config->diversity = false;
config->offset = 0.0;
config->rssi_offset = 0.0;
config->swap_channels = false;
config->edge = false;
while ((option = getopt(argc, argv, "ha:l:i:p:c:dmxgfo:s:b:r:A:R:Se")) != -1) {
while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:")) != -1) {
switch (option) {
case 'h':
print_help();
@@ -387,115 +323,39 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
case 'c':
config->chans = atoi(optarg);
break;
case 'm':
config->mcbts = true;
break;
case 'd':
config->diversity = true;
break;
case 'x':
config->extref = true;
break;
case 'g':
config->gpsref = true;
break;
case 'f':
config->filler = Transceiver::FILLER_DUMMY;
config->filler = true;
break;
case 'o':
config->offset = atof(optarg);
break;
case 's':
config->tx_sps = atoi(optarg);
break;
case 'b':
config->rx_sps = atoi(optarg);
break;
case 'r':
config->rtsc = atoi(optarg);
config->filler = Transceiver::FILLER_NORM_RAND;
break;
case 'A':
config->rach_delay = atoi(optarg);
config->filler = Transceiver::FILLER_ACCESS_RAND;
break;
case 'R':
config->rssi_offset = atof(optarg);
break;
case 'S':
config->swap_channels = true;
break;
case 'e':
config->edge = true;
config->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);
}
}
/* Force 4 SPS for EDGE or multi-ARFCN configurations */
if ((config->edge) || (config->mcbts)) {
config->tx_sps = 4;
config->rx_sps = 4;
}
if (config->gpsref && config->extref) {
printf("External and GPSDO references unavailable at the same time\n\n");
goto bad_config;
}
/* Special restrictions on (deprecated) diversity configuration */
if (config->diversity) {
if (config->mcbts || config->edge) {
std::cout << "Multi-carrier/EDGE diversity unsupported" << std::endl;
goto bad_config;
}
if (config->rx_sps != 1) {
std::cout << "Diversity only supported with 1 SPS" << std::endl;
goto bad_config;
}
if (config->chans != 2) {
std::cout << "Diversity only supported with 2 channels" << std::endl;
goto bad_config;
}
}
if (config->edge && (config->filler == Transceiver::FILLER_NORM_RAND))
config->filler = Transceiver::FILLER_EDGE_RAND;
if ((config->tx_sps != 1) && (config->tx_sps != 4) &&
(config->rx_sps != 1) && (config->rx_sps != 4)) {
printf("Unsupported samples-per-symbol %i\n\n", config->tx_sps);
goto bad_config;
}
if (config->rtsc > 7) {
printf("Invalid training sequence %i\n\n", config->rtsc);
goto bad_config;
}
if (config->rach_delay > 68) {
printf("RACH delay is too big %i\n\n", config->rach_delay);
goto bad_config;
}
return;
bad_config:
print_help();
exit(0);
}
int main(int argc, char *argv[])
{
int type, chans, ref;
int type, chans;
RadioDevice *usrp;
RadioInterface *radio = NULL;
Transceiver *trx = NULL;
RadioDevice::InterfaceType iface = RadioDevice::NORMAL;
struct trx_config config;
handle_options(argc, argv, &config);
@@ -513,19 +373,9 @@ int main(int argc, char *argv[])
srandom(time(NULL));
/* Create the low level device object */
if (config.mcbts)
iface = RadioDevice::MULTI_ARFCN;
if (config.extref)
ref = RadioDevice::REF_EXTERNAL;
else if (config.gpsref)
ref = RadioDevice::REF_GPS;
else
ref = RadioDevice::REF_INTERNAL;
usrp = RadioDevice::make(config.tx_sps, config.rx_sps, iface,
config.chans, config.offset);
type = usrp->open(config.dev_args, ref, config.swap_channels);
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;
@@ -541,6 +391,8 @@ int main(int argc, char *argv[])
if (!trx)
goto shutdown;
trx->start();
chans = trx->numChans();
std::cout << "-- Transceiver active with "
<< chans << " channel(s)" << std::endl;

View File

@@ -1,228 +0,0 @@
/*
* Segmented Ring Buffer
*
* Copyright (C) 2015 Ettus Research LLC
*
* Author: Tom Tsou <tom@tsou.cc>
*
* 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 <string.h>
#include <iostream>
#include "radioBuffer.h"
RadioBuffer::RadioBuffer(size_t numSegments, size_t segmentLen,
size_t hLen, bool outDirection)
: writeIndex(0), readIndex(0), availSamples(0)
{
if (!outDirection)
hLen = 0;
buffer = new float[2 * (hLen + numSegments * segmentLen)];
bufferLen = numSegments * segmentLen;
segments.resize(numSegments);
for (size_t i = 0; i < numSegments; i++)
segments[i] = &buffer[2 * (hLen + i * segmentLen)];
this->outDirection = outDirection;
this->numSegments = numSegments;
this->segmentLen = segmentLen;
this->hLen = hLen;
}
RadioBuffer::~RadioBuffer()
{
delete[] buffer;
}
void RadioBuffer::reset()
{
writeIndex = 0;
readIndex = 0;
availSamples = 0;
}
/*
* Output direction
*
* Return a pointer to the oldest segment or NULL if a complete segment is not
* available.
*/
const float *RadioBuffer::getReadSegment()
{
if (!outDirection) {
std::cout << "Invalid direction" << std::endl;
return NULL;
}
if (availSamples < segmentLen) {
std::cout << "Not enough samples " << std::endl;
std::cout << availSamples << " available per segment "
<< segmentLen << std::endl;
return NULL;
}
size_t num = readIndex / segmentLen;
if (num >= numSegments) {
std::cout << "Invalid segment" << std::endl;
return NULL;
} else if (!num) {
memcpy(buffer,
&buffer[2 * bufferLen],
hLen * 2 * sizeof(float));
}
availSamples -= segmentLen;
readIndex = (readIndex + segmentLen) % bufferLen;
return segments[num];
}
/*
* Output direction
*
* Write a non-segment length of samples to the buffer.
*/
bool RadioBuffer::write(const float *wr, size_t len)
{
if (!outDirection) {
std::cout << "Invalid direction" << std::endl;
return false;
}
if (availSamples + len > bufferLen) {
std::cout << "Insufficient space" << std::endl;
std::cout << bufferLen - availSamples << " available per write "
<< len << std::endl;
return false;
}
if (writeIndex + len <= bufferLen) {
memcpy(&buffer[2 * (writeIndex + hLen)],
wr, len * 2 * sizeof(float));
} else {
size_t len0 = bufferLen - writeIndex;
size_t len1 = len - len0;
memcpy(&buffer[2 * (writeIndex + hLen)], wr, len0 * 2 * sizeof(float));
memcpy(&buffer[2 * hLen], &wr[2 * len0], len1 * 2 * sizeof(float));
}
availSamples += len;
writeIndex = (writeIndex + len) % bufferLen;
return true;
}
bool RadioBuffer::zero(size_t len)
{
if (!outDirection) {
std::cout << "Invalid direction" << std::endl;
return false;
}
if (availSamples + len > bufferLen) {
std::cout << "Insufficient space" << std::endl;
std::cout << bufferLen - availSamples << " available per zero "
<< len << std::endl;
return false;
}
if (writeIndex + len <= bufferLen) {
memset(&buffer[2 * (writeIndex + hLen)],
0, len * 2 * sizeof(float));
} else {
size_t len0 = bufferLen - writeIndex;
size_t len1 = len - len0;
memset(&buffer[2 * (writeIndex + hLen)], 0, len0 * 2 * sizeof(float));
memset(&buffer[2 * hLen], 0, len1 * 2 * sizeof(float));
}
availSamples += len;
writeIndex = (writeIndex + len) % bufferLen;
return true;
}
/*
* Input direction
*/
float *RadioBuffer::getWriteSegment()
{
if (outDirection) {
std::cout << "Invalid direction" << std::endl;
return NULL;
}
if (bufferLen - availSamples < segmentLen) {
std::cout << "Insufficient samples" << std::endl;
std::cout << bufferLen - availSamples
<< " available for segment " << segmentLen
<< std::endl;
return NULL;
}
if (writeIndex % segmentLen) {
std::cout << "Internal segment error" << std::endl;
return NULL;
}
size_t num = writeIndex / segmentLen;
if (num >= numSegments)
return NULL;
availSamples += segmentLen;
writeIndex = (writeIndex + segmentLen) % bufferLen;
return segments[num];
}
bool RadioBuffer::zeroWriteSegment()
{
float *segment = getWriteSegment();
if (!segment)
return false;
memset(segment, 0, segmentLen * 2 * sizeof(float));
return true;
}
bool RadioBuffer::read(float *rd, size_t len)
{
if (outDirection) {
std::cout << "Invalid direction" << std::endl;
return false;
}
if (availSamples < len) {
std::cout << "Insufficient samples" << std::endl;
std::cout << availSamples << " available for "
<< len << std::endl;
return false;
}
if (readIndex + len <= bufferLen) {
memcpy(rd, &buffer[2 * readIndex], len * 2 * sizeof(float));
} else {
size_t len0 = bufferLen - readIndex;
size_t len1 = len - len0;
memcpy(rd, &buffer[2 * readIndex], len0 * 2 * sizeof(float));
memcpy(&rd[2 * len0], buffer, len1 * 2 * sizeof(float));
}
availSamples -= len;
readIndex = (readIndex + len) % bufferLen;
return true;
}

View File

@@ -1,45 +0,0 @@
#include <stdlib.h>
#include <stddef.h>
#include <vector>
class RadioBuffer {
public:
RadioBuffer(size_t numSegments, size_t segmentLen,
size_t hLen, bool outDirection);
~RadioBuffer();
const size_t getSegmentLen() { return segmentLen; };
const size_t getNumSegments() { return numSegments; };
const size_t getAvailSamples() { return availSamples; };
const size_t getAvailSegments() { return availSamples / segmentLen; };
const size_t getFreeSamples()
{
return bufferLen - availSamples;
}
const size_t getFreeSegments()
{
return getFreeSamples() / segmentLen;
}
void reset();
/* Output direction */
const float *getReadSegment();
bool write(const float *wr, size_t len);
bool zero(size_t len);
/* Input direction */
float *getWriteSegment();
bool zeroWriteSegment();
bool read(float *rd, size_t len);
private:
size_t writeIndex, readIndex, availSamples;
size_t bufferLen, numSegments, segmentLen, hLen;
float *buffer;
std::vector<float *> segments;
bool outDirection;
};

View File

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

View File

@@ -22,8 +22,7 @@
#include "config.h"
#endif
#define GSMRATE (1625e3/6)
#define MCBTS_SPACING 800000.0
#define GSMRATE 1625e3/6
/** a 64-bit virtual timestamp for radio data */
typedef unsigned long long TIMESTAMP;
@@ -36,25 +35,13 @@ class RadioDevice {
enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED };
/* Radio interface types */
enum InterfaceType {
NORMAL,
RESAMP_64M,
RESAMP_100M,
MULTI_ARFCN,
DIVERSITY,
};
enum RadioInterfaceType { NORMAL, RESAMP_64M, RESAMP_100M, DIVERSITY };
enum ReferenceType {
REF_INTERNAL,
REF_EXTERNAL,
REF_GPS,
};
static RadioDevice *make(size_t tx_sps, size_t rx_sps, InterfaceType type,
size_t chans = 1, double offset = 0.0);
static RadioDevice *make(size_t sps, size_t chans = 1,
bool diversity = false, double offset = 0.0);
/** Initialize the USRP */
virtual int open(const std::string &args, int ref, bool swap_channels)=0;
virtual int open(const std::string &args = "", bool extref = false)=0;
virtual ~RadioDevice() { }

View File

@@ -1,23 +1,26 @@
/*
* Radio device interface
*
* Copyright (C) 2008-2014 Free Software Foundation, Inc.
* Copyright (C) 2015 Ettus Research LLC
*
* 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.
*/
* Copyright 2008, 2009 Free Software Foundation, Inc.
*
* This software is distributed under the terms of the GNU Affero Public License.
* See the COPYING file in the main directory for details.
*
* 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 "radioInterface.h"
#include "Resampler.h"
@@ -30,11 +33,11 @@ extern "C" {
#define CHUNK 625
#define NUMCHUNKS 4
RadioInterface::RadioInterface(RadioDevice *wRadio, size_t tx_sps,
size_t rx_sps, size_t chans, size_t diversity,
RadioInterface::RadioInterface(RadioDevice *wRadio,
size_t sps, size_t chans, size_t diversity,
int wReceiveOffset, GSM::Time wStartTime)
: mRadio(wRadio), mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans),
mMIMO(diversity), underrun(false), overrun(false),
: mRadio(wRadio), mSPSTx(sps), mSPSRx(1), mChans(chans), mMIMO(diversity),
sendCursor(0), recvCursor(0), underrun(false), overrun(false),
receiveOffset(wReceiveOffset), mOn(false)
{
mClock.set(wStartTime);
@@ -62,20 +65,33 @@ bool RadioInterface::init(int type)
powerScaling.resize(mChans);
for (size_t i = 0; i < mChans; i++) {
sendBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSTx, 0, true);
recvBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSRx, 0, false);
sendBuffer[i] = new signalVector(CHUNK * mSPSTx);
recvBuffer[i] = new signalVector(NUMCHUNKS * CHUNK * mSPSRx);
convertSendBuffer[i] = new short[CHUNK * mSPSTx * 2];
convertRecvBuffer[i] = new short[CHUNK * mSPSRx * 2];
powerScaling[i] = 1.0;
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);
@@ -90,50 +106,55 @@ double RadioInterface::fullScaleOutputValue(void) {
return mRadio->fullScaleOutputValue();
}
int RadioInterface::setPowerAttenuation(int atten, size_t chan)
void RadioInterface::setPowerAttenuation(double atten, size_t chan)
{
double rfGain, digAtten;
if (chan >= mChans) {
LOG(ALERT) << "Invalid channel requested";
return -1;
return;
}
if (atten < 0.0)
atten = 0.0;
rfGain = mRadio->setTxGain(mRadio->maxTxGain() - (double) atten, chan);
digAtten = (double) atten - mRadio->maxTxGain() + rfGain;
rfGain = mRadio->setTxGain(mRadio->maxTxGain() - atten, chan);
digAtten = atten - mRadio->maxTxGain() + rfGain;
if (digAtten < 1.0)
powerScaling[chan] = 1.0;
else
powerScaling[chan] = 1.0 / sqrt(pow(10, digAtten / 10.0));
return atten;
}
int RadioInterface::radioifyVector(signalVector &wVector,
size_t chan, bool zero)
float *retVector,
bool zero)
{
if (zero)
sendBuffer[chan]->zero(wVector.size());
else
sendBuffer[chan]->write((float *) wVector.begin(), wVector.size());
if (zero) {
memset(retVector, 0, wVector.size() * 2 * sizeof(float));
return wVector.size();
}
memcpy(retVector, wVector.begin(), wVector.size() * 2 * sizeof(float));
return wVector.size();
}
int RadioInterface::unRadioifyVector(signalVector *newVector, size_t chan)
int RadioInterface::unRadioifyVector(float *floatVector,
signalVector& newVector)
{
if (newVector->size() > recvBuffer[chan]->getAvailSamples()) {
signalVector::iterator itr = newVector.begin();
if (newVector.size() > recvCursor) {
LOG(ALERT) << "Insufficient number of samples in receive buffer";
return -1;
}
recvBuffer[chan]->read((float *) newVector->begin(), newVector->size());
for (size_t i = 0; i < newVector.size(); i++) {
*itr++ = Complex<float>(floatVector[2 * i + 0],
floatVector[2 * i + 1]);
}
return newVector->size();
return newVector.size();
}
bool RadioInterface::tuneTx(double freq, size_t chan)
@@ -146,25 +167,15 @@ bool RadioInterface::tuneRx(double freq, size_t chan)
return mRadio->setRxFreq(freq, chan);
}
bool RadioInterface::start()
{
if (mOn)
return true;
LOG(INFO) << "Starting radio device";
void RadioInterface::start()
{
LOG(INFO) << "Starting radio";
#ifdef USRP1
mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter,
(void*)this);
#endif
if (!mRadio->start())
return false;
for (size_t i = 0; i < mChans; i++) {
sendBuffer[i]->reset();
recvBuffer[i]->reset();
}
mRadio->start();
writeTimestamp = mRadio->initialWriteTimestamp();
readTimestamp = mRadio->initialReadTimestamp();
@@ -173,23 +184,6 @@ bool RadioInterface::start()
mOn = true;
LOG(INFO) << "Radio started";
return true;
}
/*
* Stop the radio device
*
* This is a pass-through call to the device interface. Because the underlying
* stop command issuance generally doesn't return confirmation on device status,
* this call will only return false if the device is already stopped.
*/
bool RadioInterface::stop()
{
if (!mOn || !mRadio->stop())
return false;
mOn = false;
return true;
}
#ifdef USRP1
@@ -214,10 +208,14 @@ void RadioInterface::driveTransmitRadio(std::vector<signalVector *> &bursts,
if (!mOn)
return;
for (size_t i = 0; i < mChans; i++)
radioifyVector(*bursts[i], i, zeros[i]);
for (size_t i = 0; i < mChans; i++) {
radioifyVector(*bursts[i],
(float *) (sendBuffer[i]->begin() + sendCursor), zeros[i]);
}
while (pushBuffer());
sendCursor += bursts[0]->size();
pushBuffer();
}
bool RadioInterface::driveReceiveRadio()
@@ -232,14 +230,10 @@ bool RadioInterface::driveReceiveRadio()
GSM::Time rcvClock = mClock.get();
rcvClock.decTN(receiveOffset);
unsigned tN = rcvClock.TN();
int recvSz = recvBuffer[0]->getAvailSamples();
int recvSz = recvCursor;
int readSz = 0;
const int symbolsPerSlot = gSlotLen + 8;
int burstSize;
if (mSPSRx == 4)
burstSize = 625;
else
burstSize = symbolsPerSlot + (tN % 4 == 0);
int burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
/*
* Pre-allocate head room for the largest correlation size
@@ -255,8 +249,11 @@ bool RadioInterface::driveReceiveRadio()
for (size_t i = 0; i < mChans; i++) {
burst = new radioVector(rcvClock, burstSize, head, mMIMO);
for (size_t n = 0; n < mMIMO; n++)
unRadioifyVector(burst->getVector(n), i);
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);
@@ -266,12 +263,22 @@ bool RadioInterface::driveReceiveRadio()
mClock.incTN();
rcvClock.incTN();
readSz += burstSize;
recvSz -= burstSize;
tN = rcvClock.TN();
if (mSPSRx != 4)
burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
}
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;
}
return true;
@@ -295,66 +302,74 @@ VectorFIFO* RadioInterface::receiveFIFO(size_t chan)
double RadioInterface::setRxGain(double dB, size_t chan)
{
return mRadio->setRxGain(dB, chan);
if (mRadio)
return mRadio->setRxGain(dB, chan);
else
return -1;
}
double RadioInterface::getRxGain(size_t chan)
{
return mRadio->getRxGain(chan);
if (mRadio)
return mRadio->getRxGain(chan);
else
return -1;
}
/* Receive a timestamped chunk from the device */
void RadioInterface::pullBuffer()
{
bool local_underrun;
size_t numRecv, segmentLen = recvBuffer[0]->getSegmentLen();
int num_recv;
float *output;
if (recvBuffer[0]->getFreeSegments() <= 0)
if (recvCursor > recvBuffer[0]->size() - CHUNK)
return;
/* Outer buffer access size is fixed */
numRecv = mRadio->readSamples(convertRecvBuffer,
segmentLen,
&overrun,
readTimestamp,
&local_underrun);
if (numRecv != segmentLen) {
LOG(ALERT) << "Receive error " << numRecv;
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++) {
convert_short_float(recvBuffer[i]->getWriteSegment(),
convertRecvBuffer[i],
segmentLen * 2);
output = (float *) (recvBuffer[i]->begin() + recvCursor);
convert_short_float(output, convertRecvBuffer[i], 2 * num_recv);
}
underrun |= local_underrun;
readTimestamp += numRecv;
readTimestamp += num_recv;
recvCursor += num_recv;
}
/* Send timestamped chunk to the device with arbitrary size */
bool RadioInterface::pushBuffer()
void RadioInterface::pushBuffer()
{
size_t numSent, segmentLen = sendBuffer[0]->getSegmentLen();
int num_sent;
if (sendBuffer[0]->getAvailSegments() < 1)
return false;
if (sendCursor < CHUNK)
return;
if (sendCursor > sendBuffer[0]->size())
LOG(ALERT) << "Send buffer overflow";
for (size_t i = 0; i < mChans; i++) {
convert_float_short(convertSendBuffer[i],
(float *) sendBuffer[i]->getReadSegment(),
powerScaling[i],
segmentLen * 2);
(float *) sendBuffer[i]->begin(),
powerScaling[i], 2 * sendCursor);
}
/* Send the all samples in the send buffer */
numSent = mRadio->writeSamples(convertSendBuffer,
segmentLen,
&underrun,
writeTimestamp);
writeTimestamp += numSent;
return true;
/* Send the all samples in the send buffer */
num_sent = mRadio->writeSamples(convertSendBuffer,
sendCursor,
&underrun,
writeTimestamp);
writeTimestamp += num_sent;
sendCursor = 0;
}

View File

@@ -20,10 +20,7 @@
#include "radioDevice.h"
#include "radioVector.h"
#include "radioClock.h"
#include "radioBuffer.h"
#include "Resampler.h"
#include "Channelizer.h"
#include "Synthesis.h"
static const unsigned gSlotLen = 148; ///< number of symbols per slot, not counting guard periods
@@ -43,8 +40,10 @@ protected:
size_t mChans;
size_t mMIMO;
std::vector<RadioBuffer *> sendBuffer;
std::vector<RadioBuffer *> recvBuffer;
std::vector<signalVector *> sendBuffer;
std::vector<signalVector *> recvBuffer;
unsigned sendCursor;
unsigned recvCursor;
std::vector<short *> convertRecvBuffer;
std::vector<short *> convertSendBuffer;
@@ -62,14 +61,16 @@ protected:
private:
/** format samples to USRP */
int radioifyVector(signalVector &wVector, size_t chan, bool zero);
/** format samples to USRP */
int radioifyVector(signalVector &wVector,
float *floatVector,
bool zero);
/** format samples from USRP */
int unRadioifyVector(signalVector *wVector, size_t chan);
int unRadioifyVector(float *floatVector, signalVector &wVector);
/** push GSM bursts into the transmit buffer */
virtual bool pushBuffer(void);
virtual void pushBuffer(void);
/** pull GSM bursts from the receive buffer */
virtual void pullBuffer(void);
@@ -77,16 +78,15 @@ private:
public:
/** start the interface */
bool start();
bool stop();
void start();
/** intialization */
virtual bool init(int type);
virtual void close();
/** constructor */
RadioInterface(RadioDevice* wRadio, size_t tx_sps, size_t rx_sps,
size_t chans = 1, size_t diversity = 1,
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));
/** destructor */
@@ -102,7 +102,7 @@ public:
RadioClock* getClock(void) { return &mClock;};
/** set transmit frequency */
virtual bool tuneTx(double freq, size_t chan = 0);
bool tuneTx(double freq, size_t chan = 0);
/** set receive frequency */
virtual bool tuneRx(double freq, size_t chan = 0);
@@ -120,7 +120,7 @@ public:
/** drive reception of GSM bursts */
bool driveReceiveRadio();
int setPowerAttenuation(int atten, size_t chan = 0);
void setPowerAttenuation(double atten, size_t chan = 0);
/** returns the full-scale transmit amplitude **/
double fullScaleInputValue();
@@ -150,52 +150,30 @@ void *AlignRadioServiceLoopAdapter(RadioInterface*);
#endif
class RadioInterfaceResamp : public RadioInterface {
private:
signalVector *innerSendBuffer;
signalVector *outerSendBuffer;
signalVector *innerRecvBuffer;
signalVector *outerRecvBuffer;
bool pushBuffer();
void pushBuffer();
void pullBuffer();
public:
RadioInterfaceResamp(RadioDevice* wRadio, size_t tx_sps, size_t rx_sps);
RadioInterfaceResamp(RadioDevice* wRadio, size_t wSPS = 4, size_t chans = 1);
~RadioInterfaceResamp();
bool init(int type);
void close();
};
class RadioInterfaceMulti : public RadioInterface {
private:
bool pushBuffer();
void pullBuffer();
signalVector *outerSendBuffer;
signalVector *outerRecvBuffer;
std::vector<signalVector *> history;
std::vector<bool> active;
Resampler *dnsampler;
Resampler *upsampler;
Channelizer *channelizer;
Synthesis *synthesis;
public:
RadioInterfaceMulti(RadioDevice* radio, size_t tx_sps,
size_t rx_sps, size_t chans = 1);
~RadioInterfaceMulti();
bool init(int type);
void close();
bool tuneTx(double freq, size_t chan);
bool tuneRx(double freq, size_t chan);
double setRxGain(double dB, size_t chan);
};
class RadioInterfaceDiversity : public RadioInterface {
public:
RadioInterfaceDiversity(RadioDevice* wRadio, size_t tx_sps, size_t chans);
RadioInterfaceDiversity(RadioDevice* wRadio,
size_t sps = 4, size_t chans = 2);
~RadioInterfaceDiversity();
@@ -204,7 +182,7 @@ public:
bool tuneRx(double freq, size_t chan);
private:
Resampler *dnsampler;
std::vector<Resampler *> dnsamplers;
std::vector<float> phases;
signalVector *outerRecvBuffer;

View File

@@ -51,8 +51,8 @@ static size_t resamp_outrate = 0;
static size_t resamp_outchunk = 0;
RadioInterfaceDiversity::RadioInterfaceDiversity(RadioDevice *wRadio,
size_t tx_sps, size_t chans)
: RadioInterface(wRadio, tx_sps, 1, chans, 2), outerRecvBuffer(NULL),
size_t sps, size_t chans)
: RadioInterface(wRadio, sps, chans, 2), outerRecvBuffer(NULL),
mDiversity(false), mFreqSpacing(0.0)
{
}
@@ -65,11 +65,14 @@ RadioInterfaceDiversity::~RadioInterfaceDiversity()
void RadioInterfaceDiversity::close()
{
delete outerRecvBuffer;
delete dnsampler;
dnsampler = NULL;
outerRecvBuffer = NULL;
for (size_t i = 0; i < dnsamplers.size(); i++) {
delete dnsamplers[i];
dnsamplers[i] = NULL;
}
if (recvBuffer.size())
recvBuffer[0] = NULL;
@@ -95,16 +98,15 @@ bool RadioInterfaceDiversity::setupDiversityChannels()
return false;
}
dnsampler = new Resampler(resamp_inrate, resamp_outrate);
if (!dnsampler->init()) {
LOG(ALERT) << "Rx resampler failed to initialize";
return false;
}
/* One Receive buffer and downsampler per diversity channel */
for (size_t i = 0; i < mMIMO * mChans; i++) {
recvBuffer[i] = new RadioBuffer(NUMCHUNKS,
resamp_inchunk, 0, false);
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;
@@ -113,7 +115,7 @@ bool RadioInterfaceDiversity::setupDiversityChannels()
/* Initialize I/O specific objects */
bool RadioInterfaceDiversity::init(int type)
{
int outer_rx_len;
int tx_len, outer_rx_len;
if ((mMIMO != 2) || (mChans != 2)) {
LOG(ALERT) << "Unsupported channel configuration " << mChans;
@@ -126,11 +128,13 @@ bool RadioInterfaceDiversity::init(int type)
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++) {
@@ -138,11 +142,11 @@ bool RadioInterfaceDiversity::init(int type)
convertRecvBuffer[i] = new short[outer_rx_len * 2];
/* Send buffers (not-resampled) */
sendBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSTx, 0, true);
convertSendBuffer[i] = new short[CHUNK * mSPSTx * 2];
sendBuffer[i] = new signalVector(tx_len);
convertSendBuffer[i] = new short[tx_len * 2];
}
outerRecvBuffer = new signalVector(outer_rx_len, dnsampler->len());
outerRecvBuffer = new signalVector(outer_rx_len, dnsamplers[0]->len());
return true;
}
@@ -178,7 +182,7 @@ void RadioInterfaceDiversity::pullBuffer()
signalVector *shift, *base;
float *in, *out, rate = -mFreqSpacing * 2.0 * M_PI / 1.08333333e6;
if (recvBuffer[0]->getFreeSegments() <= 0)
if (recvCursor > recvBuffer[0]->size() - resamp_inchunk)
return;
/* Outer buffer access size is fixed */
@@ -207,10 +211,10 @@ void RadioInterfaceDiversity::pullBuffer()
/* Diversity path 1 */
base = outerRecvBuffer;
in = (float *) base->begin();
out = (float *) recvBuffer[path0]->getWriteSegment();
out = (float *) (recvBuffer[path0]->begin() + recvCursor);
rc = dnsampler->rotate(in, resamp_outchunk,
out, resamp_inchunk);
rc = dnsamplers[2 * i + 0]->rotate(in, resamp_outchunk,
out, resamp_inchunk);
if (rc < 0) {
LOG(ALERT) << "Sample rate downsampling error";
}
@@ -222,15 +226,15 @@ void RadioInterfaceDiversity::pullBuffer()
/* Diversity path 2 */
shift = new signalVector(base->size(), base->getStart());
in = (float *) shift->begin();
out = (float *) recvBuffer[path1]->getWriteSegment();
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 = dnsampler->rotate(in, resamp_outchunk,
out, resamp_inchunk);
rc = dnsamplers[2 * i + 1]->rotate(in, resamp_outchunk,
out, resamp_inchunk);
if (rc < 0) {
LOG(ALERT) << "Sample rate downsampling error";
}
@@ -240,4 +244,5 @@ void RadioInterfaceDiversity::pullBuffer()
underrun |= local_underrun;
readTimestamp += (TIMESTAMP) resamp_outchunk;
recvCursor += resamp_inchunk;
}

View File

@@ -1,391 +0,0 @@
/*
* Multi-carrier radio interface
*
* Copyright (C) 2016 Ettus Research LLC
*
* Author: Tom Tsou <tom.tsou@ettus.com>
*
* 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_INRATE 65
#define RESAMP_OUTRATE (96 / 2)
/* Universal resampling parameters */
#define NUMCHUNKS 24
#define MCHANS 4
RadioInterfaceMulti::RadioInterfaceMulti(RadioDevice *radio, size_t tx_sps,
size_t rx_sps, size_t chans)
: RadioInterface(radio, tx_sps, rx_sps, chans),
outerSendBuffer(NULL), outerRecvBuffer(NULL),
dnsampler(NULL), upsampler(NULL), channelizer(NULL), synthesis(NULL)
{
}
RadioInterfaceMulti::~RadioInterfaceMulti()
{
close();
}
void RadioInterfaceMulti::close()
{
delete outerSendBuffer;
delete outerRecvBuffer;
delete dnsampler;
delete upsampler;
delete channelizer;
delete synthesis;
outerSendBuffer = NULL;
outerRecvBuffer = NULL;
dnsampler = NULL;
upsampler = NULL;
channelizer = NULL;
synthesis = NULL;
mReceiveFIFO.resize(0);
powerScaling.resize(0);
history.resize(0);
active.resize(0);
RadioInterface::close();
}
static int getLogicalChan(size_t pchan, size_t chans)
{
switch (chans) {
case 1:
if (pchan == 0)
return 0;
else
return -1;
break;
case 2:
if (pchan == 0)
return 0;
if (pchan == 3)
return 1;
else
return -1;
break;
case 3:
if (pchan == 1)
return 0;
if (pchan == 0)
return 1;
if (pchan == 3)
return 2;
else
return -1;
break;
default:
break;
};
return -1;
}
static int getFreqShift(size_t chans)
{
switch (chans) {
case 1:
return 0;
case 2:
return 0;
case 3:
return 1;
default:
break;
};
return -1;
}
/* Initialize I/O specific objects */
bool RadioInterfaceMulti::init(int type)
{
float cutoff = 1.0f;
size_t inchunk = 0, outchunk = 0;
if (mChans > MCHANS - 1) {
LOG(ALERT) << "Invalid channel configuration " << mChans;
return false;
}
close();
sendBuffer.resize(mChans);
recvBuffer.resize(mChans);
convertSendBuffer.resize(1);
convertRecvBuffer.resize(1);
mReceiveFIFO.resize(mChans);
powerScaling.resize(mChans);
history.resize(mChans);
active.resize(MCHANS, false);
inchunk = RESAMP_INRATE * 4;
outchunk = RESAMP_OUTRATE * 4;
if (inchunk * NUMCHUNKS < 625 * 2) {
LOG(ALERT) << "Invalid inner chunk size " << inchunk;
return false;
}
dnsampler = new Resampler(RESAMP_INRATE, RESAMP_OUTRATE);
if (!dnsampler->init(1.0)) {
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;
}
channelizer = new Channelizer(MCHANS, outchunk);
if (!channelizer->init()) {
LOG(ALERT) << "Rx channelizer failed to initialize";
return false;
}
synthesis = new Synthesis(MCHANS, outchunk);
if (!synthesis->init()) {
LOG(ALERT) << "Tx synthesis filter 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.
*/
for (size_t i = 0; i < mChans; i++) {
sendBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk,
upsampler->len(), true);
recvBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk,
0, false);
history[i] = new signalVector(dnsampler->len());
synthesis->resetBuffer(i);
}
outerSendBuffer = new signalVector(synthesis->outputLen());
outerRecvBuffer = new signalVector(channelizer->inputLen());
convertSendBuffer[0] = new short[2 * synthesis->outputLen()];
convertRecvBuffer[0] = new short[2 * channelizer->inputLen()];
/* Configure channels */
switch (mChans) {
case 1:
active[0] = true;
break;
case 2:
active[0] = true;
active[3] = true;
break;
case 3:
active[0] = true;
active[1] = true;
active[3] = true;
break;
default:
LOG(ALERT) << "Unsupported channel combination";
return false;
}
return true;
}
/* Receive a timestamped chunk from the device */
void RadioInterfaceMulti::pullBuffer()
{
bool local_underrun;
size_t num;
float *buf;
if (recvBuffer[0]->getFreeSegments() <= 0)
return;
/* Outer buffer access size is fixed */
num = mRadio->readSamples(convertRecvBuffer,
outerRecvBuffer->size(),
&overrun,
readTimestamp,
&local_underrun);
if (num != channelizer->inputLen()) {
LOG(ALERT) << "Receive error " << num << ", " << channelizer->inputLen();
return;
}
convert_short_float((float *) outerRecvBuffer->begin(),
convertRecvBuffer[0], 2 * outerRecvBuffer->size());
underrun |= local_underrun;
readTimestamp += num;
channelizer->rotate((float *) outerRecvBuffer->begin(),
outerRecvBuffer->size());
for (size_t pchan = 0; pchan < MCHANS; pchan++) {
if (!active[pchan])
continue;
int lchan = getLogicalChan(pchan, mChans);
if (lchan < 0) {
LOG(ALERT) << "Invalid logical channel " << pchan;
continue;
}
/*
* Update history by writing into the head portion of the
* channelizer output buffer. For this to work, filter length of
* the polyphase channelizer partition filter should be equal to
* or larger than the resampling filter.
*/
buf = channelizer->outputBuffer(pchan);
size_t cLen = channelizer->outputLen();
size_t hLen = dnsampler->len();
size_t hSize = 2 * hLen * sizeof(float);
memcpy(&buf[2 * -hLen], history[lchan]->begin(), hSize);
memcpy(history[lchan]->begin(), &buf[2 * (cLen - hLen)], hSize);
float *wr_segment = recvBuffer[lchan]->getWriteSegment();
/* Write to the end of the inner receive buffer */
if (!dnsampler->rotate(channelizer->outputBuffer(pchan),
channelizer->outputLen(),
wr_segment,
recvBuffer[lchan]->getSegmentLen())) {
LOG(ALERT) << "Sample rate upsampling error";
}
}
}
/* Send a timestamped chunk to the device */
bool RadioInterfaceMulti::pushBuffer()
{
if (sendBuffer[0]->getAvailSegments() <= 0)
return false;
for (size_t pchan = 0; pchan < MCHANS; pchan++) {
if (!active[pchan]) {
synthesis->resetBuffer(pchan);
continue;
}
int lchan = getLogicalChan(pchan, mChans);
if (lchan < 0) {
LOG(ALERT) << "Invalid logical channel " << pchan;
continue;
}
if (!upsampler->rotate(sendBuffer[lchan]->getReadSegment(),
sendBuffer[lchan]->getSegmentLen(),
synthesis->inputBuffer(pchan),
synthesis->inputLen())) {
LOG(ALERT) << "Sample rate downsampling error";
}
}
synthesis->rotate((float *) outerSendBuffer->begin(),
outerSendBuffer->size());
convert_float_short(convertSendBuffer[0],
(float *) outerSendBuffer->begin(),
1.0 / (float) mChans, 2 * outerSendBuffer->size());
size_t num = mRadio->writeSamples(convertSendBuffer,
outerSendBuffer->size(),
&underrun,
writeTimestamp);
if (num != outerSendBuffer->size()) {
LOG(ALERT) << "Transmit error " << num;
}
writeTimestamp += num;
return true;
}
/* Frequency comparison limit */
#define FREQ_DELTA_LIMIT 10.0
static bool fltcmp(double a, double b)
{
return fabs(a - b) < FREQ_DELTA_LIMIT ? true : false;
}
bool RadioInterfaceMulti::tuneTx(double freq, size_t chan)
{
if (chan >= mChans)
return false;
double shift = (double) getFreqShift(mChans);
if (!chan)
return mRadio->setTxFreq(freq + shift * MCBTS_SPACING);
double center = mRadio->getTxFreq();
if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
LOG(NOTICE) << "Channel " << chan << " RF frequency offset is "
<< freq / 1e6 << " MHz";
}
return true;
}
bool RadioInterfaceMulti::tuneRx(double freq, size_t chan)
{
if (chan >= mChans)
return false;
double shift = (double) getFreqShift(mChans);
if (!chan)
return mRadio->setRxFreq(freq + shift * MCBTS_SPACING);
double center = mRadio->getRxFreq();
if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) {
LOG(NOTICE) << "Channel " << chan << " RF frequency offset is "
<< freq / 1e6 << " MHz";
}
return true;
}
double RadioInterfaceMulti::setRxGain(double db, size_t chan)
{
if (!chan)
return mRadio->setRxGain(db);
else
return mRadio->getRxGain();
}

View File

@@ -1,10 +1,8 @@
/*
* Radio device interface with sample rate conversion
* Written by Thomas Tsou <tom@tsou.cc>
*
* Copyright (C) 2011-2014 Free Software Foundation, Inc.
* Copyright (C) 2015 Ettus Research LLC
*
* Author: Tom 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
@@ -58,9 +56,10 @@ static size_t resamp_outrate = 0;
static size_t resamp_outchunk = 0;
RadioInterfaceResamp::RadioInterfaceResamp(RadioDevice *wRadio,
size_t tx_sps, size_t rx_sps)
: RadioInterface(wRadio, tx_sps, rx_sps, 1),
outerSendBuffer(NULL), outerRecvBuffer(NULL)
size_t sps, size_t chans)
: RadioInterface(wRadio, sps, chans),
innerSendBuffer(NULL), outerSendBuffer(NULL),
innerRecvBuffer(NULL), outerRecvBuffer(NULL)
{
}
@@ -71,13 +70,17 @@ RadioInterfaceResamp::~RadioInterfaceResamp()
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;
@@ -96,6 +99,11 @@ bool RadioInterfaceResamp::init(int type)
{
float cutoff = 1.0f;
if (mChans != 1) {
LOG(ALERT) << "Unsupported channel configuration " << mChans;
return false;
}
close();
sendBuffer.resize(1);
@@ -120,8 +128,13 @@ bool RadioInterfaceResamp::init(int type)
return false;
}
resamp_inchunk = resamp_inrate * 4 * mSPSRx;
resamp_outchunk = resamp_outrate * 4 * mSPSRx;
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;
@@ -144,18 +157,21 @@ bool RadioInterfaceResamp::init(int type)
* and requires headroom equivalent to the filter length. Low
* rate buffers are allocated in the main radio interface code.
*/
sendBuffer[0] = new RadioBuffer(NUMCHUNKS, resamp_inchunk,
upsampler->len(), true);
recvBuffer[0] = new RadioBuffer(NUMCHUNKS * 20, resamp_inchunk, 0, false);
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;
}
@@ -165,7 +181,7 @@ void RadioInterfaceResamp::pullBuffer()
bool local_underrun;
int rc, num_recv;
if (recvBuffer[0]->getFreeSegments() <= 0)
if (recvCursor > innerRecvBuffer->size() - resamp_inchunk)
return;
/* Outer buffer access size is fixed */
@@ -188,47 +204,57 @@ void RadioInterfaceResamp::pullBuffer()
/* Write to the end of the inner receive buffer */
rc = dnsampler->rotate((float *) outerRecvBuffer->begin(),
resamp_outchunk,
recvBuffer[0]->getWriteSegment(),
(float *) (innerRecvBuffer->begin() + recvCursor),
resamp_inchunk);
if (rc < 0) {
LOG(ALERT) << "Sample rate upsampling error";
}
/* Set history for the next chunk */
outerRecvBuffer->updateHistory();
recvCursor += resamp_inchunk;
}
/* Send a timestamped chunk to the device */
bool RadioInterfaceResamp::pushBuffer()
void RadioInterfaceResamp::pushBuffer()
{
int rc;
size_t numSent;
int rc, chunks, num_sent;
int inner_len, outer_len;
if (sendBuffer[0]->getAvailSegments() <= 0)
return false;
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(sendBuffer[0]->getReadSegment(),
resamp_inchunk,
(float *) outerSendBuffer->begin(),
resamp_outchunk);
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 * resamp_outchunk);
powerScaling[0], 2 * outer_len);
numSent = mRadio->writeSamples(convertSendBuffer,
resamp_outchunk,
&underrun,
writeTimestamp);
if (numSent != resamp_outchunk) {
LOG(ALERT) << "Transmit error " << numSent;
num_sent = mRadio->writeSamples(convertSendBuffer,
outer_len,
&underrun,
writeTimestamp);
if (num_sent != outer_len) {
LOG(ALERT) << "Transmit error " << num_sent;
}
writeTimestamp += resamp_outchunk;
/* Shift remaining samples to beginning of buffer */
memmove(innerSendBuffer->begin(),
innerSendBuffer->begin() + inner_len,
(sendCursor - inner_len) * 2 * sizeof(float));
return true;
writeTimestamp += outer_len;
sendCursor -= inner_len;
assert(sendCursor >= 0);
}

File diff suppressed because it is too large Load Diff

View File

@@ -20,11 +20,6 @@
#include "BitVector.h"
#include "signalVector.h"
/* Burst lengths */
#define NORMAL_BURST_NBITS 148
#define EDGE_BURST_NBITS 444
#define EDGE_BURST_NSYMS (EDGE_BURST_NBITS / 3)
/** Convolution type indicator */
enum ConvType {
START_ONLY,
@@ -33,14 +28,6 @@ enum ConvType {
UNDEFINED,
};
enum signalError {
SIGERR_NONE,
SIGERR_BOUNDS,
SIGERR_CLIP,
SIGERR_UNSUPPORTED,
SIGERR_INTERNAL,
};
/** Convert a linear number to a dB value */
float dB(float x);
@@ -54,7 +41,7 @@ float vectorNorm2(const signalVector &x);
float vectorPower(const signalVector &x);
/** Setup the signal processing library */
bool sigProcLibSetup();
bool sigProcLibSetup(int sps);
/** Destroy the signal processing library */
void sigProcLibDestroy(void);
@@ -109,25 +96,6 @@ signalVector *modulateBurst(const BitVector &wBurst,
int guardPeriodLength,
int sps, bool emptyPulse = false);
/** 8-PSK modulate a burst of bits */
signalVector *modulateEdgeBurst(const BitVector &bits,
int sps, bool emptyPulse = false);
/** Generate a EDGE burst with random payload - 4 SPS (625 samples) only */
signalVector *generateEdgeBurst(int tsc);
/** Generate an empty burst - 4 or 1 SPS */
signalVector *generateEmptyBurst(int sps, int tn);
/** Generate a normal GSM burst with random payload - 4 or 1 SPS */
signalVector *genRandNormalBurst(int tsc, int sps, int tn);
/** Generate an access GSM burst with random payload - 4 or 1 SPS */
signalVector *genRandAccessBurst(int delay, int sps, int tn);
/** Generate a dummy GSM burst - 4 or 1 SPS */
signalVector *generateDummyBurst(int sps, int tn);
/** Sinc function */
float sinc(float x);
@@ -175,6 +143,22 @@ complex peakDetect(const signalVector &rxBurst,
void scaleVector(signalVector &x,
complex scale);
/**
Generate a modulated GSM midamble, stored within the library.
@param gsmPulse The GSM pulse used for modulation.
@param sps The number of samples per GSM symbol.
@param TSC The training sequence [0..7]
@return Success.
*/
bool generateMidamble(int sps, int tsc);
/**
Generate a modulated RACH sequence, stored within the library.
@param gsmPulse The GSM pulse used for modulation.
@param sps The number of samples per GSM symbol.
@return Success.
*/
bool generateRACHSequence(int sps);
/**
Energy detector, checks to see if received burst energy is above a threshold.
@param rxBurst The received GSM burst of interest.
@@ -195,15 +179,13 @@ bool energyDetect(signalVector &rxBurst,
@param sps The number of samples per GSM symbol.
@param amplitude The estimated amplitude of received RACH burst.
@param TOA The estimate time-of-arrival of received RACH burst.
@param maxTOA The maximum expected time-of-arrival
@return positive if threshold value is reached, negative on error, zero otherwise
*/
int detectRACHBurst(signalVector &rxBurst,
float detectThreshold,
int sps,
complex &amplitude,
float &TOA,
unsigned maxTOA);
complex *amplitude,
float* TOA);
/**
Normal burst correlator, detector, channel estimator.
@@ -220,39 +202,15 @@ int detectRACHBurst(signalVector &rxBurst,
@return positive if threshold value is reached, negative on error, zero otherwise
*/
int analyzeTrafficBurst(signalVector &rxBurst,
unsigned TSC,
float detectThreshold,
int sps,
complex &amplitude,
float &TOA,
unsigned maxTOA);
/**
EDGE burst detector
@param burst The received GSM burst of interest
@param detectThreshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
@param sps The number of samples per GSM symbol.
@param amplitude The estimated amplitude of received TSC burst.
@param TOA The estimate time-of-arrival of received TSC burst.
@param maxTOA The maximum expected time-of-arrival
@return positive if threshold value is reached, negative on error, zero otherwise
*/
int detectEdgeBurst(signalVector &burst,
unsigned TSC,
float detectThreshold,
int sps,
complex &amplitude,
float &TOA,
unsigned maxTOA);
/**
Downsample 4 SPS to 1 SPS using a polyphase filterbank
@param burst Input burst of at least 624 symbols
@return Decimated signal vector of 156 symbols
*/
signalVector *downsampleBurst(signalVector &burst);
unsigned TSC,
float detectThreshold,
int sps,
complex *amplitude,
float *TOA,
unsigned maxTOA,
bool requestChannel = false,
signalVector** channelResponse = NULL,
float *channelResponseOffset = NULL);
/**
Decimate a vector.
@@ -275,14 +233,33 @@ SoftVector *demodulateBurst(signalVector &rxBurst, int sps,
complex channel, float TOA);
/**
Demodulate 8-PSK EDGE burst with soft symbol ooutput
@param rxBurst The burst to be demodulated.
@param sps The number of samples per GSM symbol.
@param channel The amplitude estimate of the received burst.
@param TOA The time-of-arrival of the received burst.
@return The demodulated bit sequence.
Design the necessary filters for a decision-feedback equalizer.
@param channelResponse The multipath channel that we're mitigating.
@param SNRestimate The signal-to-noise estimate of the channel, a linear value
@param Nf The number of taps in the feedforward filter.
@param feedForwardFilter The designed feed forward filter.
@param feedbackFilter The designed feedback filter.
@return True if DFE can be designed.
*/
SoftVector *demodEdgeBurst(signalVector &rxBurst, int sps,
complex channel, float TOA);
bool designDFE(signalVector &channelResponse,
float SNRestimate,
int Nf,
signalVector **feedForwardFilter,
signalVector **feedbackFilter);
/**
Equalize/demodulate a received burst via a decision-feedback equalizer.
@param rxBurst The received burst to be demodulated.
@param TOA The time-of-arrival of the received burst.
@param sps The number of samples per GSM symbol.
@param w The feed forward filter of the DFE.
@param b The feedback filter of the DFE.
@return The demodulated bit sequence.
*/
SoftVector *equalizeBurst(signalVector &rxBurst,
float TOA,
int sps,
signalVector &w,
signalVector &b);
#endif /* SIGPROCLIB_H */

View File

@@ -50,15 +50,6 @@ size_t signalVector::getStart() const
return mStart - mData;
}
size_t signalVector::updateHistory()
{
size_t num = getStart();
memmove(mData, mStart + this->size() - num, num * sizeof(complex));
return num;
}
Symmetry signalVector::getSymmetry() const
{
return symmetry;

View File

@@ -32,7 +32,6 @@ public:
/** Return head room */
size_t getStart() const;
size_t updateHistory();
Symmetry getSymmetry() const;
void setSymmetry(Symmetry symmetry);

View File

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

View File

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

View File

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

View File

@@ -29,7 +29,7 @@ AC_CANONICAL_BUILD
AC_CANONICAL_HOST
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([subdir-objects])
AM_INIT_AUTOMAKE
dnl Linux kernel KBuild style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -78,11 +78,6 @@ AC_ARG_WITH(neon-vfpv4, [
[enable ARM NEON FMA support])
])
AC_ARG_WITH(sse, [
AS_HELP_STRING([--with-sse],
[enable x86 SSE support (default)])
])
AS_IF([test "x$with_neon" = "xyes"], [
AC_DEFINE(HAVE_NEON, 1, Support ARM NEON)
])
@@ -97,12 +92,8 @@ AS_IF([test "x$with_usrp1" = "xyes"], [
])
AS_IF([test "x$with_usrp1" != "xyes"],[
PKG_CHECK_MODULES(UHD, uhd >= 003.009,
[AC_DEFINE(USE_UHD_3_9, 1, UHD version 3.9.0 or higher)],
[PKG_CHECK_MODULES(UHD, uhd >= 003.005.004)]
)
AC_DEFINE(USE_UHD, 1, All UHD versions)
PKG_CHECK_MODULES(FFTWF, fftw3f)
PKG_CHECK_MODULES(UHD, uhd >= 003.004.000)
AC_DEFINE(USE_UHD, 1, Define to 1 if using UHD)
])
AS_IF([test "x$with_singledb" = "xyes"], [
@@ -110,9 +101,7 @@ AS_IF([test "x$with_singledb" = "xyes"], [
])
# Find and define supported SIMD extensions
AS_IF([test "x$with_sse" != "xno"], [
AX_EXT
])
AX_EXT
AM_CONDITIONAL(USRP1, [test "x$with_usrp1" = "xyes"])
AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])
@@ -120,9 +109,6 @@ AM_CONDITIONAL(ARCH_ARM_A15, [test "x$with_neon_vfpv4" = "xyes"])
PKG_CHECK_MODULES(LIBUSB, libusb-1.0)
AC_CHECK_HEADER([boost/config.hpp],[],
[AC_MSG_ERROR([boost/config.hpp not found, install e.g. libboost-dev])])
dnl Output files
AC_CONFIG_FILES([\
Makefile \

View File

@@ -1,7 +0,0 @@
#!/bin/sh
set -ex
autoreconf --install --force
./configure
$MAKE $PARALLEL_MAKE
$MAKE check \
|| cat-testlogs.sh

48
debian/changelog vendored
View File

@@ -1,48 +0,0 @@
osmo-trx (0.1.11) trusty; urgency=medium
* Rebase fairwaves branch on master.
* New release of osmo-trx for fairwaves build.
-- Ivan Klyuchnikov <kluchnikovi@gmail.com> Wed, 15 Feb 2017 19:00:32 +0300
osmo-trx (0.1.10~3) trusty; urgency=medium
* 737fc25 transceiver: WIP: Set default max delay to 2 samples.
* 58f5333 sigProcLib: Change number of head bits in detectRACHBurst() from 4 to 8.
* ceb4171 transceiver: Add an option to emulate a RACH delay in random filler mode.
* 5bf037b transceiver: Log channel number in DEBUG output of demoded bursts.
* a057c7d radioInterface: Initialize power scale with a meaningful default.
* 3b093bb uhd: Set minimum UHD version requirement for E3XX
* 0fe41a5 uhd: Set default Tx sampling to 4 sps
* a5e0f1c uhd: Update default E3XX settings
* 2c650a6 common: Add mandatory length field to UDP receive calls
* d4555f2 common: Restrict UDP binding to localhost only
-- Alexander Chemeris <Alexander.Chemeris@gmail.com> Sat, 18 Jun 2016 16:07:17 +0300
osmo-trx (0.1.10~2) trusty; urgency=medium
* c88385c makefile: Fix build from an external path.
* 0479562 EDGE: Fix demodulation slicer input
* d2b0703 uhd: Correct timing alignment in 8-PSK and GMSK downlink bursts
-- Alexander Chemeris <Alexander.Chemeris@gmail.com> Sat, 30 Apr 2016 01:57:45 +0300
osmo-trx (0.1.10~1) trusty; urgency=medium
* some EDGE support in master
* fairwaves/rach-filler branch
-- Kirill Zakharenko <earwin@gmail.com> Sun, 27 Mar 2016 19:37:39 +0100
osmo-trx (0.1.9) trusty; urgency=medium
* Ask Ivan, really
-- Kirill Zakharenko <earwin@gmail.com> Thu, 16 Jul 2015 12:13:46 +0000
osmo-trx (0.1.8) precise; urgency=low
* Initial release
-- Ivan Klyuchnikov <Ivan.Kluchnikov@fairwaves.ru> Sun, 9 Mar 2014 14:10:10 +0400

1
debian/compat vendored
View File

@@ -1 +0,0 @@
9

45
debian/control vendored
View File

@@ -1,45 +0,0 @@
Source: osmo-trx
Section: net
Priority: optional
Maintainer: Ivan Klyuchnikov <ivan.kluchnikov@fairwaves.ru>
Build-Depends: debhelper (>= 9),
autotools-dev,
libdbd-sqlite3,
pkg-config,
dh-autoreconf,
libuhd-dev,
libusb-1.0-0-dev,
libboost-all-dev,
hardening-wrapper,
libfftw3-dev
Standards-Version: 3.9.6
Vcs-Browser: http://cgit.osmocom.org/osmo-trx
Vcs-Git: git://git.osmocom.org/osmo-trx
Homepage: https://projects.osmocom.org/projects/osmotrx
Package: osmo-trx
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, libdbd-sqlite3
Description: SDR transceiver that implements Layer 1 of a GSM BTS
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
physical layer of a BTS comprising the following 3GPP specifications:
.
TS 05.01 "Physical layer on the radio path"
TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
TS 05.04 "Modulation"
TS 05.10 "Radio subsystem synchronization"
.
In this context, BTS is "Base transceiver station". It's the stations that
connect mobile phones to the mobile network.
.
3GPP is the "3rd Generation Partnership Project" which is the collaboration
between different telecommunication associations for developing new
generations of mobile phone networks. (post-2G/GSM)
Package: osmo-trx-dbg
Architecture: any
Section: debug
Priority: extra
Depends: osmo-trx (= ${binary:Version}), ${misc:Depends}
Description: Debug symbols for the osmo-trx
Make debugging possible

162
debian/copyright vendored
View File

@@ -1,162 +0,0 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: OsmoTRX
Source: http://cgit.osmocom.org/osmo-trx/
Files-Excluded: sqlite3 Transceiver52M/std_inband.rbf
Files: *
Copyright: 2008-2013 Free Software Foundation
2010 Kestrel Signal Processing, Inc.
2010-2012 Range Networks, Inc.
License: AGPL-3+
Files: Transceiver52M/arm/*
Transceiver52M/x86/*
Transceiver52M/common/*
Transceiver52M/Resampler.cpp
Transceiver52M/Resampler.h
Transceiver52M/osmo-trx.cpp
Transceiver52M/radioInterfaceDiversity.cpp
Copyright: 2012-2013 Thomas Tsou <tom@tsou.cc>
License: LGPL-2.1+
Files: config/ax_check_compile_flag.m4
Copyright: 2008 Guido U. Draheim <guidod@gmx.de>
2011 Maarten Bosmans <mkbosmans@gmail.com>
License: GPL-3+
Files: config/ax_gcc_x86_cpuid.m4
Copyright: 2008 Steven G. Johnson <stevenj@alum.mit.edu>
2008 Matteo Frigo
License: GPL-3+
Files: config/ax_ext.m4
Copyright: 2007 Christophe Tournayre <turn3r@users.sourceforge.net>
2013 Michael Petch <mpetch@capp-sysware.com>
License: license_for_ax_ext_m4
Files: config/ax_gcc_x86_avx_xgetbv.m4
Copyright: 2013 Michael Petch <mpetch@capp-sysware.com>
License: GPL-3+
Files: CommonLibs/Makefile.am
GSM/Makefile.am
Transceiver52M/Makefile.am
sqlite3/Makefile.am
Transceiver52M/Transceiver.h
Transceiver52M/Transceiver.cpp
Copyright: 2008-2010 Free Software Foundation
2010-2012 Range Networks, Inc.
License: GPL-3+
Files: autogen.sh
Copyright: 2005-2009 United States Government as represented by
the U.S. Army Research Laboratory.
License: BSD-3-clause
Files: CommonLibs/sqlite3util.cpp
Copyright: 2010 Kestrel Signal Processing Inc.
License: none
No license described for file.
Comment: In the previous version of the file in the git repository
at upstream it is written:
Written by David A. Burgess, Kestrel Signal Processing, Inc., 2010
The author disclaims copyright to this source code.
In the git log, this is written:
I do not claim any copyright over this change, as it's very basic.
Looking forward to see it merged into mainline.
See revision e766abbf82f02473038a83fd2f78befd08544cab at
https://github.com/osmocom/osmo-trx
Files: debian/*
Copyright: 2015 Ruben Undheim <ruben.undheim@gmail.com>
License: GPL-3+
License: AGPL-3+
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/>.
License: GPL-3+
This package 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 package 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/>
.
On Debian systems, the complete text of the GNU General
Public License version 3 can be found in "/usr/share/common-licenses/GPL-3".
License: LGPL-2.1+
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 program. If not, see
<http://www.gnu.org/licenses/>
.
On Debian systems, the complete text of the GNU Lesser General
Public License version 2.1 can be found in
"/usr/share/common-licenses/LGPL-2.1".
License: license_for_ax_ext_m4
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.
License: BSD-3-clause
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
.
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
.
3. The name of the author may not be used to endorse or promote
products derived from this software without specific prior written
permission.
.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

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

15
debian/rules vendored
View File

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

View File

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

View File

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