why frequency is wrong? - matlab

I want to find fft of cos(2pi10*n). I wrote this code but it has two problems. First, the estimated frequency is not 10 Hz. Second, from theory I expected that the amplitude of frequency should be pi. I found that the amplitude is dependent to size of fft. I appreciate any help.
n = 0:1:9;
x = cos(2*pi*10*n);
xdft = fft(x);
w=0:2*pi/(10):2*pi-2*pi/(10);
plot(abs(xdft) )

You must have overlooked the fact that, since the frequency of your signal is 10 Hz, you must sample it at no less than 20 Hz, lest it appear aliased during spectral analysis:
for i=1:4
if i==1
fs=1; %this is the value originally considered in OP's code
elseif i==2
fs=8;
elseif i==3
fs=20;
else
fs=40;
end
n=0:(1/fs):9;
x=cos(2*pi*10*n);
xdft=fft(x);
f=(0:(numel(x)-1))/numel(x)*fs;
subplot(4,1,i);
stem(f,abs(xdft),'.-');
xlabel('Frequency [Hz]');
end

Related

High-frequency spur after performing an FFT in MATLAB

I have a modulated signal, and now I want to perform an FFT. However, I am getting a spur at a high frequency, which should not be there (and if it should, I have no clue as to why).
Lvl=[0.5,0.9,0.5,0.5,0.1,0.1,0.9,0.5];
fa=60; %the frequency of the parasitic source in hertz
np=2; %number of periods per bit
kl=length(Lvl);
t=0:0.01*np/fa:np*kl/fa;
Sig=sin(2*pi*fa*t);
for n=1:1:101
Sig(n)=Sig(n)*Lvl(1);
end
for n=102:1:201
Sig(n)=Sig(n)*Lvl(2);
end
for n=202:1:301
Sig(n)=Sig(n)*Lvl(3);
end
for n=302:1:401
Sig(n)=Sig(n)*Lvl(4);
end
for n=402:1:501
Sig(n)=Sig(n)*Lvl(5);
end
for n=502:1:601
Sig(n)=Sig(n)*Lvl(6);
end
for n=602:1:701
Sig(n)=Sig(n)*Lvl(7);
end
for n=702:1:801
Sig(n)=Sig(n)*Lvl(8);
end
plot(t,Sig)
%FFT
y = fft(Sig);
f = (0:length(y)-1)*(1/(0.01*np/fa))/length(y);
plot(f,abs(y))
title('Magnitude')
I'm expecting just a spike at 60Hz with spurs around it, but instead I'm getting that and a large spike at almost 3kHz with spurs around it.
This peak at almost 3 kHz should be there, since the fft of a real is signal symmetric around the nyquist frequency (actually complex conjugate). The nyquist frequency is half the samping frequency, in your case sampling is done at 3000 Hz, thus the nyquist frequency is 1500 Hz. If you look closer at the peak, you will see that it is at 2940 Hz (which is 3000-60 Hz), due to the fact that the fft is mirrored around 1500 Hz.
There are plenty of sources that explain why this is a property of the Fourier transform (e.g. here).
The actual Fourier transform would be mirrored around the zero frequency, but the fft gives you the fast Fourier transform, which is mirrored around the nyquist frequency. You can use fftshift to center the spectrum around the zero frequency.
I took the liberty to shorten your code, by avoiding repetition of several for-loops, and added the fftshift. Since your signal is real, you can also choose to show only one side of the fft, but I'll leave that up to you.
Lvl=[0.5,0.9,0.5,0.5,0.1,0.1,0.9,0.5];
fa=60; % the frequency of the parasitic source in hertz
np=2; % number of periods per bit
kl = length(Lvl);
dt = 0.01*np/fa; % time step
Tend = np*kl/fa - dt; % time span
t = 0:dt:Tend; % time vector
N = length(t); % number samples
Sig=sin(2*pi*fa*t);
for n = 1:kl
ids = (1:100) + (n-1)*100;
Sig(ids) = Sig(ids).*Lvl(n);
end
% FFT
Y = fft(Sig);
fv = (0:N-1)/(N*dt); % frequency range
% FFT shift:
Y_shift = fftshift(Y);
fv_shift = (-N/2:N/2-1)/(N*dt); % zero centered frequency vector
% Plot
figure(1); clf
subplot(311)
plot(t,Sig)
title('Signal')
subplot(312)
plot(fv,abs(Y))
title('FFT Magnitude')
subplot(313)
plot(fv_shift,abs(Y_shift))
title('FFT Magnitude zero shift')

FFT of a given diagram (data)

I have collected some data of pressure distribution of an acoustic field along an axis, i.e. Pressure_vs_distance, at a specific source frequency (Let's say 0.5 MHz). Now, I want to obtain the frequency components of this data (which will be n*0.5 MHz) but every code I try, I can't extract those frequencies and the FFT diagram is like a vertical line at zero. The data and figure are provided below. Could you please help me to see what's wrong?
P.S: My own guess is the number of axial data. But, even when I increased it, the result didn't change (shape of the fft diagram).
Pressure (MPa) _vs_axial distance (cm)
data (Size~ 2 Mb): https://ufile.io/wjhlo
L=length(y);
dx=9.43479300064157e-05;
fs=1/dx;
out=fft(y,L)/L;
figure
plot(fs/2*linspace(0,1,(length(out)/2)+1),abs(out(1:(length(out)/2)+1)))
title('One sided Spectrum')
xlabel('Normalized frequency')
ylabel('Magnitude')
From this question:
Confusion in figuring out the relation between actual frequency values and FFT plot indexes in MATLAB
[Ycomp, fHz] = getFFT(y(1:1000),1/(x(2)-x(1)))
Your sampling frequency seems to be 10.6KHz. So according to the Nyquist Shannon sampling theorem you will not detect frequencies above 5.3 KHz. Not sure where your 0.5 MHz is coming from.
First of all prepare your data: try pass your data through the pre-emphasis filter:
y1 = filter([1 -1], 1, y);
Next, observe your data and remove obvious data errors:
y1(1:3) = 0;
The Fourier analysis suites only for quasi-periodic signals. So, you should select an interval of quasi-stationarity and perform analysis on such (possible overlapping) intervals. Let's say the signal is quasi-stationary on 5000 points. And we want to perform analysis with 100 points shift:
sampleRate = 1/(x(2)-x(1));
frameSize = 5000;
frameShift = 100;
[signalSpectrogram, freqObs, timeObs] = spectrogram(y1, frameSize, frameSize-frameShift, pow2(nextpow2(frameSize)), sampleRate);
Let's display all the results:
figure('NumberTitle','off', 'Name','The Spectral Analysis', 'Units','normalized', 'Position',[0 0 1 1]);
subplot(3,1,1);
plot(x,y);
grid('on');
xLim = xlim();
title('Original Signal');
subplot(3,1,2);
plot(x,y1);
grid('on');
title('Prepared Signal');
subplot(3,1,3);
imagesc(timeObs, freqObs, 10*log10(signalSpectrogram.*conj(signalSpectrogram)));
axis('xy');
axis([xLim 0 200]);
title('Prepared Signal Spectrogram');
At the end you must get something similar to:

How to find the frequency of a periodic sound signal?

I'm working on sound signals of a walking pattern, which has obvious regular patterns:
Then I thought I can get the frequency of walking (approximately 1.7Hz from the image) using FFT function:
x = walk_5; % Walking sound with a size of 711680x2 double
Fs = 48000; % sound frquency
L=length(x);
t=(1:L)/Fs; %time base
plot(t,x);
figure;
NFFT=2^nextpow2(L);
X=fft(x,NFFT);
Px=X.*conj(X)/(NFFT*L); %Power of each freq components
fVals=Fs*(0:NFFT/2-1)/NFFT;
plot(fVals,Px(1:NFFT/2),'b','LineSmoothing','on','LineWidth',1);
title('One Sided Power Spectral Density');
xlabel('Frequency (Hz)')
ylabel('PSD');
But then it doesn't give me what I expected:
FFT result:
zoom image has lots of noises:
and there is no information near 1.7Hz
Here is the graph from log domain using
semilogy(fVals,Px(1:NFFT));
It's pretty symmetric though:
I couldn't find anything wrong with my code. Do you have any solutions to easily extract the 1.7Hz from the walking pattern?
here is the link for the audio file in mat
https://www.dropbox.com/s/craof8qkz9n5dr1/walk_sound.mat?dl=0
Thank you very much!
Kai
I suggest you to forget about DFT approach since your signal is not appropriate for this type of analysis due to many reasons. Even by looking on the spectrum in range of frequencies that you are interested in, there is no easy way to estimate the peak:
Of course you could try with PSD/STFT and other funky methods, but this is an overkill. I can think of two, rather simple methods, for this task.
First one is based simply on the Auto Correlation Function.
Calculate the ACF
Define the minimum distance between them. Since you know that expected frequency is around 1.7Hz, then it corresponds to 0.58s. Let's make it 0.5s as the minimum distance.
Calculate the average distance between peaks found.
This gave me an approximate frequency of 1.72 Hz .
Second approach is based on the observation to your signal already has some peaks which are periodic. Therefore we can simply search for them using findpeaks function.
Define the minimum peak distance in a same way as before.
Define the minimum peak height. For example 10% of maximum peak.
Get the average difference.
This gave me an average frequency of 1.7 Hz.
Easy and fast method. There are obviously some things that can be improved, such as:
Refining thresholds
Finding both positive and negative peaks
Taking care of some missing peaks, i.e. due to low amplitude
Anyway that should get you started, instead of being stuck with crappy FFT and lazy semilogx.
Code snippet:
load walk_sound
fs = 48000;
dt = 1/fs;
x = walk_5(:,1);
x = x - mean(x);
N = length(x);
t = 0:dt:(N-1)*dt;
% FFT based
win = hamming(N);
X = abs(fft(x.*win));
X = 2*X(1:N/2+1)/sum(win);
X = 20*log10(X/max(abs(X)));
f = 0:fs/N:fs/2;
subplot(2,1,1)
plot(t, x)
grid on
xlabel('t [s]')
ylabel('A')
title('Time domain signal')
subplot(2,1,2)
plot(f, X)
grid on
xlabel('f [Hz]')
ylabel('A [dB]')
title('Signal Spectrum')
% Autocorrelation
[ac, lag] = xcorr(x);
min_dist = ceil(0.5*fs);
[pks, loc] = findpeaks(ac, 'MinPeakDistance', min_dist);
% Average distance/frequency
avg_dt = mean(gradient(loc))*dt;
avg_f = 1/avg_dt;
figure
plot(lag*dt, ac);
hold on
grid on
plot(lag(loc)*dt, pks, 'xr')
title(sprintf('ACF - Average frequency: %.2f Hz', avg_f))
% Simple peak finding in time domain
[pkst, loct] = findpeaks(x, 'MinPeakDistance', min_dist, ...
'MinPeakHeight', 0.1*max(x));
avg_dt2 = mean(gradient(loct))*dt;
avg_f2 = 1/avg_dt2;
figure
plot(t, x)
grid on
hold on
plot(loct*dt, pkst, 'xr')
xlabel('t [s]')
ylabel('A')
title(sprintf('Peak search in time domain - Average frequency: %.2f Hz', avg_f2))
Here's a nifty solution:
Take the absolute value of your raw data before taking the FFT. The data has a ton of high frequency noise that is drowning out whatever low frequency periodicity is present in the signal. The amplitude of the high frequency noise gets bigger every 1.7 seconds, and the increase in amplitude is visible to the eye, and periodic, but when you multiply the signal by a low frequency sine wave and sum everything you still end up with something close to zero. Taking the absolute value changes this, making those amplitude modulations periodic at low frequencies.
Try the following code comparing the FFT of the regular data with the FFT of abs(data). Note that I took a few liberties with your code, such as combining what I assume were the two stereo channels into a single mono channel.
x = (walk_5(:,1)+walk_5(:,2))/2; % Convert from sterio to mono
Fs = 48000; % sampling frquency
L=length(x); % length of sample
fVals=(0:L-1)*(Fs/L); % frequency range for FFT
walk5abs=abs(x); % Take the absolute value of the raw data
Xold=abs(fft(x)); % FFT of the data (abs in Matlab takes complex magnitude)
Xnew=abs(fft(walk5abs-mean(walk5abs))); % FFT of the absolute value of the data, with average value subtracted
figure;
plot(fVals,Xold/max(Xold),'r',fVals,Xnew/max(Xnew),'b')
axis([0 10 0 1])
legend('old method','new method')
[~,maxInd]=max(Xnew); % Index of maximum value of FFT
walkingFrequency=fVals(maxInd) % print max value
And plotting the FFT for both the old method and the new, from 0 to 10 Hz gives:
As you can see it detects a peak at about 1.686 Hz, and for this data, that's the highest peak in the FFT spectrum.

Should I put sin or cos to represent a sinusoidal signal?

We were asked in an exercise to create a sinusoidal signal of 0.8 seconds with an amplitude of 1 and frequency equal to 100 Hz sampled at 1000 Hz, but in the answer I found:
A=1;fs=100;fe=1000;
te=1/fe;
t=0:te:0.8;
s=A*cos(2*pi*fs*t);
plot(s);
And in another exercise: write a script for a sinusoidal signal of amplitude 1 and 200kHz frequency, over a period N = 64, sampled at 150 kHz, the answer was :
%Signal
fs=200;N=64;
ts=1/fs;
A=1;
subplot(211);
t=0:0.00001:N*ts;
x=sin(2*pi*t*fs);
plot(t,x);
title('signal sinusoidal');
%Sampling
Fe=input('Fe=');
te=1/Fe;
t1=0:te:N*te;
xe=sin(2*pi*t1*fs);
subplot(212);
stem(t1,xe);
I'm so confused. When to put a sin and when to put a cosine? My question is why they put a cosine instead of a sin? Is there some kind of rule behind it?
Thanks in advance.
Any of those choices qualifies as a sinusoid. Note that
cos(2*pi*fs*t)
is the same as
sin(2*pi*fs*t + pi/2)
In general,
sin(2*pi*fs*t + phi)
is a sinusoid for any choice of phi (as would be cos(2*pi*fs*t + phi)) . phi is the initial phase (or simply phase) of the sinusoid. To know which phi to use you would need an additional condition.

Chebyshev IIR FIlter: Got Coefficients, what next?

Here is my Matlab/Octave program
clc;
close all;
%BPF of pass 400-600Hz
fs1=300;
fp1=400;
fp2=600;
fs2=700;
samp=1500;
ap=1; %passband ripple
as=60; %stopband attenuation
%Normalizing the frequency
wp=[fp1 fp2]/(samp);
ws=[fs1 fs2]/(samp);
[N,wn]=cheb1ord_test(wp,ws,ap,as); %Generates order and cutoff parameters
[b,a]=cheby1(N,ap,wn); %Generates poles and zeros for the given order and cutoff
printf("b coeffs = %f\n",b);
printf("a coeffs = %f\n",a);
[H,W]=freqz(b,a,256);
plot(W/(2*pi),20*log10(abs(H))) %Transfer function works correctly, so coefficients are correct
%100 samples of 500hz
n = 1:100;
x=10*cos(2*pi*n*500*(1/samp));
printf("Order %d\n",N); %Depends on required ripple attenuation
figure;
subplot (2,1,1); plot(x);
y=filter(b,a,x); %**Apparently i suspect this does not work**
subplot (2,1,2); plot(y);
When i see the magnitude/frequency response, the graph is perfect and indicates 400 and 600 to be my filter cutoffs.
But however when i apply an input signal of 500Hz, i must expect to see the signal pass through the filter unharmed (as observed when i used Butterworth function), but the output is distorted and almost contains no signal
So i infer that my mistake is using the filter function to combine the chebyshev coefficients with the input signal.
If this is the problem, then how do i apply chebyshev coefficients to an input digital signal ?
For cheb1ord and cheby1 the frequencies are normalized between 0 and 1 with 1 corresponding to half the sampling frequency. You should get your wp and ws using
wp=[fp1 fp2]/(samp/2);
ws=[fs1 fs2]/(samp/2);
where samp is your sampling frequency.
I think the problem is with your x signal: I don't quite know what it is, but I can tell you what it is not and that's a 500Hz input signal.
I would define the time vector first and then apply the cos function (I assume you are sampling at 1500Hz):
f_input = 500; %Hz
t = 0:1/samp:1; % Time vector [0,1] sampled at 1500Hz
x = 10*cos(2*pi*t/f_input); % 500Hz input signal