Skip to main content

embedded_dsp/
const_generics.rs

1//! Const generic safe wrappers for compile-time sized FIR filters, Biquads, and Matrices.
2
3use crate::filtering::{
4    BiquadCascadeInstanceF32, BiquadCascadeInstanceQ15, FirInstanceF32, FirInstanceQ15,
5    biquad_cascade_df1_f32, biquad_cascade_df1_q15, fir_f32, fir_q15,
6};
7use crate::matrix::{
8    MatrixInstance, MatrixInstanceMut, mat_add_f32, mat_mult_f32, mat_scale_f32, mat_sub_f32,
9    mat_trans_f32,
10};
11use crate::types::q15;
12
13/// Compile-time fixed-size FIR filter holding its own state buffer.
14#[derive(Debug, Clone)]
15#[cfg_attr(feature = "defmt", derive(defmt::Format))]
16pub struct FirFilter<const TAPS: usize> {
17    pub coeffs: [f32; TAPS],
18    state: [f32; TAPS],
19}
20
21impl<const TAPS: usize> FirFilter<TAPS> {
22    /// Create a new FIR filter with given coefficients.
23    pub fn new(coeffs: [f32; TAPS]) -> Self {
24        Self {
25            coeffs,
26            state: [0.0; TAPS],
27        }
28    }
29
30    /// Process input slice `src` into output slice `dst`.
31    pub fn process(&mut self, src: &[f32], dst: &mut [f32]) {
32        let mut instance = FirInstanceF32 {
33            num_taps: TAPS as u16,
34            coeffs: &self.coeffs,
35            state: &mut self.state,
36        };
37        fir_f32(&mut instance, src, dst);
38    }
39
40    /// Reset filter state buffer.
41    pub fn reset(&mut self) {
42        self.state.fill(0.0);
43    }
44}
45
46/// Compile-time fixed-size Biquad Cascade Direct Form I filter holding its state buffer.
47#[derive(Debug, Clone)]
48#[cfg_attr(feature = "defmt", derive(defmt::Format))]
49pub struct BiquadCascade<const COEFFS_LEN: usize, const STATE_LEN: usize> {
50    pub coeffs: [f32; COEFFS_LEN],
51    pub state: [f32; STATE_LEN],
52    num_stages: u8,
53}
54
55impl<const COEFFS_LEN: usize, const STATE_LEN: usize> BiquadCascade<COEFFS_LEN, STATE_LEN> {
56    /// Create a new Biquad cascade filter given coefficients and number of stages.
57    pub fn new(coeffs: [f32; COEFFS_LEN]) -> Self {
58        let num_stages = (COEFFS_LEN / 5) as u8;
59        Self {
60            coeffs,
61            state: [0.0; STATE_LEN],
62            num_stages,
63        }
64    }
65
66    /// Process input slice `src` into output slice `dst`.
67    pub fn process(&mut self, src: &[f32], dst: &mut [f32]) {
68        let mut instance = BiquadCascadeInstanceF32 {
69            num_stages: self.num_stages,
70            coeffs: &self.coeffs,
71            state: &mut self.state,
72        };
73        biquad_cascade_df1_f32(&mut instance, src, dst);
74    }
75
76    /// Reset internal filter delay state.
77    pub fn reset(&mut self) {
78        self.state.fill(0.0);
79    }
80}
81
82/// Compile-time fixed-size Q15 FIR filter holding its own state buffer.
83#[derive(Debug, Clone)]
84#[cfg_attr(feature = "defmt", derive(defmt::Format))]
85pub struct FirFilterQ15<const TAPS: usize> {
86    pub coeffs: [q15; TAPS],
87    state: [q15; TAPS],
88}
89
90impl<const TAPS: usize> FirFilterQ15<TAPS> {
91    pub fn new(coeffs: [q15; TAPS]) -> Self {
92        Self {
93            coeffs,
94            state: [q15::ZERO; TAPS],
95        }
96    }
97
98    pub fn process(&mut self, src: &[q15], dst: &mut [q15]) {
99        let mut instance = FirInstanceQ15 {
100            num_taps: TAPS as u16,
101            coeffs: &self.coeffs,
102            state: &mut self.state,
103        };
104        fir_q15(&mut instance, src, dst);
105    }
106
107    pub fn reset(&mut self) {
108        self.state.fill(q15::ZERO);
109    }
110}
111
112/// Compile-time fixed-size Q15 biquad cascade (Direct Form I).
113#[derive(Debug, Clone)]
114#[cfg_attr(feature = "defmt", derive(defmt::Format))]
115pub struct BiquadCascadeQ15<const COEFFS_LEN: usize, const STATE_LEN: usize> {
116    pub coeffs: [q15; COEFFS_LEN],
117    pub state: [q15; STATE_LEN],
118    num_stages: u8,
119    post_shift: u8,
120}
121
122impl<const COEFFS_LEN: usize, const STATE_LEN: usize> BiquadCascadeQ15<COEFFS_LEN, STATE_LEN> {
123    pub fn new(coeffs: [q15; COEFFS_LEN], post_shift: u8) -> Self {
124        let num_stages = (COEFFS_LEN / 5) as u8;
125        Self {
126            coeffs,
127            state: [q15::ZERO; STATE_LEN],
128            num_stages,
129            post_shift,
130        }
131    }
132
133    pub fn process(&mut self, src: &[q15], dst: &mut [q15]) {
134        let mut instance = BiquadCascadeInstanceQ15 {
135            num_stages: self.num_stages,
136            post_shift: self.post_shift,
137            coeffs: &self.coeffs,
138            state: &mut self.state,
139        };
140        biquad_cascade_df1_q15(&mut instance, src, dst);
141    }
142
143    pub fn reset(&mut self) {
144        self.state.fill(q15::ZERO);
145    }
146}
147
148/// Compile-time fixed-size 2D matrix structure.
149#[derive(Debug, Clone, Copy, PartialEq)]
150#[cfg_attr(feature = "defmt", derive(defmt::Format))]
151pub struct Matrix<const R: usize, const C: usize, const N: usize> {
152    pub data: [f32; N],
153}
154
155impl<const R: usize, const C: usize, const N: usize> Matrix<R, C, N> {
156    /// Create matrix from array.
157    pub const fn new(data: [f32; N]) -> Self {
158        Self { data }
159    }
160
161    /// Matrix zero constructor.
162    pub fn zeros() -> Self {
163        Self { data: [0.0; N] }
164    }
165
166    /// Matrix addition: `self + rhs`.
167    pub fn add(&self, rhs: &Self) -> Self {
168        let mut out = Self::zeros();
169        let a_inst = MatrixInstance::new(R as u16, C as u16, &self.data);
170        let b_inst = MatrixInstance::new(R as u16, C as u16, &rhs.data);
171        let mut out_inst = MatrixInstanceMut::new(R as u16, C as u16, &mut out.data);
172        mat_add_f32(&a_inst, &b_inst, &mut out_inst);
173        out
174    }
175
176    /// Matrix subtraction: `self - rhs`.
177    pub fn sub(&self, rhs: &Self) -> Self {
178        let mut out = Self::zeros();
179        let a_inst = MatrixInstance::new(R as u16, C as u16, &self.data);
180        let b_inst = MatrixInstance::new(R as u16, C as u16, &rhs.data);
181        let mut out_inst = MatrixInstanceMut::new(R as u16, C as u16, &mut out.data);
182        mat_sub_f32(&a_inst, &b_inst, &mut out_inst);
183        out
184    }
185
186    /// Matrix scaling: `self * scale`.
187    pub fn scale(&self, scale: f32) -> Self {
188        let mut out = Self::zeros();
189        let a_inst = MatrixInstance::new(R as u16, C as u16, &self.data);
190        let mut out_inst = MatrixInstanceMut::new(R as u16, C as u16, &mut out.data);
191        mat_scale_f32(&a_inst, scale, &mut out_inst);
192        out
193    }
194
195    /// Matrix transpose.
196    pub fn transpose(&self) -> Matrix<C, R, N> {
197        let mut out = Matrix::<C, R, N>::zeros();
198        let a_inst = MatrixInstance::new(R as u16, C as u16, &self.data);
199        let mut out_inst = MatrixInstanceMut::new(C as u16, R as u16, &mut out.data);
200        mat_trans_f32(&a_inst, &mut out_inst);
201        out
202    }
203
204    /// Matrix multiplication: `self * rhs`.
205    pub fn mul_mat<const C2: usize, const N2: usize, const N_OUT: usize>(
206        &self,
207        rhs: &Matrix<C, C2, N2>,
208    ) -> Matrix<R, C2, N_OUT> {
209        let mut out = Matrix::<R, C2, N_OUT>::zeros();
210        let a_inst = MatrixInstance::new(R as u16, C as u16, &self.data);
211        let b_inst = MatrixInstance::new(C as u16, C2 as u16, &rhs.data);
212        let mut out_inst = MatrixInstanceMut::new(R as u16, C2 as u16, &mut out.data);
213        mat_mult_f32(&a_inst, &b_inst, &mut out_inst);
214        out
215    }
216}