osmo-trx/Transceiver52M/radioInterface.cpp

376 lines
8.9 KiB
C++

/*
* Copyright 2008, 2009, 2012 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>
#include "RTMD.h"
bool started = false;
/* Device side buffers */
static short *rx_buf[CHAN_MAX];
static short *tx_buf[CHAN_MAX];
/* 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 wChanM,
int wSPS,
int wReceiveOffset,
GSM::Time wStartTime)
: mChanM(wChanM), underrun(false), sendCursor(0), rcvCursor(0), mOn(false),
mRadio(wRadio), receiveOffset(wReceiveOffset), samplesPerSymbol(wSPS),
powerScaling(1.0), loadTest(false)
{
mClock.set(wStartTime);
}
RadioInterface::~RadioInterface(void)
{
if (mOn) {
mRadio->stop();
close();
for (int i = 0; i < mChanM; i++) {
if (rcvBuffer[i] != NULL)
delete rcvBuffer[i];
if (sendBuffer[i] != NULL)
delete sendBuffer[i];
}
}
}
double RadioInterface::fullScaleInputValue(void) {
return mRadio->fullScaleInputValue();
}
double RadioInterface::fullScaleOutputValue(void) {
return mRadio->fullScaleOutputValue();
}
void RadioInterface::setPowerAttenuation(double atten, int chan)
{
double rfGain, digAtten;
rfGain = mRadio->setTxGain(mRadio->maxTxGain() - atten, chan);
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, int offset,
signalVector &newVector)
{
int i;
signalVector::iterator itr = newVector.begin();
for (i = 0; i < newVector.size(); i++) {
*itr++ = Complex<float>(floatVector[offset + 2 * i + 0],
floatVector[offset + 2 * i + 1]);
}
return newVector.size();
}
bool RadioInterface::tuneTx(double freq, int chan)
{
return mRadio->setTxFreq(freq, chan);
}
bool RadioInterface::tuneRx(double freq, int chan)
{
return mRadio->setRxFreq(freq, chan);
}
bool RadioInterface::start()
{
int i;
if (mOn)
return false;
mOn = true;
#ifdef USRP1
mAlignRadioServiceLoopThread = new Thread(32768);
mAlignRadioServiceLoopThread->start((void * (*)(void*))AlignRadioServiceLoopAdapter,
(void*)this);
#endif
writeTimestamp = mRadio->initialWriteTimestamp();
readTimestamp = mRadio->initialReadTimestamp();
for (i = 0; i < mChanM; i++) {
sendBuffer[i] = new float[8*2*INCHUNK];
rcvBuffer[i] = new float[8*2*OUTCHUNK];
}
/* Init I/O specific variables if applicable */
init();
mRadio->start();
LOG(DEBUG) << "Radio started";
mRadio->updateAlignment(writeTimestamp-10000);
mRadio->updateAlignment(writeTimestamp-10000);
return true;
}
bool RadioInterface::stop()
{
if (!mOn)
return false;
mOn = false;
mRadio->stop();
}
#ifdef USRP1
void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
{
while (radioInterface->on()) {
radioInterface->alignRadio();
pthread_testcancel();
}
return NULL;
}
void RadioInterface::alignRadio() {
sleep(60);
mRadio->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000);
}
#endif
void RadioInterface::driveTransmitRadio(signalVector **radioBurst, bool *zeroBurst)
{
int i;
RTMD_SET("drvTxRadio");
if (!mOn) {
RTMD_VAL("drvTxRadio", -1);
RTMD_CLEAR("drvTxRadio");
return;
}
for (i = 0; i < mChanM; i++) {
radioifyVector(*radioBurst[i], sendBuffer[i] + 2 * sendCursor,
powerScaling, zeroBurst[i]);
}
/*
* All bursts should be the same size since all transceivers are
* tied with a single clock in the radio interface.
*/
sendCursor += radioBurst[0]->size();
pushBuffer();
RTMD_CLEAR("drvTxRadio");
}
static inline void shiftRxBuffers(float **buf, int offset, int len, int chanM)
{
for (int i = 0; i < chanM; i++)
memmove(buf[i], buf[i] + offset, sizeof(float) * len);
}
void RadioInterface::loadVectors(unsigned tN, int samplesPerBurst,
int idx, GSM::Time rxClock)
{
int i;
for (i = 0; i < mChanM; i++) {
signalVector rxVector(samplesPerBurst);
unRadioifyVector(rcvBuffer[i], idx * 2, rxVector);
radioVector *rxBurst = new radioVector(rxVector, rxClock);
mReceiveFIFO[i].write(rxBurst);
}
}
void RadioInterface::driveReceiveRadio()
{
RTMD_SET("drvRxRadio");
if (!mOn) {
RTMD_VAL("drvRxRadio", -1);
RTMD_CLEAR("drvRxRadio");
return;
}
if (mReceiveFIFO[0].size() > 8) {
RTMD_VAL("drvRxRadio", 2);
RTMD_CLEAR("drvRxRadio");
return;
}
pullBuffer();
GSM::Time rcvClock = mClock.get();
rcvClock.decTN(receiveOffset);
unsigned tN = rcvClock.TN();
int rcvSz = rcvCursor;
int readSz = 0;
const int symbolsPerSlot = gSlotLen + 8;
int samplesPerBurst = (symbolsPerSlot + (tN % 4 == 0)) * samplesPerSymbol;
// 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 >= samplesPerBurst) {
if (rcvClock.FN() >= 0) {
loadVectors(tN, samplesPerBurst, readSz, rcvClock);
}
mClock.incTN();
rcvClock.incTN();
readSz += samplesPerBurst;
rcvSz -= samplesPerBurst;
tN = rcvClock.TN();
samplesPerBurst = (symbolsPerSlot + (tN % 4 == 0)) * samplesPerSymbol;
}
if (readSz > 0) {
rcvCursor -= readSz;
shiftRxBuffers(rcvBuffer, 2 * readSz, 2 * rcvCursor, mChanM);
}
RTMD_CLEAR("drvRxRadio");
}
double RadioInterface::setRxGain(double dB, int chan)
{
if (mRadio)
return mRadio->setRxGain(dB, chan);
else
return -1;
}
double RadioInterface::getRxGain(int chan)
{
if (mRadio)
return mRadio->getRxGain(chan);
else
return -1;
}
bool RadioInterface::init()
{
for (int i = 0; i < CHAN_MAX; i++) {
rx_buf[i] = new short[2 * OUTCHUNK];
tx_buf[i] = new short[4 * 2 * INCHUNK];
}
}
void RadioInterface::close()
{
for (int i = 0; i < CHAN_MAX; i++) {
delete rx_buf[i];
delete tx_buf[i];
}
}
/* Receive a timestamped chunk from the device */
void RadioInterface::pullBuffer()
{
bool local_underrun;
RTMD_SET("RI-PullBuff");
/* Read samples. Fail if we don't get what we want. */
int num_rd = mRadio->readSamples(rx_buf, mChanM, OUTCHUNK, readTimestamp);
LOG(DEBUG) << "Rx read " << num_rd << " samples from device";
assert(num_rd == OUTCHUNK);
underrun |= local_underrun;
readTimestamp += (TIMESTAMP) num_rd;
for (int i = 0; i < mChanM; i++)
shortToFloat(rcvBuffer[i] + 2 * rcvCursor, rx_buf[i], num_rd);
rcvCursor += num_rd;
RTMD_CLEAR("RI-PullBuff");
}
/* Send timestamped chunk to the device with arbitrary size */
void RadioInterface::pushBuffer()
{
RTMD_SET("RI-PushBuff");
if (sendCursor < INCHUNK) {
RTMD_VAL("RI-PushBuff", -1);
RTMD_CLEAR("RI-PushBuff");
return;
}
for (int i = 0; i < mChanM; i++)
floatToShort(tx_buf[i], sendBuffer[i], sendCursor);
/* Write samples. Fail if we don't get what we want. */
int num_smpls = mRadio->writeSamples(tx_buf, mChanM, sendCursor,
writeTimestamp, &underrun);
assert(num_smpls == sendCursor);
writeTimestamp += (TIMESTAMP) num_smpls;
sendCursor = 0;
RTMD_CLEAR("RI-PushBuff");
}