161 qWarning() <<
"IirFilter::designButterworth: order must be >= 1, got" << iOrder;
165 const double dC = 2.0 * dSFreq;
168 double dOmegaLow = dC * std::tan(PI * dCutoffLow / dSFreq);
170 ? dC * std::tan(PI * dCutoffHigh / dSFreq)
174 QVector<std::complex<double>> protoPoles = butterworthPrototypePoles(iOrder);
176 QVector<IirBiquad> sos;
186 for (
int k = 0; k < iOrder; ++k) {
187 std::complex<double> proto = protoPoles[k];
190 if (proto.imag() < -1e-10) {
194 if (std::abs(proto.imag()) < 1e-10) {
196 double analogPole = (type ==
LowPass)
197 ? dOmegaLow * proto.real()
198 : dOmegaLow / proto.real();
203 IirBiquad bq = realPoleToDigitalSection(analogPole, dC, 1.0);
206 double w_pb = (type ==
LowPass) ? 0.0 : PI;
207 double num = std::abs(bq.
b0 + bq.
b1 * std::cos(-w_pb));
208 double den = std::abs(1.0 + bq.
a1 * std::cos(-w_pb));
209 double gain = (den > 1e-12 && num > 1e-12) ? den / num : 1.0;
216 std::complex<double> analogPole;
220 analogPole = dOmegaLow * proto;
221 sectionGain = dOmegaLow * dOmegaLow;
224 analogPole = dOmegaLow / proto;
229 IirBiquad bq = poleToDigitalBiquad(analogPole, dC, sectionGain);
232 double hDC = (bq.
b0 + bq.
b1 + bq.
b2) / (1.0 + bq.
a1 + bq.
a2);
233 if (std::abs(hDC) > 1e-12) {
234 double scale = 1.0 / std::abs(hDC);
244 double omega0 = std::abs(analogPole);
245 double alpha = -analogPole.real();
246 double omega0sq = omega0 * omega0;
247 double d0 = dC * dC + 2.0 * alpha * dC + omega0sq;
250 bq.
b0 = dC * dC / d0;
251 bq.
b1 = -2.0 * dC * dC / d0;
252 bq.
b2 = dC * dC / d0;
253 bq.
a1 = 2.0 * (omega0sq - dC * dC) / d0;
254 bq.
a2 = (dC * dC - 2.0 * alpha * dC + omega0sq) / d0;
258 double hNy = (bq.
b0 - bq.
b1 + bq.
b2) / (1.0 - bq.
a1 + bq.
a2);
259 if (std::abs(hNy) > 1e-12) {
260 double scale = 1.0 / std::abs(hNy);
277 double dOmega0 = std::sqrt(dOmegaLow * dOmegaHigh);
278 double dBw = dOmegaHigh - dOmegaLow;
280 for (
int k = 0; k < iOrder; ++k) {
281 std::complex<double> proto = protoPoles[k];
286 std::complex<double> mid;
293 std::complex<double> discriminant = mid * mid - 4.0 * dOmega0 * dOmega0;
294 std::complex<double> sqrtDisc = std::sqrt(discriminant);
295 std::complex<double> pole1 = (mid + sqrtDisc) / 2.0;
296 std::complex<double> pole2 = (mid - sqrtDisc) / 2.0;
302 for (
auto& pole : {pole1, pole2}) {
307 double omega0 = std::abs(pole);
308 double alpha = -pole.real();
309 double omega0sq = omega0 * omega0;
310 double d0 = dC * dC + 2.0 * alpha * dC + omega0sq;
314 bq.
b0 = dBw * dC / d0;
316 bq.
b2 = -dBw * dC / d0;
317 bq.
a1 = 2.0 * (omega0sq - dC * dC) / d0;
318 bq.
a2 = (dC * dC - 2.0 * alpha * dC + omega0sq) / d0;
324 double omega0 = std::abs(pole);
325 double alpha = -pole.real();
326 double omega0sq = omega0 * omega0;
327 double dOmega0sq = dOmega0 * dOmega0;
328 double d0 = dC * dC + 2.0 * alpha * dC + omega0sq;
333 bq.
b0 = (dC * dC + dOmega0sq) / d0;
334 bq.
b1 = 2.0 * (dOmega0sq - dC * dC) / d0;
335 bq.
b2 = (dC * dC + dOmega0sq) / d0;
336 bq.
a1 = 2.0 * (omega0sq - dC * dC) / d0;
337 bq.
a2 = (dC * dC - 2.0 * alpha * dC + omega0sq) / d0;
346 double omegaCheck = (type ==
BandPass)
347 ? 2.0 * std::atan(dOmega0 / dC)
349 double totalGain = evalMagnitude(sos, omegaCheck);
350 if (totalGain > 1e-12) {
351 double scale = 1.0 / totalGain;
353 double perSection = std::pow(scale, 1.0 / sos.size());