1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-22 16:08:39 -05:00
sdrangel/wdsp/fir.cpp

416 lines
14 KiB
C++
Raw Normal View History

2024-06-16 05:31:13 -04:00
/* fir.c
This file is part of a program that implements a Software-Defined Radio.
Copyright (C) 2013, 2016, 2022 Warren Pratt, NR0V
Copyright (C) 2024 Edouard Griffiths, F4EXB Adapted to SDRangel
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
The author can be reached by email at
warren@pratt.one
*/
#define _CRT_SECURE_NO_WARNINGS
#include "fftw3.h"
#include "comm.hpp"
#include "fir.hpp"
namespace WDSP {
2024-06-24 21:50:48 -04:00
float* FIR::fftcv_mults (int NM, float* c_impulse)
2024-06-16 05:31:13 -04:00
{
2024-06-24 21:50:48 -04:00
float* mults = new float[NM * 2]; // (float *) malloc0 (NM * sizeof (wcomplex));
float* cfft_impulse = new float[NM * 2]; // (float *) malloc0 (NM * sizeof (wcomplex));
fftwf_plan ptmp = fftwf_plan_dft_1d(NM, (fftwf_complex *) cfft_impulse,
(fftwf_complex *) mults, FFTW_FORWARD, FFTW_PATIENT);
2024-06-24 16:23:10 -04:00
memset (cfft_impulse, 0, NM * sizeof (wcomplex));
2024-06-16 05:31:13 -04:00
// store complex coefs right-justified in the buffer
2024-06-24 16:23:10 -04:00
memcpy (&(cfft_impulse[NM - 2]), c_impulse, (NM / 2 + 1) * sizeof(wcomplex));
2024-06-24 21:50:48 -04:00
fftwf_execute (ptmp);
fftwf_destroy_plan (ptmp);
2024-06-16 05:31:13 -04:00
delete[] cfft_impulse;
return mults;
}
2024-06-24 21:50:48 -04:00
float* FIR::get_fsamp_window(int N, int wintype)
2024-06-16 05:31:13 -04:00
{
int i;
2024-06-24 21:50:48 -04:00
float arg0, arg1;
float* window = new float[N]; // (float *) malloc0 (N * sizeof(float));
2024-06-16 05:31:13 -04:00
switch (wintype)
{
case 0:
2024-06-24 21:50:48 -04:00
arg0 = 2.0 * PI / ((float)N - 1.0);
2024-06-16 05:31:13 -04:00
for (i = 0; i < N; i++)
{
2024-06-24 21:50:48 -04:00
arg1 = cos(arg0 * (float)i);
2024-06-16 05:31:13 -04:00
window[i] = +0.21747
+ arg1 * (-0.45325
+ arg1 * (+0.28256
+ arg1 * (-0.04672)));
}
break;
case 1:
2024-06-24 21:50:48 -04:00
arg0 = 2.0 * PI / ((float)N - 1.0);
2024-06-16 05:31:13 -04:00
for (i = 0; i < N; ++i)
{
2024-06-24 21:50:48 -04:00
arg1 = cos(arg0 * (float)i);
2024-06-16 05:31:13 -04:00
window[i] = +6.3964424114390378e-02
+ arg1 * (-2.3993864599352804e-01
+ arg1 * (+3.5015956323820469e-01
+ arg1 * (-2.4774111897080783e-01
+ arg1 * (+8.5438256055858031e-02
+ arg1 * (-1.2320203369293225e-02
+ arg1 * (+4.3778825791773474e-04))))));
}
break;
default:
for (i = 0; i < N; i++)
window[i] = 1.0;
}
return window;
}
2024-06-24 21:50:48 -04:00
float* FIR::fir_fsamp_odd (int N, float* A, int rtype, float scale, int wintype)
2024-06-16 05:31:13 -04:00
{
int i, j;
int mid = (N - 1) / 2;
2024-06-24 21:50:48 -04:00
float mag, phs;
float* window;
float *fcoef = new float[N * 2]; // (float *) malloc0 (N * sizeof (wcomplex));
float *c_impulse = new float[N * 2]; // (float *) malloc0 (N * sizeof (wcomplex));
fftwf_plan ptmp = fftwf_plan_dft_1d(N, (fftwf_complex *)fcoef, (fftwf_complex *)c_impulse, FFTW_BACKWARD, FFTW_PATIENT);
float local_scale = 1.0 / (float)N;
2024-06-16 05:31:13 -04:00
for (i = 0; i <= mid; i++)
{
mag = A[i] * local_scale;
2024-06-24 21:50:48 -04:00
phs = - (float)mid * TWOPI * (float)i / (float)N;
2024-06-16 05:31:13 -04:00
fcoef[2 * i + 0] = mag * cos (phs);
fcoef[2 * i + 1] = mag * sin (phs);
}
for (i = mid + 1, j = 0; i < N; i++, j++)
{
fcoef[2 * i + 0] = + fcoef[2 * (mid - j) + 0];
fcoef[2 * i + 1] = - fcoef[2 * (mid - j) + 1];
}
2024-06-24 21:50:48 -04:00
fftwf_execute (ptmp);
fftwf_destroy_plan (ptmp);
2024-06-16 05:31:13 -04:00
delete[] fcoef;
window = get_fsamp_window(N, wintype);
switch (rtype)
{
case 0:
for (i = 0; i < N; i++)
c_impulse[i] = scale * c_impulse[2 * i] * window[i];
break;
case 1:
for (i = 0; i < N; i++)
{
c_impulse[2 * i + 0] *= scale * window[i];
c_impulse[2 * i + 1] = 0.0;
}
break;
}
delete[] window;
return c_impulse;
}
2024-06-24 21:50:48 -04:00
float* FIR::fir_fsamp (int N, float* A, int rtype, float scale, int wintype)
2024-06-16 05:31:13 -04:00
{
int n, i, j, k;
2024-06-24 21:50:48 -04:00
float sum;
float* window;
float *c_impulse = new float[N * 2]; // (float *) malloc0 (N * sizeof (complex));
2024-06-16 05:31:13 -04:00
if (N & 1)
{
int M = (N - 1) / 2;
for (n = 0; n < M + 1; n++)
{
sum = 0.0;
for (k = 1; k < M + 1; k++)
sum += 2.0 * A[k] * cos(TWOPI * (n - M) * k / N);
c_impulse[2 * n + 0] = (1.0 / N) * (A[0] + sum);
c_impulse[2 * n + 1] = 0.0;
}
for (n = M + 1, j = 1; n < N; n++, j++)
{
c_impulse[2 * n + 0] = c_impulse[2 * (M - j) + 0];
c_impulse[2 * n + 1] = 0.0;
}
}
else
{
2024-06-24 21:50:48 -04:00
float M = (float)(N - 1) / 2.0;
2024-06-16 05:31:13 -04:00
for (n = 0; n < N / 2; n++)
{
sum = 0.0;
for (k = 1; k < N / 2; k++)
sum += 2.0 * A[k] * cos(TWOPI * (n - M) * k / N);
c_impulse[2 * n + 0] = (1.0 / N) * (A[0] + sum);
c_impulse[2 * n + 1] = 0.0;
}
for (n = N / 2, j = 1; n < N; n++, j++)
{
c_impulse[2 * n + 0] = c_impulse[2 * (N / 2 - j) + 0];
c_impulse[2 * n + 1] = 0.0;
}
}
window = get_fsamp_window (N, wintype);
switch (rtype)
{
case 0:
for (i = 0; i < N; i++)
c_impulse[i] = scale * c_impulse[2 * i] * window[i];
break;
case 1:
for (i = 0; i < N; i++)
{
c_impulse[2 * i + 0] *= scale * window[i];
c_impulse[2 * i + 1] = 0.0;
}
break;
}
delete[] window;
return c_impulse;
}
2024-06-24 21:50:48 -04:00
float* FIR::fir_bandpass (int N, float f_low, float f_high, float samplerate, int wintype, int rtype, float scale)
2024-06-16 05:31:13 -04:00
{
2024-06-24 21:50:48 -04:00
float *c_impulse = new float[N * 2]; // (float *) malloc0 (N * sizeof (complex));
float ft = (f_high - f_low) / (2.0 * samplerate);
float ft_rad = TWOPI * ft;
float w_osc = PI * (f_high + f_low) / samplerate;
2024-06-16 05:31:13 -04:00
int i, j;
2024-06-24 21:50:48 -04:00
float m = 0.5 * (float)(N - 1);
float delta = PI / m;
float cosphi;
float posi, posj;
float sinc, window, coef;
2024-06-16 05:31:13 -04:00
if (N & 1)
{
switch (rtype)
{
case 0:
c_impulse[N >> 1] = scale * 2.0 * ft;
break;
case 1:
c_impulse[N - 1] = scale * 2.0 * ft;
c_impulse[ N ] = 0.0;
break;
}
}
for (i = (N + 1) / 2, j = N / 2 - 1; i < N; i++, j--)
{
2024-06-24 21:50:48 -04:00
posi = (float)i - m;
posj = (float)j - m;
2024-06-16 05:31:13 -04:00
sinc = sin (ft_rad * posi) / (PI * posi);
switch (wintype)
{
case 0: // Blackman-Harris 4-term
cosphi = cos (delta * i);
window = + 0.21747
+ cosphi * ( - 0.45325
+ cosphi * ( + 0.28256
+ cosphi * ( - 0.04672 )));
break;
case 1: // Blackman-Harris 7-term
cosphi = cos (delta * i);
window = + 6.3964424114390378e-02
+ cosphi * ( - 2.3993864599352804e-01
+ cosphi * ( + 3.5015956323820469e-01
+ cosphi * ( - 2.4774111897080783e-01
+ cosphi * ( + 8.5438256055858031e-02
+ cosphi * ( - 1.2320203369293225e-02
+ cosphi * ( + 4.3778825791773474e-04 ))))));
break;
}
coef = scale * sinc * window;
switch (rtype)
{
case 0:
c_impulse[i] = + coef * cos (posi * w_osc);
c_impulse[j] = + coef * cos (posj * w_osc);
break;
case 1:
c_impulse[2 * i + 0] = + coef * cos (posi * w_osc);
c_impulse[2 * i + 1] = - coef * sin (posi * w_osc);
c_impulse[2 * j + 0] = + coef * cos (posj * w_osc);
c_impulse[2 * j + 1] = - coef * sin (posj * w_osc);
break;
}
}
return c_impulse;
}
2024-06-24 21:50:48 -04:00
float *FIR::fir_read (int N, const char *filename, int rtype, float scale)
2024-06-16 05:31:13 -04:00
// N = number of real or complex coefficients (see rtype)
// *filename = filename
// rtype = 0: real coefficients
// rtype = 1: complex coefficients
// scale = a scale factor that will be applied to the returned coefficients;
// if this is not needed, set it to 1.0
// NOTE: The number of values in the file must NOT exceed those implied by N and rtype
{
FILE *file;
int i;
2024-06-24 21:50:48 -04:00
float I, Q;
float *c_impulse = new float[N * 2]; // (float *) malloc0 (N * sizeof (complex));
2024-06-16 05:31:13 -04:00
file = fopen (filename, "r");
for (i = 0; i < N; i++)
{
// read in the complex impulse response
// NOTE: IF the freq response is symmetrical about 0, the imag coeffs will all be zero.
switch (rtype)
{
case 0:
{
2024-06-24 21:50:48 -04:00
int r = fscanf (file, "%e", &I);
2024-06-16 05:31:13 -04:00
fprintf(stderr, "^%d parameters read\n", r);
c_impulse[i] = + scale * I;
break;
}
case 1:
{
2024-06-24 21:50:48 -04:00
int r = fscanf (file, "%e", &I);
2024-06-16 05:31:13 -04:00
fprintf(stderr, "%d parameters read\n", r);
2024-06-24 21:50:48 -04:00
r = fscanf (file, "%e", &Q);
2024-06-16 05:31:13 -04:00
fprintf(stderr, "%d parameters read\n", r);
c_impulse[2 * i + 0] = + scale * I;
c_impulse[2 * i + 1] = - scale * Q;
break;
}
}
}
fclose (file);
return c_impulse;
}
2024-06-24 21:50:48 -04:00
void FIR::analytic (int N, float* in, float* out)
2024-06-16 05:31:13 -04:00
{
int i;
2024-06-24 21:50:48 -04:00
float inv_N = 1.0 / (float)N;
float two_inv_N = 2.0 * inv_N;
float* x = new float[N * 2]; // (float *) malloc0 (N * sizeof (complex));
fftwf_plan pfor = fftwf_plan_dft_1d (N, (fftwf_complex *) in,
(fftwf_complex *) x, FFTW_FORWARD, FFTW_PATIENT);
fftwf_plan prev = fftwf_plan_dft_1d (N, (fftwf_complex *) x,
(fftwf_complex *) out, FFTW_BACKWARD, FFTW_PATIENT);
fftwf_execute (pfor);
2024-06-16 05:31:13 -04:00
x[0] *= inv_N;
x[1] *= inv_N;
for (i = 1; i < N / 2; i++)
{
x[2 * i + 0] *= two_inv_N;
x[2 * i + 1] *= two_inv_N;
}
x[N + 0] *= inv_N;
x[N + 1] *= inv_N;
2024-06-24 21:50:48 -04:00
memset (&x[N + 2], 0, (N - 2) * sizeof (float));
fftwf_execute (prev);
fftwf_destroy_plan (prev);
fftwf_destroy_plan (pfor);
2024-06-16 05:31:13 -04:00
delete[] x;
}
2024-06-24 21:50:48 -04:00
void FIR::mp_imp (int N, float* fir, float* mpfir, int pfactor, int polarity)
2024-06-16 05:31:13 -04:00
{
int i;
int size = N * pfactor;
2024-06-24 21:50:48 -04:00
float inv_PN = 1.0 / (float)size;
float* firpad = new float[size * 2]; // (float *) malloc0 (size * sizeof (complex));
float* firfreq = new float[size * 2]; // (float *) malloc0 (size * sizeof (complex));
float* mag = new float[size]; // (float *) malloc0 (size * sizeof (float));
float* ana = new float[size * 2]; // (float *) malloc0 (size * sizeof (complex));
float* impulse = new float[size * 2]; // (float *) malloc0 (size * sizeof (complex));
float* newfreq = new float[size * 2]; // (float *) malloc0 (size * sizeof (complex));
2024-06-24 16:23:10 -04:00
memcpy (firpad, fir, N * sizeof (wcomplex));
2024-06-24 21:50:48 -04:00
fftwf_plan pfor = fftwf_plan_dft_1d (size, (fftwf_complex *) firpad,
(fftwf_complex *) firfreq, FFTW_FORWARD, FFTW_PATIENT);
fftwf_plan prev = fftwf_plan_dft_1d (size, (fftwf_complex *) newfreq,
(fftwf_complex *) impulse, FFTW_BACKWARD, FFTW_PATIENT);
2024-06-16 05:31:13 -04:00
// print_impulse("orig_imp.txt", N, fir, 1, 0);
2024-06-24 21:50:48 -04:00
fftwf_execute (pfor);
2024-06-16 05:31:13 -04:00
for (i = 0; i < size; i++)
{
mag[i] = sqrt (firfreq[2 * i + 0] * firfreq[2 * i + 0] + firfreq[2 * i + 1] * firfreq[2 * i + 1]) * inv_PN;
if (mag[i] > 0.0)
ana[2 * i + 0] = log (mag[i]);
else
ana[2 * i + 0] = log (1.0e-300);
}
analytic (size, ana, ana);
for (i = 0; i < size; i++)
{
newfreq[2 * i + 0] = + mag[i] * cos (ana[2 * i + 1]);
if (polarity)
newfreq[2 * i + 1] = + mag[i] * sin (ana[2 * i + 1]);
else
newfreq[2 * i + 1] = - mag[i] * sin (ana[2 * i + 1]);
}
2024-06-24 21:50:48 -04:00
fftwf_execute (prev);
2024-06-16 05:31:13 -04:00
if (polarity)
2024-06-24 16:23:10 -04:00
memcpy (mpfir, &impulse[2 * (pfactor - 1) * N], N * sizeof (wcomplex));
2024-06-16 05:31:13 -04:00
else
2024-06-24 16:23:10 -04:00
memcpy (mpfir, impulse, N * sizeof (wcomplex));
2024-06-16 05:31:13 -04:00
// print_impulse("min_imp.txt", N, mpfir, 1, 0);
2024-06-24 21:50:48 -04:00
fftwf_destroy_plan (prev);
fftwf_destroy_plan (pfor);
2024-06-16 05:31:13 -04:00
delete[] (newfreq);
delete[] (impulse);
delete[] (ana);
delete[] (mag);
delete[] (firfreq);
delete[] (firpad);
}
// impulse response of a zero frequency filter comprising a cascade of two resonators,
// each followed by a detrending filter
2024-06-24 21:50:48 -04:00
float* FIR::zff_impulse(int nc, float scale)
2024-06-16 05:31:13 -04:00
{
// nc = number of coefficients (power of two)
int n_resdet = nc / 2 - 1; // size of single zero-frequency resonator with detrender
int n_dresdet = 2 * n_resdet - 1; // size of two cascaded units; when we convolve these we get 2 * n - 1 length
// allocate the single and make the values
2024-06-24 21:50:48 -04:00
float* resdet = new float[n_resdet]; // (float*)malloc0 (n_resdet * sizeof(float));
2024-06-16 05:31:13 -04:00
for (int i = 1, j = 0, k = n_resdet - 1; i < nc / 4; i++, j++, k--)
2024-06-24 21:50:48 -04:00
resdet[j] = resdet[k] = (float)(i * (i + 1) / 2);
resdet[nc / 4 - 1] = (float)(nc / 4 * (nc / 4 + 1) / 2);
2024-06-16 05:31:13 -04:00
// print_impulse ("resdet", n_resdet, resdet, 0, 0);
2024-06-24 21:50:48 -04:00
// allocate the float and complex versions and make the values
float* dresdet = new float[n_dresdet]; // (float*)malloc0 (n_dresdet * sizeof(float));
float div = (float)((nc / 2 + 1) * (nc / 2 + 1)); // calculate divisor
float* c_dresdet = new float[nc * 2]; // (float*)malloc0 (nc * sizeof(complex));
2024-06-16 05:31:13 -04:00
for (int n = 0; n < n_dresdet; n++) // convolve to make the cascade
{
for (int k = 0; k < n_resdet; k++)
if ((n - k) >= 0 && (n - k) < n_resdet)
dresdet[n] += resdet[k] * resdet[n - k];
dresdet[n] /= div;
c_dresdet[2 * n + 0] = dresdet[n] * scale;
c_dresdet[2 * n + 1] = 0.0;
}
// print_impulse("dresdet", n_dresdet, dresdet, 0, 0);
// print_impulse("c_dresdet", nc, c_dresdet, 1, 0);
delete[] (dresdet);
delete[] (resdet);
return c_dresdet;
}
} // namespace WDSP