LCOV - code coverage report
Current view: top level - src/Filters - IIRFilter.hpp (source / functions) Hit Total Coverage
Test: a11a239a890b6a41006679c4a235ba6b91b3e883 Lines: 62 62 100.0 %
Date: 2022-05-08 12:14:49 Functions: 30 30 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : #pragma once
       2             : 
       3             : #include <AH/Containers/Array.hpp>
       4             : #include <AH/STL/type_traits>
       5             : #include <Filters/TransferFunction.hpp>
       6             : 
       7             : /// @addtogroup FilterImplementations
       8             : /// @{
       9             : 
      10             : /** 
      11             :  * @brief   Infinite Impulse Response filter implementation that does not 
      12             :  *          normalize the coefficients upon initialization, the division by 
      13             :  *          @f$ a_0 @f$ is carried out on each filter iteration.
      14             :  * 
      15             :  * This class is slower than @ref NormalizingIIRFilter, but it works better
      16             :  * for integer types, because it has no rounding error on the coefficients.
      17             :  * 
      18             :  * Implements the following difference equation using the Direct-Form 1 
      19             :  * implementation:
      20             :  * 
      21             :  * @f[
      22             :  * y[n] = \frac{1}{a_0} \left(\sum_{i=0}^{N_b-1} b_i \cdot x[n-i]
      23             :  *                          - \sum_{i=1}^{N_a-1} a_i \cdot y[n-i] \right)
      24             :  * @f]
      25             :  */
      26             : template <uint8_t NB, uint8_t NA, class T>
      27             : class NonNormalizingIIRFilter {
      28             :   public:
      29             :     /**
      30             :      * @brief   Construct a new Non-Normalizing IIR Filter object.
      31             :      * 
      32             :      * The coefficients @f$ b @f$ and @f$ a @f$ can be derived from the transfer
      33             :      * function:
      34             :      * 
      35             :      * @f[
      36             :      * H(z) = \frac{b_0 + b_1 z^{-1} + \ldots + b_{N_b} z ^{-N_b}}
      37             :      *             {a_0 + a_1 z^{-1} + \ldots + a_{N_b} z ^{-N_a}}
      38             :      * @f]
      39             :      * 
      40             :      * @param   b_coefficients 
      41             :      *          The coefficients of the transfer function numerator.
      42             :      * @param   a_coefficients 
      43             :      *          The coefficients of the transfer function denominator.
      44             :      */
      45           4 :     NonNormalizingIIRFilter(const AH::Array<T, NB> &b_coefficients,
      46             :                             const AH::Array<T, NA> &a_coefficients)
      47           4 :         : a0(a_coefficients[0]) {
      48          32 :         for (uint8_t i = 0; i < 2 * NB - 1; ++i)
      49          28 :             this->b_coefficients[i] = b_coefficients[(2 * NB - 1 - i) % NB];
      50          20 :         for (uint8_t i = 0; i < 2 * MA - 1; ++i)
      51          16 :             this->a_coefficients[i] = a_coefficients[(2 * MA - 2 - i) % MA + 1];
      52           4 :     }
      53             : 
      54             :     NonNormalizingIIRFilter(const TransferFunction<NB, NA, T> &tf)
      55             :         : NonNormalizingIIRFilter{tf.b, tf.a} {}
      56             : 
      57             :     /**
      58             :      * @brief   Update the internal state with the new input @f$ x[n] @f$ and
      59             :      *          return the new output @f$ y[n] @f$.
      60             :      * 
      61             :      * @param   input 
      62             :      *          The new input @f$ x[n] @f$.
      63             :      * @return  The new output @f$ y[n] @f$.
      64             :      */
      65          80 :     T operator()(T input) {
      66             :         // Save the new input to the ring buffer.
      67          80 :         x[index_b] = input;
      68             : 
      69             :         // Calculate the offset to the shifted coefficients.
      70          80 :         T *b_coeff_shift = b_coefficients.end() - NB - index_b;
      71          80 :         T *a_coeff_shift = a_coefficients.end() - MA - index_a;
      72             : 
      73             :         // Multiply and accumulate the inputs and their respective coefficients.
      74          80 :         T acc = {};
      75         400 :         for (uint8_t i = 0; i < NB; i++)
      76         320 :             acc += x[i] * b_coeff_shift[i];
      77             : 
      78             :         // Multiply and accumulate the inputs and their respective coefficients.
      79         280 :         for (uint8_t i = 0; i < MA; i++)
      80         200 :             acc -= y[i] * a_coeff_shift[i];
      81             : 
      82             :         // Save the current output
      83          80 :         acc /= a0;
      84          80 :         y[index_a] = acc;
      85             : 
      86             :         // Increment and wrap around the index of the ring buffer.
      87          80 :         index_b++;
      88          80 :         if (index_b == NB)
      89          20 :             index_b = 0;
      90             : 
      91          80 :         index_a++;
      92          80 :         if (index_a == MA)
      93          35 :             index_a = 0;
      94             : 
      95          80 :         return acc;
      96             :     }
      97             : 
      98             :   private:
      99             :     constexpr static uint8_t MA = NA - 1;
     100             :     uint8_t index_b = 0, index_a = 0;
     101             :     AH::Array<T, NB> x = {};                 ///< Previous inputs
     102             :     AH::Array<T, MA> y = {};                 ///< Previous outputs
     103             :     AH::Array<T, 2 * NB - 1> b_coefficients; ///< Numerator coefficients
     104             :     AH::Array<T, 2 * MA - 1> a_coefficients; ///< Denominator coefficients
     105             :     T a0;
     106             : };
     107             : 
     108             : /** 
     109             :  * @brief   Infinite Impulse Response filter implementation that normalizes the
     110             :  *          coefficients upon initialization.
     111             :  * 
     112             :  * This class is faster than @ref NonNormalizingIIRFilter, because each filter 
     113             :  * iteration involves only addition and multiplication, no divisions.  
     114             :  * It works great for floating point numbers, but might be less ideal
     115             :  * for integer types, because it can create large rounding errors on the 
     116             :  * coefficients.
     117             :  * 
     118             :  * Implements the following difference equation using the Direct-Form 1 
     119             :  * implementation:
     120             :  * 
     121             :  * @f[
     122             :  * y[n] = \frac{1}{a_0} \left(\sum_{i=0}^{N_b-1} b_i \cdot x[n-i]
     123             :  *                          - \sum_{i=1}^{N_a-1} a_i \cdot y[n-i] \right)
     124             :  * @f]
     125             :  */
     126             : template <uint8_t NB, uint8_t NA, class T>
     127             : class NormalizingIIRFilter {
     128             :   public:
     129             :     /**
     130             :      * @brief   Construct a new Normalizing IIR Filter object.
     131             :      * 
     132             :      * The coefficients @f$ b @f$ and @f$ a @f$ can be derived from the transfer
     133             :      * function:
     134             :      * 
     135             :      * @f[
     136             :      * H(z) = \frac{b_0 + b_1 z^{-1} + \ldots + b_{N_b} z ^{-N_b}}
     137             :      *             {a_0 + a_1 z^{-1} + \ldots + a_{N_b} z ^{-N_a}}
     138             :      * @f]
     139             :      * 
     140             :      * @param   b_coefficients 
     141             :      *          The coefficients of the transfer function numerator.
     142             :      * @param   a_coefficients 
     143             :      *          The coefficients of the transfer function denominator.
     144             :      */
     145           5 :     NormalizingIIRFilter(const AH::Array<T, NB> &b_coefficients,
     146           5 :                          const AH::Array<T, NA> &a_coefficients) {
     147           5 :         T a0 = a_coefficients[0];
     148          50 :         for (uint8_t i = 0; i < 2 * NB - 1; ++i)
     149          90 :             this->b_coefficients[i] =
     150          90 :                 b_coefficients[(2 * NB - 1 - i) % NB] / a0;
     151          36 :         for (uint8_t i = 0; i < 2 * MA - 1; ++i)
     152          62 :             this->a_coefficients[i] =
     153          62 :                 a_coefficients[(2 * MA - 2 - i) % MA + 1] / a0;
     154           5 :     }
     155             : 
     156           1 :     NormalizingIIRFilter(const TransferFunction<NB, NA, T> &tf)
     157           1 :         : NormalizingIIRFilter{tf.b, tf.a} {}
     158             : 
     159             :     /**
     160             :      * @brief   Update the internal state with the new input @f$ x[n] @f$ and
     161             :      *          return the new output @f$ y[n] @f$.
     162             :      * 
     163             :      * @param   input 
     164             :      *          The new input @f$ x[n] @f$.
     165             :      * @return  The new output @f$ y[n] @f$.
     166             :      */
     167         100 :     T operator()(T input) {
     168             :         // Save the new input to the ring buffer.
     169         100 :         x[index_b] = input;
     170             : 
     171             :         // Calculate the offset to the shifted coefficients.
     172         100 :         T *b_coeff_shift = b_coefficients.end() - NB - index_b;
     173         100 :         T *a_coeff_shift = a_coefficients.end() - MA - index_a;
     174             : 
     175             :         // Multiply and accumulate the inputs and their respective coefficients.
     176         100 :         T acc = {};
     177         600 :         for (uint8_t i = 0; i < NB; i++)
     178         500 :             acc += x[i] * b_coeff_shift[i];
     179             : 
     180             :         // Multiply and accumulate the inputs and their respective coefficients.
     181         460 :         for (uint8_t i = 0; i < MA; i++)
     182         360 :             acc -= y[i] * a_coeff_shift[i];
     183             : 
     184             :         // Save the current output
     185         100 :         y[index_a] = acc;
     186             : 
     187             :         // Increment and wrap around the index of the ring buffer.
     188         100 :         index_b++;
     189         100 :         if (index_b == NB)
     190          22 :             index_b = 0;
     191             : 
     192         100 :         index_a++;
     193         100 :         if (index_a == MA)
     194          37 :             index_a = 0;
     195             : 
     196         100 :         return acc;
     197             :     }
     198             : 
     199             :   private:
     200             :     constexpr static uint8_t MA = NA - 1;
     201             :     uint8_t index_b = 0, index_a = 0;
     202             :     AH::Array<T, NB> x = {};
     203             :     AH::Array<T, MA> y = {};
     204             :     AH::Array<T, 2 * NB - 1> b_coefficients;
     205             :     AH::Array<T, 2 * MA - 1> a_coefficients;
     206             : };
     207             : 
     208             : /// @}
     209             : 
     210             : /// Select the @ref NormalizingIIRFilter implementation if @p T is a floating
     211             : /// point type, @ref NonNormalizingIIRFilter otherwise.
     212             : template <uint8_t NB, uint8_t NA, class T>
     213             : using IIRImplementation =
     214             :     typename std::conditional<std::is_floating_point<T>::value,
     215             :                               NormalizingIIRFilter<NB, NA, T>,
     216             :                               NonNormalizingIIRFilter<NB, NA, T>>::type;
     217             : 
     218             : /// @addtogroup Filters
     219             : /// @{
     220             : 
     221             : /** 
     222             :  * @brief   Generic Infinite Impulse Response filter class.
     223             :  * 
     224             :  * Uses the @ref NormalizingIIRFilter implementation for floating point 
     225             :  * types, and @ref NonNormalizingIIRFilter for all other types. 
     226             :  * 
     227             :  * Implements the following difference equation using the Direct-Form 1 
     228             :  * implementation:
     229             :  * 
     230             :  * @f[
     231             :  * y[n] = \frac{1}{a_0} \left(\sum_{i=0}^{N_b-1} b_i \cdot x[n-i]
     232             :  *                          - \sum_{i=1}^{N_a-1} a_i \cdot y[n-i] \right)
     233             :  * @f]
     234             :  */
     235             : template <uint8_t NB, uint8_t NA = NB, class T = float>
     236             : class IIRFilter : public IIRImplementation<NB, NA, T> {
     237             :   public:
     238             :     /**
     239             :      * @brief   Construct a new IIR Filter object.
     240             :      * 
     241             :      * The coefficients @f$ b @f$ and @f$ a @f$ can be derived from the transfer
     242             :      * function:
     243             :      * 
     244             :      * @f[
     245             :      * H(z) = \frac{b_0 + b_1 z^{-1} + \ldots + b_{N_b} z ^{-N_b}}
     246             :      *             {a_0 + a_1 z^{-1} + \ldots + a_{N_b} z ^{-N_a}}
     247             :      * @f]
     248             :      * 
     249             :      * @param   b_coefficients 
     250             :      *          The coefficients of the transfer function numerator.
     251             :      * @param   a_coefficients 
     252             :      *          The coefficients of the transfer function denominator.
     253             :      */
     254           8 :     IIRFilter(const AH::Array<T, NB> &b_coefficients,
     255             :               const AH::Array<T, NA> &a_coefficients)
     256           8 :         : IIRImplementation<NB, NA, T>{b_coefficients, a_coefficients} {}
     257             : 
     258           1 :     IIRFilter(const TransferFunction<NB, NA, T> &tf)
     259           1 :         : IIRImplementation<NB, NA, T>{tf} {}
     260             : 
     261             :     /**
     262             :      * @brief   Update the internal state with the new input @f$ x[n] @f$ and
     263             :      *          return the new output @f$ y[n] @f$.
     264             :      * 
     265             :      * @param   input 
     266             :      *          The new input @f$ x[n] @f$.
     267             :      * @return  The new output @f$ y[n] @f$.
     268             :      */
     269         180 :     T operator()(T input) {
     270         180 :         return IIRImplementation<NB, NA, T>::operator()(input);
     271             :     }
     272             : };
     273             : 
     274             : /// Create an IIRFilter from the given transfer function.
     275             : template <size_t NB, size_t NA, class T = float>
     276           1 : IIRFilter<NB, NA, T> makeIIRFilter(const TransferFunction<NB, NA, T> &tf) {
     277           1 :     return tf;
     278             : }
     279             : 
     280             : /// Create an IIRFilter from the given transfer function coefficients.
     281             : template <size_t NB, size_t NA, class T = float>
     282             : IIRFilter<NB, NA, T> makeIIRFilter(const AH::Array<T, NB> &b_coefficients,
     283             :                                    const AH::Array<T, NA> &a_coefficients) {
     284             :     return {b_coefficients, a_coefficients};
     285             : }
     286             : 
     287             : /// @}

Generated by: LCOV version 1.15