use crate::core::scalar::ControlScalar;
#[derive(Debug, Clone, Copy)]
pub struct DcTestResult<S: ControlScalar> {
pub v_dc: S,
pub i_dc: S,
}
#[derive(Debug, Clone, Copy)]
pub struct NoLoadTestResult<S: ControlScalar> {
pub v_rms: S,
pub i_rms: S,
pub p_nl: S,
pub freq: S,
}
#[derive(Debug, Clone, Copy)]
pub struct LockedRotorTestResult<S: ControlScalar> {
pub v_rms: S,
pub i_rms: S,
pub p_sc: S,
pub freq: S,
}
#[derive(Debug, Clone, Copy)]
pub struct InductionMotorParams<S: ControlScalar> {
pub rs: S,
pub rr: S,
pub lls: S,
pub llr: S,
pub lm: S,
}
#[derive(Debug, Clone, Copy)]
pub struct PmsmParams<S: ControlScalar> {
pub rs: S,
pub ld: S,
pub lq: S,
pub ke: S,
}
impl<S: ControlScalar> PmsmParams<S> {
pub fn identify_rs(test: &DcTestResult<S>) -> S {
test.v_dc / (S::TWO * test.i_dc)
}
pub fn identify_ls(v_rms: S, i_rms: S, rs: S, freq: S) -> S {
let z = v_rms / i_rms;
let x_sq = z * z - rs * rs;
if x_sq <= S::ZERO {
return S::ZERO;
}
let x_l = x_sq.sqrt();
x_l / (S::TWO * S::PI * freq)
}
pub fn identify_ke(v_bemf_rms: S, omega_e: S) -> S {
if omega_e.abs() > S::from_f64(0.1) {
v_bemf_rms / omega_e
} else {
S::ZERO
}
}
}
impl<S: ControlScalar> InductionMotorParams<S> {
pub fn identify(
dc: &DcTestResult<S>,
no_load: &NoLoadTestResult<S>,
locked_rotor: &LockedRotorTestResult<S>,
) -> Self {
let two_pi = S::TWO * S::PI;
let rs = dc.v_dc / (S::TWO * dc.i_dc);
let omega_nl = two_pi * no_load.freq;
let z_nl = no_load.v_rms / no_load.i_rms;
let x_m = (z_nl * z_nl - rs * rs)
.clamp_val(S::ZERO, S::from_f64(f64::MAX / 2.0))
.sqrt();
let lm = if omega_nl > S::ZERO {
x_m / omega_nl
} else {
S::ZERO
};
let rsc = locked_rotor.p_sc / (locked_rotor.i_rms * locked_rotor.i_rms);
let rr = (rsc - rs).clamp_val(S::ZERO, S::from_f64(f64::MAX / 2.0));
let omega_lr = two_pi * locked_rotor.freq;
let z_sc = locked_rotor.v_rms / locked_rotor.i_rms;
let x_sc = (z_sc * z_sc - rsc * rsc)
.clamp_val(S::ZERO, S::from_f64(f64::MAX / 2.0))
.sqrt();
let lls = if omega_lr > S::ZERO {
x_sc / (S::TWO * omega_lr)
} else {
S::ZERO
};
Self {
rs,
rr,
lls,
llr: lls,
lm,
}
}
pub fn ls(&self) -> S {
self.lls + self.lm
}
pub fn lr(&self) -> S {
self.llr + self.lm
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dc_test_identifies_rs() {
let test = DcTestResult {
v_dc: 10.0_f64,
i_dc: 2.5,
};
let rs = PmsmParams::identify_rs(&test);
assert!((rs - 2.0).abs() < 1e-10, "rs={rs:.4}");
}
#[test]
fn identify_inductance_from_ac_test() {
let ls = PmsmParams::identify_ls(10.0_f64, 1.0, 3.0, 50.0);
let expected = (91.0_f64.sqrt()) / (2.0 * core::f64::consts::PI * 50.0);
assert!(
(ls - expected).abs() < 1e-6,
"ls={ls:.6}, expected={expected:.6}"
);
}
#[test]
fn induction_motor_identification() {
let dc = DcTestResult {
v_dc: 20.0_f64,
i_dc: 2.0,
};
let no_load = NoLoadTestResult {
v_rms: 220.0,
i_rms: 3.0,
p_nl: 200.0,
freq: 50.0,
};
let locked = LockedRotorTestResult {
v_rms: 50.0,
i_rms: 5.0,
p_sc: 100.0,
freq: 50.0,
};
let params = InductionMotorParams::identify(&dc, &no_load, &locked);
assert!((params.rs - 5.0).abs() < 1e-10, "rs={:.4}", params.rs);
assert!(params.rr >= 0.0, "rr={:.4}", params.rr);
assert!(params.lm > 0.0, "lm={:.4}", params.lm);
}
#[test]
fn ke_identification() {
let ke = PmsmParams::identify_ke(100.0_f64, 1000.0);
assert!((ke - 0.1).abs() < 1e-10, "ke={ke:.6}");
}
}