/* * 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; }