use crate::core::scalar::ControlScalar;
#[derive(Debug, Clone, Copy)]
pub struct BackEmfObserver<S: ControlScalar> {
pub r_s: S,
pub l_s: S,
pub omega_cutoff: S,
psi_alpha: S,
psi_beta: S,
i_alpha_prev: S,
i_beta_prev: S,
theta_e: S,
theta_e_prev: S,
omega_e: S,
omega_alpha: S,
initialized: bool,
}
impl<S: ControlScalar> BackEmfObserver<S> {
pub fn new(r_s: S, l_s: S, omega_cutoff: S, omega_filter: S) -> Self {
Self {
r_s,
l_s,
omega_cutoff,
psi_alpha: S::ZERO,
psi_beta: S::ZERO,
i_alpha_prev: S::ZERO,
i_beta_prev: S::ZERO,
theta_e: S::ZERO,
theta_e_prev: S::ZERO,
omega_e: S::ZERO,
omega_alpha: omega_filter,
initialized: false,
}
}
pub fn update(&mut self, v_alpha: S, v_beta: S, i_alpha: S, i_beta: S, dt: S) -> (S, S) {
if !self.initialized {
self.i_alpha_prev = i_alpha;
self.i_beta_prev = i_beta;
self.initialized = true;
return (S::ZERO, S::ZERO);
}
let di_alpha_dt = (i_alpha - self.i_alpha_prev) / dt;
let di_beta_dt = (i_beta - self.i_beta_prev) / dt;
let e_alpha = v_alpha - self.r_s * i_alpha - self.l_s * di_alpha_dt;
let e_beta = v_beta - self.r_s * i_beta - self.l_s * di_beta_dt;
self.psi_alpha += (e_alpha - self.omega_cutoff * self.psi_alpha) * dt;
self.psi_beta += (e_beta - self.omega_cutoff * self.psi_beta) * dt;
self.theta_e_prev = self.theta_e;
self.theta_e = (-self.psi_alpha).atan2(self.psi_beta);
let mut d_theta = self.theta_e - self.theta_e_prev;
let pi = S::PI;
let two_pi = S::TWO * pi;
if d_theta > pi {
d_theta -= two_pi;
} else if d_theta < -pi {
d_theta += two_pi;
}
let omega_raw = if dt > S::ZERO { d_theta / dt } else { S::ZERO };
let tau = if self.omega_alpha > S::ZERO {
S::ONE / self.omega_alpha
} else {
S::from_f64(0.01)
};
let alpha = S::ONE - (-dt / tau).exp();
self.omega_e += alpha * (omega_raw - self.omega_e);
self.i_alpha_prev = i_alpha;
self.i_beta_prev = i_beta;
(self.theta_e, self.omega_e)
}
pub fn theta_e(&self) -> S {
self.theta_e
}
pub fn omega_e(&self) -> S {
self.omega_e
}
pub fn flux_alpha(&self) -> S {
self.psi_alpha
}
pub fn flux_beta(&self) -> S {
self.psi_beta
}
pub fn reset(&mut self) {
self.psi_alpha = S::ZERO;
self.psi_beta = S::ZERO;
self.i_alpha_prev = S::ZERO;
self.i_beta_prev = S::ZERO;
self.theta_e = S::ZERO;
self.theta_e_prev = S::ZERO;
self.omega_e = S::ZERO;
self.initialized = false;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn observer_initializes() {
let mut obs = BackEmfObserver::new(0.5_f64, 0.001, 100.0, 50.0);
let (theta, omega) = obs.update(0.0, 0.0, 0.0, 0.0, 0.001);
assert_eq!(theta, 0.0);
assert_eq!(omega, 0.0);
}
#[test]
fn non_zero_voltage_updates_flux() {
let mut obs = BackEmfObserver::new(0.5_f64, 0.001, 100.0, 50.0);
for _ in 0..100 {
obs.update(10.0, 0.0, 1.0, 0.0, 0.001);
}
let total_flux =
(obs.flux_alpha() * obs.flux_alpha() + obs.flux_beta() * obs.flux_beta()).sqrt();
assert!(total_flux > 0.0, "flux should be non-zero");
}
#[test]
fn reset_clears_state() {
let mut obs = BackEmfObserver::new(0.5_f64, 0.001, 100.0, 50.0);
for _ in 0..50 {
obs.update(5.0, 5.0, 0.5, 0.5, 0.001);
}
obs.reset();
assert_eq!(obs.flux_alpha(), 0.0);
assert_eq!(obs.flux_beta(), 0.0);
assert_eq!(obs.omega_e(), 0.0);
}
#[test]
fn sinusoidal_back_emf_produces_angle() {
let mut obs = BackEmfObserver::new(0.1_f64, 0.001, 50.0, 20.0);
let dt = 0.0001;
let omega = 200.0_f64; let psi_f = 0.05_f64;
for step in 0..2000 {
let t = step as f64 * dt;
let e_alpha = -psi_f * omega * (omega * t).sin();
let e_beta = psi_f * omega * (omega * t).cos();
obs.update(e_alpha, e_beta, 0.0, 0.0, dt);
}
let omega_est = obs.omega_e().abs();
assert!(omega_est > 10.0, "omega_est={:.1}", omega_est);
}
}