1#ifndef CONSTFILT_ELLIPTIC_HPP
2#define CONSTFILT_ELLIPTIC_HPP
4#include "analog_filter.hpp"
5#include "vendor/consteig/consteig.hpp"
6#include "vendor/gcem_wrapper.hpp"
32template <
typename T, consteig::Size N,
typename Method = Tustin,
33 typename FilterType = LowPass>
34class Elliptic :
public AnalogFilter<T, N, Method>
36 static_assert(N >= 1u,
"Elliptic order must be at least 1");
39 constexpr Elliptic(T cutoff_hz, T ripple_db, T attenuation_db,
41 : AnalogFilter<T, N, Method>(
42 compute_continuous_tf(cutoff_hz, ripple_db, attenuation_db),
48 using Complex = consteig::Complex<T>;
51 static constexpr consteig::Size M{N / 2u};
55 static constexpr double LN10_OVER_10{0.23025850929940457};
58 static constexpr int AGM_ITERATIONS{64};
62 static constexpr int SERIES_TERMS{30};
64 static constexpr int NOME_COEFF_Q0_5{2};
65 static constexpr int NOME_COEFF_Q0_9{15};
66 static constexpr int NOME_COEFF_Q0_13{150};
68 static constexpr TransferFunction<T, N + 1u, N + 1u> compute_continuous_tf(
69 T cutoff_hz, T ripple_db, T attenuation_db)
71 const T wc =
static_cast<T
>(2) *
static_cast<T
>(GCEM_PI) * cutoff_hz;
72 TransferFunction<T, N + 1u, N + 1u> tf{};
73 elliptic_tf(wc, ripple_db, attenuation_db, tf.b, tf.a, FilterType{});
82 static constexpr T elliptic_K(T k)
84 T a =
static_cast<T
>(1);
85 T b = gcem::sqrt(
static_cast<T
>(1) - k * k);
86 for (
int i = 0; i < AGM_ITERATIONS; ++i)
88 const T a2 = (a + b) /
static_cast<T
>(2);
89 const T b2 = gcem::sqrt(a * b);
93 return static_cast<T
>(GCEM_PI) / (
static_cast<T
>(2) * a);
97 static constexpr T from_db10(T x)
99 return gcem::exp(x *
static_cast<T
>(LN10_OVER_10));
105 static constexpr T compute_nome(T k)
107 const T kp = gcem::sqrt(
static_cast<T
>(1) - k * k);
108 const T sqrt_kp = gcem::sqrt(kp);
109 const T q0 =
static_cast<T
>(0.5) * (
static_cast<T
>(1) - sqrt_kp) /
110 (
static_cast<T
>(1) + sqrt_kp);
111 const T q0_2 = q0 * q0;
112 const T q0_4 = q0_2 * q0_2;
113 const T q0_5 = q0_4 * q0;
114 const T q0_9 = q0_5 * q0_4;
115 const T q0_13 = q0_9 * q0_4;
116 return q0 +
static_cast<T
>(NOME_COEFF_Q0_5) * q0_5 +
117 static_cast<T
>(NOME_COEFF_Q0_9) * q0_9 +
118 static_cast<T
>(NOME_COEFF_Q0_13) * q0_13;
131 static constexpr T modulus_from_nome(T q)
133 const T q14 = gcem::sqrt(gcem::sqrt(q));
139 T theta2 =
static_cast<T
>(0);
140 T qpow =
static_cast<T
>(1);
141 T q_2n =
static_cast<T
>(1);
142 for (
int n = 0; n <= SERIES_TERMS; ++n)
151 theta2 *=
static_cast<T
>(2) * q14;
154 T theta3 =
static_cast<T
>(1);
157 for (
int n = 1; n <= SERIES_TERMS; ++n)
164 theta3 +=
static_cast<T
>(2) * qpow3;
167 const T ratio = theta2 / theta3;
168 return ratio * ratio;
177 static constexpr T compute_sig0(T ripple_db, T q)
179 const T gain = from_db10(ripple_db /
static_cast<T
>(2));
191 gcem::log((gain +
static_cast<T
>(1)) / (gain -
static_cast<T
>(1))) /
192 (
static_cast<T
>(2) *
static_cast<T
>(N));
197 T sig01 =
static_cast<T
>(0);
198 T qpow1 =
static_cast<T
>(1);
199 T q_2m =
static_cast<T
>(1);
200 for (
int m = 0; m <= SERIES_TERMS; ++m)
208 (m % 2 == 0) ?
static_cast<T
>(1) :
static_cast<T
>(-1);
209 const T x =
static_cast<T
>(2 * m + 1) * l;
210 sig01 += sign * qpow1 * gcem::sinh(x);
214 T sig02 =
static_cast<T
>(0);
217 for (
int m = 1; m <= SERIES_TERMS; ++m)
225 (m % 2 == 0) ?
static_cast<T
>(1) :
static_cast<T
>(-1);
226 const T x =
static_cast<T
>(2 * m) * l;
227 sig02 += sign * qpow2 * gcem::cosh(x);
230 const T q14 = gcem::sqrt(gcem::sqrt(q));
232 const T sig0 =
static_cast<T
>(2) * q14 * sig01 /
233 (
static_cast<T
>(1) +
static_cast<T
>(2) * sig02);
235 return (sig0 <
static_cast<T
>(0)) ? -sig0 : sig0;
244 static constexpr T compute_wi(consteig::Size ii, T q)
246 const T mu = (N % 2u == 1u) ?
static_cast<T
>(ii)
247 :
static_cast<T
>(ii) -
static_cast<T
>(0.5);
248 const T q14 = gcem::sqrt(gcem::sqrt(q));
250 const T pi_mu_n =
static_cast<T
>(GCEM_PI) * mu /
static_cast<T
>(N);
253 T soma1 =
static_cast<T
>(0);
254 T qpow1 =
static_cast<T
>(1);
255 T q_2m =
static_cast<T
>(1);
256 for (
int m = 0; m <= SERIES_TERMS; ++m)
264 (m % 2 == 0) ?
static_cast<T
>(1) :
static_cast<T
>(-1);
265 const T arg =
static_cast<T
>(2 * m + 1) * pi_mu_n;
266 soma1 += sign * qpow1 * gcem::sin(arg);
268 soma1 *=
static_cast<T
>(2) * q14;
271 T soma2 =
static_cast<T
>(0);
274 for (
int m = 1; m <= SERIES_TERMS; ++m)
282 (m % 2 == 0) ?
static_cast<T
>(1) :
static_cast<T
>(-1);
283 const T arg =
static_cast<T
>(2 * m) * pi_mu_n;
284 soma2 += sign * qpow2 * gcem::cos(arg);
286 soma2 *=
static_cast<T
>(2);
288 return soma1 / (
static_cast<T
>(1) + soma2);
295 static constexpr void poly_mul_root(Complex (&poly)[N + 1u],
296 consteig::Size deg, Complex root)
298 for (consteig::Size j = deg; j > 0u; --j)
300 poly[j] = poly[j - 1u] - root * poly[j];
303 Complex{
static_cast<T
>(0),
static_cast<T
>(0)} - root * poly[0];
320 static constexpr void elliptic_tf(T wc, T ripple_db, T attenuation_db,
321 T (&b)[N + 1u], T (&a)[N + 1u], LowPass)
325 const T ep = gcem::sqrt(from_db10(ripple_db) -
static_cast<T
>(1));
326 const T es = gcem::sqrt(from_db10(attenuation_db) -
static_cast<T
>(1));
327 const T k1 = ep / es;
334 const T q1 = compute_nome(k1);
335 const T q = gcem::exp(gcem::log(q1) /
static_cast<T
>(N));
336 const T k = modulus_from_nome(q);
344 const T sig0 = compute_sig0(ripple_db, q);
357 const T ws =
static_cast<T
>(1) / k;
358 const T sqrt_ws = gcem::sqrt(ws);
359 const T w = gcem::sqrt((
static_cast<T
>(1) + k * sig0 * sig0) *
360 (
static_cast<T
>(1) + sig0 * sig0 / k));
362 Complex poly_a[N + 1u]{};
363 Complex poly_b[N + 1u]{};
364 poly_a[0] = Complex{
static_cast<T
>(1),
static_cast<T
>(0)};
365 poly_b[0] = Complex{
static_cast<T
>(1),
static_cast<T
>(0)};
367 consteig::Size deg_a = 0u;
368 consteig::Size deg_b = 0u;
370 for (consteig::Size ii = 1u; ii <= M; ++ii)
372 const T wi = compute_wi(ii, q);
373 const T Vi = gcem::sqrt((
static_cast<T
>(1) - k * wi * wi) *
374 (
static_cast<T
>(1) - wi * wi / k));
376 const T omega_z = sqrt_ws / wi;
378 poly_mul_root(poly_b, deg_b, Complex{
static_cast<T
>(0), omega_z});
380 poly_mul_root(poly_b, deg_b, Complex{
static_cast<T
>(0), -omega_z});
382 const T denom =
static_cast<T
>(1) + sig0 * sig0 * wi * wi;
383 const T p_re = sqrt_ws * (-sig0 * Vi) / denom;
384 const T p_im = sqrt_ws * (wi * w) / denom;
387 poly_mul_root(poly_a, deg_a, Complex{p_re, p_im});
389 poly_mul_root(poly_a, deg_a, Complex{p_re, -p_im});
395 poly_mul_root(poly_a, deg_a,
396 Complex{-sig0 * sqrt_ws,
static_cast<T
>(0)});
400 for (consteig::Size i = 0u; i <= N; ++i)
402 a[i] = poly_a[N - i].real;
403 b[i] = poly_b[N - i].real;
410 static_cast<T
>(1) / gcem::sqrt(
static_cast<T
>(1) + ep * ep);
411 const T H0 = (N % 2u == 1u) ?
static_cast<T
>(1) : Gp;
412 const T gain = H0 * a[N] / b[N];
413 for (consteig::Size i = 0u; i <= N; ++i)
421 for (consteig::Size i = 0u; i <= N; ++i)
423 const T sc = gcem::pow(wc,
static_cast<int>(i));
434 static constexpr void elliptic_tf(T wc, T ripple_db, T attenuation_db,
435 T (&b)[N + 1u], T (&a)[N + 1u], HighPass)
439 elliptic_tf(
static_cast<T
>(1), ripple_db, attenuation_db, b_lp, a_lp,
442 for (consteig::Size j = 0u; j <= N; ++j)
444 const T sc = gcem::pow(wc,
static_cast<int>(j));
445 a[j] = a_lp[N - j] * sc;
446 b[j] = b_lp[N - j] * sc;