182 qWarning() <<
"IirFilter::designButterworth: order must be >= 1, got" << iOrder;
186 const double dC = 2.0 * dSFreq;
189 double dOmegaLow = dC * std::tan(PI * dCutoffLow / dSFreq);
191 ? dC * std::tan(PI * dCutoffHigh / dSFreq)
195 QVector<std::complex<double>> protoPoles = butterworthPrototypePoles(iOrder);
197 QVector<IirBiquad> sos;
207 for (
int k = 0; k < iOrder; ++k) {
208 std::complex<double> proto = protoPoles[k];
211 if (proto.imag() < -1e-10) {
215 if (std::abs(proto.imag()) < 1e-10) {
217 double analogPole = (type ==
LowPass)
218 ? dOmegaLow * proto.real()
219 : dOmegaLow / proto.real();
224 IirBiquad bq = realPoleToDigitalSection(analogPole, dC, 1.0);
227 double w_pb = (type ==
LowPass) ? 0.0 : PI;
228 double num = std::abs(bq.
b0 + bq.
b1 * std::cos(-w_pb));
229 double den = std::abs(1.0 + bq.
a1 * std::cos(-w_pb));
230 double gain = (den > 1e-12 && num > 1e-12) ? den / num : 1.0;
237 std::complex<double> analogPole;
241 analogPole = dOmegaLow * proto;
242 sectionGain = dOmegaLow * dOmegaLow;
245 analogPole = dOmegaLow / proto;
250 IirBiquad bq = poleToDigitalBiquad(analogPole, dC, sectionGain);
253 double hDC = (bq.
b0 + bq.
b1 + bq.
b2) / (1.0 + bq.
a1 + bq.
a2);
254 if (std::abs(hDC) > 1e-12) {
255 double scale = 1.0 / std::abs(hDC);
256 bq.
b0 *= scale; bq.
b1 *= scale; bq.
b2 *= scale;
263 double omega0 = std::abs(analogPole);
264 double alpha = -analogPole.real();
265 double omega0sq = omega0 * omega0;
266 double d0 = dC * dC + 2.0 * alpha * dC + omega0sq;
269 bq.
b0 = dC * dC / d0;
270 bq.
b1 = -2.0 * dC * dC / d0;
271 bq.
b2 = dC * dC / d0;
272 bq.
a1 = 2.0 * (omega0sq - dC * dC) / d0;
273 bq.
a2 = (dC * dC - 2.0 * alpha * dC + omega0sq) / d0;
277 double hNy = (bq.
b0 - bq.
b1 + bq.
b2) / (1.0 - bq.
a1 + bq.
a2);
278 if (std::abs(hNy) > 1e-12) {
279 double scale = 1.0 / std::abs(hNy);
280 bq.
b0 *= scale; bq.
b1 *= scale; bq.
b2 *= scale;
294 double dOmega0 = std::sqrt(dOmegaLow * dOmegaHigh);
295 double dBw = dOmegaHigh - dOmegaLow;
297 for (
int k = 0; k < iOrder; ++k) {
298 std::complex<double> proto = protoPoles[k];
303 std::complex<double> mid;
310 std::complex<double> discriminant = mid * mid - 4.0 * dOmega0 * dOmega0;
311 std::complex<double> sqrtDisc = std::sqrt(discriminant);
312 std::complex<double> pole1 = (mid + sqrtDisc) / 2.0;
313 std::complex<double> pole2 = (mid - sqrtDisc) / 2.0;
319 for (
auto& pole : {pole1, pole2}) {
324 double omega0 = std::abs(pole);
325 double alpha = -pole.real();
326 double omega0sq = omega0 * omega0;
327 double d0 = dC * dC + 2.0 * alpha * dC + omega0sq;
331 bq.
b0 = dBw * dC / d0;
333 bq.
b2 = -dBw * dC / d0;
334 bq.
a1 = 2.0 * (omega0sq - dC * dC) / d0;
335 bq.
a2 = (dC * dC - 2.0 * alpha * dC + omega0sq) / d0;
341 double omega0 = std::abs(pole);
342 double alpha = -pole.real();
343 double omega0sq = omega0 * omega0;
344 double dOmega0sq = dOmega0 * dOmega0;
345 double d0 = dC * dC + 2.0 * alpha * dC + omega0sq;
350 bq.
b0 = (dC * dC + dOmega0sq) / d0;
351 bq.
b1 = 2.0 * (dOmega0sq - dC * dC) / d0;
352 bq.
b2 = (dC * dC + dOmega0sq) / d0;
353 bq.
a1 = 2.0 * (omega0sq - dC * dC) / d0;
354 bq.
a2 = (dC * dC - 2.0 * alpha * dC + omega0sq) / d0;
363 double omegaCheck = (type ==
BandPass)
364 ? 2.0 * std::atan(dOmega0 / dC)
366 double totalGain = evalMagnitude(sos, omegaCheck);
367 if (totalGain > 1e-12) {
368 double scale = 1.0 / totalGain;
370 double perSection = std::pow(scale, 1.0 / sos.size());