dspk_ops/filter/design/
sos.rs1use super::OpsError;
2
3pub const DEFAULT_SETTLING_TOLERANCE: f64 = 1e-3;
5
6const SETTLING_HORIZON_MARGIN: usize = 64;
8
9const A0_TOLERANCE: f64 = 1e-12;
11
12const NEGLIGIBLE_POLE_RADIUS: f64 = 1e-12;
14
15#[derive(Debug, Clone, Copy, PartialEq)]
17pub struct Section {
18 pub b: [f64; 3],
19 pub a: [f64; 3],
20}
21
22impl Section {
23 pub fn pole_radius(&self) -> f64 {
25 let (a1, a2) = (self.a[1], self.a[2]);
26 let disc = a1 * a1 - 4.0 * a2;
27 if disc < 0.0 {
28 a2.sqrt()
29 } else {
30 let sq = disc.sqrt();
31 ((-a1 + sq) / 2.0).abs().max(((-a1 - sq) / 2.0).abs())
32 }
33 }
34
35 fn step_zi(&self) -> [f64; 2] {
37 let [b0, b1, b2] = self.b;
38 let [_, a1, a2] = self.a;
39 let r0 = b1 - a1 * b0;
40 let r1 = b2 - a2 * b0;
41 let z0 = (r0 + r1) / (1.0 + a1 + a2);
42 [z0, r1 - a2 * z0]
43 }
44
45 fn dc_gain(&self) -> f64 {
46 self.b.iter().sum::<f64>() / self.a.iter().sum::<f64>()
47 }
48
49 pub fn svf(&self) -> [f64; 6] {
56 let [b0, b1, b2] = self.b;
57 let [_, a1, a2] = self.a;
58 let (n0, n1, n2) = (b0 + b1 + b2, 2.0 * (b0 - b2), b0 - b1 + b2);
59 let (d0, d1, d2) = (1.0 + a1 + a2, 2.0 * (1.0 - a2), 1.0 - a1 + a2);
60 let g = (d0 / d2).sqrt();
61 let k = d1 / (d2 * g);
62 let m0 = n2 / d2;
63 let m1 = n1 / (d2 * g) - m0 * k;
64 let m2 = n0 / (d2 * g * g) - m0;
65 let c1 = 1.0 / (1.0 + g * (g + k));
66 let c2 = g * c1;
67 [c1, c2, g * c2, m0, m1, m2]
68 }
69}
70
71#[derive(Debug, Clone, PartialEq)]
73pub struct Sos {
74 pub sections: Vec<Section>,
75}
76
77impl Sos {
78 pub fn new(sections: Vec<Section>) -> Self {
79 Self { sections }
80 }
81
82 pub fn len(&self) -> usize {
83 self.sections.len()
84 }
85
86 pub fn is_empty(&self) -> bool {
87 self.sections.is_empty()
88 }
89
90 pub fn validate(&self) -> Result<(), OpsError> {
97 let ok = !self.sections.is_empty()
98 && self.sections.iter().all(|s| {
99 s.b.iter().chain(s.a.iter()).all(|c| c.is_finite())
100 && (s.a[0] - 1.0).abs() < A0_TOLERANCE
101 && s.pole_radius() < 1.0
102 });
103 if ok { Ok(()) } else { Err(OpsError::InvalidSections) }
104 }
105
106 pub fn zi(&self) -> Vec<[f64; 2]> {
109 let mut scale = 1.0;
110 self.sections
111 .iter()
112 .map(|s| {
113 let [z0, z1] = s.step_zi();
114 let zi = [scale * z0, scale * z1];
115 scale *= s.dc_gain();
116 zi
117 })
118 .collect()
119 }
120
121 pub fn settling_samples(&self, tol: f64) -> usize {
128 let horizon = 2 * self.pole_settling_bound(tol) + SETTLING_HORIZON_MARGIN;
129 let mut impulse = vec![0.0; horizon];
130 impulse[0] = 1.0;
131 let h = self.filter(&impulse, false);
132 let total: f64 = h.iter().map(|v| v.abs()).sum();
133 let mut tail = 0.0;
134 for (t, v) in h.iter().enumerate().rev() {
135 tail += v.abs();
136 if tail > tol * total {
137 return t + 1;
138 }
139 }
140 0
141 }
142
143 pub fn pole_settling_bound(&self, tol: f64) -> usize {
146 self.sections
147 .iter()
148 .map(|s| {
149 let r = s.pole_radius();
150 if r < NEGLIGIBLE_POLE_RADIUS {
151 2
152 } else {
153 (tol.ln() / r.ln()).ceil().max(2.0) as usize
154 }
155 })
156 .sum()
157 }
158
159 pub fn dc_gain(&self) -> f64 {
161 self.sections.iter().map(Section::dc_gain).product()
162 }
163
164 pub fn filter(&self, x: &[f64], steady_state: bool) -> Vec<f64> {
167 let x0 = x.first().copied().unwrap_or(0.0);
168 let mut state: Vec<[f64; 2]> = if steady_state {
169 self.zi().into_iter().map(|[a, b]| [a * x0, b * x0]).collect()
170 } else {
171 vec![[0.0; 2]; self.sections.len()]
172 };
173 x.iter()
174 .map(|&xi| {
175 let mut v = xi;
176 for (s, z) in self.sections.iter().zip(state.iter_mut()) {
177 let y = s.b[0] * v + z[0];
178 z[0] = s.b[1] * v - s.a[1] * y + z[1];
179 z[1] = s.b[2] * v - s.a[2] * y;
180 v = y;
181 }
182 v
183 })
184 .collect()
185 }
186
187 pub fn filtfilt(&self, x: &[f64], padlen: usize) -> Vec<f64> {
190 let n = x.len();
191 if n == 0 {
192 return Vec::new();
193 }
194 let p = padlen.min(n - 1);
195 let mut ext = Vec::with_capacity(n + 2 * p);
196 ext.extend((1..=p).rev().map(|k| 2.0 * x[0] - x[k]));
197 ext.extend_from_slice(x);
198 ext.extend((1..=p).map(|k| 2.0 * x[n - 1] - x[n - 1 - k]));
199 let mut y = self.filter(&ext, true);
200 y.reverse();
201 let mut y = self.filter(&y, true);
202 y.reverse();
203 y[p..p + n].to_vec()
204 }
205}