use crate::core::scalar::ControlScalar;
use crate::motor::transform::clarke::AlphaBeta;
#[derive(Debug, Clone, Copy)]
pub struct SvpwmDuty<S: ControlScalar> {
pub ta: S,
pub tb: S,
pub tc: S,
}
pub fn svpwm<S: ControlScalar>(ab: &AlphaBeta<S>, vdc: S) -> SvpwmDuty<S> {
if vdc <= S::ZERO {
return SvpwmDuty {
ta: S::HALF,
tb: S::HALF,
tc: S::HALF,
};
}
let sqrt3 = S::from_f64(1.7320508075688772);
let inv_vdc = S::ONE / vdc;
let sqrt3_over2 = sqrt3 * S::HALF;
let va = ab.alpha;
let vb = -ab.alpha * S::HALF + sqrt3_over2 * ab.beta;
let vc = -ab.alpha * S::HALF - sqrt3_over2 * ab.beta;
let v_min = va.min(vb).min(vc);
let v_max = va.max(vb).max(vc);
let v_offset = -(v_min + v_max) * S::HALF;
let da = S::HALF + (va + v_offset) * inv_vdc * S::HALF;
let db = S::HALF + (vb + v_offset) * inv_vdc * S::HALF;
let dc = S::HALF + (vc + v_offset) * inv_vdc * S::HALF;
SvpwmDuty {
ta: da.clamp_val(S::ZERO, S::ONE),
tb: db.clamp_val(S::ZERO, S::ONE),
tc: dc.clamp_val(S::ZERO, S::ONE),
}
}
pub fn spwm<S: ControlScalar>(ab: &AlphaBeta<S>, vdc: S) -> SvpwmDuty<S> {
if vdc <= S::ZERO {
return SvpwmDuty {
ta: S::HALF,
tb: S::HALF,
tc: S::HALF,
};
}
let sqrt3_over2 = S::from_f64(1.7320508075688772 / 2.0);
let inv_vdc = S::ONE / vdc;
let va = ab.alpha;
let vb = -ab.alpha * S::HALF + sqrt3_over2 * ab.beta;
let vc = -ab.alpha * S::HALF - sqrt3_over2 * ab.beta;
let da = (S::HALF + va * inv_vdc * S::HALF).clamp_val(S::ZERO, S::ONE);
let db = (S::HALF + vb * inv_vdc * S::HALF).clamp_val(S::ZERO, S::ONE);
let dc = (S::HALF + vc * inv_vdc * S::HALF).clamp_val(S::ZERO, S::ONE);
SvpwmDuty {
ta: da,
tb: db,
tc: dc,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zero_voltage_gives_half_duty() {
let ab = AlphaBeta {
alpha: 0.0_f64,
beta: 0.0,
zero: 0.0,
};
let duty = svpwm(&ab, 24.0);
assert!((duty.ta - 0.5).abs() < 1e-6, "ta={}", duty.ta);
assert!((duty.tb - 0.5).abs() < 1e-6, "tb={}", duty.tb);
assert!((duty.tc - 0.5).abs() < 1e-6, "tc={}", duty.tc);
}
#[test]
fn duty_in_range() {
for angle_deg in (0..360).step_by(15) {
let theta = (angle_deg as f64) * core::f64::consts::PI / 180.0;
let ab = AlphaBeta {
alpha: theta.cos(),
beta: theta.sin(),
zero: 0.0,
};
let duty = svpwm(&ab, 24.0);
assert!(
duty.ta >= 0.0 && duty.ta <= 1.0,
"ta={} at {}°",
duty.ta,
angle_deg
);
assert!(
duty.tb >= 0.0 && duty.tb <= 1.0,
"tb={} at {}°",
duty.tb,
angle_deg
);
assert!(
duty.tc >= 0.0 && duty.tc <= 1.0,
"tc={} at {}°",
duty.tc,
angle_deg
);
}
}
#[test]
fn svpwm_symmetry() {
let ab = AlphaBeta {
alpha: 12.0_f64,
beta: 0.0,
zero: 0.0,
};
let duty = svpwm(&ab, 24.0);
assert!(duty.ta > duty.tb, "ta={}, tb={}", duty.ta, duty.tb);
assert!(
(duty.tb - duty.tc).abs() < 1e-10,
"tb={}, tc={}",
duty.tb,
duty.tc
);
}
#[test]
fn zero_vdc_gives_half_duty() {
let ab = AlphaBeta {
alpha: 1.0_f64,
beta: 0.0,
zero: 0.0,
};
let duty = svpwm(&ab, 0.0);
assert_eq!(duty.ta, 0.5);
assert_eq!(duty.tb, 0.5);
assert_eq!(duty.tc, 0.5);
}
}