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 118 : bool update(T_in inputLevel) {
50 118 : T_in prevLevelFull = (T_in(prevLevel) << Bits) | offset;
51 118 : T_in lowerbound = prevLevel > 0 ? prevLevelFull - margin : 0;
52 118 : T_in upperbound = prevLevel < max_out ? prevLevelFull + margin : max_in;
53 118 : if (inputLevel < lowerbound || inputLevel > upperbound) {
54 52 : setValue(inputLevel);
55 52 : 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 141 : T_out getValue() const { return prevLevel; }
66 :
67 : /**
68 : * @brief Forcefully update the internal state to the given level.
69 : */
70 60 : 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) { prevLevel = inputLevel; }
92 :
93 : private:
94 : T_in prevLevel = 0;
95 : };
96 :
97 : /// @}
98 :
99 : END_AH_NAMESPACE
|