/* PolyphaseIIR2Designer.cpp Copyright (c) 2005 Laurent de Soras --- Legal stuff --- This program is free software. It comes without any warranty, to the extent permitted by applicable law. You can redistribute it and/or modify it under the terms of the Do What The Fuck You Want To Public License, Version 2, as published by Sam Hocevar. See http://sam.zoy.org/wtfpl/COPYING for more details. */ #if defined (_MSC_VER) #pragma warning (1 : 4130) // "'operator' : logical operation on address of string constant" #pragma warning (1 : 4223) // "nonstandard extension used : non-lvalue array converted to pointer" #pragma warning (1 : 4705) // "statement has no effect" #pragma warning (1 : 4706) // "assignment within conditional expression" #pragma warning (4 : 4786) // "identifier was truncated to '255' characters in the debug information" #pragma warning (4 : 4800) // "forcing value to bool 'true' or 'false' (performance warning)" #pragma warning (4 : 4355) // "'this' : used in base member initializer list" #endif #include "PolyphaseIIR2Designer.h" #include #include namespace hiir { static const double PI = 3.141592653589793238; int round_int (double x) { return (static_cast (std::floor (x + 0.5))); } int ceil_int (double x) { return (static_cast (std::ceil (x))); } template T ipowp (T x, long n) { assert (n >= 0); T z (1); while (n != 0) { if ((n & 1) != 0) { z *= x; } n >>= 1; x *= x; } return (z); } int PolyphaseIIR2Designer::compute_nbr_coefs_from_proto (double attenuation, double transition) { assert (attenuation > 0); assert (transition > 0); assert (transition < 0.5); double k; double q; compute_transition_param (k, q, transition); const int order = compute_order (attenuation, q); const int nbr_coefs = (order - 1) / 2; return (nbr_coefs); } double PolyphaseIIR2Designer::compute_atten_from_order_tbw (int nbr_coefs, double transition) { assert (nbr_coefs > 0); assert (transition > 0); assert (transition < 0.5); double k; double q; compute_transition_param (k, q, transition); const int order = nbr_coefs * 2 + 1; const double attenuation = compute_atten (q, order); return (attenuation); } int PolyphaseIIR2Designer::compute_coefs (double coef_arr[], double attenuation, double transition) { assert (&coef_arr != 0); assert (attenuation > 0); assert (transition > 0); assert (transition < 0.5); double k; double q; compute_transition_param (k, q, transition); // Computes number of required coefficients const int order = compute_order (attenuation, q); const int nbr_coefs = (order - 1) / 2; // Coefficient calculation for (int index = 0; index < nbr_coefs; ++index) { coef_arr [index] = compute_coef (index, k, q, order); } return (nbr_coefs); } void PolyphaseIIR2Designer::compute_coefs_spec_order_tbw (double coef_arr[], int nbr_coefs, double transition) { assert (&coef_arr != 0); assert (nbr_coefs > 0); assert (transition > 0); assert (transition < 0.5); double k; double q; compute_transition_param (k, q, transition); const int order = nbr_coefs * 2 + 1; // Coefficient calculation for (int index = 0; index < nbr_coefs; ++index) { coef_arr [index] = compute_coef (index, k, q, order); } } void PolyphaseIIR2Designer::compute_transition_param (double &k, double &q, double transition) { assert (&k != 0); assert (&q != 0); assert (transition > 0); assert (transition < 0.5); using namespace std; k = tan ((1 - transition * 2) * hiir::PI / 4); k *= k; assert (k < 1); assert (k > 0); double kksqrt = pow (1 - k * k, 0.25); const double e = 0.5 * (1 - kksqrt) / (1 + kksqrt); const double e2 = e * e; const double e4 = e2 * e2; q = e * (1 + e4 * (2 + e4 * (15 + 150 * e4))); assert (q > 0); } int PolyphaseIIR2Designer::compute_order (double attenuation, double q) { assert (attenuation > 0); assert (q > 0); using namespace std; const double attn_p2 = pow (10.0, -attenuation / 10); const double a = attn_p2 / (1 - attn_p2); int order = hiir::ceil_int (log (a * a / 16) / log (q)); if ((order & 1) == 0) { ++ order; } if (order == 1) { order = 3; } return (order); } double PolyphaseIIR2Designer::compute_atten (double q, int order) { assert (q > 0); assert (order > 0); assert ((order & 1) == 1); using namespace std; const double a = 4 * exp (order * 0.5 * log (q)); assert (a != -1.0); const double attn_p2 = a / (1 + a); const double attenuation = -10 * log10 (attn_p2); assert (attenuation > 0); return (attenuation); } double PolyphaseIIR2Designer::compute_coef (int index, double k, double q, int order) { assert (index >= 0); assert (index * 2 < order); using namespace std; const int c = index + 1; const double num = compute_acc_num (q, order, c) * pow (q, 0.25); const double den = compute_acc_den (q, order, c) + 0.5; const double ww = num / den; const double wwsq = ww * ww; const double x = sqrt ((1 - wwsq * k) * (1 - wwsq / k)) / (1 + wwsq); const double coef = (1 - x) / (1 + x); return (coef); } double PolyphaseIIR2Designer::compute_acc_num (double q, int order, int c) { assert (c >= 1); assert (c < order * 2); using namespace std; int i = 0; int j = 1; double acc = 0; double q_ii1; do { q_ii1 = hiir::ipowp (q, i * (i + 1)); q_ii1 *= sin ((i * 2 + 1) * c * hiir::PI / order) * j; acc += q_ii1; j = -j; ++i; } while (fabs (q_ii1) > 1e-100); return (acc); } double PolyphaseIIR2Designer::compute_acc_den (double q, int order, int c) { assert (c >= 1); assert (c < order * 2); using namespace std; int i = 1; int j = -1; double acc = 0; double q_i2; do { q_i2 = hiir::ipowp (q, i * i); q_i2 *= cos (i * 2 * c * hiir::PI / order) * j; acc += q_i2; j = -j; ++i; } while (fabs (q_i2) > 1e-100); return (acc); } } // namespace hiir