diff --git a/libraries/Filter/LowPassFilter.cpp b/libraries/Filter/LowPassFilter.cpp new file mode 100644 index 0000000000..6d1ffb261e --- /dev/null +++ b/libraries/Filter/LowPassFilter.cpp @@ -0,0 +1,94 @@ +// +/// @file LowPassFilter.cpp +/// @brief A class to implement a low pass filter without losing precision even for int types +/// the downside being that it's a little slower as it internally uses a float +/// and it consumes an extra 4 bytes of memory to hold the constant gain + + +#include "LowPassFilter.h" + +//////////////////////////////////////////////////////////////////////////////////////////// +// DigitalLPF +//////////////////////////////////////////////////////////////////////////////////////////// + +template +DigitalLPF::DigitalLPF() { + // built in initialization + _output = T(); +} + +// add a new raw value to the filter, retrieve the filtered result +template +T DigitalLPF::apply(const T &sample, float cutoff_freq, float dt) { + if (cutoff_freq <= 0.0f || dt <= 0.0f) { + _output = sample; + return _output; + } + + float rc = 1.0f/(M_2PI_F*cutoff_freq); + float alpha = constrain_float(dt/(dt+rc), 0.0f, 1.0f); + _output += (sample - _output) * alpha; + return _output; +} + +// get latest filtered value from filter (equal to the value returned by latest call to apply method) +template +const T &DigitalLPF::get() const { + return _output; +} + +template +void DigitalLPF::reset(T value) { + _output = value; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// LowPassFilter +//////////////////////////////////////////////////////////////////////////////////////////// +template +LowPassFilter::LowPassFilter() : _cutoff_freq(0.0f) { + +} +// constructor +template +LowPassFilter::LowPassFilter(float cutoff_freq) : _cutoff_freq(cutoff_freq) { + +} + +// change parameters +template +void LowPassFilter::set_cutoff_frequency(float cutoff_freq) { + _cutoff_freq = cutoff_freq; +} + +// return the cutoff frequency +template +float LowPassFilter::get_cutoff_freq(void) const { + return _cutoff_freq; +} + +template +T LowPassFilter::apply(T sample, float dt) { + return _filter.apply(sample, _cutoff_freq, dt); +} + +template +const T &LowPassFilter::get() const { + return _filter.get(); +} + +template +void LowPassFilter::reset(T value) { + _filter.reset(value); +} + +/* + * Make an instances + * Otherwise we have to move the constructor implementations to the header file :P + */ +template class LowPassFilter; +template class LowPassFilter; +template class LowPassFilter; +template class LowPassFilter; +template class LowPassFilter; +template class LowPassFilter; \ No newline at end of file diff --git a/libraries/Filter/LowPassFilter.h b/libraries/Filter/LowPassFilter.h index c551c18537..5ac478ca1b 100644 --- a/libraries/Filter/LowPassFilter.h +++ b/libraries/Filter/LowPassFilter.h @@ -27,156 +27,67 @@ #include "FilterClass.h" // DigitalLPF implements the filter math -class DigitalLPF -{ +template +class DigitalLPF { public: - // constructor - DigitalLPF() : - _output(0.0f) {} - struct lpf_params { float cutoff_freq; float sample_freq; float alpha; - }; + }; + DigitalLPF(); // add a new raw value to the filter, retrieve the filtered result - float apply(float sample, float cutoff_freq, float dt) { - if (cutoff_freq <= 0.0f || dt <= 0.0f) { - _output = sample; - return _output; - } - - float rc = 1.0f/(M_2PI_F*cutoff_freq); - float alpha = constrain_float(dt/(dt+rc), 0.0f, 1.0f); - _output += (sample - _output) * alpha; - return _output; - } - + T apply(const T &sample, float cutoff_freq, float dt); // get latest filtered value from filter (equal to the value returned by latest call to apply method) - float get() const { - return _output; - } - - void reset(float value) { _output = value; } + const T &get() const; + void reset(T value); private: - float _output; + T _output; }; // LPF base class -class LowPassFilter -{ +template +class LowPassFilter { public: - LowPassFilter() : - _cutoff_freq(0.0f) { } - // constructor - LowPassFilter(float cutoff_freq) : - _cutoff_freq(cutoff_freq) { } + LowPassFilter(); + LowPassFilter(float cutoff_freq); // change parameters - void set_cutoff_frequency(float cutoff_freq) { - _cutoff_freq = cutoff_freq; - } - + void set_cutoff_frequency(float cutoff_freq); // return the cutoff frequency - float get_cutoff_freq(void) const { - return _cutoff_freq; - } - + float get_cutoff_freq(void) const; + T apply(T sample, float dt); + const T &get() const; + void reset(T value); + protected: float _cutoff_freq; -}; - -// LPF for a single float -class LowPassFilterFloat : public LowPassFilter -{ -public: - LowPassFilterFloat() : - LowPassFilter() {} - - LowPassFilterFloat(float cutoff_freq): - LowPassFilter(cutoff_freq) {} - - float apply(float sample, float dt) { - return _filter.apply(sample, _cutoff_freq, dt); - } - - float get() const { - return _filter.get(); - } - - void reset(float value) { - _filter.reset(value); - } -private: - DigitalLPF _filter; -}; - -// LPF for a 2D vector -class LowPassFilterVector2f : public LowPassFilter -{ -public: - LowPassFilterVector2f() : - LowPassFilter() {} - - LowPassFilterVector2f(float cutoff_freq) : - LowPassFilter(cutoff_freq) {} - - Vector2f apply(const Vector2f &sample, float dt) { - Vector2f ret; - ret.x = _filter_x.apply(sample.x, _cutoff_freq, dt); - ret.y = _filter_y.apply(sample.y, _cutoff_freq, dt); - return ret; - } - - void reset(const Vector2f& value) { - _filter_x.reset(value.x); - _filter_y.reset(value.y); - } private: - DigitalLPF _filter_x; - DigitalLPF _filter_y; + DigitalLPF _filter; }; -// LPF for 3D vector -class LowPassFilterVector3f : public LowPassFilter -{ -public: - LowPassFilterVector3f() : - LowPassFilter() {} +// Uncomment this, if you decide to remove the instantiations in the implementation file +/* +template +LowPassFilter::LowPassFilter() : _cutoff_freq(0.0f) { + +} +// constructor +template +LowPassFilter::LowPassFilter(float cutoff_freq) : _cutoff_freq(cutoff_freq) { + +} +*/ - LowPassFilterVector3f(float cutoff_freq) : - LowPassFilter(cutoff_freq) {} +// typedefs for compatibility +typedef LowPassFilter LowPassFilterInt; +typedef LowPassFilter LowPassFilterLong; +typedef LowPassFilter LowPassFilterFloat; +typedef LowPassFilter LowPassFilterDouble; +typedef LowPassFilter LowPassFilterVector2f; +typedef LowPassFilter LowPassFilterVector3f; - Vector3f apply(const Vector3f &sample, float dt) { - Vector3f ret; - ret.x = _filter_x.apply(sample.x, _cutoff_freq, dt); - ret.y = _filter_y.apply(sample.y, _cutoff_freq, dt); - ret.z = _filter_z.apply(sample.z, _cutoff_freq, dt); - return ret; - } - - // get latest filtered value from filter (equal to the value returned by latest call to apply method) - Vector3f get() const { - Vector3f ret; - ret.x = _filter_x.get(); - ret.y = _filter_y.get(); - ret.z = _filter_z.get(); - return ret; - } - - void reset(const Vector3f& value) { - _filter_x.reset(value.x); - _filter_y.reset(value.y); - _filter_z.reset(value.z); - } - -private: - DigitalLPF _filter_x; - DigitalLPF _filter_y; - DigitalLPF _filter_z; -}; - -#endif // __LOW_PASS_FILTER_H__ +#endif // __LOW_PASS_FILTER_H__ \ No newline at end of file diff --git a/libraries/Filter/LowPassFilter2p.cpp b/libraries/Filter/LowPassFilter2p.cpp index dbd0972dc3..dd963d5644 100644 --- a/libraries/Filter/LowPassFilter2p.cpp +++ b/libraries/Filter/LowPassFilter2p.cpp @@ -1,39 +1,24 @@ -// -*- tab-width: 4; Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- - -/* - 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 . - */ - -/// @file LowPassFilter.cpp -/// @brief A class to implement a second order low pass filter -/// Author: Leonard Hall - -#include -#include #include "LowPassFilter2p.h" -float DigitalBiquadFilter::apply(float sample, const struct biquad_params ¶ms) -{ + +//////////////////////////////////////////////////////////////////////////////////////////// +// DigitalBiquadFilter +//////////////////////////////////////////////////////////////////////////////////////////// + +template +DigitalBiquadFilter::DigitalBiquadFilter() { + _delay_element_1 = T(); + _delay_element_2 = T(); +} + +template +T DigitalBiquadFilter::apply(const T &sample, const struct biquad_params ¶ms) { if(is_zero(params.cutoff_freq) || is_zero(params.sample_freq)) { return sample; } - float delay_element_0 = sample - _delay_element_1 * params.a1 - _delay_element_2 * params.a2; - if (isnan(delay_element_0) || isinf(delay_element_0)) { - delay_element_0 = sample; - } - float output = delay_element_0 * params.b0 + _delay_element_1 * params.b1 + _delay_element_2 * params.b2; + T delay_element_0 = sample - _delay_element_1 * params.a1 - _delay_element_2 * params.a2; + T output = delay_element_0 * params.b0 + _delay_element_1 * params.b1 + _delay_element_2 * params.b2; _delay_element_2 = _delay_element_1; _delay_element_1 = delay_element_0; @@ -41,8 +26,13 @@ float DigitalBiquadFilter::apply(float sample, const struct biquad_params ¶m return output; } -void DigitalBiquadFilter::compute_params(float sample_freq, float cutoff_freq, biquad_params &ret) -{ +template +void DigitalBiquadFilter::reset() { + _delay_element_1 = _delay_element_2 = T(); +} + +template +void DigitalBiquadFilter::compute_params(float sample_freq, float cutoff_freq, biquad_params &ret) { ret.cutoff_freq = cutoff_freq; ret.sample_freq = sample_freq; @@ -56,3 +46,53 @@ void DigitalBiquadFilter::compute_params(float sample_freq, float cutoff_freq, b ret.a1 = 2.0f*(ohm*ohm-1.0f)/c; ret.a2 = (1.0f-2.0f*cosf(PI/4.0f)*ohm+ohm*ohm)/c; } + + +//////////////////////////////////////////////////////////////////////////////////////////// +// LowPassFilter2p +//////////////////////////////////////////////////////////////////////////////////////////// + +template +LowPassFilter2p::LowPassFilter2p() { + memset(&_params, 0, sizeof(_params) ); +} + +// constructor +template +LowPassFilter2p::LowPassFilter2p(float sample_freq, float cutoff_freq) { + // set initial parameters + set_cutoff_frequency(sample_freq, cutoff_freq); +} + +// change parameters +template +void LowPassFilter2p::set_cutoff_frequency(float sample_freq, float cutoff_freq) { + DigitalBiquadFilter::compute_params(sample_freq, cutoff_freq, _params); +} + +// return the cutoff frequency +template +float LowPassFilter2p::get_cutoff_freq(void) const { + return _params.cutoff_freq; +} + +template +float LowPassFilter2p::get_sample_freq(void) const { + return _params.sample_freq; +} + +template +T LowPassFilter2p::apply(const T &sample) { + return _filter.apply(sample, _params); +} + +/* + * Make an instances + * Otherwise we have to move the constructor implementations to the header file :P + */ +template class LowPassFilter2p; +template class LowPassFilter2p; +template class LowPassFilter2p; +template class LowPassFilter2p; +template class LowPassFilter2p; +template class LowPassFilter2p; diff --git a/libraries/Filter/LowPassFilter2p.h b/libraries/Filter/LowPassFilter2p.h index 05f797bf1b..bab01c599e 100644 --- a/libraries/Filter/LowPassFilter2p.h +++ b/libraries/Filter/LowPassFilter2p.h @@ -18,17 +18,17 @@ #ifndef LOWPASSFILTER2P_H #define LOWPASSFILTER2P_H +#include +#include +#include + + /// @file LowPassFilter2p.h /// @brief A class to implement a second order low pass filter -/// Author: Leonard Hall - -class DigitalBiquadFilter -{ +/// @authors: Leonard Hall , template implmentation: Daniel Frenzel +template +class DigitalBiquadFilter { public: - DigitalBiquadFilter() : - _delay_element_1(0.0f), - _delay_element_2(0.0f){} - struct biquad_params { float cutoff_freq; float sample_freq; @@ -38,84 +38,59 @@ public: float b1; float b2; }; + + DigitalBiquadFilter(); - float apply(float sample, const struct biquad_params ¶ms); - - void reset() { _delay_element_1 = _delay_element_2 = 0.0f; } - + T apply(const T &sample, const struct biquad_params ¶ms); + void reset(); static void compute_params(float sample_freq, float cutoff_freq, biquad_params &ret); - + private: - float _delay_element_1; - float _delay_element_2; + T _delay_element_1; + T _delay_element_2; }; -class LowPassFilter2p -{ +template +class LowPassFilter2p { public: - LowPassFilter2p() { memset(&_params, 0, sizeof(_params)); } + LowPassFilter2p(); // constructor - LowPassFilter2p(float sample_freq, float cutoff_freq) { - // set initial parameters - set_cutoff_frequency(sample_freq, cutoff_freq); - } - + LowPassFilter2p(float sample_freq, float cutoff_freq); // change parameters - void set_cutoff_frequency(float sample_freq, float cutoff_freq) { - DigitalBiquadFilter::compute_params(sample_freq, cutoff_freq, _params); - } - + void set_cutoff_frequency(float sample_freq, float cutoff_freq); // return the cutoff frequency - float get_cutoff_freq(void) const { - return _params.cutoff_freq; - } - - float get_sample_freq(void) const { - return _params.sample_freq; - } + float get_cutoff_freq(void) const; + float get_sample_freq(void) const; + T apply(const T &sample); protected: - struct DigitalBiquadFilter::biquad_params _params; -}; - -class LowPassFilter2pfloat : public LowPassFilter2p -{ -public: - LowPassFilter2pfloat() : - LowPassFilter2p() {} - - LowPassFilter2pfloat(float sample_freq, float cutoff_freq): - LowPassFilter2p(sample_freq,cutoff_freq) {} - - float apply(float sample) { - return _filter.apply(sample, _params); - } + struct DigitalBiquadFilter::biquad_params _params; + private: - DigitalBiquadFilter _filter; + DigitalBiquadFilter _filter; }; -class LowPassFilter2pVector3f : public LowPassFilter2p -{ -public: - LowPassFilter2pVector3f() : - LowPassFilter2p() {} +// Uncomment this, if you decide to remove the instantiations in the implementation file +/* +template +LowPassFilter2p::LowPassFilter2p() { + memset(&_params, 0, sizeof(_params) ); +} - LowPassFilter2pVector3f(float sample_freq, float cutoff_freq) : - LowPassFilter2p(sample_freq,cutoff_freq) {} +// constructor +template +LowPassFilter2p::LowPassFilter2p(float sample_freq, float cutoff_freq) { + // set initial parameters + set_cutoff_frequency(sample_freq, cutoff_freq); +} +*/ - Vector3f apply(const Vector3f &sample) { - Vector3f ret; - ret.x = _filter_x.apply(sample.x, _params); - ret.y = _filter_y.apply(sample.y, _params); - ret.z = _filter_z.apply(sample.z, _params); - return ret; - } - -private: - DigitalBiquadFilter _filter_x; - DigitalBiquadFilter _filter_y; - DigitalBiquadFilter _filter_z; -}; +typedef LowPassFilter2p LowPassFilter2pInt; +typedef LowPassFilter2p LowPassFilter2pLong; +typedef LowPassFilter2p LowPassFilter2pFloat; +typedef LowPassFilter2p LowPassFilter2pDouble; +typedef LowPassFilter2p LowPassFilter2pVector2f; +typedef LowPassFilter2p LowPassFilter2pVector3f; -#endif // LOWPASSFILTER2P_H +#endif // LOWPASSFILTER2P_H \ No newline at end of file