diff options
Diffstat (limited to 'src/filter.c')
| -rw-r--r-- | src/filter.c | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/src/filter.c b/src/filter.c new file mode 100644 index 0000000..ccb58cd --- /dev/null +++ b/src/filter.c @@ -0,0 +1,281 @@ +/* + Copyright (C) 2018 James C. Ahlstrom + + All rights reserved. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License version 2.1, as + published by the Free Software Foundation. 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 General Public + License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include <stdlib.h> +#include <string.h> +#include <math.h> +#include <complex.h> + +#include "filter.h" +#include "filter_coef.h" +#include "debug_alloc.h" + +#define cmplx(value) (cosf(value) + sinf(value) * I) + +/* + * This is a library of filter functions. They were copied from Quisk and converted to single precision. + */ + +/*---------------------------------------------------------------------------*\ + + FUNCTIONS...: quisk_filt_cfInit + AUTHOR......: Jim Ahlstrom + DATE CREATED: 27 August 2015 + MODIFIED: 4 June 2018 + + Initialize a FIR filter that has complex samples, and either real or complex coefficients. + +\*---------------------------------------------------------------------------*/ + +void quisk_filt_cfInit(struct quisk_cfFilter * filter, float * coefs, int taps) { + // Prepare a new filter using coefs and taps. Samples are complex. Coefficients can + // be real or complex. + filter->dCoefs = coefs; + filter->cpxCoefs = NULL; + filter->cSamples = (complex float *)MALLOC(taps * sizeof(complex float)); + memset(filter->cSamples, 0, taps * sizeof(complex float)); + filter->ptcSamp = filter->cSamples; + filter->nTaps = taps; + filter->cBuf = NULL; + filter->nBuf = 0; + filter->decim_index = 0; +} + +/*---------------------------------------------------------------------------*\ + + FUNCTIONS...: quisk_filt_destroy + AUTHOR......: Jim Ahlstrom + DATE CREATED: 27 August 2015 + MODIFIED: 4 June 2018 + + Destroy the FIR filter and free all resources. + +\*---------------------------------------------------------------------------*/ + +void quisk_filt_destroy(struct quisk_cfFilter * filter) { + if (filter->cSamples) { + FREE(filter->cSamples); + filter->cSamples = NULL; + } + + if (filter->cBuf) { + FREE(filter->cBuf); + filter->cBuf = NULL; + } + + if (filter->cpxCoefs) { + FREE(filter->cpxCoefs); + filter->cpxCoefs = NULL; + } +} + +/*---------------------------------------------------------------------------*\ + + FUNCTIONS...: quisk_cfInterpDecim + AUTHOR......: Jim Ahlstrom + DATE CREATED: 27 August 2015 + MODIFIED: 4 June 2018 + + Take an array of samples cSamples of length count, multiply the sample rate + by interp, and then divide the sample rate by decim. Return the new number + of samples. Each specific interp and decim will require its own custom + low pass FIR filter with real coefficients. + +\*---------------------------------------------------------------------------*/ + +int quisk_cfInterpDecim(complex float * cSamples, int count, struct quisk_cfFilter * filter, int interp, int decim) { + // Interpolate by interp, and then decimate by decim. + // This uses the float coefficients of filter (not the complex). Samples are complex. + int i, k, nOut; + float * ptCoef; + complex float * ptSample; + complex float csample; + + if (count > filter->nBuf) { // increase size of sample buffer + filter->nBuf = count * 2; + + if (filter->cBuf) + FREE(filter->cBuf); + + filter->cBuf = (complex float *)MALLOC(filter->nBuf * sizeof(complex float)); + } + + memcpy(filter->cBuf, cSamples, count * sizeof(complex float)); + nOut = 0; + + for (i = 0; i < count; i++) { + // Put samples into buffer left to right. Use samples right to left. + *filter->ptcSamp = filter->cBuf[i]; + + while (filter->decim_index < interp) { + ptSample = filter->ptcSamp; + ptCoef = filter->dCoefs + filter->decim_index; + csample = 0; + + for (k = 0; k < filter->nTaps / interp; k++, ptCoef += interp) { + csample += *ptSample * *ptCoef; + + if (--ptSample < filter->cSamples) + ptSample = filter->cSamples + filter->nTaps - 1; + } + + cSamples[nOut] = csample * interp; + nOut++; + filter->decim_index += decim; + } + + if (++filter->ptcSamp >= filter->cSamples + filter->nTaps) + filter->ptcSamp = filter->cSamples; + + filter->decim_index = filter->decim_index - interp; + } + + return nOut; +} + +/*---------------------------------------------------------------------------*\ + + FUNCTIONS...: quisk_ccfInterpDecim + AUTHOR......: Jim Ahlstrom + DATE CREATED: 7 June 2018 + + Take an array of samples cSamples of length count, multiply the sample rate + by interp, and then divide the sample rate by decim. Return the new number + of samples. Each specific interp and decim will require its own custom + low pass FIR filter with complex coefficients. This filter can be tuned. + + This filter is not currently used. + +\*---------------------------------------------------------------------------*/ +#if 0 +int quisk_ccfInterpDecim(complex float * cSamples, int count, struct quisk_cfFilter * filter, int interp, int decim) { + // Interpolate by interp, and then decimate by decim. + // This uses the complex coefficients of filter (not the real). Samples are complex. + int i, k, nOut; + complex float * ptCoef; + complex float * ptSample; + complex float csample; + + if (count > filter->nBuf) { // increase size of sample buffer + filter->nBuf = count * 2; + if (filter->cBuf) + FREE(filter->cBuf); + filter->cBuf = (complex float *)MALLOC(filter->nBuf * sizeof(complex float)); + } + + memcpy(filter->cBuf, cSamples, count * sizeof(complex float)); + nOut = 0; + + for (i = 0; i < count; i++) { + // Put samples into buffer left to right. Use samples right to left. + *filter->ptcSamp = filter->cBuf[i]; + + while (filter->decim_index < interp) { + ptSample = filter->ptcSamp; + ptCoef = filter->cpxCoefs + filter->decim_index; + csample = 0; + + for (k = 0; k < filter->nTaps / interp; k++, ptCoef += interp) { + csample += *ptSample * *ptCoef; + + if (--ptSample < filter->cSamples) + ptSample = filter->cSamples + filter->nTaps - 1; + } + + cSamples[nOut] = csample * interp; + nOut++; + filter->decim_index += decim; + } + + if (++filter->ptcSamp >= filter->cSamples + filter->nTaps) + filter->ptcSamp = filter->cSamples; + + filter->decim_index = filter->decim_index - interp; + } + + return nOut; +} +#endif + +/*---------------------------------------------------------------------------*\ + + FUNCTIONS...: quisk_cfTune + AUTHOR......: Jim Ahlstrom + DATE CREATED: 4 June 2018 + + Tune a low pass filter with float coefficients into an analytic I/Q bandpass filter + with complex coefficients. The "freq" is the center frequency / sample rate. + If the float coefs represent a low pass filter with bandwidth 1 kHz, the new bandpass + filter has width 2 kHz. The filter can be re-tuned repeatedly. + +\*---------------------------------------------------------------------------*/ + +void quisk_cfTune(struct quisk_cfFilter * filter, float freq) { + float D, tune; + int i; + + if ( ! filter->cpxCoefs) + filter->cpxCoefs = (complex float *)MALLOC(filter->nTaps * sizeof(complex float)); + + tune = 2.0 * M_PI * freq; + D = (filter->nTaps - 1.0) / 2.0; + + for (i = 0; i < filter->nTaps; i++) { + float tval = tune * (i - D); + filter->cpxCoefs[i] = cmplx(tval) * filter->dCoefs[i]; + } +} + +/*---------------------------------------------------------------------------*\ + + FUNCTIONS...: quisk_ccfFilter + AUTHOR......: Jim Ahlstrom + DATE CREATED: 4 June 2018 + + Filter complex samples using complex coefficients. The inSamples and outSamples may be + the same array. The loop runs forward over coefficients but backwards over samples. + Therefore, the coefficients must be reversed unless they are created by quisk_cfTune. + Low pass filter coefficients are symmetrical, so this does not usually matter. + +\*---------------------------------------------------------------------------*/ + +void quisk_ccfFilter(complex float * inSamples, complex float * outSamples, int count, struct quisk_cfFilter * filter) { + int i, k; + complex float * ptSample; + complex float * ptCoef; + complex float accum; + + for (i = 0; i < count; i++) { + *filter->ptcSamp = inSamples[i]; + accum = 0; + ptSample = filter->ptcSamp; + ptCoef = filter->cpxCoefs; + + for (k = 0; k < filter->nTaps; k++, ptCoef++) { + accum += *ptSample * *ptCoef; + + if (--ptSample < filter->cSamples) + ptSample = filter->cSamples + filter->nTaps - 1; + } + + outSamples[i] = accum; + + if (++filter->ptcSamp >= filter->cSamples + filter->nTaps) + filter->ptcSamp = filter->cSamples; + } +} + |
