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