use crate::{FRAC_1_SQRT_3, SQRT_3};
use fixed::types::I16F16;
#[derive(Debug, Clone)]
pub struct RotatingReferenceFrame {
pub d: I16F16,
pub q: I16F16,
}
#[derive(Debug, Clone)]
pub struct TwoPhaseReferenceFrame {
pub alpha: I16F16,
pub beta: I16F16,
}
#[derive(Debug, Clone)]
pub struct ThreePhaseReferenceFrame {
pub a: I16F16,
pub b: I16F16,
pub c: I16F16,
}
#[derive(Debug, Clone)]
pub struct ThreePhaseBalancedReferenceFrame {
pub a: I16F16,
pub b: I16F16,
}
pub fn clarke(inputs: ThreePhaseBalancedReferenceFrame) -> TwoPhaseReferenceFrame {
TwoPhaseReferenceFrame {
alpha: inputs.a,
beta: FRAC_1_SQRT_3 * (inputs.a + 2 * inputs.b),
}
}
pub fn inverse_clarke(inputs: TwoPhaseReferenceFrame) -> ThreePhaseReferenceFrame {
ThreePhaseReferenceFrame {
a: inputs.alpha,
b: (-inputs.alpha + SQRT_3 * inputs.beta) / 2,
c: (-inputs.alpha - SQRT_3 * inputs.beta) / 2,
}
}
pub fn park(
cos_angle: I16F16,
sin_angle: I16F16,
inputs: TwoPhaseReferenceFrame,
) -> RotatingReferenceFrame {
RotatingReferenceFrame {
d: cos_angle * inputs.alpha + sin_angle * inputs.beta,
q: cos_angle * inputs.beta - sin_angle * inputs.alpha,
}
}
pub fn inverse_park(
cos_angle: I16F16,
sin_angle: I16F16,
inputs: RotatingReferenceFrame,
) -> TwoPhaseReferenceFrame {
TwoPhaseReferenceFrame {
alpha: cos_angle * inputs.d - sin_angle * inputs.q,
beta: sin_angle * inputs.d + cos_angle * inputs.q,
}
}
#[cfg(test)]
mod tests {
use super::*;
#[track_caller]
fn clark_e_round_trip(a: f32, b: f32) {
let input = ThreePhaseBalancedReferenceFrame {
a: I16F16::from_num(a),
b: I16F16::from_num(b),
};
let two_phase = clarke(input.clone());
dbg!(&two_phase);
let result = inverse_clarke(two_phase);
dbg!(&result);
assert!(result.a.abs_diff(input.a) < 0.0001);
assert!(result.b.abs_diff(input.b) < 0.0001);
}
#[test]
fn clarke_round_trip_zero() {
clark_e_round_trip(0., 0.);
}
#[test]
fn clarke_round_trip_two_inputs() {
clark_e_round_trip(0., 1.);
clark_e_round_trip(1., 0.);
clark_e_round_trip(-0.5, -0.5);
clark_e_round_trip(-0.1, -0.2);
clark_e_round_trip(13., 21.);
}
#[test]
fn park_round_trip() {
let angle = I16F16::from_num(0.82);
let (sin_angle, cos_angle) = cordic::sin_cos(angle);
let input = TwoPhaseReferenceFrame {
alpha: I16F16::from_num(2),
beta: I16F16::from_num(3),
};
let moving_reference = park(cos_angle, sin_angle, input.clone());
dbg!(&moving_reference);
let result = inverse_park(cos_angle, sin_angle, moving_reference);
dbg!(&result);
assert!(result.alpha.abs_diff(input.alpha) < 0.001);
assert!(result.beta.abs_diff(input.beta) < 0.001);
}
}