transceiver: remove old resampling transceiver
parent
1898c37e43
commit
4fcc2bd352
|
@ -31,7 +31,6 @@ SUBDIRS = \
|
|||
SIP \
|
||||
GSM \
|
||||
SMS \
|
||||
Transceiver \
|
||||
Transceiver52M \
|
||||
TRXManager \
|
||||
Control \
|
||||
|
|
|
@ -1,277 +0,0 @@
|
|||
/**@file templates for Complex classes
|
||||
unlike the built-in complex<> templates, these inline most operations for speed
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
#ifndef COMPLEXCPP_H
|
||||
#define COMPLEXCPP_H
|
||||
|
||||
#include <math.h>
|
||||
#include <ostream>
|
||||
|
||||
|
||||
template<class Real> class Complex {
|
||||
|
||||
public:
|
||||
|
||||
Real r, i;
|
||||
|
||||
/**@name constructors */
|
||||
//@{
|
||||
/**@name from real */
|
||||
//@{
|
||||
Complex(Real real, Real imag) {r=real; i=imag;} // x=complex(a,b)
|
||||
Complex(Real real) {r=real; i=0;} // x=complex(a)
|
||||
//@}
|
||||
/**@name from nothing */
|
||||
//@{
|
||||
Complex() {r=(Real)0; i=(Real)0;} // x=complex()
|
||||
//@}
|
||||
/**@name from other complex */
|
||||
//@{
|
||||
Complex(const Complex<float>& z) {r=z.r; i=z.i;} // x=complex(z)
|
||||
Complex(const Complex<double>& z) {r=z.r; i=z.i;} // x=complex(z)
|
||||
Complex(const Complex<long double>& z) {r=z.r; i=z.i;} // x=complex(z)
|
||||
//@}
|
||||
//@}
|
||||
|
||||
/**@name casting up from basic numeric types */
|
||||
//@{
|
||||
Complex& operator=(char a) { r=(Real)a; i=(Real)0; return *this; }
|
||||
Complex& operator=(int a) { r=(Real)a; i=(Real)0; return *this; }
|
||||
Complex& operator=(long int a) { r=(Real)a; i=(Real)0; return *this; }
|
||||
Complex& operator=(short a) { r=(Real)a; i=(Real)0; return *this; }
|
||||
Complex& operator=(float a) { r=(Real)a; i=(Real)0; return *this; }
|
||||
Complex& operator=(double a) { r=(Real)a; i=(Real)0; return *this; }
|
||||
Complex& operator=(long double a) { r=(Real)a; i=(Real)0; return *this; }
|
||||
//@}
|
||||
|
||||
/**@name arithmetic */
|
||||
//@{
|
||||
/**@ binary operators */
|
||||
//@{
|
||||
Complex operator+(const Complex<Real>& a) const { return Complex<Real>(r+a.r, i+a.i); }
|
||||
Complex operator+(Real a) const { return Complex<Real>(r+a,i); }
|
||||
Complex operator-(const Complex<Real>& a) const { return Complex<Real>(r-a.r, i-a.i); }
|
||||
Complex operator-(Real a) const { return Complex<Real>(r-a,i); }
|
||||
Complex operator*(const Complex<Real>& a) const { return Complex<Real>(r*a.r-i*a.i, r*a.i+i*a.r); }
|
||||
Complex operator*(Real a) const { return Complex<Real>(r*a, i*a); }
|
||||
Complex operator/(const Complex<Real>& a) const { return operator*(a.inv()); }
|
||||
Complex operator/(Real a) const { return Complex<Real>(r/a, i/a); }
|
||||
//@}
|
||||
/*@name component-wise product */
|
||||
//@{
|
||||
Complex operator&(const Complex<Real>& a) const { return Complex<Real>(r*a.r, i*a.i); }
|
||||
//@}
|
||||
/*@name inplace operations */
|
||||
//@{
|
||||
Complex& operator+=(const Complex<Real>&);
|
||||
Complex& operator-=(const Complex<Real>&);
|
||||
Complex& operator*=(const Complex<Real>&);
|
||||
Complex& operator/=(const Complex<Real>&);
|
||||
Complex& operator+=(Real);
|
||||
Complex& operator-=(Real);
|
||||
Complex& operator*=(Real);
|
||||
Complex& operator/=(Real);
|
||||
//@}
|
||||
//@}
|
||||
|
||||
/**@name comparisons */
|
||||
//@{
|
||||
bool operator==(const Complex<Real>& a) const { return ((i==a.i)&&(r==a.r)); }
|
||||
bool operator!=(const Complex<Real>& a) const { return ((i!=a.i)||(r!=a.r)); }
|
||||
bool operator<(const Complex<Real>& a) const { return norm2()<a.norm2(); }
|
||||
bool operator>(const Complex<Real>& a) const { return norm2()>a.norm2(); }
|
||||
//@}
|
||||
|
||||
/// reciprocation
|
||||
Complex inv() const;
|
||||
|
||||
// unary functions -- inlined
|
||||
/**@name unary functions */
|
||||
//@{
|
||||
/**@name inlined */
|
||||
//@{
|
||||
Complex conj() const { return Complex<Real>(r,-i); }
|
||||
Real norm2() const { return i*i+r*r; }
|
||||
Complex flip() const { return Complex<Real>(i,r); }
|
||||
Real real() const { return r;}
|
||||
Real imag() const { return i;}
|
||||
Complex neg() const { return Complex<Real>(-r, -i); }
|
||||
bool isZero() const { return ((r==(Real)0) && (i==(Real)0)); }
|
||||
//@}
|
||||
/**@name not inlined due to outside calls */
|
||||
//@{
|
||||
Real abs() const { return ::sqrt(norm2()); }
|
||||
Real arg() const { return ::atan2(i,r); }
|
||||
float dB() const { return 10.0*log10(norm2()); }
|
||||
Complex exp() const { return expj(i)*(::exp(r)); }
|
||||
Complex unit() const; ///< unit phasor with same angle
|
||||
Complex log() const { return Complex(::log(abs()),arg()); }
|
||||
Complex pow(double n) const { return expj(arg()*n)*(::pow(abs(),n)); }
|
||||
Complex sqrt() const { return pow(0.5); }
|
||||
//@}
|
||||
//@}
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**@name standard Complex manifestations */
|
||||
//@{
|
||||
typedef Complex<float> complex;
|
||||
typedef Complex<double> dcomplex;
|
||||
typedef Complex<short> complex16;
|
||||
typedef Complex<long> complex32;
|
||||
//@}
|
||||
|
||||
|
||||
template<class Real> inline Complex<Real> Complex<Real>::inv() const
|
||||
{
|
||||
Real nVal;
|
||||
|
||||
nVal = norm2();
|
||||
return Complex<Real>(r/nVal, -i/nVal);
|
||||
}
|
||||
|
||||
template<class Real> Complex<Real>& Complex<Real>::operator+=(const Complex<Real>& a)
|
||||
{
|
||||
r += a.r;
|
||||
i += a.i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Real> Complex<Real>& Complex<Real>::operator*=(const Complex<Real>& a)
|
||||
{
|
||||
operator*(a);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Real> Complex<Real>& Complex<Real>::operator-=(const Complex<Real>& a)
|
||||
{
|
||||
r -= a.r;
|
||||
i -= a.i;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Real> Complex<Real>& Complex<Real>::operator/=(const Complex<Real>& a)
|
||||
{
|
||||
operator/(a);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
/* op= style operations with reals */
|
||||
|
||||
template<class Real> Complex<Real>& Complex<Real>::operator+=(Real a)
|
||||
{
|
||||
r += a;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Real> Complex<Real>& Complex<Real>::operator*=(Real a)
|
||||
{
|
||||
r *=a;
|
||||
i *=a;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Real> Complex<Real>& Complex<Real>::operator-=(Real a)
|
||||
{
|
||||
r -= a;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<class Real> Complex<Real>& Complex<Real>::operator/=(Real a)
|
||||
{
|
||||
r /= a;
|
||||
i /= a;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
template<class Real> Complex<Real> Complex<Real>::unit() const
|
||||
{
|
||||
Real absVal = abs();
|
||||
return (Complex<Real>(r/absVal, i/absVal));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**@name complex functions outside of the Complex<> class. */
|
||||
//@{
|
||||
|
||||
/** this allows type-commutative multiplication */
|
||||
template<class Real> Complex<Real> operator*(Real a, const Complex<Real>& z)
|
||||
{
|
||||
return Complex<Real>(z.r*a, z.i*a);
|
||||
}
|
||||
|
||||
|
||||
/** this allows type-commutative addition */
|
||||
template<class Real> Complex<Real> operator+(Real a, const Complex<Real>& z)
|
||||
{
|
||||
return Complex<Real>(z.r+a, z.i);
|
||||
}
|
||||
|
||||
|
||||
/** this allows type-commutative subtraction */
|
||||
template<class Real> Complex<Real> operator-(Real a, const Complex<Real>& z)
|
||||
{
|
||||
return Complex<Real>(z.r-a, z.i);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// e^jphi
|
||||
template<class Real> Complex<Real> expj(Real phi)
|
||||
{
|
||||
return Complex<Real>(cos(phi),sin(phi));
|
||||
}
|
||||
|
||||
/// phasor expression of a complex number
|
||||
template<class Real> Complex<Real> phasor(Real C, Real phi)
|
||||
{
|
||||
return (expj(phi)*C);
|
||||
}
|
||||
|
||||
/// formatted stream output
|
||||
template<class Real> std::ostream& operator<<(std::ostream& os, const Complex<Real>& z)
|
||||
{
|
||||
os << z.r << ' ';
|
||||
//os << z.r << ", ";
|
||||
//if (z.i>=0) { os << "+"; }
|
||||
os << z.i << "j";
|
||||
os << "\n";
|
||||
return os;
|
||||
}
|
||||
|
||||
//@}
|
||||
|
||||
|
||||
#endif
|
|
@ -1,88 +0,0 @@
|
|||
#
|
||||
# Copyright 2008 Free Software Foundation, Inc.
|
||||
#
|
||||
# This software is distributed under the terms of the GNU Affero Public License.
|
||||
# See the COPYING file in the main directory for details.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
if UHD
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(UHD_CFLAGS)
|
||||
else
|
||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) $(USRP_CFLAGS)
|
||||
endif
|
||||
AM_CXXFLAGS = -Wall -O3 -g -pthread
|
||||
|
||||
rev2dir = $(datadir)/usrp/rev2
|
||||
rev4dir = $(datadir)/usrp/rev4
|
||||
|
||||
dist_rev2_DATA = std_inband.rbf
|
||||
dist_rev4_DATA = std_inband.rbf
|
||||
|
||||
EXTRA_DIST = \
|
||||
README \
|
||||
README.Talgorithm
|
||||
|
||||
noinst_LTLIBRARIES = libtransceiver.la
|
||||
|
||||
libtransceiver_la_SOURCES = \
|
||||
radioInterface.cpp \
|
||||
sigProcLib.cpp \
|
||||
Transceiver.cpp
|
||||
|
||||
noinst_PROGRAMS = \
|
||||
USRPping \
|
||||
transceiver
|
||||
|
||||
noinst_HEADERS = \
|
||||
Complex.h \
|
||||
radioInterface.h \
|
||||
rcvLPF_651.h \
|
||||
sendLPF_961.h \
|
||||
sigProcLib.h \
|
||||
Transceiver.h \
|
||||
USRPDevice.h
|
||||
|
||||
USRPping_SOURCES = USRPping.cpp
|
||||
USRPping_LDADD = \
|
||||
libtransceiver.la \
|
||||
$(COMMON_LA)
|
||||
|
||||
transceiver_SOURCES = runTransceiver.cpp
|
||||
transceiver_LDADD = \
|
||||
libtransceiver.la \
|
||||
$(GSM_LA) \
|
||||
$(COMMON_LA)
|
||||
|
||||
if UHD
|
||||
libtransceiver_la_SOURCES += UHDDevice.cpp
|
||||
transceiver_LDADD += $(UHD_LIBS)
|
||||
USRPping_LDADD += $(UHD_LIBS)
|
||||
else
|
||||
libtransceiver_la_SOURCES += USRPDevice.cpp
|
||||
transceiver_LDADD += $(USRP_LIBS)
|
||||
USRPping_LDADD += $(USRP_LIBS)
|
||||
endif
|
||||
|
||||
MOSTLYCLEANFILES +=
|
||||
|
||||
#radioInterface.cpp
|
||||
#ComplexTest.cpp
|
||||
#sigProcLibTest.cpp
|
||||
#sweepGenerator.cpp
|
||||
#testRadio.cpp
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
The Transceiver
|
||||
|
||||
The transceiver consists of three modules:
|
||||
--- transceiver
|
||||
--- radioInterface
|
||||
--- USRPDevice
|
||||
|
||||
The USRPDevice module is basically a driver that reads/writes
|
||||
packets to a USRP with two RFX900 daughterboards, board
|
||||
A is the Tx chain and board B is the Rx chain.
|
||||
|
||||
The radioInterface module is basically an interface b/w the
|
||||
transceiver and the USRP. It operates the basestation clock
|
||||
based upon the sample count of received USRP samples. Packets
|
||||
from the USRP are queued and segmented into GSM bursts that are
|
||||
passed up to the transceiver; bursts from the transceiver are
|
||||
passed down to the USRP. Our current implementation includes
|
||||
a polyphase resampler, since there is no 64e6/N sample rate that
|
||||
nicely matches a multiple of the GSM symbol rate of 1.625e6/6.
|
||||
A better implementation would involve running the USRP off of a
|
||||
13Mhz clock; then the resampler can be discarded altogether.
|
||||
|
||||
The transceiver basically operates "layer 0" of the GSM stack,
|
||||
performing the modulation, detection, and demodulation of GSM
|
||||
bursts. It communicates with the GSM stack via three UDP sockets,
|
||||
one socket for data, one for control messages, and one socket to
|
||||
pass clocking information. The transceiver contains a priority
|
||||
queue to sort to-be-transmitted bursts, and a filler table to fill
|
||||
in timeslots that do not have bursts in the priority queue. The
|
||||
transceiver tries to stay ahead of the basestation clock, adapting
|
||||
its latency when underruns are reported by the radioInterface/USRP.
|
||||
Received bursts (from the radioInterface) pass through a simple
|
||||
energy detector, a RACH or midamble correlator, and a DFE-based demodulator.
|
||||
|
||||
NOTE: There's a SWLOOPBACK #define statement, where the USRP is replaced
|
||||
with a memory buffer. In this mode, data written to the USRP is actually stored
|
||||
in a buffer, and read commands to the USRP simply pull data from this buffer.
|
||||
This was very useful in early testing, and still may be useful in testing basic
|
||||
Transceiver and radioInterface functionality.
|
|
@ -1,15 +0,0 @@
|
|||
Basic model:
|
||||
|
||||
Have channel H = {h_0, h_1, ..., h_{K-1}}.
|
||||
Have received sequence Y = {y_0, ..., y_{K+N}}.
|
||||
Have transmitted sequence X = {x_0, ..., x_{N-1}}.
|
||||
Denote state S_n = {x_n, x_{n-1}, ..., x_{n-L}}.
|
||||
|
||||
Define a bag as an unordered collection with two operations, add and take.
|
||||
We have three bags:
|
||||
S: a bag of survivors.
|
||||
F: a bag of available data structures.
|
||||
|
||||
At time n, start with a non-empty bag S of survivors from time n-1.
|
||||
Take a member out of S, and create all possible branches and their corresponding metrics. If metric ratio is above T, discard branch. Otherwise, check branch against entry in pruning table P. If branch metric is smaller than the existing entry's metric in P, then replace entry with branch. Otherwise, discard branch.
|
||||
Once all possible branches of S have been created and pruned, S should be empty.Empty pruning table back into S, thus P is now empty. Repeat.
|
|
@ -1,814 +0,0 @@
|
|||
/*
|
||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Compilation switches
|
||||
TRANSMIT_LOGGING write every burst on the given slot to a log
|
||||
*/
|
||||
|
||||
|
||||
#include "config.h"
|
||||
#include "Transceiver.h"
|
||||
#include <Logger.h>
|
||||
#include <cstdio>
|
||||
|
||||
|
||||
Transceiver::Transceiver(int wBasePort,
|
||||
const char *TRXAddress,
|
||||
int wSamplesPerSymbol,
|
||||
GSM::Time wTransmitLatency,
|
||||
RadioInterface *wRadioInterface)
|
||||
:mDataSocket(wBasePort+2,TRXAddress,wBasePort+102),
|
||||
mControlSocket(wBasePort+1,TRXAddress,wBasePort+101),
|
||||
mClockSocket(wBasePort,TRXAddress,wBasePort+100)
|
||||
{
|
||||
//GSM::Time startTime(0,0);
|
||||
//GSM::Time startTime(gHyperframe/2 - 4*216*60,0);
|
||||
GSM::Time startTime(random() % gHyperframe,0);
|
||||
|
||||
mSamplesPerSymbol = wSamplesPerSymbol;
|
||||
mRadioInterface = wRadioInterface;
|
||||
mTransmitLatency = wTransmitLatency;
|
||||
mTransmitDeadlineClock = startTime;
|
||||
mLastClockUpdateTime = startTime;
|
||||
mLatencyUpdateTime = startTime;
|
||||
mRadioInterface->getClock()->set(startTime);
|
||||
|
||||
// generate pulse and setup up signal processing library
|
||||
gsmPulse = generateGSMPulse(2,mSamplesPerSymbol);
|
||||
LOG(DEBUG) << "gsmPulse: " << *gsmPulse;
|
||||
sigProcLibSetup(mSamplesPerSymbol);
|
||||
|
||||
txFullScale = mRadioInterface->fullScaleInputValue();
|
||||
rxFullScale = mRadioInterface->fullScaleOutputValue();
|
||||
|
||||
// initialize filler tables with dummy bursts, initialize other per-timeslot variables
|
||||
for (int i = 0; i < 8; i++) {
|
||||
signalVector* modBurst = modulateBurst(gDummyBurst,*gsmPulse,
|
||||
8 + (i % 4 == 0),
|
||||
mSamplesPerSymbol);
|
||||
//if (i!=0) scaleVector(*modBurst,0.0001);
|
||||
fillerModulus[i]=26;
|
||||
for (int j = 0; j < 102; j++) {
|
||||
fillerTable[j][i] = new signalVector(*modBurst);
|
||||
}
|
||||
delete modBurst;
|
||||
mChanType[i] = NONE;
|
||||
channelResponse[i] = NULL;
|
||||
DFEForward[i] = NULL;
|
||||
DFEFeedback[i] = NULL;
|
||||
channelEstimateTime[i] = startTime;
|
||||
}
|
||||
|
||||
mOn = false;
|
||||
mTxFreq = 0.0;
|
||||
mRxFreq = 0.0;
|
||||
mPower = -10;
|
||||
mEnergyThreshold = 250.0; // based on empirical data
|
||||
prevFalseDetectionTime = startTime;
|
||||
}
|
||||
|
||||
Transceiver::~Transceiver()
|
||||
{
|
||||
delete gsmPulse;
|
||||
sigProcLibDestroy();
|
||||
mTransmitPriorityQueue.clear();
|
||||
}
|
||||
|
||||
|
||||
void Transceiver::addRadioVector(BitVector &burst,
|
||||
int RSSI,
|
||||
GSM::Time &wTime)
|
||||
{
|
||||
// modulate and stick into queue
|
||||
signalVector* modBurst = modulateBurst(burst,*gsmPulse,
|
||||
8 + (wTime.TN() % 4 == 0),
|
||||
mSamplesPerSymbol);
|
||||
scaleVector(*modBurst,pow(10,-RSSI/10));
|
||||
radioVector *newVec = new radioVector(*modBurst,wTime);
|
||||
mTransmitPriorityQueue.write(newVec);
|
||||
|
||||
delete modBurst;
|
||||
}
|
||||
|
||||
#ifdef TRANSMIT_LOGGING
|
||||
void Transceiver::unModulateVector(signalVector wVector)
|
||||
{
|
||||
SoftVector *burst = demodulateBurst(wVector,
|
||||
*gsmPulse,
|
||||
mSamplesPerSymbol,
|
||||
1.0,0.0);
|
||||
LOG(DEEPDEBUG) << "LOGGED BURST: " << *burst;
|
||||
|
||||
/*
|
||||
unsigned char burstStr[gSlotLen+1];
|
||||
SoftVector::iterator burstItr = burst->begin();
|
||||
for (int i = 0; i < gSlotLen; i++) {
|
||||
// FIXME: Demod bits are inverted!
|
||||
burstStr[i] = (unsigned char) ((*burstItr++)*255.0);
|
||||
}
|
||||
burstStr[gSlotLen]='\0';
|
||||
LOG(DEEPDEBUG) << "LOGGED BURST: " << burstStr;
|
||||
*/
|
||||
delete burst;
|
||||
}
|
||||
#endif
|
||||
|
||||
void Transceiver::pushRadioVector(GSM::Time &nowTime)
|
||||
{
|
||||
|
||||
// dump stale bursts, if any
|
||||
while ((mTransmitPriorityQueue.size() > 0) &&
|
||||
(mTransmitPriorityQueue.nextTime() < nowTime)) {
|
||||
// Even if the burst is stale, put it in the fillter table.
|
||||
// (It might be an idle pattern.)
|
||||
LOG(NOTICE) << "dumping STALE burst in TRX->USRP interface";
|
||||
const GSM::Time& nextTime = mTransmitPriorityQueue.nextTime();
|
||||
int TN = nextTime.TN();
|
||||
int modFN = nextTime.FN() % fillerModulus[TN];
|
||||
delete fillerTable[modFN][TN];
|
||||
fillerTable[modFN][TN] = mTransmitPriorityQueue.readNoBlock();
|
||||
}
|
||||
|
||||
int TN = nowTime.TN();
|
||||
int modFN = nowTime.FN() % fillerModulus[nowTime.TN()];
|
||||
|
||||
// if queue contains data at the desired timestamp, stick it into FIFO
|
||||
if ((mTransmitPriorityQueue.size() > 0) &&
|
||||
(mTransmitPriorityQueue.nextTime() == nowTime)) {
|
||||
radioVector *next = mTransmitPriorityQueue.readNoBlock();
|
||||
LOG(DEEPDEBUG) << "transmitFIFO: wrote burst " << next << " at time: " << nowTime;
|
||||
delete fillerTable[modFN][TN];
|
||||
fillerTable[modFN][TN] = new signalVector(*(next));
|
||||
mTransmitFIFO->write(next);
|
||||
#ifdef TRANSMIT_LOGGING
|
||||
if (nowTime.TN()==TRANSMIT_LOGGING) {
|
||||
unModulateVector(*(fillerTable[modFN][TN]));
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
// otherwise, pull filler data, and push to radio FIFO
|
||||
radioVector *tmpVec = new radioVector(*fillerTable[modFN][TN],nowTime);
|
||||
mTransmitFIFO->write(tmpVec);
|
||||
#ifdef TRANSMIT_LOGGING
|
||||
if (nowTime.TN()==TRANSMIT_LOGGING)
|
||||
unModulateVector(*fillerTable[modFN][TN]);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void Transceiver::setModulus(int timeslot)
|
||||
{
|
||||
switch (mChanType[timeslot]) {
|
||||
case NONE:
|
||||
case I:
|
||||
case II:
|
||||
case III:
|
||||
fillerModulus[timeslot] = 26;
|
||||
break;
|
||||
case IV:
|
||||
case VI:
|
||||
case V:
|
||||
fillerModulus[timeslot] = 51;
|
||||
break;
|
||||
//case V:
|
||||
case VII:
|
||||
fillerModulus[timeslot] = 102;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime)
|
||||
{
|
||||
|
||||
unsigned burstTN = currTime.TN();
|
||||
unsigned burstFN = currTime.FN();
|
||||
|
||||
switch (mChanType[burstTN]) {
|
||||
case NONE:
|
||||
return OFF;
|
||||
break;
|
||||
case I:
|
||||
return TSC;
|
||||
/*if (burstFN % 26 == 25)
|
||||
return IDLE;
|
||||
else
|
||||
return TSC;*/
|
||||
break;
|
||||
case II:
|
||||
if (burstFN % 2 == 1)
|
||||
return IDLE;
|
||||
else
|
||||
return TSC;
|
||||
break;
|
||||
case III:
|
||||
return TSC;
|
||||
break;
|
||||
case IV:
|
||||
case VI:
|
||||
if ((burstFN % 51) % 10 < 2)
|
||||
return RACH;
|
||||
else
|
||||
return OFF;
|
||||
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 == 12) return IDLE;
|
||||
if (burstFN%51 == 13) return IDLE;
|
||||
if (burstFN%51 == 14) return IDLE;
|
||||
return TSC;
|
||||
break;
|
||||
case LOOPBACK:
|
||||
if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
|
||||
return IDLE;
|
||||
else
|
||||
return TSC;
|
||||
break;
|
||||
default:
|
||||
return OFF;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
|
||||
int &RSSI,
|
||||
int &timingOffset)
|
||||
{
|
||||
radioVector *rxBurst = NULL;
|
||||
rxBurst = mReceiveFIFO->read();
|
||||
if (!rxBurst) return NULL;
|
||||
|
||||
LOG(DEEPDEBUG) << "receiveFIFO: read radio vector at time: " << rxBurst->time() << ", new size: " << mReceiveFIFO->size();
|
||||
|
||||
// receive chain is offset from transmit chain
|
||||
rxBurst->time(rxBurst->time());
|
||||
|
||||
int timeslot = rxBurst->time().TN();
|
||||
|
||||
CorrType corrType = expectedCorrType(rxBurst->time());
|
||||
|
||||
if ((corrType==OFF) || (corrType==IDLE)) {
|
||||
delete rxBurst;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// check to see if received burst has sufficient energy
|
||||
signalVector *vectorBurst = rxBurst;
|
||||
complex amplitude = 0.0;
|
||||
float TOA = 0.0;
|
||||
float avgPwr = 0.0;
|
||||
if (!energyDetect(*vectorBurst,20*mSamplesPerSymbol,mEnergyThreshold,&avgPwr)) {
|
||||
LOG(DEEPDEBUG) << "Estimated Energy: " << sqrt(avgPwr) << ", at time " << rxBurst->time();
|
||||
double framesElapsed = rxBurst->time()-prevFalseDetectionTime;
|
||||
if (framesElapsed > 50) { // if we haven't had any false detections for a while, lower threshold
|
||||
mEnergyThreshold -= 10.0;
|
||||
if (mEnergyThreshold < 0.0)
|
||||
mEnergyThreshold = 0.0;
|
||||
|
||||
prevFalseDetectionTime = rxBurst->time();
|
||||
}
|
||||
delete rxBurst;
|
||||
return NULL;
|
||||
}
|
||||
LOG(DEEPDEBUG) << "Estimated Energy: " << sqrt(avgPwr) << ", at time " << rxBurst->time();
|
||||
|
||||
// run the proper correlator
|
||||
bool success = false;
|
||||
if (corrType==TSC) {
|
||||
LOG(DEEPDEBUG) << "looking for TSC at time: " << rxBurst->time();
|
||||
signalVector *channelResp;
|
||||
double framesElapsed = rxBurst->time()-channelEstimateTime[timeslot];
|
||||
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;
|
||||
}
|
||||
float chanOffset;
|
||||
success = analyzeTrafficBurst(*vectorBurst,
|
||||
mTSC,
|
||||
3.0,
|
||||
mSamplesPerSymbol,
|
||||
&litude,
|
||||
&TOA, //);
|
||||
estimateChannel,
|
||||
&channelResp,
|
||||
&chanOffset);
|
||||
if (success) {
|
||||
LOG(DEBUG) << "FOUND TSC!!!!!! " << amplitude << " " << TOA;
|
||||
mEnergyThreshold -= 1.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]);
|
||||
channelEstimateTime[timeslot] = rxBurst->time();
|
||||
LOG(DEBUG) << "SNR: " << SNRestimate[timeslot] << ", DFE forward: " << *DFEForward[timeslot] << ", DFE backward: " << *DFEFeedback[timeslot];
|
||||
}
|
||||
}
|
||||
else {
|
||||
double framesElapsed = rxBurst->time()-prevFalseDetectionTime;
|
||||
LOG(DEEPDEBUG) << "wTime: " << rxBurst->time() << ", pTime: " << prevFalseDetectionTime << ", fElapsed: " << framesElapsed;
|
||||
mEnergyThreshold += 10.0F*exp(-framesElapsed);
|
||||
prevFalseDetectionTime = rxBurst->time();
|
||||
channelResponse[timeslot] = NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// RACH burst
|
||||
success = detectRACHBurst(*vectorBurst,
|
||||
5.0, // detection threshold
|
||||
mSamplesPerSymbol,
|
||||
&litude,
|
||||
&TOA);
|
||||
if (success) {
|
||||
LOG(DEBUG) << "FOUND RACH!!!!!! " << amplitude << " " << TOA;
|
||||
mEnergyThreshold -= 1.0F;
|
||||
if (mEnergyThreshold < 0.0) mEnergyThreshold = 0.0;
|
||||
channelResponse[timeslot] = NULL;
|
||||
}
|
||||
else {
|
||||
double framesElapsed = rxBurst->time()-prevFalseDetectionTime;
|
||||
mEnergyThreshold += 10.0F*exp(-framesElapsed);
|
||||
prevFalseDetectionTime = rxBurst->time();
|
||||
}
|
||||
}
|
||||
LOG(DEBUG) << "energy Threshold = " << mEnergyThreshold;
|
||||
|
||||
// demodulate burst
|
||||
SoftVector *burst = NULL;
|
||||
if ((rxBurst) && (success)) {
|
||||
if (corrType==RACH) {
|
||||
burst = demodulateBurst(*vectorBurst,
|
||||
*gsmPulse,
|
||||
mSamplesPerSymbol,
|
||||
amplitude,TOA);
|
||||
}
|
||||
else { // TSC
|
||||
scaleVector(*vectorBurst,complex(1.0,0.0)/amplitude);
|
||||
burst = equalizeBurst(*vectorBurst,
|
||||
TOA-chanRespOffset[timeslot],
|
||||
mSamplesPerSymbol,
|
||||
*DFEForward[timeslot],
|
||||
*DFEFeedback[timeslot]);
|
||||
}
|
||||
wTime = rxBurst->time();
|
||||
RSSI = (int) floor(20.0*log10(rxFullScale/amplitude.abs()));
|
||||
LOG(DEBUG) << "RSSI: " << RSSI;
|
||||
timingOffset = (int) round(TOA*256.0/mSamplesPerSymbol);
|
||||
}
|
||||
|
||||
//if (burst) LOG(DEEPDEBUG) << "burst: " << *burst << '\n';
|
||||
|
||||
delete rxBurst;
|
||||
|
||||
return burst;
|
||||
}
|
||||
|
||||
void Transceiver::start()
|
||||
{
|
||||
|
||||
mReceiveFIFOServiceLoopThread.start((void * (*)(void*))ReceiveFIFOServiceLoopAdapter,(void*) this);
|
||||
mTransmitFIFOServiceLoopThread.start((void * (*)(void*))TransmitFIFOServiceLoopAdapter,(void*) this);
|
||||
mControlServiceLoopThread.start((void * (*)(void*))ControlServiceLoopAdapter,(void*) this);
|
||||
mTransmitPriorityQueueServiceLoopThread.start((void * (*)(void*))TransmitPriorityQueueServiceLoopAdapter,(void*) this);
|
||||
|
||||
mLock.unlock();
|
||||
|
||||
writeClockInterface();
|
||||
|
||||
generateRACHSequence(*gsmPulse,mSamplesPerSymbol);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Transceiver::reset()
|
||||
{
|
||||
mLock.lock();
|
||||
mTransmitPriorityQueue.clear();
|
||||
mTransmitFIFO->clear();
|
||||
mReceiveFIFO->clear();
|
||||
mLock.unlock();
|
||||
}
|
||||
|
||||
|
||||
void Transceiver::driveControl()
|
||||
{
|
||||
mLock.lock();
|
||||
|
||||
int MAX_PACKET_LENGTH = 100;
|
||||
|
||||
// check control socket
|
||||
char buffer[MAX_PACKET_LENGTH];
|
||||
int msgLen = -1;
|
||||
buffer[0] = '\0';
|
||||
|
||||
msgLen = mControlSocket.read(buffer);
|
||||
|
||||
if (msgLen < 1) {
|
||||
mLock.unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
char cmdcheck[4];
|
||||
char command[MAX_PACKET_LENGTH];
|
||||
char response[MAX_PACKET_LENGTH];
|
||||
|
||||
sscanf(buffer,"%3s %s",cmdcheck,command);
|
||||
|
||||
writeClockInterface();
|
||||
|
||||
if (strcmp(cmdcheck,"CMD")!=0) {
|
||||
LOG(WARN) << "bogus message on control interface";
|
||||
mLock.unlock();
|
||||
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
|
||||
if (!mTxFreq || !mRxFreq)
|
||||
sprintf(response,"RSP POWERON 1");
|
||||
else {
|
||||
sprintf(response,"RSP POWERON 0");
|
||||
if (!mOn) {
|
||||
mPower = -20;
|
||||
mRadioInterface->start();
|
||||
mOn = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (strcmp(command,"SETRXGAIN")==0) {
|
||||
//set expected maximum time-of-arrival
|
||||
int newGain;
|
||||
sscanf(buffer,"%3s %s %d",cmdcheck,command,&newGain);
|
||||
newGain = mRadioInterface->setRxGain(newGain);
|
||||
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;
|
||||
mRadioInterface->setPowerAttenuation(dbPwr);
|
||||
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);
|
||||
}
|
||||
}
|
||||
#define FREQOFFSET 0//11.2e3
|
||||
else if (strcmp(command,"RXTUNE")==0) {
|
||||
// tune receiver
|
||||
int freqKhz;
|
||||
sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
|
||||
if (mOn)
|
||||
sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
|
||||
else {
|
||||
mRxFreq = freqKhz*1.0e3+FREQOFFSET;
|
||||
if (!mRadioInterface->tuneRx(mRxFreq)) {
|
||||
LOG(ALARM) << "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);
|
||||
if (mOn)
|
||||
sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
|
||||
else {
|
||||
//freqKhz = 890e3;
|
||||
mTxFreq = freqKhz*1.0e3+FREQOFFSET;
|
||||
if (!mRadioInterface->tuneTx(mTxFreq)) {
|
||||
LOG(ALARM) << "TX failed to tune";
|
||||
sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
|
||||
}
|
||||
else
|
||||
sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
|
||||
}
|
||||
}
|
||||
else if (strcmp(command,"SETTSC")==0) {
|
||||
// set TSC
|
||||
int TSC;
|
||||
sscanf(buffer,"%3s %s %d",cmdcheck,command,&TSC);
|
||||
if (mOn)
|
||||
sprintf(response,"RSP SETTSC 1 %d",TSC);
|
||||
else {
|
||||
mTSC = TSC;
|
||||
generateMidamble(*gsmPulse,mSamplesPerSymbol,TSC);
|
||||
sprintf(response,"RSP SETTSC 0 %d",TSC);
|
||||
}
|
||||
}
|
||||
else if (strcmp(command,"SETSLOT")==0) {
|
||||
// set TSC
|
||||
int corrCode;
|
||||
int timeslot;
|
||||
sscanf(buffer,"%3s %s %d %d",cmdcheck,command,×lot,&corrCode);
|
||||
if ((timeslot < 0) || (timeslot > 7)) {
|
||||
LOG(WARN) << "bogus message on control interface";
|
||||
sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
|
||||
mLock.unlock();
|
||||
return;
|
||||
}
|
||||
mChanType[timeslot] = (ChannelCombination) corrCode;
|
||||
setModulus(timeslot);
|
||||
sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
|
||||
|
||||
}
|
||||
else {
|
||||
LOG(WARN) << "bogus command " << command << " on control interface.";
|
||||
}
|
||||
|
||||
mControlSocket.write(response,strlen(response)+1);
|
||||
|
||||
mLock.unlock();
|
||||
}
|
||||
|
||||
bool Transceiver::driveTransmitPriorityQueue()
|
||||
{
|
||||
|
||||
char buffer[gSlotLen+50];
|
||||
|
||||
// check data socket
|
||||
size_t msgLen = mDataSocket.read(buffer);
|
||||
|
||||
if (msgLen!=gSlotLen+1+4+1) {
|
||||
LOG(ERROR) << "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]);
|
||||
|
||||
/*
|
||||
if (GSM::Time(frameNum,timeSlot) > mTransmitDeadlineClock + GSM::Time(51,0)) {
|
||||
// stale burst
|
||||
//LOG(DEBUG) << "FAST! "<< GSM::Time(frameNum,timeSlot);
|
||||
//writeClockInterface();
|
||||
}*/
|
||||
|
||||
/*
|
||||
DAB -- Just let these go through the demod.
|
||||
if (GSM::Time(frameNum,timeSlot) < mTransmitDeadlineClock) {
|
||||
// stale burst from GSM core
|
||||
LOG(NOTICE) << "STALE packet on GSM->TRX interface at time "<< GSM::Time(frameNum,timeSlot);
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
// periodically update GSM core clock
|
||||
if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
|
||||
writeClockInterface();
|
||||
|
||||
|
||||
LOG(DEEPDEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
|
||||
|
||||
int RSSI = (int) buffer[5];
|
||||
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(DEEPDEBUG) "added burst - time: " << currTime << ", RSSI: " << RSSI; // << ", data: " << newBurst;
|
||||
|
||||
return true;
|
||||
|
||||
|
||||
}
|
||||
|
||||
void Transceiver::driveReceiveFIFO()
|
||||
{
|
||||
|
||||
SoftVector *rxBurst = NULL;
|
||||
int RSSI;
|
||||
int TOA; // in 1/256 of a symbol
|
||||
GSM::Time burstTime;
|
||||
|
||||
rxBurst = pullRadioVector(burstTime,RSSI,TOA);
|
||||
|
||||
if (rxBurst) {
|
||||
|
||||
/*LOG(DEEPDEBUG) << ("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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Transceiver::driveTransmitFIFO()
|
||||
{
|
||||
|
||||
/**
|
||||
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
|
||||
while (radioClock->get() + mTransmitLatency >
|
||||
mTransmitDeadlineClock) {
|
||||
#ifndef USE_UHD
|
||||
// if underrun, then we're not providing bursts to radio/USRP fast
|
||||
// enough. Need to increase latency by one GSM frame.
|
||||
if (mRadioInterface->isUnderrun()) {
|
||||
// only do latency update every 10 frames, so we don't over update
|
||||
if (radioClock->get() > mLatencyUpdateTime + GSM::Time(10,0)) {
|
||||
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
|
||||
if (mTransmitLatency > GSM::Time(1,1)) {
|
||||
if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
|
||||
mTransmitLatency.decTN();
|
||||
LOG(INFO) << "reduced latency: " << mTransmitLatency;
|
||||
mLatencyUpdateTime = radioClock->get();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// time to push burst to transmit FIFO
|
||||
pushRadioVector(mTransmitDeadlineClock);
|
||||
mTransmitDeadlineClock.incTN();
|
||||
}
|
||||
|
||||
}
|
||||
// FIXME -- This should not be a hard spin.
|
||||
// But any delay here causes us to throw omni_thread_fatal.
|
||||
//else radioClock->wait();
|
||||
}
|
||||
|
||||
|
||||
|
||||
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;
|
||||
|
||||
mClockSocket.write(command,strlen(command)+1);
|
||||
|
||||
mLastClockUpdateTime = mTransmitDeadlineClock;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void *TransmitFIFOServiceLoopAdapter(Transceiver *transceiver)
|
||||
{
|
||||
while (1) {
|
||||
transceiver->driveTransmitFIFO();
|
||||
pthread_testcancel();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *ReceiveFIFOServiceLoopAdapter(Transceiver *transceiver)
|
||||
{
|
||||
while (1) {
|
||||
transceiver->driveReceiveFIFO();
|
||||
pthread_testcancel();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *ControlServiceLoopAdapter(Transceiver *transceiver)
|
||||
{
|
||||
while (1) {
|
||||
transceiver->driveControl();
|
||||
pthread_testcancel();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *TransmitPriorityQueueServiceLoopAdapter(Transceiver *transceiver)
|
||||
{
|
||||
while (1) {
|
||||
bool stale = false;
|
||||
// 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.
|
||||
transceiver->writeClockInterface();
|
||||
}
|
||||
pthread_testcancel();
|
||||
}
|
||||
return NULL;
|
||||
}
|
|
@ -1,214 +0,0 @@
|
|||
/*
|
||||
* Copyright 2008 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
/*
|
||||
Compilation switches
|
||||
TRANSMIT_LOGGING write every burst on the given slot to a log
|
||||
*/
|
||||
|
||||
#include "radioInterface.h"
|
||||
#include "Interthread.h"
|
||||
#include "GSMCommon.h"
|
||||
#include "Sockets.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
|
||||
/** Define this to be the slot number to be logged. */
|
||||
//#define TRANSMIT_LOGGING 1
|
||||
|
||||
/** The Transceiver class, responsible for physical layer of basestation */
|
||||
class Transceiver {
|
||||
|
||||
private:
|
||||
|
||||
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
|
||||
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
|
||||
|
||||
UDPSocket mDataSocket; ///< socket for writing to/reading from GSM core
|
||||
UDPSocket mControlSocket; ///< socket for writing/reading control commands from GSM core
|
||||
UDPSocket mClockSocket; ///< socket for writing clock updates to GSM core
|
||||
|
||||
VectorQueue mTransmitPriorityQueue; ///< priority queue of transmit bursts received from GSM core
|
||||
VectorFIFO* mTransmitFIFO; ///< radioInterface FIFO of transmit bursts
|
||||
VectorFIFO* mReceiveFIFO; ///< radioInterface FIFO of receive bursts
|
||||
|
||||
mutable Mutex mLock; ///< internal lock used for timeslicing threads
|
||||
mutable Mutex mDataSocketLock; ///< lock for handling the data socket
|
||||
|
||||
Thread mTransmitFIFOServiceLoopThread; ///< thread to push bursts into transmit FIFO
|
||||
Thread mReceiveFIFOServiceLoopThread; ///< thread for pulling bursts from receive FIFO
|
||||
Thread mControlServiceLoopThread; ///< thread to process control messages from GSM core
|
||||
Thread mTransmitPriorityQueueServiceLoopThread;///< thread to process transmit bursts from GSM core
|
||||
|
||||
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
|
||||
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
|
||||
|
||||
RadioInterface *mRadioInterface; ///< associated radioInterface object
|
||||
double txFullScale; ///< full scale input to radio
|
||||
double rxFullScale; ///< full scale output to radio
|
||||
|
||||
/** Codes for burst types of received bursts*/
|
||||
typedef enum {
|
||||
OFF, ///< timeslot is off
|
||||
TSC, ///< timeslot should contain a normal burst
|
||||
RACH, ///< timeslot should contain an access burst
|
||||
IDLE ///< timeslot is an idle (or dummy) burst
|
||||
} CorrType;
|
||||
|
||||
|
||||
/** Codes for channel combinations */
|
||||
typedef enum {
|
||||
NONE, ///< Channel is inactive
|
||||
I, ///< TCH/FS
|
||||
II, ///< TCH/HS, idle every other slot
|
||||
III, ///< TCH/HS
|
||||
IV, ///< FCCH+SCH+CCCH+BCCH, uplink RACH
|
||||
V, ///< FCCH+SCH+CCCH+BCCH+SDCCH/4+SACCH/4, uplink RACH+SDCCH/4
|
||||
VI, ///< CCCH+BCCH, uplink RACH
|
||||
VII, ///< SDCCH/8 + SACCH/8
|
||||
LOOPBACK ///< similar go VII, used in loopback testing
|
||||
} ChannelCombination;
|
||||
|
||||
|
||||
/** unmodulate a modulated burst */
|
||||
#ifdef TRANSMIT_LOGGING
|
||||
void unModulateVector(signalVector wVector);
|
||||
#endif
|
||||
|
||||
/** modulate and add a burst to the transmit queue */
|
||||
void addRadioVector(BitVector &burst,
|
||||
int RSSI,
|
||||
GSM::Time &wTime);
|
||||
|
||||
/** Push modulated burst into transmit FIFO corresponding to a particular timestamp */
|
||||
void pushRadioVector(GSM::Time &nowTime);
|
||||
|
||||
/** Pull and demodulate a burst from the receive FIFO */
|
||||
SoftVector *pullRadioVector(GSM::Time &wTime,
|
||||
int &RSSI,
|
||||
int &timingOffset);
|
||||
|
||||
/** Set modulus for specific timeslot */
|
||||
void setModulus(int timeslot);
|
||||
|
||||
/** return the expected burst type for the specified timestamp */
|
||||
CorrType expectedCorrType(GSM::Time currTime);
|
||||
|
||||
/** send messages over the clock socket */
|
||||
void writeClockInterface(void);
|
||||
|
||||
signalVector *gsmPulse; ///< the GSM shaping pulse for modulation
|
||||
|
||||
int mSamplesPerSymbol; ///< number of samples per GSM symbol
|
||||
|
||||
bool mOn; ///< flag to indicate that transceiver is powered on
|
||||
ChannelCombination mChanType[8]; ///< channel types for all timeslots
|
||||
double mTxFreq; ///< the transmit frequency
|
||||
double mRxFreq; ///< the receive frequency
|
||||
int mPower; ///< the transmit power in dB
|
||||
unsigned mTSC; ///< the midamble sequence code
|
||||
double mEnergyThreshold; ///< threshold to determine if received data is potentially a GSM burst
|
||||
GSM::Time prevFalseDetectionTime; ///< last timestamp of a false energy detection
|
||||
int fillerModulus[8]; ///< modulus values of all timeslots, in frames
|
||||
signalVector *fillerTable[102][8]; ///< table of modulated filler waveforms for all timeslots
|
||||
|
||||
GSM::Time channelEstimateTime[8]; ///< last timestamp of each timeslot's channel estimate
|
||||
signalVector *channelResponse[8]; ///< most recent channel estimate of all timeslots
|
||||
float SNRestimate[8]; ///< most recent SNR estimate of all timeslots
|
||||
signalVector *DFEForward[8]; ///< most recent DFE feedforward filter of all timeslots
|
||||
signalVector *DFEFeedback[8]; ///< most recent DFE feedback filter of all timeslots
|
||||
float chanRespOffset[8]; ///< most recent timing offset, e.g. TOA, of all timeslots
|
||||
complex chanRespAmplitude[8]; ///< most recent channel amplitude of all timeslots
|
||||
|
||||
public:
|
||||
|
||||
/** Transceiver constructor
|
||||
@param wBasePort base port number of UDP sockets
|
||||
@param TRXAddress IP address of the TRX manager, as a string
|
||||
@param wSamplesPerSymbol number of samples per GSM symbol
|
||||
@param wTransmitLatency initial setting of transmit latency
|
||||
@param radioInterface associated radioInterface object
|
||||
*/
|
||||
Transceiver(int wBasePort,
|
||||
const char *TRXAddress,
|
||||
int wSamplesPerSymbol,
|
||||
GSM::Time wTransmitLatency,
|
||||
RadioInterface *wRadioInterface);
|
||||
|
||||
/** Destructor */
|
||||
~Transceiver();
|
||||
|
||||
/** start the Transceiver */
|
||||
void start();
|
||||
|
||||
/** attach the radioInterface receive FIFO */
|
||||
void receiveFIFO(VectorFIFO *wFIFO) { mReceiveFIFO = wFIFO;}
|
||||
|
||||
/** attach the radioInterface transmit FIFO */
|
||||
void transmitFIFO(VectorFIFO *wFIFO) { mTransmitFIFO = wFIFO;}
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
/** drive reception and demodulation of GSM bursts */
|
||||
void driveReceiveFIFO();
|
||||
|
||||
/** drive transmission of GSM bursts */
|
||||
void driveTransmitFIFO();
|
||||
|
||||
/** drive handling of control messages from GSM core */
|
||||
void driveControl();
|
||||
|
||||
/**
|
||||
drive modulation and sorting of GSM bursts from GSM core
|
||||
@return true if a burst was transferred successfully
|
||||
*/
|
||||
bool driveTransmitPriorityQueue();
|
||||
|
||||
friend void *TransmitFIFOServiceLoopAdapter(Transceiver *);
|
||||
|
||||
friend void *ReceiveFIFOServiceLoopAdapter(Transceiver *);
|
||||
|
||||
friend void *ControlServiceLoopAdapter(Transceiver *);
|
||||
|
||||
friend void *TransmitPriorityQueueServiceLoopAdapter(Transceiver *);
|
||||
|
||||
void reset();
|
||||
};
|
||||
|
||||
/** transmit FIFO thread loop */
|
||||
void *TransmitFIFOServiceLoopAdapter(Transceiver *);
|
||||
|
||||
/** receive FIFO thread loop */
|
||||
void *ReceiveFIFOServiceLoopAdapter(Transceiver *);
|
||||
|
||||
/** control message handler thread loop */
|
||||
void *ControlServiceLoopAdapter(Transceiver *);
|
||||
|
||||
/** transmit queueing thread loop */
|
||||
void *TransmitPriorityQueueServiceLoopAdapter(Transceiver *);
|
||||
|
|
@ -1,882 +0,0 @@
|
|||
/*
|
||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
|
||||
*
|
||||
* This software is distributed under the terms of the GNU Affero Public License.
|
||||
* See the COPYING file in the main directory for details.
|
||||
*
|
||||
* This use of this software may be subject to additional restrictions.
|
||||
* See the LEGAL file in the main directory for details.
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||