iface: Add inner ring-buffer implementation

Two buffers, inner and outer, are used in the transceiver
implementation. The outer buffer interfaces with the device receive
interface to guarantee timestamp aligned and contiguously allocated
sample buffers. The inner buffer absorbs vector size differences between
GSM bursts (156 or 157 samples) and the resampler interface (typically
fixed multiples of 65).

Reimplement the inner buffer with a ring buffer that allows fixed size
segments on the outer (resampler) portion and variable lengths (GSM
side) on the inner side. Compared to the previous stack-like version,
this implementation removes unnecessary copying of buffer contents.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
diff --git a/Transceiver52M/radioInterface.cpp b/Transceiver52M/radioInterface.cpp
index b719c94..052b439 100644
--- a/Transceiver52M/radioInterface.cpp
+++ b/Transceiver52M/radioInterface.cpp
@@ -1,26 +1,23 @@
 /*
-* 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/>.
-
-*/
+ * Radio device interface
+ *
+ * Copyright (C) 2008-2014 Free Software Foundation, Inc.
+ * Copyright (C) 2015 Ettus Research LLC
+ *
+ * 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 "Resampler.h"
@@ -37,8 +34,8 @@
                                size_t rx_sps, size_t chans, size_t diversity,
                                int wReceiveOffset, GSM::Time wStartTime)
   : mRadio(wRadio), mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans),
-    mMIMO(diversity), sendCursor(0), recvCursor(0), underrun(false),
-    overrun(false), receiveOffset(wReceiveOffset), mOn(false)
+    mMIMO(diversity), underrun(false), overrun(false),
+    receiveOffset(wReceiveOffset), mOn(false)
 {
   mClock.set(wStartTime);
 }
@@ -65,35 +62,20 @@
   powerScaling.resize(mChans);
 
   for (size_t i = 0; i < mChans; i++) {
-    sendBuffer[i] = new signalVector(CHUNK * mSPSTx);
-    recvBuffer[i] = new signalVector(NUMCHUNKS * CHUNK * mSPSRx);
+    sendBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSTx, 0, true);
+    recvBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSRx, 0, false);
 
-    convertSendBuffer[i] = new short[sendBuffer[i]->size() * 2];
-    convertRecvBuffer[i] = new short[recvBuffer[i]->size() * 2];
+    convertSendBuffer[i] = new short[CHUNK * mSPSTx * 2];
+    convertRecvBuffer[i] = new short[CHUNK * mSPSRx * 2];
 
     powerScaling[i] = 1.0;
   }
 
-  sendCursor = 0;
-  recvCursor = 0;
-
   return true;
 }
 
 void RadioInterface::close()
 {
-  for (size_t i = 0; i < sendBuffer.size(); i++)
-    delete sendBuffer[i];
-
-  for (size_t i = 0; i < recvBuffer.size(); i++)
-    delete recvBuffer[i];
-
-  for (size_t i = 0; i < convertSendBuffer.size(); i++)
-    delete convertSendBuffer[i];
-
-  for (size_t i = 0; i < convertRecvBuffer.size(); i++)
-    delete convertRecvBuffer[i];
-
   sendBuffer.resize(0);
   recvBuffer.resize(0);
   convertSendBuffer.resize(0);
@@ -132,35 +114,26 @@
 }
 
 int RadioInterface::radioifyVector(signalVector &wVector,
-				   float *retVector,
-				   bool zero)
+                                   size_t chan, bool zero)
 {
-  if (zero) {
-    memset(retVector, 0, wVector.size() * 2 * sizeof(float));
-    return wVector.size();
-  }
-
-  memcpy(retVector, wVector.begin(), wVector.size() * 2 * sizeof(float));
+  if (zero)
+    sendBuffer[chan]->zero(wVector.size());
+  else
+    sendBuffer[chan]->write((float *) wVector.begin(), wVector.size());
 
   return wVector.size();
 }
 
-int RadioInterface::unRadioifyVector(float *floatVector,
-				     signalVector& newVector)
+int RadioInterface::unRadioifyVector(signalVector *newVector, size_t chan)
 {
-  signalVector::iterator itr = newVector.begin();
-
-  if (newVector.size() > recvCursor) {
+  if (newVector->size() > recvBuffer[chan]->getAvailSamples()) {
     LOG(ALERT) << "Insufficient number of samples in receive buffer";
     return -1;
   }
 
-  for (size_t i = 0; i < newVector.size(); i++) {
-    *itr++ = Complex<float>(floatVector[2 * i + 0],
-			    floatVector[2 * i + 1]);
-  }
+  recvBuffer[chan]->read((float *) newVector->begin(), newVector->size());
 
-  return newVector.size();
+  return newVector->size();
 }
 
 bool RadioInterface::tuneTx(double freq, size_t chan)
@@ -187,8 +160,10 @@
   if (!mRadio->start())
     return false;
 
-  recvCursor = 0;
-  sendCursor = 0;
+  for (size_t i = 0; i < mChans; i++) {
+    sendBuffer[i]->reset();
+    recvBuffer[i]->reset();
+  }
 
   writeTimestamp = mRadio->initialWriteTimestamp();
   readTimestamp = mRadio->initialReadTimestamp();
@@ -239,14 +214,10 @@
   if (!mOn)
     return;
 
-  for (size_t i = 0; i < mChans; i++) {
-    radioifyVector(*bursts[i],
-                   (float *) (sendBuffer[i]->begin() + sendCursor), zeros[i]);
-  }
+  for (size_t i = 0; i < mChans; i++)
+    radioifyVector(*bursts[i], i, zeros[i]);
 
-  sendCursor += bursts[0]->size();
-
-  pushBuffer();
+  while (pushBuffer());
 }
 
 bool RadioInterface::driveReceiveRadio()
@@ -261,8 +232,7 @@
   GSM::Time rcvClock = mClock.get();
   rcvClock.decTN(receiveOffset);
   unsigned tN = rcvClock.TN();
-  int recvSz = recvCursor;
-  int readSz = 0;
+  int recvSz = recvBuffer[0]->getAvailSamples();
   const int symbolsPerSlot = gSlotLen + 8;
   int burstSize;
 
@@ -285,11 +255,8 @@
     for (size_t i = 0; i < mChans; i++) {
       burst = new radioVector(rcvClock, burstSize, head, mMIMO);
 
-      for (size_t n = 0; n < mMIMO; n++) {
-        unRadioifyVector((float *)
-                         (recvBuffer[mMIMO * i + n]->begin() + readSz),
-                         *burst->getVector(n));
-      }
+      for (size_t n = 0; n < mMIMO; n++)
+        unRadioifyVector(burst->getVector(n), i);
 
       if (mReceiveFIFO[i].size() < 32)
         mReceiveFIFO[i].write(burst);
@@ -299,7 +266,6 @@
 
     mClock.incTN();
     rcvClock.incTN();
-    readSz += burstSize;
     recvSz -= burstSize;
 
     tN = rcvClock.TN();
@@ -308,16 +274,6 @@
       burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
   }
 
-  if (readSz > 0) {
-    for (size_t i = 0; i < recvBuffer.size(); i++) {
-      memmove(recvBuffer[i]->begin(),
-              recvBuffer[i]->begin() + readSz,
-              (recvCursor - readSz) * 2 * sizeof(float));
-    }
-
-    recvCursor -= readSz;
-  }
-
   return true;
 }
 
@@ -339,74 +295,66 @@
 
 double RadioInterface::setRxGain(double dB, size_t chan)
 {
-  if (mRadio)
-    return mRadio->setRxGain(dB, chan);
-  else
-    return -1;
+  return mRadio->setRxGain(dB, chan);
 }
 
 double RadioInterface::getRxGain(size_t chan)
 {
-  if (mRadio)
-    return mRadio->getRxGain(chan);
-  else
-    return -1;
+  return mRadio->getRxGain(chan);
 }
 
 /* Receive a timestamped chunk from the device */
 void RadioInterface::pullBuffer()
 {
   bool local_underrun;
-  int num_recv;
-  float *output;
+  size_t numRecv, segmentLen = recvBuffer[0]->getSegmentLen();
 
-  if (recvCursor > recvBuffer[0]->size() - CHUNK)
+  if (recvBuffer[0]->getFreeSegments() <= 0)
     return;
 
   /* Outer buffer access size is fixed */
-  num_recv = mRadio->readSamples(convertRecvBuffer,
-                                 CHUNK,
-                                 &overrun,
-                                 readTimestamp,
-                                 &local_underrun);
-  if (num_recv != CHUNK) {
-          LOG(ALERT) << "Receive error " << num_recv;
+  numRecv = mRadio->readSamples(convertRecvBuffer,
+                                segmentLen,
+                                &overrun,
+                                readTimestamp,
+                                &local_underrun);
+
+  if (numRecv != segmentLen) {
+          LOG(ALERT) << "Receive error " << numRecv;
           return;
   }
 
   for (size_t i = 0; i < mChans; i++) {
-    output = (float *) (recvBuffer[i]->begin() + recvCursor);
-    convert_short_float(output, convertRecvBuffer[i], 2 * num_recv);
+    convert_short_float(recvBuffer[i]->getWriteSegment(),
+			convertRecvBuffer[i],
+			segmentLen * 2);
   }
 
   underrun |= local_underrun;
-
-  readTimestamp += num_recv;
-  recvCursor += num_recv;
+  readTimestamp += numRecv;
 }
 
 /* Send timestamped chunk to the device with arbitrary size */
-void RadioInterface::pushBuffer()
+bool RadioInterface::pushBuffer()
 {
-  int num_sent;
+  size_t numSent, segmentLen = sendBuffer[0]->getSegmentLen();
 
-  if (sendCursor < CHUNK)
-    return;
-
-  if (sendCursor > sendBuffer[0]->size())
-    LOG(ALERT) << "Send buffer overflow";
+  if (sendBuffer[0]->getAvailSegments() < 1)
+    return false;
 
   for (size_t i = 0; i < mChans; i++) {
     convert_float_short(convertSendBuffer[i],
-                        (float *) sendBuffer[i]->begin(),
-                        powerScaling[i], 2 * sendCursor);
+                        (float *) sendBuffer[0]->getReadSegment(),
+                        powerScaling[i],
+                        segmentLen * 2);
   }
 
-  /* Send the all samples in the send buffer */ 
-  num_sent = mRadio->writeSamples(convertSendBuffer,
-                                  sendCursor,
-                                  &underrun,
-                                  writeTimestamp);
-  writeTimestamp += num_sent;
-  sendCursor = 0;
+  /* Send the all samples in the send buffer */
+  numSent = mRadio->writeSamples(convertSendBuffer,
+                                 segmentLen,
+                                 &underrun,
+                                 writeTimestamp);
+  writeTimestamp += numSent;
+
+  return true;
 }