use crate::error::SignalError;
use crate::scalar::Numeric;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Deadband<T: Numeric = f64> {
threshold: T,
recentered: bool,
}
impl<T: Numeric> Deadband<T> {
pub fn plain(threshold: T) -> Result<Self, SignalError> {
Self::build(threshold, false)
}
pub fn recentered(threshold: T) -> Result<Self, SignalError> {
Self::build(threshold, true)
}
#[inline]
#[must_use]
pub fn apply(&self, input: T) -> T {
if input.abs() <= self.threshold {
T::ZERO
} else if self.recentered {
input - self.threshold.copysign(input)
} else {
input
}
}
#[inline]
#[must_use]
pub fn threshold(&self) -> T {
self.threshold
}
fn build(threshold: T, recentered: bool) -> Result<Self, SignalError> {
if !threshold.is_finite() {
return Err(SignalError::NonFinite);
}
if threshold < T::ZERO {
return Err(SignalError::NegativeThreshold);
}
Ok(Self {
threshold,
recentered,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Hysteresis<T: Numeric = f64> {
lower: T,
upper: T,
is_high: bool,
}
impl<T: Numeric> Hysteresis<T> {
pub fn new(lower: T, upper: T) -> Result<Self, SignalError> {
if !lower.is_finite() || !upper.is_finite() {
return Err(SignalError::NonFinite);
}
if lower >= upper {
return Err(SignalError::ThresholdsOutOfOrder);
}
Ok(Self {
lower,
upper,
is_high: false,
})
}
#[inline]
#[must_use]
pub fn update(&mut self, input: T) -> bool {
if input > self.upper {
self.is_high = true;
} else if input < self.lower {
self.is_high = false;
}
self.is_high
}
#[inline]
pub fn reset(&mut self) {
self.is_high = false;
}
#[inline]
#[must_use]
pub fn is_high(&self) -> bool {
self.is_high
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SlewRateLimiter<T: Numeric = f64> {
rise_per_second: T,
fall_per_second: T,
dt: T,
state: T,
initialized: bool,
}
impl<T: Numeric> SlewRateLimiter<T> {
pub fn new(rise_per_second: T, fall_per_second: T, dt: T) -> Result<Self, SignalError> {
if !rise_per_second.is_finite() || !fall_per_second.is_finite() || !dt.is_finite() {
return Err(SignalError::NonFinite);
}
if dt <= T::ZERO {
return Err(SignalError::NonPositiveTimestep);
}
if rise_per_second <= T::ZERO || fall_per_second <= T::ZERO {
return Err(SignalError::NonPositiveRate);
}
Ok(Self {
rise_per_second,
fall_per_second,
dt,
state: T::ZERO,
initialized: false,
})
}
pub fn symmetric(rate_per_second: T, dt: T) -> Result<Self, SignalError> {
Self::new(rate_per_second, rate_per_second, dt)
}
#[inline]
#[must_use]
pub fn filter(&mut self, target: T) -> T {
if !self.initialized {
self.state = target;
self.initialized = true;
return self.state;
}
let up = self.rise_per_second * self.dt;
let down = self.fall_per_second * self.dt;
let step = target - self.state;
self.state += if step > up {
up
} else if step < -down {
-down
} else {
step
};
self.state
}
#[inline]
pub fn reset(&mut self) {
self.state = T::ZERO;
self.initialized = false;
}
#[inline]
#[must_use]
pub fn value(&self) -> T {
self.state
}
}