use serde::{Deserialize, Serialize};
use crate::CalcError;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ReactanceResult {
pub xc_ohms: Option<f64>,
pub xl_ohms: Option<f64>,
pub f_res_hz: Option<f64>,
}
pub fn reactance(
freq_hz: f64,
capacitance_f: Option<f64>,
inductance_h: Option<f64>,
) -> Result<ReactanceResult, CalcError> {
if freq_hz <= 0.0 {
return Err(CalcError::OutOfRange {
name: "freq_hz",
value: freq_hz,
expected: "> 0",
});
}
if let Some(c) = capacitance_f {
if c <= 0.0 {
return Err(CalcError::OutOfRange {
name: "capacitance_f",
value: c,
expected: "> 0",
});
}
}
if let Some(l) = inductance_h {
if l <= 0.0 {
return Err(CalcError::OutOfRange {
name: "inductance_h",
value: l,
expected: "> 0",
});
}
}
let two_pi_f = 2.0 * std::f64::consts::PI * freq_hz;
let xc_ohms = capacitance_f.map(|c| 1.0 / (two_pi_f * c));
let xl_ohms = inductance_h.map(|l| two_pi_f * l);
let f_res_hz = match (capacitance_f, inductance_h) {
(Some(c), Some(l)) => Some(1.0 / (2.0 * std::f64::consts::PI * (l * c).sqrt())),
_ => None,
};
Ok(ReactanceResult {
xc_ohms,
xl_ohms,
f_res_hz,
})
}
#[cfg(test)]
mod tests {
use approx::assert_relative_eq;
use super::*;
#[test]
fn saturn_page41_vector() {
let result = reactance(1e6, Some(1e-6), Some(1e-3)).unwrap();
assert_relative_eq!(result.xc_ohms.unwrap(), 0.15915494, epsilon = 1e-4);
assert_relative_eq!(result.xl_ohms.unwrap(), 6283.1853, epsilon = 1e-1);
assert_relative_eq!(result.f_res_hz.unwrap(), 5032.9255, epsilon = 1e-2);
}
#[test]
fn xc_10nf_1khz() {
let result = reactance(1e3, Some(10e-9), None).unwrap();
assert_relative_eq!(result.xc_ohms.unwrap(), 15915.494, epsilon = 1e-1);
}
#[test]
fn xl_100uh_1khz() {
let result = reactance(1e3, None, Some(100e-6)).unwrap();
assert_relative_eq!(result.xl_ohms.unwrap(), 0.6283185, epsilon = 1e-4);
}
#[test]
fn error_on_zero_freq() {
assert!(reactance(0.0, Some(1e-6), None).is_err());
}
#[test]
fn error_on_negative_capacitance() {
assert!(reactance(1e6, Some(-1e-6), None).is_err());
}
}