hluyen
Newbie level 5
- Joined
- Mar 13, 2013
- Messages
- 9
- Helped
- 1
- Reputation
- 2
- Reaction score
- 1
- Trophy points
- 1,283
- Activity points
- 1,351
Do Anyone known caculate frequency from FFT algorithm with samples input ?
Follow along with the video below to see how to install our site as a web app on your home screen.
Note: This feature may not be available in some browsers.
Code C - [expand] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 % Fabricate a test signal to give me something to look at Fs = 1.06E+3; % Sampling frequency N = 3125; % Number of samples sigF = 189; % Frequency of test tone sig = sin(2*pi*sigF*(1:N)/Fs) + 0.1*rand(1, N); % Test tone + noise % Calculate DFT Fr = zeros(1, N); Fi = zeros(1, N); for k=1:N for n=1:N Fr(k) = Fr(k)+sig(n)*cos(2*pi*k*n/N); Fi(k) = Fi(k)-sig(n)*sin(2*pi*k*n/N); end end % Find peak freq P = Fr.^2+Fi.^2; peakVal = 0; for i=1:floor(N/2) if P(i) > peakVal peakVal = P(i); peakLoc = i; end end peakFreq = peakLoc*Fs/N plot(P);
Code C - [expand] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 double primaryFreqComponent(int *ADCdata, int Fs, int N) { int n, k; int peakLoc; // Location (=frequency) of peak signal component double peakVal = 0.0; // Magnitude of peak signal found double Fr[N]; // <--- Buffers of intermediate data the same length as the input data double Fi[N]; // (malloc() or statically allocate ... or whatever for (k=0; k<N; k++) // Calculate DFT for (n=0; n<N; n++) { Fr[k] += (double)(ADCdata[n])*cos(2*pi*k*n/N); Fi[k] -= (double)(ADCdata[n])*sin(2*pi*k*n/N); } for (n=0; n<N; n++) // Search for peak if (Fr(n)*Fr(n)+Fi(n)*Fi(n) > peakVal) { peakVal = Fr(n)*Fr(n)+Fi(n)*Fi(n); peakLoc = n; } return (double)(peakLoc*Fs)/(double)(N); }
I previously wrote a script to report the dominant frequency present in a series of captured ADC samples that might suit your need...
It [laboriously] implements a discrete Fourier transform and returns the frequency of the transform bin with the largest power. Note that it was intended to debug an embedded ADC/CPU - it's not written for speed!
Enjoy
The (working) MATLAB implementation:
Code C - [expand] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 % Fabricate a test signal to give me something to look at Fs = 1.06E+3; % Sampling frequency N = 3125; % Number of samples sigF = 189; % Frequency of test tone sig = sin(2*pi*sigF*(1:N)/Fs) + 0.1*rand(1, N); % Test tone + noise % Calculate DFT Fr = zeros(1, N); Fi = zeros(1, N); for k=1:N for n=1:N Fr(k) = Fr(k)+sig(n)*cos(2*pi*k*n/N); Fi(k) = Fi(k)-sig(n)*sin(2*pi*k*n/N); end end % Find peak freq P = Fr.^2+Fi.^2; peakVal = 0; for i=1:floor(N/2) if P(i) > peakVal peakVal = P(i); peakLoc = i; end end peakFreq = peakLoc*Fs/N plot(P);
And a never tested/built C-psuedo code version...
Code C - [expand] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 double primaryFreqComponent(int *ADCdata, int Fs, int N) { int n, k; int peakLoc; // Location (=frequency) of peak signal component double peakVal = 0.0; // Magnitude of peak signal found double Fr[N]; // <--- Buffers of intermediate data the same length as the input data double Fi[N]; // (malloc() or statically allocate ... or whatever for (k=0; k<N; k++) // Calculate DFT for (n=0; n<N; n++) { Fr[k] += (double)(ADCdata[n])*cos(2*pi*k*n/N); Fi[k] -= (double)(ADCdata[n])*sin(2*pi*k*n/N); } for (n=0; n<N; n++) // Search for peak if (Fr(n)*Fr(n)+Fi(n)*Fi(n) > peakVal) { peakVal = Fr(n)*Fr(n)+Fi(n)*Fi(n); peakLoc = n; } return (double)(peakLoc*Fs)/(double)(N); }
Code C - [expand] 1 2 for (n=0; n<(N/2); n++) // Search for peak { ...
Code C - [expand] 1 2 for (k=0; k<(N/2); k++) // Calculate DFT { ...
Well, that turned out to be an insidious little bug! The good news is the code is not fundamentally flawed, it just needs a tweak
The problem is in the loop that searches for the peak amplitude frequency bin - for 'simplicity', I've searched over every transformed frequency bin (i.e. all n<N). This isn't necessary, since for a real valued input (such as a single stream of ADC samples) the DFT bins above N/2 are (theoretically) the mirror image of the bins below N/2. [The frequency of bin N/2 corresponds to the Nyquist frequency of the specified sampling rate].
As luck (!) would have it - due to numerical rounding errors/precision - for the values you specified, the magnitude of (image) bin #38 was ~1E-9 larger than it's (fundamental) in bin #12. As such, the frequency that was being reported was of the image, i.e. Fs-50 = 150Hz... The 3Hz error (i.e. 153 Hz instead of the correct 150Hz) is a consequence of the coarse frequency binning - with N=50, the frequency resolution is only ~8Hz (~= 2Fs/N).
The fix is easy though! Simply change the loop declaration/conditional to only search the frequency bins below Nyquist. i.e.:
Code C - [expand] 1 2 for (n=0; n<(N/2); n++) // Search for peak { ...
Works like a charm now... enjoy!
(...and thanks for adding a wrapper/testing it! Neat
Come to think of it - if we're not going to search over the bins > N/2, then there's no point calculating them either. The previous loop involving k could also be similarly truncated:
Code C - [expand] 1 2 for (k=0; k<(N/2); k++) // Calculate DFT { ...
(...as well as the declarion of the arrays Fr and Fi...)
Code C - [expand] 1 2 3 4 5 6 for(i = 0;i<sampleN;i++){ if (ADCsample[i] > 0) ADCsample[i] = 1; else ADCsample[i] = 0; }