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/Makefile.am b/Transceiver52M/Makefile.am
index 4ec06e0..ec72f8c 100644
--- a/Transceiver52M/Makefile.am
+++ b/Transceiver52M/Makefile.am
@@ -54,6 +54,7 @@
 	radioInterface.cpp \
 	radioVector.cpp \
 	radioClock.cpp \
+	radioBuffer.cpp \
 	sigProcLib.cpp \
 	signalVector.cpp \
 	Transceiver.cpp
@@ -72,6 +73,7 @@
 	radioVector.h \
 	radioClock.h \
 	radioDevice.h \
+	radioBuffer.h \
 	sigProcLib.h \
 	signalVector.h \
 	Transceiver.h \
diff --git a/Transceiver52M/Resampler.cpp b/Transceiver52M/Resampler.cpp
index e4b66a7..070adda 100644
--- a/Transceiver52M/Resampler.cpp
+++ b/Transceiver52M/Resampler.cpp
@@ -167,22 +167,13 @@
 	}
 }
 
-int Resampler::rotate(float *in, size_t in_len, float *out, size_t out_len)
+int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len)
 {
 	int n, path;
-	int hist_len = filt_len - 1;
 
 	if (!check_vec_len(in_len, out_len, p, q))
 		return -1;
 
-	if (history_on) {
-		memcpy(&in[-2 * hist_len],
-		       history, hist_len * 2 * sizeof(float));
-	} else {
-		memset(&in[-2 * hist_len], 0,
-		       hist_len * 2 * sizeof(float));
-	}
-
 	/* Generate output from precomputed input/output paths */
 	for (size_t i = 0; i < out_len; i++) {
 		n = in_index[i]; 
@@ -194,27 +185,15 @@
 			      n, 1, 1, 0);
 	}
 
-	/* Save history */
-	if (history_on) {
-		memcpy(history, &in[2 * (in_len - hist_len)],
-		       hist_len * 2 * sizeof(float));
-	}
-
 	return out_len;
 }
 
 bool Resampler::init(float bw)
 {
-	size_t hist_len = filt_len - 1;
-
 	/* Filterbank filter internals */
 	if (initFilters(bw) < 0)
 		return false;
 
-	/* History buffer */
-	history = new float[2 * hist_len];
-	memset(history, 0, 2 * hist_len * sizeof(float));
-
 	/* Precompute filterbank paths */
 	in_index = new size_t[MAX_OUTPUT_LEN];
 	out_path = new size_t[MAX_OUTPUT_LEN];
@@ -228,14 +207,8 @@
 	return filt_len;
 }
 
-void Resampler::enableHistory(bool on)
-{
-	history_on = on;
-}
-
 Resampler::Resampler(size_t p, size_t q, size_t filt_len)
-	: in_index(NULL), out_path(NULL), partitions(NULL),
-	  history(NULL), history_on(true)
+	: in_index(NULL), out_path(NULL), partitions(NULL)
 {
 	this->p = p;
 	this->q = q;
@@ -246,7 +219,6 @@
 {
 	releaseFilters();
 
-	delete history;
 	delete in_index;
 	delete out_path;
 }
diff --git a/Transceiver52M/Resampler.h b/Transceiver52M/Resampler.h
index 072ec92..c9f9787 100644
--- a/Transceiver52M/Resampler.h
+++ b/Transceiver52M/Resampler.h
@@ -52,18 +52,13 @@
 	 * Input and output vector lengths must of be equal multiples of the
 	 * rational conversion rate denominator and numerator respectively.
 	 */
-	int rotate(float *in, size_t in_len, float *out, size_t out_len);
+	int rotate(const float *in, size_t in_len, float *out, size_t out_len);
 
 	/* Get filter length
 	 *   @return number of taps in each filter partition 
 	 */
 	size_t len();
 
-	/*
-	 * Enable/disable history 
-	 */
-	void enableHistory(bool on);
-
 private:
 	size_t p;
 	size_t q;
@@ -72,8 +67,6 @@
 	size_t *out_path;
 
 	float **partitions;
-	float *history;
-	bool history_on;
 
 	bool initFilters(float bw);
 	void releaseFilters();
diff --git a/Transceiver52M/radioBuffer.cpp b/Transceiver52M/radioBuffer.cpp
new file mode 100644
index 0000000..9e6f079
--- /dev/null
+++ b/Transceiver52M/radioBuffer.cpp
@@ -0,0 +1,228 @@
+/*
+ * Segmented Ring Buffer
+ *
+ * Copyright (C) 2015 Ettus Research LLC
+ *
+ * Author: Tom Tsou <tom@tsou.cc>
+ *
+ * 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 <string.h>
+#include <iostream>
+#include "radioBuffer.h"
+
+RadioBuffer::RadioBuffer(size_t numSegments, size_t segmentLen,
+			 size_t hLen, bool outDirection)
+	: writeIndex(0), readIndex(0), availSamples(0)
+{
+	if (!outDirection)
+		hLen = 0;
+
+	buffer = new float[2 * (hLen + numSegments * segmentLen)];
+	bufferLen = numSegments * segmentLen;
+
+	segments.resize(numSegments);
+
+	for (size_t i = 0; i < numSegments; i++)
+		segments[i] = &buffer[2 * (hLen + i * segmentLen)];
+
+	this->outDirection = outDirection;
+	this->numSegments = numSegments;
+	this->segmentLen = segmentLen;
+	this->hLen = hLen;
+}
+
+RadioBuffer::~RadioBuffer()
+{
+	delete buffer;
+}
+
+void RadioBuffer::reset()
+{
+	writeIndex = 0;
+	readIndex = 0;
+	availSamples = 0;
+}
+
+/*
+ * Output direction
+ *
+ * Return a pointer to the oldest segment or NULL if a complete segment is not
+ * available.
+ */
+const float *RadioBuffer::getReadSegment()
+{
+	if (!outDirection) {
+		std::cout << "Invalid direction" << std::endl;
+		return NULL;
+	}
+	if (availSamples < segmentLen) {
+		std::cout << "Not enough samples " << std::endl;
+		std::cout << availSamples << " available per segment "
+			  << segmentLen << std::endl;
+		return NULL;
+	}
+
+	size_t num = readIndex / segmentLen;
+
+	if (num >= numSegments) {
+		std::cout << "Invalid segment" << std::endl;
+		return NULL;
+	} else if (!num) {
+		memcpy(buffer,
+		       &buffer[2 * bufferLen],
+		       hLen * 2 * sizeof(float));
+	}
+
+	availSamples -= segmentLen;
+	readIndex = (readIndex + segmentLen) % bufferLen;
+
+	return segments[num];
+}
+
+/*
+ * Output direction
+ *
+ * Write a non-segment length of samples to the buffer. 
+ */
+bool RadioBuffer::write(const float *wr, size_t len)
+{
+	if (!outDirection) {
+		std::cout << "Invalid direction" << std::endl;
+		return false;
+	}
+	if (availSamples + len > bufferLen) {
+		std::cout << "Insufficient space" << std::endl;
+		std::cout << bufferLen - availSamples << " available per write "
+			  << len << std::endl;
+		return false;
+	}
+
+	if (writeIndex + len <= bufferLen) {
+		memcpy(&buffer[2 * (writeIndex + hLen)],
+		       wr, len * 2 * sizeof(float));
+	} else {
+		size_t len0 = bufferLen - writeIndex;
+		size_t len1 = len - len0;
+		memcpy(&buffer[2 * (writeIndex + hLen)], wr, len0 * 2 * sizeof(float));
+		memcpy(&buffer[2 * hLen], &wr[2 * len0], len1 * 2 * sizeof(float));
+	}
+
+	availSamples += len;
+	writeIndex = (writeIndex + len) % bufferLen;
+
+	return true;
+}
+
+bool RadioBuffer::zero(size_t len)
+{
+	if (!outDirection) {
+		std::cout << "Invalid direction" << std::endl;
+		return false;
+	}
+	if (availSamples + len > bufferLen) {
+		std::cout << "Insufficient space" << std::endl;
+		std::cout << bufferLen - availSamples << " available per zero "
+			  << len << std::endl;
+		return false;
+	}
+
+	if (writeIndex + len <= bufferLen) {
+		memset(&buffer[2 * (writeIndex + hLen)],
+		       0, len * 2 * sizeof(float));
+	} else {
+		size_t len0 = bufferLen - writeIndex;
+		size_t len1 = len - len0;
+		memset(&buffer[2 * (writeIndex + hLen)], 0, len0 * 2 * sizeof(float));
+		memset(&buffer[2 * hLen], 0, len1 * 2 * sizeof(float));
+	}
+
+	availSamples += len;
+	writeIndex = (writeIndex + len) % bufferLen;
+
+	return true;
+}
+
+/*
+ * Input direction
+ */
+float *RadioBuffer::getWriteSegment()
+{
+	if (outDirection) {
+		std::cout << "Invalid direction" << std::endl;
+		return NULL;
+	}
+	if (bufferLen - availSamples < segmentLen) {
+		std::cout << "Insufficient samples" << std::endl;
+		std::cout << bufferLen - availSamples
+			  << " available for segment " << segmentLen
+			  << std::endl;
+		return NULL;
+	}
+	if (writeIndex % segmentLen) {
+		std::cout << "Internal segment error" << std::endl;
+		return NULL;
+	}
+
+	size_t num = writeIndex / segmentLen;
+
+	if (num >= numSegments)
+		return NULL;
+
+	availSamples += segmentLen;
+	writeIndex = (writeIndex + segmentLen) % bufferLen;
+
+	return segments[num];
+}
+
+bool RadioBuffer::zeroWriteSegment()
+{
+	float *segment = getWriteSegment();
+	if (!segment)
+		return false;
+
+	memset(segment, 0, segmentLen * 2 * sizeof(float));
+
+	return true;
+}
+
+bool RadioBuffer::read(float *rd, size_t len)
+{
+	if (outDirection) {
+		std::cout << "Invalid direction" << std::endl;
+		return false;
+	}
+	if (availSamples < len) {
+		std::cout << "Insufficient samples" << std::endl;
+		std::cout << availSamples << " available for "
+			  << len << std::endl;
+		return false;
+	}
+
+	if (readIndex + len <= bufferLen) {
+		memcpy(rd, &buffer[2 * readIndex], len * 2 * sizeof(float));
+	} else {
+		size_t len0 = bufferLen - readIndex;
+		size_t len1 = len - len0;
+		memcpy(rd, &buffer[2 * readIndex], len0 * 2 * sizeof(float));
+		memcpy(&rd[2 * len0], buffer, len1 * 2 * sizeof(float));
+	}
+
+	availSamples -= len;
+	readIndex = (readIndex + len) % bufferLen;
+
+	return true;
+}
diff --git a/Transceiver52M/radioBuffer.h b/Transceiver52M/radioBuffer.h
new file mode 100644
index 0000000..afb6e63
--- /dev/null
+++ b/Transceiver52M/radioBuffer.h
@@ -0,0 +1,45 @@
+#include <stdlib.h>
+#include <stddef.h>
+#include <vector>
+
+class RadioBuffer {
+public:
+	RadioBuffer(size_t numSegments, size_t segmentLen,
+		    size_t hLen, bool outDirection);
+
+	~RadioBuffer();
+
+	const size_t getSegmentLen() { return segmentLen; };
+	const size_t getNumSegments() { return numSegments; };
+	const size_t getAvailSamples() { return availSamples; };
+	const size_t getAvailSegments() { return availSamples / segmentLen; };
+
+	const size_t getFreeSamples()
+	{
+		return bufferLen - availSamples;
+	}
+
+	const size_t getFreeSegments()
+	{
+		return getFreeSamples() / segmentLen;
+	}
+
+	void reset();
+
+	/* Output direction */
+	const float *getReadSegment();
+	bool write(const float *wr, size_t len);
+	bool zero(size_t len);
+
+	/* Input direction */
+	float *getWriteSegment();
+	bool zeroWriteSegment();
+	bool read(float *rd, size_t len);
+
+private:
+	size_t writeIndex, readIndex, availSamples;
+	size_t bufferLen, numSegments, segmentLen, hLen;
+	float *buffer;
+	std::vector<float *> segments;
+	bool outDirection;
+};
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;
 }
diff --git a/Transceiver52M/radioInterface.h b/Transceiver52M/radioInterface.h
index ce06578..1f225a2 100644
--- a/Transceiver52M/radioInterface.h
+++ b/Transceiver52M/radioInterface.h
@@ -20,6 +20,7 @@
 #include "radioDevice.h"
 #include "radioVector.h"
 #include "radioClock.h"
+#include "radioBuffer.h"
 #include "Resampler.h"
 
 static const unsigned gSlotLen = 148;      ///< number of symbols per slot, not counting guard periods
@@ -40,10 +41,8 @@
   size_t mChans;
   size_t mMIMO;
 
-  std::vector<signalVector *> sendBuffer;
-  std::vector<signalVector *> recvBuffer;
-  unsigned sendCursor;
-  unsigned recvCursor;
+  std::vector<RadioBuffer *> sendBuffer;
+  std::vector<RadioBuffer *> recvBuffer;
 
   std::vector<short *> convertRecvBuffer;
   std::vector<short *> convertSendBuffer;
@@ -61,16 +60,14 @@
 
 private:
 
-  /** format samples to USRP */ 
-  int radioifyVector(signalVector &wVector,
-                     float *floatVector,
-                     bool zero);
+  /** format samples to USRP */
+  int radioifyVector(signalVector &wVector, size_t chan, bool zero);
 
   /** format samples from USRP */
-  int unRadioifyVector(float *floatVector, signalVector &wVector);
+  int unRadioifyVector(signalVector *wVector, size_t chan);
 
   /** push GSM bursts into the transmit buffer */
-  virtual void pushBuffer(void);
+  virtual bool pushBuffer(void);
 
   /** pull GSM bursts from the receive buffer */
   virtual void pullBuffer(void);
@@ -152,20 +149,15 @@
 #endif
 
 class RadioInterfaceResamp : public RadioInterface {
-
 private:
-  signalVector *innerSendBuffer;
   signalVector *outerSendBuffer;
-  signalVector *innerRecvBuffer;
   signalVector *outerRecvBuffer;
 
-  void pushBuffer();
+  bool pushBuffer();
   void pullBuffer();
 
 public:
-
   RadioInterfaceResamp(RadioDevice* wRadio, size_t wSPS = 4, size_t chans = 1);
-
   ~RadioInterfaceResamp();
 
   bool init(int type);
@@ -184,7 +176,7 @@
   bool tuneRx(double freq, size_t chan);
 
 private:
-  std::vector<Resampler *> dnsamplers;
+  Resampler *dnsampler;
   std::vector<float> phases;
   signalVector *outerRecvBuffer;
 
diff --git a/Transceiver52M/radioInterfaceDiversity.cpp b/Transceiver52M/radioInterfaceDiversity.cpp
index 8e921b1..b3973e5 100644
--- a/Transceiver52M/radioInterfaceDiversity.cpp
+++ b/Transceiver52M/radioInterfaceDiversity.cpp
@@ -65,14 +65,11 @@
 void RadioInterfaceDiversity::close()
 {
 	delete outerRecvBuffer;
+	delete dnsampler;
 
+	dnsampler = NULL;
 	outerRecvBuffer = NULL;
 
-	for (size_t i = 0; i < dnsamplers.size(); i++) {
-		delete dnsamplers[i];
-		dnsamplers[i] = NULL;
-	}
-
 	if (recvBuffer.size())
 		recvBuffer[0] = NULL;
 
@@ -98,15 +95,16 @@
 		return false;
 	}
 
+	dnsampler = new Resampler(resamp_inrate, resamp_outrate);
+	if (!dnsampler->init()) {
+		LOG(ALERT) << "Rx resampler failed to initialize";
+		return false;
+	}
+
 	/* One Receive buffer and downsampler per diversity channel */
 	for (size_t i = 0; i < mMIMO * mChans; i++) {
-		dnsamplers[i] = new Resampler(resamp_inrate, resamp_outrate);
-		if (!dnsamplers[i]->init()) {
-			LOG(ALERT) << "Rx resampler failed to initialize";
-			return false;
-		}
-
-		recvBuffer[i] = new signalVector(inner_rx_len);
+		recvBuffer[i] = new RadioBuffer(NUMCHUNKS,
+						resamp_inchunk, 0, false);
 	}
 
 	return true;
@@ -115,7 +113,7 @@
 /* Initialize I/O specific objects */
 bool RadioInterfaceDiversity::init(int type)
 {
-	int tx_len, outer_rx_len;
+	int outer_rx_len;
 
 	if ((mMIMO != 2) || (mChans != 2)) {
 		LOG(ALERT) << "Unsupported channel configuration " << mChans;
@@ -128,13 +126,11 @@
 	convertSendBuffer.resize(mChans);
 	convertRecvBuffer.resize(mChans);
 	mReceiveFIFO.resize(mChans);
-	dnsamplers.resize(mChans * mMIMO);
 	phases.resize(mChans);
 
 	if (!setupDiversityChannels())
 		return false;
 
-	tx_len = CHUNK * mSPSTx;
 	outer_rx_len = resamp_outchunk;
 
 	for (size_t i = 0; i < mChans; i++) {
@@ -142,11 +138,11 @@
 		convertRecvBuffer[i] = new short[outer_rx_len * 2];
 
 		/* Send buffers (not-resampled) */
-		sendBuffer[i] = new signalVector(tx_len);
-		convertSendBuffer[i] = new short[tx_len * 2];
+		sendBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSTx, 0, true);
+		convertSendBuffer[i] = new short[CHUNK * mSPSTx * 2];
 	}
 
-	outerRecvBuffer = new signalVector(outer_rx_len, dnsamplers[0]->len());
+	outerRecvBuffer = new signalVector(outer_rx_len, dnsampler->len());
 
 	return true;
 }
@@ -182,7 +178,7 @@
 	signalVector *shift, *base;
 	float *in, *out, rate = -mFreqSpacing * 2.0 * M_PI / 1.08333333e6;
 
-	if (recvCursor > recvBuffer[0]->size() - resamp_inchunk)
+	if (recvBuffer[0]->getFreeSegments() <= 0)
 		return;
 
 	/* Outer buffer access size is fixed */
@@ -211,10 +207,10 @@
 		/* Diversity path 1 */
 		base = outerRecvBuffer;
 		in = (float *) base->begin();
-		out = (float *) (recvBuffer[path0]->begin() + recvCursor);
+		out = (float *) recvBuffer[path0]->getWriteSegment();
 
-		rc = dnsamplers[2 * i + 0]->rotate(in, resamp_outchunk,
-						   out, resamp_inchunk);
+		rc = dnsampler->rotate(in, resamp_outchunk,
+				       out, resamp_inchunk);
 		if (rc < 0) {
 			LOG(ALERT) << "Sample rate downsampling error";
 		}
@@ -226,15 +222,15 @@
 		/* Diversity path 2 */
 		shift = new signalVector(base->size(), base->getStart());
 		in = (float *) shift->begin();
-		out = (float *) (recvBuffer[path1]->begin() + recvCursor);
+		out = (float *) recvBuffer[path1]->getWriteSegment();
 
 		rate = i ? -rate : rate;
 		if (!frequencyShift(shift, base, rate, phases[i], &phases[i])) {
 			LOG(ALERT) << "Frequency shift failed";
 		}
 
-		rc = dnsamplers[2 * i + 1]->rotate(in, resamp_outchunk,
-						   out, resamp_inchunk);
+		rc = dnsampler->rotate(in, resamp_outchunk,
+				       out, resamp_inchunk);
 		if (rc < 0) {
 			LOG(ALERT) << "Sample rate downsampling error";
 		}
@@ -244,5 +240,4 @@
 
 	underrun |= local_underrun;
 	readTimestamp += (TIMESTAMP) resamp_outchunk;
-	recvCursor += resamp_inchunk;
 }
diff --git a/Transceiver52M/radioInterfaceResamp.cpp b/Transceiver52M/radioInterfaceResamp.cpp
index f898d65..26dd40b 100644
--- a/Transceiver52M/radioInterfaceResamp.cpp
+++ b/Transceiver52M/radioInterfaceResamp.cpp
@@ -1,8 +1,10 @@
 /*
  * Radio device interface with sample rate conversion
- * Written by Thomas Tsou <tom@tsou.cc>
  *
- * Copyright 2011, 2012, 2013 Free Software Foundation, Inc.
+ * Copyright (C) 2011-2014 Free Software Foundation, Inc.
+ * Copyright (C) 2015 Ettus Research LLC
+ *
+ * Author: Tom Tsou <tom@tsou.cc>
  *
  * 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
@@ -58,8 +60,8 @@
 RadioInterfaceResamp::RadioInterfaceResamp(RadioDevice *wRadio,
 					   size_t sps, size_t chans)
 	: RadioInterface(wRadio, sps, chans),
-	  innerSendBuffer(NULL), outerSendBuffer(NULL),
-	  innerRecvBuffer(NULL), outerRecvBuffer(NULL)
+	  outerSendBuffer(NULL),
+	  outerRecvBuffer(NULL)
 {
 }
 
@@ -70,17 +72,13 @@
 
 void RadioInterfaceResamp::close()
 {
-	delete innerSendBuffer;
 	delete outerSendBuffer;
-	delete innerRecvBuffer;
 	delete outerRecvBuffer;
 
 	delete upsampler;
 	delete dnsampler;
 
-	innerSendBuffer = NULL;
 	outerSendBuffer = NULL;
-	innerRecvBuffer = NULL;
 	outerRecvBuffer = NULL;
 
 	upsampler = NULL;
@@ -157,21 +155,18 @@
 	 * and requires headroom equivalent to the filter length. Low
 	 * rate buffers are allocated in the main radio interface code.
 	 */
-	innerSendBuffer =
-		new signalVector(NUMCHUNKS * resamp_inchunk, upsampler->len());
+	sendBuffer[0] = new RadioBuffer(NUMCHUNKS, resamp_inchunk,
+					  upsampler->len(), true);
+	recvBuffer[0] = new RadioBuffer(NUMCHUNKS * 20, resamp_inchunk, 0, false);
+
 	outerSendBuffer =
 		new signalVector(NUMCHUNKS * resamp_outchunk);
 	outerRecvBuffer =
 		new signalVector(resamp_outchunk, dnsampler->len());
-	innerRecvBuffer =
-		new signalVector(NUMCHUNKS * resamp_inchunk / mSPSTx);
 
 	convertSendBuffer[0] = new short[outerSendBuffer->size() * 2];
 	convertRecvBuffer[0] = new short[outerRecvBuffer->size() * 2];
 
-	sendBuffer[0] = innerSendBuffer;
-	recvBuffer[0] = innerRecvBuffer;
-
 	return true;
 }
 
@@ -181,7 +176,7 @@
 	bool local_underrun;
 	int rc, num_recv;
 
-	if (recvCursor > innerRecvBuffer->size() - resamp_inchunk)
+	if (recvBuffer[0]->getFreeSegments() <= 0)
 		return;
 
 	/* Outer buffer access size is fixed */
@@ -204,57 +199,47 @@
 	/* Write to the end of the inner receive buffer */
 	rc = dnsampler->rotate((float *) outerRecvBuffer->begin(),
 			       resamp_outchunk,
-			       (float *) (innerRecvBuffer->begin() + recvCursor),
+			       recvBuffer[0]->getWriteSegment(),
 			       resamp_inchunk);
 	if (rc < 0) {
 		LOG(ALERT) << "Sample rate upsampling error";
 	}
 
-	recvCursor += resamp_inchunk;
+	/* Set history for the next chunk */
+	outerRecvBuffer->updateHistory();
 }
 
 /* Send a timestamped chunk to the device */
-void RadioInterfaceResamp::pushBuffer()
+bool RadioInterfaceResamp::pushBuffer()
 {
-	int rc, chunks, num_sent;
-	int inner_len, outer_len;
+	int rc;
+	size_t numSent;
 
-	if (sendCursor < resamp_inchunk)
-		return;
-
-	if (sendCursor > innerSendBuffer->size())
-		LOG(ALERT) << "Send buffer overflow";
-
-	chunks = sendCursor / resamp_inchunk;
-
-	inner_len = chunks * resamp_inchunk;
-	outer_len = chunks * resamp_outchunk;
+	if (sendBuffer[0]->getAvailSegments() <= 0)
+		return false;
 
 	/* Always send from the beginning of the buffer */
-	rc = upsampler->rotate((float *) innerSendBuffer->begin(), inner_len,
-			       (float *) outerSendBuffer->begin(), outer_len);
+	rc = upsampler->rotate(sendBuffer[0]->getReadSegment(),
+			       resamp_inchunk,
+			       (float *) outerSendBuffer->begin(),
+			       resamp_outchunk);
 	if (rc < 0) {
 		LOG(ALERT) << "Sample rate downsampling error";
 	}
 
 	convert_float_short(convertSendBuffer[0],
 			    (float *) outerSendBuffer->begin(),
-			    powerScaling[0], 2 * outer_len);
+			    powerScaling[0], 2 * resamp_outchunk);
 
-	num_sent = mRadio->writeSamples(convertSendBuffer,
-					outer_len,
-					&underrun,
-					writeTimestamp);
-	if (num_sent != outer_len) {
-		LOG(ALERT) << "Transmit error " << num_sent;
+	numSent = mRadio->writeSamples(convertSendBuffer,
+				       resamp_outchunk,
+				       &underrun,
+				       writeTimestamp);
+	if (numSent != resamp_outchunk) {
+		LOG(ALERT) << "Transmit error " << numSent;
 	}
 
-	/* Shift remaining samples to beginning of buffer */
-	memmove(innerSendBuffer->begin(),
-		innerSendBuffer->begin() + inner_len,
-		(sendCursor - inner_len) * 2 * sizeof(float));
+	writeTimestamp += resamp_outchunk;
 
-	writeTimestamp += outer_len;
-	sendCursor -= inner_len;
-	assert(sendCursor >= 0);
+	return true;
 }
diff --git a/Transceiver52M/sigProcLib.cpp b/Transceiver52M/sigProcLib.cpp
index 477449f..7a032b3 100644
--- a/Transceiver52M/sigProcLib.cpp
+++ b/Transceiver52M/sigProcLib.cpp
@@ -2128,7 +2128,6 @@
     goto fail;
   }
 
-  dnsampler->enableHistory(false);
   dnsampler_in = new signalVector(DOWNSAMPLE_IN_LEN, dnsampler->len());
 
   return true;
diff --git a/Transceiver52M/signalVector.cpp b/Transceiver52M/signalVector.cpp
index 4b4099e..798a3c7 100644
--- a/Transceiver52M/signalVector.cpp
+++ b/Transceiver52M/signalVector.cpp
@@ -50,6 +50,15 @@
 	return mStart - mData;
 }
 
+size_t signalVector::updateHistory()
+{
+	size_t num = getStart();
+
+	memmove(mData, mStart + this->size() - num, num * sizeof(complex));
+
+	return num;
+}
+
 Symmetry signalVector::getSymmetry() const
 {
 	return symmetry;
diff --git a/Transceiver52M/signalVector.h b/Transceiver52M/signalVector.h
index c9cb105..38541fe 100644
--- a/Transceiver52M/signalVector.h
+++ b/Transceiver52M/signalVector.h
@@ -32,6 +32,7 @@
 
 	/** Return head room */
 	size_t getStart() const;
+	size_t updateHistory();
 
 	Symmetry getSymmetry() const;
 	void setSymmetry(Symmetry symmetry);