mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-11-02 21:23:16 +00:00
Compare commits
2 Commits
fairwaves/
...
ramp-mask
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e11ef4d969 | ||
|
|
5adc5ad68a |
45
.gitignore
vendored
45
.gitignore
vendored
@@ -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?
|
||||
@@ -1,3 +0,0 @@
|
||||
[gerrit]
|
||||
host=gerrit.osmocom.org
|
||||
project=osmo-trx
|
||||
@@ -35,7 +35,7 @@
|
||||
#ifdef DEBUG_CONFIG
|
||||
#define debugLogEarly gLogEarly
|
||||
#else
|
||||
#define debugLogEarly(x,y,z)
|
||||
#define debugLogEarly
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -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,10 @@ const char *levelNames[] = {
|
||||
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
|
||||
};
|
||||
int numLevels = 8;
|
||||
bool gLogToConsole = true;
|
||||
bool gLogToSyslog = false;
|
||||
FILE *gLogToFile = NULL;
|
||||
Mutex gLogToLock;
|
||||
|
||||
|
||||
int levelStringToInt(const string& name)
|
||||
@@ -197,7 +193,7 @@ 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;
|
||||
}
|
||||
@@ -210,7 +206,7 @@ Log::~Log()
|
||||
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 +218,7 @@ Log::~Log()
|
||||
if (neednl) {fputc('\n',gLogToFile);}
|
||||
fflush(gLogToFile);
|
||||
}
|
||||
gLogToLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -273,32 +270,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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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. */
|
||||
|
||||
260
README
260
README
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -22,8 +22,6 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iomanip> // std::setprecision
|
||||
#include <fstream>
|
||||
#include "Transceiver.h"
|
||||
#include <Logger.h>
|
||||
|
||||
@@ -44,15 +42,6 @@ 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)
|
||||
{
|
||||
@@ -80,67 +69,92 @@ TransceiverState::~TransceiverState()
|
||||
}
|
||||
}
|
||||
|
||||
bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
|
||||
static BitVector *genRandNormalBurst(size_t tsc)
|
||||
{
|
||||
if (tsc > 7)
|
||||
return NULL;
|
||||
|
||||
BitVector *bits = new BitVector(148);
|
||||
|
||||
size_t i = 0;
|
||||
|
||||
/* Tail bits */
|
||||
for (; i < 3; i++)
|
||||
(*bits)[i] = 0;
|
||||
|
||||
/* Random bits */
|
||||
for (; i < 61; i++)
|
||||
(*bits)[i] = rand() % 2;
|
||||
|
||||
/* Training sequence */
|
||||
for (int j = 0; i < 87; i++, j++)
|
||||
(*bits)[i] = GSM::gTrainingSequence[tsc][j];
|
||||
|
||||
/* Random bits */
|
||||
for (; i < 145; i++)
|
||||
(*bits)[i] = rand() % 2;
|
||||
|
||||
/* Tail bits */
|
||||
for (; i < 148; i++)
|
||||
(*bits)[i] = 0;
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc)
|
||||
{
|
||||
BitVector *bits;
|
||||
signalVector *burst;
|
||||
time_t t;
|
||||
|
||||
srand((unsigned) time(&t));
|
||||
|
||||
if ((sps != 1) && (sps != 4))
|
||||
return false;
|
||||
|
||||
for (size_t n = 0; n < 8; n++) {
|
||||
size_t guard = 8 + !(n % 4);
|
||||
size_t len = sps == 4 ? 625 : 148 + guard;
|
||||
|
||||
for (size_t i = 0; i < 102; i++) {
|
||||
switch (filler) {
|
||||
case Transceiver::FILLER_DUMMY:
|
||||
burst = generateDummyBurst(sps, n);
|
||||
burst = modulateBurst(gDummyBurst, guard, sps);
|
||||
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);
|
||||
case Transceiver::FILLER_RAND:
|
||||
bits = genRandNormalBurst(rtsc);
|
||||
burst = modulateBurst(*bits, guard, sps);
|
||||
delete bits;
|
||||
break;
|
||||
case Transceiver::FILLER_ZERO:
|
||||
default:
|
||||
burst = generateEmptyBurst(sps, n);
|
||||
burst = new signalVector(len);
|
||||
}
|
||||
|
||||
scaleVector(*burst, scale);
|
||||
fillerTable[i][n] = burst;
|
||||
}
|
||||
|
||||
if ((filler == Transceiver::FILLER_NORM_RAND) ||
|
||||
(filler == Transceiver::FILLER_EDGE_RAND)) {
|
||||
chanType[n] = Transceiver::TSC;
|
||||
}
|
||||
if (filler == Transceiver::FILLER_RAND)
|
||||
chanType[n] = Transceiver::TSC;
|
||||
}
|
||||
|
||||
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)
|
||||
const char *wTRXAddress,
|
||||
size_t wSPS, size_t wChans,
|
||||
GSM::Time wTransmitLatency,
|
||||
RadioInterface *wRadioInterface)
|
||||
: 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)
|
||||
mSPSTx(wSPS), mSPSRx(1), mChans(wChans), mOn(false),
|
||||
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelay(0)
|
||||
{
|
||||
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()
|
||||
@@ -169,7 +183,7 @@ Transceiver::~Transceiver()
|
||||
* 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(int filler, size_t rtsc)
|
||||
{
|
||||
int d_srcport, d_dstport, c_srcport, c_dstport;
|
||||
|
||||
@@ -178,13 +192,11 @@ bool Transceiver::init(int filler, size_t rtsc, unsigned rach_delay, bool edge)
|
||||
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);
|
||||
@@ -227,7 +239,7 @@ bool Transceiver::init(int filler, size_t rtsc, unsigned rach_delay, bool edge)
|
||||
if (i && filler == FILLER_DUMMY)
|
||||
filler = FILLER_ZERO;
|
||||
|
||||
mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
|
||||
mStates[i].init(filler, mSPSTx, txFullScale, rtsc);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -349,12 +361,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 +462,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 +474,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 +498,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 +505,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 +530,104 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
||||
}
|
||||
}
|
||||
|
||||
int Transceiver::detectBurst(signalVector &burst,
|
||||
complex &, 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 &, 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, &, &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 &, 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, &,
|
||||
&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 +638,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,47 +662,41 @@ 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 (type == TSC)
|
||||
success = detectTSC(state, *burst, amp, toa, time);
|
||||
else
|
||||
success = detectRACH(state, *burst, amp, toa);
|
||||
|
||||
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";
|
||||
/* Update noise average if no bust detected or alert on error */
|
||||
if (success <= 0) {
|
||||
if (!success) {
|
||||
state->mNoises.insert(avg);
|
||||
} else if (success == -SIGERR_CLIP) {
|
||||
LOG(ALERT) << "Clipping detected on RACH input";
|
||||
} else if (success < 0) {
|
||||
LOG(ALERT) << "Unhandled RACH error";
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -718,7 +716,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;
|
||||
@@ -744,42 +742,18 @@ void Transceiver::driveControl(size_t chan)
|
||||
sprintf(response,"RSP POWEROFF 0");
|
||||
}
|
||||
else if (strcmp(command,"POWERON")==0) {
|
||||
if (!start()) {
|
||||
if (!start())
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
@@ -840,11 +814,13 @@ 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 || (TSC < 0) || (TSC > 7))
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -863,14 +839,6 @@ 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");
|
||||
@@ -881,20 +849,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;
|
||||
}
|
||||
@@ -907,7 +867,7 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
|
||||
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())
|
||||
@@ -932,63 +892,40 @@ void Transceiver::driveReceiveRadio()
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
@@ -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);
|
||||
bool init(int filler, size_t sps, float scale, size_t rtsc);
|
||||
|
||||
int chanType[8];
|
||||
|
||||
@@ -97,17 +97,16 @@ 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);
|
||||
bool init(int filler, size_t rtsc);
|
||||
|
||||
/** attach the radioInterface receive FIFO */
|
||||
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
|
||||
@@ -147,16 +146,13 @@ public:
|
||||
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,
|
||||
FILLER_RAND,
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -185,8 +181,6 @@ private:
|
||||
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);
|
||||
@@ -198,9 +192,8 @@ private:
|
||||
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);
|
||||
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);
|
||||
@@ -211,27 +204,30 @@ private:
|
||||
/** send messages over the clock socket */
|
||||
void writeClockInterface(void);
|
||||
|
||||
/** Detectbursts */
|
||||
int detectBurst(signalVector &burst,
|
||||
complex &, float &toa, CorrType type);
|
||||
/** Detect RACH bursts */
|
||||
bool detectRACH(TransceiverState *state,
|
||||
signalVector &burst,
|
||||
complex &, float &toa);
|
||||
|
||||
/** Demodulate burst and output soft bits */
|
||||
SoftVector *demodulate(signalVector &burst,
|
||||
complex amp, float toa, CorrType type);
|
||||
/** Detect normal bursts */
|
||||
bool detectTSC(TransceiverState *state,
|
||||
signalVector &burst,
|
||||
complex &, 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 mEdge;
|
||||
bool mOn; ///< flag to indicate that transceiver is powered on
|
||||
bool mHandover[8][8]; ///< expect handover to the timeslot/subslot
|
||||
bool mOn; ///< flag to indicate that transceiver is powered on
|
||||
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
|
||||
unsigned mMaxExpectedDelay; ///< maximum expected time-of-arrival offset in GSM symbols
|
||||
|
||||
std::vector<TransceiverState> mStates;
|
||||
|
||||
@@ -277,8 +273,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 *);
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
/*
|
||||
* Device support for Ettus Research UHD driver
|
||||
* Written by Thomas Tsou <ttsou@vt.edu>
|
||||
*
|
||||
* Copyright 2010,2011 Free Software Foundation, Inc.
|
||||
* Copyright (C) 2015 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
|
||||
@@ -35,14 +33,11 @@
|
||||
#endif
|
||||
|
||||
#define B2XX_CLK_RT 26e6
|
||||
#define B2XX_MCBTS_CLK_RT 3.2e6
|
||||
#define E1XX_CLK_RT 52e6
|
||||
#define LIMESDR_CLK_RT (GSMRATE*32)
|
||||
#define B100_BASE_RT 400000
|
||||
#define USRP2_BASE_RT 390625
|
||||
#define USRP_TX_AMPL 0.3
|
||||
#define UMTRX_TX_AMPL 0.7
|
||||
#define LIMESDR_TX_AMPL 0.3
|
||||
#define SAMPLE_BUF_SZ (1 << 20)
|
||||
|
||||
/*
|
||||
@@ -64,36 +59,20 @@ enum uhd_dev_type {
|
||||
B100,
|
||||
B200,
|
||||
B210,
|
||||
B2XX_MCBTS,
|
||||
E1XX,
|
||||
E3XX,
|
||||
X3XX,
|
||||
UMTRX,
|
||||
LIMESDR,
|
||||
NUM_USRP_TYPES,
|
||||
};
|
||||
|
||||
struct uhd_dev_offset {
|
||||
enum uhd_dev_type type;
|
||||
size_t tx_sps;
|
||||
size_t rx_sps;
|
||||
int sps;
|
||||
double offset;
|
||||
const std::string desc;
|
||||
};
|
||||
|
||||
/*
|
||||
* USRP version dependent device timings
|
||||
*/
|
||||
#ifdef USE_UHD_3_9
|
||||
#define B2XX_TIMING_1SPS 1.7153e-4
|
||||
#define B2XX_TIMING_4SPS 1.1696e-4
|
||||
#define B2XX_TIMING_4_4SPS 6.18462e-5
|
||||
#else
|
||||
#define B2XX_TIMING_1SPS 9.9692e-5
|
||||
#define B2XX_TIMING_4SPS 6.9248e-5
|
||||
#define B2XX_TIMING_4_4SPS 4.52308e-5
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Tx / Rx sample offset values. In a perfect world, there is no group delay
|
||||
* though analog components, and behaviour through digital filters exactly
|
||||
@@ -104,75 +83,100 @@ struct uhd_dev_offset {
|
||||
* Notes:
|
||||
* USRP1 with timestamps is not supported by UHD.
|
||||
*/
|
||||
static struct uhd_dev_offset uhd_offsets[] = {
|
||||
{ USRP1, 1, 1, 0.0, "USRP1 not supported" },
|
||||
{ USRP1, 4, 1, 0.0, "USRP1 not supported"},
|
||||
{ USRP2, 1, 1, 1.2184e-4, "N2XX 1 SPS" },
|
||||
{ USRP2, 4, 1, 7.6547e-5, "N2XX 4/1 SPS" },
|
||||
{ B100, 1, 1, 1.2104e-4, "B100 1 SPS" },
|
||||
{ B100, 4, 1, 7.9307e-5, "B100 4 SPS" },
|
||||
{ B200, 1, 1, B2XX_TIMING_1SPS, "B200 1 SPS" },
|
||||
{ B200, 4, 1, B2XX_TIMING_4SPS, "B200 4/1 Tx/Rx SPS" },
|
||||
{ B210, 1, 1, B2XX_TIMING_1SPS, "B210 1 SPS" },
|
||||
{ B210, 4, 1, B2XX_TIMING_4SPS, "B210 4/1 Tx/Rx SPS" },
|
||||
{ B2XX_MCBTS, 4, 4, 1.07188e-4, "B200/B210 4 SPS Multi-ARFCN" },
|
||||
{ E1XX, 1, 1, 9.5192e-5, "E1XX 1 SPS" },
|
||||
{ E1XX, 4, 1, 6.5571e-5, "E1XX 4/1 Tx/Rx SPS" },
|
||||
{ E3XX, 1, 1, 1.84616e-4, "E3XX 1 SPS" },
|
||||
{ E3XX, 4, 1, 1.29231e-4, "E3XX 4/1 Tx/Rx SPS" },
|
||||
{ X3XX, 1, 1, 1.5360e-4, "X3XX 1 SPS"},
|
||||
{ X3XX, 4, 1, 1.1264e-4, "X3XX 4/1 Tx/Rx SPS"},
|
||||
{ UMTRX, 1, 1, 9.9692e-5, "UmTRX 1 SPS" },
|
||||
{ UMTRX, 4, 1, 7.3846e-5, "UmTRX 4/1 Tx/Rx SPS" },
|
||||
{ USRP2, 4, 4, 4.6080e-5, "N2XX 4 SPS" },
|
||||
{ B200, 4, 4, B2XX_TIMING_4_4SPS, "B200 4 SPS" },
|
||||
{ B210, 4, 4, B2XX_TIMING_4_4SPS, "B210 4 SPS" },
|
||||
{ X3XX, 4, 4, 5.6567e-5, "X3XX 4 SPS"},
|
||||
{ UMTRX, 4, 4, 5.1503e-5, "UmTRX 4 SPS" },
|
||||
{ LIMESDR, 4, 4, 16.5/GSMRATE, "STREAM/LimeSDR (4 SPS TX/RX)" },
|
||||
static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 2] = {
|
||||
{ USRP1, 1, 0.0, "USRP1 not supported" },
|
||||
{ USRP1, 4, 0.0, "USRP1 not supported"},
|
||||
{ USRP2, 1, 1.2184e-4, "N2XX 1 SPS" },
|
||||
{ USRP2, 4, 8.0230e-5, "N2XX 4 SPS" },
|
||||
{ B100, 1, 1.2104e-4, "B100 1 SPS" },
|
||||
{ B100, 4, 7.9307e-5, "B100 4 SPS" },
|
||||
{ B200, 1, 9.9692e-5, "B200 1 SPS" },
|
||||
{ B200, 4, 6.9248e-5, "B200 4 SPS" },
|
||||
{ B210, 1, 9.9692e-5, "B210 1 SPS" },
|
||||
{ B210, 4, 6.9248e-5, "B210 4 SPS" },
|
||||
{ E1XX, 1, 9.5192e-5, "E1XX 1 SPS" },
|
||||
{ E1XX, 4, 6.5571e-5, "E1XX 4 SPS" },
|
||||
{ E3XX, 1, 1.5000e-4, "E3XX 1 SPS" },
|
||||
{ E3XX, 4, 1.2740e-4, "E3XX 4 SPS" },
|
||||
{ X3XX, 1, 1.5360e-4, "X3XX 1 SPS"},
|
||||
{ X3XX, 4, 1.1264e-4, "X3XX 4 SPS"},
|
||||
{ UMTRX, 1, 9.9692e-5, "UmTRX 1 SPS" },
|
||||
{ UMTRX, 4, 7.3846e-5, "UmTRX 4 SPS" },
|
||||
};
|
||||
#define NUM_UHD_OFFSETS (sizeof(uhd_offsets)/sizeof(uhd_offsets[0]))
|
||||
|
||||
/*
|
||||
* Offset handling for special cases. Currently used for UmTRX dual channel
|
||||
* diversity receiver only.
|
||||
*/
|
||||
static struct uhd_dev_offset special_offsets[] = {
|
||||
{ UMTRX, 1, 1, 8.0875e-5, "UmTRX diversity, 1 SPS" },
|
||||
{ UMTRX, 4, 1, 5.2103e-5, "UmTRX diversity, 4 SPS" },
|
||||
{ UMTRX, 1, 8.0875e-5, "UmTRX diversity, 1 SPS" },
|
||||
{ UMTRX, 4, 5.2103e-5, "UmTRX diversity, 4 SPS" },
|
||||
};
|
||||
|
||||
static double get_dev_offset(enum uhd_dev_type type,
|
||||
int sps, bool diversity = false)
|
||||
{
|
||||
struct uhd_dev_offset *offset = NULL;
|
||||
|
||||
/* Reject USRP1 */
|
||||
if (type == USRP1) {
|
||||
LOG(ERR) << "Invalid device type";
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/* Special cases (e.g. diversity receiver) */
|
||||
if (diversity) {
|
||||
if (type != UMTRX) {
|
||||
LOG(ALERT) << "Diversity on UmTRX only";
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
switch (sps) {
|
||||
case 1:
|
||||
offset = &special_offsets[0];
|
||||
break;
|
||||
case 4:
|
||||
default:
|
||||
offset = &special_offsets[1];
|
||||
}
|
||||
} else {
|
||||
/* Search for matching offset value */
|
||||
for (int i = 0; i < NUM_USRP_TYPES * 2; i++) {
|
||||
if ((type == uhd_offsets[i].type) &&
|
||||
(sps == uhd_offsets[i].sps)) {
|
||||
offset = &uhd_offsets[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!offset) {
|
||||
LOG(ERR) << "Invalid device configuration";
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
std::cout << "-- Setting " << offset->desc << std::endl;
|
||||
|
||||
return offset->offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* Select sample rate based on device type and requested samples-per-symbol.
|
||||
* The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
|
||||
* usable channel spacing of 400 kHz.
|
||||
*/
|
||||
static double select_rate(uhd_dev_type type, int sps,
|
||||
RadioDevice::InterfaceType iface)
|
||||
static double select_rate(uhd_dev_type type, int sps, bool diversity = false)
|
||||
{
|
||||
if (iface == RadioDevice::DIVERSITY) {
|
||||
if (type == UMTRX)
|
||||
return GSMRATE * 4;
|
||||
|
||||
if (diversity && (type == UMTRX)) {
|
||||
return GSMRATE * 4;
|
||||
} else if (diversity) {
|
||||
LOG(ALERT) << "Diversity supported on UmTRX only";
|
||||
return -9999.99;
|
||||
}
|
||||
|
||||
|
||||
if ((sps != 4) && (sps != 1))
|
||||
return -9999.99;
|
||||
|
||||
if (iface == RadioDevice::MULTI_ARFCN) {
|
||||
switch (type) {
|
||||
case B2XX_MCBTS:
|
||||
return 4 * MCBTS_SPACING;
|
||||
default:
|
||||
LOG(ALERT) << "Invalid device combination";
|
||||
return -9999.99;
|
||||
}
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case USRP2:
|
||||
case X3XX:
|
||||
@@ -184,7 +188,6 @@ static double select_rate(uhd_dev_type type, int sps,
|
||||
case E1XX:
|
||||
case E3XX:
|
||||
case UMTRX:
|
||||
case LIMESDR:
|
||||
return GSMRATE * sps;
|
||||
default:
|
||||
break;
|
||||
@@ -194,8 +197,25 @@ static double select_rate(uhd_dev_type type, int sps,
|
||||
return -9999.99;
|
||||
}
|
||||
|
||||
/** Timestamp conversion
|
||||
@param timestamp a UHD or OpenBTS timestamp
|
||||
@param rate sample rate
|
||||
@return the converted timestamp
|
||||
*/
|
||||
uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
|
||||
{
|
||||
double secs = (double) ticks / rate;
|
||||
return uhd::time_spec_t(secs);
|
||||
}
|
||||
|
||||
TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
|
||||
{
|
||||
TIMESTAMP ticks = ts.get_full_secs() * rate;
|
||||
return ts.get_tick_count(rate) + ticks;
|
||||
}
|
||||
|
||||
/*
|
||||
Sample Buffer - Allows reading and writing of timed samples using osmo-trx
|
||||
Sample Buffer - Allows reading and writing of timed samples using OpenBTS
|
||||
or UHD style timestamps. Time conversions are handled
|
||||
internally or accessable through the static convert calls.
|
||||
*/
|
||||
@@ -267,11 +287,10 @@ private:
|
||||
*/
|
||||
class uhd_device : public RadioDevice {
|
||||
public:
|
||||
uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type,
|
||||
size_t chans, double offset);
|
||||
uhd_device(size_t sps, size_t chans, bool diversity, double offset);
|
||||
~uhd_device();
|
||||
|
||||
int open(const std::string &args, int ref, bool swap_channels);
|
||||
int open(const std::string &args, bool extref);
|
||||
bool start();
|
||||
bool stop();
|
||||
bool restart();
|
||||
@@ -289,8 +308,8 @@ public:
|
||||
bool setTxFreq(double wFreq, size_t chan);
|
||||
bool setRxFreq(double wFreq, size_t chan);
|
||||
|
||||
TIMESTAMP initialWriteTimestamp();
|
||||
TIMESTAMP initialReadTimestamp();
|
||||
inline TIMESTAMP initialWriteTimestamp() { return ts_initial * sps; }
|
||||
inline TIMESTAMP initialReadTimestamp() { return ts_initial; }
|
||||
|
||||
double fullScaleInputValue();
|
||||
double fullScaleOutputValue();
|
||||
@@ -331,7 +350,7 @@ private:
|
||||
enum TxWindowType tx_window;
|
||||
enum uhd_dev_type dev_type;
|
||||
|
||||
size_t tx_sps, rx_sps, chans;
|
||||
size_t sps, chans;
|
||||
double tx_rate, rx_rate;
|
||||
|
||||
double tx_gain_min, tx_gain_max;
|
||||
@@ -353,7 +372,6 @@ private:
|
||||
std::vector<smpl_buf *> rx_buffers;
|
||||
|
||||
void init_gains();
|
||||
double get_dev_offset();
|
||||
int set_master_clk(double rate);
|
||||
int set_rates(double tx_rate, double rx_rate);
|
||||
bool parse_dev_type();
|
||||
@@ -367,7 +385,7 @@ private:
|
||||
bool set_freq(double freq, size_t chan, bool tx);
|
||||
|
||||
Thread *async_event_thrd;
|
||||
InterfaceType iface;
|
||||
bool diversity;
|
||||
Mutex tune_lock;
|
||||
};
|
||||
|
||||
@@ -411,19 +429,17 @@ static void thread_enable_cancel(bool cancel)
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
|
||||
}
|
||||
|
||||
uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
|
||||
InterfaceType iface, size_t chans, double offset)
|
||||
uhd_device::uhd_device(size_t sps, size_t chans, bool diversity, double offset)
|
||||
: tx_gain_min(0.0), tx_gain_max(0.0),
|
||||
rx_gain_min(0.0), rx_gain_max(0.0),
|
||||
tx_spp(0), rx_spp(0),
|
||||
started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
|
||||
prev_ts(0,0), ts_initial(0), ts_offset(0)
|
||||
{
|
||||
this->tx_sps = tx_sps;
|
||||
this->rx_sps = rx_sps;
|
||||
this->sps = sps;
|
||||
this->chans = chans;
|
||||
this->offset = offset;
|
||||
this->iface = iface;
|
||||
this->diversity = diversity;
|
||||
}
|
||||
|
||||
uhd_device::~uhd_device()
|
||||
@@ -457,24 +473,18 @@ void uhd_device::init_gains()
|
||||
tx_gain_min = range.start();
|
||||
tx_gain_max = range.stop();
|
||||
}
|
||||
LOG(INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
|
||||
|
||||
range = usrp_dev->get_rx_gain_range();
|
||||
rx_gain_min = range.start();
|
||||
rx_gain_max = range.stop();
|
||||
LOG(INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
|
||||
|
||||
for (size_t i = 0; i < tx_gains.size(); i++) {
|
||||
double gain = (tx_gain_min + tx_gain_max) / 2;
|
||||
LOG(INFO) << "Default setting Tx gain for channel " << i << " to " << gain;
|
||||
usrp_dev->set_tx_gain(gain, i);
|
||||
usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2, i);
|
||||
tx_gains[i] = usrp_dev->get_tx_gain(i);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < rx_gains.size(); i++) {
|
||||
double gain = (rx_gain_min + rx_gain_max) / 2;
|
||||
LOG(INFO) << "Default setting Rx gain for channel " << i << " to " << gain;
|
||||
usrp_dev->set_rx_gain(gain, i);
|
||||
usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2, i);
|
||||
rx_gains[i] = usrp_dev->get_rx_gain(i);
|
||||
}
|
||||
|
||||
@@ -482,53 +492,6 @@ void uhd_device::init_gains()
|
||||
|
||||
}
|
||||
|
||||
double uhd_device::get_dev_offset()
|
||||
{
|
||||
struct uhd_dev_offset *offset = NULL;
|
||||
|
||||
/* Reject USRP1 */
|
||||
if (dev_type == USRP1) {
|
||||
LOG(ERR) << "Invalid device type";
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/* Special cases (e.g. diversity receiver) */
|
||||
if (iface == DIVERSITY) {
|
||||
if ((dev_type != UMTRX) || (rx_sps != 1)) {
|
||||
LOG(ALERT) << "Unsupported device configuration";
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
switch (tx_sps) {
|
||||
case 1:
|
||||
offset = &special_offsets[0];
|
||||
break;
|
||||
case 4:
|
||||
default:
|
||||
offset = &special_offsets[1];
|
||||
}
|
||||
} else {
|
||||
/* Search for matching offset value */
|
||||
for (size_t i = 0; i < NUM_UHD_OFFSETS; i++) {
|
||||
if ((dev_type == uhd_offsets[i].type) &&
|
||||
(tx_sps == uhd_offsets[i].tx_sps) &&
|
||||
(rx_sps == uhd_offsets[i].rx_sps)) {
|
||||
offset = &uhd_offsets[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!offset) {
|
||||
LOG(ERR) << "Invalid device configuration";
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
std::cout << "-- Setting " << offset->desc << std::endl;
|
||||
|
||||
return offset->offset;
|
||||
}
|
||||
|
||||
int uhd_device::set_master_clk(double clk_rate)
|
||||
{
|
||||
double actual, offset, limit = 1.0;
|
||||
@@ -563,18 +526,11 @@ int uhd_device::set_rates(double tx_rate, double rx_rate)
|
||||
if ((dev_type == B200) || (dev_type == B210) || (dev_type == E3XX)) {
|
||||
if (set_master_clk(B2XX_CLK_RT) < 0)
|
||||
return -1;
|
||||
} else if (dev_type == E1XX) {
|
||||
}
|
||||
else if (dev_type == E1XX) {
|
||||
if (set_master_clk(E1XX_CLK_RT) < 0)
|
||||
return -1;
|
||||
} else if (dev_type == B2XX_MCBTS) {
|
||||
if (set_master_clk(B2XX_MCBTS_CLK_RT) < 0)
|
||||
return -1;
|
||||
}
|
||||
else if (dev_type == LIMESDR) {
|
||||
if (set_master_clk(LIMESDR_CLK_RT) < 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// Set sample rates
|
||||
try {
|
||||
@@ -602,9 +558,6 @@ int uhd_device::set_rates(double tx_rate, double rx_rate)
|
||||
|
||||
double uhd_device::setTxGain(double db, size_t chan)
|
||||
{
|
||||
if (iface == MULTI_ARFCN)
|
||||
chan = 0;
|
||||
|
||||
if (chan >= tx_gains.size()) {
|
||||
LOG(ALERT) << "Requested non-existent channel" << chan;
|
||||
return 0.0f;
|
||||
@@ -629,7 +582,7 @@ double uhd_device::setTxGain(double db, size_t chan)
|
||||
|
||||
tx_gains[chan] = usrp_dev->get_tx_gain(chan);
|
||||
|
||||
LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)";
|
||||
LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB";
|
||||
|
||||
return tx_gains[chan];
|
||||
}
|
||||
@@ -644,16 +597,13 @@ double uhd_device::setRxGain(double db, size_t chan)
|
||||
usrp_dev->set_rx_gain(db, chan);
|
||||
rx_gains[chan] = usrp_dev->get_rx_gain(chan);
|
||||
|
||||
LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
|
||||
LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB";
|
||||
|
||||
return rx_gains[chan];
|
||||
}
|
||||
|
||||
double uhd_device::getRxGain(size_t chan)
|
||||
{
|
||||
if (iface == MULTI_ARFCN)
|
||||
chan = 0;
|
||||
|
||||
if (chan >= rx_gains.size()) {
|
||||
LOG(ALERT) << "Requested non-existent channel " << chan;
|
||||
return 0.0f;
|
||||
@@ -672,8 +622,8 @@ bool uhd_device::parse_dev_type()
|
||||
{
|
||||
std::string mboard_str, dev_str;
|
||||
uhd::property_tree::sptr prop_tree;
|
||||
size_t usrp1_str, usrp2_str, e100_str, e110_str, e310_str, e3xx_str,
|
||||
b100_str, b200_str, b210_str, x300_str, x310_str, umtrx_str, limesdr_str;
|
||||
size_t usrp1_str, usrp2_str, e100_str, e110_str, e310_str,
|
||||
b100_str, b200_str, b210_str, x300_str, x310_str, umtrx_str;
|
||||
|
||||
prop_tree = usrp_dev->get_device()->get_tree();
|
||||
dev_str = prop_tree->access<std::string>("/name").get();
|
||||
@@ -687,12 +637,9 @@ bool uhd_device::parse_dev_type()
|
||||
e100_str = mboard_str.find("E100");
|
||||
e110_str = mboard_str.find("E110");
|
||||
e310_str = mboard_str.find("E310");
|
||||
e3xx_str = mboard_str.find("E3XX");
|
||||
x300_str = mboard_str.find("X300");
|
||||
x310_str = mboard_str.find("X310");
|
||||
umtrx_str = dev_str.find("UmTRX");
|
||||
// LimeSDR is based on STREAM board, so it's advertized as such
|
||||
limesdr_str = dev_str.find("STREAM");
|
||||
|
||||
if (usrp1_str != std::string::npos) {
|
||||
LOG(ALERT) << "USRP1 is not supported using the UHD driver";
|
||||
@@ -719,8 +666,7 @@ bool uhd_device::parse_dev_type()
|
||||
} else if (usrp2_str != std::string::npos) {
|
||||
tx_window = TX_WINDOW_FIXED;
|
||||
dev_type = USRP2;
|
||||
} else if ((e310_str != std::string::npos) ||
|
||||
(e3xx_str != std::string::npos)) {
|
||||
} else if (e310_str != std::string::npos) {
|
||||
tx_window = TX_WINDOW_FIXED;
|
||||
dev_type = E3XX;
|
||||
} else if (x300_str != std::string::npos) {
|
||||
@@ -732,12 +678,8 @@ bool uhd_device::parse_dev_type()
|
||||
} else if (umtrx_str != std::string::npos) {
|
||||
tx_window = TX_WINDOW_FIXED;
|
||||
dev_type = UMTRX;
|
||||
} else if (limesdr_str != std::string::npos) {
|
||||
tx_window = TX_WINDOW_USRP1;
|
||||
dev_type = LIMESDR;
|
||||
} else {
|
||||
LOG(ALERT) << "Unknown UHD device type "
|
||||
<< dev_str << " " << mboard_str;
|
||||
LOG(ALERT) << "Unknown UHD device type " << dev_str;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -752,30 +694,8 @@ bool uhd_device::parse_dev_type()
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for UHD version > 3.9.0 for E3XX support
|
||||
*/
|
||||
static bool uhd_e3xx_version_chk()
|
||||
int uhd_device::open(const std::string &args, bool extref)
|
||||
{
|
||||
std::string ver = uhd::get_version_string();
|
||||
std::string major_str(ver.begin(), ver.begin() + 3);
|
||||
std::string minor_str(ver.begin() + 4, ver.begin() + 7);
|
||||
|
||||
int major_val = atoi(major_str.c_str());
|
||||
int minor_val = atoi(minor_str.c_str());
|
||||
|
||||
if (major_val < 3)
|
||||
return false;
|
||||
if (minor_val < 9)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int uhd_device::open(const std::string &args, int ref, bool swap_channels)
|
||||
{
|
||||
const char *refstr;
|
||||
|
||||
// Find UHD devices
|
||||
uhd::device_addr_t addr(args);
|
||||
uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
|
||||
@@ -797,30 +717,12 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
|
||||
if (!parse_dev_type())
|
||||
return -1;
|
||||
|
||||
if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
|
||||
LOG(ALERT) << "E3XX requires UHD 003.009.000 or greater";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Verify and set channels
|
||||
if (iface == MULTI_ARFCN) {
|
||||
if ((dev_type != B200) && (dev_type != B210)) {
|
||||
LOG(ALERT) << "Unsupported device configuration";
|
||||
return -1;
|
||||
}
|
||||
|
||||
dev_type = B2XX_MCBTS;
|
||||
chans = 1;
|
||||
} else if (chans == 2) {
|
||||
if (dev_type == B210) {
|
||||
} else if (dev_type == UMTRX) {
|
||||
uhd::usrp::subdev_spec_t subdev_spec(swap_channels?"B:0 A:0":"A:0 B:0");
|
||||
usrp_dev->set_tx_subdev_spec(subdev_spec);
|
||||
usrp_dev->set_rx_subdev_spec(subdev_spec);
|
||||
} else {
|
||||
LOG(ALERT) << "Invalid device configuration";
|
||||
return -1;
|
||||
}
|
||||
if ((dev_type == B210) && (chans == 2)) {
|
||||
} else if ((dev_type == UMTRX) && (chans == 2)) {
|
||||
uhd::usrp::subdev_spec_t subdev_spec("A:0 B:0");
|
||||
usrp_dev->set_tx_subdev_spec(subdev_spec);
|
||||
usrp_dev->set_rx_subdev_spec(subdev_spec);
|
||||
} else if (chans != 1) {
|
||||
LOG(ALERT) << "Invalid channel combination for device";
|
||||
return -1;
|
||||
@@ -832,29 +734,16 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
|
||||
rx_gains.resize(chans);
|
||||
rx_buffers.resize(chans);
|
||||
|
||||
switch (ref) {
|
||||
case REF_INTERNAL:
|
||||
refstr = "internal";
|
||||
break;
|
||||
case REF_EXTERNAL:
|
||||
refstr = "external";
|
||||
break;
|
||||
case REF_GPS:
|
||||
refstr = "gpsdo";
|
||||
break;
|
||||
default:
|
||||
LOG(ALERT) << "Invalid reference type";
|
||||
return -1;
|
||||
}
|
||||
|
||||
usrp_dev->set_clock_source(refstr);
|
||||
if (extref)
|
||||
usrp_dev->set_clock_source("external");
|
||||
|
||||
// Set rates
|
||||
double _rx_rate = select_rate(dev_type, rx_sps, iface);
|
||||
double _tx_rate = select_rate(dev_type, tx_sps, iface);
|
||||
|
||||
if (iface == DIVERSITY)
|
||||
_rx_rate = select_rate(dev_type, 1, iface);
|
||||
double _rx_rate;
|
||||
double _tx_rate = select_rate(dev_type, sps);
|
||||
if (diversity)
|
||||
_rx_rate = select_rate(dev_type, 1, true);
|
||||
else
|
||||
_rx_rate = _tx_rate / sps;
|
||||
|
||||
if ((_tx_rate < 0.0) || (_rx_rate < 0.0))
|
||||
return -1;
|
||||
@@ -866,14 +755,9 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
|
||||
// Setting LMS6002D LPF to 500kHz gives us the best signal quality
|
||||
for (size_t i = 0; i < chans; i++) {
|
||||
usrp_dev->set_tx_bandwidth(500*1000*2, i);
|
||||
if (iface != DIVERSITY)
|
||||
if (!diversity)
|
||||
usrp_dev->set_rx_bandwidth(500*1000*2, i);
|
||||
}
|
||||
} else if (dev_type == LIMESDR) {
|
||||
for (size_t i = 0; i < chans; i++) {
|
||||
usrp_dev->set_tx_bandwidth(5e6, i);
|
||||
usrp_dev->set_rx_bandwidth(5e6, i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Create TX and RX streamers */
|
||||
@@ -893,10 +777,8 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
|
||||
for (size_t i = 0; i < rx_buffers.size(); i++)
|
||||
rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
|
||||
|
||||
// Set receive chain sample offset. Trigger the EDGE offset
|
||||
// table by checking for 4 SPS on the receive path. No other
|
||||
// configuration supports using 4 SPS.
|
||||
double offset = get_dev_offset();
|
||||
// Set receive chain sample offset
|
||||
double offset = get_dev_offset(dev_type, sps, diversity);
|
||||
if (offset == 0.0) {
|
||||
LOG(ERR) << "Unsupported configuration, no correction applied";
|
||||
ts_offset = 0;
|
||||
@@ -910,10 +792,8 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
|
||||
// Print configuration
|
||||
LOG(INFO) << "\n" << usrp_dev->get_pp_string();
|
||||
|
||||
if (iface == DIVERSITY)
|
||||
if (diversity)
|
||||
return DIVERSITY;
|
||||
if (iface == MULTI_ARFCN)
|
||||
return MULTI_ARFCN;
|
||||
|
||||
switch (dev_type) {
|
||||
case B100:
|
||||
@@ -925,7 +805,6 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
|
||||
case B210:
|
||||
case E1XX:
|
||||
case E3XX:
|
||||
case LIMESDR:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -960,7 +839,7 @@ bool uhd_device::flush_recv(size_t num_pkts)
|
||||
}
|
||||
}
|
||||
|
||||
ts_initial = md.time_spec.to_ticks(rx_rate);
|
||||
ts_initial = convert_time(md.time_spec, rx_rate);
|
||||
}
|
||||
|
||||
LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
|
||||
@@ -1097,7 +976,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||
// Shift read time with respect to transmit clock
|
||||
timestamp += ts_offset;
|
||||
|
||||
ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
|
||||
ts = convert_time(timestamp, rx_rate);
|
||||
LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
|
||||
|
||||
// Check that timestamp is valid
|
||||
@@ -1179,7 +1058,7 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
|
||||
metadata.has_time_spec = true;
|
||||
metadata.start_of_burst = false;
|
||||
metadata.end_of_burst = false;
|
||||
metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
|
||||
metadata.time_spec = convert_time(timestamp, tx_rate);
|
||||
|
||||
*underrun = false;
|
||||
|
||||
@@ -1236,7 +1115,7 @@ uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
|
||||
uhd::tune_request_t treq(freq);
|
||||
|
||||
if (dev_type == UMTRX) {
|
||||
if (offset != 0.0)
|
||||
if (offset > 0.0)
|
||||
return uhd::tune_request_t(freq, offset);
|
||||
|
||||
// Don't use DSP tuning, because LMS6002D PLL steps are small enough.
|
||||
@@ -1247,7 +1126,6 @@ uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
|
||||
treq.rf_freq = freq;
|
||||
treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
|
||||
treq.dsp_freq = 0.0;
|
||||
return treq;
|
||||
} else if (chans == 1) {
|
||||
if (offset == 0.0)
|
||||
return treq;
|
||||
@@ -1364,28 +1242,8 @@ double uhd_device::getRxFreq(size_t chan)
|
||||
return rx_freqs[chan];
|
||||
}
|
||||
|
||||
/*
|
||||
* Only allow sampling the Rx path lower than Tx and not vice-versa.
|
||||
* Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
|
||||
* combination.
|
||||
*/
|
||||
TIMESTAMP uhd_device::initialWriteTimestamp()
|
||||
{
|
||||
if ((iface == MULTI_ARFCN) || (rx_sps == tx_sps))
|
||||
return ts_initial;
|
||||
else
|
||||
return ts_initial * tx_sps;
|
||||
}
|
||||
|
||||
TIMESTAMP uhd_device::initialReadTimestamp()
|
||||
{
|
||||
return ts_initial;
|
||||
}
|
||||
|
||||
double uhd_device::fullScaleInputValue()
|
||||
{
|
||||
if (dev_type == LIMESDR)
|
||||
return (double) 2047 * LIMESDR_TX_AMPL;
|
||||
if (dev_type == UMTRX)
|
||||
return (double) SHRT_MAX * UMTRX_TX_AMPL;
|
||||
else
|
||||
@@ -1394,7 +1252,6 @@ double uhd_device::fullScaleInputValue()
|
||||
|
||||
double uhd_device::fullScaleOutputValue()
|
||||
{
|
||||
if (dev_type == LIMESDR) return (double) 2047;
|
||||
return (double) SHRT_MAX;
|
||||
}
|
||||
|
||||
@@ -1514,7 +1371,7 @@ ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
|
||||
|
||||
ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
|
||||
{
|
||||
return avail_smpls(timespec.to_ticks(clk_rt));
|
||||
return avail_smpls(convert_time(timespec, clk_rt));
|
||||
}
|
||||
|
||||
ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
|
||||
@@ -1560,7 +1417,7 @@ ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
|
||||
|
||||
ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
|
||||
{
|
||||
return read(buf, len, ts.to_ticks(clk_rt));
|
||||
return read(buf, len, convert_time(ts, clk_rt));
|
||||
}
|
||||
|
||||
ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
|
||||
@@ -1573,19 +1430,6 @@ ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
|
||||
if ((timestamp + len) <= time_end)
|
||||
return ERROR_TIMESTAMP;
|
||||
|
||||
if (timestamp < time_end) {
|
||||
LOG(ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
|
||||
uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
|
||||
LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt;
|
||||
// Do not return error here, because it's a rounding error and is not fatal
|
||||
}
|
||||
if (timestamp > time_end && time_end != 0) {
|
||||
LOG(ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
|
||||
uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
|
||||
LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt;
|
||||
// Do not return error here, because it's a rounding error and is not fatal
|
||||
}
|
||||
|
||||
// Starting index
|
||||
size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
|
||||
|
||||
@@ -1617,7 +1461,7 @@ ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
|
||||
|
||||
ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
|
||||
{
|
||||
return write(buf, len, ts.to_ticks(clk_rt));
|
||||
return write(buf, len, convert_time(ts, clk_rt));
|
||||
}
|
||||
|
||||
std::string smpl_buf::str_status(size_t ts) const
|
||||
@@ -1650,8 +1494,8 @@ std::string smpl_buf::str_code(ssize_t code)
|
||||
}
|
||||
}
|
||||
|
||||
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
|
||||
InterfaceType iface, size_t chans, double offset)
|
||||
RadioDevice *RadioDevice::make(size_t sps, size_t chans,
|
||||
bool diversity, double offset)
|
||||
{
|
||||
return new uhd_device(tx_sps, rx_sps, iface, chans, offset);
|
||||
return new uhd_device(sps, chans, diversity, offset);
|
||||
}
|
||||
|
||||
@@ -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, double)
|
||||
{
|
||||
return new USRPDevice(tx_sps);
|
||||
return new USRPDevice(sps, chans, diversity);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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_ */
|
||||
@@ -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,13 @@ 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 diversity;
|
||||
bool mcbts;
|
||||
double offset;
|
||||
double rssi_offset;
|
||||
bool swap_channels;
|
||||
bool edge;
|
||||
};
|
||||
|
||||
ConfigurationTable gConfig;
|
||||
@@ -129,7 +119,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,25 +155,12 @@ bool trx_setup_config(struct trx_config *config)
|
||||
config->diversity = DEFAULT_DIVERSITY;
|
||||
}
|
||||
|
||||
if (!config->chans)
|
||||
config->chans = DEFAULT_CHANS;
|
||||
/* Diversity only supported on 2 channels */
|
||||
if (config->diversity)
|
||||
config->chans = 2;
|
||||
|
||||
if (config->mcbts && ((config->chans < 0) || (config->chans > 5))) {
|
||||
std::cout << "Unsupported number of channels" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
edgestr = config->edge ? "Enabled" : "Disabled";
|
||||
refstr = config->extref ? "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";
|
||||
@@ -191,15 +168,9 @@ bool trx_setup_config(struct trx_config *config)
|
||||
case Transceiver::FILLER_ZERO:
|
||||
fillstr = "Disabled";
|
||||
break;
|
||||
case Transceiver::FILLER_NORM_RAND:
|
||||
case Transceiver::FILLER_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("");
|
||||
@@ -209,16 +180,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 +204,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 +239,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, config->rtsc)) {
|
||||
LOG(ALERT) << "Failed to initialize transceiver";
|
||||
delete trx;
|
||||
return NULL;
|
||||
@@ -329,20 +286,13 @@ 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",
|
||||
" -r Random burst test mode with TSC\n",
|
||||
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
|
||||
}
|
||||
|
||||
@@ -351,22 +301,15 @@ 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->sps = DEFAULT_SPS;
|
||||
config->chans = DEFAULT_CHANS;
|
||||
config->rtsc = 0;
|
||||
config->rach_delay = 0;
|
||||
config->extref = false;
|
||||
config->gpsref = false;
|
||||
config->filler = Transceiver::FILLER_ZERO;
|
||||
config->mcbts = 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:r:")) != -1) {
|
||||
switch (option) {
|
||||
case 'h':
|
||||
print_help();
|
||||
@@ -387,18 +330,12 @@ 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;
|
||||
break;
|
||||
@@ -406,27 +343,11 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
||||
config->offset = atof(optarg);
|
||||
break;
|
||||
case 's':
|
||||
config->tx_sps = atoi(optarg);
|
||||
break;
|
||||
case 'b':
|
||||
config->rx_sps = atoi(optarg);
|
||||
config->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->filler = Transceiver::FILLER_RAND;
|
||||
break;
|
||||
default:
|
||||
print_help();
|
||||
@@ -434,68 +355,25 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
||||
}
|
||||
}
|
||||
|
||||
/* 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->sps != 1) && (config->sps != 4)) {
|
||||
printf("Unsupported samples-per-symbol %i\n\n", config->sps);
|
||||
print_help();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
if (config->rtsc > 7) {
|
||||
printf("Invalid training sequence %i\n\n", config->rtsc);
|
||||
goto bad_config;
|
||||
print_help();
|
||||
exit(0);
|
||||
}
|
||||
|
||||
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 +391,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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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() { }
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -114,26 +130,35 @@ int RadioInterface::setPowerAttenuation(int atten, size_t chan)
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -160,10 +185,8 @@ bool RadioInterface::start()
|
||||
if (!mRadio->start())
|
||||
return false;
|
||||
|
||||
for (size_t i = 0; i < mChans; i++) {
|
||||
sendBuffer[i]->reset();
|
||||
recvBuffer[i]->reset();
|
||||
}
|
||||
recvCursor = 0;
|
||||
sendCursor = 0;
|
||||
|
||||
writeTimestamp = mRadio->initialWriteTimestamp();
|
||||
readTimestamp = mRadio->initialReadTimestamp();
|
||||
@@ -214,10 +237,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 +259,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 +278,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 +292,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 +331,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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -85,8 +86,8 @@ public:
|
||||
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 +103,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);
|
||||
@@ -150,52 +151,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 +183,7 @@ public:
|
||||
bool tuneRx(double freq, size_t chan);
|
||||
|
||||
private:
|
||||
Resampler *dnsampler;
|
||||
std::vector<Resampler *> dnsamplers;
|
||||
std::vector<float> phases;
|
||||
signalVector *outerRecvBuffer;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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
@@ -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,
|
||||
@@ -54,7 +49,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 +104,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 +151,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 +187,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 &litude,
|
||||
float &TOA,
|
||||
unsigned maxTOA);
|
||||
complex *amplitude,
|
||||
float* TOA);
|
||||
|
||||
/**
|
||||
Normal burst correlator, detector, channel estimator.
|
||||
@@ -220,39 +210,15 @@ int detectRACHBurst(signalVector &rxBurst,
|
||||
@return positive if threshold value is reached, negative on error, zero otherwise
|
||||
*/
|
||||
int analyzeTrafficBurst(signalVector &rxBurst,
|
||||
unsigned TSC,
|
||||
float detectThreshold,
|
||||
int sps,
|
||||
complex &litude,
|
||||
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 &litude,
|
||||
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 +241,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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -32,7 +32,6 @@ public:
|
||||
|
||||
/** Return head room */
|
||||
size_t getStart() const;
|
||||
size_t updateHistory();
|
||||
|
||||
Symmetry getSymmetry() const;
|
||||
void setSymmetry(Symmetry symmetry);
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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;
|
||||
|
||||
11
configure.ac
11
configure.ac
@@ -97,12 +97,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"], [
|
||||
@@ -120,9 +116,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 \
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -ex
|
||||
autoreconf --install --force
|
||||
./configure
|
||||
$MAKE $PARALLEL_MAKE
|
||||
$MAKE check \
|
||||
|| cat-testlogs.sh
|
||||
48
debian/changelog
vendored
48
debian/changelog
vendored
@@ -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
1
debian/compat
vendored
@@ -1 +0,0 @@
|
||||
9
|
||||
45
debian/control
vendored
45
debian/control
vendored
@@ -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
162
debian/copyright
vendored
@@ -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.
|
||||
1
debian/osmo-trx.install
vendored
1
debian/osmo-trx.install
vendored
@@ -1 +0,0 @@
|
||||
/usr/bin/osmo-trx
|
||||
15
debian/rules
vendored
15
debian/rules
vendored
@@ -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
|
||||
1
debian/source/format
vendored
1
debian/source/format
vendored
@@ -1 +0,0 @@
|
||||
3.0 (native)
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/sh
|
||||
sudo tcpdump -i lo0 -A udp port 5700
|
||||
|
||||
Reference in New Issue
Block a user