mirror of
https://github.com/RangeNetworks/openbts.git
synced 2025-11-01 20:33:33 +00:00
Replace the polyphase filter and resampler with a separate implementation using SSE enabled convolution. The USRP2 (including derived devices N200, N210) are the only supported devices that require sample rate conversion, so set the default resampling parameters for the 100 MHz FPGA clock. This changes the previous resampling ratios. 270.833 kHz -> 400 kHz (65 / 96) 270.833 kHz -> 390.625 kHz (52 / 75) The new resampling factor uses a USRP resampling factor of 256 instead of 250. On the device, this allows two halfband filters to be used rather than one. The end result is reduced distortial and aliasing effecits from CIC filter rolloff. B100 and USRP1 will no be supported at 400 ksps with these changes. Signed-off-by: Thomas Tsou <tom@tsou.cc> git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@6733 19bc5d8c-e614-43d4-8b26-e1612bc8e597
329 lines
7.9 KiB
C++
329 lines
7.9 KiB
C++
/*
|
|
* Copyright 2008, 2009 Free Software Foundation, Inc.
|
|
*
|
|
* This software is distributed under the terms of the GNU Affero Public License.
|
|
* See the COPYING file in the main directory for details.
|
|
*
|
|
* This use of this software may be subject to additional restrictions.
|
|
* See the LEGAL file in the main directory for details.
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as published by
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
#include "radioInterface.h"
|
|
#include <Logger.h>
|
|
|
|
bool started = false;
|
|
|
|
/* Device side buffers */
|
|
static short rx_buf[OUTCHUNK * 2 * 2];
|
|
static short tx_buf[INCHUNK * 2 * 2];
|
|
|
|
/* Complex float to short conversion */
|
|
static void floatToShort(short *out, float *in, int num)
|
|
{
|
|
for (int i = 0; i < num; i++) {
|
|
out[2 * i + 0] = (short) in[2 * i + 0];
|
|
out[2 * i + 1] = (short) in[2 * i + 1];
|
|
}
|
|
}
|
|
|
|
/* Complex short to float conversion */
|
|
static void shortToFloat(float *out, short *in, int num)
|
|
{
|
|
for (int i = 0; i < num; i++) {
|
|
out[2 * i + 0] = (float) in[2 * i + 0];
|
|
out[2 * i + 1] = (float) in[2 * i + 1];
|
|
}
|
|
}
|
|
|
|
RadioInterface::RadioInterface(RadioDevice *wRadio,
|
|
int wReceiveOffset,
|
|
int wSPS,
|
|
GSM::Time wStartTime)
|
|
: underrun(false), sendCursor(0), recvCursor(0), mOn(false),
|
|
mRadio(wRadio), receiveOffset(wReceiveOffset),
|
|
sps(wSPS), powerScaling(1.0),
|
|
loadTest(false), sendBuffer(NULL), recvBuffer(NULL),
|
|
convertRecvBuffer(NULL), convertSendBuffer(NULL)
|
|
{
|
|
mClock.set(wStartTime);
|
|
}
|
|
|
|
RadioInterface::~RadioInterface(void)
|
|
{
|
|
close();
|
|
}
|
|
|
|
bool RadioInterface::init()
|
|
{
|
|
close();
|
|
|
|
sendBuffer = new signalVector(OUTCHUNK * 20);
|
|
recvBuffer = new signalVector(INCHUNK * 20);
|
|
|
|
convertSendBuffer = new short[OUTCHUNK * 2 * 20];
|
|
convertRecvBuffer = new short[OUTCHUNK * 2 * 2];
|
|
|
|
sendCursor = 0;
|
|
recvCursor = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
void RadioInterface::close()
|
|
{
|
|
delete sendBuffer;
|
|
delete recvBuffer;
|
|
delete convertSendBuffer;
|
|
delete convertRecvBuffer;
|
|
|
|
sendBuffer = NULL;
|
|
recvBuffer = NULL;
|
|
convertRecvBuffer = NULL;
|
|
convertSendBuffer = NULL;
|
|
}
|
|
|
|
double RadioInterface::fullScaleInputValue(void) {
|
|
return mRadio->fullScaleInputValue();
|
|
}
|
|
|
|
double RadioInterface::fullScaleOutputValue(void) {
|
|
return mRadio->fullScaleOutputValue();
|
|
}
|
|
|
|
|
|
void RadioInterface::setPowerAttenuation(double atten)
|
|
{
|
|
double rfGain, digAtten;
|
|
|
|
rfGain = mRadio->setTxGain(mRadio->maxTxGain() - atten);
|
|
digAtten = atten - mRadio->maxTxGain() + rfGain;
|
|
|
|
if (digAtten < 1.0)
|
|
powerScaling = 1.0;
|
|
else
|
|
powerScaling = 1.0/sqrt(pow(10, (digAtten/10.0)));
|
|
}
|
|
|
|
int RadioInterface::radioifyVector(signalVector &wVector,
|
|
float *retVector,
|
|
float scale,
|
|
bool zero)
|
|
{
|
|
int i;
|
|
signalVector::iterator itr = wVector.begin();
|
|
|
|
if (zero) {
|
|
memset(retVector, 0, wVector.size() * 2 * sizeof(float));
|
|
return wVector.size();
|
|
}
|
|
|
|
for (i = 0; i < wVector.size(); i++) {
|
|
retVector[2 * i + 0] = itr->real() * scale;
|
|
retVector[2 * i + 1] = itr->imag() * scale;
|
|
itr++;
|
|
}
|
|
|
|
return wVector.size();
|
|
}
|
|
|
|
int RadioInterface::unRadioifyVector(float *floatVector,
|
|
signalVector& newVector)
|
|
{
|
|
int i;
|
|
signalVector::iterator itr = newVector.begin();
|
|
|
|
for (i = 0; i < newVector.size(); i++) {
|
|
*itr++ = Complex<float>(floatVector[2 * i + 0],
|
|
floatVector[2 * i + 1]);
|
|
}
|
|
|
|
return newVector.size();
|
|
}
|
|
|
|
bool RadioInterface::tuneTx(double freq)
|
|
{
|
|
return mRadio->setTxFreq(freq);
|
|
}
|
|
|
|
bool RadioInterface::tuneRx(double freq)
|
|
{
|
|
return mRadio->setRxFreq(freq);
|
|
}
|
|
|
|
|
|
void RadioInterface::start()
|
|
{
|
|
LOG(INFO) << "starting radio interface...";
|
|
#ifdef USRP1
|
|
mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter,
|
|
(void*)this);
|
|
#endif
|
|
writeTimestamp = mRadio->initialWriteTimestamp();
|
|
readTimestamp = mRadio->initialReadTimestamp();
|
|
mRadio->start();
|
|
LOG(DEBUG) << "Radio started";
|
|
mRadio->updateAlignment(writeTimestamp-10000);
|
|
mRadio->updateAlignment(writeTimestamp-10000);
|
|
|
|
mOn = true;
|
|
|
|
}
|
|
|
|
#ifdef USRP1
|
|
void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
|
|
{
|
|
while (1) {
|
|
radioInterface->alignRadio();
|
|
pthread_testcancel();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void RadioInterface::alignRadio() {
|
|
sleep(60);
|
|
mRadio->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000);
|
|
}
|
|
#endif
|
|
|
|
void RadioInterface::driveTransmitRadio(signalVector &radioBurst, bool zeroBurst)
|
|
{
|
|
if (!mOn)
|
|
return;
|
|
|
|
radioifyVector(radioBurst,
|
|
(float *) (sendBuffer->begin() + sendCursor),
|
|
powerScaling, zeroBurst);
|
|
|
|
sendCursor += radioBurst.size();
|
|
|
|
pushBuffer();
|
|
}
|
|
|
|
void RadioInterface::driveReceiveRadio() {
|
|
|
|
if (!mOn) return;
|
|
|
|
if (mReceiveFIFO.size() > 8) return;
|
|
|
|
pullBuffer();
|
|
|
|
GSM::Time rcvClock = mClock.get();
|
|
rcvClock.decTN(receiveOffset);
|
|
unsigned tN = rcvClock.TN();
|
|
int rcvSz = recvCursor;
|
|
int readSz = 0;
|
|
const int symbolsPerSlot = gSlotLen + 8;
|
|
|
|
// while there's enough data in receive buffer, form received
|
|
// GSM bursts and pass up to Transceiver
|
|
// Using the 157-156-156-156 symbols per timeslot format.
|
|
while (rcvSz > (symbolsPerSlot + (tN % 4 == 0)) * sps) {
|
|
signalVector rxVector((symbolsPerSlot + (tN % 4 == 0)) * sps);
|
|
unRadioifyVector((float *) (recvBuffer->begin() + readSz), rxVector);
|
|
GSM::Time tmpTime = rcvClock;
|
|
if (rcvClock.FN() >= 0) {
|
|
//LOG(DEBUG) << "FN: " << rcvClock.FN();
|
|
radioVector *rxBurst = NULL;
|
|
if (!loadTest)
|
|
rxBurst = new radioVector(rxVector,tmpTime);
|
|
else {
|
|
if (tN % 4 == 0)
|
|
rxBurst = new radioVector(*finalVec9,tmpTime);
|
|
else
|
|
rxBurst = new radioVector(*finalVec,tmpTime);
|
|
}
|
|
mReceiveFIFO.put(rxBurst);
|
|
}
|
|
mClock.incTN();
|
|
rcvClock.incTN();
|
|
readSz += (symbolsPerSlot+(tN % 4 == 0)) * sps;
|
|
rcvSz -= (symbolsPerSlot+(tN % 4 == 0)) * sps;
|
|
|
|
tN = rcvClock.TN();
|
|
}
|
|
|
|
if (readSz > 0) {
|
|
memmove(recvBuffer->begin(),
|
|
recvBuffer->begin() + readSz,
|
|
(recvCursor - readSz) * 2 * sizeof(float));
|
|
|
|
recvCursor -= readSz;
|
|
}
|
|
}
|
|
|
|
bool RadioInterface::isUnderrun()
|
|
{
|
|
bool retVal = underrun;
|
|
underrun = false;
|
|
|
|
return retVal;
|
|
}
|
|
|
|
double RadioInterface::setRxGain(double dB)
|
|
{
|
|
if (mRadio)
|
|
return mRadio->setRxGain(dB);
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
double RadioInterface::getRxGain()
|
|
{
|
|
if (mRadio)
|
|
return mRadio->getRxGain();
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
/* Receive a timestamped chunk from the device */
|
|
void RadioInterface::pullBuffer()
|
|
{
|
|
bool local_underrun;
|
|
|
|
/* Read samples. Fail if we don't get what we want. */
|
|
int num_rd = mRadio->readSamples(rx_buf, OUTCHUNK, &overrun,
|
|
readTimestamp, &local_underrun);
|
|
|
|
LOG(DEBUG) << "Rx read " << num_rd << " samples from device";
|
|
assert(num_rd == OUTCHUNK);
|
|
|
|
underrun |= local_underrun;
|
|
readTimestamp += (TIMESTAMP) num_rd;
|
|
|
|
shortToFloat((float *) recvBuffer->begin() + recvCursor, rx_buf, num_rd);
|
|
recvCursor += num_rd;
|
|
}
|
|
|
|
/* Send timestamped chunk to the device with arbitrary size */
|
|
void RadioInterface::pushBuffer()
|
|
{
|
|
if (sendCursor < INCHUNK)
|
|
return;
|
|
|
|
floatToShort(tx_buf, (float *) sendBuffer->begin(), sendCursor);
|
|
|
|
/* Write samples. Fail if we don't get what we want. */
|
|
int num_smpls = mRadio->writeSamples(tx_buf,
|
|
sendCursor,
|
|
&underrun,
|
|
writeTimestamp);
|
|
assert(num_smpls == sendCursor);
|
|
|
|
writeTimestamp += (TIMESTAMP) num_smpls;
|
|
sendCursor = 0;
|
|
}
|