Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scale FFT to dB/MHz #1211

Closed
wants to merge 1 commit into from
Closed

Conversation

willcode
Copy link
Contributor

FFT power was scaled using 1/(fftsize^2). This works out approximately for fftsize 1M, but was causing the power levels to shift at other sizes.

The plotter shows the maximum value of all those mapped to a pixel on the screen. This is useful, but does not represent the average power for the bins mapped to that pixel. The code was probably written to make the answer on the screen look correct in this situation, and show the noise floor averaging down at higher fftsize.

Separate modes to show the average power in a fft bin, and the minimum power, are the subjects of a separate commit.

FFT power was scaled using 1/(fftsize^2). This works out approximately
for fftsize 1M, but was causing the power levels to shift at other
sizes.

The plotter shows the maximum value of all those mapped to a pixel on
the screen. This is useful, but does not represent the average power
for the bins mapped to that pixel. The code was probably written to
make the answer on the screen look correct in this situation, and show
the noise floor averaging down at higher fftsize.

Separate modes to show the average power in a fft bin, and the minimum
power, are the subjects of a separate commit.

Signed-off-by: Jeff Long <willcode4@gmail.com>
@vladisslav2011
Copy link
Contributor

Hello.

but was causing the power levels to shift at other sizes.

It depends on the signal and window type:

4m
1m

128k
32k
8k

Different FFT sizes, but the level is the same.

See resolved discussion here: #1190

@willcode
Copy link
Contributor Author

willcode commented Mar 27, 2023

This is a separate bug, in addition to your window normalization, which was also necessary. Or is it actually the window scaling that makes the second fftsize divide unnecessary? In any case, I found the inconsistency because I was dumping the results of the fft and the average power over a frequency span changed with fftsize. Now, it stays the same. Having the gui show the same levels for each fftsize is a good goal, but given the max function used for fftsize > width, anything to make the screen look consistent is a hack. So, that's why I'm separately adding the ability to look at average bin for each pixel.

@vladisslav2011
Copy link
Contributor

vladisslav2011 commented Mar 27, 2023

This PR makes Gqrx show incorrect levels on the plotter.
And it does opposite thing to what is written in the description to me.

Master:

m1M
m64k
m8k
m1k

This PR:

w1M
w64k
w8k
w1k

There is a commit vladisslav2011@d466c7c that adds an option to switch between different windows normalization schemes, but @argilo decided to not add this functionality, so I have not opened a PR...

@willcode
Copy link
Contributor Author

willcode commented Mar 27, 2023

Yes, it changes the max(bin value for each pixel). But that's the way it's supposed to be. The problem is that max() is not always the right measurement. If you use avg(), the power levels stay the same. You might want to see max() to see the narrowest signals with a large fft, or you may want to see avg() to see average power levels.

My goal is to make Gqrx usable for measurements, or at least to the extent you can use a SDR for that.

@vladisslav2011
Copy link
Contributor

Hmmm. This PR makes Gqrx unusable for measurements to me (it shows completely wrong levels), so I'll revert it in my fork if it will get merged :-)

@willcode
Copy link
Contributor Author

willcode commented Mar 27, 2023

If it helps, here is the normalization code from GNU Radio

    if (normalize) {
        auto win = build(type, ntaps, param, false);
        const double pwr_acc = // sum(win**2) / len(win)
            std::accumulate(win.cbegin(),
                            win.cend(),
                            0.0,
                            [](const double a, const double b) { return a + b * b; }) /
            win.size();
        const float norm_fac = static_cast<float>(std::sqrt(pwr_acc));
        std::transform(win.begin(), win.end(), win.begin(), [norm_fac](const float tap) {
            return tap / norm_fac;
        });
        return win;
    }

Note the sqrt(). Is that what the Gqrx window normalization code does?

@vladisslav2011
Copy link
Contributor

I have seen this code.
These 2 lines vladisslav2011@d466c7c#diff-fcf4675691a139e7f1a9f0884be68dd18e1daa93b3da5517d039e0c6c935d852R94-R95 do the same thing.
See resolved discussion here: #1190

@willcode
Copy link
Contributor Author

If you're interested, try that change along wiht #1212 in Sampling=Average mode. That uses the actual average power over bins mapped to a pixel, and I think it works for measurement.

@vladisslav2011
Copy link
Contributor

I tried. It shows peak level of -40dbfs when the signal level is at -3dbfs. Increasing the frontend gain by 1..2db makes clipping products appear, so -3 dbfs is correct and -40dbfs is wrong.

I don't think that wasting CPU cycles to calculate wide FFT first and wasting CPU cycles again to calculate an average is a correct way to get average power per plot pixel... Just selecting flattop window and reducing FFT size to be close to the screen width does the same thing at much less cost.
On the other hand averaging gives a nice clean FFT plot with all narrow weak signals removed... I'll rebase it and test with IQ Tool "plot on seek on pause" feature to see if it will be useful to me.

@willcode
Copy link
Contributor Author

willcode commented Mar 27, 2023

Removing the 1000000.0 would fix that. Or sqrt(1000000). I don't have anything to calibrate with.

@willcode
Copy link
Contributor Author

Ugh, GNU Radio's Frequency Sink gives different amplitudes for different fft sizes. Now, I have to go figure out what's going on there too.

@willcode
Copy link
Contributor Author

I'll close this one - not really sure what the solution should be at the moment.

@willcode willcode closed this Mar 28, 2023
@willcode willcode deleted the fix/fft-scaling branch March 28, 2023 14:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants