2011-10-12 07:44:40 +00:00
|
|
|
/*
|
2012-02-28 20:41:50 +00:00
|
|
|
* Copyright 2008, 2009, 2010, 2012 Free Software Foundation, Inc.
|
2011-10-12 07:44:40 +00:00
|
|
|
*
|
|
|
|
* This software is distributed under the terms of the GNU Public License.
|
|
|
|
* See the COPYING file in the main directory for details.
|
|
|
|
*
|
|
|
|
* This use of this software may be subject to additional restrictions.
|
|
|
|
* See the LEGAL file in the main directory for details.
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
Compilation switches
|
|
|
|
TRANSMIT_LOGGING write every burst on the given slot to a log
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#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
|
|
|
|
2012-12-06 15:43:55 +00:00
|
|
|
#define INIT_ENERGY_THRSHD 5.0f
|
|
|
|
|
2013-04-26 23:30:55 +00:00
|
|
|
Transceiver::Transceiver(int wBasePort, const char *TRXAddress,
|
|
|
|
DriveLoop *wDriveLoop, RadioInterface *wRadioInterface,
|
2013-09-02 05:24:13 +00:00
|
|
|
int wSPS, int wChannel, bool wPrimary)
|
2011-10-12 07:44:40 +00:00
|
|
|
:mDataSocket(wBasePort+2,TRXAddress,wBasePort+102),
|
|
|
|
mControlSocket(wBasePort+1,TRXAddress,wBasePort+101),
|
2013-04-26 23:30:55 +00:00
|
|
|
mDriveLoop(wDriveLoop), mRadioInterface(wRadioInterface),
|
2013-09-02 05:24:13 +00:00
|
|
|
mSPS(wSPS), mTransmitPriorityQueue(NULL),
|
2013-04-26 23:30:55 +00:00
|
|
|
mChannel(wChannel), mPrimary(wPrimary)
|
2011-10-12 07:44:40 +00:00
|
|
|
{
|
multi-arfcn, trx: allocate threads on heap and fix thread release
The underlying pthread of the Thread object isn't created until
Thread::start(). If the Thread object is contructed, but not
started, then the destructor will fail with a variety of
unpredictable errors such as the following or double free() in
certain cases.
Program received signal SIGSEGV, Segmentation fault.
__GI___libc_free (mem=0x3811abed3e946178) at malloc.c:2972
2972 if (chunk_is_mmapped(p))
If the Thread object is stack allocated, but start() isn't called,
destructor is guaranteed to run and will fail. The previous
approach was to dynamically allocate threads, but not free them,
thus avoiding memory errors, but creating memory leaks.
To get around this limitation, dynamically allocate Thread objects
and initialize with NULL. Then allocate immediately prior to start
such that pthread allocation is tied to the Thread object
constructor. Deallocation can check that the Thread pointer is
valid through NULL or other tracking methods.
Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2012-03-17 20:47:01 +00:00
|
|
|
mFIFOServiceLoopThread = NULL;
|
|
|
|
mControlServiceLoopThread = NULL;
|
|
|
|
mTransmitPriorityQueueServiceLoopThread = NULL;
|
2013-09-05 20:35:57 +00:00
|
|
|
#ifndef TRX_LOAD_TESTING
|
2011-10-12 07:44:40 +00:00
|
|
|
mMaxExpectedDelay = 0;
|
2013-09-05 20:35:57 +00:00
|
|
|
#else
|
|
|
|
mMaxExpectedDelay = 10;
|
|
|
|
#endif
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2012-03-29 22:53:57 +00:00
|
|
|
mTransmitPriorityQueue = mDriveLoop->priorityQueue(mChannel);
|
|
|
|
mReceiveFIFO = mRadioInterface->receiveFIFO(mChannel);
|
|
|
|
|
2011-10-12 07:44:40 +00:00
|
|
|
txFullScale = mRadioInterface->fullScaleInputValue();
|
|
|
|
rxFullScale = mRadioInterface->fullScaleOutputValue();
|
|
|
|
|
2012-02-28 20:41:50 +00:00
|
|
|
// initialize per-timeslot variables
|
2011-10-12 07:44:40 +00:00
|
|
|
for (int i = 0; i < 8; i++) {
|
|
|
|
channelResponse[i] = NULL;
|
|
|
|
DFEForward[i] = NULL;
|
|
|
|
DFEFeedback[i] = NULL;
|
2012-03-20 20:29:43 +00:00
|
|
|
channelEstimateTime[i] = mDriveLoop->getStartTime();
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mOn = false;
|
2012-03-17 19:02:32 +00:00
|
|
|
mRunning = false;
|
2011-10-12 07:44:40 +00:00
|
|
|
mTxFreq = 0.0;
|
|
|
|
mRxFreq = 0.0;
|
2013-04-26 23:30:55 +00:00
|
|
|
mFreqOffset = 0.0;
|
2012-06-06 00:31:28 +00:00
|
|
|
|
2011-10-12 07:44:40 +00:00
|
|
|
mPower = -10;
|
2012-12-06 15:43:55 +00:00
|
|
|
mEnergyThreshold = INIT_ENERGY_THRSHD;
|
2012-03-20 20:29:43 +00:00
|
|
|
prevFalseDetectionTime = mDriveLoop->getStartTime();
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Transceiver::~Transceiver()
|
|
|
|
{
|
2012-02-28 20:41:50 +00:00
|
|
|
mTransmitPriorityQueue->clear();
|
multi-arfcn, trx: allocate threads on heap and fix thread release
The underlying pthread of the Thread object isn't created until
Thread::start(). If the Thread object is contructed, but not
started, then the destructor will fail with a variety of
unpredictable errors such as the following or double free() in
certain cases.
Program received signal SIGSEGV, Segmentation fault.
__GI___libc_free (mem=0x3811abed3e946178) at malloc.c:2972
2972 if (chunk_is_mmapped(p))
If the Thread object is stack allocated, but start() isn't called,
destructor is guaranteed to run and will fail. The previous
approach was to dynamically allocate threads, but not free them,
thus avoiding memory errors, but creating memory leaks.
To get around this limitation, dynamically allocate Thread objects
and initialize with NULL. Then allocate immediately prior to start
such that pthread allocation is tied to the Thread object
constructor. Deallocation can check that the Thread pointer is
valid through NULL or other tracking methods.
Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2012-03-17 20:47:01 +00:00
|
|
|
|
|
|
|
delete mFIFOServiceLoopThread;
|
|
|
|
delete mControlServiceLoopThread;
|
|
|
|
delete mTransmitPriorityQueueServiceLoopThread;
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Transceiver::addRadioVector(BitVector &burst,
|
|
|
|
int RSSI,
|
|
|
|
GSM::Time &wTime)
|
|
|
|
{
|
|
|
|
// modulate and stick into queue
|
2013-09-02 05:51:11 +00:00
|
|
|
signalVector* modBurst = modulateBurst(burst,
|
|
|
|
8 + (wTime.TN() % 4 == 0),
|
|
|
|
mSPS);
|
2011-10-12 07:44:40 +00:00
|
|
|
scaleVector(*modBurst,txFullScale * pow(10,-RSSI/10));
|
|
|
|
radioVector *newVec = new radioVector(*modBurst,wTime);
|
2012-02-28 20:41:50 +00:00
|
|
|
mTransmitPriorityQueue->write(newVec);
|
2011-10-12 07:44:40 +00:00
|
|
|
|
|
|
|
delete modBurst;
|
|
|
|
}
|
|
|
|
|
|
|
|
SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
|
|
|
|
int &RSSI,
|
|
|
|
int &timingOffset)
|
|
|
|
{
|
2013-09-05 09:35:08 +00:00
|
|
|
bool needDFE = false;
|
|
|
|
|
|
|
|
if ((mSPS == 1) && (mMaxExpectedDelay > 1))
|
|
|
|
needDFE = true;
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2012-02-28 20:41:50 +00:00
|
|
|
radioVector *rxBurst = (radioVector *) mReceiveFIFO->read();
|
2011-10-12 07:44:40 +00:00
|
|
|
|
|
|
|
if (!rxBurst) return NULL;
|
|
|
|
|
2011-11-26 03:18:43 +00:00
|
|
|
LOG(DEBUG) << "receiveFIFO: read radio vector at time: " << rxBurst->getTime() << ", new size: " << mReceiveFIFO->size();
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2011-11-26 03:18:43 +00:00
|
|
|
int timeslot = rxBurst->getTime().TN();
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2012-02-28 20:41:50 +00:00
|
|
|
DriveLoop::CorrType corrType = mDriveLoop->expectedCorrType(mChannel, rxBurst->getTime());
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2013-09-05 20:35:57 +00:00
|
|
|
#ifndef TRX_LOAD_TESTING
|
2012-02-28 20:41:50 +00:00
|
|
|
if ((corrType == DriveLoop::OFF) || (corrType == DriveLoop::IDLE)) {
|
2011-10-12 07:44:40 +00:00
|
|
|
delete rxBurst;
|
|
|
|
return NULL;
|
|
|
|
}
|
2013-09-05 20:35:57 +00:00
|
|
|
#endif
|
|
|
|
|
2011-10-12 07:44:40 +00:00
|
|
|
// check to see if received burst has sufficient
|
|
|
|
signalVector *vectorBurst = rxBurst;
|
|
|
|
complex amplitude = 0.0;
|
|
|
|
float TOA = 0.0;
|
|
|
|
float avgPwr = 0.0;
|
2013-09-02 06:39:35 +00:00
|
|
|
#ifdef ENERGY_DETECT
|
2013-09-02 05:24:13 +00:00
|
|
|
if (!energyDetect(*vectorBurst,20*mSPS,mEnergyThreshold,&avgPwr)) {
|
2011-11-26 03:18:43 +00:00
|
|
|
LOG(DEBUG) << "Estimated Energy: " << sqrt(avgPwr) << ", at time " << rxBurst->getTime();
|
|
|
|
double framesElapsed = rxBurst->getTime()-prevFalseDetectionTime;
|
2011-10-12 07:44:40 +00:00
|
|
|
if (framesElapsed > 50) { // if we haven't had any false detections for a while, lower threshold
|
|
|
|
mEnergyThreshold -= 10.0/10.0;
|
2011-11-26 03:17:52 +00:00
|
|
|
if (mEnergyThreshold < 0.0)
|
|
|
|
mEnergyThreshold = 0.0;
|
|
|
|
|
2011-11-26 03:18:43 +00:00
|
|
|
prevFalseDetectionTime = rxBurst->getTime();
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
2013-09-05 20:35:57 +00:00
|
|
|
#ifndef TRX_LOAD_TESTING
|
2011-10-12 07:44:40 +00:00
|
|
|
delete rxBurst;
|
|
|
|
return NULL;
|
2013-09-05 20:35:57 +00:00
|
|
|
#endif
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
2011-11-26 03:18:43 +00:00
|
|
|
LOG(DEBUG) << "Estimated Energy: " << sqrt(avgPwr) << ", at time " << rxBurst->getTime();
|
2013-09-02 06:39:35 +00:00
|
|
|
#endif
|
2011-10-12 07:44:40 +00:00
|
|
|
// run the proper correlator
|
|
|
|
bool success = false;
|
2012-02-28 20:41:50 +00:00
|
|
|
if (corrType == DriveLoop::TSC) {
|
2011-11-26 03:18:43 +00:00
|
|
|
LOG(DEBUG) << "looking for TSC at time: " << rxBurst->getTime();
|
2012-06-06 00:31:28 +00:00
|
|
|
|
2011-10-12 07:44:40 +00:00
|
|
|
signalVector *channelResp;
|
2011-11-26 03:18:43 +00:00
|
|
|
double framesElapsed = rxBurst->getTime()-channelEstimateTime[timeslot];
|
2011-10-12 07:44:40 +00:00
|
|
|
bool estimateChannel = false;
|
|
|
|
if ((framesElapsed > 50) || (channelResponse[timeslot]==NULL)) {
|
|
|
|
if (channelResponse[timeslot]) delete channelResponse[timeslot];
|
|
|
|
if (DFEForward[timeslot]) delete DFEForward[timeslot];
|
|
|
|
if (DFEFeedback[timeslot]) delete DFEFeedback[timeslot];
|
|
|
|
channelResponse[timeslot] = NULL;
|
|
|
|
DFEForward[timeslot] = NULL;
|
|
|
|
DFEFeedback[timeslot] = NULL;
|
|
|
|
estimateChannel = true;
|
|
|
|
}
|
|
|
|
if (!needDFE) estimateChannel = false;
|
|
|
|
float chanOffset;
|
|
|
|
success = analyzeTrafficBurst(*vectorBurst,
|
|
|
|
mTSC,
|
|
|
|
3.0,
|
2013-09-02 05:24:13 +00:00
|
|
|
mSPS,
|
2011-10-12 07:44:40 +00:00
|
|
|
&litude,
|
|
|
|
&TOA,
|
|
|
|
mMaxExpectedDelay,
|
|
|
|
estimateChannel,
|
|
|
|
&channelResp,
|
|
|
|
&chanOffset);
|
2013-09-05 20:35:57 +00:00
|
|
|
#ifdef TRX_LOAD_TESTING
|
|
|
|
success = true;
|
|
|
|
#endif
|
2011-10-12 07:44:40 +00:00
|
|
|
if (success) {
|
|
|
|
LOG(DEBUG) << "FOUND TSC!!!!!! " << amplitude << " " << TOA;
|
|
|
|
mEnergyThreshold -= 1.0F/10.0F;
|
|
|
|
if (mEnergyThreshold < 0.0) mEnergyThreshold = 0.0;
|
|
|
|
SNRestimate[timeslot] = amplitude.norm2()/(mEnergyThreshold*mEnergyThreshold+1.0); // this is not highly accurate
|
|
|
|
if (estimateChannel) {
|
|
|
|
LOG(DEBUG) << "estimating channel...";
|
|
|
|
channelResponse[timeslot] = channelResp;
|
|
|
|
chanRespOffset[timeslot] = chanOffset;
|
|
|
|
chanRespAmplitude[timeslot] = amplitude;
|
|
|
|
scaleVector(*channelResp, complex(1.0,0.0)/amplitude);
|
|
|
|
designDFE(*channelResp, SNRestimate[timeslot], 7, &DFEForward[timeslot], &DFEFeedback[timeslot]);
|
2011-11-26 03:18:43 +00:00
|
|
|
channelEstimateTime[timeslot] = rxBurst->getTime();
|
2011-10-12 07:44:40 +00:00
|
|
|
LOG(DEBUG) << "SNR: " << SNRestimate[timeslot] << ", DFE forward: " << *DFEForward[timeslot] << ", DFE backward: " << *DFEFeedback[timeslot];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2011-11-26 03:18:43 +00:00
|
|
|
double framesElapsed = rxBurst->getTime()-prevFalseDetectionTime;
|
|
|
|
LOG(DEBUG) << "wTime: " << rxBurst->getTime() << ", pTime: " << prevFalseDetectionTime << ", fElapsed: " << framesElapsed;
|
2011-10-12 07:44:40 +00:00
|
|
|
mEnergyThreshold += 10.0F/10.0F*exp(-framesElapsed);
|
2011-11-26 03:18:43 +00:00
|
|
|
prevFalseDetectionTime = rxBurst->getTime();
|
2011-10-12 07:44:40 +00:00
|
|
|
channelResponse[timeslot] = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// RACH burst
|
|
|
|
success = detectRACHBurst(*vectorBurst,
|
|
|
|
5.0, // detection threshold
|
2013-09-02 05:24:13 +00:00
|
|
|
mSPS,
|
2011-10-12 07:44:40 +00:00
|
|
|
&litude,
|
|
|
|
&TOA);
|
2013-09-05 20:35:57 +00:00
|
|
|
#ifdef TRX_LOAD_TESTING
|
|
|
|
success = true;
|
|
|
|
#endif
|
2011-10-12 07:44:40 +00:00
|
|
|
if (success) {
|
|
|
|
LOG(DEBUG) << "FOUND RACH!!!!!! " << amplitude << " " << TOA;
|
|
|
|
mEnergyThreshold -= (1.0F/10.0F);
|
|
|
|
if (mEnergyThreshold < 0.0) mEnergyThreshold = 0.0;
|
|
|
|
channelResponse[timeslot] = NULL;
|
|
|
|
}
|
|
|
|
else {
|
2011-11-26 03:18:43 +00:00
|
|
|
double framesElapsed = rxBurst->getTime()-prevFalseDetectionTime;
|
2011-10-12 07:44:40 +00:00
|
|
|
mEnergyThreshold += (1.0F/10.0F)*exp(-framesElapsed);
|
2011-11-26 03:18:43 +00:00
|
|
|
prevFalseDetectionTime = rxBurst->getTime();
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
LOG(DEBUG) << "energy Threshold = " << mEnergyThreshold;
|
|
|
|
|
|
|
|
// demodulate burst
|
|
|
|
SoftVector *burst = NULL;
|
|
|
|
if ((rxBurst) && (success)) {
|
2012-02-28 20:41:50 +00:00
|
|
|
if ((corrType == DriveLoop::RACH) || (!needDFE)) {
|
2011-10-12 07:44:40 +00:00
|
|
|
burst = demodulateBurst(*vectorBurst,
|
2013-09-02 05:24:13 +00:00
|
|
|
mSPS,
|
2011-10-12 07:44:40 +00:00
|
|
|
amplitude,TOA);
|
|
|
|
}
|
|
|
|
else { // TSC
|
|
|
|
scaleVector(*vectorBurst,complex(1.0,0.0)/amplitude);
|
|
|
|
burst = equalizeBurst(*vectorBurst,
|
|
|
|
TOA-chanRespOffset[timeslot],
|
2013-09-02 05:24:13 +00:00
|
|
|
mSPS,
|
2011-10-12 07:44:40 +00:00
|
|
|
*DFEForward[timeslot],
|
|
|
|
*DFEFeedback[timeslot]);
|
|
|
|
}
|
2011-11-26 03:18:43 +00:00
|
|
|
wTime = rxBurst->getTime();
|
2011-10-12 07:44:40 +00:00
|
|
|
RSSI = (int) floor(20.0*log10(rxFullScale/amplitude.abs()));
|
|
|
|
LOG(DEBUG) << "RSSI: " << RSSI;
|
2013-09-02 05:24:13 +00:00
|
|
|
timingOffset = (int) round(TOA*256.0/mSPS);
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//if (burst) LOG(DEBUG) << "burst: " << *burst << '\n';
|
|
|
|
|
|
|
|
delete rxBurst;
|
|
|
|
|
|
|
|
return burst;
|
|
|
|
}
|
|
|
|
|
2012-02-28 20:41:50 +00:00
|
|
|
void Transceiver::pullFIFO()
|
|
|
|
{
|
|
|
|
SoftVector *rxBurst = NULL;
|
|
|
|
int RSSI;
|
|
|
|
int TOA; // in 1/256 of a symbol
|
|
|
|
GSM::Time burstTime;
|
|
|
|
|
|
|
|
rxBurst = pullRadioVector(burstTime,RSSI,TOA);
|
|
|
|
|
|
|
|
if (rxBurst) {
|
|
|
|
LOG(DEBUG) << "burst parameters: "
|
|
|
|
<< " time: " << burstTime
|
|
|
|
<< " RSSI: " << RSSI
|
|
|
|
<< " TOA: " << TOA
|
|
|
|
<< " bits: " << *rxBurst;
|
|
|
|
|
|
|
|
char burstString[gSlotLen+10];
|
|
|
|
burstString[0] = burstTime.TN();
|
|
|
|
for (int i = 0; i < 4; i++) {
|
|
|
|
burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
|
|
|
|
}
|
|
|
|
|
|
|
|
burstString[5] = RSSI;
|
|
|
|
burstString[6] = (TOA >> 8) & 0x0ff;
|
|
|
|
burstString[7] = TOA & 0x0ff;
|
|
|
|
SoftVector::iterator burstItr = rxBurst->begin();
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < gSlotLen; i++) {
|
|
|
|
burstString[8+i] =(char) round((*burstItr++)*255.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
burstString[gSlotLen+9] = '\0';
|
|
|
|
delete rxBurst;
|
|
|
|
|
|
|
|
mDataSocket.write(burstString,gSlotLen+10);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-12 07:44:40 +00:00
|
|
|
void Transceiver::start()
|
|
|
|
{
|
2012-03-17 19:02:32 +00:00
|
|
|
mRunning = true;
|
multi-arfcn, trx: allocate threads on heap and fix thread release
The underlying pthread of the Thread object isn't created until
Thread::start(). If the Thread object is contructed, but not
started, then the destructor will fail with a variety of
unpredictable errors such as the following or double free() in
certain cases.
Program received signal SIGSEGV, Segmentation fault.
__GI___libc_free (mem=0x3811abed3e946178) at malloc.c:2972
2972 if (chunk_is_mmapped(p))
If the Thread object is stack allocated, but start() isn't called,
destructor is guaranteed to run and will fail. The previous
approach was to dynamically allocate threads, but not free them,
thus avoiding memory errors, but creating memory leaks.
To get around this limitation, dynamically allocate Thread objects
and initialize with NULL. Then allocate immediately prior to start
such that pthread allocation is tied to the Thread object
constructor. Deallocation can check that the Thread pointer is
valid through NULL or other tracking methods.
Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2012-03-17 20:47:01 +00:00
|
|
|
mControlServiceLoopThread = new Thread(32768);
|
2011-10-12 07:44:40 +00:00
|
|
|
mControlServiceLoopThread->start((void * (*)(void*))ControlServiceLoopAdapter,(void*) this);
|
2012-06-06 00:31:28 +00:00
|
|
|
|
|
|
|
if (!mPrimary) {
|
|
|
|
mOn = true;
|
|
|
|
mFIFOServiceLoopThread = new Thread(32768);
|
|
|
|
mFIFOServiceLoopThread->start((void * (*)(void*))FIFOServiceLoopAdapter,(void*) this);
|
|
|
|
|
|
|
|
mTransmitPriorityQueueServiceLoopThread = new Thread(32768);
|
|
|
|
mTransmitPriorityQueueServiceLoopThread->start((void * (*)(void*))TransmitPriorityQueueServiceLoopAdapter,(void*) this);
|
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
2012-03-17 19:02:32 +00:00
|
|
|
void Transceiver::shutdown()
|
|
|
|
{
|
|
|
|
mOn = false;
|
|
|
|
mRunning = false;
|
|
|
|
}
|
|
|
|
|
2011-10-12 07:44:40 +00:00
|
|
|
void Transceiver::reset()
|
|
|
|
{
|
2012-02-28 20:41:50 +00:00
|
|
|
mTransmitPriorityQueue->clear();
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Transceiver::driveControl()
|
|
|
|
{
|
|
|
|
|
|
|
|
int MAX_PACKET_LENGTH = 100;
|
|
|
|
|
|
|
|
// check control socket
|
|
|
|
char buffer[MAX_PACKET_LENGTH];
|
|
|
|
int msgLen = -1;
|
|
|
|
buffer[0] = '\0';
|
|
|
|
|
2012-03-17 19:02:32 +00:00
|
|
|
try {
|
|
|
|
msgLen = mControlSocket.read(buffer);
|
|
|
|
if (msgLen < 1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} catch (...) {
|
|
|
|
/* Ignore the read exception on shutdown */
|
|
|
|
if (!mRunning) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOG(ALERT) << "Caught UHD socket exception";
|
2011-10-12 07:44:40 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
char cmdcheck[4];
|
|
|
|
char command[MAX_PACKET_LENGTH];
|
|
|
|
char response[MAX_PACKET_LENGTH];
|
|
|
|
|
|
|
|
sscanf(buffer,"%3s %s",cmdcheck,command);
|
2012-02-28 20:41:50 +00:00
|
|
|
|
2012-06-06 00:31:28 +00:00
|
|
|
mDriveLoop->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) {
|
|
|
|
// turn off transmitter/demod
|
|
|
|
sprintf(response,"RSP POWEROFF 0");
|
|
|
|
}
|
|
|
|
else if (strcmp(command,"POWERON")==0) {
|
|
|
|
// turn on transmitter/demod
|
2013-04-07 22:11:56 +00:00
|
|
|
if (!mTxFreq || !mRxFreq || (mTSC<0))
|
2011-10-12 07:44:40 +00:00
|
|
|
sprintf(response,"RSP POWERON 1");
|
|
|
|
else {
|
|
|
|
sprintf(response,"RSP POWERON 0");
|
2012-06-06 00:31:28 +00:00
|
|
|
if (mPrimary && !mOn) {
|
2011-10-12 07:44:40 +00:00
|
|
|
// Prepare for thread start
|
|
|
|
mPower = -20;
|
2012-06-06 00:31:28 +00:00
|
|
|
mRadioInterface->start();
|
|
|
|
mDriveLoop->start();
|
|
|
|
mDriveLoop->writeClockInterface();
|
2011-10-12 07:44:40 +00:00
|
|
|
|
|
|
|
// Start radio interface threads.
|
multi-arfcn, trx: allocate threads on heap and fix thread release
The underlying pthread of the Thread object isn't created until
Thread::start(). If the Thread object is contructed, but not
started, then the destructor will fail with a variety of
unpredictable errors such as the following or double free() in
certain cases.
Program received signal SIGSEGV, Segmentation fault.
__GI___libc_free (mem=0x3811abed3e946178) at malloc.c:2972
2972 if (chunk_is_mmapped(p))
If the Thread object is stack allocated, but start() isn't called,
destructor is guaranteed to run and will fail. The previous
approach was to dynamically allocate threads, but not free them,
thus avoiding memory errors, but creating memory leaks.
To get around this limitation, dynamically allocate Thread objects
and initialize with NULL. Then allocate immediately prior to start
such that pthread allocation is tied to the Thread object
constructor. Deallocation can check that the Thread pointer is
valid through NULL or other tracking methods.
Signed-off-by: Thomas Tsou <ttsou@vt.edu>
2012-03-17 20:47:01 +00:00
|
|
|
mOn = true;
|
|
|
|
mFIFOServiceLoopThread = new Thread(32768);
|
2011-10-12 07:44:40 +00:00
|
|
|
mFIFOServiceLoopThread->start((void * (*)(void*))FIFOServiceLoopAdapter,(void*) this);
|
2012-06-06 00:31:28 +00:00
|
|
|
|
|
|
|
mTransmitPriorityQueueServiceLoopThread = new Thread(32768);
|
2011-10-12 07:44:40 +00:00
|
|
|
mTransmitPriorityQueueServiceLoopThread->start((void * (*)(void*))TransmitPriorityQueueServiceLoopAdapter,(void*) this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strcmp(command,"SETMAXDLY")==0) {
|
|
|
|
//set expected maximum time-of-arrival
|
|
|
|
int maxDelay;
|
|
|
|
sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay);
|
2013-09-05 20:35:57 +00:00
|
|
|
#ifndef TRX_LOAD_TESTING
|
2011-10-12 07:44:40 +00:00
|
|
|
mMaxExpectedDelay = maxDelay; // 1 GSM symbol is approx. 1 km
|
2013-09-05 20:35:57 +00:00
|
|
|
#endif
|
2011-10-12 07:44:40 +00:00
|
|
|
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);
|
2012-12-06 15:43:55 +00:00
|
|
|
mEnergyThreshold = INIT_ENERGY_THRSHD;
|
2013-07-13 16:29:08 +00:00
|
|
|
newGain = mRadioInterface->setRxGain(newGain, mChannel);
|
2011-10-12 07:44:40 +00:00
|
|
|
sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
|
|
|
|
}
|
|
|
|
else if (strcmp(command,"NOISELEV")==0) {
|
|
|
|
if (mOn) {
|
|
|
|
sprintf(response,"RSP NOISELEV 0 %d",
|
|
|
|
(int) round(20.0*log10(rxFullScale/mEnergyThreshold)));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
sprintf(response,"RSP NOISELEV 1 0");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strcmp(command,"SETPOWER")==0) {
|
|
|
|
// set output power in dB
|
|
|
|
int dbPwr;
|
|
|
|
sscanf(buffer,"%3s %s %d",cmdcheck,command,&dbPwr);
|
|
|
|
if (!mOn)
|
|
|
|
sprintf(response,"RSP SETPOWER 1 %d",dbPwr);
|
|
|
|
else {
|
|
|
|
mPower = dbPwr;
|
2013-07-13 16:29:08 +00:00
|
|
|
mRadioInterface->setPowerAttenuation(dbPwr, mChannel);
|
2011-10-12 07:44:40 +00:00
|
|
|
sprintf(response,"RSP SETPOWER 0 %d",dbPwr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strcmp(command,"ADJPOWER")==0) {
|
|
|
|
// adjust power in dB steps
|
|
|
|
int dbStep;
|
|
|
|
sscanf(buffer,"%3s %s %d",cmdcheck,command,&dbStep);
|
|
|
|
if (!mOn)
|
|
|
|
sprintf(response,"RSP ADJPOWER 1 %d",mPower);
|
|
|
|
else {
|
|
|
|
mPower += dbStep;
|
|
|
|
sprintf(response,"RSP ADJPOWER 0 %d",mPower);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strcmp(command,"RXTUNE")==0) {
|
|
|
|
// tune receiver
|
|
|
|
int freqKhz;
|
|
|
|
sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
|
2012-06-06 00:31:28 +00:00
|
|
|
mRxFreq = freqKhz * 1.0e3 + mFreqOffset;
|
2013-04-26 23:30:55 +00:00
|
|
|
if (!mRadioInterface->tuneRx(mRxFreq, mChannel)) {
|
2012-06-06 00:31:28 +00:00
|
|
|
LOG(ALERT) << "RX failed to tune";
|
|
|
|
sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
|
2012-02-28 20:41:50 +00:00
|
|
|
} else {
|
|
|
|
sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
|
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
else if (strcmp(command,"TXTUNE")==0) {
|
|
|
|
// tune txmtr
|
|
|
|
int freqKhz;
|
|
|
|
sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
|
|
|
|
//freqKhz = 890e3;
|
2012-06-06 00:31:28 +00:00
|
|
|
mTxFreq = freqKhz * 1.0e3 + mFreqOffset;
|
2013-04-26 23:30:55 +00:00
|
|
|
if (!mRadioInterface->tuneTx(mTxFreq, mChannel)) {
|
2012-06-06 00:31:28 +00:00
|
|
|
LOG(ALERT) << "TX failed to tune";
|
|
|
|
sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
|
2012-02-28 20:41:50 +00:00
|
|
|
} else {
|
2012-06-06 00:31:28 +00:00
|
|
|
sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strcmp(command,"SETTSC")==0) {
|
|
|
|
// set TSC
|
|
|
|
int TSC;
|
|
|
|
sscanf(buffer,"%3s %s %d",cmdcheck,command,&TSC);
|
2013-04-07 22:11:56 +00:00
|
|
|
if (mOn || (TSC<0) || (TSC>7))
|
2011-10-12 07:44:40 +00:00
|
|
|
sprintf(response,"RSP SETTSC 1 %d",TSC);
|
|
|
|
else {
|
|
|
|
mTSC = TSC;
|
2013-09-02 05:51:11 +00:00
|
|
|
generateMidamble(mSPS, TSC);
|
2011-10-12 07:44:40 +00:00
|
|
|
sprintf(response,"RSP SETTSC 0 %d",TSC);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (strcmp(command,"SETSLOT")==0) {
|
2013-04-07 22:11:56 +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);
|
2013-07-08 14:09:56 +00:00
|
|
|
if ((timeslot < 0) || (timeslot > 7)) {
|
2011-10-12 07:44:40 +00:00
|
|
|
LOG(WARNING) << "bogus message on control interface";
|
|
|
|
sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
|
|
|
|
return;
|
|
|
|
}
|
2012-02-28 20:41:50 +00:00
|
|
|
mDriveLoop->setTimeslot(mChannel, timeslot, (DriveLoop::ChannelCombination) corrCode);
|
|
|
|
mDriveLoop->setModulus(mChannel, timeslot);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
mControlSocket.write(response,strlen(response)+1);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Transceiver::driveTransmitPriorityQueue()
|
|
|
|
{
|
|
|
|
char buffer[gSlotLen+50];
|
|
|
|
|
2012-03-17 19:02:32 +00:00
|
|
|
if (!mOn)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
try {
|
|
|
|
size_t msgLen = mDataSocket.read(buffer);
|
|
|
|
if (msgLen!=gSlotLen+1+4+1) {
|
|
|
|
LOG(ERR) << "badly formatted packet on GSM->TRX interface";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} catch (...) {
|
|
|
|
if (!mOn) {
|
|
|
|
/* Shutdown condition. End the thread. */
|
|
|
|
return true;
|
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
|
2012-03-17 19:02:32 +00:00
|
|
|
LOG(ALERT) << "Caught UHD socket exception";
|
2011-10-12 07:44:40 +00:00
|
|
|
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]);
|
|
|
|
|
|
|
|
// periodically update GSM core clock
|
2012-06-06 00:31:28 +00:00
|
|
|
LOG(DEBUG) << "mTransmitDeadlineClock " << mDriveLoop->getDeadlineClock()
|
|
|
|
<< " mLastClockUpdateTime " << mDriveLoop->getLastClockUpdate();
|
|
|
|
if (mDriveLoop->getDeadlineClock() > mDriveLoop->getLastClockUpdate() + GSM::Time(216,0)) {
|
|
|
|
mDriveLoop->writeClockInterface();
|
|
|
|
}
|
2011-10-12 07:44:40 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
addRadioVector(newBurst,RSSI,currTime);
|
|
|
|
|
|
|
|
LOG(DEBUG) "added burst - time: " << currTime << ", RSSI: " << RSSI; // << ", data: " << newBurst;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void *FIFOServiceLoopAdapter(Transceiver *transceiver)
|
|
|
|
{
|
2012-03-17 19:02:32 +00:00
|
|
|
while (transceiver->on()) {
|
2012-02-28 20:41:50 +00:00
|
|
|
transceiver->pullFIFO();
|
2011-10-12 07:44:40 +00:00
|
|
|
pthread_testcancel();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *ControlServiceLoopAdapter(Transceiver *transceiver)
|
|
|
|
{
|
2012-03-17 19:02:32 +00:00
|
|
|
while (transceiver->running()) {
|
2011-10-12 07:44:40 +00:00
|
|
|
transceiver->driveControl();
|
|
|
|
pthread_testcancel();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *TransmitPriorityQueueServiceLoopAdapter(Transceiver *transceiver)
|
|
|
|
{
|
2012-03-17 19:02:32 +00:00
|
|
|
while (transceiver->on()) {
|
2011-10-12 07:44:40 +00:00
|
|
|
bool stale = false;
|
2012-06-06 00:31:28 +00:00
|
|
|
|
2011-10-12 07:44:40 +00:00
|
|
|
// Flush the UDP packets until a successful transfer.
|
|
|
|
while (!transceiver->driveTransmitPriorityQueue()) {
|
|
|
|
stale = true;
|
|
|
|
}
|
|
|
|
if (stale) {
|
|
|
|
// If a packet was stale, remind the GSM stack of the clock.
|
2012-06-06 00:31:28 +00:00
|
|
|
transceiver->getDriveLoop()->writeClockInterface();
|
2011-10-12 07:44:40 +00:00
|
|
|
}
|
|
|
|
pthread_testcancel();
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|