16
16
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17
17
*/
18
18
19
- #include <complex .h>
19
+ #include <float .h>
20
20
#include <math.h>
21
21
#include "cap5.h"
22
22
@@ -35,7 +35,7 @@ void cap5_reset(struct cap5_state *state)
35
35
void cap5_butterworth_ap (double complex ap [3 ])
36
36
{
37
37
for (int i = 0 ; i < 3 ; ++ i ) {
38
- const double theta = (2 * i + 1 )* M_PI /10.0 ;
38
+ const double theta = (2 * i + 1 )* M_PI /( 2.0 * 5 ) ;
39
39
ap [i ] = - sin (theta ) + cos (theta )* I ; /* normalized pole in s-plane */
40
40
}
41
41
}
@@ -47,21 +47,149 @@ void cap5_chebyshev_ap(int gen_type2, double stop_dB, double complex ap[3])
47
47
return ;
48
48
}
49
49
const double epsilon = sqrt (pow (10.0 , stop_dB /10.0 ) - 1.0 );
50
- const double sigma = asinh (epsilon )/5.0 ;
50
+ const double sigma = asinh (epsilon )/5 ;
51
+ const double scale = cosh (acosh (epsilon )/5 );
51
52
for (int i = 0 ; i < 3 ; ++ i ) {
52
- const double theta = (2 * i + 1 )* M_PI /10.0 ;
53
+ const double theta = (2 * i + 1 )* M_PI /( 2.0 * 5 ) ;
53
54
ap [i ] = - sinh (sigma )* sin (theta ) + cosh (sigma )* cos (theta )* I ; /* normalized pole in s-plane */
54
- ap [i ] = ap [i ] / cosh ( acosh ( epsilon )/ 5.0 ) ; /* scale so H(1) = sqrt(0.5) */
55
+ ap [i ] = ap [i ] / scale ; /* scale so H(1) = sqrt(0.5) */
55
56
if (gen_type2 ) ap [i ] = 1.0 /ap [i ];
56
57
}
57
58
}
58
59
59
- void cap5_elliptic_ap (double complex ap [ 3 ] )
60
+ static inline int fz_sgn (double x )
60
61
{
61
- /* 35dB stopband attenuation for low pass; 45dB for high pass */
62
- ap [0 ] = -0.185287191997037 + 0.990129317340409 * I ;
63
- ap [1 ] = -0.686015538373767 + 0.810354587786414 * I ;
64
- ap [2 ] = -1.118174003343493 ;
62
+ if (x < 0.0 ) return -1 ;
63
+ else if (x > 0.0 ) return 1 ;
64
+ return 0 ;
65
+ }
66
+
67
+ #define FIND_ZERO_MAX_ITER 100
68
+ static double find_zero (double (* fn )(double , const void * ), const void * arg , double a , double b , double tol )
69
+ {
70
+ double c = a , fn_a = fn (a , arg ), fn_b = fn (b , arg );
71
+ if (tol < DBL_EPSILON ) tol = DBL_EPSILON * 2 ;
72
+ for (int i = 0 , side = 0 ; i < FIND_ZERO_MAX_ITER ; ++ i ) {
73
+ c = (fn_a * b - fn_b * a ) / (fn_a - fn_b );
74
+ if (fabs (b - a ) < tol * fabs (b + a )) return c ;
75
+ const double fn_c = fn (c , arg );
76
+ if (fz_sgn (fn_b ) == fz_sgn (fn_c )) {
77
+ b = c ; fn_b = fn_c ;
78
+ if (side == -1 ) fn_a /= 2.0 ;
79
+ side = -1 ;
80
+ }
81
+ else if (fz_sgn (fn_a ) == fz_sgn (fn_c )) {
82
+ a = c ; fn_a = fn_c ;
83
+ if (side == 1 ) fn_b /= 2.0 ;
84
+ side = 1 ;
85
+ }
86
+ else {
87
+ if (i == 0 ) return - NAN ; /* no zero within interval [a, b] */
88
+ return c ;
89
+ }
90
+ }
91
+ return - NAN ; /* failed to converge */
92
+ }
93
+
94
+ static double ellip_q_err (double k , const void * arg )
95
+ {
96
+ const double target_q = * ((double * ) arg );
97
+ //LOG_FMT(LL_VERBOSE, "%s(): k=%.15e; target_q=%.15e", __func__, k, target_q);
98
+ const double kp = sqrt (sqrt (1.0 - k * k ));
99
+ const double l = (1.0 - kp )/((1.0 + kp )* 2.0 );
100
+ return (l + 2.0 * pow (l , 5 ) + 15.0 * pow (l , 9 ) + 150.0 * pow (l , 13 )) - target_q ;
101
+ }
102
+
103
+ /* Evaluate allpass given by poles ap at jw (=j*w) */
104
+ static double complex eval_allpass_ap (const double complex * ap , int n , double complex jw )
105
+ {
106
+ const int has_real = (cimag (ap [n - 1 ]) == 0 ); /* real root is always last */
107
+ double complex num = (has_real ) ? jw + ap [n - 1 ] : 1.0 ;
108
+ double complex den = (has_real ) ? jw - ap [n - 1 ] : 1.0 ;
109
+ for (int i = 0 , np = (has_real )?n - 1 :n ; i < np ; ++ i ) {
110
+ num *= (jw + ap [i ]) * (jw + conj (ap [i ])); /* conjugates not stored */
111
+ den *= (jw - ap [i ]) * (jw - conj (ap [i ]));
112
+ }
113
+ return num / den ;
114
+ }
115
+
116
+ /* Dot product treating a and b as vectors */
117
+ static inline double geom_dot (double complex a , double complex b )
118
+ {
119
+ return creal (a )* creal (b ) + cimag (a )* cimag (b );
120
+ }
121
+
122
+ struct ellip_wc_err_arg {
123
+ const double complex * ap0 , * ap1 ;
124
+ int n0 , n1 ;
125
+ };
126
+
127
+ static double ellip_wc_err (double w , const void * arg )
128
+ {
129
+ const double complex jw = w * I ;
130
+ struct ellip_wc_err_arg * a = (struct ellip_wc_err_arg * ) arg ;
131
+ //LOG_FMT(LL_VERBOSE, "%s(): w=%.15e", __func__, w);
132
+ return geom_dot (eval_allpass_ap (a -> ap0 , a -> n0 , jw ), eval_allpass_ap (a -> ap1 , a -> n1 , jw ));
133
+ }
134
+
135
+ void cap5_elliptic_ap (double stop_dB_lp , double stop_dB_hp , double complex ap [3 ])
136
+ {
137
+ if (stop_dB_lp > 100.0 ) {
138
+ cap5_chebyshev_ap (0 , stop_dB_hp , ap );
139
+ return ;
140
+ }
141
+ else if (stop_dB_hp > 100.0 ) {
142
+ cap5_chebyshev_ap (1 , stop_dB_lp , ap );
143
+ return ;
144
+ }
145
+
146
+ const double e2 = 1.0 / (pow (10.0 , stop_dB_hp /10.0 ) - 1.0 );
147
+ const double D = (pow (10.0 , stop_dB_lp /10.0 ) - 1.0 ) / e2 ;
148
+ const double q = 1.0 / (exp2 (4.0 /5 ) * pow (D , 1.0 /5 ));
149
+ const double k = find_zero (ellip_q_err , & q , 0.0 , 1.0 , 0 );
150
+ if (!isnormal (k )) goto fail_fz ;
151
+
152
+ const double L = log ((sqrt (1.0 + e2 )+ 1.0 ) / (sqrt (1.0 + e2 )- 1.0 )) / (2.0 * 5 );
153
+ double sigma0_s0 = sinh (L ), sigma0_s1 = 0.0 ;
154
+ for (int m = 1 ; m < 6 ; ++ m ) {
155
+ const int sgn = (m & 1 )?-1 :1 ;
156
+ sigma0_s0 += sgn * pow (q , m * (m + 1 )) * sinh ((2 * m + 1 )* L );
157
+ sigma0_s1 += sgn * pow (q , m * m ) * cosh (2 * m * L );
158
+ }
159
+ const double sigma0 = fabs ((2.0 * sqrt (sqrt (q ))* sigma0_s0 ) / (1.0 + 2.0 * sigma0_s1 ));
160
+ const double sigma02 = sigma0 * sigma0 ;
161
+
162
+ const double W = sqrt ((1.0 + k * sigma02 ) * (1.0 + sigma02 /k ));
163
+ for (int i = 0 ; i < 2 ; ++ i ) {
164
+ const double mu = 2.0 - i ;
165
+ double omega_s0 = sin (M_PI * mu /5 ), omega_s1 = 0.0 ;
166
+ for (int m = 1 ; m < 6 ; ++ m ) {
167
+ const int sgn = (m & 1 )?-1 :1 ;
168
+ omega_s0 += sgn * pow (q , m * (m + 1 )) * sin ((2 * m + 1 )* M_PI * mu /5 );
169
+ omega_s1 += sgn * pow (q , m * m ) * cos (2 * m * M_PI * mu /5 );
170
+ }
171
+ const double omega = (2.0 * sqrt (sqrt (q ))* omega_s0 ) / (1.0 + 2.0 * omega_s1 );
172
+ const double omega2 = omega * omega ;
173
+ const double Vi = sqrt ((1.0 - k * omega2 )* (1.0 - omega2 /k ));
174
+ ap [i ] = (-2.0 * sigma0 * Vi + 2.0 * omega * W * I ) / (2.0 * (1.0 + sigma02 * omega2 )); /* complex poles (conjugates not stored) */
175
+ }
176
+ ap [2 ] = - sigma0 ; /* real pole */
177
+
178
+ if (fabs (stop_dB_lp - stop_dB_hp ) > 0.01 ) {
179
+ /* scale so that magnitude at w=1 is sqrt(0.5) */
180
+ const double complex ap0 [1 ] = { ap [1 ] };
181
+ const double complex ap1 [2 ] = { ap [0 ], ap [2 ] };
182
+ const struct ellip_wc_err_arg err_arg = { .ap0 = ap0 , .ap1 = ap1 , .n0 = 1 , .n1 = 2 };
183
+ const double half_width = sqrt (1.0 /k );
184
+ const double wc = find_zero (ellip_wc_err , & err_arg , 1.0 /half_width , half_width , 0 );
185
+ if (!isnormal (wc )) goto fail_fz ;
186
+ for (int i = 0 ; i < 3 ; ++ i ) ap [i ] /= wc ;
187
+ }
188
+ return ;
189
+
190
+ fail_fz :
191
+ LOG_FMT (LL_ERROR , "%s(): BUG: failed to converge; falling back to butterworth" , __func__ );
192
+ cap5_butterworth_ap (ap );
65
193
}
66
194
67
195
void cap5_init (struct cap5_state * state , double fs , double fc , const double complex ap [3 ])
0 commit comments