2016-03-16 01:05:20 +00:00
|
|
|
|
//
|
|
|
|
|
// LinearFilter.c
|
|
|
|
|
// Clock Signal
|
|
|
|
|
//
|
|
|
|
|
// Created by Thomas Harte on 01/10/2011.
|
|
|
|
|
// Copyright 2011 Thomas Harte. All rights reserved.
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
#include "FIRFilter.hpp"
|
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
|
|
using namespace SignalProcessing;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
|
|
A Kaiser-Bessel filter is a real time window filter. It looks at the last n samples
|
|
|
|
|
of an incoming data source and computes a filtered value, which is the value you'd
|
|
|
|
|
get after applying the specified filter, at the centre of the sampling window.
|
|
|
|
|
|
|
|
|
|
Hence, if you request a 37 tap filter then filtering introduces a latency of 18
|
|
|
|
|
samples. Suppose you're receiving input at 44,100Hz and using 4097 taps, then you'll
|
|
|
|
|
introduce a latency of 2048 samples, which is about 46ms.
|
|
|
|
|
|
|
|
|
|
There's a correlation between the number of taps and the quality of the filtering.
|
|
|
|
|
More samples = better filtering, at the cost of greater latency. Internally, applying
|
|
|
|
|
the filter involves calculating a weighted sum of previous values, so increasing the
|
|
|
|
|
number of taps is quite cheap in processing terms.
|
|
|
|
|
|
|
|
|
|
Original source for this filter:
|
|
|
|
|
|
|
|
|
|
"DIGITAL SIGNAL PROCESSING, II", IEEE Press, pages 123–126.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// our little fixed point scheme
|
|
|
|
|
#define kCSKaiserBesselFilterFixedMultiplier 32767.0f
|
|
|
|
|
#define kCSKaiserBesselFilterFixedShift 15
|
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
|
/*! Evaluates the 0th order Bessel function at @c a. */
|
|
|
|
|
float FIRFilter::ino(float a) {
|
2016-03-16 01:05:20 +00:00
|
|
|
|
float d = 0.0f;
|
|
|
|
|
float ds = 1.0f;
|
|
|
|
|
float s = 1.0f;
|
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
|
do {
|
2016-03-16 01:05:20 +00:00
|
|
|
|
d += 2.0f;
|
|
|
|
|
ds *= (a * a) / (d * d);
|
|
|
|
|
s += ds;
|
2017-03-26 18:34:47 +00:00
|
|
|
|
} while(ds > s*1e-6f);
|
2016-03-16 01:05:20 +00:00
|
|
|
|
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
|
void FIRFilter::coefficients_for_idealised_filter_response(short *filterCoefficients, float *A, float attenuation, unsigned int numberOfTaps) {
|
2016-03-16 01:05:20 +00:00
|
|
|
|
/* calculate alpha, which is the Kaiser-Bessel window shape factor */
|
|
|
|
|
float a; // to take the place of alpha in the normal derivation
|
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
|
if(attenuation < 21.0f) {
|
2016-03-16 01:05:20 +00:00
|
|
|
|
a = 0.0f;
|
2017-03-26 18:34:47 +00:00
|
|
|
|
} else {
|
2016-03-16 01:05:20 +00:00
|
|
|
|
if(attenuation > 50.0f)
|
|
|
|
|
a = 0.1102f * (attenuation - 8.7f);
|
|
|
|
|
else
|
|
|
|
|
a = 0.5842f * powf(attenuation - 21.0f, 0.4f) + 0.7886f * (attenuation - 21.0f);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-16 01:33:18 +00:00
|
|
|
|
float *filterCoefficientsFloat = new float[numberOfTaps];
|
2016-03-16 01:05:20 +00:00
|
|
|
|
|
|
|
|
|
/* work out the right hand side of the filter coefficients */
|
|
|
|
|
unsigned int Np = (numberOfTaps - 1) / 2;
|
2016-03-22 02:01:25 +00:00
|
|
|
|
float I0 = ino(a);
|
2016-03-16 01:05:20 +00:00
|
|
|
|
float NpSquared = (float)(Np * Np);
|
2017-03-26 18:34:47 +00:00
|
|
|
|
for(unsigned int i = 0; i <= Np; i++) {
|
2016-03-16 01:05:20 +00:00
|
|
|
|
filterCoefficientsFloat[Np + i] =
|
|
|
|
|
A[i] *
|
2016-03-22 02:01:25 +00:00
|
|
|
|
ino(a * sqrtf(1.0f - ((float)(i * i) / NpSquared) )) /
|
2016-03-16 01:05:20 +00:00
|
|
|
|
I0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* coefficients are symmetrical, so copy from right hand side to left side */
|
2017-03-26 18:34:47 +00:00
|
|
|
|
for(unsigned int i = 0; i < Np; i++) {
|
2016-03-16 01:05:20 +00:00
|
|
|
|
filterCoefficientsFloat[i] = filterCoefficientsFloat[numberOfTaps - 1 - i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* scale back up so that we retain 100% of input volume */
|
|
|
|
|
float coefficientTotal = 0.0f;
|
2017-03-26 18:34:47 +00:00
|
|
|
|
for(unsigned int i = 0; i < numberOfTaps; i++) {
|
2016-03-16 01:05:20 +00:00
|
|
|
|
coefficientTotal += filterCoefficientsFloat[i];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* we'll also need integer versions, potentially */
|
|
|
|
|
float coefficientMultiplier = 1.0f / coefficientTotal;
|
2017-03-26 18:34:47 +00:00
|
|
|
|
for(unsigned int i = 0; i < numberOfTaps; i++) {
|
2016-03-16 01:05:20 +00:00
|
|
|
|
filterCoefficients[i] = (short)(filterCoefficientsFloat[i] * kCSKaiserBesselFilterFixedMultiplier * coefficientMultiplier);
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-16 01:33:18 +00:00
|
|
|
|
delete[] filterCoefficientsFloat;
|
2016-03-16 01:05:20 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
|
void FIRFilter::get_coefficients(float *coefficients) {
|
|
|
|
|
for(unsigned int i = 0; i < number_of_taps_; i++) {
|
2016-03-22 02:01:25 +00:00
|
|
|
|
coefficients[i] = (float)filter_coefficients_[i] / kCSKaiserBesselFilterFixedMultiplier;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
|
FIRFilter::FIRFilter(unsigned int number_of_taps, float input_sample_rate, float low_frequency, float high_frequency, float attenuation) {
|
2016-03-16 01:05:20 +00:00
|
|
|
|
// we must be asked to filter based on an odd number of
|
|
|
|
|
// taps, and at least three
|
|
|
|
|
if(number_of_taps < 3) number_of_taps = 3;
|
|
|
|
|
if(attenuation < 21.0f) attenuation = 21.0f;
|
|
|
|
|
|
|
|
|
|
// ensure we have an odd number of taps
|
|
|
|
|
number_of_taps |= 1;
|
|
|
|
|
|
|
|
|
|
// store instance variables
|
|
|
|
|
number_of_taps_ = number_of_taps;
|
|
|
|
|
filter_coefficients_ = new short[number_of_taps_];
|
|
|
|
|
|
|
|
|
|
/* calculate idealised filter response */
|
|
|
|
|
unsigned int Np = (number_of_taps - 1) / 2;
|
2016-04-23 18:16:49 +00:00
|
|
|
|
float twoOverSampleRate = 2.0f / input_sample_rate;
|
2016-03-16 01:05:20 +00:00
|
|
|
|
|
|
|
|
|
float *A = new float[Np+1];
|
2016-04-23 18:16:49 +00:00
|
|
|
|
A[0] = 2.0f * (high_frequency - low_frequency) / input_sample_rate;
|
2017-03-26 18:34:47 +00:00
|
|
|
|
for(unsigned int i = 1; i <= Np; i++) {
|
2016-03-16 01:05:20 +00:00
|
|
|
|
float iPi = (float)i * (float)M_PI;
|
|
|
|
|
A[i] =
|
|
|
|
|
(
|
|
|
|
|
sinf(twoOverSampleRate * iPi * high_frequency) -
|
|
|
|
|
sinf(twoOverSampleRate * iPi * low_frequency)
|
|
|
|
|
) / iPi;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-22 02:01:25 +00:00
|
|
|
|
FIRFilter::coefficients_for_idealised_filter_response(filter_coefficients_, A, attenuation, number_of_taps_);
|
2016-03-16 01:05:20 +00:00
|
|
|
|
|
|
|
|
|
/* clean up */
|
|
|
|
|
delete[] A;
|
|
|
|
|
}
|
|
|
|
|
|
2017-03-26 18:34:47 +00:00
|
|
|
|
FIRFilter::~FIRFilter() {
|
2016-03-16 01:05:20 +00:00
|
|
|
|
delete[] filter_coefficients_;
|
|
|
|
|
}
|