Skip to main content

static_fir/
lib.rs

1//! Finite-impulse response (FIR) convolution with static tap coefficients.
2//!
3//! ## Example
4//!
5//! The following example shows typical API usage:
6//!
7//! ```rust
8//! #[macro_use]
9//! extern crate static_fir;
10//!
11//! use static_fir::FirFilter;
12//!
13//! impl_fir!(LowpassFir, f32, 21, [
14//!     -0.0022183273232,
15//!     -0.00364708336518,
16//!     -0.0058179856702,
17//!     -0.00616633506547,
18//!     2.60007787671e-18,
19//!     0.0172901503422,
20//!     0.0472883481821,
21//!     0.0864914386425,
22//!     0.126465151635,
23//!     0.156489628279,
24//!     0.167650028687,
25//!     0.156489628279,
26//!     0.126465151635,
27//!     0.0864914386425,
28//!     0.0472883481821,
29//!     0.0172901503422,
30//!     2.60007787671e-18,
31//!     -0.00616633506547,
32//!     -0.0058179856702,
33//!     -0.00364708336518,
34//!     -0.0022183273232,
35//! ]);
36//!
37//! fn main() {
38//!     let mut filt = FirFilter::<LowpassFir>::new();
39//!
40//!     // Run filter over a couple samples.
41//!     assert_eq!(filt.feed(1.0), -0.0022183273232);
42//!     assert_eq!(filt.feed(2.0), -0.008083738011580001);
43//!
44//!     // Pad out rest of history.
45//!     for _ in 0..19 {
46//!         filt.feed(0.0);
47//!     }
48//!
49//!     // Iterate over history in order.
50//!     let mut hist = filt.history();
51//!     assert_eq!(hist.next().unwrap(), &1.0);
52//!     assert_eq!(hist.next().unwrap(), &2.0);
53//!     assert_eq!(hist.next().unwrap(), &0.0);
54//!
55//!     // Compute energy of stored samples.
56//!     assert_eq!(filt.history_unordered().fold(0.0, |s, x| {
57//!         s + x.powi(2)
58//!     }), 5.0);
59//! }
60//! ```
61
62#![feature(conservative_impl_trait)]
63
64use std::ops::{Add, Mul, Deref, DerefMut};
65
66/// Provides a sequence of coefficients and storage for sample history.
67pub trait FirCoefs: Default + Deref<Target = [<Self as FirCoefs>::Sample]> + DerefMut {
68    /// Type of sample stored in the history.
69    type Sample: Copy + Clone + Default + Add<Output = Self::Sample> +
70        Mul<f32, Output = Self::Sample>;
71
72    /// Number of coefficients/stored samples.
73    fn size() -> usize;
74    /// Sequence of coefficients.
75    fn coefs() -> &'static [f32];
76
77    /// Verify the requirement that the filter coefficients are symmetric around the
78    /// center (either even or odd length.)
79    fn verify_symmetry() {
80        for i in 0..Self::size() / 2 {
81            assert_eq!(Self::coefs()[i], Self::coefs()[Self::size() - i - 1]);
82        }
83    }
84}
85
86/// Implement `FirCoefs` for a history buffer with the given name, input sample type,
87/// storage size, and sequence of coefficients.
88#[macro_export]
89macro_rules! impl_fir {
90    ($name:ident, $sample:ty, $size:expr, $coefs:expr) => {
91        pub struct $name([$sample; $size]);
92
93        impl $crate::FirCoefs for $name {
94            type Sample = $sample;
95            fn size() -> usize { $size }
96            fn coefs() -> &'static [f32] {
97                static COEFS: [f32; $size] = $coefs;
98                &COEFS[..]
99            }
100        }
101
102        impl Default for $name {
103            fn default() -> Self {
104                $name([<Self as $crate::FirCoefs>::Sample::default(); $size])
105            }
106        }
107
108        impl std::ops::Deref for $name {
109            type Target = [$sample];
110            fn deref(&self) -> &Self::Target { &self.0[..] }
111        }
112
113        impl std::ops::DerefMut for $name {
114            fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0[..] }
115        }
116    };
117}
118
119/// A FIR filter for convolving with a series of samples.
120pub struct FirFilter<C: FirCoefs> {
121    /// Coefficients and history storage.
122    inner: C,
123    /// The index of the most-recently added sample.
124    idx: usize,
125}
126
127impl<C: FirCoefs> FirFilter<C> {
128    /// Create a new `FirFilter` with empty history.
129    pub fn new() -> FirFilter<C> {
130        FirFilter {
131            inner: C::default(),
132            idx: 0,
133        }
134    }
135
136    /// Add a sample to the current history and calculate the convolution.
137    pub fn feed(&mut self, sample: C::Sample) -> C::Sample {
138        // Store the given sample in the current history slot.
139        self.inner[self.idx] = sample;
140
141        // Move to the next slot and wrap around.
142        self.idx += 1;
143        self.idx %= C::size();
144
145        self.calc()
146    }
147
148    /// Calculate the convolution of saved samples with coefficients, where the given
149    /// index gives the position of the most recent sample in the history ring buffer.
150    fn calc(&self) -> C::Sample {
151        let (hleft, hright) = self.inner.split_at(self.idx);
152        let (cleft, cright) = C::coefs().split_at(C::size() - self.idx);
153
154        cleft.iter().zip(hright)
155            .fold(C::Sample::default(), |s, (&c, &x)| s + x * c) +
156        cright.iter().zip(hleft)
157            .fold(C::Sample::default(), |s, (&c, &x)| s + x * c)
158    }
159
160    /// Create an iterator over the history of stored samples, with the oldest sample as
161    /// the first item yielded and the newest as the last.
162    #[inline]
163    pub fn history<'a>(&'a self) -> impl Iterator<Item = &'a C::Sample> {
164        let (left, right) = self.inner.split_at(self.idx);
165        right.iter().chain(left.iter())
166    }
167
168    /// Create an iterator over the history of stored samples, where the samples are
169    /// yielded in the order they're stored in the underlying ring buffer.
170    ///
171    /// This may be more efficient than `history` if the order of the samples is
172    /// insignificant.
173    pub fn history_unordered<'a>(&'a self) -> impl Iterator<Item = &'a C::Sample> {
174        self.inner.iter()
175    }
176}
177
178#[cfg(test)]
179mod test {
180    use super::*;
181
182    impl_fir!(TestFIR, f32, 4, [
183        1.0,
184        0.0,
185        2.0,
186        0.0,
187    ]);
188
189    impl_fir!(SymmetricOddFIR, f32, 5, [
190        0.2,
191        0.4,
192        1.0,
193        0.4,
194        0.2,
195    ]);
196
197    impl_fir!(SymmetricEvenFIR, f32, 6, [
198        0.2,
199        0.4,
200        1.0,
201        1.0,
202        0.4,
203        0.2,
204    ]);
205
206    impl_fir!(NonSymmetricOddFIR, f32, 5, [
207        0.2,
208        0.4,
209        1.0,
210        0.5,
211        0.2,
212    ]);
213
214    impl_fir!(NonSymmetricEvenFIR, f32, 6, [
215        0.2,
216        0.4,
217        1.0,
218        1.0,
219        0.5,
220        0.2,
221    ]);
222
223    #[test]
224    fn test_fir() {
225        let mut f = FirFilter::<TestFIR>::new();
226
227        assert!(f.feed(100.0) == 0.0);
228        assert!(f.feed(200.0) == 200.0);
229        assert!(f.feed(300.0) == 400.0);
230        assert!(f.feed(400.0) == 700.0);
231        assert!(f.feed(0.0) == 1000.0);
232        assert!(f.feed(0.0) == 300.0);
233        assert!(f.feed(0.0) == 400.0);
234        assert!(f.feed(0.0) == 0.0);
235        assert!(f.feed(0.0) == 0.0);
236        assert!(f.feed(100.0) == 0.0);
237        assert!(f.feed(200.0) == 200.0);
238        assert!(f.feed(300.0) == 400.0);
239        assert!(f.feed(400.0) == 700.0);
240
241        let mut iter = f.history();
242
243        assert_eq!(iter.next().unwrap(), &100.0);
244        assert_eq!(iter.next().unwrap(), &200.0);
245        assert_eq!(iter.next().unwrap(), &300.0);
246        assert_eq!(iter.next().unwrap(), &400.0);
247
248        let mut iter = f.history_unordered();
249
250        assert_eq!(iter.next().unwrap(), &400.0);
251        assert_eq!(iter.next().unwrap(), &100.0);
252        assert_eq!(iter.next().unwrap(), &200.0);
253        assert_eq!(iter.next().unwrap(), &300.0);
254    }
255
256    #[test]
257    fn test_verify_symmetry() {
258        SymmetricOddFIR::verify_symmetry();
259        SymmetricEvenFIR::verify_symmetry();
260    }
261
262    #[test]
263    #[should_panic]
264    fn test_verify_nonsymmetry_odd() {
265        NonSymmetricOddFIR::verify_symmetry();
266    }
267
268    #[test]
269    #[should_panic]
270    fn test_verify_nonsymmetry_even() {
271        NonSymmetricEvenFIR::verify_symmetry();
272    }
273}