FFT measurements, DC bin and Power - Page 1
Forum section: electronics / general

author: radiolistener
author_rank: Super Contributor
timestamp: March 29, 2023, 05:59:29 pm
content: I needs to implement some measurements on ADC sampled data with FFT (such as dBm power in selected bandwidth, etc), and I stuck with one question.Let's assume that load R=1. So, the power of a sine waveform signal with a peak amplitude A can be calculated as:Power = A^2 / 2but at the same time power of DC signal can be calculated as:Power = A^2This is because DC signal is constant, so it has constant power and don't needs to be averaged over full cycle with positive and negative part of the waveform.I'm not sure, may be I'm wrong, but at a glance it seems that a magnitude of DC bin in the FFT result needs to be corrected in order to get proper power value, or at least it needs to be handled in a different way than other FFT bins. I think that such correction is required because FFT DC bin represents constant signal and other FFT bins represents sine carriers.I asked chat-gpt and he said that I'm correct and DC bin of FFT needs to be corrected for a proper power measurements. But strange thing that I didn't hear about such correction before, so I'm not sure if such correction is relevant for FFT spectrum measurements.So, I wonder when such correction is needed and when it is not for a spectrum measurements with FFT? What is the best practice?To be sure, I'm talking about signal which is represented according to AES17 standard meaning, where signal is symmetric around zero value and minimum negative code is not used. So, full scale sine has 0 dBFS level and full scale square wave has +3.01 dBFS level.And now I'm not sure what is the level of DC signal with maximum positive amplitude? And what is the power of such DC signal?

author: gf
author_rank: Super Contributor
timestamp: March 29, 2023, 06:09:20 pm
content: Consider that there is only one bin for DC and one for Nyquist, but the power of other frequencies is split into positive and negative frequency bins.EDIT: The sum of the power of the time domain samples is the same as the sum of the power of all (positive and negative!) frequency bins.Example:Code:>> N=100;>> signal=rand(1,N); % a random real signal>> spectrum=fft(signal); % corresponding (complex) FFT spectrum% power sum of time domain samples>> sum(signal.**2)ans = 32.211% power sum of spectrum>> sum(spectrum.*conj(spectrum))/Nans = 32.211% power sum of spectrum (alternative calculation method)>> sum(abs(spectrum).**2)/Nans = 32.211

author: MasterT
author_rank: Frequent Contributor
timestamp: March 29, 2023, 10:03:40 pm
content: Quote from: radiolistener on March 29, 2023, 05:59:29 pmYou confused A , for AC signal A is peak voltage this is why division by 2 comes. Shoud be RMS, than in both cases DC or AC formula is the same P = A^2P = Ap-p ^2 / 2 = (A-rms * sqrt(2)) ^2 / 2 = A-rms ^2.Correction is not required if all magnitudes inside bines are considered as RMS values.

author: gf
author_rank: Super Contributor
timestamp: March 29, 2023, 11:01:56 pm
content: Quote from: radiolistener on March 29, 2023, 05:59:29 pmIf you discard the negative frequency bins and consider only the positive ones, then you need to double the power of each bin (in order to account for the power in the discarded negative frequency counterparts), but don't double the power of the DC and the Nyquist bins (which exist only once and do not have negative frequency counterparts).

author: radiolistener
author_rank: Super Contributor
timestamp: March 30, 2023, 05:39:38 pm
content: Here is MATLAB test code, I added 3 test vectors - sine wave, square wave and DC (two of them are commented):Code:Fs = 128;N = 128;t = 0:1/Fs:(N-1)/Fs;signal = sin(2*pi*2*t); % sine wave%signal = cat(2, ones(1, N/2), -ones(1, N/2)); % square wave%signal = ones(1, N) * 1; % DC wavespectr = fft(signal);psignal = sum(signal.^2)/N;pspectr = sum(abs(spectr/N).^2);fprintf('psignal = %f = %+.2f dBFS\n', psignal, 20*log10(psignal));fprintf('pspectr = %f = %+.2f dBFS\n', pspectr, 20*log10(pspectr));the output for sine wave is:Code:psignal = 0.500000 = -6.02 dBFSpspectr = 0.500000 = -6.02 dBFSAs we see power in time and freq domain are the same which is ok.But according to AES17 standard, it should be 0 dBFS for sine wave with max amplitude.How to fix calcualtions to get correct results for AES17 standard?If assume that 0.5 is a power of a full scale sine wave, we can try to divide power with 0.5 to get level relative to a full scale sine:Code:psignal = (sum(signal.^2)/N) / 0.5;pspectr = (sum(abs(spectr/N).^2)) / 0.5;For sine wave it shows 0 dBFS as expected. For square wave and DC it shows the same value +6.02 dBFS. So it looks that there is no need to use correction for DC bin.But as I remember AES17 standard mention that square wave with max amplitude has +3.01 dBFS level:Quoteso I'm not sure where is mistake.Update: just notice, that AES17 talking about signal level as amplitude...so I tried to get sqrt to get amplitude from power:Code:asignal = sqrt(sum(signal.^2)/N);aspectr = sqrt(sum(abs(spectr/N).^2));fprintf('asignal = %f = %+.2f dBFS\n', asignal, 20*log10(asignal));fprintf('aspectr = %f = %+.2f dBFS\n', aspectr, 20*log10(aspectr));it shows the following result:Code:asignal = 0.707107 = -3.01 dBFSaspectr = 0.707107 = -3.01 dBFSSo, I divided it with 0.707107 (full scale amplitude):Code:asignal = sqrt(sum(signal.^2)/N) * sqrt(2);aspectr = sqrt(sum(abs(spectr/N).^2)) * sqrt(2);and it shows expected result:Code:asignal = 1.000000 = +0.00 dBFSaspectr = 1.000000 = +0.00 dBFSfor both - square wave and DC it shows:Code:asignal = 1.414214 = +3.01 dBFSaspectr = 1.414214 = +3.01 dBFSis this correct?

author: gf
author_rank: Super Contributor
timestamp: March 30, 2023, 07:30:33 pm
content: The problem is here: 20*log10(psignal)Forpowerratios, dB is10*log10(psignal)For voltage ratios , dB is 20*log10(psignal)

author: radiolistener
author_rank: Super Contributor
timestamp: March 31, 2023, 03:17:38 am
content: Found that there is required fft result normalization with 1 / sqrt(N). With this normalization rms value calculated in time and frequency domain will be the same:Code:Fs = 128;N = 128;t = 0:1/Fs:(N-1)/Fs;signal = sin(2*pi*2*t); % sine wave%signal = cat(2, ones(1, N/2), -ones(1, N/2)); % square wave%signal = ones(1, N) * 1; % DC wavespectr = fft(signal) / sqrt(N);rmsSignal = sqrt(sum(abs(signal).^2)/N);rmsSpectr = sqrt(sum(abs(spectr).^2)/N);fprintf('rmsSignal: %f V = %f W\n', rmsSignal, rmsSignal^2);fprintf('rmsSpectr: %f V = %f W\n', rmsSpectr, rmsSpectr^2);for sine wave it shows:Code:rmsSignal: 0.707107 V = 0.500000 WrmsSpectr: 0.707107 V = 0.500000 Wfor square wave and DC it shows:Code:rmsSignal: 1.000000 V = 1.000000 WrmsSpectr: 1.000000 V = 1.000000 Wwhich looks good, as expected.So, now in order to get AES17 signal level in dBFS there is need to divide it with rms amplitude of a full scale sine which is 1/sqrt(2), by swapping the numerator and denominator we get multiplier sqrt(2). And then amplitude relation needs to be converted to dB with 20*log10():Code:fprintf('rmsSignal: %+.2f dBFS\n', 20*log10(rmsSignal*sqrt(2)));fprintf('rmsSpectr: %+.2f dBFS\n', 20*log10(rmsSpectr*sqrt(2)));for sine wave it shows:Code:rmsSignal: +0.00 dBFSrmsSpectr: +0.00 dBFSfor square wave and DC it shows:Code:rmsSignal: +3.01 dBFSrmsSpectr: +3.01 dBFSBy the way, if we use calculations as power it can eliminate redundant sqrt:Code:Fs = 128;N = 128;t = 0:1/Fs:(N-1)/Fs;signal = sin(2*pi*2*t); % sine wave%signal = cat(2, ones(1, N/2), -ones(1, N/2)); % square wave%signal = ones(1, N) * 1; % DC wavespectr = fft(signal) / sqrt(N);pSignal = sum(abs(signal).^2)/N;pSpectr = sum(abs(spectr).^2)/N;fprintf('rmsSignal: %f V = %f W\n', sqrt(pSignal), pSignal);fprintf('rmsSpectr: %f V = %f W\n', sqrt(pSpectr), pSpectr);fprintf('rmsSignal: %+.2f dBFS\n', 10*log10(pSignal*2));fprintf('rmsSpectr: %+.2f dBFS\n', 10*log10(pSpectr*2));Code:rmsSignal: 0.707107 V = 0.500000 WrmsSpectr: 0.707107 V = 0.500000 WrmsSignal: -0.00 dBFSrmsSpectr: -0.00 dBFSIf I understand correctly, 1/sqrt(N) normalization for FFT is required here to preserveParseval-Plancherel or Rayleigh's energy theorem.correct me please if there is some mistake

author: radiolistener
author_rank: Super Contributor
timestamp: March 31, 2023, 04:21:05 am
content: And now, there is need to calculate power of specific bin in the FFT result. I can do it with the following expression:Code:P = 2*abs(spectr(binNumber)).^2/N;N is FFT size. I'm using multiplier by 2 because there are two parts of spectrum with negative and positive frequency and since I'm use single side spectrum, I need to multiply bin magnitude by 2.This expression works ok for non DC bin. For example for sine signal it returns the same signal level of specific bin as a total signal level. But for DC signal it returns +6.02 dBFS at DC bin while total rmsSignal = +3.01 dBFS.So, it seems that there is need to remove multiply by two for DC bin:Code:p0 = abs(spectr(1)).^2/N;p1 = 2*abs(spectr(2)).^2/N;p2 = 2*abs(spectr(3)).^2/N;Is that correct?By the way, if we use FFT normalization 1/N, it allows to eliminate divide by N for calculations in frequency domain:Code:spectr = fft(signal) / N;pSignal = sum(abs(signal).^2)/N;pSpectr = sum(abs(spectr).^2);fprintf('rmsSignal: %f V = %f W\n', sqrt(pSignal), pSignal);fprintf('rmsSpectr: %f V = %f W\n', sqrt(pSpectr), pSpectr);fprintf('rmsSignal: %+.2f dBFS\n', 10*log10(pSignal*2));fprintf('rmsSpectr: %+.2f dBFS\n', 10*log10(pSpectr*2));p0 = abs(spectr(1)).^2;p1 = 2*abs(spectr(2)).^2;p2 = 2*abs(spectr(3)).^2;p3 = 2*abs(spectr(4)).^2;p4 = 2*abs(spectr(5)).^2;fprintf('p0 = %f V = %+.2f dBFS\n', sqrt(p0), 10*log10(p0*2));fprintf('p1 = %f V = %+.2f dBFS\n', sqrt(p1), 10*log10(p1*2));fprintf('p2 = %f V = %+.2f dBFS\n', sqrt(p2), 10*log10(p2*2));fprintf('p3 = %f V = %+.2f dBFS\n', sqrt(p3), 10*log10(p3*2));fprintf('p4 = %f V = %+.2f dBFS\n', sqrt(p4), 10*log10(p4*2));Code:rmsSignal: 0.707107 V = 0.500000 WrmsSpectr: 0.707107 V = 0.500000 WrmsSignal: +0.00 dBFSrmsSpectr: +0.00 dBFSp0 = 0.000000 V = -345.45 dBFSp1 = 0.707107 V = +0.00 dBFSp2 = 0.000000 V = -325.27 dBFSp3 = 0.000000 V = -326.48 dBFSp4 = 0.000000 V = -331.67 dBFS

author: gf
author_rank: Super Contributor
timestamp: March 31, 2023, 12:28:19 pm
content: Quote from: radiolistener on March 31, 2023, 04:21:05 amYes.QuoteAt least for the Matlab/Octave implementation of FFT. Note that FFT normalization is implementation-dependent, and other FFT libraries may require a different normalization.

author: gf
author_rank: Super Contributor
timestamp: March 31, 2023, 09:24:25 pm
content: @radiolistener, I also suggest to readthis paperwhich covers even more aspects.I wanted to post this link earlier, but I couldn't find it right away.

author: mawyatt
author_rank: Super Contributor
timestamp: April 01, 2023, 06:32:03 pm
content: @ gf,That's a good reference, thanks for postingBest,

author: radiolistener
author_rank: Super Contributor
timestamp: April 01, 2023, 08:59:11 pm
content: Quote from: gf on March 31, 2023, 09:24:25 pmThanks, it contains a lot of info about different window functions, some of them I even didn't hear before

author: MegaVolt
author_rank: Frequent Contributor
timestamp: April 05, 2023, 10:51:37 am
content: Can someone who is sure of their numbers post some test data? For different window functions?At the input, for example, 32 numbers; at the output, 16 amplitudes and 16 phases.

author: radiolistener
author_rank: Super Contributor
timestamp: April 05, 2023, 08:53:58 pm
content: Quote from: MegaVolt on April 05, 2023, 10:51:37 amyou can do it in MATLAB/Octave/R.But I notice that MATLAB implementation looks not the best.For example I wrote my Kaiser window implementation and tried to compare it with MATLAB one. First I found that my function has some minor errors, so I wrote Taylor series approximation for Bessel function which use many terms, and it looks almost the same as MATLAB, but MATLAB has a little difference for a least significant digits.Here is example of Kaiser window function for length=16 and beta=38:MATLAB:Code:4.8344100587269438e-167.6303044075880228e-096.3285566773886172e-060.000559996344220984390.0131716443357857960.117231406122835490.468846105842593790.919967112319325260.919967112319325260.468846105842593790.117231406122835490.0131716443357857960.000559996344220984396.3285566773886172e-067.6303044075880228e-094.8344100587269438e-16my implementation with Taylor series approximation of Bessel function which use first 100 terms:Code:4.8344100587269438e-167.6303044075880245e-096.3285566773886138e-060.000559996344220984290.0131716443357857960.117231406122835520.468846105842594010.919967112319325260.919967112319325260.468846105842594010.117231406122835520.0131716443357857960.000559996344220984296.3285566773886138e-067.6303044075880245e-094.8344100587269438e-16I like Kaiser window, because it allows to see very good dynamic range on FFT, but note that it make bin bandwidth pretty wide. For example, if you use Kaiser window on a sample length N=256 it leads to a more wide bin bandwidth, each bin with max amplitude will have disturbance for at least 12-13 bins on each side, but on the other hand it allows to see details down to -300 dB.For example, here is a clean sine 32 Hz on FFT with N=256 with applying Kaiser window with beta=38.And for comparison, here is also screenshot of the same sine on FFT N=256 with applying Blackman-Harris 4 term window: