ruohoruotsi / Butterworth-Filter-Design

C++ classes for designing high-order Butterworth IIR & equalization filters
GNU General Public License v3.0
163 stars 42 forks source link

Gain needs to be controlled #3

Open gauss256 opened 8 years ago

gauss256 commented 8 years ago

Hi, thanks for your very useful code. I found that the gain of the filter is unpredictable. I'd like the gain at the center frequency to be 1, i.e., no change between input and output. But in my code below, a gain gets applied that varies depending on the parameters chosen. How do I normalize things so that no gain gets applied?

// Design the filter
double overallGain = 1.0;
bool designedCorrectly = butterworth.bandPass(
    args.dSamplerate,               // sample rate (Hz)
    args.dLow,                      // lo cutoff (Hz)
    args.dHigh,                     // hi cutoff (Hz)
    args.nOrder,                    // filter order
    coeffs,                         // vector to hold coefficients
    overallGain);                   // gain (how to choose?)
if (!designedCorrectly) {
    throw std::runtime_error("Filter design failed");
}

// Implement the filter
int stages = (int) coeffs.size();
BiquadChain chain(stages);
chain.processBiquad(input, output, 1, (int)nItems, &coeffs[0]);
ruohoruotsi commented 8 years ago

Hi, thank you for your comment. Can you please post a full test, with all your parameters that are giving you unpredictable gain? I'll try to understand the issue and advise! Cheers!

gauss256 commented 8 years ago

My test file is a frequency sweep from 0 Hz to 20 kHz, peak normalized to -3 dB.

https://www.dropbox.com/s/fpzid8sz9avzse9/sweep_0_20K.wav?dl=0

I read it in and normalize the input values to be floats within the range [-1, 1]. Because of the peak normalization, the range is actually [-0.7, 0.7].

The results are shown below. The maxOutput is the maximum absolute value of the output after the filter is applied. If there was unity gain that maximum should be 0.7. If I scale the output by maxOutput and write out the file, it looks exactly as I would expect.

    fs      f1      f2      filterOrder overallGain maxOutput
    48000   80      10000    4          1                13.7
    48000   1000    10000    4          1                18.6
    48000   80      10000    6          1                59.7
    48000   5000    10000    8          1             22369.7
    48000   1000     5000   10          1           2030042.0
ruohoruotsi commented 8 years ago

Okay, this looks like a bug. Let me run your examples and see if I can't fix it, or at least localize the problem. Is it only with bandpass, or have you seen gain issues with other filter types?

gauss256 commented 8 years ago

I've only tried bandpass so far.

gauss256 commented 8 years ago

I did some quick tests with loPass and hiPass, and see similar problems. The gain applied in the loPass case is large (>10^3 in the examples I tried) but is more modest for hiPass (<10^2).

ruohoruotsi commented 8 years ago

okay, I'm looking through my test cases and the gain calculations and there's this logic that I need to refresh on in https://github.com/ruohoruotsi/Butterworth-Filter-Design/blob/master/Butterworth.cpp.

Tonight, I'll run over your test file to reproduce the issue. My test-cases check against the filter results generated from MATLAB, so its possible there is some assumption that in practise does not work. I'll update the issue soon with my findings! Thanks for filing this!

    // correct the overall gain
    if(filter == kLoPass || filter == kBandPass){ // pre-blt is okay for S-plane
        ba[0] = preBLTgain * (preBLTgain / gain); // 2nd term is how much BLT boosts,
    }
    else if(filter == kHiPass || kBandStop){ // HF gain != DC gain
        ba[0] = 1 / ba[0];
    }
oukradation commented 6 years ago

Hi, great code. I am using your code in my ecg project.

However, the gain issue is still consistent after your latest commit. It behaves almost same as @gauss256 describes.

I am a beginner in c++, so don't take my word seriously but I wonder if it is overflowing issue due to different compiler, if you can't regenerate the same problem? I am using your code in Qt 5.10 with gcc 64bit.

guo-max commented 2 years ago

Hi, Thanks for your code. It is very useful. I had the same problem about the gain and here is my fix. I just multiply the input with ba[0] and everything looks fine. Only tested with lowpass filter.