macro_rules! impl_slew {
($name:ident, $ty:ty, $zero:expr) => {
#[derive(Debug, Clone)]
pub struct $name {
max_rate: $ty,
value: $ty,
initialized: bool,
}
impl $name {
#[inline]
pub fn new(max_rate: $ty) -> Result<Self, crate::ConfigError> {
#[allow(clippy::neg_cmp_op_on_partial_ord)]
if !(max_rate > $zero) {
return Err(crate::ConfigError::Invalid("max_rate must be positive"));
}
Ok(Self {
max_rate,
value: $zero,
initialized: false,
})
}
#[inline]
#[must_use]
pub fn update(&mut self, sample: $ty) -> $ty {
if !self.initialized {
self.value = sample;
self.initialized = true;
return sample;
}
self.value = sample.clamp(self.value - self.max_rate, self.value + self.max_rate);
self.value
}
#[inline]
#[must_use]
pub fn value(&self) -> $ty {
self.value
}
#[inline]
pub fn reset(&mut self) {
self.value = $zero;
self.initialized = false;
}
}
};
}
impl_slew!(SlewF64, f64, 0.0);
impl_slew!(SlewF32, f32, 0.0);
impl_slew!(SlewI64, i64, 0);
impl_slew!(SlewI32, i32, 0);
impl_slew!(SlewI128, i128, 0);
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[allow(clippy::float_cmp)]
fn spike_clamped() {
let mut s = SlewF64::new(10.0).unwrap();
assert_eq!(s.update(100.0), 100.0); assert_eq!(s.update(200.0), 110.0); assert_eq!(s.update(200.0), 120.0); }
#[test]
#[allow(clippy::float_cmp)]
fn gradual_passes_through() {
let mut s = SlewF64::new(10.0).unwrap();
assert_eq!(s.update(100.0), 100.0);
assert_eq!(s.update(105.0), 105.0); }
#[test]
#[allow(clippy::float_cmp)]
fn negative_clamped() {
let mut s = SlewF64::new(10.0).unwrap();
assert_eq!(s.update(100.0), 100.0);
assert_eq!(s.update(50.0), 90.0); }
#[test]
fn i64_basic() {
let mut s = SlewI64::new(5).unwrap();
assert_eq!(s.update(100), 100);
assert_eq!(s.update(200), 105);
}
#[test]
#[allow(clippy::float_cmp)]
fn reset() {
let mut s = SlewF64::new(10.0).unwrap();
let _ = s.update(100.0);
s.reset();
assert_eq!(s.update(50.0), 50.0); }
#[test]
fn rejects_zero_max_rate() {
assert!(matches!(
SlewF64::new(0.0),
Err(crate::ConfigError::Invalid(_))
));
assert!(matches!(
SlewI64::new(0),
Err(crate::ConfigError::Invalid(_))
));
}
#[test]
fn i128_basic() {
let mut s = SlewI128::new(5).unwrap();
assert_eq!(s.update(100), 100);
assert_eq!(s.update(200), 105);
}
}