fixed_filters/
lib.rs

1// Copyright 2023 Enphase Energy, Inc.
2//
3//    Licensed under the Apache License, Version 2.0 (the "License");
4//    you may not use this file except in compliance with the License.
5//    You may obtain a copy of the License at
6//
7//        http://www.apache.org/licenses/LICENSE-2.0
8//
9//    Unless required by applicable law or agreed to in writing, software
10//    distributed under the License is distributed on an "AS IS" BASIS,
11//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//    See the License for the specific language governing permissions and
13//    limitations under the License.
14
15/*!
16# `fixed-filters`
17
18Biquad filters for fixed-point numbers.
19
20`fixed-filters` is a `#![no_std]` library for creating [biquad filters](https://en.wikipedia.org/wiki/Digital_biquad_filter).  These are simple 2nd order IIR filters which can be configured to implement a variety of filter behaviors (i.e. lowpass, bandpass, highpass, notch, proportional-integral, etc).  It is designed specifically to work with 32-bit fixed-point numbers based on the [fixed](https://crates.io/crates/fixed) crate.
21
22**Alpha:** This crate requires the alpha release of 2.0.0 of the [fixed](https://crates.io/crates/fixed) crate that makes use of const generics instead of the [*typenum* crate](https://crates.io/crate/typenum). This version requires the nightly compiler with the [`generic_const_exprs` feature] enabled.
23
24# How to use
25
26The main data structure for this crate is [Biquad<X,Y>](crate::biquad::Biquad) where `X` and `Y` are `i32` constant generics which represent the number of fractional bits of the input and output signals respectively.  While the run functions are all designed use fixed-point arithmetic, the construction APIs accept floating point variables to simplify use.  The most generic way of creating a filter is to construct with `new()` and pass the filter coefficients.
27
28```rust
29#![feature(generic_const_exprs)]
30
31use fixed_filters::Biquad;
32use fixed::FixedI32;
33
34// poles
35let a0 = 1.;
36let a1 = -1.97985135;
37let a2 = 0.980052318;
38
39// zeros
40let b0 = 0.000050241;
41let b1 = 0.000100482;
42let b2 = 0.000050241;
43
44// make a filter with 20 fractional bits on input and 16 fractional bits on output
45let mut filter = Biquad::<20, 16>::new(a0, a1, a2, b0, b1, b2);
46
47let signal = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
48let mut y = FixedI32::<16>::ZERO;
49for s in signal {
50    let x = FixedI32::<20>::from_num(s); // convert to fixed-point number
51    y = filter.update(x);
52}
53```
54
55While passing the pole and zero coefficients is the most universal way of creating a filter, `Biquad` includes a number of associated functions for creating standard filter forms with the user specifying just the sampling period, the center frequency, and the q factor.
56
57```rust
58use fixed_filters::Biquad;
59
60// create a butterworth lowpass filter
61let ts = 1./10e3;
62let f0 = 100.;
63let q = core::f32::consts::FRAC_1_SQRT_2;
64let mut filter = Biquad::<16, 16>::lowpass(ts, f0, q);
65```
66
67The library includes associted functions for creating the following filter forms:
68
691.  [lowpass](crate::biquad::Biquad::lowpass)
702.  [bandpass](crate::biquad::Biquad::bandpass)
713.  [highpass](crate::biquad::Biquad::highpass)
724.  [notch](crate::biquad::Biquad::notch)
735.  [single_pole_lowpass](crate::biquad::Biquad::single_pole_lowpass)
746.  [proportional-integral behavior](crate::biquad::Biquad::pi)
75
76By default, the filters do not limit their output and can in fact over-flow if you are not careful.  You can add limits to an existing filter using the `set_min`, `set_max`, or `set_limit` functions, or you can setup limits at construction using a builder pattern with the `with_min`, `with_max`, or `with_limit` functions.
77
78```rust
79use fixed_filters::Biquad;
80
81// create a proportional-integral controller with a limit ot +-20
82let kp = 0.1;
83let ki = 0.01;
84let mut filter = Biquad::<16, 16>::pi(kp, ki).with_limit(20.0);
85```
86
87# Benchmarking
88
89The performance of this crate was benchmarked against other available `#![no_std]` biquad filter implementations by running a filter at 10kHz in a simple [RTIC](https://rtic.rs/1/book/en/) application on an [STM32F411RE Nucelo](https://www.st.com/en/evaluation-tools/nucleo-f411re.html) board.  The RTIC monotonic API was used to quantify the number of CPU cycles used for each filters' update method.
90
91| Crate                         | Cycles  |
92| :---------------------------- |:-------:|
93| fixed-filters                 | 80      |
94| biquad::DirectForm1           | 80      |
95| biquad::DirectForm2Transposed | 72      |
96| idsp::iir_int                 | 90      |
97
98From these results, it can be seen that the crates all provide similar performance in terms of execution efficiency with [biquad](https://crates.io/crates/biquad/0.3.0) DirectForm2Transposed having a slight edge in microcontrollers with an FPU.  This crate however does not support internal limiting of the output signal which can be critical in certain applications such as implementing anti-windup and PI controllers.
99*/
100#![cfg_attr(not(test), no_std)]
101
102pub mod biquad;
103
104pub use biquad::Biquad;