rill_core_model/wdf/
diode_clipper.rs1use crate::constants::NEWTON_TOLERANCE;
3use crate::elements::Resistor;
4
5crate::wdf_element! {
6 name: AntiParallelDiode<T>,
7 params: { is: T, vt: T },
8 state: { rp: T },
9 port_resistance: |s| s.rp,
10 scattering: |s, a| {
11 let tolerance = T::from_f64(NEWTON_TOLERANCE);
12 let guess = s.vt * (T::ONE + a.abs() / (T::from_f32(2.0) * s.rp * s.is)).ln();
13 let mut v = guess.max(T::ZERO);
14 for _ in 0..12 {
15 let ev = (v / s.vt).exp();
16 let env = (-v / s.vt).exp();
17 let i = s.is * (ev - env);
18 let g = s.is * (ev + env) / s.vt;
19 let f = v + s.rp * i - a;
20 if f.abs() < tolerance { break; }
21 let df = T::ONE + s.rp * g;
22 v -= f / df;
23 }
24 let ev = (v / s.vt).exp();
25 let env = (-v / s.vt).exp();
26 let g = s.is * (ev + env) / s.vt;
27 s.rp = T::ONE / g.max(T::from_f64(1e-12));
28 s.voltage = v;
29 T::from_f32(2.0) * v - a
30 },
31 update: |_s| {},
32 reset: |s| { s.rp = T::from_f64(1e-3); },
33}
34
35crate::wdf_compose! {
36 name: DiodeClipper<T>,
37 kind: Series,
38 elements: (Resistor<T>, AntiParallelDiode<T>),
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44 use crate::constants::{BOLTZMANN, ELECTRON_CHARGE};
45 use crate::WdfElement;
46
47 fn make_clipper() -> DiodeClipper<f64> {
48 let r = Resistor::new(1000.0);
49 let vt = BOLTZMANN * 300.0 / ELECTRON_CHARGE;
50 let mut diode = AntiParallelDiode::new(1e-15, vt);
51 diode.reset();
52 DiodeClipper::new(r, diode)
53 }
54
55 #[test]
56 fn test_clipper_positive_clip() {
57 let mut c = make_clipper();
58 WdfElement::process_incident(&mut c, 10.0);
59 c.update_state();
60 let v: f64 = c.right.voltage();
61 assert!(
62 v > 0.0 && v < 1.0,
63 "should clip positive to ~0.6V: got {}",
64 v
65 );
66 }
67
68 #[test]
69 fn test_clipper_negative_clip() {
70 let mut c = make_clipper();
71 WdfElement::process_incident(&mut c, -10.0);
72 c.update_state();
73 let v: f64 = c.right.voltage();
74 assert!(
75 v < 0.0 && v > -1.0,
76 "should clip negative to ~-0.6V: got {}",
77 v
78 );
79 }
80
81 #[test]
82 fn test_clipper_symmetry() {
83 let mut c1 = make_clipper();
84 WdfElement::process_incident(&mut c1, 5.0);
85 c1.update_state();
86 let vp: f64 = c1.right.voltage();
87 let mut c2 = make_clipper();
88 WdfElement::process_incident(&mut c2, -5.0);
89 c2.update_state();
90 let vn: f64 = c2.right.voltage();
91 assert!((vp + vn).abs() < 0.1, "pos={} neg={}", vp, vn);
92 }
93}