use crate::core::scalar::ControlScalar;
#[derive(Debug, Clone, Copy)]
pub struct AlphaBeta<S: ControlScalar> {
pub alpha: S,
pub beta: S,
pub zero: S,
}
pub fn clarke<S: ControlScalar>(a: S, b: S, c: S) -> AlphaBeta<S> {
let two_thirds = S::from_f64(2.0 / 3.0);
let one_third = S::from_f64(1.0 / 3.0);
let half = S::HALF;
let sqrt3_over2 = S::from_f64(0.8660254037844386);
let alpha = two_thirds * (a - half * b - half * c);
let beta = two_thirds * (sqrt3_over2 * b - sqrt3_over2 * c);
let zero = one_third * (a + b + c);
AlphaBeta { alpha, beta, zero }
}
pub fn clarke_2ph<S: ControlScalar>(a: S, b: S) -> AlphaBeta<S> {
clarke(a, b, -a - b)
}
pub fn clarke_inverse<S: ControlScalar>(ab: &AlphaBeta<S>) -> (S, S, S) {
let sqrt3_over2 = S::from_f64(0.8660254037844386);
let half = S::HALF;
let a = ab.alpha + ab.zero;
let b = -half * ab.alpha + sqrt3_over2 * ab.beta + ab.zero;
let c = -half * ab.alpha - sqrt3_over2 * ab.beta + ab.zero;
(a, b, c)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn clarke_balanced_a_axis() {
let ab = clarke(1.0_f64, -0.5, -0.5);
assert!((ab.alpha - 1.0).abs() < 1e-10, "alpha={}", ab.alpha);
assert!((ab.beta).abs() < 1e-10, "beta={}", ab.beta);
assert!((ab.zero).abs() < 1e-10, "zero={}", ab.zero);
}
#[test]
fn clarke_balanced_sum_zero() {
let ab = clarke(1.0_f64, -0.5, -0.5);
assert!((ab.zero).abs() < 1e-10);
}
#[test]
fn clarke_90_degree() {
let phase = core::f64::consts::PI / 2.0;
let a = phase.cos();
let b = (phase - 2.0 * core::f64::consts::PI / 3.0).cos();
let c = (phase - 4.0 * core::f64::consts::PI / 3.0).cos();
let ab = clarke(a, b, c);
assert!(ab.alpha.abs() < 1e-10, "alpha={}", ab.alpha);
assert!((ab.beta - 1.0).abs() < 1e-10, "beta={}", ab.beta);
}
#[test]
fn inverse_clarke_roundtrip() {
let ab = clarke(1.0_f64, -0.5, -0.5);
let (a2, b2, c2) = clarke_inverse(&ab);
assert!((a2 - 1.0).abs() < 1e-10, "a={}", a2);
assert!((b2 - (-0.5)).abs() < 1e-10, "b={}", b2);
assert!((c2 - (-0.5)).abs() < 1e-10, "c={}", c2);
}
#[test]
fn clarke_2ph_matches_3ph() {
let a = 1.0_f64;
let b = -0.5;
let ab2 = clarke_2ph(a, b);
let ab3 = clarke(a, b, -a - b);
assert!((ab2.alpha - ab3.alpha).abs() < 1e-10);
assert!((ab2.beta - ab3.beta).abs() < 1e-10);
}
#[test]
fn amplitude_preserved() {
let amp = 5.0_f64;
let ab = clarke(amp, -amp / 2.0, -amp / 2.0);
let reconstructed_amp = (ab.alpha * ab.alpha + ab.beta * ab.beta).sqrt();
assert!(
(reconstructed_amp - amp).abs() < 1e-10,
"amp={}, reconstructed={}",
amp,
reconstructed_amp
);
}
}