aboutsummaryrefslogtreecommitdiffstats
path: root/Transceiver52M
diff options
context:
space:
mode:
Diffstat (limited to 'Transceiver52M')
-rw-r--r--Transceiver52M/Makefile.am2
-rw-r--r--Transceiver52M/Resampler.cpp239
-rw-r--r--Transceiver52M/Resampler.h77
-rw-r--r--Transceiver52M/UHDDevice.cpp2
-rw-r--r--Transceiver52M/radioInterface.cpp72
-rw-r--r--Transceiver52M/radioInterface.h25
-rw-r--r--Transceiver52M/radioInterfaceResamp.cpp395
-rw-r--r--Transceiver52M/runTransceiver.cpp3
-rw-r--r--Transceiver52M/sigProcLib.cpp188
-rw-r--r--Transceiver52M/sigProcLib.h34
10 files changed, 539 insertions, 498 deletions
diff --git a/Transceiver52M/Makefile.am b/Transceiver52M/Makefile.am
index 67ab0ea..c2f8ece 100644
--- a/Transceiver52M/Makefile.am
+++ b/Transceiver52M/Makefile.am
@@ -57,6 +57,7 @@ COMMON_SOURCES = \
libtransceiver_la_SOURCES = \
$(COMMON_SOURCES) \
+ Resampler.cpp \
radioInterfaceResamp.cpp
noinst_PROGRAMS = \
@@ -76,6 +77,7 @@ noinst_HEADERS = \
DummyLoad.h \
rcvLPF_651.h \
sendLPF_961.h \
+ Resampler.h \
convolve.h
USRPping_SOURCES = USRPping.cpp
diff --git a/Transceiver52M/Resampler.cpp b/Transceiver52M/Resampler.cpp
new file mode 100644
index 0000000..624b666
--- /dev/null
+++ b/Transceiver52M/Resampler.cpp
@@ -0,0 +1,239 @@
+/*
+ * Rational Sample Rate Conversion
+ * Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+#include <malloc.h>
+#include <iostream>
+
+#include "Resampler.h"
+
+extern "C" {
+#include "convolve.h"
+}
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846264338327f
+#endif
+
+#define MAX_OUTPUT_LEN 4096
+
+static float sinc(float x)
+{
+ if (x == 0.0)
+ return 0.9999999999;
+
+ return sin(M_PI * x) / (M_PI * x);
+}
+
+bool Resampler::initFilters(float bw)
+{
+ size_t proto_len = p * filt_len;
+ float *proto, val, cutoff;
+ float sum = 0.0f, scale = 0.0f;
+ float midpt = (float) (proto_len - 1.0) / 2.0;
+
+ /*
+ * Allocate partition filters and the temporary prototype filter
+ * according to numerator of the rational rate. Coefficients are
+ * real only and must be 16-byte memory aligned for SSE usage.
+ */
+ proto = new float[proto_len];
+ if (!proto)
+ return false;
+
+ partitions = (float **) malloc(sizeof(float *) * p);
+ if (!partitions) {
+ free(proto);
+ return false;
+ }
+
+ for (size_t i = 0; i < p; i++) {
+ partitions[i] = (float *)
+ memalign(16, filt_len * 2 * sizeof(float));
+ }
+
+ /*
+ * Generate the prototype filter with a Blackman-harris window.
+ * Scale coefficients with DC filter gain set to unity divided
+ * by the number of filter partitions.
+ */
+ float a0 = 0.35875;
+ float a1 = 0.48829;
+ float a2 = 0.14128;
+ float a3 = 0.01168;
+
+ if (p > q)
+ cutoff = (float) p;
+ else
+ cutoff = (float) q;
+
+ for (size_t i = 0; i < proto_len; i++) {
+ proto[i] = sinc(((float) i - midpt) / cutoff * bw);
+ proto[i] *= a0 -
+ a1 * cos(2 * M_PI * i / (proto_len - 1)) +
+ a2 * cos(4 * M_PI * i / (proto_len - 1)) -
+ a3 * cos(6 * M_PI * i / (proto_len - 1));
+ sum += proto[i];
+ }
+ scale = p / sum;
+
+ /* Populate filter partitions from the prototype filter */
+ for (size_t i = 0; i < filt_len; i++) {
+ for (size_t n = 0; n < p; n++) {
+ partitions[n][2 * i + 0] = proto[i * p + n] * scale;
+ partitions[n][2 * i + 1] = 0.0f;
+ }
+ }
+
+ /* For convolution, we store the filter taps in reverse */
+ for (size_t n = 0; n < p; n++) {
+ for (size_t i = 0; i < filt_len / 2; i++) {
+ val = partitions[n][2 * i];
+ partitions[n][2 * i] = partitions[n][2 * (filt_len - 1 - i)];
+ partitions[n][2 * (filt_len - 1 - i)] = val;
+ }
+ }
+
+ delete proto;
+
+ return true;
+}
+
+void Resampler::releaseFilters()
+{
+ if (partitions) {
+ for (size_t i = 0; i < p; i++)
+ free(partitions[i]);
+ }
+
+ free(partitions);
+ partitions = NULL;
+}
+
+static bool check_vec_len(int in_len, int out_len, int p, int q)
+{
+ if (in_len % q) {
+ std::cerr << "Invalid input length " << in_len
+ << " is not multiple of " << q << std::endl;
+ return false;
+ }
+
+ if (out_len % p) {
+ std::cerr << "Invalid output length " << out_len
+ << " is not multiple of " << p << std::endl;
+ return false;
+ }
+
+ if ((in_len / q) != (out_len / p)) {
+ std::cerr << "Input/output block length mismatch" << std::endl;
+ std::cerr << "P = " << p << ", Q = " << q << std::endl;
+ std::cerr << "Input len: " << in_len << std::endl;
+ std::cerr << "Output len: " << out_len << std::endl;
+ return false;
+ }
+
+ if (out_len > MAX_OUTPUT_LEN) {
+ std::cerr << "Block length of " << out_len
+ << " exceeds max of " << MAX_OUTPUT_LEN << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
+void Resampler::computePath()
+{
+ for (int i = 0; i < MAX_OUTPUT_LEN; i++) {
+ in_index[i] = (q * i) / p;
+ out_path[i] = (q * i) % p;
+ }
+}
+
+int Resampler::rotate(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;
+
+ /* Insert history */
+ memcpy(&in[-2 * hist_len], history, 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];
+ path = out_path[i];
+
+ convolve_real(in, in_len,
+ partitions[path], filt_len,
+ &out[2 * i], out_len - i,
+ n, 1, 1, 0);
+ }
+
+ /* Save history */
+ 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];
+ computePath();
+
+ return true;
+}
+
+size_t Resampler::len()
+{
+ return filt_len;
+}
+
+Resampler::Resampler(size_t p, size_t q, size_t filt_len)
+ : in_index(NULL), out_path(NULL), partitions(NULL), history(NULL)
+{
+ this->p = p;
+ this->q = q;
+ this->filt_len = filt_len;
+}
+
+Resampler::~Resampler()
+{
+ releaseFilters();
+
+ delete history;
+ delete in_index;
+ delete out_path;
+}
diff --git a/Transceiver52M/Resampler.h b/Transceiver52M/Resampler.h
new file mode 100644
index 0000000..cf2defd
--- /dev/null
+++ b/Transceiver52M/Resampler.h
@@ -0,0 +1,77 @@
+/*
+ * Rational Sample Rate Conversion
+ * Copyright (C) 2012, 2013 Thomas Tsou <tom@tsou.cc>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#ifndef _RESAMPLER_H_
+#define _RESAMPLER_H_
+
+class Resampler {
+public:
+ /* Constructor for rational sample rate conversion
+ * @param p numerator of resampling ratio
+ * @param q denominator of resampling ratio
+ * @param filt_len length of each polyphase subfilter
+ */
+ Resampler(size_t p, size_t q, size_t filt_len = 16);
+ ~Resampler();
+
+ /* Initilize resampler filterbank.
+ * @param bw bandwidth factor on filter generation (pre-window)
+ * @return false on error, zero otherwise
+ *
+ * Automatic setting is to compute the filter to prevent aliasing with
+ * a Blackman-Harris window. Adjustment is made through a bandwith
+ * factor to shift the cutoff and/or the constituent filter lengths.
+ * Calculation of specific rolloff factors or 3-dB cutoff points is
+ * left as an excersize for the reader.
+ */
+ bool init(float bw = 1.0f);
+
+ /* Rotate "commutator" and drive samples through filterbank
+ * @param in continuous buffer of input complex float values
+ * @param in_len input buffer length
+ * @param out continuous buffer of output complex float values
+ * @param out_len output buffer length
+ * @return number of samples outputted, negative on error
+ *
+ * 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);
+
+ /* Get filter length
+ * @return number of taps in each filter partition
+ */
+ size_t len();
+
+private:
+ size_t p;
+ size_t q;
+ size_t filt_len;
+ size_t *in_index;
+ size_t *out_path;
+
+ float **partitions;
+ float *history;
+
+ bool initFilters(float bw);
+ void releaseFilters();
+ void computePath();
+};
+
+#endif /* _RESAMPLER_H_ */
diff --git a/Transceiver52M/UHDDevice.cpp b/Transceiver52M/UHDDevice.cpp
index a627a64..261a32f 100644
--- a/Transceiver52M/UHDDevice.cpp
+++ b/Transceiver52M/UHDDevice.cpp
@@ -34,7 +34,7 @@
#define B100_CLK_RT 52e6
#define B100_BASE_RT GSMRATE
-#define USRP2_BASE_RT 400e3
+#define USRP2_BASE_RT 390625
#define TX_AMPL 0.3
#define SAMPLE_BUF_SZ (1 << 20)
diff --git a/Transceiver52M/radioInterface.cpp b/Transceiver52M/radioInterface.cpp
index e3fa2a4..1e25b5d 100644
--- a/Transceiver52M/radioInterface.cpp
+++ b/Transceiver52M/radioInterface.cpp
@@ -53,18 +53,47 @@ RadioInterface::RadioInterface(RadioDevice *wRadio,
int wReceiveOffset,
int wSPS,
GSM::Time wStartTime)
- : underrun(false), sendCursor(0), rcvCursor(0), mOn(false),
+ : underrun(false), sendCursor(0), recvCursor(0), mOn(false),
mRadio(wRadio), receiveOffset(wReceiveOffset),
sps(wSPS), powerScaling(1.0),
- loadTest(false)
+ loadTest(false), sendBuffer(NULL), recvBuffer(NULL),
+ convertRecvBuffer(NULL), convertSendBuffer(NULL)
{
mClock.set(wStartTime);
}
+RadioInterface::~RadioInterface(void)
+{
+ close();
+}
+
+bool RadioInterface::init()
+{
+ close();
+
+ sendBuffer = new signalVector(OUTCHUNK * 20);
+ recvBuffer = new signalVector(INCHUNK * 20);
+
+ convertSendBuffer = new short[OUTCHUNK * 2 * 20];
+ convertRecvBuffer = new short[OUTCHUNK * 2 * 2];
+
+ sendCursor = 0;
+ recvCursor = 0;
-RadioInterface::~RadioInterface(void) {
- if (rcvBuffer!=NULL) delete rcvBuffer;
- //mReceiveFIFO.clear();
+ return true;
+}
+
+void RadioInterface::close()
+{
+ delete sendBuffer;
+ delete recvBuffer;
+ delete convertSendBuffer;
+ delete convertRecvBuffer;
+
+ sendBuffer = NULL;
+ recvBuffer = NULL;
+ convertRecvBuffer = NULL;
+ convertSendBuffer = NULL;
}
double RadioInterface::fullScaleInputValue(void) {
@@ -150,9 +179,6 @@ void RadioInterface::start()
mRadio->updateAlignment(writeTimestamp-10000);
mRadio->updateAlignment(writeTimestamp-10000);
- sendBuffer = new float[2*2*INCHUNK*sps];
- rcvBuffer = new float[2*2*OUTCHUNK*sps];
-
mOn = true;
}
@@ -173,11 +199,14 @@ void RadioInterface::alignRadio() {
}
#endif
-void RadioInterface::driveTransmitRadio(signalVector &radioBurst, bool zeroBurst) {
-
- if (!mOn) return;
+void RadioInterface::driveTransmitRadio(signalVector &radioBurst, bool zeroBurst)
+{
+ if (!mOn)
+ return;
- radioifyVector(radioBurst, sendBuffer + 2 * sendCursor, powerScaling, zeroBurst);
+ radioifyVector(radioBurst,
+ (float *) (sendBuffer->begin() + sendCursor),
+ powerScaling, zeroBurst);
sendCursor += radioBurst.size();
@@ -195,7 +224,7 @@ void RadioInterface::driveReceiveRadio() {
GSM::Time rcvClock = mClock.get();
rcvClock.decTN(receiveOffset);
unsigned tN = rcvClock.TN();
- int rcvSz = rcvCursor;
+ int rcvSz = recvCursor;
int readSz = 0;
const int symbolsPerSlot = gSlotLen + 8;
@@ -204,7 +233,7 @@ void RadioInterface::driveReceiveRadio() {
// Using the 157-156-156-156 symbols per timeslot format.
while (rcvSz > (symbolsPerSlot + (tN % 4 == 0)) * sps) {
signalVector rxVector((symbolsPerSlot + (tN % 4 == 0)) * sps);
- unRadioifyVector(rcvBuffer+readSz*2,rxVector);
+ unRadioifyVector((float *) (recvBuffer->begin() + readSz), rxVector);
GSM::Time tmpTime = rcvClock;
if (rcvClock.FN() >= 0) {
//LOG(DEBUG) << "FN: " << rcvClock.FN();
@@ -221,8 +250,6 @@ void RadioInterface::driveReceiveRadio() {
}
mClock.incTN();
rcvClock.incTN();
- //if (mReceiveFIFO.size() >= 16) mReceiveFIFO.wait(8);
- //LOG(DEBUG) << "receiveFIFO: wrote radio vector at time: " << mClock.get() << ", new size: " << mReceiveFIFO.size() ;
readSz += (symbolsPerSlot+(tN % 4 == 0)) * sps;
rcvSz -= (symbolsPerSlot+(tN % 4 == 0)) * sps;
@@ -230,8 +257,11 @@ void RadioInterface::driveReceiveRadio() {
}
if (readSz > 0) {
- rcvCursor -= readSz;
- memmove(rcvBuffer,rcvBuffer+2*readSz,sizeof(float) * 2 * rcvCursor);
+ memmove(recvBuffer->begin(),
+ recvBuffer->begin() + readSz,
+ (recvCursor - readSz) * 2 * sizeof(float));
+
+ recvCursor -= readSz;
}
}
@@ -274,8 +304,8 @@ void RadioInterface::pullBuffer()
underrun |= local_underrun;
readTimestamp += (TIMESTAMP) num_rd;
- shortToFloat(rcvBuffer + 2 * rcvCursor, rx_buf, num_rd);
- rcvCursor += num_rd;
+ shortToFloat((float *) recvBuffer->begin() + recvCursor, rx_buf, num_rd);
+ recvCursor += num_rd;
}
/* Send timestamped chunk to the device with arbitrary size */
@@ -284,7 +314,7 @@ void RadioInterface::pushBuffer()
if (sendCursor < INCHUNK)
return;
- floatToShort(tx_buf, sendBuffer, sendCursor);
+ floatToShort(tx_buf, (float *) sendBuffer->begin(), sendCursor);
/* Write samples. Fail if we don't get what we want. */
int num_smpls = mRadio->writeSamples(tx_buf,
diff --git a/Transceiver52M/radioInterface.h b/Transceiver52M/radioInterface.h
index a64702b..690d572 100644
--- a/Transceiver52M/radioInterface.h
+++ b/Transceiver52M/radioInterface.h
@@ -39,12 +39,14 @@ protected:
RadioDevice *mRadio; ///< the USRP object
- float *sendBuffer;
+ signalVector *sendBuffer;
+ signalVector *recvBuffer;
unsigned sendCursor;
+ unsigned recvCursor;
+
+ short *convertRecvBuffer;
+ short *convertSendBuffer;
- float *rcvBuffer;
- unsigned rcvCursor;
-
bool underrun; ///< indicates writes to USRP are too slow
bool overrun; ///< indicates reads from USRP are too slow
TIMESTAMP writeTimestamp; ///< sample timestamp of next packet written to USRP
@@ -85,6 +87,10 @@ public:
/** start the interface */
void start();
+ /** intialization */
+ virtual bool init();
+ virtual void close();
+
/** constructor */
RadioInterface(RadioDevice* wRadio = NULL,
int receiveOffset = 3,
@@ -92,7 +98,7 @@ public:
GSM::Time wStartTime = GSM::Time(0));
/** destructor */
- ~RadioInterface();
+ virtual ~RadioInterface();
/** check for underrun, resets underrun value */
bool isUnderrun();
@@ -156,6 +162,10 @@ void *AlignRadioServiceLoopAdapter(RadioInterface*);
class RadioInterfaceResamp : public RadioInterface {
private:
+ signalVector *innerSendBuffer;
+ signalVector *outerSendBuffer;
+ signalVector *innerRecvBuffer;
+ signalVector *outerRecvBuffer;
void pushBuffer();
void pullBuffer();
@@ -166,4 +176,9 @@ public:
int receiveOffset = 3,
int wSPS = SAMPSPERSYM,
GSM::Time wStartTime = GSM::Time(0));
+
+ ~RadioInterfaceResamp();
+
+ bool init();
+ void close();
};
diff --git a/Transceiver52M/radioInterfaceResamp.cpp b/Transceiver52M/radioInterfaceResamp.cpp
index c7f17ea..d3dc82e 100644
--- a/Transceiver52M/radioInterfaceResamp.cpp
+++ b/Transceiver52M/radioInterfaceResamp.cpp
@@ -1,8 +1,8 @@
/*
* Radio device interface with sample rate conversion
- * Written by Thomas Tsou <ttsou@vt.edu>
+ * Written by Thomas Tsou <tom@tsou.cc>
*
- * Copyright 2011 Free Software Foundation, Inc.
+ * Copyright 2011, 2012, 2013 Free Software Foundation, Inc.
*
* 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
@@ -22,6 +22,12 @@
#include <radioInterface.h>
#include <Logger.h>
+#include "Resampler.h"
+
+extern "C" {
+#include "convert.h"
+}
+
/* New chunk sizes for resampled rate */
#ifdef INCHUNK
#undef INCHUNK
@@ -30,303 +36,194 @@
#undef OUTCHUNK
#endif
-/* Resampling parameters */
-#define INRATE 65 * SAMPSPERSYM
-#define INHISTORY INRATE * 2
-#define INCHUNK INRATE * 9
-
-#define OUTRATE 96 * SAMPSPERSYM
-#define OUTHISTORY OUTRATE * 2
-#define OUTCHUNK OUTRATE * 9
-
-/* Resampler low pass filters */
-signalVector *tx_lpf = 0;
-signalVector *rx_lpf = 0;
+/* Resampling parameters for 100 MHz clocking */
+#define RESAMP_INRATE 52
+#define RESAMP_OUTRATE 75
+#define RESAMP_FILT_LEN 16
-/* Resampler history */
-signalVector *tx_hist = 0;
-signalVector *rx_hist = 0;
+#define INCHUNK (RESAMP_INRATE * 4)
+#define OUTCHUNK (RESAMP_OUTRATE * 4)
-/* Resampler input buffer */
-signalVector *tx_vec = 0;
-signalVector *rx_vec = 0;
+static Resampler *upsampler = NULL;
+static Resampler *dnsampler = NULL;
+short *convertRecvBuffer = NULL;
+short *convertSendBuffer = NULL;
-/*
- * High rate (device facing) buffers
- *
- * Transmit side samples are pushed after each burst so accomodate
- * a resampled burst plus up to a chunk left over from the previous
- * resampling operation.
- *
- * Receive side samples always pulled with a fixed size.
- */
-short tx_buf[INCHUNK * 2 * 4];
-short rx_buf[OUTCHUNK * 2 * 2];
-
-/*
- * Utilities and Conversions
- *
- * Manipulate signal vectors dynamically for two reasons. For one,
- * it's simpler. And two, it doesn't make any reasonable difference
- * relative to the high overhead generated by the resampling.
- */
-
-/* Concatenate signal vectors. Deallocate input vectors. */
-signalVector *concat(signalVector *a, signalVector *b)
+/* Complex float to short conversion */
+static void floatToShort(short *out, float *in, int num)
{
- signalVector *vec = new signalVector(*a, *b);
- delete a;
- delete b;
-
- return vec;
+ for (int i = 0; i < num; i++)
+ out[i] = (short) in[i];
}
-/* Segment a signal vector. Deallocate the input vector. */
-signalVector *segment(signalVector *a, int indx, int sz)
+/* Complex short to float conversion */
+static void shortToFloat(float *out, short *in, int num)
{
- signalVector *vec = new signalVector(sz);
- a->segmentCopyTo(*vec, indx, sz);
- delete a;
-
- return vec;
+ for (int i = 0; i < num; i++)
+ out[i] = (float) in[i];
}
-/* Create a new signal vector from a short array. */
-signalVector *short_to_sigvec(short *smpls, size_t sz)
+RadioInterfaceResamp::RadioInterfaceResamp(RadioDevice *wRadio,
+ int wReceiveOffset,
+ int wSPS,
+ GSM::Time wStartTime)
+ : RadioInterface(wRadio, wReceiveOffset, wSPS, wStartTime),
+ innerSendBuffer(NULL), outerSendBuffer(NULL),
+ innerRecvBuffer(NULL), outerRecvBuffer(NULL)
{
- int i;
- signalVector *vec = new signalVector(sz);
- signalVector::iterator itr = vec->begin();
-
- for (i = 0; i < sz; i++) {
- *itr++ = Complex<float>(smpls[2 * i + 0], smpls[2 * i + 1]);
- }
-
- return vec;
}
-/* Convert and deallocate a signal vector into a short array. */
-int sigvec_to_short(signalVector *vec, short *smpls)
+RadioInterfaceResamp::~RadioInterfaceResamp()
{
- int i;
- signalVector::iterator itr = vec->begin();
-
- for (i = 0; i < vec->size(); i++) {
- smpls[2 * i + 0] = itr->real();
- smpls[2 * i + 1] = itr->imag();
- itr++;
- }
- delete vec;
-
- return i;
+ close();
}
-/* Create a new signal vector from a float array. */
-signalVector *float_to_sigvec(float *smpls, int sz)
+void RadioInterfaceResamp::close()
{
- int i;
- signalVector *vec = new signalVector(sz);
- signalVector::iterator itr = vec->begin();
+ RadioInterface::close();
- for (i = 0; i < sz; i++) {
- *itr++ = Complex<float>(smpls[2 * i + 0], smpls[2 * i + 1]);
- }
-
- return vec;
-}
+ delete innerSendBuffer;
+ delete outerSendBuffer;
+ delete innerRecvBuffer;
+ delete outerRecvBuffer;
-/* Convert and deallocate a signal vector into a float array. */
-int sigvec_to_float(signalVector *vec, float *smpls)
-{
- int i;
- signalVector::iterator itr = vec->begin();
+ delete upsampler;
+ delete dnsampler;
- for (i = 0; i < vec->size(); i++) {
- smpls[2 * i + 0] = itr->real();
- smpls[2 * i + 1] = itr->imag();
- itr++;
- }
- delete vec;
+ innerSendBuffer = NULL;
+ outerSendBuffer = NULL;
+ innerRecvBuffer = NULL;
+ outerRecvBuffer = NULL;
- return i;
+ upsampler = NULL;
+ dnsampler = NULL;
}
-/* Initialize resampling signal vectors */
-void init_resampler(signalVector **lpf,
- signalVector **buf,
- signalVector **hist,
- int tx)
+/* Initialize I/O specific objects */
+bool RadioInterfaceResamp::init()
{
- int P, Q, taps, hist_len;
- float cutoff_freq;
-
- if (tx) {
- LOG(INFO) << "Initializing Tx resampler";
- P = OUTRATE;
- Q = INRATE;
- taps = 651;
- hist_len = INHISTORY;
- } else {
- LOG(INFO) << "Initializing Rx resampler";
- P = INRATE;
- Q = OUTRATE;
- taps = 961;
- hist_len = OUTHISTORY;
+ float cutoff = 1.0f;
+
+ close();
+
+ /*
+ * With oversampling, restrict bandwidth to 150% of base rate. This also
+ * provides last ditch bandwith limiting if the pulse shaping filter is
+ * insufficient.
+ */
+ if (sps > 1)
+ cutoff = 1.5 / sps;
+
+ dnsampler = new Resampler(RESAMP_INRATE, RESAMP_OUTRATE);
+ if (!dnsampler->init(cutoff)) {
+ LOG(ALERT) << "Rx resampler failed to initialize";
+ return false;
}
- if (!*lpf) {
- cutoff_freq = (P < Q) ? (1.0/(float) Q) : (1.0/(float) P);
- *lpf = createLPF(cutoff_freq, taps, P);
+ upsampler = new Resampler(RESAMP_OUTRATE, RESAMP_INRATE);
+ if (!upsampler->init(cutoff)) {
+ LOG(ALERT) << "Tx resampler failed to initialize";
+ return false;
}
- if (!*buf) {
- *buf = new signalVector();
- }
+ /*
+ * 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.
+ */
+ innerSendBuffer = new signalVector(INCHUNK * 20, RESAMP_FILT_LEN);
+ outerSendBuffer = new signalVector(OUTCHUNK * 20);
- if (!*hist);
- *hist = new signalVector(hist_len);
-}
+ outerRecvBuffer = new signalVector(OUTCHUNK * 2, RESAMP_FILT_LEN);
+ innerRecvBuffer = new signalVector(INCHUNK * 20);
-/* Resample a signal vector
- *
- * The input vector is deallocated and the pointer returned with a vector
- * of any unconverted samples.
- */
-signalVector *resmpl_sigvec(signalVector *hist, signalVector **vec,
- signalVector *lpf, double in_rate,
- double out_rate, int chunk_sz)
-{
- signalVector *resamp_vec;
- int num_chunks = (*vec)->size() / chunk_sz;
-
- /* Truncate to a chunk multiple */
- signalVector trunc_vec(num_chunks * chunk_sz);
- (*vec)->segmentCopyTo(trunc_vec, 0, num_chunks * chunk_sz);
-
- /* Update sample buffer with remainder */
- *vec = segment(*vec, trunc_vec.size(), (*vec)->size() - trunc_vec.size());
-
- /* Add history and resample */
- signalVector input_vec(*hist, trunc_vec);
- resamp_vec = polyphaseResampleVector(input_vec, in_rate,
- out_rate, lpf);
-
- /* Update history */
- trunc_vec.segmentCopyTo(*hist, trunc_vec.size() - hist->size(),
- hist->size());
- return resamp_vec;
-}
-
-/* Wrapper for receive-side integer-to-float array resampling */
- int rx_resmpl_int_flt(float *smpls_out, short *smpls_in, int num_smpls)
-{
- int num_resmpld, num_chunks;
- signalVector *convert_vec, *resamp_vec, *trunc_vec;
-
- if (!rx_lpf || !rx_vec || !rx_hist)
- init_resampler(&rx_lpf, &rx_vec, &rx_hist, false);
-
- /* Convert and add samples to the receive buffer */
- convert_vec = short_to_sigvec(smpls_in, num_smpls);
- rx_vec = concat(rx_vec, convert_vec);
-
- num_chunks = rx_vec->size() / OUTCHUNK;
- if (num_chunks < 1)
- return 0;
-
- /* Resample */
- resamp_vec = resmpl_sigvec(rx_hist, &rx_vec, rx_lpf,
- INRATE, OUTRATE, OUTCHUNK);
- /* Truncate */
- trunc_vec = segment(resamp_vec, INHISTORY,
- resamp_vec->size() - INHISTORY);
- /* Convert */
- num_resmpld = sigvec_to_float(trunc_vec, smpls_out);
-
- return num_resmpld;
-}
-
-/* Wrapper for transmit-side float-to-int array resampling */
-int tx_resmpl_flt_int(short *smpls_out, float *smpls_in, int num_smpls)
-{
- int num_resmpl, num_chunks;
- signalVector *convert_vec, *resamp_vec;
-
- if (!tx_lpf || !tx_vec || !tx_hist)
- init_resampler(&tx_lpf, &tx_vec, &tx_hist, true);
-
- /* Convert and add samples to the transmit buffer */
- convert_vec = float_to_sigvec(smpls_in, num_smpls);
- tx_vec = concat(tx_vec, convert_vec);
-
- num_chunks = tx_vec->size() / INCHUNK;
- if (num_chunks < 1)
- return 0;
-
- /* Resample and convert to an integer array */
- resamp_vec = resmpl_sigvec(tx_hist, &tx_vec, tx_lpf,
- OUTRATE, INRATE, INCHUNK);
- num_resmpl = sigvec_to_short(resamp_vec, smpls_out);
+ convertSendBuffer = new short[OUTCHUNK * 2 * 20];
+ convertRecvBuffer = new short[OUTCHUNK * 2 * 2];
- return num_resmpl;
-}
+ sendBuffer = innerSendBuffer;
+ recvBuffer = innerRecvBuffer;
-RadioInterfaceResamp::RadioInterfaceResamp(RadioDevice *wRadio,
- int wReceiveOffset,
- int wSPS,
- GSM::Time wStartTime)
- : RadioInterface(wRadio, wReceiveOffset, wSPS, wStartTime)
-{
+ return true;
}
-/* Receive a timestamped chunk from the device */
+/* Receive a timestamped chunk from the device */
void RadioInterfaceResamp::pullBuffer()
{
- int num_cv, num_rd;
bool local_underrun;
+ int rc, num_recv;
+ int inner_len = INCHUNK;
+ int outer_len = OUTCHUNK;
+
+ /* Outer buffer access size is fixed */
+ num_recv = mRadio->readSamples(convertRecvBuffer,
+ outer_len,
+ &overrun,
+ readTimestamp,
+ &local_underrun);
+ if (num_recv != outer_len) {
+ LOG(ALERT) << "Receive error " << num_recv;
+ return;
+ }
- /* Read samples. Fail if we don't get what we want. */
- num_rd = mRadio->readSamples(rx_buf, OUTCHUNK, &overrun,
- readTimestamp, &local_underrun);
-
- LOG(DEBUG) << "Rx read " << num_rd << " samples from device";
- assert(num_rd == OUTCHUNK);
+ shortToFloat((float *) outerRecvBuffer->begin(),
+ convertRecvBuffer, 2 * outer_len);
underrun |= local_underrun;
- readTimestamp += (TIMESTAMP) num_rd;
-
- /* Convert and resample */
- num_cv = rx_resmpl_int_flt(rcvBuffer + 2 * rcvCursor,
- rx_buf, num_rd);
-
- LOG(DEBUG) << "Rx read " << num_cv << " samples from resampler";
+ readTimestamp += (TIMESTAMP) num_recv;
+
+ /* Write to the end of the inner receive buffer */
+ rc = dnsampler->rotate((float *) outerRecvBuffer->begin(), outer_len,
+ (float *) (innerRecvBuffer->begin() + recvCursor),
+ inner_len);
+ if (rc < 0) {
+ LOG(ALERT) << "Sample rate upsampling error";
+ }
- rcvCursor += num_cv;
+ recvCursor += inner_len;
}
-/* Send a timestamped chunk to the device */
+/* Send a timestamped chunk to the device */
void RadioInterfaceResamp::pushBuffer()
{
- int num_cv, num_wr;
+ int rc, chunks, num_sent;
+ int inner_len, outer_len;
if (sendCursor < INCHUNK)
return;
- LOG(DEBUG) << "Tx wrote " << sendCursor << " samples to resampler";
+ chunks = sendCursor / INCHUNK;
+ if (chunks > 8)
+ chunks = 8;
- /* Resample and convert */
- num_cv = tx_resmpl_flt_int(tx_buf, sendBuffer, sendCursor);
- assert(num_cv > sendCursor);
+ inner_len = chunks * INCHUNK;
+ outer_len = chunks * OUTCHUNK;
- /* Write samples. Fail if we don't get what we want. */
- num_wr = mRadio->writeSamples(tx_buf + OUTHISTORY * 2,
- num_cv - OUTHISTORY,
- &underrun,
- writeTimestamp);
+ /* Always send from the beginning of the buffer */
+ rc = upsampler->rotate((float *) innerSendBuffer->begin(), inner_len,
+ (float *) outerSendBuffer->begin(), outer_len);
+ if (rc < 0) {
+ LOG(ALERT) << "Sample rate downsampling error";
+ }
+
+ floatToShort(convertSendBuffer,
+ (float *) outerSendBuffer->begin(),
+ 2 * outer_len);
+
+ num_sent = mRadio->writeSamples(convertSendBuffer,
+ outer_len,
+ &underrun,
+ writeTimestamp);
+ if (num_sent != outer_len) {
+ LOG(ALERT) << "Transmit error " << num_sent;
+ }
- LOG(DEBUG) << "Tx wrote " << num_wr << " samples to device";
- assert(num_wr == num_wr);
+ /* Shift remaining samples to beginning of buffer */
+ memmove(innerSendBuffer->begin(),
+ innerSendBuffer->begin() + inner_len,
+ (sendCursor - inner_len) * 2 * sizeof(float));
- writeTimestamp += (TIMESTAMP) num_wr;
- sendCursor = 0;
+ writeTimestamp += outer_len;
+ sendCursor -= inner_len;
+ assert(sendCursor >= 0);
}
diff --git a/Transceiver52M/runTransceiver.cpp b/Transceiver52M/runTransceiver.cpp
index f268752..5401c22 100644
--- a/Transceiver52M/runTransceiver.cpp
+++ b/Transceiver52M/runTransceiver.cpp
@@ -157,6 +157,9 @@ int main(int argc, char *argv[])
LOG(ALERT) << "Unsupported configuration";
return EXIT_FAILURE;
}
+ if (!radio->init()) {
+ LOG(ALERT) << "Failed to initialize radio interface";
+ }
Transceiver *trx = new Transceiver(trxPort, trxAddr.c_str(),
SAMPSPERSYM, GSM::Time(3,0), radio);
diff --git a/Transceiver52M/sigProcLib.cpp b/Transceiver52M/sigProcLib.cpp
index 8237aa5..a647f84 100644
--- a/Transceiver52M/sigProcLib.cpp
+++ b/Transceiver52M/sigProcLib.cpp
@@ -1128,195 +1128,7 @@ SoftVector *demodulateBurst(signalVector &rxBurst, int sps,
return burstBits;
}
-
-
-// 1.0 is sampling frequency
-// must satisfy cutoffFreq > 1/filterLen
-signalVector *createLPF(float cutoffFreq,
- int filterLen,
- float gainDC)
-{
-#if 0
- signalVector *LPF = new signalVector(filterLen-1);
- LPF->isRealOnly(true);
- LPF->setSymmetry(ABSSYM);
- signalVector::iterator itr = LPF->begin();
- double sum = 0.0;
- for (int i = 1; i < filterLen; i++) {
- float ys = sinc(M_2PI_F*cutoffFreq*((float)i-(float)(filterLen)/2.0F));
- float yg = 4.0F * cutoffFreq;
- // Blackman -- less brickwall (sloping transition) but larger stopband attenuation
- float yw = 0.42 - 0.5*cos(((float)i)*M_2PI_F/(float)(filterLen)) + 0.08*cos(((float)i)*2*M_2PI_F/(float)(filterLen));
- // Hamming -- more brickwall with smaller stopband attenuation
- //float yw = 0.53836F - 0.46164F * cos(((float)i)*M_2PI_F/(float)(filterLen+1));
- *itr++ = (complex) ys*yg*yw;
- sum += ys*yg*yw;
- }
-#else
- double sum = 0.0;
- signalVector *LPF;
- signalVector::iterator itr;
- if (filterLen == 651) { // receive LPF
- LPF = new signalVector(651);
- LPF->isRealOnly(true);
- itr = LPF->begin();
- for (int i = 0; i < filterLen; i++) {
- *itr++ = complex(rcvLPF_651[i],0.0);
- sum += rcvLPF_651[i];
- }
- }
- else {
- LPF = new signalVector(961);
- LPF->isRealOnly(true);
- itr = LPF->begin();
- for (int i = 0; i < filterLen; i++) {
- *itr++ = complex(sendLPF_961[i],0.0);
- sum += sendLPF_961[i];
- }
- }
-#endif
-
- float normFactor = gainDC/sum; //sqrtf(gainDC/vectorNorm2(*LPF));
- // normalize power
- itr = LPF->begin();
- for (int i = 0; i < filterLen; i++) {
- *itr = *itr*normFactor;
- itr++;
- }
- return LPF;
-
-}
-
-
-#define POLYPHASESPAN 10
-
-// assumes filter group delay is 0.5*(length of filter)
-signalVector *polyphaseResampleVector(signalVector &wVector,
- int P, int Q,
- signalVector *LPF)
-
-{
-
- bool deleteLPF = false;
-
- if (LPF==NULL) {
- float cutoffFreq = (P < Q) ? (1.0/(float) Q) : (1.0/(float) P);
- LPF = createLPF(cutoffFreq/3.0,100*POLYPHASESPAN+1,Q);
- deleteLPF = true;
- }
-
- signalVector *resampledVector = new signalVector((int) ceil(wVector.size()*(float) P / (float) Q));
- resampledVector->fill(0);
- resampledVector->isRealOnly(wVector.isRealOnly());
- signalVector::iterator newItr = resampledVector->begin();
-
- //FIXME: need to update for real-only vectors
- int outputIx = (LPF->size()+1)/2/Q; //((P > Q) ? P : Q);
- while (newItr < resampledVector->end()) {
- int outputBranch = (outputIx*Q) % P;
- int inputOffset = (outputIx*Q - outputBranch)/P;
- signalVector::const_iterator inputItr = wVector.begin() + inputOffset;
- signalVector::const_iterator filtItr = LPF->begin() + outputBranch;
- while (inputItr >= wVector.end()) {
- inputItr--;
- filtItr+=P;
- }
- complex sum = 0.0;
- if ((LPF->getSymmetry()!=ABSSYM) || (P>1)) {
- if (!LPF->isRealOnly()) {
- while ( (inputItr >= wVector.begin()) && (filtItr < LPF->end()) ) {
- sum += (*inputItr)*(*filtItr);
- inputItr--;
- filtItr += P;
- }
- }
- else {
- while ( (inputItr >= wVector.begin()) && (filtItr < LPF->end()) ) {
- sum += (*inputItr)*(filtItr->real());
- inputItr--;
- filtItr += P;
- }
- }
- }
- else {
- signalVector::const_iterator revInputItr = inputItr- LPF->size() + 1;
- signalVector::const_iterator filtMidpoint = LPF->begin()+(LPF->size()-1)/2;
- if (!LPF->isRealOnly()) {
- while (filtItr <= filtMidpoint) {
- if (inputItr < revInputItr) break;
- if (inputItr == revInputItr)
- sum += (*inputItr)*(*filtItr);
- else if ( (inputItr < wVector.end()) && (revInputItr >= wVector.begin()) )
- sum += (*inputItr + *revInputItr)*(*filtItr);
- else if ( inputItr < wVector.end() )
- sum += (*inputItr)*(*filtItr);
- else if ( revInputItr >= wVector.begin() )
- sum += (*revInputItr)*(*filtItr);
- inputItr--;
- revInputItr++;
- filtItr++;
- }
- }
- else {
- while (filtItr <= filtMidpoint) {
- if (inputItr < revInputItr) break;
- if (inputItr == revInputItr)
- sum += (*inputItr)*(filtItr->real());
- else if ( (inputItr < wVector.end()) && (revInputItr >= wVector.begin()) )
- sum += (*inputItr + *revInputItr)*(filtItr->real());
- else if ( inputItr < wVector.end() )
- sum += (*inputItr)*(filtItr->real());
- else if ( revInputItr >= wVector.begin() )
- sum += (*revInputItr)*(filtItr->real());
- inputItr--;
- revInputItr++;
- filtItr++;
- }
- }
- }
- *newItr = sum;
- newItr++;
- outputIx++;
- }
-
- if (deleteLPF) delete LPF;
-
- return resampledVector;
-}
-
-
-signalVector *resampleVector(signalVector &wVector,
- float expFactor,
- complex endPoint)
-
-{
-
- if (expFactor < 1.0) return NULL;
-
- signalVector *retVec = new signalVector((int) ceil(wVector.size()*expFactor));
-
- float t = 0.0;
-
- signalVector::iterator retItr = retVec->begin();
- while (retItr < retVec->end()) {
- unsigned tLow = (unsigned int) floor(t);
- unsigned tHigh = tLow + 1;
- if (tLow > wVector.size()-1) break;
- if (tHigh > wVector.size()) break;
- complex lowPoint = wVector[tLow];
- complex highPoint = (tHigh == wVector.size()) ? endPoint : wVector[tHigh];
- complex a = (tHigh-t);
- complex b = (t-tLow);
- *retItr = (a*lowPoint + b*highPoint);
- t += 1.0/expFactor;
- }
-
- return retVec;
-
-}
-
-
// Assumes symbol-spaced sampling!!!
// Based upon paper by Al-Dhahir and Cioffi
bool designDFE(signalVector &channelResponse,
diff --git a/Transceiver52M/sigProcLib.h b/Transceiver52M/sigProcLib.h
index 194002f..109ffa8 100644
--- a/Transceiver52M/sigProcLib.h
+++ b/Transceiver52M/sigProcLib.h
@@ -333,40 +333,6 @@ SoftVector *demodulateBurst(signalVector &rxBurst, int sps,
complex channel, float TOA);
/**
- Creates a simple Kaiser-windowed low-pass FIR filter.
- @param cutoffFreq The digital 3dB bandwidth of the filter.
- @param filterLen The number of taps in the filter.
- @param gainDC The DC gain of the filter.
- @return The desired LPF
-*/
-signalVector *createLPF(float cutoffFreq,
- int filterLen,
- float gainDC = 1.0);
-
-/**
- Change sampling rate of a vector via polyphase resampling.
- @param wVector The vector to be resampled.
- @param P The numerator, i.e. the amount of upsampling.
- @param Q The denominator, i.e. the amount of downsampling.
- @param LPF An optional low-pass filter used in the resampling process.
- @return A vector resampled at P/Q of the original sampling rate.
-*/
-signalVector *polyphaseResampleVector(signalVector &wVector,
- int P, int Q,
- signalVector *LPF);
-
-/**
- Change the sampling rate of a vector via linear interpolation.
- @param wVector The vector to be resampled.
- @param expFactor Ratio of new sampling rate/original sampling rate.
- @param endPoint ???
- @return A vector resampled a expFactor*original sampling rate.
-*/
-signalVector *resampleVector(signalVector &wVector,
- float expFactor,
- complex endPoint);
-
-/**
Design the necessary filters for a decision-feedback equalizer.
@param channelResponse The multipath channel that we're mitigating.
@param SNRestimate The signal-to-noise estimate of the channel, a linear value