aboutsummaryrefslogtreecommitdiffstats
path: root/Transceiver52M/radioInterfaceMulti.cpp
diff options
context:
space:
mode:
authorTom Tsou <tom.tsou@ettus.com>2016-06-24 14:25:39 -0700
committerTom Tsou <tom.tsou@ettus.com>2016-07-01 03:14:15 -0700
commit76764278169d252980853251daeb9f1ba0c246e1 (patch)
tree0d1631e938ae48d72d0b2b5d5e102116ab2d5b9d /Transceiver52M/radioInterfaceMulti.cpp
parent35222296fef378977a83a4ee89d8c3ef9bc62a3f (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.cpp387
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();
+}