diff options
author | Tom Tsou <tom.tsou@ettus.com> | 2016-06-24 14:25:39 -0700 |
---|---|---|
committer | Tom Tsou <tom.tsou@ettus.com> | 2016-07-01 03:14:15 -0700 |
commit | 76764278169d252980853251daeb9f1ba0c246e1 (patch) | |
tree | 0d1631e938ae48d72d0b2b5d5e102116ab2d5b9d /Transceiver52M/radioInterfaceMulti.cpp | |
parent | 35222296fef378977a83a4ee89d8c3ef9bc62a3f (diff) |
mcbts: Add multi-ARFCN radio support
Add new radio interface "radioInterfaceMulti" for multi-carrier
support.
Only USRP B200/B210 devices are supported because of sample
rate requirements (3.2 Msps).
Only 4 SPS operation Tx/RX is supported.
8-PSK is supported.
Other options may be added at a later time
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
Diffstat (limited to 'Transceiver52M/radioInterfaceMulti.cpp')
-rw-r--r-- | Transceiver52M/radioInterfaceMulti.cpp | 387 |
1 files changed, 387 insertions, 0 deletions
diff --git a/Transceiver52M/radioInterfaceMulti.cpp b/Transceiver52M/radioInterfaceMulti.cpp new file mode 100644 index 0000000..ba81fe1 --- /dev/null +++ b/Transceiver52M/radioInterfaceMulti.cpp @@ -0,0 +1,387 @@ +/* + * Multi-carrier radio interface + * + * Copyright (C) 2016 Ettus Research LLC + * + * Author: Tom Tsou <tom.tsou@ettus.com> + * + * 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/>. + * See the COPYING file in the main directory for details. + */ + +#include <radioInterface.h> +#include <Logger.h> + +#include "Resampler.h" + +extern "C" { +#include "convert.h" +} + +/* Resampling parameters for 64 MHz clocking */ +#define RESAMP_INRATE 65 +#define RESAMP_OUTRATE (96 / 2) + +/* Universal resampling parameters */ +#define NUMCHUNKS 24 + +#define MCHANS 4 + +RadioInterfaceMulti::RadioInterfaceMulti(RadioDevice *radio, size_t tx_sps, + size_t rx_sps, size_t chans) + : RadioInterface(radio, tx_sps, rx_sps, chans), + outerSendBuffer(NULL), outerRecvBuffer(NULL), + dnsampler(NULL), upsampler(NULL), channelizer(NULL), synthesis(NULL) +{ +} + +RadioInterfaceMulti::~RadioInterfaceMulti() +{ + close(); +} + +void RadioInterfaceMulti::close() +{ + delete outerSendBuffer; + delete outerRecvBuffer; + delete dnsampler; + delete upsampler; + delete channelizer; + delete synthesis; + + outerSendBuffer = NULL; + outerRecvBuffer = NULL; + dnsampler = NULL; + upsampler = NULL; + channelizer = NULL; + synthesis = NULL; + + mReceiveFIFO.resize(0); + powerScaling.resize(0); + history.resize(0); + active.resize(0); + + RadioInterface::close(); +} + +static int getLogicalChan(size_t pchan, size_t chans) +{ + switch (chans) { + case 1: + if (pchan == 0) + return 0; + else + return -1; + break; + case 2: + if (pchan == 0) + return 0; + if (pchan == 3) + return 1; + else + return -1; + break; + case 3: + if (pchan == 1) + return 0; + if (pchan == 0) + return 1; + if (pchan == 3) + return 2; + else + return -1; + break; + default: + break; + }; + + return -1; +} + +static int getFreqShift(size_t chans) +{ + switch (chans) { + case 1: + return 0; + case 2: + return 0; + case 3: + return 1; + default: + break; + }; + + return -1; +} + +/* Initialize I/O specific objects */ +bool RadioInterfaceMulti::init(int type) +{ + float cutoff = 1.0f; + size_t inchunk = 0, outchunk = 0; + + if (mChans > MCHANS - 1) { + LOG(ALERT) << "Invalid channel configuration " << mChans; + return false; + } + + close(); + + sendBuffer.resize(mChans); + recvBuffer.resize(mChans); + convertSendBuffer.resize(1); + convertRecvBuffer.resize(1); + + mReceiveFIFO.resize(mChans); + powerScaling.resize(mChans); + history.resize(mChans); + active.resize(MCHANS, false); + + inchunk = RESAMP_INRATE * 4; + outchunk = RESAMP_OUTRATE * 4; + + if (inchunk * NUMCHUNKS < 625 * 2) { + LOG(ALERT) << "Invalid inner chunk size " << inchunk; + return false; + } + + dnsampler = new Resampler(RESAMP_INRATE, RESAMP_OUTRATE); + if (!dnsampler->init(1.0)) { + LOG(ALERT) << "Rx resampler failed to initialize"; + return false; + } + + upsampler = new Resampler(RESAMP_OUTRATE, RESAMP_INRATE); + if (!upsampler->init(cutoff)) { + LOG(ALERT) << "Tx resampler failed to initialize"; + return false; + } + + channelizer = new Channelizer(MCHANS, outchunk); + if (!channelizer->init()) { + LOG(ALERT) << "Rx channelizer failed to initialize"; + return false; + } + + synthesis = new Synthesis(MCHANS, outchunk); + if (!synthesis->init()) { + LOG(ALERT) << "Tx synthesis filter failed to initialize"; + return false; + } + + /* + * Allocate high and low rate buffers. The high rate receive + * buffer and low rate transmit vectors feed into the resampler + * and requires headroom equivalent to the filter length. Low + * rate buffers are allocated in the main radio interface code. + */ + for (size_t i = 0; i < mChans; i++) { + sendBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk, + upsampler->len(), true); + recvBuffer[i] = new RadioBuffer(NUMCHUNKS, inchunk, + 0, false); + history[i] = new signalVector(dnsampler->len()); + + synthesis->resetBuffer(i); + } + + outerSendBuffer = new signalVector(synthesis->outputLen()); + outerRecvBuffer = new signalVector(channelizer->inputLen()); + + convertSendBuffer[0] = new short[2 * synthesis->outputLen()]; + convertRecvBuffer[0] = new short[2 * channelizer->inputLen()]; + + /* Configure channels */ + switch (mChans) { + case 1: + active[0] = true; + break; + case 2: + active[0] = true; + active[3] = true; + break; + case 3: + active[0] = true; + active[1] = true; + active[3] = true; + break; + default: + LOG(ALERT) << "Unsupported channel combination"; + return false; + } + + return true; +} + +/* Receive a timestamped chunk from the device */ +void RadioInterfaceMulti::pullBuffer() +{ + bool local_underrun; + size_t num; + float *buf; + + if (recvBuffer[0]->getFreeSegments() <= 0) + return; + + /* Outer buffer access size is fixed */ + num = mRadio->readSamples(convertRecvBuffer, + outerRecvBuffer->size(), + &overrun, + readTimestamp, + &local_underrun); + if (num != channelizer->inputLen()) { + LOG(ALERT) << "Receive error " << num << ", " << channelizer->inputLen(); + return; + } + + convert_short_float((float *) outerRecvBuffer->begin(), + convertRecvBuffer[0], 2 * outerRecvBuffer->size()); + + underrun |= local_underrun; + readTimestamp += num; + + channelizer->rotate((float *) outerRecvBuffer->begin(), + outerRecvBuffer->size()); + + for (size_t pchan = 0; pchan < MCHANS; pchan++) { + if (!active[pchan]) + continue; + + int lchan = getLogicalChan(pchan, mChans); + if (lchan < 0) { + LOG(ALERT) << "Invalid logical channel " << pchan; + continue; + } + + /* + * Update history by writing into the head portion of the + * channelizer output buffer. For this to work, filter length of + * the polyphase channelizer partition filter should be equal to + * or larger than the resampling filter. + */ + buf = channelizer->outputBuffer(pchan); + size_t cLen = channelizer->outputLen(); + size_t hLen = dnsampler->len(); + size_t hSize = 2 * hLen * sizeof(float); + + memcpy(&buf[2 * -hLen], history[lchan]->begin(), hSize); + memcpy(history[lchan]->begin(), &buf[2 * (cLen - hLen)], hSize); + + float *wr_segment = recvBuffer[lchan]->getWriteSegment(); + + /* Write to the end of the inner receive buffer */ + if (!dnsampler->rotate(channelizer->outputBuffer(pchan), + channelizer->outputLen(), + wr_segment, + recvBuffer[lchan]->getSegmentLen())) { + LOG(ALERT) << "Sample rate upsampling error"; + } + } +} + +/* Send a timestamped chunk to the device */ +bool RadioInterfaceMulti::pushBuffer() +{ + if (sendBuffer[0]->getAvailSegments() <= 0) + return false; + + for (size_t pchan = 0; pchan < MCHANS; pchan++) { + if (!active[pchan]) { + synthesis->resetBuffer(pchan); + continue; + } + + int lchan = getLogicalChan(pchan, mChans); + if (lchan < 0) { + LOG(ALERT) << "Invalid logical channel " << pchan; + continue; + } + + if (!upsampler->rotate(sendBuffer[lchan]->getReadSegment(), + sendBuffer[lchan]->getSegmentLen(), + synthesis->inputBuffer(pchan), + synthesis->inputLen())) { + LOG(ALERT) << "Sample rate downsampling error"; + } + } + + synthesis->rotate((float *) outerSendBuffer->begin(), + outerSendBuffer->size()); + + convert_float_short(convertSendBuffer[0], + (float *) outerSendBuffer->begin(), + 1.0 / (float) mChans, 2 * outerSendBuffer->size()); + + size_t num = mRadio->writeSamples(convertSendBuffer, + outerSendBuffer->size(), + &underrun, + writeTimestamp); + if (num != outerSendBuffer->size()) { + LOG(ALERT) << "Transmit error " << num; + } + + writeTimestamp += num; + + return true; +} + +/* Frequency comparison limit */ +#define FREQ_DELTA_LIMIT 10.0 + +static bool fltcmp(double a, double b) +{ + return fabs(a - b) < FREQ_DELTA_LIMIT ? true : false; +} + +bool RadioInterfaceMulti::tuneTx(double freq, size_t chan) +{ + if (chan >= mChans) + return false; + + double shift = (double) getFreqShift(mChans); + + if (!chan) + return mRadio->setTxFreq(freq + shift * MCBTS_SPACING); + + double center = mRadio->getTxFreq(); + if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) + return false; + + return true; +} + +bool RadioInterfaceMulti::tuneRx(double freq, size_t chan) +{ + if (chan >= mChans) + return false; + + double shift = (double) getFreqShift(mChans); + + if (!chan) + return mRadio->setRxFreq(freq + shift * MCBTS_SPACING); + + double center = mRadio->getRxFreq(); + if (!fltcmp(freq, center + (double) (chan - shift) * MCBTS_SPACING)) + return false; + + return true; +} + +double RadioInterfaceMulti::setRxGain(double db, size_t chan) +{ + if (!chan) + return mRadio->setRxGain(db); + else + return mRadio->getRxGain(); +} |