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