nk 1.0.0
C++ Numerical Computing Runtime
Loading...
Searching...
No Matches
iir_filter.hpp
1// ops/include/nk/ops/iir_filter.hpp
2//
3// Direct-Form-II-transposed IIR filter recurrence — the numerical core of
4// signal's filter()/filtfilt() (and the zi/zf initial-condition forms exposed
5// through the register layer). Raw-buffer, core-free; the recurrence is
6// inherently sequential (each output feeds the next), so there is no SIMD
7// backend — the value of living in ops is dedup (one kernel for filter/filtfilt/
8// the register half) plus a single native codegen-lowering target for filter().
9//
10// b/a are expected a0-normalised by the caller (the Value-API divides by a(1)).
11
12#pragma once
13
14#include <nk/value/value.hpp> // nk::Complex
15#include <nk/value/scratch.hpp> // ScratchVec
16
17#include <cstddef>
18#include <memory_resource>
19
20namespace nk::ops {
21
22// Direct-form-II transposed IIR filter (real), applied to a flat input buffer.
23// Optional `zi` (length ziLen) seeds the initial delay state; when `zfOut` is
24// non-null the final state (length max(nb,na)-1) is written there — this backs
25// MATLAB's filter(b,a,x,zi) and [y,zf] = filter(...).
26ScratchVec<double> applyFilterDf2t(const double *bn, std::size_t nb, const double *an,
27 std::size_t na, const double *input, std::size_t len,
28 std::pmr::memory_resource *mr,
29 const double *zi = nullptr, std::size_t ziLen = 0,
30 double *zfOut = nullptr);
31
32// Complex DF2T variant. filter() is bilinear (the recursive a-part mixes
33// terms), so a real/imag split does not apply — the recurrence runs over
34// Complex.
35ScratchVec<Complex> applyFilterDf2tComplex(const Complex *bn, std::size_t nb,
36 const Complex *an, std::size_t na,
37 const Complex *input, std::size_t len,
38 std::pmr::memory_resource *mr,
39 const Complex *zi = nullptr,
40 std::size_t ziLen = 0,
41 Complex *zfOut = nullptr);
42
43// ── Second-order section (biquad) DF2T ────────────────────────────────────
44// One biquad section applied in place to a single-channel signal, Direct-Form-II
45// transposed. b/a are pre-normalised so a0 = 1:
46// y[n] = b0*x[n] + s1; s1 = b1*x[n] - a1*y[n] + s2; s2 = b2*x[n] - a2*y[n].
47// The building block of an SOS cascade (signal sosfilt/sosfiltfilt). Like the
48// general DF2T above it is a sequential recurrence — no SIMD.
49void biquadDf2t(double b0, double b1, double b2, double a1, double a2,
50 const double *x, double *y, std::size_t n);
51
52// Same recurrence seeded with an explicit initial state (s1_init, s2_init) —
53// used by the forward/backward passes of sosfiltfilt.
54void biquadDf2tWithState(double b0, double b1, double b2, double a1, double a2,
55 const double *x, double *y, std::size_t n,
56 double s1_init, double s2_init);
57
58} // namespace nk::ops