I have a circuit/ee background and I think the Matlab code from "learner" is very good but not very correct and I would like to correct it based on my own standing. (low reputation hence cannot comment) The FFT results should divided by its own NFFT length instead of divided by N, that's not a correct scaling factor.
In the example, the actual analog signal noise is constant. When there are more FFT bins (higher N), the total noise is distributed into more bins and hence each bin get less noise. The overall noise floor level looks lower but has the same total noise. That's why we should expect to see FFT size = N gets lowest noise floor while FFT size =N/8 has highest noise floor (while the total noise is actually the same for all 4 cases in the analog signal domain)
The FFT process gain is outside of the equation SNR = 6.02N + 1.76dB. This SNR equation (6.02N+.76) only considers quantization noise. The actual signal noise floor before the ADC quantization can be lower than the quantization noise. I think a very good note about this is here (Taking the Mystery out of the Infamous Formula, "SNR = 6.02N + 1.76dB," and Why You Should Care) I still have a few things that I don't understand but thanks to the Matlab code example here.
For question 2, I think since FFT and iFFT are linear transform. They should be the same. And with my above explanation, since the noise is actually constant and doesn't change. It doesn't matter transfer between FFT or iFFT.
as you can see in this plot, the blue curve with FFT size = N shows lowest noise average level outside of the signal frequency bin. But because it has the largest number of bins. After you summed up all the bins from the blue, you actually get the same noise as all other cases. The FFT size = N/8 has the least number of frequency bins and hence each bin needs to have higher noise to make the total noise equal to other FFT cases.
clear;
close all;
rng default
%f1=1e3;
fs=16.384e3;
N=8192;
M=816;
f1=M/N*fs
t=0:1/fs:(N-1)/fs;
%t=0:1/fs:1; %1 second of sampling time
NFFT = round([N/8 N/4 N/2 N]); %FFT size
amp1=1;
amp_rms1=amp1/sqrt(2);
SNR_dB=-10;
Vnoise=amp_rms1/(10^(SNR_dB/20))
sx= amp_rms1*sqrt(2)*sin(2*pi*f1*t);
%sn = Vnoise*randn(1,N);
sn = Vnoise*randn(size(t));
xn = sx + sn;
figure(1);
plot(t(1:100),xn(1:100));hold on;
plot(t(1:100),sx(1:100),'r')
legend('noisy','clean');
%FFT of different size
figure(2)
Xf = fft(xn, NFFT(4));
NFFT_4_FFTresult_length=length(Xf)
P2=abs(Xf)/NFFT(4);
P1=P2(1:NFFT(4)/2+1);
P1(2:end-1)=2*P1(2:end-1);
plot(fs*(0:(NFFT(4)/2))/NFFT(4), P1, '.-');hold on;
%noise summary
ymax=0;
for i=1:length(P1)
if P1(i)>ymax
ymax = P1(i);
end
end
noiseSum=0;
for i=1:length(P1)
if P1(i) >= ymax
% method 1, remove the signal bin
% below line remove the signal and also the noise from the freq bin
%%P1(i) = 0;
% method 2, borrow noise from neighbour bin, not accurate
%keep the noise on this block
% if i == 1
% P1(i)=P1(i+1);
% else
% P1(i)=P1(i-1);
% end
% method 3, subtract known signal amplitude from the signal bin
P1(i) = P1(i)-amp1;
i
ymax
end
noiseSum=noiseSum+(P1(i)/sqrt(2))^2;
end
%noiseSum
sqrtNoiseSum=sqrt(noiseSum)
Xf = fft(xn, NFFT(3));
NFFT_3_FFTresult_length=length(Xf)
P2=abs(Xf)/NFFT(3);
P1=P2(1:NFFT(3)/2+1);
P1(2:end-1)=2*P1(2:end-1);
plot(fs*(0:(NFFT(3)/2))/NFFT(3), P1, 'r.-');
Xf = fft(xn, NFFT(2));
NFFT_2_FFTresult_length=length(Xf)
P2=abs(Xf)/NFFT(2);
P1=P2(1:NFFT(2)/2+1);
P1(2:end-1)=2*P1(2:end-1);
plot(fs*(0:(NFFT(2)/2))/NFFT(2), P1, 'g.-');
Xf = fft(xn, NFFT(1));
NFFT_1_FFTresult_length=length(Xf)
P2=abs(Xf)/NFFT(1);
P1=P2(1:NFFT(1)/2+1);
P1(2:end-1)=2*P1(2:end-1);
plot(fs*(0:(NFFT(1)/2))/NFFT(1), P1, 'k.-');
%noise summary
ymax=0;
for i=1:length(P1)
if P1(i)>ymax
ymax = P1(i);
end
end
noiseSum=0;
for i=1:length(P1)
if P1(i) >= ymax
% method 1, remove the signal bin
% below line remove the signal and also the noise from the freq bin
%%P1(i) = 0;
% method 2, borrow noise from neighbour bin, not accurate
%keep the noise on this block
% if i == 1
% P1(i)=P1(i+1);
% else
% P1(i)=P1(i-1);
% end
% method 3, subtract known signal amplitude from the signal bin
P1(i) = P1(i)-amp1;
i
ymax
end
noiseSum=noiseSum+(P1(i)/sqrt(2))^2;
end
%noiseSum
sqrtNoiseSum=sqrt(noiseSum)
legend('FFT size = N', 'FFT size = N/2', 'FFT size = N/4', 'FFT size = N/8');
%yscale log;
set(gca, 'YScale', 'log')
grid on;
Different FFT length separate the sampling frequency fs into smaller frequency bin (or bandpass filter). Each bin has a bandwidth of fs/N
For $N/8=1024$, each bin is equal to $16.384e3/1024=16 $ Hz
For N=8192, each bin in FFT is equal to $16.384e3/8192=2$ Hz
It’s expected that the noise will have spectrum leakage after FFT, but if the signal (not noise) can be contained within the bandwidth of a frequency bin. The purpose of coherent sampling is achieved.
choose $f_1=M/N×f_s=816/8192×16.384e3 = 1632 Hz$
1st frequency bin is at 0 Hz, it will sample signal from 0 to $1*16384/8192$ (including 0 but not including $16384/8192=2$ Hz)
Frequency f1 is located at the $(816+1)$th bin, the frequency is same as f1=1632 Hz, it samples signal from $816*16.384e3/8192=1632$ Hz to $817*16384/8192=1634$ Hz.
Because frequency f1 is located at the sampling frequency of the FFT bin, hence there is no spectral leakage, the magnitude of the signal is kept integral.
The FFT based on the matlab code is shown above.
As can be found in the plot, blue curve with FFT size=N shows lower noise floor while FFT size=N/8 shows higher noise floor. While at the same time, the total noise across the whole bandwidth of $fs/2=8192$ Hz is kept constant. The noise rms or standard deviation is 2.236V as calculated above.
So why FFT size =N/8 shows higher noise floor. It’s because the total number of bins existed in this FFT is only N/8=1024. The total noise is divided by this 1024 bandpass filter and distributed into every one of them. As you can see that each frequency bin (bandpass filter) didn’t get the same amount of noise, hence the noise floor is up and down with different value. The calculation in the matlab code with variable name sqrtNoiseSum re-calculates the noise. The value is closed to 2.236V but cannot exactly equal to 2.236V because one of the frequency bins has signal and noise summed up together.
Even if we can remove the signal amplitude from the frequency bin and leave the noise there (method 3 is doing that), the calculated root sum squared noise is still slightly off from 2.236V. I believe that’s due to the spectral leakage. Maybe windowing method can solve this problem but the value is very close to 2.236V already.
For the FFT size = N and FFT size = N/8 cases, the re-calculated noise is equal to 2.2397V and 2.3035V.
The calculated results are not the same each time due to random noise distribution. However, with “rng default” in the Matlab code, the results can be repeated.