use crate::core::scalar::{ControlScalar, PidScalar};
use crate::core::signal::{ControlOutput, Feedback, Setpoint};
pub trait Controller<S: PidScalar> {
fn update(&mut self, setpoint: &Setpoint<S>, feedback: &Feedback<S>, dt: S)
-> ControlOutput<S>;
fn reset(&mut self);
fn is_saturated(&self) -> bool;
}
pub trait Estimator<S: ControlScalar, const N: usize> {
fn predict(&mut self, dt: S);
fn correct(&mut self, measurement: &[S; N]);
fn state(&self) -> &[S; N];
fn covariance(&self) -> &[S; N];
}
pub trait Plant<S: ControlScalar> {
fn step(&mut self, u: S, dt: S);
fn output(&self) -> S;
fn state(&self) -> &[S];
}
#[cfg(test)]
mod tests {
use super::*;
struct MockController {
saturated: bool,
}
impl Controller<f64> for MockController {
fn update(
&mut self,
sp: &Setpoint<f64>,
fb: &Feedback<f64>,
_dt: f64,
) -> ControlOutput<f64> {
ControlOutput::new(sp.value() - fb.value())
}
fn reset(&mut self) {
self.saturated = false;
}
fn is_saturated(&self) -> bool {
self.saturated
}
}
#[test]
fn mock_controller_computes_error() {
let mut ctrl = MockController { saturated: false };
let sp = Setpoint::new(10.0);
let fb = Feedback::new(7.0);
let out = ctrl.update(&sp, &fb, 0.01);
assert!((out.value() - 3.0).abs() < 1e-10);
}
#[test]
fn mock_controller_reset() {
let mut ctrl = MockController { saturated: true };
assert!(ctrl.is_saturated());
ctrl.reset();
assert!(!ctrl.is_saturated());
}
}