LCOV - code coverage report
Current view: top level - src/Helpers - EMA.hpp (source / functions) Hit Total Coverage
Test: 19d2efc7037c2e176feca44750a12594c76f466f Lines: 14 14 100.0 %
Date: 2019-11-24 14:50:27 Functions: 9 9 100.0 %
Legend: Lines: hit not hit

          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             : };

Generated by: LCOV version 1.14-5-g4ff2ed6