Skip to main content

embedded_dsp/
filtering.rs

1//! Digital filtering functions (FIR, Biquad IIR Direct Form I & II, LMS Adaptive Filter, Convolution, Correlation).
2
3use crate::types::*;
4
5// --- FIR Filter ---
6
7/// Instance structure for the floating-point FIR filter.
8pub struct FirInstanceF32<'a> {
9    pub num_taps: u16,
10    pub coeffs: &'a [f32],
11    pub state: &'a mut [f32],
12}
13
14impl<'a> FirInstanceF32<'a> {
15    pub fn init(num_taps: u16, coeffs: &'a [f32], state: &'a mut [f32]) -> Self {
16        state.fill(0.0);
17        Self {
18            num_taps,
19            coeffs,
20            state,
21        }
22    }
23}
24
25pub fn fir_f32(instance: &mut FirInstanceF32, src: &[f32], dst: &mut [f32]) {
26    let num_taps = instance.num_taps as usize;
27    let block_size = src.len().min(dst.len());
28
29    for i in 0..block_size {
30        // Shift state
31        for k in (1..num_taps).rev() {
32            instance.state[k] = instance.state[k - 1];
33        }
34        instance.state[0] = src[i];
35
36        // Compute dot product with coefficients
37        let mut acc = 0.0f32;
38        for k in 0..num_taps {
39            acc += instance.state[k] * instance.coeffs[k];
40        }
41        dst[i] = acc;
42    }
43}
44
45/// Instance structure for the Q31 FIR filter.
46pub struct FirInstanceQ31<'a> {
47    pub num_taps: u16,
48    pub coeffs: &'a [q31],
49    pub state: &'a mut [q31],
50}
51
52impl<'a> FirInstanceQ31<'a> {
53    pub fn init(num_taps: u16, coeffs: &'a [q31], state: &'a mut [q31]) -> Self {
54        state.fill(0);
55        Self {
56            num_taps,
57            coeffs,
58            state,
59        }
60    }
61}
62
63pub fn fir_q31(instance: &mut FirInstanceQ31, src: &[q31], dst: &mut [q31]) {
64    let num_taps = instance.num_taps as usize;
65    let block_size = src.len().min(dst.len());
66
67    for i in 0..block_size {
68        for k in (1..num_taps).rev() {
69            instance.state[k] = instance.state[k - 1];
70        }
71        instance.state[0] = src[i];
72
73        let mut acc: i64 = 0;
74        for k in 0..num_taps {
75            acc += (instance.state[k] as i64 * instance.coeffs[k] as i64) >> 31;
76        }
77        dst[i] = acc.clamp(i32::MIN as i64, i32::MAX as i64) as q31;
78    }
79}
80
81/// Instance structure for the Q15 FIR filter.
82pub struct FirInstanceQ15<'a> {
83    pub num_taps: u16,
84    pub coeffs: &'a [q15],
85    pub state: &'a mut [q15],
86}
87
88impl<'a> FirInstanceQ15<'a> {
89    pub fn init(num_taps: u16, coeffs: &'a [q15], state: &'a mut [q15]) -> Self {
90        state.fill(0);
91        Self {
92            num_taps,
93            coeffs,
94            state,
95        }
96    }
97}
98
99pub fn fir_q15(instance: &mut FirInstanceQ15, src: &[q15], dst: &mut [q15]) {
100    let num_taps = instance.num_taps as usize;
101    let block_size = src.len().min(dst.len());
102
103    for i in 0..block_size {
104        for k in (1..num_taps).rev() {
105            instance.state[k] = instance.state[k - 1];
106        }
107        instance.state[0] = src[i];
108
109        let mut acc: i32 = 0;
110        for k in 0..num_taps {
111            acc += (instance.state[k] as i32 * instance.coeffs[k] as i32) >> 15;
112        }
113        dst[i] = acc.clamp(i16::MIN as i32, i16::MAX as i32) as q15;
114    }
115}
116
117// --- Biquad Cascade Direct Form I Filter ---
118
119/// Instance structure for the floating-point Biquad Cascade Direct Form I filter.
120pub struct BiquadCascadeInstanceF32<'a> {
121    pub num_stages: u8,
122    pub coeffs: &'a [f32],    // 5 * num_stages: [b0, b1, b2, a1, a2]
123    pub state: &'a mut [f32], // 4 * num_stages: [x[n-1], x[n-2], y[n-1], y[n-2]]
124}
125
126impl<'a> BiquadCascadeInstanceF32<'a> {
127    pub fn init(num_stages: u8, coeffs: &'a [f32], state: &'a mut [f32]) -> Self {
128        state.fill(0.0);
129        Self {
130            num_stages,
131            coeffs,
132            state,
133        }
134    }
135}
136
137pub fn biquad_cascade_df1_f32(
138    instance: &mut BiquadCascadeInstanceF32,
139    src: &[f32],
140    dst: &mut [f32],
141) {
142    let num_stages = instance.num_stages as usize;
143    let block_size = src.len().min(dst.len());
144
145    let mut in_val;
146    let mut out_val;
147
148    for i in 0..block_size {
149        in_val = src[i];
150        for stage in 0..num_stages {
151            let b0 = instance.coeffs[stage * 5];
152            let b1 = instance.coeffs[stage * 5 + 1];
153            let b2 = instance.coeffs[stage * 5 + 2];
154            let a1 = instance.coeffs[stage * 5 + 3];
155            let a2 = instance.coeffs[stage * 5 + 4];
156
157            let x1 = instance.state[stage * 4];
158            let x2 = instance.state[stage * 4 + 1];
159            let y1 = instance.state[stage * 4 + 2];
160            let y2 = instance.state[stage * 4 + 3];
161
162            out_val = b0 * in_val + b1 * x1 + b2 * x2 + a1 * y1 + a2 * y2;
163
164            instance.state[stage * 4 + 1] = x1;
165            instance.state[stage * 4] = in_val;
166            instance.state[stage * 4 + 3] = y1;
167            instance.state[stage * 4 + 2] = out_val;
168
169            in_val = out_val;
170        }
171        dst[i] = in_val;
172    }
173}
174
175/// Instance structure for the floating-point Biquad Cascade Transposed Direct Form II filter.
176///
177/// Same SOS layout `[b0, b1, b2, a1, a2]` as [`BiquadCascadeInstanceF32`].
178/// State is two delays per stage (`[s1, s2, ...]`).
179pub struct BiquadCascadeDf2tInstanceF32<'a> {
180    pub num_stages: u8,
181    pub coeffs: &'a [f32],
182    pub state: &'a mut [f32],
183}
184
185impl<'a> BiquadCascadeDf2tInstanceF32<'a> {
186    pub fn init(num_stages: u8, coeffs: &'a [f32], state: &'a mut [f32]) -> Self {
187        state.fill(0.0);
188        Self {
189            num_stages,
190            coeffs,
191            state,
192        }
193    }
194}
195
196pub fn biquad_cascade_df2t_f32(
197    instance: &mut BiquadCascadeDf2tInstanceF32,
198    src: &[f32],
199    dst: &mut [f32],
200) {
201    let num_stages = instance.num_stages as usize;
202    let block_size = src.len().min(dst.len());
203
204    for i in 0..block_size {
205        let mut in_val = src[i];
206        for stage in 0..num_stages {
207            let b0 = instance.coeffs[stage * 5];
208            let b1 = instance.coeffs[stage * 5 + 1];
209            let b2 = instance.coeffs[stage * 5 + 2];
210            let a1 = instance.coeffs[stage * 5 + 3];
211            let a2 = instance.coeffs[stage * 5 + 4];
212
213            let s1 = instance.state[stage * 2];
214            let s2 = instance.state[stage * 2 + 1];
215
216            let y = b0 * in_val + s1;
217            instance.state[stage * 2] = b1 * in_val + a1 * y + s2;
218            instance.state[stage * 2 + 1] = b2 * in_val + a2 * y;
219            in_val = y;
220        }
221        dst[i] = in_val;
222    }
223}
224
225/// Instance structure for the Q15 Biquad Cascade Direct Form I filter.
226///
227/// Coeffs are Q1.15 `[b0, b1, b2, a1, a2]` per stage (same layout as the f32 cascade).
228/// `post_shift` extra headroom in stored coeffs (`coeff_f32 / 2^{post_shift}` in Q15);
229/// the MAC is shifted `15 - post_shift` (CMSIS-style).
230pub struct BiquadCascadeInstanceQ15<'a> {
231    pub num_stages: u8,
232    pub post_shift: u8,
233    pub coeffs: &'a [q15],
234    pub state: &'a mut [q15],
235}
236
237impl<'a> BiquadCascadeInstanceQ15<'a> {
238    pub fn init(num_stages: u8, coeffs: &'a [q15], state: &'a mut [q15], post_shift: u8) -> Self {
239        state.fill(0);
240        Self {
241            num_stages,
242            post_shift,
243            coeffs,
244            state,
245        }
246    }
247}
248
249pub fn biquad_cascade_df1_q15(
250    instance: &mut BiquadCascadeInstanceQ15,
251    src: &[q15],
252    dst: &mut [q15],
253) {
254    let num_stages = instance.num_stages as usize;
255    let block_size = src.len().min(dst.len());
256    let shift = 15u32.saturating_sub(instance.post_shift as u32).min(31);
257
258    for i in 0..block_size {
259        let mut in_val = src[i] as i64;
260        for stage in 0..num_stages {
261            let b0 = instance.coeffs[stage * 5] as i64;
262            let b1 = instance.coeffs[stage * 5 + 1] as i64;
263            let b2 = instance.coeffs[stage * 5 + 2] as i64;
264            let a1 = instance.coeffs[stage * 5 + 3] as i64;
265            let a2 = instance.coeffs[stage * 5 + 4] as i64;
266
267            let x1 = instance.state[stage * 4] as i64;
268            let x2 = instance.state[stage * 4 + 1] as i64;
269            let y1 = instance.state[stage * 4 + 2] as i64;
270            let y2 = instance.state[stage * 4 + 3] as i64;
271
272            let acc = b0 * in_val + b1 * x1 + b2 * x2 + a1 * y1 + a2 * y2;
273            let out_val = (acc >> shift).clamp(i16::MIN as i64, i16::MAX as i64);
274
275            instance.state[stage * 4 + 1] = x1 as q15;
276            instance.state[stage * 4] = in_val as q15;
277            instance.state[stage * 4 + 3] = y1 as q15;
278            instance.state[stage * 4 + 2] = out_val as q15;
279
280            in_val = out_val;
281        }
282        dst[i] = in_val as q15;
283    }
284}
285
286/// Instance structure for the Q31 Biquad Cascade Direct Form I filter.
287pub struct BiquadCascadeInstanceQ31<'a> {
288    pub num_stages: u8,
289    pub post_shift: u8,
290    pub coeffs: &'a [q31],
291    pub state: &'a mut [q31],
292}
293
294impl<'a> BiquadCascadeInstanceQ31<'a> {
295    pub fn init(num_stages: u8, coeffs: &'a [q31], state: &'a mut [q31], post_shift: u8) -> Self {
296        state.fill(0);
297        Self {
298            num_stages,
299            post_shift,
300            coeffs,
301            state,
302        }
303    }
304}
305
306pub fn biquad_cascade_df1_q31(
307    instance: &mut BiquadCascadeInstanceQ31,
308    src: &[q31],
309    dst: &mut [q31],
310) {
311    let num_stages = instance.num_stages as usize;
312    let block_size = src.len().min(dst.len());
313    let shift = 31u32.saturating_sub(instance.post_shift as u32).min(63);
314
315    for i in 0..block_size {
316        let mut in_val = src[i] as i64;
317        for stage in 0..num_stages {
318            let b0 = instance.coeffs[stage * 5] as i64;
319            let b1 = instance.coeffs[stage * 5 + 1] as i64;
320            let b2 = instance.coeffs[stage * 5 + 2] as i64;
321            let a1 = instance.coeffs[stage * 5 + 3] as i64;
322            let a2 = instance.coeffs[stage * 5 + 4] as i64;
323
324            let x1 = instance.state[stage * 4] as i64;
325            let x2 = instance.state[stage * 4 + 1] as i64;
326            let y1 = instance.state[stage * 4 + 2] as i64;
327            let y2 = instance.state[stage * 4 + 3] as i64;
328
329            let acc = b0 * in_val + b1 * x1 + b2 * x2 + a1 * y1 + a2 * y2;
330            let out_val = (acc >> shift).clamp(i32::MIN as i64, i32::MAX as i64);
331
332            instance.state[stage * 4 + 1] = x1 as q31;
333            instance.state[stage * 4] = in_val as q31;
334            instance.state[stage * 4 + 3] = y1 as q31;
335            instance.state[stage * 4 + 2] = out_val as q31;
336
337            in_val = out_val;
338        }
339        dst[i] = in_val as q31;
340    }
341}
342
343/// Instance structure for the Q15 Biquad Cascade Transposed Direct Form II filter.
344///
345/// Same SOS layout `[b0, b1, b2, a1, a2]` and `post_shift` as
346/// [`BiquadCascadeInstanceQ15`]. State is two delays per stage (`[s1, s2, ...]`),
347/// which is better-conditioned for high-Q poles than Direct Form I.
348pub struct BiquadCascadeDf2tInstanceQ15<'a> {
349    pub num_stages: u8,
350    pub post_shift: u8,
351    pub coeffs: &'a [q15],
352    pub state: &'a mut [q15],
353}
354
355impl<'a> BiquadCascadeDf2tInstanceQ15<'a> {
356    pub fn init(num_stages: u8, coeffs: &'a [q15], state: &'a mut [q15], post_shift: u8) -> Self {
357        state.fill(0);
358        Self {
359            num_stages,
360            post_shift,
361            coeffs,
362            state,
363        }
364    }
365}
366
367pub fn biquad_cascade_df2t_q15(
368    instance: &mut BiquadCascadeDf2tInstanceQ15,
369    src: &[q15],
370    dst: &mut [q15],
371) {
372    let num_stages = instance.num_stages as usize;
373    let block_size = src.len().min(dst.len());
374    let shift = 15u32.saturating_sub(instance.post_shift as u32).min(31);
375
376    for i in 0..block_size {
377        let mut in_val = src[i] as i64;
378        for stage in 0..num_stages {
379            let b0 = instance.coeffs[stage * 5] as i64;
380            let b1 = instance.coeffs[stage * 5 + 1] as i64;
381            let b2 = instance.coeffs[stage * 5 + 2] as i64;
382            let a1 = instance.coeffs[stage * 5 + 3] as i64;
383            let a2 = instance.coeffs[stage * 5 + 4] as i64;
384
385            let s1 = instance.state[stage * 2] as i64;
386            let s2 = instance.state[stage * 2 + 1] as i64;
387
388            let y = (b0 * in_val + (s1 << shift)).clamp(i64::MIN >> 1, i64::MAX >> 1) >> shift;
389            let out_val = y.clamp(i16::MIN as i64, i16::MAX as i64);
390            let s1_new = (b1 * in_val + a1 * out_val + (s2 << shift)) >> shift;
391            let s2_new = (b2 * in_val + a2 * out_val) >> shift;
392
393            instance.state[stage * 2] =
394                s1_new.clamp(i16::MIN as i64, i16::MAX as i64) as q15;
395            instance.state[stage * 2 + 1] =
396                s2_new.clamp(i16::MIN as i64, i16::MAX as i64) as q15;
397            in_val = out_val;
398        }
399        dst[i] = in_val as q15;
400    }
401}
402
403/// Instance structure for the Q31 Biquad Cascade Transposed Direct Form II filter.
404pub struct BiquadCascadeDf2tInstanceQ31<'a> {
405    pub num_stages: u8,
406    pub post_shift: u8,
407    pub coeffs: &'a [q31],
408    pub state: &'a mut [q31],
409}
410
411impl<'a> BiquadCascadeDf2tInstanceQ31<'a> {
412    pub fn init(num_stages: u8, coeffs: &'a [q31], state: &'a mut [q31], post_shift: u8) -> Self {
413        state.fill(0);
414        Self {
415            num_stages,
416            post_shift,
417            coeffs,
418            state,
419        }
420    }
421}
422
423pub fn biquad_cascade_df2t_q31(
424    instance: &mut BiquadCascadeDf2tInstanceQ31,
425    src: &[q31],
426    dst: &mut [q31],
427) {
428    let num_stages = instance.num_stages as usize;
429    let block_size = src.len().min(dst.len());
430    let shift = 31u32.saturating_sub(instance.post_shift as u32).min(63);
431
432    for i in 0..block_size {
433        let mut in_val = src[i] as i64;
434        for stage in 0..num_stages {
435            let b0 = instance.coeffs[stage * 5] as i64;
436            let b1 = instance.coeffs[stage * 5 + 1] as i64;
437            let b2 = instance.coeffs[stage * 5 + 2] as i64;
438            let a1 = instance.coeffs[stage * 5 + 3] as i64;
439            let a2 = instance.coeffs[stage * 5 + 4] as i64;
440
441            let s1 = instance.state[stage * 2] as i64;
442            let s2 = instance.state[stage * 2 + 1] as i64;
443
444            let y = (b0 * in_val + (s1 << shift)).clamp(i64::MIN >> 1, i64::MAX >> 1) >> shift;
445            let out_val = y.clamp(i32::MIN as i64, i32::MAX as i64);
446            let s1_new = (b1 * in_val + a1 * out_val + (s2 << shift)) >> shift;
447            let s2_new = (b2 * in_val + a2 * out_val) >> shift;
448
449            instance.state[stage * 2] =
450                s1_new.clamp(i32::MIN as i64, i32::MAX as i64) as q31;
451            instance.state[stage * 2 + 1] =
452                s2_new.clamp(i32::MIN as i64, i32::MAX as i64) as q31;
453            in_val = out_val;
454        }
455        dst[i] = in_val as q31;
456    }
457}
458
459// --- LMS Adaptive Filter ---
460
461/// Instance structure for the floating-point LMS adaptive filter.
462pub struct LmsInstanceF32<'a> {
463    pub num_taps: u16,
464    pub coeffs: &'a mut [f32],
465    pub state: &'a mut [f32],
466    pub mu: f32,
467}
468
469impl<'a> LmsInstanceF32<'a> {
470    pub fn init(num_taps: u16, coeffs: &'a mut [f32], state: &'a mut [f32], mu: f32) -> Self {
471        state.fill(0.0);
472        coeffs.fill(0.0);
473        Self {
474            num_taps,
475            coeffs,
476            state,
477            mu,
478        }
479    }
480}
481
482pub fn lms_f32(
483    instance: &mut LmsInstanceF32,
484    src: &[f32],
485    ref_signal: &[f32],
486    out: &mut [f32],
487    err: &mut [f32],
488) {
489    let num_taps = instance.num_taps as usize;
490    let block_size = src
491        .len()
492        .min(ref_signal.len())
493        .min(out.len())
494        .min(err.len());
495
496    for i in 0..block_size {
497        for k in (1..num_taps).rev() {
498            instance.state[k] = instance.state[k - 1];
499        }
500        instance.state[0] = src[i];
501
502        let mut acc = 0.0f32;
503        for k in 0..num_taps {
504            acc += instance.state[k] * instance.coeffs[k];
505        }
506        out[i] = acc;
507        let e = ref_signal[i] - acc;
508        err[i] = e;
509
510        // Update coefficients: w[n+1] = w[n] + 2 * mu * e[n] * x[n]
511        let alpha = 2.0 * instance.mu * e;
512        for k in 0..num_taps {
513            instance.coeffs[k] += alpha * instance.state[k];
514        }
515    }
516}
517
518/// Leaky LMS: `w ← (1 - leak) w + 2 μ e x`. `leak = 0` matches [`lms_f32`].
519pub fn lms_leaky_f32(
520    instance: &mut LmsInstanceF32,
521    src: &[f32],
522    ref_signal: &[f32],
523    out: &mut [f32],
524    err: &mut [f32],
525    leak: f32,
526) {
527    let num_taps = instance.num_taps as usize;
528    let block_size = src
529        .len()
530        .min(ref_signal.len())
531        .min(out.len())
532        .min(err.len());
533    let keep = 1.0 - leak;
534
535    for i in 0..block_size {
536        for k in (1..num_taps).rev() {
537            instance.state[k] = instance.state[k - 1];
538        }
539        instance.state[0] = src[i];
540
541        let mut acc = 0.0f32;
542        for k in 0..num_taps {
543            acc += instance.state[k] * instance.coeffs[k];
544        }
545        out[i] = acc;
546        let e = ref_signal[i] - acc;
547        err[i] = e;
548
549        let alpha = 2.0 * instance.mu * e;
550        for k in 0..num_taps {
551            instance.coeffs[k] = keep * instance.coeffs[k] + alpha * instance.state[k];
552        }
553    }
554}
555
556/// Normalized LMS instance (`eps` floors the power denominator).
557pub struct NlmsInstanceF32<'a> {
558    pub num_taps: u16,
559    pub coeffs: &'a mut [f32],
560    pub state: &'a mut [f32],
561    pub mu: f32,
562    pub eps: f32,
563}
564
565impl<'a> NlmsInstanceF32<'a> {
566    pub fn init(
567        num_taps: u16,
568        coeffs: &'a mut [f32],
569        state: &'a mut [f32],
570        mu: f32,
571        eps: f32,
572    ) -> Self {
573        state.fill(0.0);
574        coeffs.fill(0.0);
575        Self {
576            num_taps,
577            coeffs,
578            state,
579            mu,
580            eps,
581        }
582    }
583}
584
585/// NLMS: `w ← w + μ e x / (eps + ||x||²)`.
586pub fn nlms_f32(
587    instance: &mut NlmsInstanceF32,
588    src: &[f32],
589    ref_signal: &[f32],
590    out: &mut [f32],
591    err: &mut [f32],
592) {
593    let num_taps = instance.num_taps as usize;
594    let block_size = src
595        .len()
596        .min(ref_signal.len())
597        .min(out.len())
598        .min(err.len());
599
600    for i in 0..block_size {
601        for k in (1..num_taps).rev() {
602            instance.state[k] = instance.state[k - 1];
603        }
604        instance.state[0] = src[i];
605
606        let mut acc = 0.0f32;
607        let mut power = instance.eps;
608        for k in 0..num_taps {
609            acc += instance.state[k] * instance.coeffs[k];
610            power += instance.state[k] * instance.state[k];
611        }
612        out[i] = acc;
613        let e = ref_signal[i] - acc;
614        err[i] = e;
615
616        let alpha = instance.mu * e / power;
617        for k in 0..num_taps {
618            instance.coeffs[k] += alpha * instance.state[k];
619        }
620    }
621}
622
623/// Q15 LMS adaptive filter.
624pub struct LmsInstanceQ15<'a> {
625    pub num_taps: u16,
626    pub coeffs: &'a mut [q15],
627    pub state: &'a mut [q15],
628    pub mu: q15,
629}
630
631impl<'a> LmsInstanceQ15<'a> {
632    pub fn init(num_taps: u16, coeffs: &'a mut [q15], state: &'a mut [q15], mu: q15) -> Self {
633        state.fill(0);
634        coeffs.fill(0);
635        Self {
636            num_taps,
637            coeffs,
638            state,
639            mu,
640        }
641    }
642}
643
644fn lms_q15_inner(
645    instance: &mut LmsInstanceQ15,
646    src: &[q15],
647    ref_signal: &[q15],
648    out: &mut [q15],
649    err: &mut [q15],
650    leak: q15,
651) {
652    let num_taps = instance.num_taps as usize;
653    let block_size = src
654        .len()
655        .min(ref_signal.len())
656        .min(out.len())
657        .min(err.len());
658    let keep = 32767i32 - leak.max(0) as i32;
659
660    for i in 0..block_size {
661        for k in (1..num_taps).rev() {
662            instance.state[k] = instance.state[k - 1];
663        }
664        instance.state[0] = src[i];
665
666        let mut acc: i64 = 0;
667        for k in 0..num_taps {
668            acc += instance.state[k] as i64 * instance.coeffs[k] as i64;
669        }
670        let y = (acc >> 15).clamp(i16::MIN as i64, i16::MAX as i64);
671        out[i] = y as q15;
672        let e = (ref_signal[i] as i32 - y as i32).clamp(i16::MIN as i32, i16::MAX as i32);
673        err[i] = e as q15;
674
675        let alpha = (2i64 * instance.mu as i64 * e as i64) >> 15;
676        for k in 0..num_taps {
677            let leaked = (keep as i64 * instance.coeffs[k] as i64) >> 15;
678            let upd = leaked + ((alpha * instance.state[k] as i64) >> 15);
679            instance.coeffs[k] = upd.clamp(i16::MIN as i64, i16::MAX as i64) as q15;
680        }
681    }
682}
683
684pub fn lms_q15(
685    instance: &mut LmsInstanceQ15,
686    src: &[q15],
687    ref_signal: &[q15],
688    out: &mut [q15],
689    err: &mut [q15],
690) {
691    lms_q15_inner(instance, src, ref_signal, out, err, 0);
692}
693
694/// Leaky LMS in Q15. `leak` is Q1.15 (`0` matches [`lms_q15`]).
695pub fn lms_leaky_q15(
696    instance: &mut LmsInstanceQ15,
697    src: &[q15],
698    ref_signal: &[q15],
699    out: &mut [q15],
700    err: &mut [q15],
701    leak: q15,
702) {
703    lms_q15_inner(instance, src, ref_signal, out, err, leak);
704}
705
706/// Q15 NLMS instance.
707pub struct NlmsInstanceQ15<'a> {
708    pub num_taps: u16,
709    pub coeffs: &'a mut [q15],
710    pub state: &'a mut [q15],
711    pub mu: q15,
712    pub eps: q15,
713}
714
715impl<'a> NlmsInstanceQ15<'a> {
716    pub fn init(
717        num_taps: u16,
718        coeffs: &'a mut [q15],
719        state: &'a mut [q15],
720        mu: q15,
721        eps: q15,
722    ) -> Self {
723        state.fill(0);
724        coeffs.fill(0);
725        Self {
726            num_taps,
727            coeffs,
728            state,
729            mu,
730            eps,
731        }
732    }
733}
734
735pub fn nlms_q15(
736    instance: &mut NlmsInstanceQ15,
737    src: &[q15],
738    ref_signal: &[q15],
739    out: &mut [q15],
740    err: &mut [q15],
741) {
742    let num_taps = instance.num_taps as usize;
743    let block_size = src
744        .len()
745        .min(ref_signal.len())
746        .min(out.len())
747        .min(err.len());
748
749    for i in 0..block_size {
750        for k in (1..num_taps).rev() {
751            instance.state[k] = instance.state[k - 1];
752        }
753        instance.state[0] = src[i];
754
755        let mut acc: i64 = 0;
756        let mut power: i64 = instance.eps.max(1) as i64;
757        for k in 0..num_taps {
758            let x = instance.state[k] as i64;
759            acc += x * instance.coeffs[k] as i64;
760            power += (x * x) >> 15;
761        }
762        let y = (acc >> 15).clamp(i16::MIN as i64, i16::MAX as i64);
763        out[i] = y as q15;
764        let e = (ref_signal[i] as i32 - y as i32).clamp(i16::MIN as i32, i16::MAX as i32);
765        err[i] = e as q15;
766
767        let alpha = (instance.mu as i64 * e as i64) / power;
768        for k in 0..num_taps {
769            let upd =
770                instance.coeffs[k] as i64 + ((alpha * instance.state[k] as i64) >> 15);
771            instance.coeffs[k] = upd.clamp(i16::MIN as i64, i16::MAX as i64) as q15;
772        }
773    }
774}
775
776// --- Convolution ---
777
778pub fn conv_f32(src_a: &[f32], src_b: &[f32], dst: &mut [f32]) {
779    let len_a = src_a.len();
780    let len_b = src_b.len();
781    let out_len = (len_a + len_b - 1).min(dst.len());
782
783    dst[..out_len].fill(0.0);
784    for i in 0..len_a {
785        for j in 0..len_b {
786            if i + j < out_len {
787                dst[i + j] += src_a[i] * src_b[j];
788            }
789        }
790    }
791}
792
793pub fn conv_q31(src_a: &[q31], src_b: &[q31], dst: &mut [q31]) {
794    let len_a = src_a.len();
795    let len_b = src_b.len();
796    let out_len = (len_a + len_b - 1).min(dst.len());
797
798    for n in 0..out_len {
799        let mut acc: i64 = 0;
800        let k_min = if n >= len_b - 1 { n - (len_b - 1) } else { 0 };
801        let k_max = n.min(len_a - 1);
802        for k in k_min..=k_max {
803            acc += (src_a[k] as i64 * src_b[n - k] as i64) >> 31;
804        }
805        dst[n] = acc.clamp(i32::MIN as i64, i32::MAX as i64) as q31;
806    }
807}
808
809pub fn conv_q15(src_a: &[q15], src_b: &[q15], dst: &mut [q15]) {
810    let len_a = src_a.len();
811    let len_b = src_b.len();
812    let out_len = (len_a + len_b - 1).min(dst.len());
813
814    for n in 0..out_len {
815        let mut acc: i32 = 0;
816        let k_min = if n >= len_b - 1 { n - (len_b - 1) } else { 0 };
817        let k_max = n.min(len_a - 1);
818        for k in k_min..=k_max {
819            acc += (src_a[k] as i32 * src_b[n - k] as i32) >> 15;
820        }
821        dst[n] = acc.clamp(i16::MIN as i32, i16::MAX as i32) as q15;
822    }
823}
824
825pub fn conv_q7(src_a: &[q7], src_b: &[q7], dst: &mut [q7]) {
826    let len_a = src_a.len();
827    let len_b = src_b.len();
828    let out_len = (len_a + len_b - 1).min(dst.len());
829
830    for n in 0..out_len {
831        let mut acc: i32 = 0;
832        let k_min = if n >= len_b - 1 { n - (len_b - 1) } else { 0 };
833        let k_max = n.min(len_a - 1);
834        for k in k_min..=k_max {
835            acc += (src_a[k] as i32 * src_b[n - k] as i32) >> 7;
836        }
837        dst[n] = acc.clamp(i8::MIN as i32, i8::MAX as i32) as q7;
838    }
839}
840
841// --- Correlation ---
842
843pub fn correlate_f32(src_a: &[f32], src_b: &[f32], dst: &mut [f32]) {
844    let len_a = src_a.len();
845    let len_b = src_b.len();
846    let out_len = (len_a + len_b - 1).min(dst.len());
847
848    dst[..out_len].fill(0.0);
849    for n in 0..out_len {
850        let mut acc = 0.0f32;
851        for k in 0..len_a {
852            let idx_b = (k as isize) + (len_b as isize - 1) - (n as isize);
853            if idx_b >= 0 && (idx_b as usize) < len_b {
854                acc += src_a[k] * src_b[idx_b as usize];
855            }
856        }
857        dst[n] = acc;
858    }
859}
860
861pub fn correlate_q31(src_a: &[q31], src_b: &[q31], dst: &mut [q31]) {
862    let len_a = src_a.len();
863    let len_b = src_b.len();
864    let out_len = (len_a + len_b - 1).min(dst.len());
865
866    for n in 0..out_len {
867        let mut acc: i64 = 0;
868        for k in 0..len_a {
869            let idx_b = (k as isize) + (len_b as isize - 1) - (n as isize);
870            if idx_b >= 0 && (idx_b as usize) < len_b {
871                acc += (src_a[k] as i64 * src_b[idx_b as usize] as i64) >> 31;
872            }
873        }
874        dst[n] = acc.clamp(i32::MIN as i64, i32::MAX as i64) as q31;
875    }
876}
877
878pub fn correlate_q15(src_a: &[q15], src_b: &[q15], dst: &mut [q15]) {
879    let len_a = src_a.len();
880    let len_b = src_b.len();
881    let out_len = (len_a + len_b - 1).min(dst.len());
882
883    for n in 0..out_len {
884        let mut acc: i32 = 0;
885        for k in 0..len_a {
886            let idx_b = (k as isize) + (len_b as isize - 1) - (n as isize);
887            if idx_b >= 0 && (idx_b as usize) < len_b {
888                acc += (src_a[k] as i32 * src_b[idx_b as usize] as i32) >> 15;
889            }
890        }
891        dst[n] = acc.clamp(i16::MIN as i32, i16::MAX as i32) as q15;
892    }
893}
894
895// --- Non-linear Filtering (Median & Conditional Median) ---
896
897#[allow(unused_imports)]
898use crate::math::FloatMath;
899#[cfg(feature = "transform")]
900use crate::transform::cfft_f32;
901
902/// 1D Conditional / Thresholded Median Filter for f32.
903///
904/// Replaces sample `src[i]` with the local median only if `|src[i] - median| > threshold`.
905/// When `threshold == 0.0`, performs standard median filtering.
906///
907/// `window_len` must be odd and $\le 63$.
908pub fn median_filter_1d_f32(
909    src: &[f32],
910    dst: &mut [f32],
911    window_len: usize,
912    threshold: f32,
913) -> Status {
914    let n = src.len();
915    if n == 0 || dst.len() < n {
916        return Status::LengthError;
917    }
918    if window_len == 0 || window_len % 2 == 0 || window_len > 63 {
919        return Status::ArgumentError;
920    }
921
922    let half = window_len / 2;
923    let mut sort_buf = [0.0f32; 64];
924
925    for i in 0..n {
926        // Populate window with boundary clamping
927        for j in 0..window_len {
928            let idx = (i as isize + j as isize - half as isize).clamp(0, (n - 1) as isize) as usize;
929            sort_buf[j] = src[idx];
930        }
931
932        // Insertion sort on small stack buffer
933        for a in 1..window_len {
934            let mut b = a;
935            while b > 0 && sort_buf[b - 1] > sort_buf[b] {
936                sort_buf.swap(b - 1, b);
937                b -= 1;
938            }
939        }
940
941        let med = sort_buf[half];
942        let center = src[i];
943        if (center - med).abs() >= threshold {
944            dst[i] = med;
945        } else {
946            dst[i] = center;
947        }
948    }
949
950    Status::Success
951}
952
953/// 1D Conditional Median Filter for Q15.
954pub fn median_filter_1d_q15(
955    src: &[q15],
956    dst: &mut [q15],
957    window_len: usize,
958    threshold: q15,
959) -> Status {
960    let n = src.len();
961    if n == 0 || dst.len() < n {
962        return Status::LengthError;
963    }
964    if window_len == 0 || window_len % 2 == 0 || window_len > 63 {
965        return Status::ArgumentError;
966    }
967
968    let half = window_len / 2;
969    let mut sort_buf = [0i16; 64];
970
971    for i in 0..n {
972        for j in 0..window_len {
973            let idx = (i as isize + j as isize - half as isize).clamp(0, (n - 1) as isize) as usize;
974            sort_buf[j] = src[idx];
975        }
976
977        for a in 1..window_len {
978            let mut b = a;
979            while b > 0 && sort_buf[b - 1] > sort_buf[b] {
980                sort_buf.swap(b - 1, b);
981                b -= 1;
982            }
983        }
984
985        let med = sort_buf[half];
986        let center = src[i];
987        let diff = (center as i32 - med as i32).abs();
988        if diff >= threshold as i32 {
989            dst[i] = med;
990        } else {
991            dst[i] = center;
992        }
993    }
994
995    Status::Success
996}
997
998/// 1D Conditional Median Filter for Q31.
999pub fn median_filter_1d_q31(
1000    src: &[q31],
1001    dst: &mut [q31],
1002    window_len: usize,
1003    threshold: q31,
1004) -> Status {
1005    let n = src.len();
1006    if n == 0 || dst.len() < n {
1007        return Status::LengthError;
1008    }
1009    if window_len == 0 || window_len % 2 == 0 || window_len > 63 {
1010        return Status::ArgumentError;
1011    }
1012
1013    let half = window_len / 2;
1014    let mut sort_buf = [0i32; 64];
1015
1016    for i in 0..n {
1017        for j in 0..window_len {
1018            let idx = (i as isize + j as isize - half as isize).clamp(0, (n - 1) as isize) as usize;
1019            sort_buf[j] = src[idx];
1020        }
1021
1022        for a in 1..window_len {
1023            let mut b = a;
1024            while b > 0 && sort_buf[b - 1] > sort_buf[b] {
1025                sort_buf.swap(b - 1, b);
1026                b -= 1;
1027            }
1028        }
1029
1030        let med = sort_buf[half];
1031        let center = src[i];
1032        let diff = (center as i64 - med as i64).abs();
1033        if diff >= threshold as i64 {
1034            dst[i] = med;
1035        } else {
1036            dst[i] = center;
1037        }
1038    }
1039
1040    Status::Success
1041}
1042
1043// --- FFT Fast Convolution ---
1044
1045/// Performs fast linear convolution of `signal` and `kernel` via FFT multiplication.
1046/// Output length is `signal.len() + kernel.len() - 1`.
1047///
1048/// Requires the `transform` feature (enabled by `full`).
1049#[cfg(feature = "transform")]
1050pub fn fast_convolve_f32(signal: &[f32], kernel: &[f32], dst: &mut [f32]) -> Status {
1051    let len_sig = signal.len();
1052    let len_ker = kernel.len();
1053    if len_sig == 0 || len_ker == 0 {
1054        return Status::LengthError;
1055    }
1056    let total_len = len_sig + len_ker - 1;
1057    if dst.len() < total_len {
1058        return Status::LengthError;
1059    }
1060
1061    // Find next power of 2
1062    let mut fft_n = 1;
1063    while fft_n < total_len {
1064        fft_n <<= 1;
1065    }
1066
1067    if fft_n > 512 {
1068        // Fall back to time-domain convolution if size exceeds stack scratch buffer
1069        conv_f32(signal, kernel, dst);
1070        return Status::Success;
1071    }
1072
1073    let mut sig_buf = [0.0f32; 1024]; // 2 * fft_n
1074    let mut ker_buf = [0.0f32; 1024];
1075
1076    for i in 0..len_sig {
1077        sig_buf[2 * i] = signal[i];
1078    }
1079    for i in 0..len_ker {
1080        ker_buf[2 * i] = kernel[i];
1081    }
1082
1083    cfft_f32(&mut sig_buf[..2 * fft_n], fft_n, 0, 1);
1084    cfft_f32(&mut ker_buf[..2 * fft_n], fft_n, 0, 1);
1085
1086    // Pointwise complex multiplication: (a + jb) * (c + jd)
1087    for i in 0..fft_n {
1088        let a = sig_buf[2 * i];
1089        let b = sig_buf[2 * i + 1];
1090        let c = ker_buf[2 * i];
1091        let d = ker_buf[2 * i + 1];
1092        sig_buf[2 * i] = a * c - b * d;
1093        sig_buf[2 * i + 1] = a * d + b * c;
1094    }
1095
1096    // Inverse FFT
1097    cfft_f32(&mut sig_buf[..2 * fft_n], fft_n, 1, 1);
1098
1099    for i in 0..total_len {
1100        dst[i] = sig_buf[2 * i];
1101    }
1102
1103    Status::Success
1104}
1105
1106// --- Real-time Circular Buffer & Delay Line ---
1107
1108/// Const-generic zero-allocation circular buffer and delay line for real-time DSP sample streams.
1109#[derive(Debug, Clone, Copy)]
1110pub struct CircularBuffer<T, const N: usize> {
1111    buffer: [T; N],
1112    head: usize,
1113    count: usize,
1114}
1115
1116impl<T: Copy, const N: usize> CircularBuffer<T, N> {
1117    /// Creates a new circular buffer initialized with `init_val`.
1118    pub const fn new(init_val: T) -> Self {
1119        Self {
1120            buffer: [init_val; N],
1121            head: 0,
1122            count: 0,
1123        }
1124    }
1125
1126    /// Pushes a new sample into the buffer, overwriting the oldest sample when full.
1127    #[inline(always)]
1128    pub fn push(&mut self, sample: T) {
1129        if N == 0 {
1130            return;
1131        }
1132        self.buffer[self.head] = sample;
1133        self.head = (self.head + 1) % N;
1134        if self.count < N {
1135            self.count += 1;
1136        }
1137    }
1138
1139    /// Gets sample with historical lag $k$, where $k = 0$ is the newest sample (`x[n]`), $k = 1$ is `x[n-1]`, etc.
1140    /// Returns `None` if `lag >= self.len()`.
1141    #[inline(always)]
1142    pub fn get(&self, lag: usize) -> Option<T> {
1143        if lag >= self.count || N == 0 {
1144            return None;
1145        }
1146        let idx = (self.head + N - 1 - (lag % N)) % N;
1147        Some(self.buffer[idx])
1148    }
1149
1150    /// Returns the most recently pushed sample (`x[n]`).
1151    #[inline(always)]
1152    pub fn latest(&self) -> Option<T> {
1153        self.get(0)
1154    }
1155
1156    /// Returns the oldest sample stored in the buffer.
1157    #[inline(always)]
1158    pub fn oldest(&self) -> Option<T> {
1159        if self.count == 0 {
1160            None
1161        } else {
1162            self.get(self.count - 1)
1163        }
1164    }
1165
1166    /// Returns the number of valid samples currently stored in the buffer.
1167    #[inline(always)]
1168    pub const fn len(&self) -> usize {
1169        self.count
1170    }
1171
1172    /// Returns the capacity of the circular buffer (`N`).
1173    #[inline(always)]
1174    pub const fn capacity(&self) -> usize {
1175        N
1176    }
1177
1178    /// Returns `true` if the buffer contains no samples.
1179    #[inline(always)]
1180    pub const fn is_empty(&self) -> bool {
1181        self.count == 0
1182    }
1183
1184    /// Returns `true` if the buffer is filled to capacity `N`.
1185    #[inline(always)]
1186    pub const fn is_full(&self) -> bool {
1187        self.count == N
1188    }
1189
1190    /// Clears the circular buffer, resetting sample count and filling with `reset_val`.
1191    pub fn clear(&mut self, reset_val: T) {
1192        self.buffer = [reset_val; N];
1193        self.head = 0;
1194        self.count = 0;
1195    }
1196}
1197
1198// --- Single-Pole Recursive Filter (Steven W. Smith, Ch. 19) ---
1199
1200/// The cheapest possible IIR filter: a single-pole recursive low-pass or high-pass filter
1201/// (Steven W. Smith, Ch. 19, Eq. 19-2 / 19-3), needing only one or two multiplies per sample.
1202/// Coefficients are designed from a decay factor `x` (see
1203/// [`crate::filter_design::single_pole_decay_from_cutoff`] /
1204/// [`crate::filter_design::single_pole_decay_from_time_constant`]).
1205#[derive(Debug, Clone, Copy, Default)]
1206#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1207pub struct SinglePoleFilter {
1208    b0: f32,
1209    b1: f32,
1210    a1: f32,
1211    x1: f32,
1212    y1: f32,
1213}
1214
1215impl SinglePoleFilter {
1216    /// Creates a single-pole low-pass filter from decay factor `x` (`0.0..1.0`); larger `x`
1217    /// means slower decay (a lower cutoff frequency).
1218    pub fn lowpass(decay: f32) -> Self {
1219        Self {
1220            b0: 1.0 - decay,
1221            b1: 0.0,
1222            a1: decay,
1223            x1: 0.0,
1224            y1: 0.0,
1225        }
1226    }
1227
1228    /// Creates a single-pole high-pass filter from the same decay factor `x` used by
1229    /// [`SinglePoleFilter::lowpass`].
1230    pub fn highpass(decay: f32) -> Self {
1231        let b0 = (1.0 + decay) / 2.0;
1232        Self {
1233            b0,
1234            b1: -b0,
1235            a1: decay,
1236            x1: 0.0,
1237            y1: 0.0,
1238        }
1239    }
1240
1241    /// Processes a single input sample and returns the filtered output.
1242    #[inline(always)]
1243    pub fn process(&mut self, x: f32) -> f32 {
1244        let y = self.b0 * x + self.b1 * self.x1 + self.a1 * self.y1;
1245        self.x1 = x;
1246        self.y1 = y;
1247        y
1248    }
1249
1250    /// Resets the filter's delay state to zero.
1251    pub fn reset(&mut self) {
1252        self.x1 = 0.0;
1253        self.y1 = 0.0;
1254    }
1255}
1256
1257/// Q15 single-pole recursive low-pass or high-pass filter (same recurrence as
1258/// [`SinglePoleFilter`]). `decay` is Q1.15 in `0..1` (larger → lower cutoff).
1259#[derive(Debug, Clone, Copy, Default)]
1260#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1261pub struct SinglePoleFilterQ15 {
1262    b0: q15,
1263    b1: q15,
1264    a1: q15,
1265    x1: q15,
1266    y1: q15,
1267}
1268
1269impl SinglePoleFilterQ15 {
1270    /// Creates a single-pole low-pass filter from Q15 decay `x`.
1271    pub fn lowpass(decay: q15) -> Self {
1272        let decay = decay.max(0);
1273        Self {
1274            b0: (32767i32 - decay as i32) as q15,
1275            b1: 0,
1276            a1: decay,
1277            x1: 0,
1278            y1: 0,
1279        }
1280    }
1281
1282    /// Creates a single-pole high-pass filter from the same Q15 decay used by
1283    /// [`SinglePoleFilterQ15::lowpass`].
1284    pub fn highpass(decay: q15) -> Self {
1285        let decay = decay.max(0);
1286        let b0 = ((32767i32 + decay as i32) / 2) as q15;
1287        Self {
1288            b0,
1289            b1: -b0,
1290            a1: decay,
1291            x1: 0,
1292            y1: 0,
1293        }
1294    }
1295
1296    /// Quantizes a floating-point decay in `0.0..1.0` to Q15 and builds a low-pass.
1297    pub fn lowpass_from_f32(decay: f32) -> Self {
1298        Self::lowpass((decay * 32767.0).clamp(0.0, 32767.0) as q15)
1299    }
1300
1301    /// Quantizes a floating-point decay in `0.0..1.0` to Q15 and builds a high-pass.
1302    pub fn highpass_from_f32(decay: f32) -> Self {
1303        Self::highpass((decay * 32767.0).clamp(0.0, 32767.0) as q15)
1304    }
1305
1306    /// Processes a single Q15 input sample and returns the filtered output.
1307    #[inline(always)]
1308    pub fn process(&mut self, x: q15) -> q15 {
1309        let y = (self.b0 as i64 * x as i64
1310            + self.b1 as i64 * self.x1 as i64
1311            + self.a1 as i64 * self.y1 as i64)
1312            >> 15;
1313        let y = y.clamp(i16::MIN as i64, i16::MAX as i64) as q15;
1314        self.x1 = x;
1315        self.y1 = y;
1316        y
1317    }
1318
1319    /// Resets the filter's delay state to zero.
1320    pub fn reset(&mut self) {
1321        self.x1 = 0;
1322        self.y1 = 0;
1323    }
1324}
1325
1326/// High-pass single-pole used as a DC blocker (Smith Ch. 19).
1327#[derive(Debug, Clone, Copy, Default)]
1328#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1329pub struct DcBlockerQ15 {
1330    inner: SinglePoleFilterQ15,
1331}
1332
1333impl DcBlockerQ15 {
1334    /// `decay` is the same Q15 factor as [`SinglePoleFilterQ15::highpass`].
1335    pub fn new(decay: q15) -> Self {
1336        Self {
1337            inner: SinglePoleFilterQ15::highpass(decay),
1338        }
1339    }
1340
1341    /// Quantizes a floating-point decay in `0.0..1.0`.
1342    pub fn from_f32_decay(decay: f32) -> Self {
1343        Self {
1344            inner: SinglePoleFilterQ15::highpass_from_f32(decay),
1345        }
1346    }
1347
1348    #[inline(always)]
1349    pub fn process(&mut self, x: q15) -> q15 {
1350        self.inner.process(x)
1351    }
1352
1353    pub fn reset(&mut self) {
1354        self.inner.reset();
1355    }
1356}
1357
1358// --- Recursive Moving Average Filter (Steven W. Smith, Ch. 15) ---
1359
1360/// Const-generic `N`-point moving average filter implemented recursively (Steven W. Smith,
1361/// Ch. 15, Eq. 15-3): each sample is updated with a single add and subtract, instead of an
1362/// `O(N)` convolution sum.
1363#[derive(Debug, Clone)]
1364#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1365pub struct RecursiveMovingAverage<const N: usize> {
1366    history: CircularBuffer<f32, N>,
1367    sum: f32,
1368}
1369
1370impl<const N: usize> RecursiveMovingAverage<N> {
1371    /// Creates a new `N`-point recursive moving average filter with empty history.
1372    pub const fn new() -> Self {
1373        Self {
1374            history: CircularBuffer::new(0.0),
1375            sum: 0.0,
1376        }
1377    }
1378
1379    /// Pushes a new input sample and returns the updated moving average. While fewer than `N`
1380    /// samples have been seen, the average is taken over the (growing) window received so far.
1381    #[inline(always)]
1382    pub fn process(&mut self, x: f32) -> f32 {
1383        let oldest = if self.history.is_full() {
1384            self.history.oldest().unwrap_or(0.0)
1385        } else {
1386            0.0
1387        };
1388        self.sum += x - oldest;
1389        self.history.push(x);
1390        if self.history.len() == 0 {
1391            0.0
1392        } else {
1393            self.sum / self.history.len() as f32
1394        }
1395    }
1396
1397    /// Resets the filter to its initial, empty state.
1398    pub fn reset(&mut self) {
1399        self.history.clear(0.0);
1400        self.sum = 0.0;
1401    }
1402}
1403
1404impl<const N: usize> Default for RecursiveMovingAverage<N> {
1405    fn default() -> Self {
1406        Self::new()
1407    }
1408}
1409
1410/// Q15 recursive `N`-point moving average (same recurrence as [`RecursiveMovingAverage`]).
1411#[derive(Debug, Clone)]
1412#[cfg_attr(feature = "defmt", derive(defmt::Format))]
1413pub struct RecursiveMovingAverageQ15<const N: usize> {
1414    history: CircularBuffer<q15, N>,
1415    sum: i32,
1416}
1417
1418impl<const N: usize> RecursiveMovingAverageQ15<N> {
1419    pub const fn new() -> Self {
1420        Self {
1421            history: CircularBuffer::new(0),
1422            sum: 0,
1423        }
1424    }
1425
1426    #[inline(always)]
1427    pub fn process(&mut self, x: q15) -> q15 {
1428        let oldest = if self.history.is_full() {
1429            self.history.oldest().unwrap_or(0)
1430        } else {
1431            0
1432        };
1433        self.sum += x as i32 - oldest as i32;
1434        self.history.push(x);
1435        if self.history.len() == 0 {
1436            0
1437        } else {
1438            (self.sum / self.history.len() as i32).clamp(i16::MIN as i32, i16::MAX as i32) as q15
1439        }
1440    }
1441
1442    pub fn reset(&mut self) {
1443        self.history.clear(0);
1444        self.sum = 0;
1445    }
1446}
1447
1448impl<const N: usize> Default for RecursiveMovingAverageQ15<N> {
1449    fn default() -> Self {
1450        Self::new()
1451    }
1452}