aboutsummaryrefslogtreecommitdiffstats
path: root/Transceiver52M
diff options
context:
space:
mode:
authorkurtis.heimerl <kurtis.heimerl@19bc5d8c-e614-43d4-8b26-e1612bc8e597>2011-11-26 03:18:39 +0000
committerkurtis.heimerl <kurtis.heimerl@19bc5d8c-e614-43d4-8b26-e1612bc8e597>2011-11-26 03:18:39 +0000
commitce31733860392debd9c9d0a6c93eb52ab47da0d8 (patch)
tree68a467239f5b53f8710514dcb3ce981ba6b926ee /Transceiver52M
parent513a6291833a589ac7b8fc791b051131d1b8f158 (diff)
transceiver: add option for host based resampling
The resampling transceiver is unified with the 52MHz version. The option to resample 400ksps from the device to a GSM appropriate 270.833ksps is enabled at compile time with the following option. ./configure --with-resamp Signed-off-by: Thomas Tsou <ttsou@vt.edu> git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@2672 19bc5d8c-e614-43d4-8b26-e1612bc8e597
Diffstat (limited to 'Transceiver52M')
-rw-r--r--Transceiver52M/UHDDevice.cpp16
-rw-r--r--Transceiver52M/radioIOResamp.cpp316
2 files changed, 330 insertions, 2 deletions
diff --git a/Transceiver52M/UHDDevice.cpp b/Transceiver52M/UHDDevice.cpp
index 37a3b96..cc90d73 100644
--- a/Transceiver52M/UHDDevice.cpp
+++ b/Transceiver52M/UHDDevice.cpp
@@ -25,10 +25,15 @@
#include <uhd/usrp/single_usrp.hpp>
#include <uhd/utils/thread_priority.hpp>
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
/*
use_ext_ref - Enable external 10MHz clock reference
- master_clk_rt - Master clock frequency
+ master_clk_rt - Master clock frequency - ignored if host resampling is
+ enabled
rx_smpl_offset - Timing correction in seconds between receive and
transmit timestamps. This value corrects for delays on
@@ -41,10 +46,15 @@
*/
const bool use_ext_ref = false;
const double master_clk_rt = 52e6;
-const double rx_smpl_offset = .0000869;
const size_t smpl_buf_sz = (1 << 20);
const float tx_ampl = .3;
+#ifdef RESAMPLE
+const double rx_smpl_offset = .00005;
+#else
+const double rx_smpl_offset = .0000869;
+#endif
+
/** Timestamp conversion
@param timestamp a UHD or OpenBTS timestamp
@param rate sample rate
@@ -290,6 +300,7 @@ double uhd_device::set_rates(double rate)
{
double actual_rt, actual_clk_rt;
+#ifndef RESAMPLE
// Set master clock rate
usrp_dev->set_master_clock_rate(master_clk_rt);
actual_clk_rt = usrp_dev->get_master_clock_rate();
@@ -298,6 +309,7 @@ double uhd_device::set_rates(double rate)
LOG(ERROR) << "Failed to set master clock rate";
return -1.0;
}
+#endif
// Set sample rates
usrp_dev->set_tx_rate(rate);
diff --git a/Transceiver52M/radioIOResamp.cpp b/Transceiver52M/radioIOResamp.cpp
new file mode 100644
index 0000000..134576c
--- /dev/null
+++ b/Transceiver52M/radioIOResamp.cpp
@@ -0,0 +1,316 @@
+/*
+ * Radio device interface with sample rate conversion
+ * Written by Thomas Tsou <ttsou@vt.edu>
+ *
+ * Copyright 2011 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
+ * 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>
+
+/* New chunk sizes for resampled rate */
+#ifdef INCHUNK
+ #undef INCHUNK
+#endif
+#ifdef OUTCHUNK
+ #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;
+
+/* Resampler history */
+signalVector *tx_hist = 0;
+signalVector *rx_hist = 0;
+
+/* Resampler input buffer */
+signalVector *tx_vec = 0;
+signalVector *rx_vec = 0;
+
+/* High rate (device facing) buffers */
+short tx_buf[INCHUNK * 2 * 2];
+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)
+{
+ signalVector *vec = new signalVector(*a, *b);
+ delete a;
+ delete b;
+
+ return vec;
+}
+
+/* Segment a signal vector. Deallocate the input vector. */
+signalVector *segment(signalVector *a, int indx, int sz)
+{
+ signalVector *vec = new signalVector(sz);
+ a->segmentCopyTo(*vec, indx, sz);
+ delete a;
+
+ return vec;
+}
+
+/* Create a new signal vector from a short array. */
+signalVector *short_to_sigvec(short *smpls, size_t sz)
+{
+ 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)
+{
+ 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;
+}
+
+/* Create a new signal vector from a float array. */
+signalVector *float_to_sigvec(float *smpls, int sz)
+{
+ 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 float array. */
+int sigvec_to_float(signalVector *vec, float *smpls)
+{
+ 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;
+}
+
+/* Initialize resampling signal vectors */
+void init_resampler(signalVector **lpf,
+ signalVector **buf,
+ signalVector **hist,
+ int tx)
+{
+ 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;
+ }
+
+ if (!*lpf) {
+ cutoff_freq = (P < Q) ? (1.0/(float) Q) : (1.0/(float) P);
+ *lpf = createLPF(cutoff_freq, taps, P);
+ }
+
+ if (!*buf) {
+ *buf = new signalVector();
+ }
+
+ if (!*hist);
+ *hist = new signalVector(hist_len);
+}
+
+/* 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);
+
+ return num_resmpl;
+}
+
+/* Receive a timestamped chunk from the device */
+void RadioInterface::pullBuffer()
+{
+ int num_cv, num_rd;
+ bool local_underrun;
+
+ /* Read samples. Fail if we don't get what we want. */
+ num_rd = mRadio->readSamples(rx_buf, OUTCHUNK, &overrun,
+ readTimestamp, &local_underrun);
+
+ LOG(DEEPDEBUG) << "Rx read " << num_rd << " samples from device";
+ assert(num_rd == OUTCHUNK);
+
+ underrun |= local_underrun;
+ readTimestamp += (TIMESTAMP) num_rd;
+
+ /* Convert and resample */
+ num_cv = rx_resmpl_int_flt(rcvBuffer + 2 * rcvCursor,
+ rx_buf, num_rd);
+
+ LOG(DEEPDEBUG) << "Rx read " << num_cv << " samples from resampler";
+
+ rcvCursor += num_cv;
+}
+
+/* Send a timestamped chunk to the device */
+void RadioInterface::pushBuffer()
+{
+ int num_cv, num_wr;
+
+ if (sendCursor < INCHUNK)
+ return;
+
+ LOG(DEEPDEBUG) << "Tx wrote " << sendCursor << " samples to resampler";
+
+ /* Resample and convert */
+ num_cv = tx_resmpl_flt_int(tx_buf, sendBuffer, sendCursor);
+ assert(num_cv > sendCursor);
+
+ /* Write samples. Fail if we don't get what we want. */
+ num_wr = mRadio->writeSamples(tx_buf + OUTHISTORY * 2,
+ num_cv - OUTHISTORY,
+ &underrun,
+ writeTimestamp);
+
+ LOG(DEEPDEBUG) << "Tx wrote " << num_wr << " samples to device";
+ assert(num_wr == num_wr);
+
+ writeTimestamp += (TIMESTAMP) num_wr;
+ sendCursor = 0;
+}