# hysteresis.py : platform-independent non-linear filters

# No copyright, 2020-2011, Garth Zeglin.  This file is explicitly placed in
# the public domain.
#--------------------------------------------------------------------------------
class Hysteresis:
    def __init__(self, lower=16000, upper=48000):
        """Filter to quantize an input stream into a binary state.  Dual thresholds are
        needed to implement hysteresis: the input needs to rise above the upper
        threshold to trigger a high output, then drop below the input threshold
        to return to the low output.  One bit of state is required.
        """
        self.lower = lower
        self.upper = upper
        self.state = False

    def update(self, input):
        """Apply a new sample value to the quantizing filter.  Returns a boolean state."""
        if self.state is True:
            if input < self.lower:
                self.state = False
        else:
            if input > self.upper:
                self.state = True

        return self.state
    
#--------------------------------------------------------------------------------
class Suppress:
    def __init__(self, value=0):
        """Filter to suppress a specific value in an input stream."""
        self.suppress = value
        self.previous = None
        
    def update(self, input):
        if input != self.suppress:
            self.previous = input
        return self.previous

#--------------------------------------------------------------------------------
class Debounce:
    def __init__(self, samples=5):
        """Filter to 'debounce' an integer stream by suppressing changes from the previous value
        until a specific new value has been observed a minimum number of times."""

        self.samples = samples          # number of samples required to change        
        self.current_value = 0          # current stable value
        self.new_value = None           # possible new value        
        self.count = 0                  # count of new values observed

    def update(self, input):
        if input == self.current_value:
            # if the input is unchanged, keep the counter at zero            
            self.count = 0
            self.new_value = None
        else:
            if input != self.new_value:
                # start a new count
                self.new_value = input
                self.count = 1
            else:
                # count repeated changes
                self.count += 1
                if self.count >= self.samples:
                    # switch state after a sufficient number of changes
                    self.current_value = self.new_value
                    self.count = 0
                    self.new_value = None
                    
        return self.current_value
