268 lines
6.2 KiB
C++
268 lines
6.2 KiB
C++
/*
|
|
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 <cassert>
|
|
#include <cmath>
|
|
|
|
namespace hiir
|
|
{
|
|
static const double PI = 3.141592653589793238;
|
|
|
|
int round_int (double x)
|
|
{
|
|
return (static_cast <int> (std::floor (x + 0.5)));
|
|
}
|
|
|
|
int ceil_int (double x)
|
|
{
|
|
return (static_cast <int> (std::ceil (x)));
|
|
}
|
|
|
|
template <class T>
|
|
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
|
|
|