1#ifndef CONSTFILT_BUTTERWORTH_HPP
2#define CONSTFILT_BUTTERWORTH_HPP
4#include "analog_filter.hpp"
5#include "vendor/consteig/consteig.hpp"
6#include "vendor/gcem_wrapper.hpp"
24template <
typename T, consteig::Size N,
typename Method = Tustin,
25 typename FilterType = LowPass>
26class Butterworth :
public AnalogFilter<T, N, Method>
28 static_assert(N >= 1u,
"Butterworth order must be at least 1");
32 constexpr Butterworth(T cutoff_hz, T sample_rate_hz)
33 : AnalogFilter<T, N, Method>(compute_continuous_tf(cutoff_hz),
40 static constexpr TransferFunction<T, N + 1u, N + 1u> compute_continuous_tf(
43 const T wc =
static_cast<T
>(2) *
static_cast<T
>(GCEM_PI) * cutoff_hz;
44 TransferFunction<T, N + 1u, N + 1u> tf{};
45 continuous_tf(wc, tf.b, tf.a, FilterType{});
55 static constexpr void continuous_tf(T wc, T (&b)[N + 1u], T (&a)[N + 1u],
58 b[N] = gcem::pow(wc,
static_cast<int>(N));
61 butterworth_poly_coeffs(p);
62 for (consteig::Size k = 0; k <= N; ++k)
64 a[k] = p[k] * gcem::pow(wc,
static_cast<int>(k));
77 static constexpr void continuous_tf(T wc, T (&b)[N + 1u], T (&a)[N + 1u],
80 b[0] =
static_cast<T
>(1);
83 butterworth_poly_coeffs(p);
84 for (consteig::Size k = 0; k <= N; ++k)
86 a[k] = p[N - k] * gcem::pow(wc,
static_cast<int>(k));
98 static constexpr void butterworth_poly_coeffs(T (&result)[N + 1u])
100 using Complex = consteig::Complex<T>;
104 Complex poly[N + 1u]{};
105 poly[0] = Complex{
static_cast<T
>(1),
static_cast<T
>(0)};
108 for (consteig::Size k = 1u; k <= N; ++k)
110 const T theta =
static_cast<T
>(GCEM_PI) *
111 static_cast<T
>(2u * k + N - 1u) /
112 static_cast<T
>(2u * N);
113 const Complex pole{gcem::cos(theta), gcem::sin(theta)};
117 for (consteig::Size j = k; j > 0u; --j)
119 poly[j] = poly[j - 1u] - pole * poly[j];
122 Complex{
static_cast<T
>(0),
static_cast<T
>(0)} - pole * poly[0];
127 for (consteig::Size i = 0u; i <= N; ++i)
129 result[i] = poly[N - i].real;
135template <
typename T,
typename Method = Tustin>
136using FirstOrderLowPass = Butterworth<T, 1u, Method, LowPass>;
138template <
typename T,
typename Method = Tustin>
139using FirstOrderHighPass = Butterworth<T, 1u, Method, HighPass>;