From 03669856b74bd1e82a3986a5e05154fec88942ac Mon Sep 17 00:00:00 2001 From: Thomas Tsou Date: Mon, 8 Apr 2013 14:41:11 -0400 Subject: Transceiver52M: Set resampling option automatically based on device Remove the built time resampling selection and link both options. Move the normal push/pullBuffer() calls back to the base class and overload them in the inherited resampling class. USRP2/N2xx devices are the only devices that require resampling so return that resampling is necessary on the device open(), which is the point at which the device type will be known. Signed-off-by: Thomas Tsou --- Transceiver52M/Makefile.am | 8 +- Transceiver52M/UHDDevice.cpp | 17 +- Transceiver52M/USRPDevice.cpp | 12 +- Transceiver52M/USRPDevice.h | 2 +- Transceiver52M/radioDevice.h | 5 +- Transceiver52M/radioIO.cpp | 91 --------- Transceiver52M/radioIOResamp.cpp | 324 ------------------------------- Transceiver52M/radioInterface.cpp | 60 ++++++ Transceiver52M/radioInterface.h | 23 ++- Transceiver52M/radioInterfaceResamp.cpp | 332 ++++++++++++++++++++++++++++++++ Transceiver52M/runTransceiver.cpp | 17 +- configure.ac | 10 - 12 files changed, 449 insertions(+), 452 deletions(-) delete mode 100644 Transceiver52M/radioIO.cpp delete mode 100644 Transceiver52M/radioIOResamp.cpp create mode 100644 Transceiver52M/radioInterfaceResamp.cpp diff --git a/Transceiver52M/Makefile.am b/Transceiver52M/Makefile.am index 7bdc181..6fcef7c 100644 --- a/Transceiver52M/Makefile.am +++ b/Transceiver52M/Makefile.am @@ -55,15 +55,9 @@ COMMON_SOURCES = \ Transceiver.cpp \ DummyLoad.cpp -if RESAMPLE libtransceiver_la_SOURCES = \ $(COMMON_SOURCES) \ - radioIOResamp.cpp -else -libtransceiver_la_SOURCES = \ - $(COMMON_SOURCES) \ - radioIO.cpp -endif + radioInterfaceResamp.cpp noinst_PROGRAMS = \ USRPping \ diff --git a/Transceiver52M/UHDDevice.cpp b/Transceiver52M/UHDDevice.cpp index 8ef03bc..890fd20 100644 --- a/Transceiver52M/UHDDevice.cpp +++ b/Transceiver52M/UHDDevice.cpp @@ -213,7 +213,7 @@ public: uhd_device(int sps, bool skip_rx); ~uhd_device(); - bool open(const std::string &args); + int open(const std::string &args); bool start(); bool stop(); void restart(uhd::time_spec_t ts); @@ -513,7 +513,7 @@ bool uhd_device::parse_dev_type() return true; } -bool uhd_device::open(const std::string &args) +int uhd_device::open(const std::string &args) { // Register msg handler uhd::msg::register_handler(&uhd_msg_handler); @@ -523,7 +523,7 @@ bool uhd_device::open(const std::string &args) uhd::device_addrs_t dev_addrs = uhd::device::find(addr); if (dev_addrs.size() == 0) { LOG(ALERT) << "No UHD devices found with address '" << args << "'"; - return false; + return -1; } // Use the first found device @@ -532,12 +532,12 @@ bool uhd_device::open(const std::string &args) usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]); } catch(...) { LOG(ALERT) << "UHD make failed, device " << dev_addrs[0].to_string(); - return false; + return -1; } // Check for a valid device type and set bus type if (!parse_dev_type()) - return false; + return -1; #ifdef EXTREF set_ref_clk(true); @@ -562,7 +562,7 @@ bool uhd_device::open(const std::string &args) // Set rates desired_smpl_rt = select_rate(dev_type, sps); if (set_rates(desired_smpl_rt) < 0) - return false; + return -1; // Create receive buffer size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t); @@ -583,7 +583,10 @@ bool uhd_device::open(const std::string &args) // Print configuration LOG(INFO) << "\n" << usrp_dev->get_pp_string(); - return true; + if (dev_type == USRP2) + return RESAMP; + + return NORMAL; } bool uhd_device::flush_recv(size_t num_pkts) diff --git a/Transceiver52M/USRPDevice.cpp b/Transceiver52M/USRPDevice.cpp index cb933a3..5c99003 100644 --- a/Transceiver52M/USRPDevice.cpp +++ b/Transceiver52M/USRPDevice.cpp @@ -75,7 +75,7 @@ USRPDevice::USRPDevice(int sps, bool skipRx) #endif } -bool USRPDevice::open(const std::string &) +int USRPDevice::open(const std::string &) { writeLock.unlock(); @@ -97,7 +97,7 @@ bool USRPDevice::open(const std::string &) catch(...) { LOG(ALERT) << "make failed on Rx"; m_uRx.reset(); - return false; + return -1; } if (m_uRx->fpga_master_clock_freq() != masterClockRate) @@ -105,7 +105,7 @@ bool USRPDevice::open(const std::string &) LOG(ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq() << ", desired clock freq = " << masterClockRate; m_uRx.reset(); - return false; + return -1; } } @@ -120,7 +120,7 @@ bool USRPDevice::open(const std::string &) catch(...) { LOG(ALERT) << "make failed on Tx"; m_uTx.reset(); - return false; + return -1; } if (m_uTx->fpga_master_clock_freq() != masterClockRate) @@ -128,7 +128,7 @@ bool USRPDevice::open(const std::string &) LOG(ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq() << ", desired clock freq = " << masterClockRate; m_uTx.reset(); - return false; + return -1; } if (!skipRx) m_uRx->stop(); @@ -165,7 +165,7 @@ bool USRPDevice::open(const std::string &) samplesWritten = 0; started = false; - return true; + return NORMAL; } diff --git a/Transceiver52M/USRPDevice.h b/Transceiver52M/USRPDevice.h index 1417beb..f5bc939 100644 --- a/Transceiver52M/USRPDevice.h +++ b/Transceiver52M/USRPDevice.h @@ -115,7 +115,7 @@ private: USRPDevice(int sps, bool skipRx); /** Instantiate the USRP */ - bool open(const std::string &); + int open(const std::string &); /** Start the USRP */ bool start(); diff --git a/Transceiver52M/radioDevice.h b/Transceiver52M/radioDevice.h index d2308d5..b796ffd 100644 --- a/Transceiver52M/radioDevice.h +++ b/Transceiver52M/radioDevice.h @@ -33,10 +33,13 @@ class RadioDevice { /* Available transport bus types */ enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED }; + /* Radio interface types */ + enum RadioInterfaceType { NORMAL, RESAMP }; + static RadioDevice *make(int sps, bool skipRx = false); /** Initialize the USRP */ - virtual bool open(const std::string &args)=0; + virtual int open(const std::string &args)=0; /** Start the USRP */ virtual bool start()=0; diff --git a/Transceiver52M/radioIO.cpp b/Transceiver52M/radioIO.cpp deleted file mode 100644 index 9956e87..0000000 --- a/Transceiver52M/radioIO.cpp +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Radio device I/O interface - * Written by Thomas Tsou - * - * 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 . - * See the COPYING file in the main directory for details. - */ - -#include -#include - -/* Device side buffers */ -static short rx_buf[OUTCHUNK * 2 * 2]; -static short tx_buf[INCHUNK * 2 * 2]; - -/* Complex float to short conversion */ -static int float_to_short(short *shrt_out, float *flt_in, int num) -{ - int i; - - for (i = 0; i < num; i++) { - shrt_out[2 * i + 0] = flt_in[2 * i + 0]; - shrt_out[2 * i + 1] = flt_in[2 * i + 1]; - } - - return i; -} - -/* Comlpex short to float conversion */ -static int short_to_float(float *flt_out, short *shrt_in, int num) -{ - int i; - - for (i = 0; i < num; i++) { - flt_out[2 * i + 0] = shrt_in[2 * i + 0]; - flt_out[2 * i + 1] = shrt_in[2 * i + 1]; - } - - return i; -} - -/* Receive a timestamped chunk from the device */ -void RadioInterface::pullBuffer() -{ - bool local_underrun; - - /* Read samples. Fail if we don't get what we want. */ - int num_rd = mRadio->readSamples(rx_buf, OUTCHUNK, &overrun, - readTimestamp, &local_underrun); - - LOG(DEBUG) << "Rx read " << num_rd << " samples from device"; - assert(num_rd == OUTCHUNK); - - underrun |= local_underrun; - readTimestamp += (TIMESTAMP) num_rd; - - short_to_float(rcvBuffer + 2 * rcvCursor, rx_buf, num_rd); - rcvCursor += num_rd; -} - -/* Send timestamped chunk to the device with arbitrary size */ -void RadioInterface::pushBuffer() -{ - if (sendCursor < INCHUNK) - return; - - float_to_short(tx_buf, sendBuffer, sendCursor); - - /* Write samples. Fail if we don't get what we want. */ - int num_smpls = mRadio->writeSamples(tx_buf, - sendCursor, - &underrun, - writeTimestamp); - assert(num_smpls == sendCursor); - - writeTimestamp += (TIMESTAMP) num_smpls; - sendCursor = 0; -} diff --git a/Transceiver52M/radioIOResamp.cpp b/Transceiver52M/radioIOResamp.cpp deleted file mode 100644 index 8e8ac75..0000000 --- a/Transceiver52M/radioIOResamp.cpp +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Radio device interface with sample rate conversion - * Written by Thomas Tsou - * - * 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 . - * See the COPYING file in the main directory for details. - */ - -#include -#include - -/* 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 - * - * 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) -{ - 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(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(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(DEBUG) << "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(DEBUG) << "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(DEBUG) << "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(DEBUG) << "Tx wrote " << num_wr << " samples to device"; - assert(num_wr == num_wr); - - writeTimestamp += (TIMESTAMP) num_wr; - sendCursor = 0; -} diff --git a/Transceiver52M/radioInterface.cpp b/Transceiver52M/radioInterface.cpp index 95b8ab6..3cc15db 100644 --- a/Transceiver52M/radioInterface.cpp +++ b/Transceiver52M/radioInterface.cpp @@ -27,6 +27,28 @@ bool started = false; +/* Device side buffers */ +static short rx_buf[OUTCHUNK * 2 * 2]; +static short tx_buf[INCHUNK * 2 * 2]; + +/* Complex float to short conversion */ +static void floatToShort(short *out, float *in, int num) +{ + for (int i = 0; i < num; i++) { + out[2 * i + 0] = (short) in[2 * i + 0]; + out[2 * i + 1] = (short) in[2 * i + 1]; + } +} + +/* Complex short to float conversion */ +static void shortToFloat(float *out, short *in, int num) +{ + for (int i = 0; i < num; i++) { + out[2 * i + 0] = (float) in[2 * i + 0]; + out[2 * i + 1] = (float) in[2 * i + 1]; + } +} + RadioInterface::RadioInterface(RadioDevice *wRadio, int wReceiveOffset, int wSPS, @@ -236,3 +258,41 @@ double RadioInterface::getRxGain() else return -1; } + +/* Receive a timestamped chunk from the device */ +void RadioInterface::pullBuffer() +{ + bool local_underrun; + + /* Read samples. Fail if we don't get what we want. */ + int num_rd = mRadio->readSamples(rx_buf, OUTCHUNK, &overrun, + readTimestamp, &local_underrun); + + LOG(DEBUG) << "Rx read " << num_rd << " samples from device"; + assert(num_rd == OUTCHUNK); + + underrun |= local_underrun; + readTimestamp += (TIMESTAMP) num_rd; + + shortToFloat(rcvBuffer + 2 * rcvCursor, rx_buf, num_rd); + rcvCursor += num_rd; +} + +/* Send timestamped chunk to the device with arbitrary size */ +void RadioInterface::pushBuffer() +{ + if (sendCursor < INCHUNK) + return; + + floatToShort(tx_buf, sendBuffer, sendCursor); + + /* Write samples. Fail if we don't get what we want. */ + int num_smpls = mRadio->writeSamples(tx_buf, + sendCursor, + &underrun, + writeTimestamp); + assert(num_smpls == sendCursor); + + writeTimestamp += (TIMESTAMP) num_smpls; + sendCursor = 0; +} diff --git a/Transceiver52M/radioInterface.h b/Transceiver52M/radioInterface.h index 216cf3e..94d8917 100644 --- a/Transceiver52M/radioInterface.h +++ b/Transceiver52M/radioInterface.h @@ -31,7 +31,7 @@ static const unsigned gSlotLen = 148; ///< number of symbols per slot, not /** class to interface the transceiver with the USRP */ class RadioInterface { -private: +protected: Thread mAlignRadioServiceLoopThread; ///< thread that synchronizes transmit and receive sections @@ -63,6 +63,8 @@ private: int mNumARFCNs; signalVector *finalVec, *finalVec9; +private: + /** format samples to USRP */ int radioifyVector(signalVector &wVector, float *floatVector, @@ -73,10 +75,10 @@ private: int unRadioifyVector(float *floatVector, signalVector &wVector); /** push GSM bursts into the transmit buffer */ - void pushBuffer(void); + virtual void pushBuffer(void); /** pull GSM bursts from the receive buffer */ - void pullBuffer(void); + virtual void pullBuffer(void); public: @@ -154,3 +156,18 @@ protected: /** synchronization thread loop */ void *AlignRadioServiceLoopAdapter(RadioInterface*); #endif + +class RadioInterfaceResamp : public RadioInterface { + +private: + + void pushBuffer(); + void pullBuffer(); + +public: + + RadioInterfaceResamp(RadioDevice* wRadio = NULL, + int receiveOffset = 3, + int wSPS = SAMPSPERSYM, + GSM::Time wStartTime = GSM::Time(0)); +}; diff --git a/Transceiver52M/radioInterfaceResamp.cpp b/Transceiver52M/radioInterfaceResamp.cpp new file mode 100644 index 0000000..c7f17ea --- /dev/null +++ b/Transceiver52M/radioInterfaceResamp.cpp @@ -0,0 +1,332 @@ +/* + * Radio device interface with sample rate conversion + * Written by Thomas Tsou + * + * 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 . + * See the COPYING file in the main directory for details. + */ + +#include +#include + +/* 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 + * + * 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) +{ + 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(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(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; +} + +RadioInterfaceResamp::RadioInterfaceResamp(RadioDevice *wRadio, + int wReceiveOffset, + int wSPS, + GSM::Time wStartTime) + : RadioInterface(wRadio, wReceiveOffset, wSPS, wStartTime) +{ +} + +/* Receive a timestamped chunk from the device */ +void RadioInterfaceResamp::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(DEBUG) << "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(DEBUG) << "Rx read " << num_cv << " samples from resampler"; + + rcvCursor += num_cv; +} + +/* Send a timestamped chunk to the device */ +void RadioInterfaceResamp::pushBuffer() +{ + int num_cv, num_wr; + + if (sendCursor < INCHUNK) + return; + + LOG(DEBUG) << "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(DEBUG) << "Tx wrote " << num_wr << " samples to device"; + assert(num_wr == num_wr); + + writeTimestamp += (TIMESTAMP) num_wr; + sendCursor = 0; +} diff --git a/Transceiver52M/runTransceiver.cpp b/Transceiver52M/runTransceiver.cpp index 6b35797..e61ba34 100644 --- a/Transceiver52M/runTransceiver.cpp +++ b/Transceiver52M/runTransceiver.cpp @@ -84,7 +84,8 @@ int main(int argc, char *argv[]) srandom(time(NULL)); RadioDevice *usrp = RadioDevice::make(SAMPSPERSYM); - if (!usrp->open(deviceArgs)) { + int radioType = usrp->open(deviceArgs); + if (radioType < 0) { LOG(ALERT) << "Transceiver exiting..." << std::endl; return EXIT_FAILURE; } @@ -102,7 +103,19 @@ int main(int argc, char *argv[]) LOG(INFO) << "transceiver using transmit antenna " << usrp->getRxAntenna(); LOG(INFO) << "transceiver using receive antenna " << usrp->getTxAntenna(); - RadioInterface* radio = new RadioInterface(usrp, 3, SAMPSPERSYM, false); + RadioInterface* radio; + switch (radioType) { + case RadioDevice::NORMAL: + radio = new RadioInterface(usrp, 3, SAMPSPERSYM, false); + break; + case RadioDevice::RESAMP: + radio = new RadioInterfaceResamp(usrp, 3, SAMPSPERSYM, false); + break; + default: + LOG(ALERT) << "Unsupported configuration"; + return EXIT_FAILURE; + } + Transceiver *trx = new Transceiver(gConfig.getNum("TRX.Port"),gConfig.getStr("TRX.IP").c_str(),SAMPSPERSYM,GSM::Time(3,0),radio); trx->receiveFIFO(radio->receiveFIFO()); diff --git a/configure.ac b/configure.ac index 1c96f51..3cf2783 100644 --- a/configure.ac +++ b/configure.ac @@ -72,11 +72,6 @@ AC_ARG_WITH(singledb, [ [enable single daughterboard use on USRP1]) ]) -AC_ARG_WITH(resamp, [ - AS_HELP_STRING([--with-resamp], - [enable resampling for non-52MHz devices]) -]) - AC_ARG_WITH(extref, [ AS_HELP_STRING([--with-extref], [enable external reference on UHD devices]) @@ -102,10 +97,6 @@ AS_IF([test "x$with_uhd" = "xyes"],[ AC_DEFINE(USE_UHD, 1, Define to 1 if using UHD) ]) -AS_IF([test "x$with_resamp" = "xyes"], [ - AC_DEFINE(RESAMPLE, 1, Define to 1 for resampling) -]) - AS_IF([test "x$with_extref" = "xyes"], [ AC_DEFINE(EXTREF, 1, Define to 1 for external reference) ]) @@ -114,7 +105,6 @@ AS_IF([test "x$with_singledb" = "xyes"], [ AC_DEFINE(SINGLEDB, 1, Define to 1 for single daughterboard) ]) -AM_CONDITIONAL(RESAMPLE, [test "x$with_resamp" = "xyes"]) AM_CONDITIONAL(UHD, [test "x$with_uhd" = "xyes"]) AM_CONDITIONAL(USRP1, [test "x$with_usrp1" = "xyes"]) -- cgit v1.2.1