use core::marker::PhantomData;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SchmittState {
Low,
High,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum HysteresisMode<T> {
None(PhantomData<T>),
ChangeThreshold { threshold: T },
SchmittTrigger { rising: T, falling: T },
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct HysteresisState<T> {
pub last_output: T,
pub schmitt_state: SchmittState,
}
impl<T> Default for HysteresisState<T>
where
T: Default,
{
fn default() -> Self {
Self {
last_output: T::default(),
schmitt_state: SchmittState::Low,
}
}
}
impl<T> HysteresisMode<T> {
pub const fn none() -> Self {
HysteresisMode::None(PhantomData)
}
}
impl<T> HysteresisMode<T>
where
T: Copy + PartialOrd + core::ops::Sub<Output = T> + core::ops::Add<Output = T>,
{
pub fn apply(&self, input: T, state: &mut HysteresisState<T>) -> T {
match self {
HysteresisMode::None(_) => input,
HysteresisMode::ChangeThreshold { threshold } => {
let diff = if input > state.last_output {
input - state.last_output
} else {
state.last_output - input
};
let output = if diff > *threshold {
input
} else {
state.last_output
};
state.last_output = output;
output
}
HysteresisMode::SchmittTrigger { rising, falling } => {
if input >= *rising {
state.schmitt_state = SchmittState::High;
} else if input <= *falling {
state.schmitt_state = SchmittState::Low;
}
let output = match state.schmitt_state {
SchmittState::High => *rising,
SchmittState::Low => *falling,
};
state.last_output = output;
output
}
}
}
pub fn validate(&self) -> Result<(), &'static str> {
match self {
HysteresisMode::None(_) => Ok(()),
HysteresisMode::ChangeThreshold { .. } => Ok(()),
HysteresisMode::SchmittTrigger { rising, falling } => {
if rising <= falling {
Err("Schmitt trigger: rising threshold must be greater than falling threshold")
} else {
Ok(())
}
}
}
}
}