Added FIR filter to libfilter
This commit is contained in:
parent
55a5a4ea2b
commit
5070bc70bd
|
@ -3,4 +3,6 @@ AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
|
||||||
noinst_LIBRARIES = libfilter.a
|
noinst_LIBRARIES = libfilter.a
|
||||||
|
|
||||||
libfilter_a_SOURCES = \
|
libfilter_a_SOURCES = \
|
||||||
iir_filter.c
|
iir_filter.c \
|
||||||
|
fir_filter.c
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,197 @@
|
||||||
|
/* FIR filter
|
||||||
|
*
|
||||||
|
* (C) 2017 by Andreas Eversberg <jolly@eversberg.eu>
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* 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 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 General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include "../libsample/sample.h"
|
||||||
|
#include "fir_filter.h"
|
||||||
|
|
||||||
|
//#define DEBUG_TAPS
|
||||||
|
|
||||||
|
static void kernel(double *taps, int M, double cutoff, int invert)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
double sum;
|
||||||
|
|
||||||
|
for (i = 0; i <= M; i++) {
|
||||||
|
/* gen sinc */
|
||||||
|
if (i == M / 2)
|
||||||
|
taps[i] = 2.0 * M_PI * cutoff;
|
||||||
|
else
|
||||||
|
taps[i] = sin(2.0 * M_PI * cutoff * (double)(i - M / 2))
|
||||||
|
/ (double)(i - M / 2);
|
||||||
|
/* blackman window */
|
||||||
|
taps[i] *= 0.42 - 0.50 * cos(2 * M_PI * (double)(i / M))
|
||||||
|
+ 0.08 * cos(4 * M_PI * (double)(i / M));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* normalize */
|
||||||
|
sum = 0;
|
||||||
|
for (i = 0; i <= M; i++)
|
||||||
|
sum += taps[i];
|
||||||
|
for (i = 0; i <= M; i++)
|
||||||
|
taps[i] /= sum;
|
||||||
|
|
||||||
|
/* invert */
|
||||||
|
if (invert) {
|
||||||
|
for (i = 0; i <= M; i++)
|
||||||
|
taps[i] = -taps[i];
|
||||||
|
taps[M / 2] += 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_TAPS
|
||||||
|
puts("start");
|
||||||
|
for (i = 0; i <= M; i++)
|
||||||
|
puts(debug_amplitude(taps[i]));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static fir_filter_t *fir_init(double samplerate, double transition_bandwidth)
|
||||||
|
{
|
||||||
|
fir_filter_t *fir;
|
||||||
|
int M;
|
||||||
|
|
||||||
|
/* alloc struct */
|
||||||
|
fir = calloc(1, sizeof(*fir));
|
||||||
|
if (!fir) {
|
||||||
|
fprintf(stderr, "No memory creating FIR filter!\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* transition bandwidth */
|
||||||
|
M = ceil(1.0 / (transition_bandwidth / samplerate));
|
||||||
|
if ((M & 1))
|
||||||
|
M++;
|
||||||
|
|
||||||
|
// printf("cutoff=%.4f\n", cutoff / samplerate);
|
||||||
|
// printf("tb=%.4f\n", transition_bandwidth / samplerate);
|
||||||
|
fir->ntaps = M + 1;
|
||||||
|
fir->delay = M / 2;
|
||||||
|
|
||||||
|
/* alloc taps */
|
||||||
|
fir->taps = calloc(fir->ntaps, sizeof(*fir->taps));
|
||||||
|
if (!fir->taps) {
|
||||||
|
fprintf(stderr, "No memory creating FIR filter!\n");
|
||||||
|
fir_exit(fir);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* alloc ring buffer */
|
||||||
|
fir->buffer = calloc(fir->ntaps, sizeof(*fir->buffer));
|
||||||
|
if (!fir->buffer) {
|
||||||
|
fprintf(stderr, "No memory creating FIR filter!\n");
|
||||||
|
fir_exit(fir);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return fir;
|
||||||
|
}
|
||||||
|
|
||||||
|
fir_filter_t *fir_lowpass_init(double samplerate, double cutoff, double transition_bandwidth)
|
||||||
|
{
|
||||||
|
/* calculate kernel */
|
||||||
|
fir_filter_t *fir = fir_init(samplerate, transition_bandwidth);
|
||||||
|
if (!fir)
|
||||||
|
return NULL;
|
||||||
|
kernel(fir->taps, fir->ntaps - 1, cutoff / samplerate, 0);
|
||||||
|
return fir;
|
||||||
|
}
|
||||||
|
|
||||||
|
fir_filter_t *fir_highpass_init(double samplerate, double cutoff, double transition_bandwidth)
|
||||||
|
{
|
||||||
|
fir_filter_t *fir = fir_init(samplerate, transition_bandwidth);
|
||||||
|
if (!fir)
|
||||||
|
return NULL;
|
||||||
|
kernel(fir->taps, fir->ntaps - 1, cutoff / samplerate, 1);
|
||||||
|
return fir;
|
||||||
|
}
|
||||||
|
|
||||||
|
fir_filter_t *fir_allpass_init(double samplerate, double transition_bandwidth)
|
||||||
|
{
|
||||||
|
fir_filter_t *fir = fir_init(samplerate, transition_bandwidth);
|
||||||
|
if (!fir)
|
||||||
|
return NULL;
|
||||||
|
fir->taps[(fir->ntaps - 1) / 2] = 1.0;
|
||||||
|
return fir;
|
||||||
|
}
|
||||||
|
|
||||||
|
fir_filter_t *fir_twopass_init(double samplerate, double cutoff_low, double cutoff_high, double transition_bandwidth)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
double sum;
|
||||||
|
fir_filter_t *fir = fir_init(samplerate, transition_bandwidth);
|
||||||
|
if (!fir)
|
||||||
|
return NULL;
|
||||||
|
double lp_taps[fir->ntaps], hp_taps[fir->ntaps];
|
||||||
|
kernel(lp_taps, fir->ntaps - 1, cutoff_low / samplerate, 0);
|
||||||
|
kernel(hp_taps, fir->ntaps - 1, cutoff_high / samplerate, 1);
|
||||||
|
sum = 0;
|
||||||
|
printf("#warning does not work as expected\n");
|
||||||
|
abort();
|
||||||
|
for (i = 0; i < fir->ntaps; i++) {
|
||||||
|
fir->taps[i] = lp_taps[i] + hp_taps[i];
|
||||||
|
sum += fir->taps[i];
|
||||||
|
}
|
||||||
|
/* hp will die */
|
||||||
|
// for (i = 0; i < fir->ntaps; i++)
|
||||||
|
// fir->taps[i] /= sum;
|
||||||
|
return fir;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fir_exit(fir_filter_t *fir)
|
||||||
|
{
|
||||||
|
if (!fir)
|
||||||
|
return;
|
||||||
|
free(fir->taps);
|
||||||
|
free(fir->buffer);
|
||||||
|
free(fir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fir_process(fir_filter_t *fir, sample_t *samples, int num)
|
||||||
|
{
|
||||||
|
int i, j;
|
||||||
|
double y;
|
||||||
|
|
||||||
|
for (i = 0; i < num; i++) {
|
||||||
|
/* put sample in ring buffer */
|
||||||
|
fir->buffer[fir->buffer_pos] = samples[i];
|
||||||
|
if (++fir->buffer_pos == fir->ntaps)
|
||||||
|
fir->buffer_pos = 0;
|
||||||
|
|
||||||
|
/* convolve samples */
|
||||||
|
y = 0;
|
||||||
|
for (j = 0; j < fir->ntaps; j++) {
|
||||||
|
/* convolve sample from ring buffer, starting with oldes */
|
||||||
|
y += fir->buffer[fir->buffer_pos] * fir->taps[j];
|
||||||
|
if (++fir->buffer_pos == fir->ntaps)
|
||||||
|
fir->buffer_pos = 0;
|
||||||
|
}
|
||||||
|
samples[i] = y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int fir_get_delay(fir_filter_t *fir)
|
||||||
|
{
|
||||||
|
return fir->delay;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
#ifndef _FIR_FILTER_H
|
||||||
|
#define _FIR_FILTER_H
|
||||||
|
|
||||||
|
typedef struct fir_filter {
|
||||||
|
int ntaps;
|
||||||
|
int delay;
|
||||||
|
double *taps;
|
||||||
|
double *buffer;
|
||||||
|
int buffer_pos;
|
||||||
|
} fir_filter_t;
|
||||||
|
|
||||||
|
fir_filter_t *fir_lowpass_init(double samplerate, double cutoff, double transition_bandwidth);
|
||||||
|
fir_filter_t *fir_highpass_init(double samplerate, double cutoff, double transition_bandwidth);
|
||||||
|
fir_filter_t *fir_allpass_init(double samplerate, double transition_bandwidth);
|
||||||
|
fir_filter_t *fir_twopass_init(double samplerate, double cutoff_low, double cutoff_high, double transition_bandwidth);
|
||||||
|
void fir_exit(fir_filter_t *fir);
|
||||||
|
void fir_process(fir_filter_t *fir, sample_t *samples, int num);
|
||||||
|
int fir_get_delay(fir_filter_t *fir);
|
||||||
|
|
||||||
|
#endif /* _FIR_FILTER_H */
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include "../libsample/sample.h"
|
#include "../libsample/sample.h"
|
||||||
#include "../libfilter/iir_filter.h"
|
#include "../libfilter/iir_filter.h"
|
||||||
|
#include "../libfilter/fir_filter.h"
|
||||||
#include "../libdebug/debug.h"
|
#include "../libdebug/debug.h"
|
||||||
|
|
||||||
#define level2db(level) (20 * log10(level))
|
#define level2db(level) (20 * log10(level))
|
||||||
|
@ -40,6 +41,7 @@ int main(void)
|
||||||
{
|
{
|
||||||
iir_filter_t filter_low;
|
iir_filter_t filter_low;
|
||||||
iir_filter_t filter_high;
|
iir_filter_t filter_high;
|
||||||
|
fir_filter_t *fir_low/*, *fir_high*/;
|
||||||
sample_t samples[SAMPLERATE];
|
sample_t samples[SAMPLERATE];
|
||||||
double level;
|
double level;
|
||||||
int iter = 2;
|
int iter = 2;
|
||||||
|
@ -102,6 +104,46 @@ int main(void)
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double freq = 2000.0;
|
||||||
|
double tb = 400.0;
|
||||||
|
printf("testing low-pass FIR filter with %.0fHz transition bandwidth\n", tb);
|
||||||
|
|
||||||
|
fir_low = fir_lowpass_init(SAMPLERATE, freq, tb);
|
||||||
|
printf("Using %d taps\n", fir_low->ntaps);
|
||||||
|
|
||||||
|
for (i = 0; i < 4001; i += 100) {
|
||||||
|
gen_samples(samples, (double)i);
|
||||||
|
fir_process(fir_low, samples, SAMPLERATE);
|
||||||
|
level = get_level(samples);
|
||||||
|
printf("%s%s%4d Hz: %.1f dB", debug_amplitude(level), debug_db(level), i, level2db(level));
|
||||||
|
if (i == freq)
|
||||||
|
printf(" cutoff\n");
|
||||||
|
else
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
fir_exit(fir_low);
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
double freq1 = 1000.0, freq2 = 2000.0;
|
||||||
|
tb = 100.0;
|
||||||
|
printf("testing two-pass FIR filter\n");
|
||||||
|
|
||||||
|
fir_high = fir_twopass_init(SAMPLERATE, freq1, freq2, tb);
|
||||||
|
printf("Using %d taps\n", fir_high->ntaps);
|
||||||
|
|
||||||
|
for (i = 0; i < 4001; i += 100) {
|
||||||
|
gen_samples(samples, (double)i);
|
||||||
|
fir_process(fir_high, samples, SAMPLERATE);
|
||||||
|
level = get_level(samples);
|
||||||
|
printf("%s%s%4d Hz: %.1f dB", debug_amplitude(level), debug_db(level), i, level2db(level));
|
||||||
|
if (i == freq1 || i == freq2)
|
||||||
|
printf(" cutoff\n");
|
||||||
|
else
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
fir_exit(fir_high);
|
||||||
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue