LCOV - code coverage report
Current view: top level - src/AH/Filters - EMA.hpp (source / functions) Hit Total Coverage
Test: 90a1b9beff85a60dc6ebcea034a947a845e56960 Lines: 14 14 100.0 %
Date: 2019-11-30 15:53:32 Functions: 9 9 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          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()

Generated by: LCOV version 1.14-5-g4ff2ed6