tomlankhorst/control
biquad.h
1 /*
2  * Bi-quadratic filters
3 */
4 
5 #pragma once
6 
7 #include <array>
8 #include <complex>
9 #include <tuple>
10 
11 #include "control/system/type.h"
12 
13 namespace control::filter {
14 
15 template<typename T = float>
16 using TC = std::complex<T>;
17 
18 template<typename T = float>
19 using TCS = std::pair<TC<T>, TC<T>>;
20 
37 template<typename T = float, typename S = const T>
38 class Biquad : public system::SISO<T> {
39  public:
49  Biquad(T b0, T b1, T b2, T a1, T a2) : B{b0, b1, b2}, A{a1, a2} {};
50 
61  Biquad(T b0, T b1, T b2, T a0, T a1, T a2) : B{b0 / a0, b1 / a0, b2 / a0}, A{a1 / a0, a2 / a0} {};
62 
70  Biquad(TCS<T> z, TCS<T> p, T k) {
71  static_assert(!std::is_const<S>::value, "Storage type S must be non-const to use ZPK.");
72  std::tie(B[0], B[1], B[2], A[0], A[1]) = zpk2coef(z, p, k);
73  };
74 
81  T step(T x) {
82  T y;
83 
84  /* Direct form II transposed */
85  y = x * B[0] + wz[0];
86  wz[0] = x * B[1] - A[0] * y + wz[1];
87  wz[1] = x * B[2] - A[1] * y;
88 
89  return y;
90  }
91 
100  TCS<T> poles() {
101  return solve((T) 1, A[0], A[1]);
102  }
103 
110  TCS<T> zeros() {
111  return solve(B[0], B[1], B[2]);
112  }
113 
121  bool stable() {
122  auto p = poles();
123 
124  // Whether the magnitude of both poles is leq unity
125  return abs(std::get<0>(p)) <= (T) 1
126  && abs(std::get<1>(p)) <= (T) 1;
127  }
128 
132  void reset() {
133  wz[0] = 0;
134  wz[1] = 0;
135  }
136 
137  protected:
138 
143  T wz[2] = {0, 0};
144 
149  S B[3];
150 
155  S A[2];
156 
160  std::tuple<T, T, T, T, T> zpk2coef(TCS<T> z, TCS<T> p, T k) {
161  auto b = zero2coef(z);
162  auto a = zero2coef(p);
163  return std::make_tuple(
164  k,
165  k * std::get<0>(b),
166  k * std::get<1>(b),
167  std::get<0>(a),
168  std::get<1>(a)
169  );
170  }
171 
182  std::tuple<T, T> zero2coef(TCS<T> z) {
183  auto z1 = std::get<0>(z);
184  auto z2 = std::get<1>(z);
185 
186  auto z1r = z1.real();
187  auto z1i = z1.imag();
188  auto z2r = z2.real();
189 
190  // Assume this is a complex conjugate pair if p1 has imag part
191  return z1i
192  ? std::make_tuple(-2 * z1r, z1r * z1r + z1i * z1i)
193  : std::make_tuple(-z1r - z2r, z1r * z2r);
194  };
195 
206  TCS<T> solve(T a, T b, T c) {
207  // Normalize
208  b /= a;
209  c /= a;
210 
211  // sqrt(b^2 - 4*a*c)
212  TC<T> ds = std::sqrt(TC<T>(b * b, 0) - 4 * c);
213 
214  // (-b±ds)/2a
215  return std::make_pair<TC<T>, TC<T>>((-b + ds) / (T) 2, (-b - ds) / (T) 2);
216  }
217 
218 };
219 
224 template<typename T>
226  typedef T arithmetic_type;
227  typedef T storage_type;
228 };
229 
230 template<template<typename, typename> class B, typename T, typename S>
231 struct inspect_types<B<T, S>> {
232  typedef T arithmetic_type;
233  typedef S storage_type;
234 };
235 
244 template<typename B=Biquad<>, size_t N = 1>
245 class BiquadCascade : public system::SISO<typename inspect_types<B>::arithmetic_type> {
246  using T = typename inspect_types<B>::arithmetic_type;
247  using BS = std::array<B, N>;
248  public:
249 
255  template<typename... T>
256  BiquadCascade(T... bs) : bs{bs...} {}
257 
264  T step(T u) {
265  for (auto &b : bs)
266  u = b.step(u);
267 
268  return u;
269  }
270 
271  protected:
272 
276  BS bs;
277 };
278 
279 }
280 
Biquad(TCS< T > z, TCS< T > p, T k)
Definition: biquad.h:70
Definition: biquad.h:38
Definition: type.h:16
Definition: biquad.h:245
TCS< T > poles()
Definition: biquad.h:100
TCS< T > solve(T a, T b, T c)
Definition: biquad.h:206
BiquadCascade(T... bs)
Definition: biquad.h:256
Definition: biquad.h:13
BS bs
Definition: biquad.h:276
Definition: biquad.h:225
void reset()
Definition: biquad.h:132
T step(T u)
Definition: biquad.h:264
Biquad(T b0, T b1, T b2, T a1, T a2)
Definition: biquad.h:49
std::tuple< T, T, T, T, T > zpk2coef(TCS< T > z, TCS< T > p, T k)
Definition: biquad.h:160
bool stable()
Definition: biquad.h:121
TCS< T > zeros()
Definition: biquad.h:110
Biquad(T b0, T b1, T b2, T a0, T a1, T a2)
Definition: biquad.h:61
T step(T x)
Definition: biquad.h:81
std::tuple< T, T > zero2coef(TCS< T > z)
Definition: biquad.h:182