From f94e4b43752664047f4a346864c259de227b7fbc Mon Sep 17 00:00:00 2001 From: Andrew Tridgell Date: Thu, 10 Nov 2016 11:42:58 +1100 Subject: [PATCH] Filter: allow 1P LowPassFilter to work without alpha recalc per sample this makes the 1P filter optionally syntax compatible with the 2P filter and much more CPU efficient. --- libraries/Filter/LowPassFilter.cpp | 31 ++++++++++++++++++++++-- libraries/Filter/LowPassFilter.h | 39 +++++++++++++++++++++++++----- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/libraries/Filter/LowPassFilter.cpp b/libraries/Filter/LowPassFilter.cpp index eba28c3922..be14b7148b 100644 --- a/libraries/Filter/LowPassFilter.cpp +++ b/libraries/Filter/LowPassFilter.cpp @@ -24,13 +24,29 @@ T DigitalLPF::apply(const T &sample, float cutoff_freq, float dt) { _output = sample; return _output; } - float rc = 1.0f/(M_2PI*cutoff_freq); - float alpha = constrain_float(dt/(dt+rc), 0.0f, 1.0f); + alpha = constrain_float(dt/(dt+rc), 0.0f, 1.0f); _output += (sample - _output) * alpha; return _output; } +template +T DigitalLPF::apply(const T &sample) { + _output += (sample - _output) * alpha; + return _output; +} + +template +void DigitalLPF::compute_alpha(float sample_freq, float cutoff_freq) { + if (cutoff_freq <= 0.0f || sample_freq <= 0.0f) { + alpha = 1.0; + } else { + float dt = 1.0/sample_freq; + float rc = 1.0f/(M_2PI*cutoff_freq); + alpha = constrain_float(dt/(dt+rc), 0.0f, 1.0f); + } +} + // get latest filtered value from filter (equal to the value returned by latest call to apply method) template const T &DigitalLPF::get() const { @@ -61,6 +77,12 @@ void LowPassFilter::set_cutoff_frequency(float cutoff_freq) { _cutoff_freq = cutoff_freq; } +template +void LowPassFilter::set_cutoff_frequency(float sample_freq, float cutoff_freq) { + _cutoff_freq = cutoff_freq; + _filter.compute_alpha(sample_freq, cutoff_freq); +} + // return the cutoff frequency template float LowPassFilter::get_cutoff_freq(void) const { @@ -72,6 +94,11 @@ T LowPassFilter::apply(T sample, float dt) { return _filter.apply(sample, _cutoff_freq, dt); } +template +T LowPassFilter::apply(T sample) { + return _filter.apply(sample); +} + template const T &LowPassFilter::get() const { return _filter.get(); diff --git a/libraries/Filter/LowPassFilter.h b/libraries/Filter/LowPassFilter.h index dffd006380..6781e28b67 100644 --- a/libraries/Filter/LowPassFilter.h +++ b/libraries/Filter/LowPassFilter.h @@ -18,6 +18,30 @@ /// @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 + +/* + Note that this filter can be used in 2 ways: + + 1) providing dt on every sample, and calling apply like this: + + // call once + filter.set_cutoff_frequency(frequency_hz); + + // then on each sample + output = filter.apply(sample, dt); + + 2) providing a sample freq and cutoff_freq once at start + + // call once + filter.set_cutoff_frequency(sample_freq, frequency_hz); + + // then on each sample + output = filter.apply(sample); + + The second approach is more CPU efficient as it doesn't have to + recalculate alpha each time, but it assumes that dt is constant + */ + #pragma once #include @@ -27,21 +51,20 @@ template class DigitalLPF { public: - struct lpf_params { - float cutoff_freq; - float sample_freq; - float alpha; - }; - DigitalLPF(); // add a new raw value to the filter, retrieve the filtered result T apply(const T &sample, float cutoff_freq, float dt); + T apply(const T &sample); + + void compute_alpha(float sample_freq, float cutoff_freq); + // get latest filtered value from filter (equal to the value returned by latest call to apply method) const T &get() const; void reset(T value); private: T _output; + float alpha = 1.0f; }; // LPF base class @@ -53,11 +76,15 @@ public: // change parameters void set_cutoff_frequency(float cutoff_freq); + void set_cutoff_frequency(float sample_freq, float cutoff_freq); + // return the cutoff frequency float get_cutoff_freq(void) const; T apply(T sample, float dt); + T apply(T sample); const T &get() const; void reset(T value); + void reset(void) { reset(T()); } protected: float _cutoff_freq;