Coherent sampling is quite difficult to meet under the lab conditions. One has to go for windowing to characterize the dynamic performance of ADC. Though seminal papers and reports [1-2] lie on my desk for quite long time, I still feel difficult to really understand windowing. Recently, I found two posts from Nerd Rage (Windows of Opportunity & ENBW) quite helpful, which are easier to digest.

And now, finally, I decide to write down my very limited understanding on FFT with windowing, focusing on characterizing the dynamic performance of ADC in Matlab. Let’s go straightforward by first looking at the Matlab script I am using to calculate the SNR/SNDR of ADC.

fs = 1e6; % Sample rate nfft = 1024; % Number of FFT cycles = 113; % Number of input periods fin = cycles/nfft*fs; % Input frequency data = data(1:nfft); % Number of data to be the same as nfft w = 0.5*(1 - cos(2*pi*(0:nfft-1)/nfft)); % Hann window cg = sum(w)/nfft; % Normalized coherent gain enbw = sum(w.*w)/(sum(w)^2)*nfft; % Normalized equivalent noise bandwidth nb = 3; % Signal bins dcbin = (nb+1)/2; % Number of DC bins if (size(data,1)~= size(w,1)) % Check dimention w = w'; end ss = abs(fft(data.*w)); % FFT with windowing ss = ss/nfft/cg; % Compensate for window attenuation ss = ss(1:nfft/2).*2; % Drop the redundant half but keep total power the same signal_bin = cycles+1; % Signal bin, Matlab array starts from 1 dc_bins = 1:dcbin; % DC bins all_bins = setdiff(1:nfft/2, dc_bins); % Disregard DC bins signal_bins = signal_bin + (-(nb-1)/2:(nb-1)/2); % Signal leakage bins other_bins = setdiff(all_bins, signal_bins); % Further discard signal bins fh = (2:10)*fin/fs; % Harmonic tone: (-/+m)fin + (-/+k)fs while max(fh) > 1/2 fh = abs(fh - (fh > 1/2)); % If harmonic tone fh>fs/2, it is equal to fs-fh end harm_bins = round(nfft * fh) + 1; % Harmonic bins (2nd - 10th) harm_binsl = zeros(length(harm_bins),nb); % Find Harmonic leakage bins for i = 1:length(harm_bins) harm_binsl(i,:) = ((harm_bins(i) + (-(nb-1)/2 : (nb-1)/2))); end harm_binsl=reshape(harm_binsl',length(harm_bins)*nb,1); % Convert matrix to array harm_binsl=unique(harm_binsl); % Discard the repetitive harmonic bins noise_bins = setdiff(other_bins, harm_binsl); % Further discard the harmonic bins Psignal = sum(ss(signal_bin).^2); % Signal power PnoiseD = sum(ss(noise_bins).^2)/enbw/length(noise_bins);% Noise PSD Pnoise = PnoiseD*length(all_bins); % Total noise power Pharm = sum(ss(harm_bins).^2); % Power of harmonics snr = 10*log10(Psignal/Pnoise); % Calculate SNR sndr = 10*log10(Psignal/(Pnoise+Pharm)); % Calculate SNDR enob = (sndr - 1.76)/6.02; % Calculate ENOB thd = -10*log10(Psignal/Pharm); % Calculate THD Pharm_max = max(ss(harm_bins)).^2; Pnoise_max = max(ss(noise_bins)).^2; sfdr = 10*log10(Psignal/max(Pharm_max,Pnoise_max)); % Calculate SFDR sdb = 10*log10(ss.^2); f = (1:length(ss))/nfft; % Frequency vector normalized to fs plot(f, sdb, 'k-','linewidth',1.5); xlabel('Frequency [ f / f_s ]','FontSize',10); ylabel('Power Spectrum [ dB ]','FontSize',10); grid on; text(0.3, -40,... sprintf('SNDR = %.1fdB \n SNR = %.1fdB \n THD = %.1fdB \n SFDR = %.1fdB \n ENOB = %.1fbits', sndr, snr, thd, sfdr, enob), ... 'FontSize',10);

An example of FFT plot will look like this:

A default FFT using rectangular/box window has a normalized coherent gain as 1, a normalized equivalent noise bandwidth as 1, and only 1 signal bin. Hence, if other types of window are used, the corresponding window properties need to be specified. It’s all about two values: 1) the sum of the window terms; and 2) the sum of the squares of the window terms. Equations from the seminal paper [1] tell us why the two sums matter.

Let the input sampled sequence be defined by

where q(nT) is a white-noise sequence with variance . Then the signal component of the windowed spectrum is given by

Hence, the output amplitude of the noiseless signal is the input amplitude multiplied by a term which is **the** **sum of the window terms (S1)**. This term is called the processing gain (sometimes called coherent gain) of the window. The rectangular window has the largest gain compared to other windows. In the Matlab script, the normalized coherent gain (normalized by S1 of the rectangular window) is specified.

The noise component of the windowed spectrum is given by

The noise power is calculated using the expectation operator

As additive noise is assumed to be white, the above value represents the noise floor level (or the noise power spectral density), which is also constant. Notice the power gain of noise is **the sum of the squares of the window terms (S2)**.

The noise bandwidth is calculated by the total noise power (sigma^2*S2*fs) divided by the peak power gain of the window (S1^2). Here we only focus on the multiplication term to the input noise (S2*fs/S1^2). Further introducing a parameter, fres, the width of one frequency bin (fs/N), then the normalized equivalent noise bandwidth (ENBW) is given by

Therefore, to obtain correct power levels of the signal and the noise, the normalized coherent gain is subtracted from the PSD and the normalized ENBW is subtracted from the calculated total noise power, respectively.

Nerd Rage explains the ENBW from window energy point of view, which is quite insightful. Allow me to copy one of his picture and some remarks here:

“For the Hann window the main lobe height is -6.02dB and therefore the height of any single spectral line will be 6.02dB below its real value. To obtain correct energy levels of the spectral peaks (in the absence of scalloping), the main lobe height (in dB units) is usually subtracted from the PSD. However, this overcompensates the window-specific reduction of the noise floor – for the Hann window, peak compensation is 6.02dB while the noise floor is only 4.26dB below its true value. This decreases the observed SNR by 1.76dB or a factor of 1.5.”

Note: for Hann window, 20log(s1/N) = -6.02dB; 10log(s2/N)=-4.26dB; N*s2/s1^2 = 1.5.

Regarding the number of spread bins for a certain window function, a simple Matlab script performing FFT with windowing can do the job:

% Here you create an input fs = 1024; nfft = 1024; fin = 16; cycles = 16; vin = sin(2.*pi.*fin.*(0:1:nfft-1)./fs); % Here you perform FFT with and without windowing ss = abs(fft(vin(1:nfft)))/(nfft/2); w = 0.5*(1 - cos(2*pi*(0:nfft-1)/nfft)); cg = sum(w)/nfft; sshann = abs(fft(vin(1:nfft).*w))/(nfft/2)/cg; % Here you do the plot figure(1) subplot(1,2,1) stem(ss); title('Rectangular'); xlim([1 25]); grid on; subplot(1,2,2) stem(sshann); xlim([1 25]); title('Hann'); grid on;

Then it comes the plot:

Sorry for the boring and not well-written post (though I’ve tried my best). Fig.4 is just for a smile when one finishes reading all the above dull words.

Last but not least, may Mr. Fourier’s wisdom be always with us!

[1] Frederick J. Harris, “On the Use of Windows for Harmonic Analysis with the Discrete Fourier Transform,” Proceedings of the IEEE, vol 66, pp. 51-83, January 1978.

[2] G. Heinzel, A.Rudiger, and R. Schilling, “Spectrum and spectral density estimation by the Discrete Fourier transform (DFT), including a comprehensive list of window functions and some new flat-top windows,” February 15, 2002.