Biquad

Struct Biquad 

Source
pub struct Biquad<C> {
    pub ba: [C; 5],
}
Expand description

Biquad IIR (second order section)

A biquadratic IIR filter supports up to two zeros and two poles in the transfer function.

The Biquad performs the following operation to compute a new output sample y0 from a new input sample x0 given its configuration and previous samples:

y0 = b0*x0 + b1*x1 + b2*x2 + a1*y1 + a2*y2

This implementation here saves storage and improves caching opportunities by decoupling filter configuration (coefficients, limits and offset) from filter state and thus supports both (a) sharing a single filter between multiple states (“channels”) and (b) rapid switching of filters (tuning, transfer) for a given state without copying either state of configuration.

§Filter architecture

Direct Form 1 (DF1) and Direct Form 2 transposed (DF2T) are the only IIR filter structures with an (effective in the case of TDF2) single summing junction this allows clamping of the output before feedback.

DF1 allows atomic coefficient change because only inputs and outputs are stored. The summing junction pipelining of TDF2 would require incremental coefficient changes and is thus less amenable to online tuning.

DF2T needs less state storage (2 instead of 4). This is in addition to the coefficient storage (5 plus 2 limits plus 1 offset)

DF2T is less efficient and less accurate for fixed-point architectures as quantization happens at each intermediate summing junction in addition to the output quantization. This is especially true for common i64 + i32 * i32 -> i64 MACC architectures. One could use wide state storage for fixed point DF2T but that would negate the storage and processing advantages.

§Coefficients

ba: [T; 5] = [b0, b1, b2, a1, a2] is the coefficients type. To represent the IIR coefficients, this contains the feed-forward coefficients b0, b1, b2 followed by the feed-back coefficients a1, a2, all five normalized such that a0 = 1.

The summing junction of the BiquadClamp filter also receives an offset u and applies clamping such that min <= y <= max.

See crate::iir::coefficients::Filter and crate::iir::pid::Builder for ways to generate coefficients.

§Fixed point

Coefficient scaling for fixed point (i.e. integer) processing relies on [dsp_fixedpoint::Q].

Choose the number of fractional bits to meet coefficient range (e.g. potentially a1 = 2 for a double integrator) and guard bits.

§PID controller

The IIR coefficients can be mapped to other transfer function representations, for example PID controllers as described in https://hackmd.io/IACbwcOTSt6Adj3_F9bKuw and https://arxiv.org/abs/1508.06319.

Using a Biquad as a template for a PID controller achieves several important properties:

  • Its transfer function is universal in the sense that any biquadratic transfer function can be implemented (high-passes, gain limits, second order integrators with inherent anti-windup, notches etc) without code changes preserving all features.
  • It inherits a universal implementation of “integrator anti-windup”, also and especially in the presence of set-point changes and in the presence of proportional or derivative gain without any back-off that would reduce steady-state output range.
  • It has universal derivative-kick (undesired, unlimited, and un-physical amplification of set-point changes by the derivative term) avoidance.
  • An offset at the input of an IIR filter (a.k.a. “set-point”) is equivalent to an offset at the summing junction (in output units). They are related by the overall (DC feed-forward) gain of the filter.
  • It stores only previous outputs and inputs. These have direct and invariant interpretation (independent of coefficients and offset). Therefore it can trivially implement bump-less transfer between any coefficients/offset sets.
  • Cascading multiple IIR filters allows stable and robust implementation of transfer functions beyond biquadratic terms.

Fields§

§ba: [C; 5]

Coefficients

[b0, b1, b2, a1, a2]

Such that y0 = (b0*x0 + b1*x1 + b2*x2 + a1*y1 + a2*y2)/(1 << F) where x0, x1, x2 are current, delayed, and doubly delayed inputs and y0, y1, y2 are current, delayed, and doubly delayed outputs.

Note the a1, a2 sign. The transfer function is: H(z) = (b0 + b1*z^-1 + b2*z^-2)/((1 << F) - a2*z^-1 - a2*z^-2)

Implementations§

Source§

impl<C: Clamp + Copy> Biquad<C>

Source

pub const IDENTITY: Self

A unity gain filter

let x0 = 3.0;
let y0 = Biquad::<f32>::IDENTITY.process(&mut DirectForm1::default(), x0);
assert_eq!(y0, x0);
Source

pub const HOLD: Self

A “hold” filter that ingests input and maintains output

let mut state = DirectForm1::<f32>::default();
state.xy[2] = 2.0;
let x0 = 7.0;
let y0 = Biquad::<f32>::HOLD.process(&mut state, x0);
assert_eq!(y0, 2.0);
assert_eq!(state.xy, [x0, 0.0, y0, y0]);
Source

pub const fn proportional(k: C) -> Self

A filter with the given proportional gain at all frequencies

let x0 = 3.0;
let y0 = Biquad::<f32>::proportional(2.0).process(&mut DirectForm1::default(), x0);
assert_eq!(y0, 2.0 * x0);
Source§

impl<C: Copy + Add<Output = C>> Biquad<C>

Source

pub fn forward_gain(&self) -> C

DC forward gain fro input to summing junction

assert_eq!(Biquad::proportional(3.0).forward_gain(), 3.0);

Trait Implementations§

Source§

impl<C, T> Build<Biquad<C>> for Builder<T>
where Self: Build<[C; 5]>, Biquad<C>: From<[C; 5]>,

Source§

type Context = <Builder<T> as Build<[C; 5]>>::Context

The context of the convesion. Read more
Source§

fn build(&self, ctx: &Self::Context) -> Biquad<C>

Perform the conversion
Source§

impl<C: Clone> Clone for Biquad<C>

Source§

fn clone(&self) -> Biquad<C>

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<C: Debug> Debug for Biquad<C>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<C: Default> Default for Biquad<C>

Source§

fn default() -> Biquad<C>

Returns the “default value” for a type. Read more
Source§

impl<'de, C> Deserialize<'de> for Biquad<C>
where C: Deserialize<'de>,

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl<C> From<[[f32; 3]; 2]> for Biquad<C>
where [f32; 5]: Into<Biquad<C>>,

Source§

fn from(ba: [[f32; 3]; 2]) -> Self

Converts to this type from the input type.
Source§

impl<C> From<[[f64; 3]; 2]> for Biquad<C>
where [f64; 5]: Into<Biquad<C>>,

Source§

fn from(ba: [[f64; 3]; 2]) -> Self

Converts to this type from the input type.
Source§

impl<C: Copy + 'static, T> From<[T; 5]> for Biquad<C>
where T: AsPrimitive<C>,

Normalized and sign-flipped coefficients [b0, b1, b2, a1, a2]

Source§

fn from(ba: [T; 5]) -> Self

Converts to this type from the input type.
Source§

impl<C: PartialEq> PartialEq for Biquad<C>

Source§

fn eq(&self, other: &Biquad<C>) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl<C: PartialOrd> PartialOrd for Biquad<C>

Source§

fn partial_cmp(&self, other: &Biquad<C>) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the <= operator. Read more
1.0.0 · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > operator. Read more
1.0.0 · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by the >= operator. Read more
Source§

impl<C> Serialize for Biquad<C>
where C: Serialize,

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl<C, T: Copy, S> SplitInplace<T, S> for Biquad<C>
where Self: SplitProcess<T, T, S>,

§

fn inplace(&self, state: &mut S, xy: &mut [X])

See also [Inplace::inplace]
Source§

impl<T: 'static + Copy, C: Copy + Mul<T, Output = A>, A: Add<Output = A> + AsPrimitive<T>> SplitProcess<T, T, DirectForm1<T>> for Biquad<C>

let mut state = DirectForm1 {
    xy: [0.0, 1.0, 2.0, 3.0],
};
let x0 = 4.0;
let y0 = Biquad::<f32>::IDENTITY.process(&mut state, x0);
assert_eq!(y0, x0);
assert_eq!(state.xy, [x0, 0.0, y0, 2.0]);
Source§

fn process(&self, state: &mut DirectForm1<T>, x0: T) -> T

Process an input into an output Read more
§

fn block(&self, state: &mut S, x: &[X], y: &mut [Y])

Process a block of inputs Read more
Source§

impl<T: Copy + Mul<Output = T> + Add<Output = T>> SplitProcess<T, T, DirectForm2Transposed<T>> for Biquad<T>

use dsp_process::SplitProcess;
use idsp::iir::*;
let biquad = Biquad::<f32>::IDENTITY;
let mut state = DirectForm2Transposed::default();
let x = 3.0f32;
let y = biquad.process(&mut state, x);
assert_eq!(x, y);
Source§

fn process(&self, state: &mut DirectForm2Transposed<T>, x0: T) -> T

Process an input into an output Read more
§

fn block(&self, state: &mut S, x: &[X], y: &mut [Y])

Process a block of inputs Read more
Source§

impl<const F: i8> SplitProcess<i32, i32, DirectForm1Dither> for Biquad<Q<i32, i64, F>>

let mut state = DirectForm1Dither {
    xy: [1, 2, 3, 4],
    e: 5,
};
let x0 = 6;
let y0 = Biquad::<Q32<30>>::IDENTITY.process(&mut state, x0);
assert_eq!(y0, x0);
assert_eq!(state.xy, [x0, 1, y0, 3]);
assert_eq!(state.e, 20);
Source§

fn process(&self, state: &mut DirectForm1Dither, x0: i32) -> i32

Process an input into an output Read more
§

fn block(&self, state: &mut S, x: &[X], y: &mut [Y])

Process a block of inputs Read more
Source§

impl<const F: i8> SplitProcess<i32, i32, DirectForm1Wide> for Biquad<Q<i32, i64, F>>

Source§

fn process(&self, state: &mut DirectForm1Wide, x0: i32) -> i32

Process an input into an output Read more
§

fn block(&self, state: &mut S, x: &[X], y: &mut [Y])

Process a block of inputs Read more
Source§

impl<C> StructuralPartialEq for Biquad<C>

Auto Trait Implementations§

§

impl<C> Freeze for Biquad<C>
where C: Freeze,

§

impl<C> RefUnwindSafe for Biquad<C>
where C: RefUnwindSafe,

§

impl<C> Send for Biquad<C>
where C: Send,

§

impl<C> Sync for Biquad<C>
where C: Sync,

§

impl<C> Unpin for Biquad<C>
where C: Unpin,

§

impl<C> UnwindSafe for Biquad<C>
where C: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,