1use serde::{Deserialize, Serialize};
8
9use crate::CalcError;
10
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13pub struct ReactanceResult {
14 pub xc_ohms: Option<f64>,
16 pub xl_ohms: Option<f64>,
18 pub f_res_hz: Option<f64>,
20}
21
22pub fn reactance(
32 freq_hz: f64,
33 capacitance_f: Option<f64>,
34 inductance_h: Option<f64>,
35) -> Result<ReactanceResult, CalcError> {
36 if freq_hz <= 0.0 {
37 return Err(CalcError::OutOfRange {
38 name: "freq_hz",
39 value: freq_hz,
40 expected: "> 0",
41 });
42 }
43
44 if let Some(c) = capacitance_f {
45 if c <= 0.0 {
46 return Err(CalcError::OutOfRange {
47 name: "capacitance_f",
48 value: c,
49 expected: "> 0",
50 });
51 }
52 }
53
54 if let Some(l) = inductance_h {
55 if l <= 0.0 {
56 return Err(CalcError::OutOfRange {
57 name: "inductance_h",
58 value: l,
59 expected: "> 0",
60 });
61 }
62 }
63
64 let two_pi_f = 2.0 * std::f64::consts::PI * freq_hz;
65
66 let xc_ohms = capacitance_f.map(|c| 1.0 / (two_pi_f * c));
67 let xl_ohms = inductance_h.map(|l| two_pi_f * l);
68
69 let f_res_hz = match (capacitance_f, inductance_h) {
70 (Some(c), Some(l)) => Some(1.0 / (2.0 * std::f64::consts::PI * (l * c).sqrt())),
71 _ => None,
72 };
73
74 Ok(ReactanceResult {
75 xc_ohms,
76 xl_ohms,
77 f_res_hz,
78 })
79}
80
81#[cfg(test)]
82mod tests {
83 use approx::assert_relative_eq;
84
85 use super::*;
86
87 #[test]
90 fn saturn_page41_vector() {
91 let result = reactance(1e6, Some(1e-6), Some(1e-3)).unwrap();
92 assert_relative_eq!(result.xc_ohms.unwrap(), 0.15915494, epsilon = 1e-4);
93 assert_relative_eq!(result.xl_ohms.unwrap(), 6283.1853, epsilon = 1e-1);
94 assert_relative_eq!(result.f_res_hz.unwrap(), 5032.9255, epsilon = 1e-2);
95 }
96
97 #[test]
99 fn xc_10nf_1khz() {
100 let result = reactance(1e3, Some(10e-9), None).unwrap();
101 assert_relative_eq!(result.xc_ohms.unwrap(), 15915.494, epsilon = 1e-1);
102 }
103
104 #[test]
106 fn xl_100uh_1khz() {
107 let result = reactance(1e3, None, Some(100e-6)).unwrap();
108 assert_relative_eq!(result.xl_ohms.unwrap(), 0.6283185, epsilon = 1e-4);
109 }
110
111 #[test]
112 fn error_on_zero_freq() {
113 assert!(reactance(0.0, Some(1e-6), None).is_err());
114 }
115
116 #[test]
117 fn error_on_negative_capacitance() {
118 assert!(reactance(1e6, Some(-1e-6), None).is_err());
119 }
120}