| /* |
| * 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> |
| |
| extern "C" { |
| #include "convert.h" |
| } |
| |
| bool started = false; |
| |
| 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, |
| bool zero) |
| { |
| if (zero) { |
| memset(retVector, 0, wVector.size() * 2 * sizeof(float)); |
| return wVector.size(); |
| } |
| |
| memcpy(retVector, wVector.begin(), wVector.size() * 2 * sizeof(float)); |
| |
| return wVector.size(); |
| } |
| |
| int RadioInterface::unRadioifyVector(float *floatVector, |
| signalVector& newVector) |
| { |
| signalVector::iterator itr = newVector.begin(); |
| |
| if (newVector.size() > recvCursor) { |
| LOG(ALERT) << "Insufficient number of samples in receive buffer"; |
| return -1; |
| } |
| |
| for (int 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), 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; |
| int num_recv; |
| |
| /* Outer buffer access size is fixed */ |
| num_recv = mRadio->readSamples(convertRecvBuffer, |
| OUTCHUNK, |
| &overrun, |
| readTimestamp, |
| &local_underrun); |
| if (num_recv != OUTCHUNK) { |
| LOG(ALERT) << "Receive error " << num_recv; |
| return; |
| } |
| |
| convert_short_float((float *) (recvBuffer->begin() + recvCursor), |
| convertRecvBuffer, 2 * OUTCHUNK); |
| |
| underrun |= local_underrun; |
| readTimestamp += num_recv; |
| |
| recvCursor += num_recv; |
| } |
| |
| /* Send timestamped chunk to the device with arbitrary size */ |
| void RadioInterface::pushBuffer() |
| { |
| int num_sent; |
| |
| if (sendCursor < INCHUNK) |
| return; |
| |
| convert_float_short(convertSendBuffer, |
| (float *) sendBuffer->begin(), |
| powerScaling, 2 * sendCursor); |
| |
| /* Send the all samples in the send buffer */ |
| num_sent = mRadio->writeSamples(convertSendBuffer, |
| sendCursor, |
| &underrun, |
| writeTimestamp); |
| if (num_sent != sendCursor) { |
| LOG(ALERT) << "Transmit error " << num_sent; |
| } |
| |
| writeTimestamp += num_sent; |
| sendCursor = 0; |
| } |