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 14 : class EMA { 43 : public: 44 : /** 45 : * @brief Filter the input: Given @f$ x[n] @f$, calculate @f$ y[n] @f$. 46 : * 47 : * @param input 48 : * The new raw input value. 49 : * @return The new filtered output value. 50 : */ 51 2030 : uint_t filter(uint_t input) { 52 2030 : filtered += input; 53 2030 : uint_t output = (filtered + fixedPointAHalf) >> K; 54 2030 : filtered -= output; 55 2030 : return output; 56 : } 57 : 58 : /** 59 : * @brief Filter the input: Given @f$ x[n] @f$, calculate @f$ y[n] @f$. 60 : * 61 : * @param value 62 : * The new raw input value. 63 : * @return The new filtered output value. 64 : */ 65 2012 : uint_t operator()(uint_t value) { return filter(value); } 66 : 67 : static_assert( 68 : uint_t(0) < uint_t(-1), // Check that `uint_t` is an unsigned type 69 : "Error: the uint_t type should be an unsigned integer, otherwise, " 70 : "the division using bit shifts is invalid."); 71 : 72 : private: 73 14 : uint_t filtered = 0; 74 : constexpr static uint_t fixedPointAHalf = 1 << (K - 1); 75 : }; 76 : 77 : // -------------------------------------------------------------------------- // 78 : 79 : /** 80 : * @brief A class for single-pole infinite impulse response filters 81 : * or exponential moving average filters. 82 : * 83 : * This version uses floating point maths. 84 : * 85 : * Difference equation: @f$ y[n] = \alpha·x[n]+(1-\alpha)·y[n-1] @f$ 86 : * @f$ x @f$ is the input sequence, and @f$ y @f$ is the output sequence. 87 : * 88 : * [An in-depth explanation of the EMA filter] 89 : * (https://tttapa.github.io/Pages/Mathematics/Systems-and-Control-Theory/Digital-filters/Exponential%20Moving%20Average/) 90 : * 91 : * @ingroup AH_Filters 92 : */ 93 : class EMA_f { 94 : public: 95 : /** 96 : * @brief Create an exponential moving average filter with a pole at the 97 : * given location. 98 : * 99 : * @param pole 100 : * The pole of the filter (@f$1-\alpha@f$). 101 : * Should be a value in the range 102 : * @f$ \left[0,1\right) @f$. 103 : * Zero means no filtering, and closer to one means more filtering. 104 : */ 105 1 : EMA_f(float pole) : alpha(1 - pole) {} 106 : 107 : /** 108 : * @brief Filter the input: Given @f$ x[n] @f$, calculate @f$ y[n] @f$. 109 : * 110 : * @param value 111 : * The new raw input value. 112 : * @return The new filtered output value. 113 : */ 114 12 : float filter(float value) { 115 12 : filtered += (value - filtered) * alpha; 116 12 : return filtered; 117 : } 118 : 119 : /** 120 : * @brief Filter the input: Given @f$ x[n] @f$, calculate @f$ y[n] @f$. 121 : * 122 : * @param value 123 : * The new raw input value. 124 : * @return The new filtered output value. 125 : */ 126 12 : float operator()(float value) { return filter(value); } 127 : 128 : private: 129 : float alpha; 130 1 : float filtered = 0; 131 : }; 132 : 133 : AH_DIAGNOSTIC_POP()