Line data Source code
1 : #pragma once 2 : 3 : #include <AH/Settings/NamespaceSettings.hpp> 4 : 5 : #include <stdint.h> 6 : 7 : BEGIN_AH_NAMESPACE 8 : 9 : /// @addtogroup AH_Filters 10 : /// @{ 11 : 12 : /** 13 : * @brief A class for applying hysteresis to a given input. 14 : * 15 : * This reduces the noise by decreasing the resolution, and it prevents flipping 16 : * back and forth between two values. 17 : * 18 : * <b>An example for `BITS` = 7 and an input from 0 to 1023</b> 19 : * ``` 20 : * 7 ┌───◄───┬─── 21 : * o 6 ┌───◄───┼───►───┘ 22 : * u 5 ┌───◄───┼───►───┘ 23 : * t 4 ┌───◄───┼───►───┘ 24 : * p 3 ┌───◄───┼───►───┘ 25 : * u 2 ┌───◄───┼───►───┘ 26 : * t 1 ┌───◄───┼───►───┘ 27 : * 0 ────┴───►───┘ 28 : * 0 128 256 384 512 640 768 896 1023 29 : * i n p u t 30 : * ``` 31 : * 32 : * @tparam BITS 33 : * The number of bits to decrease in resolution. 34 : * Increasing this number will result in a decrease in fluctuations. 35 : */ 36 : template <uint8_t Bits, class T_in = uint16_t, class T_out = uint8_t> 37 : class Hysteresis { 38 : public: 39 : /** 40 : * @brief Update the hysteresis output with a new input value. 41 : * 42 : * @param inputLevel 43 : * The input to calculate the output level from. 44 : * @retval true 45 : * The output level has changed. 46 : * @retval false 47 : * The output level is still the same. 48 : */ 49 100 : bool update(T_in inputLevel) { 50 100 : T_in prevLevelFull = (T_in(prevLevel) << Bits) | offset; 51 100 : T_in lowerbound = prevLevel > 0 ? prevLevelFull - margin : 0; 52 100 : T_in upperbound = prevLevel < max_out ? prevLevelFull + margin : max_in; 53 100 : if (inputLevel < lowerbound || inputLevel > upperbound) { 54 34 : setValue(inputLevel); 55 34 : return true; 56 : } 57 66 : return false; 58 : } 59 : 60 : /** 61 : * @brief Get the current output level. 62 : * 63 : * @return The output level. 64 : */ 65 123 : T_out getValue() const { return prevLevel; } 66 : 67 : /** 68 : * @brief Forcefully update the internal state to the given level. 69 : */ 70 36 : void setValue(T_in inputLevel) { prevLevel = inputLevel >> Bits; } 71 : 72 : private: 73 : T_out prevLevel = 0; 74 : constexpr static T_in margin = (1ul << Bits) - 1ul; 75 : constexpr static T_in offset = Bits >= 1 ? 1ul << (Bits - 1) : 0; 76 : constexpr static T_in max_in = static_cast<T_in>(-1); 77 : constexpr static T_out max_out = static_cast<T_out>(max_in >> Bits); 78 : static_assert(max_in > 0, "Error: only unsigned types are supported"); 79 : }; 80 : 81 : template <class T_in, class T_out> 82 : class Hysteresis<0, T_in, T_out> { 83 : public: 84 10 : bool update(T_in inputLevel) { 85 10 : bool changed = inputLevel != prevLevel; 86 10 : prevLevel = inputLevel; 87 10 : return changed; 88 : } 89 : 90 10 : T_out getValue() const { return prevLevel; } 91 : void setValue(T_in inputLevel) const { prevLevel = inputLevel; } 92 : 93 : private: 94 : T_in prevLevel = 0; 95 : }; 96 : 97 : /// @} 98 : 99 : END_AH_NAMESPACE