use crate::core::scalar::ControlScalar;
#[derive(Debug, Clone, Copy)]
pub struct BemfDetector<S: ControlScalar> {
pub v_dc: S,
prev_bemf: S,
delay_acc: S,
delay_target: S,
period_acc: S,
pub electrical_period: S,
commutation_pending: bool,
initialized: bool,
}
impl<S: ControlScalar> BemfDetector<S> {
pub fn new(v_dc: S, initial_period: S) -> Self {
let delay = initial_period / S::from_f64(12.0); Self {
v_dc,
prev_bemf: S::ZERO,
delay_acc: S::ZERO,
delay_target: delay,
period_acc: S::ZERO,
electrical_period: initial_period,
commutation_pending: false,
initialized: false,
}
}
pub fn update(&mut self, bemf_voltage: S, dt: S) -> bool {
let mid = self.v_dc * S::HALF;
let crossed = if self.initialized {
(self.prev_bemf < mid && bemf_voltage >= mid)
|| (self.prev_bemf > mid && bemf_voltage <= mid)
} else {
false
};
if crossed {
if self.period_acc > S::ZERO {
let measured = self.period_acc * S::from_f64(6.0);
let alpha = S::from_f64(0.3);
self.electrical_period =
alpha * measured + (S::ONE - alpha) * self.electrical_period;
self.delay_target = self.electrical_period / S::from_f64(12.0); }
self.period_acc = S::ZERO;
self.delay_acc = S::ZERO;
self.commutation_pending = true;
}
self.period_acc += dt;
self.prev_bemf = bemf_voltage;
self.initialized = true;
if self.commutation_pending {
self.delay_acc += dt;
if self.delay_acc >= self.delay_target {
self.commutation_pending = false;
self.delay_acc = S::ZERO;
return true;
}
}
false
}
pub fn omega_e(&self) -> S {
if self.electrical_period > S::ZERO {
S::TWO * S::PI / self.electrical_period
} else {
S::ZERO
}
}
pub fn reset(&mut self) {
self.prev_bemf = S::ZERO;
self.delay_acc = S::ZERO;
self.period_acc = S::ZERO;
self.commutation_pending = false;
self.initialized = false;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn detects_zero_crossing_and_commutates() {
let mut det = BemfDetector::new(48.0_f64, 0.01);
let dt = 0.0001;
let mut commutations = 0;
let freq = 100.0_f64;
let steps = (0.1 / dt) as usize; for step in 0..steps {
let t = step as f64 * dt;
let bemf = 24.0 + 20.0 * (2.0 * core::f64::consts::PI * freq * t).sin();
if det.update(bemf, dt) {
commutations += 1;
}
}
assert!(
commutations > 5,
"Expected commutations, got {}",
commutations
);
}
#[test]
fn no_commutation_with_dc_signal() {
let mut det = BemfDetector::new(48.0_f64, 0.01);
let dt = 0.001;
let mut commutations = 0;
for _ in 0..100 {
if det.update(30.0, dt) {
commutations += 1;
}
}
assert_eq!(commutations, 0);
}
#[test]
fn omega_estimate_nonzero_after_detection() {
let mut det = BemfDetector::new(48.0_f64, 0.005);
let dt = 0.0001;
let freq = 200.0_f64;
for step in 0..2000 {
let t = step as f64 * dt;
let bemf = 24.0 + 20.0 * (2.0 * core::f64::consts::PI * freq * t).sin();
det.update(bemf, dt);
}
let omega = det.omega_e();
assert!(omega > 100.0, "omega={:.1}", omega);
}
}