| /* |
| * 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"); |
| } |