LCOV - code coverage report
Current view: top level - src/AH/Filters - EMA.hpp (source / functions) Hit Total Coverage
Test: e224b347cd670555e44f06608ac41bd1ace9d9d8 Lines: 15 15 100.0 %
Date: 2020-09-08 17:44:46 Functions: 11 11 100.0 %
Legend: Lines: hit not hit

          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()

Generated by: LCOV version 1.14-6-g40580cd