2011-10-12 07:44:40 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
|
|
|
*
|
|
|
|
* This software is distributed under the terms of the GNU Public License.
|
|
|
|
* See the COPYING file in the main directory for details.
|
|
|
|
*
|
|
|
|
* This use of this software may be subject to additional restrictions.
|
|
|
|
* See the LEGAL file in the main directory for details.
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2015-06-04 03:47:56 +00:00
|
|
|
#include <iomanip> // std::setprecision
|
2011-10-12 07:44:40 +00:00
|
|
|
#include "Transceiver.h"
|
|
|
|
#include <Logger.h>
|
|
|
|
|
2012-08-08 00:51:31 +00:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2013-06-16 10:30:58 +00:00
|
|
|
using namespace GSM;
|
|
|
|
|
Alexander's patches:
1)I did an experiment and compiled OpenBTS with clang yesterday, which
immediately highlighted two potential bugs in the Transceiver52 code.
I'm not sure they are indeed bugs and not the intended behavior, but
they look very much like that. The first one is below and the second
one is in the following mail.
GSM::Time() arguments are defined like #define USB_LATENCY_INTRVL
(10,0), which means that they are expanded into GSM::Time((10,0)).
This expression is a GSM::Time() with a single parameter where (10,0)
return value of the last argument, 0 in this case. I.e.
GSM::Time((10,0)) is equivalent to GSM::Time(0). I think this was not
the intention.
2) Printing \n after every complex number breaks output when you want to
print it in a single line, e.g. in many debug output.
I do not claim any copyright over this change, as it's very basic.
Looking forward to see it merged into mainline.
git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@4515 19bc5d8c-e614-43d4-8b26-e1612bc8e597
2012-11-23 08:37:32 +00:00
|
|
|
#define USB_LATENCY_INTRVL 10,0
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2012-08-08 00:51:31 +00:00
|
|
|
#if USE_UHD
|
Alexander's patches:
1)I did an experiment and compiled OpenBTS with clang yesterday, which
immediately highlighted two potential bugs in the Transceiver52 code.
I'm not sure they are indeed bugs and not the intended behavior, but
they look very much like that. The first one is below and the second
one is in the following mail.
GSM::Time() arguments are defined like #define USB_LATENCY_INTRVL
(10,0), which means that they are expanded into GSM::Time((10,0)).
This expression is a GSM::Time() with a single parameter where (10,0)
return value of the last argument, 0 in this case. I.e.
GSM::Time((10,0)) is equivalent to GSM::Time(0). I think this was not
the intention.
2) Printing \n after every complex number breaks output when you want to
print it in a single line, e.g. in many debug output.
I do not claim any copyright over this change, as it's very basic.
Looking forward to see it merged into mainline.
git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@4515 19bc5d8c-e614-43d4-8b26-e1612bc8e597
2012-11-23 08:37:32 +00:00
|
|
|
# define USB_LATENCY_MIN 6,7
|
2012-08-08 00:51:31 +00:00
|
|
|
#else
|
Alexander's patches:
1)I did an experiment and compiled OpenBTS with clang yesterday, which
immediately highlighted two potential bugs in the Transceiver52 code.
I'm not sure they are indeed bugs and not the intended behavior, but
they look very much like that. The first one is below and the second
one is in the following mail.
GSM::Time() arguments are defined like #define USB_LATENCY_INTRVL
(10,0), which means that they are expanded into GSM::Time((10,0)).
This expression is a GSM::Time() with a single parameter where (10,0)
return value of the last argument, 0 in this case. I.e.
GSM::Time((10,0)) is equivalent to GSM::Time(0). I think this was not
the intention.
2) Printing \n after every complex number breaks output when you want to
print it in a single line, e.g. in many debug output.
I do not claim any copyright over this change, as it's very basic.
Looking forward to see it merged into mainline.
git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@4515 19bc5d8c-e614-43d4-8b26-e1612bc8e597
2012-11-23 08:37:32 +00:00
|
|
|
# define USB_LATENCY_MIN 1,1
|
2012-08-08 00:51:31 +00:00
|
|
|
#endif
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2013-10-18 01:23:34 +00:00
|
|
|
/* Number of running values use in noise average */
|
|
|
|
#define NOISE_CNT 20
|
2012-12-06 15:43:55 +00:00
|
|
|
|
2013-10-29 19:55:47 +00:00
|
|
|
TransceiverState::TransceiverState()
|
2014-11-25 23:46:56 +00:00
|
|
|
: mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT), mPower(0.0)
|
2013-10-29 19:55:47 +00:00
|
|
|
{
|
|
|
|
for (int i = 0; i < 8; i++) {
|
|
|
|
chanType[i] = Transceiver::NONE;
|
|
|
|
fillerModulus[i] = 26;
|
|
|
|
chanResponse[i] = NULL;
|
|
|
|
DFEForward[i] = NULL;
|
|
|
|
DFEFeedback[i] = NULL;
|
|
|
|
|
|
|
|
for (int n = 0; n < 102; n++)
|
|
|
|
fillerTable[n][i] = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TransceiverState::~TransceiverState()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < 8; i++) {
|
|
|
|
delete chanResponse[i];
|
|
|
|
delete DFEForward[i];
|
|
|
|
delete DFEFeedback[i];
|
|
|
|
|
|
|
|
for (int n = 0; n < 102; n++)
|
|
|
|
delete fillerTable[n][i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-20 01:26:31 +00:00
|
|
|
static BitVector *genRandNormalBurst(size_t tsc)
|
2013-10-29 19:55:47 +00:00
|
|
|
{
|
2015-05-20 01:26:31 +00:00
|
|
|
if (tsc > 7)
|
|
|
|
return NULL;
|
2014-01-25 07:34:03 +00:00
|
|
|
|
2015-05-20 01:26:31 +00:00
|
|
|
BitVector *bits = new BitVector(148);
|
2014-01-25 07:34:03 +00:00
|
|
|
|
2015-05-20 01:26:31 +00:00
|
|
|
size_t i = 0;
|
|
|
|
|
|
|
|
/* Tail bits */
|
|
|
|
for (; i < 4; i++)
|
|
|
|
(*bits)[i] = 0;
|
|
|
|
|
|
|
|
/* Random bits */
|
|
|
|
for (; i < 61; i++)
|
|
|
|
(*bits)[i] = rand() % 2;
|
|
|
|
|
|
|
|
/* Training sequence */
|
2015-05-24 23:13:38 +00:00
|
|
|
for (int j = 0; i < 87; i++, j++)
|
|
|
|
(*bits)[i] = GSM::gTrainingSequence[tsc][j];
|
2015-05-20 01:26:31 +00:00
|
|
|
|
|
|
|
/* Random bits */
|
|
|
|
for (; i < 144; 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;
|
|
|
|
|
|
|
|
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 = modulateBurst(gDummyBurst, guard, sps);
|
|
|
|
break;
|
|
|
|
case Transceiver::FILLER_RAND:
|
|
|
|
bits = genRandNormalBurst(rtsc);
|
|
|
|
burst = modulateBurst(*bits, guard, sps);
|
|
|
|
delete bits;
|
|
|
|
break;
|
|
|
|
case Transceiver::FILLER_ZERO:
|
|
|
|
default:
|
|
|
|
burst = new signalVector(len);
|
|
|
|
}
|
|
|
|
|
|
|
|
scaleVector(*burst, scale);
|
|
|
|
fillerTable[i][n] = burst;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (filler == Transceiver::FILLER_RAND)
|
|
|
|
chanType[n] = Transceiver::TSC;
|
2014-01-25 07:34:03 +00:00
|
|
|
}
|
2015-05-20 01:26:31 +00:00
|
|
|
|
|
|
|
return false;
|
2013-10-29 19:55:47 +00:00
|
|
|
}
|
|
|
|
|
2011-10-12 07:44:40 +00:00
|
|
|
Transceiver::Transceiver(int wBasePort,
|
2015-06-04 03:47:56 +00:00
|
|
|
const char *wTRXAddress,
|
|
|
|
size_t wSPS, size_t wChans,
|
|
|
|
GSM::Time wTransmitLatency,
|
|
|
|
RadioInterface *wRadioInterface,
|
|
|
|
double wRssiOffset)
|
2014-11-26 00:06:32 +00:00
|
|
|
: mBasePort(wBasePort), mAddr(wTRXAddress),
|
|
|
|
mClockSocket(wBasePort, wTRXAddress, mBasePort + 100),
|
|
|
|
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
|
2015-06-04 03:47:56 +00:00
|
|
|
rssiOffset(wRssiOffset),
|
2014-11-26 00:06:32 +00:00
|
|
|
mSPSTx(wSPS), mSPSRx(1), mChans(wChans), mOn(false),
|
2015-05-20 19:02:29 +00:00
|
|
|
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelay(0)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
|
|
|
txFullScale = mRadioInterface->fullScaleInputValue();
|
|
|
|
rxFullScale = mRadioInterface->fullScaleOutputValue();
|
2013-08-20 20:10:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Transceiver::~Transceiver()
|
|
|
|
{
|
2014-11-26 00:06:32 +00:00
|
|
|
stop();
|
2013-10-29 19:17:34 +00:00
|
|
|
|
2014-11-26 00:06:32 +00:00
|
|
|
sigProcLibDestroy();
|
2013-10-29 22:34:16 +00:00
|
|
|
|
|
|
|
for (size_t i = 0; i < mChans; i++) {
|
2014-11-26 00:06:32 +00:00
|
|
|
mControlServiceLoopThreads[i]->cancel();
|
|
|
|
mControlServiceLoopThreads[i]->join();
|
|
|
|
delete mControlServiceLoopThreads[i];
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
mTxPriorityQueues[i].clear();
|
|
|
|
delete mCtrlSockets[i];
|
|
|
|
delete mDataSockets[i];
|
|
|
|
}
|
2013-08-20 20:10:01 +00:00
|
|
|
}
|
|
|
|
|
2014-11-26 00:06:32 +00:00
|
|
|
/*
|
|
|
|
* Initialize transceiver
|
|
|
|
*
|
|
|
|
* Start or restart the control loop. Any further control is handled through the
|
|
|
|
* socket API. Randomize the central radio clock set the downlink burst
|
|
|
|
* counters. Note that the clock will not update until the radio starts, but we
|
|
|
|
* are still expected to report clock indications through control channel
|
|
|
|
* activity.
|
|
|
|
*/
|
2015-05-20 01:26:31 +00:00
|
|
|
bool Transceiver::init(int filler, size_t rtsc)
|
2013-08-20 20:10:01 +00:00
|
|
|
{
|
2013-11-14 03:40:44 +00:00
|
|
|
int d_srcport, d_dstport, c_srcport, c_dstport;
|
2013-10-29 19:55:47 +00:00
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
if (!mChans) {
|
|
|
|
LOG(ALERT) << "No channels assigned";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-10-11 17:49:55 +00:00
|
|
|
if (!sigProcLibSetup(mSPSTx)) {
|
2013-08-20 20:10:01 +00:00
|
|
|
LOG(ALERT) << "Failed to initialize signal processing library";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
mDataSockets.resize(mChans);
|
|
|
|
mCtrlSockets.resize(mChans);
|
|
|
|
mControlServiceLoopThreads.resize(mChans);
|
|
|
|
mTxPriorityQueueServiceLoopThreads.resize(mChans);
|
|
|
|
mRxServiceLoopThreads.resize(mChans);
|
|
|
|
|
|
|
|
mTxPriorityQueues.resize(mChans);
|
|
|
|
mReceiveFIFO.resize(mChans);
|
|
|
|
mStates.resize(mChans);
|
|
|
|
|
2014-04-15 21:41:28 +00:00
|
|
|
/* Filler table retransmissions - support only on channel 0 */
|
2015-05-20 01:26:31 +00:00
|
|
|
if (filler == FILLER_DUMMY)
|
2014-04-15 21:41:28 +00:00
|
|
|
mStates[0].mRetrans = true;
|
|
|
|
|
2014-11-26 00:06:32 +00:00
|
|
|
/* Setup sockets */
|
2013-10-29 22:34:16 +00:00
|
|
|
for (size_t i = 0; i < mChans; i++) {
|
2013-11-14 03:40:44 +00:00
|
|
|
c_srcport = mBasePort + 2 * i + 1;
|
|
|
|
c_dstport = mBasePort + 2 * i + 101;
|
|
|
|
d_srcport = mBasePort + 2 * i + 2;
|
|
|
|
d_dstport = mBasePort + 2 * i + 102;
|
|
|
|
|
|
|
|
mCtrlSockets[i] = new UDPSocket(c_srcport, mAddr.c_str(), c_dstport);
|
|
|
|
mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport);
|
2013-10-29 22:34:16 +00:00
|
|
|
}
|
|
|
|
|
2014-11-26 00:06:32 +00:00
|
|
|
/* Randomize the central clock */
|
|
|
|
GSM::Time startTime(random() % gHyperframe, 0);
|
|
|
|
mRadioInterface->getClock()->set(startTime);
|
|
|
|
mTransmitDeadlineClock = startTime;
|
|
|
|
mLastClockUpdateTime = startTime;
|
|
|
|
mLatencyUpdateTime = startTime;
|
|
|
|
|
|
|
|
/* Start control threads */
|
2013-10-29 22:34:16 +00:00
|
|
|
for (size_t i = 0; i < mChans; i++) {
|
2014-11-26 00:06:32 +00:00
|
|
|
TransceiverChannel *chan = new TransceiverChannel(this, i);
|
2013-10-29 22:34:16 +00:00
|
|
|
mControlServiceLoopThreads[i] = new Thread(32768);
|
2014-11-26 00:06:32 +00:00
|
|
|
mControlServiceLoopThreads[i]->start((void * (*)(void*))
|
|
|
|
ControlServiceLoopAdapter, (void*) chan);
|
2013-08-20 20:10:01 +00:00
|
|
|
|
2015-05-20 01:26:31 +00:00
|
|
|
if (i && filler == FILLER_DUMMY)
|
|
|
|
filler = FILLER_ZERO;
|
|
|
|
|
|
|
|
mStates[i].init(filler, mSPSTx, txFullScale, rtsc);
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2013-08-20 20:10:01 +00:00
|
|
|
return true;
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2014-11-26 00:06:32 +00:00
|
|
|
/*
|
|
|
|
* Start the transceiver
|
|
|
|
*
|
|
|
|
* Submit command(s) to the radio device to commence streaming samples and
|
|
|
|
* launch threads to handle sample I/O. Re-synchronize the transmit burst
|
|
|
|
* counters to the central radio clock here as well.
|
|
|
|
*/
|
|
|
|
bool Transceiver::start()
|
|
|
|
{
|
|
|
|
ScopedLock lock(mLock);
|
|
|
|
|
|
|
|
if (mOn) {
|
|
|
|
LOG(ERR) << "Transceiver already running";
|
2015-04-23 14:08:27 +00:00
|
|
|
return true;
|
2014-11-26 00:06:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
LOG(NOTICE) << "Starting the transceiver";
|
|
|
|
|
|
|
|
GSM::Time time = mRadioInterface->getClock()->get();
|
|
|
|
mTransmitDeadlineClock = time;
|
|
|
|
mLastClockUpdateTime = time;
|
|
|
|
mLatencyUpdateTime = time;
|
|
|
|
|
|
|
|
if (!mRadioInterface->start()) {
|
|
|
|
LOG(ALERT) << "Device failed to start";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Device is running - launch I/O threads */
|
|
|
|
mRxLowerLoopThread = new Thread(32768);
|
|
|
|
mTxLowerLoopThread = new Thread(32768);
|
|
|
|
mTxLowerLoopThread->start((void * (*)(void*))
|
|
|
|
TxLowerLoopAdapter,(void*) this);
|
|
|
|
mRxLowerLoopThread->start((void * (*)(void*))
|
|
|
|
RxLowerLoopAdapter,(void*) this);
|
|
|
|
|
|
|
|
/* Launch uplink and downlink burst processing threads */
|
|
|
|
for (size_t i = 0; i < mChans; i++) {
|
|
|
|
TransceiverChannel *chan = new TransceiverChannel(this, i);
|
|
|
|
mRxServiceLoopThreads[i] = new Thread(32768);
|
|
|
|
mRxServiceLoopThreads[i]->start((void * (*)(void*))
|
|
|
|
RxUpperLoopAdapter, (void*) chan);
|
|
|
|
|
|
|
|
chan = new TransceiverChannel(this, i);
|
|
|
|
mTxPriorityQueueServiceLoopThreads[i] = new Thread(32768);
|
|
|
|
mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
|
|
|
|
TxUpperLoopAdapter, (void*) chan);
|
|
|
|
}
|
|
|
|
|
|
|
|
writeClockInterface();
|
|
|
|
mOn = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Stop the transceiver
|
|
|
|
*
|
|
|
|
* Perform stopping by disabling receive streaming and issuing cancellation
|
|
|
|
* requests to running threads. Most threads will timeout and terminate once
|
|
|
|
* device is disabled, but the transmit loop may block waiting on the central
|
|
|
|
* UMTS clock. Explicitly signal the clock to make sure that the transmit loop
|
|
|
|
* makes it to the thread cancellation point.
|
|
|
|
*/
|
|
|
|
void Transceiver::stop()
|
|
|
|
{
|
|
|
|
ScopedLock lock(mLock);
|
|
|
|
|
|
|
|
if (!mOn)
|
|
|
|
return;
|
|
|
|
|
|
|
|
LOG(NOTICE) << "Stopping the transceiver";
|
|
|
|
mTxLowerLoopThread->cancel();
|
|
|
|
mRxLowerLoopThread->cancel();
|
|
|
|
|
|
|
|
for (size_t i = 0; i < mChans; i++) {
|
|
|
|
mRxServiceLoopThreads[i]->cancel();
|
|
|
|
mTxPriorityQueueServiceLoopThreads[i]->cancel();
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG(INFO) << "Stopping the device";
|
|
|
|
mRadioInterface->stop();
|
|
|
|
|
|
|
|
for (size_t i = 0; i < mChans; i++) {
|
|
|
|
mRxServiceLoopThreads[i]->join();
|
|
|
|
mTxPriorityQueueServiceLoopThreads[i]->join();
|
|
|
|
delete mRxServiceLoopThreads[i];
|
|
|
|
delete mTxPriorityQueueServiceLoopThreads[i];
|
|
|
|
|
|
|
|
mTxPriorityQueues[i].clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
mTxLowerLoopThread->join();
|
|
|
|
mRxLowerLoopThread->join();
|
|
|
|
delete mTxLowerLoopThread;
|
|
|
|
delete mRxLowerLoopThread;
|
|
|
|
|
|
|
|
mOn = false;
|
|
|
|
LOG(NOTICE) << "Transceiver stopped";
|
|
|
|
}
|
|
|
|
|
2013-11-14 03:48:11 +00:00
|
|
|
void Transceiver::addRadioVector(size_t chan, BitVector &bits,
|
2013-10-29 22:34:16 +00:00
|
|
|
int RSSI, GSM::Time &wTime)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2013-11-14 03:48:11 +00:00
|
|
|
signalVector *burst;
|
|
|
|
radioVector *radio_burst;
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
if (chan >= mTxPriorityQueues.size()) {
|
|
|
|
LOG(ALERT) << "Invalid channel " << chan;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-14 20:28:23 +00:00
|
|
|
if (wTime.TN() > 7) {
|
|
|
|
LOG(ALERT) << "Received burst with invalid slot " << wTime.TN();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-11-14 03:48:11 +00:00
|
|
|
burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
|
|
|
|
scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
|
|
|
|
|
|
|
|
radio_burst = new radioVector(wTime, burst);
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2013-11-14 03:48:11 +00:00
|
|
|
mTxPriorityQueues[chan].write(radio_burst);
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2014-01-25 07:34:03 +00:00
|
|
|
void Transceiver::updateFillerTable(size_t chan, radioVector *burst)
|
|
|
|
{
|
|
|
|
int TN, modFN;
|
|
|
|
TransceiverState *state = &mStates[chan];
|
|
|
|
|
|
|
|
TN = burst->getTime().TN();
|
|
|
|
modFN = burst->getTime().FN() % state->fillerModulus[TN];
|
|
|
|
|
|
|
|
delete state->fillerTable[modFN][TN];
|
|
|
|
state->fillerTable[modFN][TN] = burst->getVector();
|
|
|
|
burst->setVector(NULL);
|
|
|
|
}
|
|
|
|
|
2011-10-12 07:44:40 +00:00
|
|
|
void Transceiver::pushRadioVector(GSM::Time &nowTime)
|
|
|
|
{
|
2013-10-29 22:34:16 +00:00
|
|
|
int TN, modFN;
|
|
|
|
radioVector *burst;
|
|
|
|
TransceiverState *state;
|
|
|
|
std::vector<signalVector *> bursts(mChans);
|
|
|
|
std::vector<bool> zeros(mChans);
|
2014-01-25 07:34:03 +00:00
|
|
|
std::vector<bool> filler(mChans, true);
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
for (size_t i = 0; i < mChans; i ++) {
|
|
|
|
state = &mStates[i];
|
|
|
|
|
|
|
|
while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
|
|
|
|
LOG(NOTICE) << "dumping STALE burst in TRX->USRP interface";
|
2014-01-25 07:34:03 +00:00
|
|
|
if (state->mRetrans)
|
|
|
|
updateFillerTable(i, burst);
|
2013-11-14 03:48:11 +00:00
|
|
|
delete burst;
|
2013-10-29 22:34:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TN = nowTime.TN();
|
|
|
|
modFN = nowTime.FN() % state->fillerModulus[TN];
|
|
|
|
|
|
|
|
bursts[i] = state->fillerTable[modFN][TN];
|
|
|
|
zeros[i] = state->chanType[TN] == NONE;
|
|
|
|
|
|
|
|
if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
|
2013-11-14 03:48:11 +00:00
|
|
|
bursts[i] = burst->getVector();
|
2014-01-25 07:34:03 +00:00
|
|
|
|
|
|
|
if (state->mRetrans) {
|
|
|
|
updateFillerTable(i, burst);
|
|
|
|
} else {
|
|
|
|
burst->setVector(NULL);
|
|
|
|
filler[i] = false;
|
|
|
|
}
|
|
|
|
|
2013-11-14 03:48:11 +00:00
|
|
|
delete burst;
|
2013-10-29 22:34:16 +00:00
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
mRadioInterface->driveTransmitRadio(bursts, zeros);
|
|
|
|
|
2014-01-25 07:34:03 +00:00
|
|
|
for (size_t i = 0; i < mChans; i++) {
|
|
|
|
if (!filler[i])
|
|
|
|
delete bursts[i];
|
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
void Transceiver::setModulus(size_t timeslot, size_t chan)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2013-10-29 22:34:16 +00:00
|
|
|
TransceiverState *state = &mStates[chan];
|
|
|
|
|
|
|
|
switch (state->chanType[timeslot]) {
|
2011-10-12 07:44:40 +00:00
|
|
|
case NONE:
|
|
|
|
case I:
|
|
|
|
case II:
|
|
|
|
case III:
|
|
|
|
case FILL:
|
2013-10-29 22:34:16 +00:00
|
|
|
state->fillerModulus[timeslot] = 26;
|
2011-10-12 07:44:40 +00:00
|
|
|
break;
|
|
|
|
case IV:
|
|
|
|
case VI:
|
|
|
|
case V:
|
2013-10-29 22:34:16 +00:00
|
|
|
state->fillerModulus[timeslot] = 51;
|
2011-10-12 07:44:40 +00:00
|
|
|
break;
|
|
|
|
//case V:
|
|
|
|
case VII:
|
2013-10-29 22:34:16 +00:00
|
|
|
state->fillerModulus[timeslot] = 102;
|
2011-10-12 07:44:40 +00:00
|
|
|
break;
|
2013-06-09 22:38:18 +00:00
|
|
|
case XIII:
|
2013-10-29 22:34:16 +00:00
|
|
|
state->fillerModulus[timeslot] = 52;
|
2013-06-09 22:38:18 +00:00
|
|
|
break;
|
2011-10-12 07:44:40 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
|
|
|
size_t chan)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2013-10-29 22:34:16 +00:00
|
|
|
TransceiverState *state = &mStates[chan];
|
2011-10-12 07:44:40 +00:00
|
|
|
unsigned burstTN = currTime.TN();
|
|
|
|
unsigned burstFN = currTime.FN();
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
switch (state->chanType[burstTN]) {
|
2011-10-12 07:44:40 +00:00
|
|
|
case NONE:
|
|
|
|
return OFF;
|
|
|
|
break;
|
|
|
|
case FILL:
|
|
|
|
return IDLE;
|
|
|
|
break;
|
|
|
|
case I:
|
|
|
|
return TSC;
|
|
|
|
/*if (burstFN % 26 == 25)
|
|
|
|
return IDLE;
|
|
|
|
else
|
|
|
|
return TSC;*/
|
|
|
|
break;
|
|
|
|
case II:
|
2013-03-27 22:00:25 +00:00
|
|
|
return TSC;
|
2011-10-12 07:44:40 +00:00
|
|
|
break;
|
|
|
|
case III:
|
|
|
|
return TSC;
|
|
|
|
break;
|
|
|
|
case IV:
|
|
|
|
case VI:
|
|
|
|
return RACH;
|
|
|
|
break;
|
|
|
|
case V: {
|
|
|
|
int mod51 = burstFN % 51;
|
|
|
|
if ((mod51 <= 36) && (mod51 >= 14))
|
|
|
|
return RACH;
|
|
|
|
else if ((mod51 == 4) || (mod51 == 5))
|
|
|
|
return RACH;
|
|
|
|
else if ((mod51 == 45) || (mod51 == 46))
|
|
|
|
return RACH;
|
|
|
|
else
|
|
|
|
return TSC;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case VII:
|
|
|
|
if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
|
|
|
|
return IDLE;
|
|
|
|
else
|
|
|
|
return TSC;
|
|
|
|
break;
|
2013-06-09 22:38:18 +00:00
|
|
|
case XIII: {
|
|
|
|
int mod52 = burstFN % 52;
|
|
|
|
if ((mod52 == 12) || (mod52 == 38))
|
|
|
|
return RACH;
|
|
|
|
else if ((mod52 == 25) || (mod52 == 51))
|
|
|
|
return IDLE;
|
|
|
|
else
|
|
|
|
return TSC;
|
|
|
|
break;
|
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
case LOOPBACK:
|
|
|
|
if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
|
|
|
|
return IDLE;
|
|
|
|
else
|
|
|
|
return TSC;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return OFF;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-10-18 01:23:34 +00:00
|
|
|
|
2013-11-14 04:14:48 +00:00
|
|
|
/*
|
|
|
|
* Detect RACH synchronization sequence within a burst. No equalization
|
|
|
|
* is used or available on the RACH channel.
|
|
|
|
*/
|
2015-06-04 19:39:41 +00:00
|
|
|
int Transceiver::detectRACH(TransceiverState *state,
|
|
|
|
signalVector &burst,
|
|
|
|
complex &, float &toa)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2013-11-14 04:14:48 +00:00
|
|
|
float threshold = 6.0;
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2015-06-10 00:52:11 +00:00
|
|
|
return detectRACHBurst(burst, threshold, mSPSRx, amp, toa);
|
2013-11-14 04:14:48 +00:00
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2013-11-14 04:14:48 +00:00
|
|
|
/*
|
|
|
|
* Detect normal burst training sequence midamble. Update equalization
|
|
|
|
* state information and channel estimate if necessary. Equalization
|
|
|
|
* is currently disabled.
|
|
|
|
*/
|
2015-06-04 19:39:41 +00:00
|
|
|
int Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
|
|
|
|
complex &, float &toa, GSM::Time &time)
|
2013-11-14 04:14:48 +00:00
|
|
|
{
|
2015-06-04 19:39:41 +00:00
|
|
|
int success;
|
2013-11-14 04:14:48 +00:00
|
|
|
int tn = time.TN();
|
|
|
|
float chanOffset, threshold = 5.0;
|
2013-11-14 20:52:04 +00:00
|
|
|
bool noise, needDFE = false, estimateChan = false;
|
2013-11-14 04:14:48 +00:00
|
|
|
double elapsed = time - state->chanEstimateTime[tn];
|
|
|
|
signalVector *chanResp;
|
|
|
|
|
|
|
|
/* Check equalization update state */
|
2013-11-16 21:14:12 +00:00
|
|
|
if (needDFE && ((elapsed > 50) || (!state->chanResponse[tn]))) {
|
2013-11-14 04:14:48 +00:00
|
|
|
delete state->DFEForward[tn];
|
|
|
|
delete state->DFEFeedback[tn];
|
|
|
|
state->DFEForward[tn] = NULL;
|
|
|
|
state->DFEFeedback[tn] = NULL;
|
|
|
|
|
|
|
|
estimateChan = true;
|
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2013-11-14 04:14:48 +00:00
|
|
|
/* Detect normal burst midambles */
|
2015-06-10 00:52:11 +00:00
|
|
|
success = analyzeTrafficBurst(burst, mTSC, threshold, mSPSRx, amp,
|
|
|
|
toa, mMaxExpectedDelay, estimateChan,
|
2015-06-04 19:39:41 +00:00
|
|
|
&chanResp, &chanOffset);
|
|
|
|
if (success <= 0) {
|
|
|
|
return success;
|
2013-11-14 04:14:48 +00:00
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2013-11-14 20:52:04 +00:00
|
|
|
noise = state->mNoiseLev;
|
|
|
|
state->SNRestimate[tn] = amp.norm2() / (noise * noise + 1.0);
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2013-11-14 04:14:48 +00:00
|
|
|
/* 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;
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
2013-10-18 01:23:34 +00:00
|
|
|
|
2015-06-04 19:39:41 +00:00
|
|
|
return 1;
|
2013-11-14 04:14:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
if (equalize) {
|
|
|
|
scaleVector(burst, complex(1.0, 0.0) / amp);
|
|
|
|
return equalizeBurst(burst,
|
|
|
|
toa - state->chanRespOffset[tn],
|
|
|
|
mSPSRx,
|
|
|
|
*state->DFEForward[tn],
|
|
|
|
*state->DFEFeedback[tn]);
|
|
|
|
}
|
2013-10-18 01:23:34 +00:00
|
|
|
|
2013-11-14 04:14:48 +00:00
|
|
|
return demodulateBurst(burst, mSPSRx, amp, toa);
|
|
|
|
}
|
2013-10-18 01:23:34 +00:00
|
|
|
|
2013-11-14 04:14:48 +00:00
|
|
|
/*
|
|
|
|
* Pull bursts from the FIFO and handle according to the slot
|
|
|
|
* and burst correlation type. Equalzation is currently disabled.
|
|
|
|
*/
|
2015-06-09 02:46:38 +00:00
|
|
|
SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
|
2015-06-04 04:14:51 +00:00
|
|
|
double &timingOffset, double &noise,
|
|
|
|
size_t chan)
|
2013-11-14 04:14:48 +00:00
|
|
|
{
|
2015-06-04 19:39:41 +00:00
|
|
|
int success;
|
|
|
|
bool equalize = false;
|
2013-11-14 04:14:48 +00:00
|
|
|
complex amp;
|
|
|
|
float toa, pow, max = -1.0, avg = 0.0;
|
2013-11-14 04:38:09 +00:00
|
|
|
int max_i = -1;
|
2013-11-14 04:14:48 +00:00
|
|
|
signalVector *burst;
|
2013-11-14 20:31:24 +00:00
|
|
|
SoftVector *bits = NULL;
|
2013-11-14 20:52:04 +00:00
|
|
|
TransceiverState *state = &mStates[chan];
|
2015-06-09 02:46:38 +00:00
|
|
|
isRssiValid = false;
|
2013-11-14 04:14:48 +00:00
|
|
|
|
|
|
|
/* Blocking FIFO read */
|
|
|
|
radioVector *radio_burst = mReceiveFIFO[chan]->read();
|
|
|
|
if (!radio_burst)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Set time and determine correlation type */
|
|
|
|
GSM::Time time = radio_burst->getTime();
|
|
|
|
CorrType type = expectedCorrType(time, chan);
|
|
|
|
|
2015-06-09 02:46:38 +00:00
|
|
|
/* No processing if the timeslot is off.
|
|
|
|
* Not even power level or noise calculation. */
|
|
|
|
if (type == OFF) {
|
2013-11-14 04:14:48 +00:00
|
|
|
delete radio_burst;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-11-14 04:38:09 +00:00
|
|
|
/* Select the diversity channel with highest energy */
|
|
|
|
for (size_t i = 0; i < radio_burst->chans(); i++) {
|
|
|
|
energyDetect(*radio_burst->getVector(i), 20 * mSPSRx, 0.0, &pow);
|
|
|
|
if (pow > max) {
|
|
|
|
max = pow;
|
|
|
|
max_i = i;
|
|
|
|
}
|
|
|
|
avg += pow;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (max_i < 0) {
|
|
|
|
LOG(ALERT) << "Received empty burst";
|
|
|
|
delete radio_burst;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-11-14 04:14:48 +00:00
|
|
|
/* Average noise on diversity paths and update global levels */
|
2013-11-14 04:38:09 +00:00
|
|
|
burst = radio_burst->getVector(max_i);
|
2013-11-14 20:31:24 +00:00
|
|
|
avg = sqrt(avg / radio_burst->chans());
|
2015-06-09 02:46:38 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
2013-10-18 01:23:34 +00:00
|
|
|
|
2013-11-14 04:14:48 +00:00
|
|
|
/* Detect normal or RACH bursts */
|
|
|
|
if (type == TSC)
|
2013-11-14 20:52:04 +00:00
|
|
|
success = detectTSC(state, *burst, amp, toa, time);
|
2013-11-14 04:14:48 +00:00
|
|
|
else
|
2013-11-14 20:52:04 +00:00
|
|
|
success = detectRACH(state, *burst, amp, toa);
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2015-06-09 02:46:38 +00:00
|
|
|
/* Alert an error and exit */
|
2015-05-18 20:57:54 +00:00
|
|
|
if (success <= 0) {
|
2015-06-09 02:46:38 +00:00
|
|
|
if (success == -SIGERR_CLIP) {
|
2015-06-04 19:39:41 +00:00
|
|
|
LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
|
2015-06-09 02:46:38 +00:00
|
|
|
} else if (success != SIGERR_NONE) {
|
2015-06-04 19:39:41 +00:00
|
|
|
LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
|
2015-05-18 20:57:54 +00:00
|
|
|
}
|
|
|
|
|
2013-11-14 04:14:48 +00:00
|
|
|
delete radio_burst;
|
|
|
|
return NULL;
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2015-06-09 02:46:38 +00:00
|
|
|
timingOffset = toa / mSPSRx;
|
|
|
|
|
2013-11-14 04:14:48 +00:00
|
|
|
/* Demodulate and set output info */
|
|
|
|
if (equalize && (type != TSC))
|
|
|
|
equalize = false;
|
|
|
|
|
2015-06-07 05:10:11 +00:00
|
|
|
bits = demodulate(state, *burst, amp, toa, time.TN(), equalize);
|
2013-11-14 20:31:24 +00:00
|
|
|
|
2013-11-14 04:14:48 +00:00
|
|
|
delete radio_burst;
|
|
|
|
return bits;
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Transceiver::reset()
|
|
|
|
{
|
2013-10-29 22:34:16 +00:00
|
|
|
for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
|
|
|
|
mTxPriorityQueues[i].clear();
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
void Transceiver::driveControl(size_t chan)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
|
|
|
int MAX_PACKET_LENGTH = 100;
|
|
|
|
|
|
|
|
// check control socket
|
|
|
|
char buffer[MAX_PACKET_LENGTH];
|
|
|
|
int msgLen = -1;
|
|
|
|
buffer[0] = '\0';
|
2013-10-29 19:17:34 +00:00
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
msgLen = mCtrlSockets[chan]->read(buffer);
|
2011-10-12 07:44:40 +00:00
|
|
|
|
|
|
|
if (msgLen < 1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
char cmdcheck[4];
|
|
|
|
char command[MAX_PACKET_LENGTH];
|
|
|
|
char response[MAX_PACKET_LENGTH];
|
|
|
|
|
|
|
|
sscanf(buffer,"%3s %s",cmdcheck,command);
|
2013-10-29 22:34:16 +00:00
|
|
|
|
|
|
|
if (!chan)
|
|
|
|
writeClockInterface();
|
2011-10-12 07:44:40 +00:00
|
|
|
|
|
|
|
if (strcmp(cmdcheck,"CMD")!=0) {
|
|
|
|
LOG(WARNING) << "bogus message on control interface";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
LOG(INFO) << "command is " << buffer;
|
|
|
|
|
|
|
|
if (strcmp(command,"POWEROFF")==0) {
|
2014-11-26 00:06:32 +00:00
|
|
|
stop();
|
|
|
|
sprintf(response,"RSP POWEROFF 0");
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
else if (strcmp(command,"POWERON")==0) {
|
2014-11-26 00:06:32 +00:00
|
|
|
if (!start())
|
2011-10-12 07:44:40 +00:00
|
|
|
sprintf(response,"RSP POWERON 1");
|
2014-11-26 00:06:32 +00:00
|
|
|
else
|
2011-10-12 07:44:40 +00:00
|
|
|
sprintf(response,"RSP POWERON 0");
|
|
|
|
}
|
|
|
|
else if (strcmp(command,"SETMAXDLY")==0) {
|
|
|
|
//set expected maximum time-of-arrival
|
|
|
|
int maxDelay;
|
|
|
|
sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay);
|
|
|
|
mMaxExpectedDelay = maxDelay; // 1 GSM symbol is approx. 1 km
|
|
|
|
sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
|
|
|
|
}
|
|
|
|
else if (strcmp(command,"SETRXGAIN")==0) {
|
|
|
|
//set expected maximum time-of-arrival
|
|
|
|
int newGain;
|
|
|
|
sscanf(buffer,"%3s %s %d",cmdcheck,command,&newGain);
|
2013-10-29 22:34:16 +00:00
|
|
|
newGain = mRadioInterface->setRxGain(newGain, chan);
|
2011-10-12 07:44:40 +00:00
|
|
|
sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
|
|
|
|
}
|
|
|
|
else if (strcmp(command,"NOISELEV")==0) {
|
|
|
|
if (mOn) {
|
2013-11-14 20:52:04 +00:00
|
|
|
float lev = mStates[chan].mNoiseLev;
|
2011-10-12 07:44:40 +00:00
|
|
|
sprintf(response,"RSP NOISELEV 0 %d",
|
2013-11-14 20:52:04 +00:00
|
|
|
(int) round(20.0 * log10(rxFullScale / lev)));
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
sprintf(response,"RSP NOISELEV 1 0");
|
|
|
|
}
|
2013-11-15 19:15:47 +00:00
|
|
|
}
|
|
|
|
else if (!strcmp(command, "SETPOWER")) {
|
2014-11-25 23:46:56 +00:00
|
|
|
int power;
|
|
|
|
sscanf(buffer, "%3s %s %d", cmdcheck, command, &power);
|
|
|
|
power = mRadioInterface->setPowerAttenuation(power, chan);
|
|
|
|
mStates[chan].mPower = power;
|
|
|
|
sprintf(response, "RSP SETPOWER 0 %d", power);
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
2013-11-15 19:15:47 +00:00
|
|
|
else if (!strcmp(command,"ADJPOWER")) {
|
2014-11-25 23:46:56 +00:00
|
|
|
int power, step;
|
|
|
|
sscanf(buffer, "%3s %s %d", cmdcheck, command, &step);
|
|
|
|
power = mStates[chan].mPower + step;
|
|
|
|
power = mRadioInterface->setPowerAttenuation(power, chan);
|
|
|
|
mStates[chan].mPower = power;
|
|
|
|
sprintf(response, "RSP ADJPOWER 0 %d", power);
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
else if (strcmp(command,"RXTUNE")==0) {
|
|
|
|
// tune receiver
|
|
|
|
int freqKhz;
|
|
|
|
sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
|
2013-11-15 21:13:59 +00:00
|
|
|
mRxFreq = freqKhz * 1e3;
|
2013-10-29 22:34:16 +00:00
|
|
|
if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
|
2011-10-12 07:44:40 +00:00
|
|
|
LOG(ALERT) << "RX failed to tune";
|
|
|
|
sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
|
|
|
|
}
|
|
|
|
else if (strcmp(command,"TXTUNE")==0) {
|
|
|
|
// tune txmtr
|
|
|
|
int freqKhz;
|
|
|
|
sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
|
2013-11-15 21:13:59 +00:00
|
|
|
mTxFreq = freqKhz * 1e3;
|
2013-10-29 22:34:16 +00:00
|
|
|
if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
|
2011-10-12 07:44:40 +00:00
|
|
|
LOG(ALERT) << "TX failed to tune";
|
|
|
|
sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
|
|
|
|
}
|
2013-11-15 19:22:53 +00:00
|
|
|
else if (!strcmp(command,"SETTSC")) {
|
2011-10-12 07:44:40 +00:00
|
|
|
// set TSC
|
2013-11-15 21:32:54 +00:00
|
|
|
unsigned TSC;
|
2013-11-15 19:22:53 +00:00
|
|
|
sscanf(buffer, "%3s %s %d", cmdcheck, command, &TSC);
|
2015-05-20 19:02:29 +00:00
|
|
|
if (mOn || (TSC < 0) || (TSC > 7))
|
2013-11-15 19:22:53 +00:00
|
|
|
sprintf(response, "RSP SETTSC 1 %d", TSC);
|
|
|
|
else if (chan && (TSC != mTSC))
|
|
|
|
sprintf(response, "RSP SETTSC 1 %d", TSC);
|
2011-10-12 07:44:40 +00:00
|
|
|
else {
|
|
|
|
mTSC = TSC;
|
2013-10-11 17:49:55 +00:00
|
|
|
generateMidamble(mSPSRx, TSC);
|
2013-08-20 20:10:01 +00:00
|
|
|
sprintf(response,"RSP SETTSC 0 %d", TSC);
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strcmp(command,"SETSLOT")==0) {
|
2015-05-20 19:02:29 +00:00
|
|
|
// set slot type
|
2011-10-12 07:44:40 +00:00
|
|
|
int corrCode;
|
|
|
|
int timeslot;
|
|
|
|
sscanf(buffer,"%3s %s %d %d",cmdcheck,command,×lot,&corrCode);
|
|
|
|
if ((timeslot < 0) || (timeslot > 7)) {
|
|
|
|
LOG(WARNING) << "bogus message on control interface";
|
|
|
|
sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
|
|
|
|
return;
|
|
|
|
}
|
2013-10-29 22:34:16 +00:00
|
|
|
mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
|
|
|
|
setModulus(timeslot, chan);
|
2011-10-12 07:44:40 +00:00
|
|
|
sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
|
|
|
|
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
LOG(WARNING) << "bogus command " << command << " on control interface.";
|
2013-04-07 22:14:08 +00:00
|
|
|
sprintf(response,"RSP ERR 1");
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
mCtrlSockets[chan]->write(response, strlen(response) + 1);
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
bool Transceiver::driveTxPriorityQueue(size_t chan)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
|
|
|
char buffer[gSlotLen+50];
|
|
|
|
|
|
|
|
// check data socket
|
2013-10-29 22:34:16 +00:00
|
|
|
size_t msgLen = mDataSockets[chan]->read(buffer);
|
2011-10-12 07:44:40 +00:00
|
|
|
|
|
|
|
if (msgLen!=gSlotLen+1+4+1) {
|
|
|
|
LOG(ERR) << "badly formatted packet on GSM->TRX interface";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int timeSlot = (int) buffer[0];
|
|
|
|
uint64_t frameNum = 0;
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
|
|
|
|
|
|
|
|
LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
|
|
|
|
|
|
|
|
int RSSI = (int) buffer[5];
|
|
|
|
static BitVector newBurst(gSlotLen);
|
|
|
|
BitVector::iterator itr = newBurst.begin();
|
|
|
|
char *bufferItr = buffer+6;
|
|
|
|
while (itr < newBurst.end())
|
|
|
|
*itr++ = *bufferItr++;
|
|
|
|
|
|
|
|
GSM::Time currTime = GSM::Time(frameNum,timeSlot);
|
2013-10-29 22:34:16 +00:00
|
|
|
|
|
|
|
addRadioVector(chan, newBurst, RSSI, currTime);
|
2011-10-12 07:44:40 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
2013-10-29 22:34:16 +00:00
|
|
|
|
|
|
|
void Transceiver::driveReceiveRadio()
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2015-05-24 23:28:09 +00:00
|
|
|
if (!mRadioInterface->driveReceiveRadio()) {
|
2013-10-29 22:34:16 +00:00
|
|
|
usleep(100000);
|
2015-05-24 23:28:09 +00:00
|
|
|
} else {
|
|
|
|
if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
|
|
|
|
writeClockInterface();
|
|
|
|
}
|
2013-10-29 22:34:16 +00:00
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
void Transceiver::driveReceiveFIFO(size_t chan)
|
|
|
|
{
|
2011-10-12 07:44:40 +00:00
|
|
|
SoftVector *rxBurst = NULL;
|
2015-06-04 03:47:56 +00:00
|
|
|
double RSSI; // in dBFS
|
|
|
|
double dBm; // in dBm
|
|
|
|
double TOA; // in symbols
|
|
|
|
int TOAint; // in 1/256 symbols
|
2015-06-04 04:14:51 +00:00
|
|
|
double noise; // noise level in dBFS
|
2011-10-12 07:44:40 +00:00
|
|
|
GSM::Time burstTime;
|
2015-06-09 02:46:38 +00:00
|
|
|
bool isRssiValid; // are RSSI, noise and burstTime valid
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2015-06-09 02:46:38 +00:00
|
|
|
rxBurst = pullRadioVector(burstTime, RSSI, isRssiValid, TOA, noise, chan);
|
2011-10-12 07:44:40 +00:00
|
|
|
|
|
|
|
if (rxBurst) {
|
2015-06-04 03:47:56 +00:00
|
|
|
dBm = RSSI+rssiOffset;
|
|
|
|
TOAint = (int) (TOA * 256.0 + 0.5); // round to closest integer
|
|
|
|
|
2015-06-04 04:14:51 +00:00
|
|
|
LOG(DEBUG) << std::fixed << std::right
|
|
|
|
<< " 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;
|
|
|
|
|
2011-10-12 07:44:40 +00:00
|
|
|
char burstString[gSlotLen+10];
|
|
|
|
burstString[0] = burstTime.TN();
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
|
|
burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
|
2015-06-04 03:47:56 +00:00
|
|
|
burstString[5] = (int)dBm;
|
|
|
|
burstString[6] = (TOAint >> 8) & 0x0ff;
|
|
|
|
burstString[7] = TOAint & 0x0ff;
|
2011-10-12 07:44:40 +00:00
|
|
|
SoftVector::iterator burstItr = rxBurst->begin();
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < gSlotLen; i++) {
|
|
|
|
burstString[8+i] =(char) round((*burstItr++)*255.0);
|
|
|
|
}
|
|
|
|
burstString[gSlotLen+9] = '\0';
|
|
|
|
delete rxBurst;
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
mDataSockets[chan]->write(burstString,gSlotLen+10);
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
void Transceiver::driveTxFIFO()
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
Features a carefully controlled latency mechanism, to
|
|
|
|
assure that transmit packets arrive at the radio/USRP
|
|
|
|
before they need to be transmitted.
|
|
|
|
|
|
|
|
Deadline clock indicates the burst that needs to be
|
|
|
|
pushed into the FIFO right NOW. If transmit queue does
|
|
|
|
not have a burst, stick in filler data.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
RadioClock *radioClock = (mRadioInterface->getClock());
|
|
|
|
|
|
|
|
if (mOn) {
|
|
|
|
//radioClock->wait(); // wait until clock updates
|
|
|
|
LOG(DEBUG) << "radio clock " << radioClock->get();
|
|
|
|
while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
|
|
|
|
// if underrun, then we're not providing bursts to radio/USRP fast
|
|
|
|
// enough. Need to increase latency by one GSM frame.
|
2013-04-05 19:36:30 +00:00
|
|
|
if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
|
2011-11-26 03:18:55 +00:00
|
|
|
if (mRadioInterface->isUnderrun()) {
|
2012-08-08 00:51:31 +00:00
|
|
|
// only update latency at the defined frame interval
|
|
|
|
if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
|
2011-11-26 03:18:55 +00:00
|
|
|
mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
|
|
|
|
LOG(INFO) << "new latency: " << mTransmitLatency;
|
|
|
|
mLatencyUpdateTime = radioClock->get();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// if underrun hasn't occurred in the last sec (216 frames) drop
|
|
|
|
// transmit latency by a timeslot
|
2012-08-08 00:51:31 +00:00
|
|
|
if (mTransmitLatency > GSM::Time(USB_LATENCY_MIN)) {
|
2011-11-26 03:18:55 +00:00
|
|
|
if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
|
|
|
|
mTransmitLatency.decTN();
|
|
|
|
LOG(INFO) << "reduced latency: " << mTransmitLatency;
|
|
|
|
mLatencyUpdateTime = radioClock->get();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
// time to push burst to transmit FIFO
|
|
|
|
pushRadioVector(mTransmitDeadlineClock);
|
|
|
|
mTransmitDeadlineClock.incTN();
|
|
|
|
}
|
|
|
|
}
|
2013-09-28 22:04:19 +00:00
|
|
|
|
|
|
|
radioClock->wait();
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Transceiver::writeClockInterface()
|
|
|
|
{
|
|
|
|
char command[50];
|
|
|
|
// FIXME -- This should be adaptive.
|
|
|
|
sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
|
|
|
|
|
|
|
|
LOG(INFO) << "ClockInterface: sending " << command;
|
|
|
|
|
2014-11-26 00:06:32 +00:00
|
|
|
mClockSocket.write(command, strlen(command) + 1);
|
2011-10-12 07:44:40 +00:00
|
|
|
|
|
|
|
mLastClockUpdateTime = mTransmitDeadlineClock;
|
|
|
|
|
2013-09-28 22:04:19 +00:00
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
void *RxUpperLoopAdapter(TransceiverChannel *chan)
|
|
|
|
{
|
|
|
|
Transceiver *trx = chan->trx;
|
|
|
|
size_t num = chan->num;
|
|
|
|
|
|
|
|
delete chan;
|
|
|
|
|
2013-11-08 17:50:03 +00:00
|
|
|
trx->setPriority(0.42);
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
while (1) {
|
|
|
|
trx->driveReceiveFIFO(num);
|
|
|
|
pthread_testcancel();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *RxLowerLoopAdapter(Transceiver *transceiver)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2013-11-08 17:50:03 +00:00
|
|
|
transceiver->setPriority(0.45);
|
2011-11-26 03:17:21 +00:00
|
|
|
|
2011-10-12 07:44:40 +00:00
|
|
|
while (1) {
|
2013-10-29 22:34:16 +00:00
|
|
|
transceiver->driveReceiveRadio();
|
2013-09-28 22:04:19 +00:00
|
|
|
pthread_testcancel();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
void *TxLowerLoopAdapter(Transceiver *transceiver)
|
2013-09-28 22:04:19 +00:00
|
|
|
{
|
2013-11-08 17:50:03 +00:00
|
|
|
transceiver->setPriority(0.44);
|
|
|
|
|
2013-09-28 22:04:19 +00:00
|
|
|
while (1) {
|
2013-10-29 22:34:16 +00:00
|
|
|
transceiver->driveTxFIFO();
|
2011-10-12 07:44:40 +00:00
|
|
|
pthread_testcancel();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
void *ControlServiceLoopAdapter(TransceiverChannel *chan)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2013-10-29 22:34:16 +00:00
|
|
|
Transceiver *trx = chan->trx;
|
|
|
|
size_t num = chan->num;
|
|
|
|
|
|
|
|
delete chan;
|
|
|
|
|
2011-10-12 07:44:40 +00:00
|
|
|
while (1) {
|
2013-10-29 22:34:16 +00:00
|
|
|
trx->driveControl(num);
|
2011-10-12 07:44:40 +00:00
|
|
|
pthread_testcancel();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-10-29 22:34:16 +00:00
|
|
|
void *TxUpperLoopAdapter(TransceiverChannel *chan)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
2013-10-29 22:34:16 +00:00
|
|
|
Transceiver *trx = chan->trx;
|
|
|
|
size_t num = chan->num;
|
|
|
|
|
|
|
|
delete chan;
|
|
|
|
|
2013-11-10 02:44:26 +00:00
|
|
|
trx->setPriority(0.40);
|
|
|
|
|
2011-10-12 07:44:40 +00:00
|
|
|
while (1) {
|
2014-11-26 00:06:32 +00:00
|
|
|
trx->driveTxPriorityQueue(num);
|
2011-10-12 07:44:40 +00:00
|
|
|
pthread_testcancel();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|