diff options
Diffstat (limited to 'Transceiver52M/radioInterfaceResamp.cpp')
-rw-r--r-- | Transceiver52M/radioInterfaceResamp.cpp | 332 |
1 files changed, 332 insertions, 0 deletions
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 <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 + * + * 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<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; +} + +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; +} |