mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-11-03 05:33:16 +00:00
Compare commits
28 Commits
1.6.0
...
achemeris/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f28d722f54 | ||
|
|
1537d4b2d9 | ||
|
|
94325a3f60 | ||
|
|
d369f68e4e | ||
|
|
601a69a45e | ||
|
|
c939277374 | ||
|
|
79dae630c7 | ||
|
|
c8b9aced8c | ||
|
|
eac726bc17 | ||
|
|
facdadc254 | ||
|
|
eefa8e58f7 | ||
|
|
3ac1cbf40e | ||
|
|
139c84564c | ||
|
|
2cb6070e09 | ||
|
|
f2bdd1a24c | ||
|
|
34e5a3807f | ||
|
|
b49874aa64 | ||
|
|
030951695c | ||
|
|
b721d6104d | ||
|
|
c19d1f6c36 | ||
|
|
f0d8a581b4 | ||
|
|
37b445d4c8 | ||
|
|
df127bc74e | ||
|
|
6512812e43 | ||
|
|
ded68da44f | ||
|
|
37bbfa2125 | ||
|
|
fdbf914584 | ||
|
|
bbef7e4d70 |
@@ -193,7 +193,7 @@ Log::~Log()
|
|||||||
if (mDummyInit) return;
|
if (mDummyInit) return;
|
||||||
// Anything at or above LOG_CRIT is an "alarm".
|
// Anything at or above LOG_CRIT is an "alarm".
|
||||||
// Save alarms in the local list and echo them to stderr.
|
// Save alarms in the local list and echo them to stderr.
|
||||||
if (mPriority <= LOG_CRIT) {
|
if (mPriority <= LOG_ERR) {
|
||||||
if (sLoggerInited) addAlarm(mStream.str().c_str());
|
if (sLoggerInited) addAlarm(mStream.str().c_str());
|
||||||
cerr << mStream.str() << endl;
|
cerr << mStream.str() << endl;
|
||||||
}
|
}
|
||||||
@@ -206,7 +206,7 @@ Log::~Log()
|
|||||||
if (gLogToConsole||gLogToFile) {
|
if (gLogToConsole||gLogToFile) {
|
||||||
int mlen = mStream.str().size();
|
int mlen = mStream.str().size();
|
||||||
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
|
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
|
||||||
gLogToLock.lock();
|
ScopedLock lock(gLogToLock);
|
||||||
if (gLogToConsole) {
|
if (gLogToConsole) {
|
||||||
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
|
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
|
||||||
// so just use std::cout.
|
// so just use std::cout.
|
||||||
@@ -218,7 +218,6 @@ Log::~Log()
|
|||||||
if (neednl) {fputc('\n',gLogToFile);}
|
if (neednl) {fputc('\n',gLogToFile);}
|
||||||
fflush(gLogToFile);
|
fflush(gLogToFile);
|
||||||
}
|
}
|
||||||
gLogToLock.unlock();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,23 +114,21 @@ class Time {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Time& decTN(unsigned step=1)
|
Time& decTN(unsigned step=1)
|
||||||
{
|
{
|
||||||
assert(step<=8);
|
|
||||||
mTN -= step;
|
mTN -= step;
|
||||||
if (mTN<0) {
|
while (mTN<0) {
|
||||||
mTN+=8;
|
mTN+=8;
|
||||||
mFN-=1;
|
mFN-=1;
|
||||||
if (mFN<0) mFN+=gHyperframe;
|
if (mFN<0) mFN+=gHyperframe;
|
||||||
}
|
}
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Time& incTN(unsigned step=1)
|
Time& incTN(unsigned step=1)
|
||||||
{
|
{
|
||||||
assert(step<=8);
|
|
||||||
mTN += step;
|
mTN += step;
|
||||||
if (mTN>7) {
|
while (mTN>7) {
|
||||||
mTN-=8;
|
mTN-=8;
|
||||||
mFN = (mFN+1) % gHyperframe;
|
mFN = (mFN+1) % gHyperframe;
|
||||||
}
|
}
|
||||||
|
|||||||
260
README
260
README
@@ -1,168 +1,116 @@
|
|||||||
Welcome to the OpenBTS source code.
|
This is the interface to the transcevier.
|
||||||
|
|
||||||
|
Each TRX Manager UDP socket interface represents a single ARFCN.
|
||||||
For free support, please subscribe to openbts-discuss@lists.sourceforge.net.
|
Each of these per-ARFCN interfaces is a pair of UDP sockets, one for control and one for data.
|
||||||
See http://sourceforge.net/mailarchive/forum.php?forum_name=openbts-discuss
|
Give a base port B (5700), the master clock interface is at port P=B.
|
||||||
and https://lists.sourceforge.net/lists/listinfo/openbts-discuss for details.
|
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 additional information, refer to http://openbts.org.
|
For any given build, the number of ARFCN interfaces can be fixed.
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
By default, OpenBTS assumes the following UDP port assignments:
|
Indications on the Master Clock Interface
|
||||||
|
|
||||||
5060 -- Asterisk SIP interface
|
The master clock interface is output only (from the radio).
|
||||||
5061 -- local SIP softphone
|
Messages are "indications".
|
||||||
5062 -- OpenBTS SIP interface
|
|
||||||
5063 -- smqueue SIP interface
|
|
||||||
5064 -- subscriber registry SIP interface
|
|
||||||
5700-range -- OpenBTS-transceiver interface
|
|
||||||
|
|
||||||
These can be controlled in the CONFIG table in /etc/OpenBTS.db.
|
CLOCK gives the current value of the transceiver clock to be used by the core.
|
||||||
|
This message is sent whenever a trasmission packet arrives that is too late or too early. The clock value is NOT the current transceiver time. It is a time setting the the core should use to give better packet arrival times.
|
||||||
|
IND CLOCK <totalFrames>
|
||||||
|
|
||||||
Standrd paths:
|
|
||||||
/OpenBTS -- Binary installation.
|
|
||||||
/etc/OpenBTS -- Configuration databases.
|
|
||||||
/var/run/OpenBTS -- Real-time reporting databases.
|
|
||||||
|
|
||||||
The script apps/setUpFiles.sh will create these directories and install the
|
|
||||||
correct files in them.
|
Commands on the Per-ARFCN Control Interface
|
||||||
|
|
||||||
|
The per-ARFCN control interface uses a command-reponse protocol.
|
||||||
|
Commands are NULL-terminated ASCII strings, one per UDP socket.
|
||||||
|
Each command has a corresponding response.
|
||||||
|
Every command is of the form:
|
||||||
|
|
||||||
|
CMD <cmdtype> [params]
|
||||||
|
|
||||||
|
The <cmdtype> is the actual command.
|
||||||
|
Parameters are optional depending on the commands type.
|
||||||
|
Every response is of the form:
|
||||||
|
|
||||||
|
RSP <cmdtype> <status> [result]
|
||||||
|
|
||||||
|
The <status> is 0 for success and a non-zero error code for failure.
|
||||||
|
Successful responses may include results, depending on the command type.
|
||||||
|
|
||||||
|
|
||||||
|
Power Control
|
||||||
|
|
||||||
|
POWEROFF shuts off transmitter power and stops the demodulator.
|
||||||
|
CMD POWEROFF
|
||||||
|
RSP POWEROFF <status>
|
||||||
|
|
||||||
|
POWERON starts the transmitter and starts the demodulator. Initial power level is very low.
|
||||||
|
This command fails if the transmitter and receiver are not yet tuned.
|
||||||
|
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
|
||||||
|
If the transceiver is already on, it response with success to this command.
|
||||||
|
CMD POWERON
|
||||||
|
RSP POWERON <status>
|
||||||
|
|
||||||
|
SETPOWER sets output power in dB wrt full scale.
|
||||||
|
This command fails if the transmitter and receiver are not running.
|
||||||
|
CMD SETPOWER <dB>
|
||||||
|
RSP SETPOWER <status> <dB>
|
||||||
|
|
||||||
|
ADJPOWER adjusts power by the given dB step. Response returns resulting power level wrt full scale.
|
||||||
|
This command fails if the transmitter and receiver are not running.
|
||||||
|
CMD ADJPOWER <dBStep>
|
||||||
|
RSP ADJPOWER <status> <dBLevel>
|
||||||
|
|
||||||
|
|
||||||
|
Tuning Control
|
||||||
|
|
||||||
|
RXTUNE tunes the receiver to a given frequency in kHz.
|
||||||
|
This command fails if the receiver is already running.
|
||||||
|
(To re-tune you stop the radio, re-tune, and restart.)
|
||||||
|
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
|
||||||
|
CMD RXTUNE <kHz>
|
||||||
|
RSP RXTUNE <status> <kHz>
|
||||||
|
|
||||||
|
TXTUNE tunes the transmitter to a given frequency in kHz.
|
||||||
|
This command fails if the transmitter is already running.
|
||||||
|
(To re-tune you stop the radio, re-tune, and restart.)
|
||||||
|
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
|
||||||
|
CMD TXTUNE <kHz>
|
||||||
|
RSP TXTUNE <status> <kHz>
|
||||||
|
|
||||||
|
|
||||||
|
Timeslot Control
|
||||||
|
|
||||||
|
SETSLOT sets the format of the uplink timeslots in the ARFCN.
|
||||||
|
The <timeslot> indicates the timeslot of interest.
|
||||||
|
The <chantype> indicates the type of channel that occupies the timeslot.
|
||||||
|
A chantype of zero indicates the timeslot is off.
|
||||||
|
CMD SETSLOT <timeslot> <chantype>
|
||||||
|
RSP SETSLOT <status> <timeslot> <chantype>
|
||||||
|
|
||||||
|
|
||||||
|
Messages on the per-ARFCN Data Interface
|
||||||
|
|
||||||
|
Messages on the data interface carry one radio burst per UDP message.
|
||||||
|
|
||||||
|
|
||||||
|
Received Data Burst
|
||||||
|
|
||||||
|
1 byte timeslot index
|
||||||
|
4 bytes GSM frame number, big endian
|
||||||
|
1 byte RSSI in -dBm
|
||||||
|
2 bytes correlator timing offset in 1/256 symbol steps, 2's-comp, big endian
|
||||||
|
148 bytes soft symbol estimates, 0 -> definite "0", 255 -> definite "1"
|
||||||
|
|
||||||
|
|
||||||
|
Transmit Data Burst
|
||||||
|
|
||||||
|
1 byte timeslot index
|
||||||
|
4 bytes GSM frame number, big endian
|
||||||
|
1 byte transmit level wrt ARFCN max, -dB (attenuation)
|
||||||
|
148 bytes output symbol values, 0 & 1
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Release history:
|
|
||||||
|
|
||||||
Release Name SVN Reposiory SVN Rev Comments
|
|
||||||
|
|
||||||
1.0 (none) SF.net ?? completed L1, L2
|
|
||||||
|
|
||||||
1.1 Arnaudville GNU Radio r10019 (trunk)
|
|
||||||
|
|
||||||
1.2 Breaux Bridge GNU Radio r10088 (trunk) GNU Build, very early assignment
|
|
||||||
|
|
||||||
1.3 Carencro KSP r1 (trunk) first post-injunction release
|
|
||||||
|
|
||||||
1.4 Donaldsonville KSP r23 (trunk) fixed Ubuntu build error
|
|
||||||
|
|
||||||
1.5 Eunice KSP r39 (trunk) fixed L2 bugs related to segmentation
|
|
||||||
removed incomplete SMS directory
|
|
||||||
moved "abort" calls into L3 subclasses
|
|
||||||
|
|
||||||
1.6 New Iberia KSP r130 (trunk) import of all 2.2 improvements to non-SMS release
|
|
||||||
|
|
||||||
|
|
||||||
2.0 St. Francisville KSP r54 (smswork) SMS support
|
|
||||||
file-based configuration
|
|
||||||
|
|
||||||
2.1 Grand Coteau KSP r70 (smswork) DTMF support
|
|
||||||
fixed more Linux-related build errors
|
|
||||||
-lpthread
|
|
||||||
TLMessage constructor
|
|
||||||
expanded stack to prevent overflows in Linux
|
|
||||||
moved gSIPInterface to main app
|
|
||||||
fixed iterator bug in Pager
|
|
||||||
|
|
||||||
2.2 Houma KSP r122 (smswork) added LEGAL notice
|
|
||||||
removed Assert classes
|
|
||||||
stop paging on page response
|
|
||||||
fixed Pager-spin bug
|
|
||||||
fixed Transceiver spin bugs
|
|
||||||
fixed 2^32 microsecond rollover bug
|
|
||||||
reduced stack footprints in Transceiver
|
|
||||||
fixed SMS timestamps
|
|
||||||
check LAI before using TMSI in LUR
|
|
||||||
reduced memory requirement by 75%
|
|
||||||
removed PagerTest
|
|
||||||
fixed stale-transaction bug in paging handler
|
|
||||||
fixed USRP clock rollover bug
|
|
||||||
faster call connection
|
|
||||||
new USRPDevice design
|
|
||||||
|
|
||||||
2.3 Jean Lafitte KSP r190? (trunk) check for out-of-date RACH bursts
|
|
||||||
better TRX-GSM clock sync
|
|
||||||
formal logging system
|
|
||||||
command line interface
|
|
||||||
emergency call setup
|
|
||||||
|
|
||||||
2.4 Kinder KSP r208? (trunk) fixed BCCH neighbor list bug
|
|
||||||
support for neighbor lists
|
|
||||||
fixed support for non-local Asterisk servers
|
|
||||||
cleaner configuration management
|
|
||||||
more realtime control of BCCH parameters
|
|
||||||
proper rejection of Hold messages
|
|
||||||
fixed L3 hanging bug in MTDCheckBYE
|
|
||||||
|
|
||||||
2.4.1 Kinder KSP r462 fixed lots of valgrind errors
|
|
||||||
|
|
||||||
2.4.2 Kinder KSP r482 zero-length calling party number bug
|
|
||||||
g++ 4.4 #includes
|
|
||||||
|
|
||||||
2.5 Lacassine KSP r551 imported Joshua Lackey patches
|
|
||||||
SIP fixes from Anne Kwong
|
|
||||||
SIP fixes from testing with SMS server
|
|
||||||
L3 TI handling fixes
|
|
||||||
SMS server support
|
|
||||||
GNU Radio 3.2 compatibility
|
|
||||||
configurable max range and LU-reject cause
|
|
||||||
"page" & "testcall" CLI features
|
|
||||||
|
|
||||||
2.5.1 Lacassine KSP r595 fixed some build bugs for some Linux distros
|
|
||||||
|
|
||||||
2.5.2 Lacassine KSP r630 fixed channel assignment bug for Nokia DCT4+ handsets
|
|
||||||
|
|
||||||
2.5.3 Lacassine KSP r756 merged fix for transceiver startup crash
|
|
||||||
due to use of uninitialized variables (r646)
|
|
||||||
merged fix for fusb bug from trunk (r582)
|
|
||||||
|
|
||||||
2.5.4 Lacassine KSP r812 merged fixes to build under latest Fedora and
|
|
||||||
to build with git GnuRadio (r814)
|
|
||||||
|
|
||||||
2.6 Mamou KSP r886 fixed infamous fusb bug (r582)
|
|
||||||
fixed idle-filling table size bug
|
|
||||||
smoother uplink power control
|
|
||||||
load-limiting downlink power control
|
|
||||||
new "config" features (optional, static)
|
|
||||||
IMEI interrogation
|
|
||||||
fixed MOD "missing FIFO" bug
|
|
||||||
configurable short code features
|
|
||||||
fixed transceiver startup crash (r646)
|
|
||||||
readline support is back
|
|
||||||
fixed timing advance bug (r844)
|
|
||||||
added CLI "chans" command
|
|
||||||
track time-of-use in TMSI table (r844)
|
|
||||||
added CLI "noise" command (r844)
|
|
||||||
added CLI "rxpower" command (r844)
|
|
||||||
added CLI "unconfig" command
|
|
||||||
|
|
||||||
2.7 Natchitoches Range rxxx (never released publicly)
|
|
||||||
converted TMSITable to sqlite3 (r902)
|
|
||||||
sqlite3-based configuration (r???)
|
|
||||||
converted Logger to syslogd (r903)
|
|
||||||
added support for rest octets (r1022)
|
|
||||||
external database for transaction reporting (r1184)
|
|
||||||
external database for channel status reporting (r1203)
|
|
||||||
in-call delivery and submission of text messages (r1231)
|
|
||||||
RFC-2833 DMTF (r1249)
|
|
||||||
|
|
||||||
2.8 Opelousas Range rxxx move databases to /etc and /var
|
|
||||||
RRLP aiding support
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <iomanip> // std::setprecision
|
||||||
|
#include <fstream>
|
||||||
#include "Transceiver.h"
|
#include "Transceiver.h"
|
||||||
#include <Logger.h>
|
#include <Logger.h>
|
||||||
|
|
||||||
@@ -140,18 +142,24 @@ bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
Transceiver::Transceiver(int wBasePort,
|
Transceiver::Transceiver(int wBasePort,
|
||||||
const char *wTRXAddress,
|
const char *wTRXAddress,
|
||||||
size_t wSPS, size_t wChans,
|
size_t wSPS, size_t wChans,
|
||||||
GSM::Time wTransmitLatency,
|
GSM::Time wTransmitLatency,
|
||||||
RadioInterface *wRadioInterface)
|
RadioInterface *wRadioInterface,
|
||||||
|
double wRssiOffset)
|
||||||
: mBasePort(wBasePort), mAddr(wTRXAddress),
|
: mBasePort(wBasePort), mAddr(wTRXAddress),
|
||||||
mClockSocket(wBasePort, wTRXAddress, mBasePort + 100),
|
|
||||||
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
|
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
|
||||||
|
rssiOffset(wRssiOffset),
|
||||||
mSPSTx(wSPS), mSPSRx(1), mChans(wChans), mOn(false),
|
mSPSTx(wSPS), mSPSRx(1), mChans(wChans), mOn(false),
|
||||||
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelay(0)
|
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelay(0), mWriteBurstToDiskMask(0)
|
||||||
{
|
{
|
||||||
txFullScale = mRadioInterface->fullScaleInputValue();
|
txFullScale = mRadioInterface->fullScaleInputValue();
|
||||||
rxFullScale = mRadioInterface->fullScaleOutputValue();
|
rxFullScale = mRadioInterface->fullScaleOutputValue();
|
||||||
|
|
||||||
|
for (int i = 0; i < 8; i++) {
|
||||||
|
for (int j = 0; j < 8; j++)
|
||||||
|
mHandover[i][j] = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Transceiver::~Transceiver()
|
Transceiver::~Transceiver()
|
||||||
@@ -168,6 +176,7 @@ Transceiver::~Transceiver()
|
|||||||
mTxPriorityQueues[i].clear();
|
mTxPriorityQueues[i].clear();
|
||||||
delete mCtrlSockets[i];
|
delete mCtrlSockets[i];
|
||||||
delete mDataSockets[i];
|
delete mDataSockets[i];
|
||||||
|
delete mClockSockets[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,7 +191,7 @@ Transceiver::~Transceiver()
|
|||||||
*/
|
*/
|
||||||
bool Transceiver::init(int filler, size_t rtsc)
|
bool Transceiver::init(int filler, size_t rtsc)
|
||||||
{
|
{
|
||||||
int d_srcport, d_dstport, c_srcport, c_dstport;
|
int t_srcport, t_dstport, d_srcport, d_dstport, c_srcport, c_dstport;
|
||||||
|
|
||||||
if (!mChans) {
|
if (!mChans) {
|
||||||
LOG(ALERT) << "No channels assigned";
|
LOG(ALERT) << "No channels assigned";
|
||||||
@@ -196,6 +205,7 @@ bool Transceiver::init(int filler, size_t rtsc)
|
|||||||
|
|
||||||
mDataSockets.resize(mChans);
|
mDataSockets.resize(mChans);
|
||||||
mCtrlSockets.resize(mChans);
|
mCtrlSockets.resize(mChans);
|
||||||
|
mClockSockets.resize(mChans);
|
||||||
mControlServiceLoopThreads.resize(mChans);
|
mControlServiceLoopThreads.resize(mChans);
|
||||||
mTxPriorityQueueServiceLoopThreads.resize(mChans);
|
mTxPriorityQueueServiceLoopThreads.resize(mChans);
|
||||||
mRxServiceLoopThreads.resize(mChans);
|
mRxServiceLoopThreads.resize(mChans);
|
||||||
@@ -210,13 +220,16 @@ bool Transceiver::init(int filler, size_t rtsc)
|
|||||||
|
|
||||||
/* Setup sockets */
|
/* Setup sockets */
|
||||||
for (size_t i = 0; i < mChans; i++) {
|
for (size_t i = 0; i < mChans; i++) {
|
||||||
c_srcport = mBasePort + 2 * i + 1;
|
t_srcport = mBasePort + 3 * i;
|
||||||
c_dstport = mBasePort + 2 * i + 101;
|
t_dstport = mBasePort + 3 * i + 100;
|
||||||
d_srcport = mBasePort + 2 * i + 2;
|
c_srcport = mBasePort + 3 * i + 1;
|
||||||
d_dstport = mBasePort + 2 * i + 102;
|
c_dstport = mBasePort + 3 * i + 101;
|
||||||
|
d_srcport = mBasePort + 3 * i + 2;
|
||||||
|
d_dstport = mBasePort + 3 * i + 102;
|
||||||
|
|
||||||
mCtrlSockets[i] = new UDPSocket(c_srcport, mAddr.c_str(), c_dstport);
|
mCtrlSockets[i] = new UDPSocket(c_srcport, mAddr.c_str(), c_dstport);
|
||||||
mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport);
|
mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport);
|
||||||
|
mClockSockets[i] = new UDPSocket(t_srcport, mAddr.c_str(), t_dstport);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Randomize the central clock */
|
/* Randomize the central clock */
|
||||||
@@ -459,9 +472,15 @@ void Transceiver::setModulus(size_t timeslot, size_t chan)
|
|||||||
Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
||||||
size_t chan)
|
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];
|
TransceiverState *state = &mStates[chan];
|
||||||
unsigned burstTN = currTime.TN();
|
unsigned burstTN = currTime.TN();
|
||||||
unsigned burstFN = currTime.FN();
|
unsigned burstFN = currTime.FN();
|
||||||
|
int subch;
|
||||||
|
|
||||||
switch (state->chanType[burstTN]) {
|
switch (state->chanType[burstTN]) {
|
||||||
case NONE:
|
case NONE:
|
||||||
@@ -471,16 +490,25 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
|||||||
return IDLE;
|
return IDLE;
|
||||||
break;
|
break;
|
||||||
case I:
|
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;
|
return TSC;
|
||||||
/*if (burstFN % 26 == 25)
|
|
||||||
return IDLE;
|
|
||||||
else
|
|
||||||
return TSC;*/
|
|
||||||
break;
|
break;
|
||||||
case II:
|
case II:
|
||||||
|
subch = tchh_subslot[burstFN % 26];
|
||||||
|
if (subch == 1)
|
||||||
|
return IDLE;
|
||||||
|
if (mHandover[burstTN][0])
|
||||||
|
return RACH;
|
||||||
return TSC;
|
return TSC;
|
||||||
break;
|
break;
|
||||||
case III:
|
case III:
|
||||||
|
subch = tchh_subslot[burstFN % 26];
|
||||||
|
if (mHandover[burstTN][subch])
|
||||||
|
return RACH;
|
||||||
return TSC;
|
return TSC;
|
||||||
break;
|
break;
|
||||||
case IV:
|
case IV:
|
||||||
@@ -495,6 +523,8 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
|||||||
return RACH;
|
return RACH;
|
||||||
else if ((mod51 == 45) || (mod51 == 46))
|
else if ((mod51 == 45) || (mod51 == 46))
|
||||||
return RACH;
|
return RACH;
|
||||||
|
else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
|
||||||
|
return RACH;
|
||||||
else
|
else
|
||||||
return TSC;
|
return TSC;
|
||||||
break;
|
break;
|
||||||
@@ -502,6 +532,8 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
|||||||
case VII:
|
case VII:
|
||||||
if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
|
if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
|
||||||
return IDLE;
|
return IDLE;
|
||||||
|
else if (mHandover[burstTN][sdcch8_subslot[burstFN % 102]])
|
||||||
|
return RACH;
|
||||||
else
|
else
|
||||||
return TSC;
|
return TSC;
|
||||||
break;
|
break;
|
||||||
@@ -531,13 +563,13 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
|||||||
* Detect RACH synchronization sequence within a burst. No equalization
|
* Detect RACH synchronization sequence within a burst. No equalization
|
||||||
* is used or available on the RACH channel.
|
* is used or available on the RACH channel.
|
||||||
*/
|
*/
|
||||||
bool Transceiver::detectRACH(TransceiverState *state,
|
int Transceiver::detectRACH(TransceiverState *state,
|
||||||
signalVector &burst,
|
signalVector &burst,
|
||||||
complex &, float &toa)
|
complex &, float &toa)
|
||||||
{
|
{
|
||||||
float threshold = 6.0;
|
float threshold = 6.0;
|
||||||
|
|
||||||
return detectRACHBurst(burst, threshold, mSPSRx, &, &toa);
|
return detectRACHBurst(burst, threshold, mSPSRx, amp, toa);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -545,12 +577,13 @@ bool Transceiver::detectRACH(TransceiverState *state,
|
|||||||
* state information and channel estimate if necessary. Equalization
|
* state information and channel estimate if necessary. Equalization
|
||||||
* is currently disabled.
|
* is currently disabled.
|
||||||
*/
|
*/
|
||||||
bool Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
|
int Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
|
||||||
complex &, float &toa, GSM::Time &time)
|
complex &, float &toa, GSM::Time &time)
|
||||||
{
|
{
|
||||||
|
int success;
|
||||||
int tn = time.TN();
|
int tn = time.TN();
|
||||||
float chanOffset, threshold = 5.0;
|
float chanOffset, threshold = 5.0;
|
||||||
bool noise, needDFE = false, estimateChan = false;
|
bool needDFE = false, estimateChan = false;
|
||||||
double elapsed = time - state->chanEstimateTime[tn];
|
double elapsed = time - state->chanEstimateTime[tn];
|
||||||
signalVector *chanResp;
|
signalVector *chanResp;
|
||||||
|
|
||||||
@@ -565,17 +598,18 @@ bool Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Detect normal burst midambles */
|
/* Detect normal burst midambles */
|
||||||
if (!analyzeTrafficBurst(burst, mTSC, threshold, mSPSRx, &,
|
success = analyzeTrafficBurst(burst, mTSC, threshold, mSPSRx, amp,
|
||||||
&toa, mMaxExpectedDelay, estimateChan,
|
toa, mMaxExpectedDelay, estimateChan,
|
||||||
&chanResp, &chanOffset)) {
|
&chanResp, &chanOffset);
|
||||||
return false;
|
if (success <= 0) {
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
noise = state->mNoiseLev;
|
|
||||||
state->SNRestimate[tn] = amp.norm2() / (noise * noise + 1.0);
|
|
||||||
|
|
||||||
/* Set equalizer if unabled */
|
/* Set equalizer if unabled */
|
||||||
if (needDFE && estimateChan) {
|
if (needDFE && estimateChan) {
|
||||||
|
float noise = state->mNoiseLev;
|
||||||
|
state->SNRestimate[tn] = amp.norm2() / (noise * noise + 1.0);
|
||||||
|
|
||||||
state->chanResponse[tn] = chanResp;
|
state->chanResponse[tn] = chanResp;
|
||||||
state->chanRespOffset[tn] = chanOffset;
|
state->chanRespOffset[tn] = chanOffset;
|
||||||
state->chanRespAmplitude[tn] = amp;
|
state->chanRespAmplitude[tn] = amp;
|
||||||
@@ -588,7 +622,7 @@ bool Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
|
|||||||
state->chanEstimateTime[tn] = time;
|
state->chanEstimateTime[tn] = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -611,20 +645,33 @@ SoftVector *Transceiver::demodulate(TransceiverState *state,
|
|||||||
return demodulateBurst(burst, mSPSRx, amp, toa);
|
return demodulateBurst(burst, mSPSRx, amp, toa);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void writeToFile(radioVector *radio_burst, size_t chan)
|
||||||
|
{
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pull bursts from the FIFO and handle according to the slot
|
* Pull bursts from the FIFO and handle according to the slot
|
||||||
* and burst correlation type. Equalzation is currently disabled.
|
* and burst correlation type. Equalzation is currently disabled.
|
||||||
*/
|
*/
|
||||||
SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
|
SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
|
||||||
int &timingOffset, size_t chan)
|
double &timingOffset, double &noise,
|
||||||
|
size_t chan)
|
||||||
{
|
{
|
||||||
bool success, equalize = false;
|
int success;
|
||||||
|
bool equalize = false;
|
||||||
complex amp;
|
complex amp;
|
||||||
float toa, pow, max = -1.0, avg = 0.0;
|
float toa, pow, max = -1.0, avg = 0.0;
|
||||||
int max_i = -1;
|
int max_i = -1;
|
||||||
signalVector *burst;
|
signalVector *burst;
|
||||||
SoftVector *bits = NULL;
|
SoftVector *bits = NULL;
|
||||||
TransceiverState *state = &mStates[chan];
|
TransceiverState *state = &mStates[chan];
|
||||||
|
isRssiValid = false;
|
||||||
|
|
||||||
/* Blocking FIFO read */
|
/* Blocking FIFO read */
|
||||||
radioVector *radio_burst = mReceiveFIFO[chan]->read();
|
radioVector *radio_burst = mReceiveFIFO[chan]->read();
|
||||||
@@ -635,7 +682,17 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
|
|||||||
GSM::Time time = radio_burst->getTime();
|
GSM::Time time = radio_burst->getTime();
|
||||||
CorrType type = expectedCorrType(time, chan);
|
CorrType type = expectedCorrType(time, chan);
|
||||||
|
|
||||||
if ((type == OFF) || (type == IDLE)) {
|
mRadioInterface->updateBurstRxParameters(time, chan);
|
||||||
|
|
||||||
|
/* 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) {
|
||||||
delete radio_burst;
|
delete radio_burst;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -659,7 +716,25 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
|
|||||||
/* Average noise on diversity paths and update global levels */
|
/* Average noise on diversity paths and update global levels */
|
||||||
burst = radio_burst->getVector(max_i);
|
burst = radio_burst->getVector(max_i);
|
||||||
avg = sqrt(avg / radio_burst->chans());
|
avg = sqrt(avg / radio_burst->chans());
|
||||||
state->mNoiseLev = state->mNoises.avg();
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
/* Detect normal or RACH bursts */
|
/* Detect normal or RACH bursts */
|
||||||
if (type == TSC)
|
if (type == TSC)
|
||||||
@@ -667,33 +742,27 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
|
|||||||
else
|
else
|
||||||
success = detectRACH(state, *burst, amp, toa);
|
success = detectRACH(state, *burst, amp, toa);
|
||||||
|
|
||||||
/* Update noise average if no bust detected or alert on error */
|
/* Alert an error and exit */
|
||||||
if (success <= 0) {
|
if (success <= 0) {
|
||||||
if (!success) {
|
if (success == -SIGERR_CLIP) {
|
||||||
state->mNoises.insert(avg);
|
LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
|
||||||
} else if (success == -SIGERR_CLIP) {
|
} else if (success != SIGERR_NONE) {
|
||||||
LOG(ALERT) << "Clipping detected on RACH input";
|
LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
|
||||||
} else if (success < 0) {
|
|
||||||
LOG(ALERT) << "Unhandled RACH error";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
delete radio_burst;
|
delete radio_burst;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
timingOffset = toa / mSPSRx;
|
||||||
|
|
||||||
/* Demodulate and set output info */
|
/* Demodulate and set output info */
|
||||||
if (equalize && (type != TSC))
|
if (equalize && (type != TSC))
|
||||||
equalize = false;
|
equalize = false;
|
||||||
|
|
||||||
if (avg - state->mNoiseLev > 0.0)
|
bits = demodulate(state, *burst, amp, toa, time.TN(), equalize);
|
||||||
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;
|
delete radio_burst;
|
||||||
|
|
||||||
return bits;
|
return bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -743,6 +812,24 @@ void Transceiver::driveControl(size_t chan)
|
|||||||
sprintf(response,"RSP POWERON 1");
|
sprintf(response,"RSP POWERON 1");
|
||||||
else
|
else
|
||||||
sprintf(response,"RSP POWERON 0");
|
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;
|
||||||
|
LOG(WARNING) << "HANDOVER RACH at timeslot " << ts << " subslot " << ss;
|
||||||
|
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;
|
||||||
|
LOG(WARNING) << "NOHANDOVER at timeslot " << ts << " subslot " << ss;
|
||||||
|
sprintf(response,"RSP NOHANDOVER 0 %d %d",ts,ss);
|
||||||
}
|
}
|
||||||
else if (strcmp(command,"SETMAXDLY")==0) {
|
else if (strcmp(command,"SETMAXDLY")==0) {
|
||||||
//set expected maximum time-of-arrival
|
//set expected maximum time-of-arrival
|
||||||
@@ -836,6 +923,14 @@ void Transceiver::driveControl(size_t chan)
|
|||||||
sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
|
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 {
|
else {
|
||||||
LOG(WARNING) << "bogus command " << command << " on control interface.";
|
LOG(WARNING) << "bogus command " << command << " on control interface.";
|
||||||
sprintf(response,"RSP ERR 1");
|
sprintf(response,"RSP ERR 1");
|
||||||
@@ -892,27 +987,35 @@ void Transceiver::driveReceiveRadio()
|
|||||||
void Transceiver::driveReceiveFIFO(size_t chan)
|
void Transceiver::driveReceiveFIFO(size_t chan)
|
||||||
{
|
{
|
||||||
SoftVector *rxBurst = NULL;
|
SoftVector *rxBurst = NULL;
|
||||||
int RSSI;
|
double RSSI; // in dBFS
|
||||||
int TOA; // in 1/256 of a symbol
|
double dBm; // in dBm
|
||||||
|
double TOA; // in symbols
|
||||||
|
int TOAint; // in 1/256 symbols
|
||||||
|
double noise; // noise level in dBFS
|
||||||
GSM::Time burstTime;
|
GSM::Time burstTime;
|
||||||
|
bool isRssiValid; // are RSSI, noise and burstTime valid
|
||||||
|
|
||||||
rxBurst = pullRadioVector(burstTime, RSSI, TOA, chan);
|
rxBurst = pullRadioVector(burstTime, RSSI, isRssiValid, TOA, noise, chan);
|
||||||
|
|
||||||
if (rxBurst) {
|
if (rxBurst) {
|
||||||
|
dBm = RSSI+rssiOffset;
|
||||||
|
TOAint = (int) (TOA * 256.0 + 0.5); // round to closest integer
|
||||||
|
|
||||||
|
LOG(DEBUG) << std::fixed << std::right
|
||||||
|
<< " chan: " << chan
|
||||||
|
<< " time: " << burstTime
|
||||||
|
<< " 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: " << *rxBurst;
|
||||||
|
|
||||||
LOG(DEBUG) << "burst parameters: "
|
|
||||||
<< " time: " << burstTime
|
|
||||||
<< " RSSI: " << RSSI
|
|
||||||
<< " TOA: " << TOA
|
|
||||||
<< " bits: " << *rxBurst;
|
|
||||||
|
|
||||||
char burstString[gSlotLen+10];
|
char burstString[gSlotLen+10];
|
||||||
burstString[0] = burstTime.TN();
|
burstString[0] = burstTime.TN();
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
|
burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
|
||||||
burstString[5] = RSSI;
|
burstString[5] = (int)dBm;
|
||||||
burstString[6] = (TOA >> 8) & 0x0ff;
|
burstString[6] = (TOAint >> 8) & 0x0ff;
|
||||||
burstString[7] = TOA & 0x0ff;
|
burstString[7] = TOAint & 0x0ff;
|
||||||
SoftVector::iterator burstItr = rxBurst->begin();
|
SoftVector::iterator burstItr = rxBurst->begin();
|
||||||
|
|
||||||
for (unsigned int i = 0; i < gSlotLen; i++) {
|
for (unsigned int i = 0; i < gSlotLen; i++) {
|
||||||
@@ -987,7 +1090,8 @@ void Transceiver::writeClockInterface()
|
|||||||
|
|
||||||
LOG(INFO) << "ClockInterface: sending " << command;
|
LOG(INFO) << "ClockInterface: sending " << command;
|
||||||
|
|
||||||
mClockSocket.write(command, strlen(command) + 1);
|
for (size_t i=0; i<mClockSockets.size(); i++)
|
||||||
|
mClockSockets[i]->write(command, strlen(command) + 1);
|
||||||
|
|
||||||
mLastClockUpdateTime = mTransmitDeadlineClock;
|
mLastClockUpdateTime = mTransmitDeadlineClock;
|
||||||
|
|
||||||
|
|||||||
@@ -97,10 +97,11 @@ public:
|
|||||||
@param radioInterface associated radioInterface object
|
@param radioInterface associated radioInterface object
|
||||||
*/
|
*/
|
||||||
Transceiver(int wBasePort,
|
Transceiver(int wBasePort,
|
||||||
const char *TRXAddress,
|
const char *TRXAddress,
|
||||||
size_t wSPS, size_t chans,
|
size_t wSPS, size_t chans,
|
||||||
GSM::Time wTransmitLatency,
|
GSM::Time wTransmitLatency,
|
||||||
RadioInterface *wRadioInterface);
|
RadioInterface *wRadioInterface,
|
||||||
|
double wRssiOffset);
|
||||||
|
|
||||||
/** Destructor */
|
/** Destructor */
|
||||||
~Transceiver();
|
~Transceiver();
|
||||||
@@ -161,7 +162,7 @@ private:
|
|||||||
|
|
||||||
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
|
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
|
||||||
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
|
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
|
||||||
UDPSocket mClockSocket; ///< socket for writing clock updates to GSM core
|
std::vector<UDPSocket *> mClockSockets; ///< socket for writing clock updates to GSM core
|
||||||
|
|
||||||
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
|
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
|
||||||
std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
|
std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
|
||||||
@@ -181,6 +182,8 @@ private:
|
|||||||
double txFullScale; ///< full scale input to radio
|
double txFullScale; ///< full scale input to radio
|
||||||
double rxFullScale; ///< full scale output 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 */
|
/** modulate and add a burst to the transmit queue */
|
||||||
void addRadioVector(size_t chan, BitVector &bits,
|
void addRadioVector(size_t chan, BitVector &bits,
|
||||||
int RSSI, GSM::Time &wTime);
|
int RSSI, GSM::Time &wTime);
|
||||||
@@ -192,8 +195,9 @@ private:
|
|||||||
void pushRadioVector(GSM::Time &nowTime);
|
void pushRadioVector(GSM::Time &nowTime);
|
||||||
|
|
||||||
/** Pull and demodulate a burst from the receive FIFO */
|
/** Pull and demodulate a burst from the receive FIFO */
|
||||||
SoftVector *pullRadioVector(GSM::Time &wTime, int &RSSI,
|
SoftVector *pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
|
||||||
int &timingOffset, size_t chan = 0);
|
double &timingOffset, double &noise,
|
||||||
|
size_t chan = 0);
|
||||||
|
|
||||||
/** Set modulus for specific timeslot */
|
/** Set modulus for specific timeslot */
|
||||||
void setModulus(size_t timeslot, size_t chan);
|
void setModulus(size_t timeslot, size_t chan);
|
||||||
@@ -205,14 +209,14 @@ private:
|
|||||||
void writeClockInterface(void);
|
void writeClockInterface(void);
|
||||||
|
|
||||||
/** Detect RACH bursts */
|
/** Detect RACH bursts */
|
||||||
bool detectRACH(TransceiverState *state,
|
int detectRACH(TransceiverState *state,
|
||||||
signalVector &burst,
|
signalVector &burst,
|
||||||
complex &, float &toa);
|
complex &, float &toa);
|
||||||
|
|
||||||
/** Detect normal bursts */
|
/** Detect normal bursts */
|
||||||
bool detectTSC(TransceiverState *state,
|
int detectTSC(TransceiverState *state,
|
||||||
signalVector &burst,
|
signalVector &burst,
|
||||||
complex &, float &toa, GSM::Time &time);
|
complex &, float &toa, GSM::Time &time);
|
||||||
|
|
||||||
/** Demodulat burst and output soft bits */
|
/** Demodulat burst and output soft bits */
|
||||||
SoftVector *demodulate(TransceiverState *state,
|
SoftVector *demodulate(TransceiverState *state,
|
||||||
@@ -223,11 +227,13 @@ private:
|
|||||||
int mSPSRx; ///< number of samples per Rx symbol
|
int mSPSRx; ///< number of samples per Rx symbol
|
||||||
size_t mChans;
|
size_t mChans;
|
||||||
|
|
||||||
bool mOn; ///< flag to indicate that transceiver is powered on
|
bool mOn; ///< flag to indicate that transceiver is powered on
|
||||||
|
bool mHandover[8][8]; ///< expect handover to the timeslot/subslot
|
||||||
double mTxFreq; ///< the transmit frequency
|
double mTxFreq; ///< the transmit frequency
|
||||||
double mRxFreq; ///< the receive frequency
|
double mRxFreq; ///< the receive frequency
|
||||||
unsigned mTSC; ///< the midamble sequence code
|
unsigned mTSC; ///< the midamble sequence code
|
||||||
unsigned mMaxExpectedDelay; ///< maximum expected time-of-arrival offset in GSM symbols
|
unsigned mMaxExpectedDelay; ///< maximum expected time-of-arrival offset in GSM symbols
|
||||||
|
unsigned mWriteBurstToDiskMask; ///< debug: bitmask to indicate which timeslots to dump to disk
|
||||||
|
|
||||||
std::vector<TransceiverState> mStates;
|
std::vector<TransceiverState> mStates;
|
||||||
|
|
||||||
|
|||||||
@@ -210,8 +210,7 @@ uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
|
|||||||
|
|
||||||
TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
|
TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
|
||||||
{
|
{
|
||||||
TIMESTAMP ticks = ts.get_full_secs() * rate;
|
return (TIMESTAMP)(ts.get_real_secs() * rate + 0.5);
|
||||||
return ts.get_tick_count(rate) + ticks;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -290,7 +289,7 @@ public:
|
|||||||
uhd_device(size_t sps, size_t chans, bool diversity, double offset);
|
uhd_device(size_t sps, size_t chans, bool diversity, double offset);
|
||||||
~uhd_device();
|
~uhd_device();
|
||||||
|
|
||||||
int open(const std::string &args, bool extref);
|
int open(const std::string &args, bool extref, bool swap_channels);
|
||||||
bool start();
|
bool start();
|
||||||
bool stop();
|
bool stop();
|
||||||
bool restart();
|
bool restart();
|
||||||
@@ -330,6 +329,10 @@ public:
|
|||||||
inline double getSampleRate() { return tx_rate; }
|
inline double getSampleRate() { return tx_rate; }
|
||||||
inline double numberRead() { return rx_pkt_cnt; }
|
inline double numberRead() { return rx_pkt_cnt; }
|
||||||
inline double numberWritten() { return 0; }
|
inline double numberWritten() { return 0; }
|
||||||
|
TIMESTAMP getCurrentTimestampRx() { return current_time.to_ticks(rx_rate); }
|
||||||
|
TIMESTAMP getCurrentTimestampTx() { return current_time.to_ticks(tx_rate); }
|
||||||
|
|
||||||
|
void set_diversity(bool diversity, TIMESTAMP cmd_time, size_t chan);
|
||||||
|
|
||||||
/** Receive and process asynchronous message
|
/** Receive and process asynchronous message
|
||||||
@return true if message received or false on timeout or error
|
@return true if message received or false on timeout or error
|
||||||
@@ -369,8 +372,11 @@ private:
|
|||||||
uhd::time_spec_t prev_ts;
|
uhd::time_spec_t prev_ts;
|
||||||
|
|
||||||
TIMESTAMP ts_initial, ts_offset;
|
TIMESTAMP ts_initial, ts_offset;
|
||||||
|
uhd::time_spec_t current_time;
|
||||||
std::vector<smpl_buf *> rx_buffers;
|
std::vector<smpl_buf *> rx_buffers;
|
||||||
|
|
||||||
|
std::vector<uhd::property<bool>* > divsw_props;
|
||||||
|
|
||||||
void init_gains();
|
void init_gains();
|
||||||
int set_master_clk(double rate);
|
int set_master_clk(double rate);
|
||||||
int set_rates(double tx_rate, double rx_rate);
|
int set_rates(double tx_rate, double rx_rate);
|
||||||
@@ -473,18 +479,24 @@ void uhd_device::init_gains()
|
|||||||
tx_gain_min = range.start();
|
tx_gain_min = range.start();
|
||||||
tx_gain_max = range.stop();
|
tx_gain_max = range.stop();
|
||||||
}
|
}
|
||||||
|
LOG(INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
|
||||||
|
|
||||||
range = usrp_dev->get_rx_gain_range();
|
range = usrp_dev->get_rx_gain_range();
|
||||||
rx_gain_min = range.start();
|
rx_gain_min = range.start();
|
||||||
rx_gain_max = range.stop();
|
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++) {
|
for (size_t i = 0; i < tx_gains.size(); i++) {
|
||||||
usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2, i);
|
double gain = (tx_gain_min + tx_gain_max) / 2;
|
||||||
|
LOG(INFO) << "Default setting Tx gain for channel " << i << " to " << gain;
|
||||||
|
usrp_dev->set_tx_gain(gain, i);
|
||||||
tx_gains[i] = usrp_dev->get_tx_gain(i);
|
tx_gains[i] = usrp_dev->get_tx_gain(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < rx_gains.size(); i++) {
|
for (size_t i = 0; i < rx_gains.size(); i++) {
|
||||||
usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2, i);
|
double gain = (rx_gain_min + rx_gain_max) / 2;
|
||||||
|
LOG(INFO) << "Default setting Rx gain for channel " << i << " to " << gain;
|
||||||
|
usrp_dev->set_rx_gain(gain, i);
|
||||||
rx_gains[i] = usrp_dev->get_rx_gain(i);
|
rx_gains[i] = usrp_dev->get_rx_gain(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -582,7 +594,7 @@ double uhd_device::setTxGain(double db, size_t chan)
|
|||||||
|
|
||||||
tx_gains[chan] = usrp_dev->get_tx_gain(chan);
|
tx_gains[chan] = usrp_dev->get_tx_gain(chan);
|
||||||
|
|
||||||
LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB";
|
LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)";
|
||||||
|
|
||||||
return tx_gains[chan];
|
return tx_gains[chan];
|
||||||
}
|
}
|
||||||
@@ -597,7 +609,7 @@ double uhd_device::setRxGain(double db, size_t chan)
|
|||||||
usrp_dev->set_rx_gain(db, chan);
|
usrp_dev->set_rx_gain(db, chan);
|
||||||
rx_gains[chan] = usrp_dev->get_rx_gain(chan);
|
rx_gains[chan] = usrp_dev->get_rx_gain(chan);
|
||||||
|
|
||||||
LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB";
|
LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
|
||||||
|
|
||||||
return rx_gains[chan];
|
return rx_gains[chan];
|
||||||
}
|
}
|
||||||
@@ -694,7 +706,7 @@ bool uhd_device::parse_dev_type()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int uhd_device::open(const std::string &args, bool extref)
|
int uhd_device::open(const std::string &args, bool extref, bool swap_channels)
|
||||||
{
|
{
|
||||||
// Find UHD devices
|
// Find UHD devices
|
||||||
uhd::device_addr_t addr(args);
|
uhd::device_addr_t addr(args);
|
||||||
@@ -720,7 +732,7 @@ int uhd_device::open(const std::string &args, bool extref)
|
|||||||
// Verify and set channels
|
// Verify and set channels
|
||||||
if ((dev_type == B210) && (chans == 2)) {
|
if ((dev_type == B210) && (chans == 2)) {
|
||||||
} else if ((dev_type == UMTRX) && (chans == 2)) {
|
} else if ((dev_type == UMTRX) && (chans == 2)) {
|
||||||
uhd::usrp::subdev_spec_t subdev_spec("A:0 B:0");
|
uhd::usrp::subdev_spec_t subdev_spec(swap_channels?"B:0 A:0":"A:0 B:0");
|
||||||
usrp_dev->set_tx_subdev_spec(subdev_spec);
|
usrp_dev->set_tx_subdev_spec(subdev_spec);
|
||||||
usrp_dev->set_rx_subdev_spec(subdev_spec);
|
usrp_dev->set_rx_subdev_spec(subdev_spec);
|
||||||
} else if (chans != 1) {
|
} else if (chans != 1) {
|
||||||
@@ -786,6 +798,12 @@ int uhd_device::open(const std::string &args, bool extref)
|
|||||||
ts_offset = (TIMESTAMP) (offset * rx_rate);
|
ts_offset = (TIMESTAMP) (offset * rx_rate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
divsw_props.resize(chans);
|
||||||
|
for (size_t i=0; i<chans; i++) {
|
||||||
|
const uhd::fs_path db_path = uhd::fs_path("/mboards/0/dboards") / ((i==0)?"A":"B");
|
||||||
|
divsw_props[i] = &usrp_dev->get_device()->get_tree()->access<bool>(db_path / "rx_frontends" / "0" / "diversity");
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize and shadow gain values
|
// Initialize and shadow gain values
|
||||||
init_gains();
|
init_gains();
|
||||||
|
|
||||||
@@ -1020,8 +1038,9 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ts = metadata.time_spec;
|
current_time = metadata.time_spec;
|
||||||
LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
|
LOG(DEBUG) << "Received timestamp = " << current_time.get_real_secs();
|
||||||
|
// LOG(INFO) << "Received timestamp = " << convert_time(current_time, rx_rate);
|
||||||
|
|
||||||
for (size_t i = 0; i < rx_buffers.size(); i++) {
|
for (size_t i = 0; i < rx_buffers.size(); i++) {
|
||||||
rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
|
rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
|
||||||
@@ -1092,6 +1111,8 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LOG(INFO) << "Sending timestamp = " << timestamp << "(" << convert_time(metadata.time_spec, tx_rate) << ")";
|
||||||
|
|
||||||
thread_enable_cancel(false);
|
thread_enable_cancel(false);
|
||||||
size_t num_smpls = tx_stream->send(bufs, len, metadata);
|
size_t num_smpls = tx_stream->send(bufs, len, metadata);
|
||||||
thread_enable_cancel(true);
|
thread_enable_cancel(true);
|
||||||
@@ -1115,7 +1136,7 @@ uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
|
|||||||
uhd::tune_request_t treq(freq);
|
uhd::tune_request_t treq(freq);
|
||||||
|
|
||||||
if (dev_type == UMTRX) {
|
if (dev_type == UMTRX) {
|
||||||
if (offset > 0.0)
|
if (offset != 0.0)
|
||||||
return uhd::tune_request_t(freq, offset);
|
return uhd::tune_request_t(freq, offset);
|
||||||
|
|
||||||
// Don't use DSP tuning, because LMS6002D PLL steps are small enough.
|
// Don't use DSP tuning, because LMS6002D PLL steps are small enough.
|
||||||
@@ -1126,6 +1147,7 @@ uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
|
|||||||
treq.rf_freq = freq;
|
treq.rf_freq = freq;
|
||||||
treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
|
treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
|
||||||
treq.dsp_freq = 0.0;
|
treq.dsp_freq = 0.0;
|
||||||
|
return treq;
|
||||||
} else if (chans == 1) {
|
} else if (chans == 1) {
|
||||||
if (offset == 0.0)
|
if (offset == 0.0)
|
||||||
return treq;
|
return treq;
|
||||||
@@ -1255,6 +1277,26 @@ double uhd_device::fullScaleOutputValue()
|
|||||||
return (double) SHRT_MAX;
|
return (double) SHRT_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void uhd_device::set_diversity(bool diversity, TIMESTAMP cmd_timestamp, size_t chan)
|
||||||
|
{
|
||||||
|
// Shift read time with respect to transmit clock
|
||||||
|
TIMESTAMP rx_timestamp = cmd_timestamp+ts_offset;
|
||||||
|
const uhd::time_spec_t cmd_time = convert_time(rx_timestamp, rx_rate);
|
||||||
|
|
||||||
|
// LOG(INFO) << "Sending timed command at " << std::setprecision(15) << cmd_time.get_real_secs() << " sec "
|
||||||
|
// << rx_timestamp << " original cmd_timestamp=" << cmd_timestamp;
|
||||||
|
// LOG(INFO) << "Sending timed command at " << cmd_time.get_real_secs() << " sec "
|
||||||
|
// << rx_timestamp << " original cmd_timestamp=" << cmd_timestamp;
|
||||||
|
|
||||||
|
usrp_dev->set_command_time(cmd_time);
|
||||||
|
divsw_props[chan]->set(diversity ? 1 : 0);
|
||||||
|
// usrp_dev->get_device()->get_tree()->access<bool>(db_path / "rx_frontends" / "0" / "diversity").set(diversity ? 1 : 0);
|
||||||
|
// usrp_dev->set_rx_antenna((diversity?"RX2":"RX1"), chan);
|
||||||
|
// usrp_dev->set_rx_gain((diversity?rx_gains[chan]-10:rx_gains[chan]), chan);
|
||||||
|
// usrp_dev->set_rx_gain(rx_gains[chan], chan);
|
||||||
|
usrp_dev->clear_command_time();
|
||||||
|
}
|
||||||
|
|
||||||
bool uhd_device::recv_async_msg()
|
bool uhd_device::recv_async_msg()
|
||||||
{
|
{
|
||||||
uhd::async_metadata_t md;
|
uhd::async_metadata_t md;
|
||||||
@@ -1269,10 +1311,11 @@ bool uhd_device::recv_async_msg()
|
|||||||
if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
|
if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
|
||||||
aligned = false;
|
aligned = false;
|
||||||
|
|
||||||
if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
|
LOG(ERR) << str_code(md);
|
||||||
(md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
|
// if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
|
||||||
LOG(ERR) << str_code(md);
|
// (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
|
||||||
}
|
// LOG(ERR) << str_code(md);
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -1309,7 +1352,8 @@ std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (metadata.has_time_spec)
|
if (metadata.has_time_spec)
|
||||||
ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
|
ost << " at " << metadata.time_spec.get_real_secs() << " sec "
|
||||||
|
<< convert_time(metadata.time_spec, rx_rate) << " TIMESTAMP";
|
||||||
|
|
||||||
return ost.str();
|
return ost.str();
|
||||||
}
|
}
|
||||||
@@ -1342,7 +1386,9 @@ std::string uhd_device::str_code(uhd::async_metadata_t metadata)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (metadata.has_time_spec)
|
if (metadata.has_time_spec)
|
||||||
ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
|
ost << " at " << std::setprecision(15) << metadata.time_spec.get_real_secs() << " sec "
|
||||||
|
<< convert_time(metadata.time_spec, rx_rate) << " Rx TIMESTAMP "
|
||||||
|
<< convert_time(metadata.time_spec, tx_rate) << " Tx TIMESTAMP";
|
||||||
|
|
||||||
return ost.str();
|
return ost.str();
|
||||||
}
|
}
|
||||||
@@ -1430,6 +1476,19 @@ ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
|
|||||||
if ((timestamp + len) <= time_end)
|
if ((timestamp + len) <= time_end)
|
||||||
return ERROR_TIMESTAMP;
|
return ERROR_TIMESTAMP;
|
||||||
|
|
||||||
|
if (timestamp < time_end) {
|
||||||
|
LOG(ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
|
||||||
|
uhd::time_spec_t ts = convert_time(timestamp, clk_rt);
|
||||||
|
LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << convert_time(ts, 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 = convert_time(timestamp, clk_rt);
|
||||||
|
LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << convert_time(ts, clk_rt) << ") rate=" << clk_rt;
|
||||||
|
// Do not return error here, because it's a rounding error and is not fatal
|
||||||
|
}
|
||||||
|
|
||||||
// Starting index
|
// Starting index
|
||||||
size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
|
size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ USRPDevice::USRPDevice(size_t sps, size_t, bool)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int USRPDevice::open(const std::string &, bool)
|
int USRPDevice::open(const std::string &, bool, bool)
|
||||||
{
|
{
|
||||||
writeLock.unlock();
|
writeLock.unlock();
|
||||||
|
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ private:
|
|||||||
USRPDevice(size_t sps, size_t chans = 1, bool diversity = false);
|
USRPDevice(size_t sps, size_t chans = 1, bool diversity = false);
|
||||||
|
|
||||||
/** Instantiate the USRP */
|
/** Instantiate the USRP */
|
||||||
int open(const std::string &, bool);
|
int open(const std::string &, bool, bool);
|
||||||
|
|
||||||
/** Start the USRP */
|
/** Start the USRP */
|
||||||
bool start();
|
bool start();
|
||||||
|
|||||||
@@ -70,6 +70,8 @@ struct trx_config {
|
|||||||
Transceiver::FillerType filler;
|
Transceiver::FillerType filler;
|
||||||
bool diversity;
|
bool diversity;
|
||||||
double offset;
|
double offset;
|
||||||
|
double rssi_offset;
|
||||||
|
bool swap_channels;
|
||||||
};
|
};
|
||||||
|
|
||||||
ConfigurationTable gConfig;
|
ConfigurationTable gConfig;
|
||||||
@@ -185,6 +187,8 @@ bool trx_setup_config(struct trx_config *config)
|
|||||||
ost << " C0 Filler Table......... " << fillstr << std::endl;
|
ost << " C0 Filler Table......... " << fillstr << std::endl;
|
||||||
ost << " Diversity............... " << divstr << std::endl;
|
ost << " Diversity............... " << divstr << std::endl;
|
||||||
ost << " Tuning offset........... " << config->offset << 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;
|
std::cout << ost << std::endl;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -240,7 +244,7 @@ Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio)
|
|||||||
VectorFIFO *fifo;
|
VectorFIFO *fifo;
|
||||||
|
|
||||||
trx = new Transceiver(config->port, config->addr.c_str(), config->sps,
|
trx = new Transceiver(config->port, config->addr.c_str(), config->sps,
|
||||||
config->chans, GSM::Time(3,0), radio);
|
config->chans, GSM::Time(3,0), radio, config->rssi_offset);
|
||||||
if (!trx->init(config->filler, config->rtsc)) {
|
if (!trx->init(config->filler, config->rtsc)) {
|
||||||
LOG(ALERT) << "Failed to initialize transceiver";
|
LOG(ALERT) << "Failed to initialize transceiver";
|
||||||
delete trx;
|
delete trx;
|
||||||
@@ -292,7 +296,9 @@ static void print_help()
|
|||||||
" -c Number of ARFCN channels (default=1)\n"
|
" -c Number of ARFCN channels (default=1)\n"
|
||||||
" -f Enable C0 filler table\n"
|
" -f Enable C0 filler table\n"
|
||||||
" -o Set baseband frequency offset (default=auto)\n"
|
" -o Set baseband frequency offset (default=auto)\n"
|
||||||
" -r Random burst test mode with TSC\n",
|
" -r Random burst test mode with TSC\n"
|
||||||
|
" -R RSSI to dBm offset in dB (default=0)\n"
|
||||||
|
" -S Swap channels (UmTRX only)\n",
|
||||||
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
|
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,8 +314,10 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
|||||||
config->filler = Transceiver::FILLER_ZERO;
|
config->filler = Transceiver::FILLER_ZERO;
|
||||||
config->diversity = false;
|
config->diversity = false;
|
||||||
config->offset = 0.0;
|
config->offset = 0.0;
|
||||||
|
config->rssi_offset = 0.0;
|
||||||
|
config->swap_channels = false;
|
||||||
|
|
||||||
while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:r:")) != -1) {
|
while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:r:R:S")) != -1) {
|
||||||
switch (option) {
|
switch (option) {
|
||||||
case 'h':
|
case 'h':
|
||||||
print_help();
|
print_help();
|
||||||
@@ -349,6 +357,12 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
|||||||
config->rtsc = atoi(optarg);
|
config->rtsc = atoi(optarg);
|
||||||
config->filler = Transceiver::FILLER_RAND;
|
config->filler = Transceiver::FILLER_RAND;
|
||||||
break;
|
break;
|
||||||
|
case 'R':
|
||||||
|
config->rssi_offset = atof(optarg);
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
config->swap_channels = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
print_help();
|
print_help();
|
||||||
exit(0);
|
exit(0);
|
||||||
@@ -393,7 +407,7 @@ int main(int argc, char *argv[])
|
|||||||
/* Create the low level device object */
|
/* Create the low level device object */
|
||||||
usrp = RadioDevice::make(config.sps, config.chans,
|
usrp = RadioDevice::make(config.sps, config.chans,
|
||||||
config.diversity, config.offset);
|
config.diversity, config.offset);
|
||||||
type = usrp->open(config.dev_args, config.extref);
|
type = usrp->open(config.dev_args, config.extref, config.swap_channels);
|
||||||
if (type < 0) {
|
if (type < 0) {
|
||||||
LOG(ALERT) << "Failed to create radio device" << std::endl;
|
LOG(ALERT) << "Failed to create radio device" << std::endl;
|
||||||
goto shutdown;
|
goto shutdown;
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class RadioDevice {
|
|||||||
bool diversity = false, double offset = 0.0);
|
bool diversity = false, double offset = 0.0);
|
||||||
|
|
||||||
/** Initialize the USRP */
|
/** Initialize the USRP */
|
||||||
virtual int open(const std::string &args = "", bool extref = false)=0;
|
virtual int open(const std::string &args = "", bool extref = false, bool swap_channels = false)=0;
|
||||||
|
|
||||||
virtual ~RadioDevice() { }
|
virtual ~RadioDevice() { }
|
||||||
|
|
||||||
@@ -130,7 +130,11 @@ class RadioDevice {
|
|||||||
virtual double getSampleRate()=0;
|
virtual double getSampleRate()=0;
|
||||||
virtual double numberRead()=0;
|
virtual double numberRead()=0;
|
||||||
virtual double numberWritten()=0;
|
virtual double numberWritten()=0;
|
||||||
|
virtual TIMESTAMP getCurrentTimestampRx() = 0;
|
||||||
|
virtual TIMESTAMP getCurrentTimestampTx() = 0;
|
||||||
|
|
||||||
|
/** Diversity switch support */
|
||||||
|
virtual void set_diversity(bool diversity, TIMESTAMP cmd_time, size_t chan) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "radioInterface.h"
|
#include "radioInterface.h"
|
||||||
#include "Resampler.h"
|
#include "Resampler.h"
|
||||||
#include <Logger.h>
|
#include <Logger.h>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "convert.h"
|
#include "convert.h"
|
||||||
@@ -190,6 +191,7 @@ bool RadioInterface::start()
|
|||||||
|
|
||||||
writeTimestamp = mRadio->initialWriteTimestamp();
|
writeTimestamp = mRadio->initialWriteTimestamp();
|
||||||
readTimestamp = mRadio->initialReadTimestamp();
|
readTimestamp = mRadio->initialReadTimestamp();
|
||||||
|
configureTimestamp = readTimestamp;
|
||||||
|
|
||||||
mRadio->updateAlignment(writeTimestamp-10000);
|
mRadio->updateAlignment(writeTimestamp-10000);
|
||||||
mRadio->updateAlignment(writeTimestamp-10000);
|
mRadio->updateAlignment(writeTimestamp-10000);
|
||||||
@@ -275,6 +277,8 @@ bool RadioInterface::driveReceiveRadio()
|
|||||||
* pattern of 157-156-156-156 symbols per timeslot
|
* pattern of 157-156-156-156 symbols per timeslot
|
||||||
*/
|
*/
|
||||||
while (recvSz > burstSize) {
|
while (recvSz > burstSize) {
|
||||||
|
// updateBurstRxParameters();
|
||||||
|
|
||||||
for (size_t i = 0; i < mChans; i++) {
|
for (size_t i = 0; i < mChans; i++) {
|
||||||
burst = new radioVector(rcvClock, burstSize, head, mMIMO);
|
burst = new radioVector(rcvClock, burstSize, head, mMIMO);
|
||||||
|
|
||||||
@@ -294,6 +298,7 @@ bool RadioInterface::driveReceiveRadio()
|
|||||||
rcvClock.incTN();
|
rcvClock.incTN();
|
||||||
readSz += burstSize;
|
readSz += burstSize;
|
||||||
recvSz -= burstSize;
|
recvSz -= burstSize;
|
||||||
|
configureTimestamp += burstSize;
|
||||||
|
|
||||||
tN = rcvClock.TN();
|
tN = rcvClock.TN();
|
||||||
|
|
||||||
@@ -402,3 +407,42 @@ void RadioInterface::pushBuffer()
|
|||||||
writeTimestamp += num_sent;
|
writeTimestamp += num_sent;
|
||||||
sendCursor = 0;
|
sendCursor = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RadioInterface::updateBurstRxParameters(const GSM::Time &gsmTime, size_t chan)
|
||||||
|
{
|
||||||
|
if (chan != 0) return;
|
||||||
|
|
||||||
|
TIMESTAMP curTs = mRadio->getCurrentTimestampRx();
|
||||||
|
|
||||||
|
// TODO: Choose a proper value
|
||||||
|
const int burstAdvance = 8*3;
|
||||||
|
|
||||||
|
// Get TN of the burst to update
|
||||||
|
GSM::Time rcvClock = gsmTime;
|
||||||
|
// rcvClock.decTN(receiveOffset);
|
||||||
|
rcvClock.incTN(burstAdvance);
|
||||||
|
unsigned tN = rcvClock.TN();
|
||||||
|
unsigned fN = rcvClock.FN();
|
||||||
|
|
||||||
|
const double symbolsPerSlot = gSlotLen + 8.25;
|
||||||
|
// TODO: Properly take into account 156/157 burst sizes
|
||||||
|
//const double burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
|
||||||
|
const TIMESTAMP burstTimestamp = configureTimestamp + symbolsPerSlot*mSPSRx*burstAdvance;
|
||||||
|
|
||||||
|
LOG(INFO) << "chan=" << chan << " " << rcvClock << " current_rx_timestamp=" << curTs
|
||||||
|
<< " configureTimestamp=" << configureTimestamp << " (" << std::setw(5) << int(configureTimestamp)-int(curTs) << ")"
|
||||||
|
<< " burstTimestamp=" << burstTimestamp << " (" << std::setw(5) << int(burstTimestamp)-int(curTs) << ")";
|
||||||
|
|
||||||
|
// if (tN != 2 && tN != 3)
|
||||||
|
if (tN != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: real decision making
|
||||||
|
bool diversity = false;
|
||||||
|
// mRadio->set_diversity(((fN%2==0) != (tN%2==0))?false:true, burstTimestamp, chan);
|
||||||
|
// if (tN==2)
|
||||||
|
diversity = (fN%2==0)?false:true;
|
||||||
|
LOG(INFO) << "chan=" << chan << " " << rcvClock << " diversity=" << diversity;
|
||||||
|
mRadio->set_diversity(diversity, burstTimestamp, chan);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ protected:
|
|||||||
bool overrun; ///< indicates reads from USRP are too slow
|
bool overrun; ///< indicates reads from USRP are too slow
|
||||||
TIMESTAMP writeTimestamp; ///< sample timestamp of next packet written to USRP
|
TIMESTAMP writeTimestamp; ///< sample timestamp of next packet written to USRP
|
||||||
TIMESTAMP readTimestamp; ///< sample timestamp of next packet read from USRP
|
TIMESTAMP readTimestamp; ///< sample timestamp of next packet read from USRP
|
||||||
|
TIMESTAMP configureTimestamp; ///< sample timestamp of next burst to configure
|
||||||
|
|
||||||
RadioClock mClock; ///< the basestation clock!
|
RadioClock mClock; ///< the basestation clock!
|
||||||
|
|
||||||
@@ -135,6 +136,9 @@ public:
|
|||||||
/** get transport window type of attached device */
|
/** get transport window type of attached device */
|
||||||
enum RadioDevice::TxWindowType getWindowType() { return mRadio->getWindowType(); }
|
enum RadioDevice::TxWindowType getWindowType() { return mRadio->getWindowType(); }
|
||||||
|
|
||||||
|
/** update diversity switch and other reception of GSM bursts */
|
||||||
|
void updateBurstRxParameters(const GSM::Time &gsmTime, size_t chan);
|
||||||
|
|
||||||
#if USRP1
|
#if USRP1
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
#include "sigProcLib.h"
|
#include "sigProcLib.h"
|
||||||
#include "GSMCommon.h"
|
#include "GSMCommon.h"
|
||||||
|
#include "Logger.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "convolve.h"
|
#include "convolve.h"
|
||||||
@@ -1284,12 +1285,12 @@ static float computePeakRatio(signalVector *corr,
|
|||||||
complex *peak;
|
complex *peak;
|
||||||
float rms, avg = 0.0;
|
float rms, avg = 0.0;
|
||||||
|
|
||||||
peak = corr->begin() + (int) rint(toa);
|
|
||||||
|
|
||||||
/* Check for bogus results */
|
/* Check for bogus results */
|
||||||
if ((toa < 0.0) || (toa > corr->size()))
|
if ((toa < 0.0) || (toa > corr->size()))
|
||||||
return 0.0;
|
return 0.0;
|
||||||
|
|
||||||
|
peak = corr->begin() + (int) rint(toa);
|
||||||
|
|
||||||
for (int i = 2 * sps; i <= 5 * sps; i++) {
|
for (int i = 2 * sps; i <= 5 * sps; i++) {
|
||||||
if (peak - i >= corr->begin()) {
|
if (peak - i >= corr->begin()) {
|
||||||
avg += (peak - i)->norm2();
|
avg += (peak - i)->norm2();
|
||||||
@@ -1372,18 +1373,74 @@ static int detectBurst(signalVector &burst,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int detectClipping(signalVector &burst, float thresh)
|
static float maxAmplitude(signalVector &burst)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < burst.size(); i++) {
|
float max = 0.0;
|
||||||
if (fabs(burst[i].real()) > thresh)
|
for (size_t i = 0; i < burst.size(); i++) {
|
||||||
return 1;
|
if (fabs(burst[i].real()) > max)
|
||||||
if (fabs(burst[i].imag()) > thresh)
|
max = fabs(burst[i].real());
|
||||||
return 1;
|
if (fabs(burst[i].imag()) > max)
|
||||||
}
|
max = fabs(burst[i].imag());
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RACH/Normal burst detection with clipping detection
|
||||||
|
*
|
||||||
|
* Correlation window parameters:
|
||||||
|
* target: Tail bits + burst length
|
||||||
|
* head: Search symbols before target
|
||||||
|
* tail: Search symbols after target
|
||||||
|
*/
|
||||||
|
int detectGeneralBurst(signalVector &rxBurst,
|
||||||
|
float thresh,
|
||||||
|
int sps,
|
||||||
|
complex &,
|
||||||
|
float &toa,
|
||||||
|
int target, int head, int tail,
|
||||||
|
CorrelationSequence *sync)
|
||||||
|
{
|
||||||
|
int rc, start, len;
|
||||||
|
bool clipping = false;
|
||||||
|
signalVector *corr;
|
||||||
|
|
||||||
|
if ((sps != 1) && (sps != 4))
|
||||||
|
return -SIGERR_UNSUPPORTED;
|
||||||
|
|
||||||
|
// Detect potential clipping
|
||||||
|
// We still may be able to demod the burst, so we'll give it a try
|
||||||
|
// and only report clipping if we can't demod.
|
||||||
|
float maxAmpl = maxAmplitude(rxBurst);
|
||||||
|
if (maxAmpl > CLIP_THRESH) {
|
||||||
|
LOG(DEBUG) << "max burst amplitude: " << maxAmpl << " is above the clipping threshold: " << CLIP_THRESH << std::endl;
|
||||||
|
clipping = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
start = (target - head) * sps - 1;
|
||||||
|
len = (head + tail) * sps;
|
||||||
|
corr = new signalVector(len);
|
||||||
|
|
||||||
|
rc = detectBurst(rxBurst, *corr, sync,
|
||||||
|
thresh, sps, &, &toa, start, len);
|
||||||
|
delete corr;
|
||||||
|
|
||||||
|
if (rc < 0) {
|
||||||
|
return -SIGERR_INTERNAL;
|
||||||
|
} else if (!rc) {
|
||||||
|
amp = 0.0f;
|
||||||
|
toa = 0.0f;
|
||||||
|
return clipping?-SIGERR_CLIP:SIGERR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Subtract forward search bits from delay */
|
||||||
|
toa -= head * sps;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* RACH burst detection
|
* RACH burst detection
|
||||||
*
|
*
|
||||||
@@ -1393,53 +1450,23 @@ static int detectClipping(signalVector &burst, float thresh)
|
|||||||
* tail: Search 10 symbols after target
|
* tail: Search 10 symbols after target
|
||||||
*/
|
*/
|
||||||
int detectRACHBurst(signalVector &rxBurst,
|
int detectRACHBurst(signalVector &rxBurst,
|
||||||
float thresh,
|
float thresh,
|
||||||
int sps,
|
int sps,
|
||||||
complex *amp,
|
complex &,
|
||||||
float *toa)
|
float &toa)
|
||||||
{
|
{
|
||||||
int rc, start, target, head, tail, len;
|
int rc, target, head, tail;
|
||||||
float _toa;
|
|
||||||
complex _amp;
|
|
||||||
signalVector *corr;
|
|
||||||
CorrelationSequence *sync;
|
CorrelationSequence *sync;
|
||||||
|
|
||||||
if ((sps != 1) && (sps != 4))
|
|
||||||
return -SIGERR_UNSUPPORTED;
|
|
||||||
|
|
||||||
if (detectClipping(rxBurst, CLIP_THRESH))
|
|
||||||
return -SIGERR_CLIP;
|
|
||||||
|
|
||||||
target = 8 + 40;
|
target = 8 + 40;
|
||||||
head = 4;
|
head = 4;
|
||||||
tail = 10;
|
tail = 10;
|
||||||
|
|
||||||
start = (target - head) * sps - 1;
|
|
||||||
len = (head + tail) * sps;
|
|
||||||
sync = gRACHSequence;
|
sync = gRACHSequence;
|
||||||
corr = new signalVector(len);
|
|
||||||
|
|
||||||
rc = detectBurst(rxBurst, *corr, sync,
|
rc = detectGeneralBurst(rxBurst, thresh, sps, amp, toa,
|
||||||
thresh, sps, &_amp, &_toa, start, len);
|
target, head, tail, sync);
|
||||||
delete corr;
|
|
||||||
|
|
||||||
if (rc < 0) {
|
return rc;
|
||||||
return -SIGERR_INTERNAL;
|
|
||||||
} else if (!rc) {
|
|
||||||
if (amp)
|
|
||||||
*amp = 0.0f;
|
|
||||||
if (toa)
|
|
||||||
*toa = 0.0f;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Subtract forward search bits from delay */
|
|
||||||
if (toa)
|
|
||||||
*toa = _toa - head * sps;
|
|
||||||
if (amp)
|
|
||||||
*amp = _amp;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -1451,60 +1478,32 @@ int detectRACHBurst(signalVector &rxBurst,
|
|||||||
* tail: Search 4 symbols + maximum expected delay
|
* tail: Search 4 symbols + maximum expected delay
|
||||||
*/
|
*/
|
||||||
int analyzeTrafficBurst(signalVector &rxBurst, unsigned tsc, float thresh,
|
int analyzeTrafficBurst(signalVector &rxBurst, unsigned tsc, float thresh,
|
||||||
int sps, complex *amp, float *toa, unsigned max_toa,
|
int sps, complex &, float &toa, unsigned max_toa,
|
||||||
bool chan_req, signalVector **chan, float *chan_offset)
|
bool chan_req, signalVector **chan, float *chan_offset)
|
||||||
{
|
{
|
||||||
int rc, start, target, head, tail, len;
|
int rc, target, head, tail;
|
||||||
complex _amp;
|
|
||||||
float _toa;
|
|
||||||
signalVector *corr;
|
|
||||||
CorrelationSequence *sync;
|
CorrelationSequence *sync;
|
||||||
|
|
||||||
if ((tsc < 0) || (tsc > 7) || ((sps != 1) && (sps != 4)))
|
if ((tsc < 0) || (tsc > 7))
|
||||||
return -SIGERR_UNSUPPORTED;
|
return -SIGERR_UNSUPPORTED;
|
||||||
|
|
||||||
if (detectClipping(rxBurst, CLIP_THRESH))
|
|
||||||
return -SIGERR_CLIP;
|
|
||||||
|
|
||||||
target = 3 + 58 + 16 + 5;
|
target = 3 + 58 + 16 + 5;
|
||||||
head = 4;
|
head = 4;
|
||||||
tail = 4 + max_toa;
|
tail = 4 + max_toa;
|
||||||
|
|
||||||
start = (target - head) * sps - 1;
|
|
||||||
len = (head + tail) * sps;
|
|
||||||
sync = gMidambles[tsc];
|
sync = gMidambles[tsc];
|
||||||
corr = new signalVector(len);
|
|
||||||
|
|
||||||
rc = detectBurst(rxBurst, *corr, sync,
|
rc = detectGeneralBurst(rxBurst, thresh, sps, amp, toa,
|
||||||
thresh, sps, &_amp, &_toa, start, len);
|
target, head, tail, sync);
|
||||||
delete corr;
|
|
||||||
|
|
||||||
if (rc < 0) {
|
|
||||||
return -SIGERR_INTERNAL;
|
|
||||||
} else if (!rc) {
|
|
||||||
if (amp)
|
|
||||||
*amp = 0.0f;
|
|
||||||
if (toa)
|
|
||||||
*toa = 0.0f;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Subtract forward search bits from delay */
|
|
||||||
_toa -= head * sps;
|
|
||||||
if (toa)
|
|
||||||
*toa = _toa;
|
|
||||||
if (amp)
|
|
||||||
*amp = _amp;
|
|
||||||
|
|
||||||
/* Equalization not currently supported */
|
/* Equalization not currently supported */
|
||||||
if (chan_req) {
|
if (rc > 0 && chan_req) {
|
||||||
*chan = new signalVector(6 * sps);
|
*chan = new signalVector(6 * sps);
|
||||||
|
|
||||||
if (chan_offset)
|
if (chan_offset)
|
||||||
*chan_offset = 0.0;
|
*chan_offset = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
signalVector *decimateVector(signalVector &wVector, size_t factor)
|
signalVector *decimateVector(signalVector &wVector, size_t factor)
|
||||||
|
|||||||
@@ -192,8 +192,8 @@ bool energyDetect(signalVector &rxBurst,
|
|||||||
int detectRACHBurst(signalVector &rxBurst,
|
int detectRACHBurst(signalVector &rxBurst,
|
||||||
float detectThreshold,
|
float detectThreshold,
|
||||||
int sps,
|
int sps,
|
||||||
complex *amplitude,
|
complex &litude,
|
||||||
float* TOA);
|
float &TOA);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Normal burst correlator, detector, channel estimator.
|
Normal burst correlator, detector, channel estimator.
|
||||||
@@ -210,15 +210,15 @@ int detectRACHBurst(signalVector &rxBurst,
|
|||||||
@return positive if threshold value is reached, negative on error, zero otherwise
|
@return positive if threshold value is reached, negative on error, zero otherwise
|
||||||
*/
|
*/
|
||||||
int analyzeTrafficBurst(signalVector &rxBurst,
|
int analyzeTrafficBurst(signalVector &rxBurst,
|
||||||
unsigned TSC,
|
unsigned TSC,
|
||||||
float detectThreshold,
|
float detectThreshold,
|
||||||
int sps,
|
int sps,
|
||||||
complex *amplitude,
|
complex &litude,
|
||||||
float *TOA,
|
float &TOA,
|
||||||
unsigned maxTOA,
|
unsigned maxTOA,
|
||||||
bool requestChannel = false,
|
bool requestChannel = false,
|
||||||
signalVector** channelResponse = NULL,
|
signalVector** channelResponse = NULL,
|
||||||
float *channelResponseOffset = NULL);
|
float *channelResponseOffset = NULL);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Decimate a vector.
|
Decimate a vector.
|
||||||
|
|||||||
3
utils/clockdump.sh
Executable file
3
utils/clockdump.sh
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
sudo tcpdump -i lo0 -A udp port 5700
|
||||||
|
|
||||||
Reference in New Issue
Block a user