pcb_toolkit/impedance/
microstrip.rs1use crate::CalcError;
7use crate::impedance::{common, types::ImpedanceResult};
8
9pub struct MicrostripInput {
11 pub width: f64,
13 pub height: f64,
15 pub thickness: f64,
17 pub er: f64,
19 pub frequency: f64,
21}
22
23pub fn calculate(input: &MicrostripInput) -> Result<ImpedanceResult, CalcError> {
25 let MicrostripInput { width, height, thickness, er, .. } = *input;
26
27 if width <= 0.0 {
28 return Err(CalcError::NegativeDimension { name: "width", value: width });
29 }
30 if height <= 0.0 {
31 return Err(CalcError::NegativeDimension { name: "height", value: height });
32 }
33 if er < 1.0 {
34 return Err(CalcError::OutOfRange {
35 name: "er",
36 value: er,
37 expected: ">= 1.0",
38 });
39 }
40
41 let we = common::effective_width(width, height, thickness);
43 let u = we / height;
44
45 let er_eff = common::er_eff_static(u, er);
47
48 let zo = if u <= 1.0 {
50 (60.0 / er_eff.sqrt()) * (8.0 * height / we + we / (4.0 * height)).ln()
52 } else {
53 (120.0 * std::f64::consts::PI / er_eff.sqrt())
55 / (u + 1.393 + 0.667 * (u + 1.444).ln())
56 };
57
58 let tpd = common::propagation_delay(er_eff);
60 let lo = common::inductance_per_length(zo, tpd);
61 let co = common::capacitance_per_length(zo, tpd);
62
63 Ok(ImpedanceResult {
64 zo,
65 er_eff,
66 tpd_ps_per_in: tpd,
67 lo_nh_per_in: lo,
68 co_pf_per_in: co,
69 })
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn basic_microstrip() {
78 let result = calculate(&MicrostripInput {
79 width: 10.0,
80 height: 5.0,
81 thickness: 1.4,
82 er: 4.6,
83 frequency: 0.0,
84 })
85 .unwrap();
86
87 assert!(result.zo > 20.0 && result.zo < 80.0, "Zo = {}", result.zo);
89 assert!(result.er_eff > 1.0 && result.er_eff < 4.6, "Er_eff = {}", result.er_eff);
90 assert!(result.tpd_ps_per_in > 0.0);
91 assert!(result.lo_nh_per_in > 0.0);
92 assert!(result.co_pf_per_in > 0.0);
93 }
94
95 #[test]
96 fn narrow_trace_higher_impedance() {
97 let narrow = calculate(&MicrostripInput {
98 width: 3.0,
99 height: 5.0,
100 thickness: 1.4,
101 er: 4.6,
102 frequency: 0.0,
103 })
104 .unwrap();
105
106 let wide = calculate(&MicrostripInput {
107 width: 20.0,
108 height: 5.0,
109 thickness: 1.4,
110 er: 4.6,
111 frequency: 0.0,
112 })
113 .unwrap();
114
115 assert!(
116 narrow.zo > wide.zo,
117 "narrow Zo {} should be > wide Zo {}",
118 narrow.zo,
119 wide.zo
120 );
121 }
122
123 #[test]
124 fn rejects_negative_width() {
125 let result = calculate(&MicrostripInput {
126 width: -1.0,
127 height: 5.0,
128 thickness: 1.4,
129 er: 4.6,
130 frequency: 0.0,
131 });
132 assert!(result.is_err());
133 }
134}