#![allow(dead_code)]
use crate::core::scalar::ControlScalar;
#[derive(Debug, Clone, Copy)]
pub struct GradientMonitor<S: ControlScalar> {
pub max_gradient: S,
prev_value: S,
prev_derivative: S,
initialized: bool,
has_derivative: bool,
}
impl<S: ControlScalar> GradientMonitor<S> {
pub fn new(max_gradient: S) -> Self {
Self {
max_gradient,
prev_value: S::ZERO,
prev_derivative: S::ZERO,
initialized: false,
has_derivative: false,
}
}
pub fn check(&mut self, value: S, dt: S) -> bool {
if dt <= S::ZERO {
return true;
}
if !self.initialized {
self.prev_value = value;
self.initialized = true;
return true;
}
let derivative = (value - self.prev_value) / dt;
if !self.has_derivative {
self.prev_derivative = derivative;
self.prev_value = value;
self.has_derivative = true;
return true;
}
let gradient = (derivative - self.prev_derivative) / dt;
self.prev_derivative = derivative;
self.prev_value = value;
let abs_gradient = if gradient >= S::ZERO {
gradient
} else {
S::ZERO - gradient
};
abs_gradient <= self.max_gradient
}
pub fn derivative(&self) -> S {
self.prev_derivative
}
pub fn reset(&mut self) {
self.prev_value = S::ZERO;
self.prev_derivative = S::ZERO;
self.initialized = false;
self.has_derivative = false;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn constant_signal_no_violation() {
let mut mon = GradientMonitor::new(10.0_f64);
for _ in 0..10 {
assert!(mon.check(5.0, 0.01));
}
}
#[test]
fn linear_ramp_no_violation() {
let mut mon = GradientMonitor::new(1.0_f64);
let dt = 0.1_f64;
for i in 0..20 {
let v = 2.0 * (i as f64) * dt;
assert!(mon.check(v, dt), "step {i}");
}
}
#[test]
fn step_change_triggers_violation() {
let mut mon = GradientMonitor::new(1.0_f64);
let dt = 0.01_f64;
mon.check(0.0, dt);
mon.check(0.0, dt);
let ok = mon.check(100.0, dt);
assert!(!ok, "should detect large gradient");
}
#[test]
fn reset_clears_state() {
let mut mon = GradientMonitor::new(1.0_f64);
mon.check(0.0, 0.01);
mon.check(1.0, 0.01);
mon.check(100.0, 0.01);
mon.reset();
assert!(mon.check(0.0, 0.01));
assert!(mon.check(50.0, 0.01)); assert_eq!(mon.derivative(), 50.0 / 0.01);
}
}