65 static std::vector<Coefficients>
67 return cheb1_cascade(order, freq_hz, ripple_db, sample_rate,
false);
71 static std::vector<Coefficients>
73 return cheb1_cascade(order, freq_hz, ripple_db, sample_rate,
true);
86 static std::vector<Coefficients>
88 return cheb2_cascade(order, stop_freq_hz, stop_atten_db, sample_rate,
false);
92 static std::vector<Coefficients>
94 return cheb2_cascade(order, stop_freq_hz, stop_atten_db, sample_rate,
true);
122 static std::vector<Coefficients>
124 float passband_ripple_db,
float stopband_attn_db,
126 return elliptic_cascade(order, freq_hz, passband_ripple_db,
127 stopband_attn_db, sample_rate,
132 static std::vector<Coefficients>
134 float passband_ripple_db,
float stopband_attn_db,
136 return elliptic_cascade(order, freq_hz, passband_ripple_db,
137 stopband_attn_db, sample_rate,
142 static constexpr double kPi = 3.14159265358979323846;
150 static Coefficients bilinear_sos(
double B0,
double B1,
double B2,
151 double A0,
double A1,
double A2,
156 double a0_d = A0 * K2 + A1 * K + A2;
157 double a1_d = 2.0 * (A2 - A0 * K2);
158 double a2_d = A0 * K2 - A1 * K + A2;
159 double b0_d = B0 * K2 + B1 * K + B2;
160 double b1_d = 2.0 * (B2 - B0 * K2);
161 double b2_d = B0 * K2 - B1 * K + B2;
162 double inv = 1.0 / a0_d;
164 static_cast<float>(b0_d * inv),
165 static_cast<float>(b1_d * inv),
166 static_cast<float>(b2_d * inv),
167 static_cast<float>(a1_d * inv),
168 static_cast<float>(a2_d * inv),
172 static double prewarp_omega(
double freq_hz,
double sample_rate) {
173 double T = 1.0 / sample_rate;
174 return (2.0 / T) * std::tan(kPi * freq_hz / sample_rate);
184 static std::vector<std::complex<double>>
185 cheb1_prototype_poles(
int order,
double ripple_db) {
186 double eps = std::sqrt(std::pow(10.0, ripple_db / 10.0) - 1.0);
187 double mu = std::asinh(1.0 / eps) /
static_cast<double>(order);
188 double sinh_mu = std::sinh(mu);
189 double cosh_mu = std::cosh(mu);
191 std::vector<std::complex<double>> poles;
192 poles.reserve(
static_cast<size_t>(order));
193 for (
int k = 1;
k <= order; ++
k) {
194 double theta = kPi * (2.0 *
k - 1.0) / (2.0 * order);
195 poles.emplace_back(-sinh_mu * std::sin(theta),
196 cosh_mu * std::cos(theta));
201 static std::vector<Coefficients>
202 cheb1_cascade(
int order,
double freq_hz,
double ripple_db,
203 double sample_rate,
bool highpass) {
204 if (order < 2 || (order & 1) != 0)
return {};
205 if (freq_hz <= 0.0 || freq_hz >= 0.5 * sample_rate)
return {};
206 if (ripple_db <= 0.0)
return {};
208 double T = 1.0 / sample_rate;
209 double wc = prewarp_omega(freq_hz, sample_rate);
211 auto poles = cheb1_prototype_poles(order, ripple_db);
213 int num_sections = N / 2;
219 double eps = std::sqrt(std::pow(10.0, ripple_db / 10.0) - 1.0);
220 double target_dc = 1.0 / std::sqrt(1.0 + eps * eps);
224 std::sort(poles.begin(), poles.end(),
225 [](
const std::complex<double>&
a,
const std::complex<double>& b) {
226 return a.imag() < b.imag();
229 std::vector<Coefficients> result;
230 result.reserve(
static_cast<size_t>(num_sections));
232 for (
int sec = 0; sec < num_sections; ++sec) {
233 auto p = poles[
static_cast<size_t>(sec)];
236 double A1 = -2.0 *
p.real();
237 double A2 =
p.real() *
p.real() +
p.imag() *
p.imag();
239 double B0 = 0.0, B1 = 0.0, B2 = A2;
243 A1 *= wc; A2 *= wc * wc;
244 B1 *= wc; B2 *= wc * wc;
248 std::complex<double> p_hp = 1.0 / poles[
static_cast<size_t>(sec)];
250 A1 = -2.0 * p_hp.real() * wc;
251 A2 = (p_hp.real() * p_hp.real() + p_hp.imag() * p_hp.imag()) * wc * wc;
254 B0 = 1.0; B1 = 0.0; B2 = 0.0;
257 result.push_back(bilinear_sos(B0, B1, B2, A0, A1, A2, T));
263 if (!result.empty()) {
264 float scale =
static_cast<float>(target_dc);
265 result[0].b0 *=
scale;
266 result[0].b1 *=
scale;
267 result[0].b2 *=
scale;
280 static std::vector<Coefficients>
281 cheb2_cascade(
int order,
double stop_freq_hz,
double stop_atten_db,
282 double sample_rate,
bool highpass) {
283 if (order < 2 || (order & 1) != 0)
return {};
284 if (stop_freq_hz <= 0.0 || stop_freq_hz >= 0.5 * sample_rate)
return {};
285 if (stop_atten_db <= 0.0)
return {};
287 double T = 1.0 / sample_rate;
288 double ws = prewarp_omega(stop_freq_hz, sample_rate);
290 double eps = 1.0 / std::sqrt(std::pow(10.0, stop_atten_db / 10.0) - 1.0);
291 double mu = std::asinh(1.0 / eps) /
static_cast<double>(order);
292 double sinh_mu = std::sinh(mu);
293 double cosh_mu = std::cosh(mu);
296 int num_sections = N / 2;
298 std::vector<std::complex<double>> poles;
299 std::vector<std::complex<double>> zeros;
300 poles.reserve(
static_cast<size_t>(N));
301 zeros.reserve(
static_cast<size_t>(N));
303 for (
int k = 1;
k <= N; ++
k) {
304 double theta = kPi * (2.0 *
k - 1.0) / (2.0 * N);
305 std::complex<double> cheb_pole(-sinh_mu * std::sin(theta),
306 cosh_mu * std::cos(theta));
307 poles.push_back(1.0 / cheb_pole);
308 double c = std::cos(theta);
309 zeros.emplace_back(0.0, 1.0 /
c);
313 std::sort(poles.begin(), poles.end(),
314 [](
const std::complex<double>&
a,
const std::complex<double>& b) {
315 return a.imag() < b.imag();
317 std::sort(zeros.begin(), zeros.end(),
318 [](
const std::complex<double>&
a,
const std::complex<double>& b) {
319 return a.imag() < b.imag();
322 std::vector<Coefficients> result;
323 result.reserve(
static_cast<size_t>(num_sections));
325 for (
int sec = 0; sec < num_sections; ++sec) {
326 auto p = poles[
static_cast<size_t>(sec)];
327 auto z = zeros[
static_cast<size_t>(sec)];
329 double A1 = -2.0 *
p.real();
330 double A2 =
p.real() *
p.real() +
p.imag() *
p.imag();
334 double B2 =
z.imag() *
z.imag();
336 double scale_num = A2 / B2;
342 A1 *= ws; A2 *= ws * ws;
343 B1 *= ws; B2 *= ws * ws;
347 std::complex<double> p_hp_proto = 1.0 / poles[
static_cast<size_t>(sec)];
348 std::complex<double> z_hp_proto = 1.0 / zeros[
static_cast<size_t>(sec)];
350 A1 = -2.0 * p_hp_proto.real() * ws;
351 A2 = (p_hp_proto.real() * p_hp_proto.real() +
352 p_hp_proto.imag() * p_hp_proto.imag()) * ws * ws;
353 double zr = z_hp_proto.real();
354 double zi = z_hp_proto.imag();
357 B2 = (zr * zr + zi * zi) * ws * ws;
358 result.push_back(bilinear_sos(B0, B1, B2, A0, A1, A2, T));
362 result.push_back(bilinear_sos(B0, B1, B2, A0, A1, A2, T));
392 static double elliptic_solve_k(
int N,
double k1) {
393 if (k1 <= 0.0)
return 0.0;
394 if (k1 >= 1.0)
return 1.0 - 1
e-15;
397 double q = std::pow(q1, 1.0 /
static_cast<double>(N));
403 for (
int n = 0;
n < 30; ++
n) {
404 theta2 += std::pow(q, n * (n + 1));
406 theta2 *= 2.0 * std::pow(q, 0.25);
407 for (
int n = 1;
n < 30; ++
n) {
408 theta3 += 2.0 * std::pow(q,
static_cast<double>(n) * n);
410 double r = theta2 / theta3;
412 if (k <= 0.0)
k = 1
e-15;
413 if (k >= 1.0)
k = 1.0 - 1
e-15;
417 static std::vector<Coefficients>
418 elliptic_cascade(
int order,
double freq_hz,
419 double passband_ripple_db,
double stopband_attn_db,
420 double sample_rate,
bool highpass) {
421 if (order < 2 || (order & 1) != 0)
return {};
422 if (freq_hz <= 0.0 || freq_hz >= 0.5 * sample_rate)
return {};
423 if (passband_ripple_db <= 0.0)
return {};
424 if (stopband_attn_db <= passband_ripple_db)
return {};
426 double eps = std::sqrt(std::pow(10.0, passband_ripple_db / 10.0) - 1.0);
427 double As_lin = std::sqrt(std::pow(10.0, stopband_attn_db / 10.0) - 1.0);
428 double k1 = eps / As_lin;
431 if (!(k1 > 0.0 && k1 < 1.0))
return {};
433 double k = elliptic_solve_k(order, k1);
465 const double inv_eps = 1.0 / eps;
466 const double sn_arg = inv_eps / std::sqrt(1.0 + inv_eps * inv_eps);
467 const double m_prime = 1.0 - k1 * k1;
469 double v0 = (K *
r) / (
static_cast<double>(order) * K_k1);
491 double T = 1.0 / sample_rate;
492 double wc = prewarp_omega(freq_hz, sample_rate);
494 std::vector<Coefficients> result;
495 result.reserve(
static_cast<size_t>(L));
497 for (
int idx = 0; idx < L; ++idx) {
498 int jpos = 2 * idx + 1;
499 double u_k = jpos * K /
static_cast<double>(N);
501 double s_u, c_u, d_u;
505 double omega_z = 1.0 / (
k * std::abs(s_u));
508 double dsv = d_u * sv;
509 double inv = 1.0 / (1.0 - dsv * dsv);
510 double p_re = -(c_u * d_u * sv * cv) * inv;
511 double p_im = -(s_u * dv) * inv;
514 p_im = std::abs(p_im);
517 if (p_re >= 0.0) p_re = -std::abs(p_re);
522 double pa1_proto = -2.0 * p_re;
523 double pa2_proto = p_re * p_re + p_im * p_im;
524 double pb2_proto = omega_z * omega_z;
526 double A0, A1, A2, B0, B1, B2;
532 A2 = pa2_proto * wc * wc;
535 B2 = pb2_proto * wc * wc;
536 double scale_num = A2 / B2;
556 double pole_mag2 = pa2_proto;
558 A1 = (pa1_proto * wc) / pole_mag2;
559 A2 = (wc * wc) / pole_mag2;
562 B2 = (wc * wc) / pb2_proto;
565 result.push_back(bilinear_sos(B0, B1, B2, A0, A1, A2, T));
576 if (!result.empty()) {
577 double target = 1.0 / std::sqrt(1.0 + eps * eps);
578 float scale =
static_cast<float>(target);
579 result[0].b0 *=
scale;
580 result[0].b1 *=
scale;
581 result[0].b2 *=
scale;