Line data Source code
1 : /* ✔ */ 2 : 3 : #pragma once 4 : 5 : #include <stdint.h> 6 : 7 : /** 8 : * @brief A class for single-pole infinite impulse response filters 9 : * or exponential moving average filters. 10 : * 11 : * Optimized implementation of the difference equation with a slight 12 : * optimization by using a factor of two as the pole location (this means 13 : * that no division or floating point operations are required). 14 : * 15 : * Difference equation: @f$ y[n] = \alpha·x[n]+(1-\alpha)·y[n-1] @f$ 16 : * where @f$ \alpha = \left(\frac{1}{2}\right)^{K} @f$, @f$ x @f$ is the 17 : * input sequence, and @f$ y @f$ is the output sequence. 18 : * 19 : * [An in-depth explanation of the EMA filter] 20 : * (https://tttapa.github.io/Pages/Mathematics/Systems-and-Control-Theory/Digital-filters/Exponential%20Moving%20Average/) 21 : * 22 : * @tparam K 23 : * The amount of bits to shift by. This determines the location 24 : * of the pole in the EMA transfer function, and therefore the 25 : * cut-off frequency. 26 : * The higher this number, the more filtering takes place. 27 : * The pole location is @f$ 1 - 2^{-K} @f$. 28 : * @tparam uint_t 29 : * The (signed) integer type to use for the input, intermediate values 30 : * and the output. 31 : * Should be at least @f$ M+K @f$ bits wide, where @f$ M @f$ 32 : * is the maximum number of bits of the input. 33 : * In case of the Arduino's built-in ADC, 34 : * @f$ M = 10 = \log_2(1024) @f$. 35 : * 36 : * @ingroup Utilities 37 : */ 38 : template <uint8_t K, class uint_t> 39 14 : class EMA { 40 : public: 41 : /** 42 : * @brief Filter the input: Given @f$ x[n] @f$, calculate @f$ y[n] @f$. 43 : * 44 : * @param input 45 : * The new raw input value. 46 : * @return The new filtered output value. 47 : */ 48 2030 : uint_t filter(uint_t input) { 49 2030 : filtered += input; 50 2030 : uint_t output = (filtered + fixedPointAHalf) >> K; 51 2030 : filtered -= output; 52 2030 : return output; 53 : } 54 : 55 : /** 56 : * @brief Filter the input: Given @f$ x[n] @f$, calculate @f$ y[n] @f$. 57 : * 58 : * @param value 59 : * The new raw input value. 60 : * @return The new filtered output value. 61 : */ 62 2012 : uint_t operator()(uint_t value) { return filter(value); } 63 : 64 : static_assert( 65 : uint_t(0) < uint_t(-1), // Check that `uint_t` is an unsigned type 66 : "Error: the uint_t type should be an unsigned integer, otherwise, " 67 : "the division using bit shifts is invalid."); 68 : 69 : private: 70 14 : uint_t filtered = 0; 71 : constexpr static uint_t fixedPointAHalf = 1 << (K - 1); 72 : }; 73 : 74 : // -------------------------------------------------------------------------- // 75 : 76 : /** 77 : * @brief A class for single-pole infinite impulse response filters 78 : * or exponential moving average filters. 79 : * 80 : * This version uses floating point maths. 81 : * 82 : * Difference equation: @f$ y[n] = \alpha·x[n]+(1-\alpha)·y[n-1] @f$ 83 : * @f$ x @f$ is the input sequence, and @f$ y @f$ is the output sequence. 84 : * 85 : * [An in-depth explanation of the EMA filter] 86 : * (https://tttapa.github.io/Pages/Mathematics/Systems-and-Control-Theory/Digital-filters/Exponential%20Moving%20Average/) 87 : * 88 : * @ingroup Utilities 89 : */ 90 : class EMA_f { 91 : public: 92 : /** 93 : * @brief Create an exponential moving average filter with a pole at the 94 : * given location. 95 : * 96 : * @param pole 97 : * The pole of the filter (@f$1-\alpha@f$). 98 : * Should be a value in the range 99 : * @f$ \left[0,1\right) @f$. 100 : * Zero means no filtering, and closer to one means more filtering. 101 : */ 102 1 : EMA_f(float pole) : alpha(1 - pole) {} 103 : 104 : /** 105 : * @brief Filter the input: Given @f$ x[n] @f$, calculate @f$ y[n] @f$. 106 : * 107 : * @param value 108 : * The new raw input value. 109 : * @return The new filtered output value. 110 : */ 111 12 : float filter(float value) { 112 12 : filtered += (value - filtered) * alpha; 113 12 : return filtered; 114 : } 115 : 116 : /** 117 : * @brief Filter the input: Given @f$ x[n] @f$, calculate @f$ y[n] @f$. 118 : * 119 : * @param value 120 : * The new raw input value. 121 : * @return The new filtered output value. 122 : */ 123 12 : float operator()(float value) { return filter(value); } 124 : 125 : private: 126 : float alpha; 127 1 : float filtered = 0; 128 : };