Correct frequency axis using FFT - matlab

How can I get the correct frequency vector to plot using the FFT of MATLAB?
My problem:
N = 64;
n = 0:N-1;
phi1 = 2*(rand-0.5)*pi;
omega1 = pi/6;
phi2 = 2*(rand-0.5)*pi;
omega2 = 5*pi/6;
w = randn(1,N); % noise
x = 2*exp(1i*(n*omega1+phi1))+4*sin(n*omega2+phi2);
h = rectwin(N).';
x = x.*h;
X = abs(fft(x));
Normally I'd do this :
f = f = Fs/Nsamples*(0:Nsamples/2-1); % Prepare freq data for plot
The problem is this time I do not have a Fs (sample frequency).
How can I do it correctly in this case?

If you don't have a Fs, simply set it to 1 (as in one sample per sample). This is the typical solution I've always used and seen everybody else use. Your frequencies will run from 0 to 1 (or -0.5 to 0.5), without units. This will be recognized by everyone as meaning "periods per sample".
Edit
From your comment I conclude that you are interested in radial frequencies. In that case you want to set your plot x-axis to
omega = 2*pi*f;

Related

Plotting a collection of sine waves

I have the following code:
Fs = 1000;
T = 1/Fs;
L = 1000;
t = (0:L-1)*T;
k = 25:1:50;
m = 1:1:25;
where k and m are corresponding. I want to plot the 25 sine waves resulting from:
x = m*sin(2*pi*k*t);
I thought about doing it using a for loop that takes one value from m and k each time, but I'm unsure how to proceed.
Below is a very basic plotting solution. You will notice that it's very difficult to see what's going on in the plot, so you might want to consider other ways to present this data.
function q45532082
Fs = 1000;
T = 1/Fs;
L = 1000;
t = (0:L-1)*T;
k = 26:1:50;
m = 1:1:25;
%% Plotting
assert(numel(m) == numel(k)); % We make sure that the number of elements is the same.
figure(); hold on; % "hold" is needed if you want to see all curves at the same time.
for ind1 = 1:numel(m)
plot(t,m(ind1)*sin(2*pi*k(ind1)*t));
end
This is the result:
Note that the number of elements in k and m in your code is different, so I had to change it.
Using the functionality of plot you can also plot all sine waves without a loop:
Fs = 1000;
T = 1/Fs;
L = 1000;
t = (0:L-1)*T;
k = 26:1:50;
m = 1:1:25;
x = m.*sin(2.*pi.*bsxfun(#times,t.',k)); %this results in an L*25 matrix, each column is data of one wave
% or, if you have version 2016b or newer:
% x = m.*sin(2.*pi.*t.'*k);
plot(t,x) % plot all sines at ones
and as #Dev-iL noted, I also had to change k.
The result with L = 1000 is too crowded, so I plot it here with L = 50:

Matlab: convolution with bandpass filter does not cut the unwanted frequencies

I have a 6ms-long signal with three frequency components sampled at 60kHz:
fs = 60000;
T = 0.006;
t = 0:1/fs:T;
x = 0.3*sin(2*pi*2000*t) + sin(2*pi*5000*t) + 0.4*sin(2*pi*8000*t);
I have a bandpass filter with impulse response being the difference between two sinc functions:
M = 151;
N = 303;
n = 0:(N-1);
h = (sin(0.5760*pi*(n-M))-sin(0.3665*pi*(n-M)))./pi./(n-M);
h(n==M) = 0.2094;
I designed a function that convolves the input with filter:
function y = fir_filter(h,x)
y = zeros(1,length(x)+length(h)-1);
for i = 1:length(x)
for j = 1:length(h)
y(i+j-1) = y(i+j-1) + x(i)*h(length(h)-j);
end
end
And then applied the filter:
y = fir_filter(h,x);
This produced strange results:
figure(21)
ax1 = subplot(311);
plot(x);
title('Input Signal');
ax2 = subplot(312);
plot(h);
title('FIR');
ax3 = subplot(313);
plot(y);
title('Output Signal');
linkaxes([ax1,ax2,ax3],'x')
ax2.XLim = [0,length(y)];
Since the filter is bandpass, only one frequency component was expected to survive.
I tried to use yy = filter(h,1,[x,zeros(1,length(h)-1)]); and yyy = conv(h,x); and got the same results.
Please, can anybody explain me what I am doing wrong? Thank you!
Your passband does not cover any of the three signal frequency components. This can be seen directly on the graph (the second figure, containing the impulse response, has too fast variations compared with the signal). Or it can be seen from the numbers 0.5760 and 0.3655 in
h = (sin(0.5760*pi*(n-M))-sin(0.3665*pi*(n-M)))./pi./(n-M);
Why did you choose those numbers? The normalized frecuencies of the signal are [2 5 8]/60, that is, 0.0333 0.0833 0.1333. They are all below the passband [.3665 .5760]. As a result, the filter greatly attenuates the three components of the input signal.
Say you want to isolate the center frequency component (f = 5000 Hz, or f/fs = 0.08333 normalized frequency). You need a bandpass filter than lets that frequency through, and rejects the others. So you would use for example a normalized passband [.06 .1]:
h = (sin(0.06*pi*(n-M))-sin(0.1*pi*(n-M)))./pi./(n-M);
h(n==M) = (h(n==M+1)+h(n==M-1))/2; %// auto adjustment to avoid the 0/0 sample
A second problem with your code is that it gives two errors because you index h with 0. To solve this, change
n = 0:(N-1);
to
n = 1:N;
and
y(i+j-1) = y(i+j-1) + x(i)*h(length(h)-j);
to
y(i+j-1) = y(i+j-1) + x(i)*h(length(h)-j+1);
So, the modified code to isolate the central frequency component is:
fs = 60000;
T = 0.006;
t = 0:1/fs:T;
x = 0.3*sin(2*pi*2000*t) + sin(2*pi*5000*t) + 0.4*sin(2*pi*8000*t);
M = 151;
N = 303;
n = 1:N; %// modified
h = (sin(0.06*pi*(n-M))-sin(0.1*pi*(n-M)))./pi./(n-M); %// modified
h(n==M) = (h(n==M+1)+h(n==M-1))/2; %// modified
y = zeros(1,length(x)+length(h)-1);
for i = 1:length(x)
for j = 1:length(h)
y(i+j-1) = y(i+j-1) + x(i)*h(length(h)-j+1); %// modified
end
end
figure(21)
ax1 = subplot(311);
plot(x);
title('Input Signal');
ax2 = subplot(312);
plot(h);
title('FIR');
ax3 = subplot(313);
plot(y);
title('Output Signal');
linkaxes([ax1,ax2,ax3],'x')
ax2.XLim = [0,length(y)];
The result is as follows.
As can be seen, only the central frequency component is present in the output signal.
It's also observed that the envelope of the output signal is not constant. That's because the duration of the input signal is comparable to the filter lenght. That is, you are seeing the transient response of the filter. Note that the envelop rise times is approximately the length of the filter's impulse response h.
It's interesting to note an interesting trade-off here (signal processing is full of trade-offs). To make the transient shorter you could use a wider passband (filter length is inversely proportional to passband). But then the filter would be less selective, that is, it would have less attenuation at the unwanted frequencies. For example, see the result with passband [.04 .12]:
As expected, the transient is now shorter, but the desired frequency is less pure: you can see some "modulation" caused by remains of the other frequencies.

Low pass filter implementation Correct or wrong?

I've been working on 2 sensors signals measuring vibrations of a rotating shaft. Since there is residual noise in the signal. I tried to filter it by detrending, zero padding and applying low pass filter. Below I'm attaching the graphs of the signal before and after filtering. There is a huge variation in the signal after filtering that makes me think if I'm really doing it in the right way.
My Matlab code is
X = xlsread(filename,'F:F');
Y = xlsread(filename,'G:G');
%Calculate frequency axis
fs = 1e6 ; % Sampling frequency (Hz)
NFFT = 2^nextpow2(length(X)); % Zero padding to nearest N power 2
df = fs/NFFT;
dt = 1/df;
%Frequency Axis defintion
f = (-(fs-df)/2:df:(fs-df)/2)';
X(2^ceil(log2(length(X))))=0;
Y(2^ceil(log2(length(Y))))=0;
%calculate time axis
T = (dt:dt:(length(X)*dt))';
subplot(2,2,1)
plot(T,X);
xlabel('Time(s)')
ylabel('X amplitude')
title('X signal before filtering')
subplot(2,2,2)
plot(T,Y);
xlabel('Time(s)')
ylabel('Y amplitude')
title('Y signal before filtering')
X = detrend(X,0); % Removing DC Offset
Y = detrend(Y,0); % Removing DC Offset
% Filter parameters:
M = length(X); % signal length
L = M; % filter length
fc = 2*(38000/60); % cutoff frequency
% Design the filter using the window method:
hsupp = (-(L-1)/2:(L-1)/2);
hideal = (2*fc/fs)*sinc(2*fc*hsupp/fs);
h = hamming(L)' .* hideal; % h is our filter
% Zero pad the signal and impulse response:
X(2^ceil(log2(M)))=0;
xzp = X;
hzp = [ h zeros(1,NFFT-L) ];
% Transform the signal and the filter:
X = fft(xzp);
H = fft(hzp)';
X = X .* H;
X = ifft(X);
relrmserrX = norm(imag(X))/norm(X); % checked... this for zero
X = real(X)';
% Zero pad the signal and impulse response:
Y(2^ceil(log2(M)))=0;
xzp = Y;
hzp = [ h zeros(1,NFFT-L) ];
% Transform the signal and the filter:
Y = fft(xzp);
H = fft(hzp)';
Y = Y .* H;
Y = ifft(Y);
relrmserrY = norm(imag(Y))/norm(Y); % check... should be zero
Y = real(Y)';
I plotted the after filtering and as you can see in the picture there is a clear deviation. I want to only filter noise but the signal seem to loose other components and I'm little confused if thats the right way of doing filtering.
Any suggestion, hints or ideas would be helpful.
In the end I want to plot X vs Y to give orbits of the shaft vibration. Please also find below another picture of the unfiltered and filtered orbit. As you can see in the picture there is also change in the orbits from the original one (left image with lot of noise).
P.S.: I don't have DSP tool box
There is no problem with your FFT and IFFT.
You can test your code with a simple sine wave, with a very low frequency. The final output should be (almost) the same sine wave, since you are low-pass filtering it.
You've defined X (and Y) from 0 to some value. However, you've defined H from - (L-1)/2 to some positive value. Mathematically, this is fine, but you are simply taking an fft of H. Matlab thinks this has the same time-scale as X, when you multiply the ffts together!
So, in reality, you've taken the fft of XHfft(delta(t-d)), where d is the resulting time-shift. You can either undo this time-shift in the frequency domain, or the time-domain.

Gradient descent in linear regression goes wrong

I actually want to use a linear model to fit a set of 'sin' data, but it turns out the loss function goes larger during each iteration. Is there any problem with my code below ? (gradient descent method)
Here is my code in Matlab
m=20;
rate = 0.1;
x = linspace(0,2*pi,20);
x = [ones(1,length(x));x]
y = sin(x);
w = rand(1,2);
for i=1:500
h = w*x;
loss = sum((h-y).^2)/m/2
total_loss = [total_loss loss];
**gradient = (h-y)*x'./m ;**
w = w - rate.*gradient;
end
Here is the data I want to fit
There isn't a problem with your code. With your current framework, if you can define data in the form of y = m*x + b, then this code is more than adequate. I actually ran it through a few tests where I define an equation of the line and add some Gaussian random noise to it (amplitude = 0.1, mean = 0, std. dev = 1).
However, one problem I will mention to you is that if you take a look at your sinusoidal data, you define a domain between [0,2*pi]. As you can see, you have multiple x values that get mapped to the same y value but of different magnitude. For example, at x = pi/2 we get 1 but at x = -3*pi/2 we get -1. This high variability will not bode well with linear regression, and so one suggestion I have is to restrict your domain... so something like [0, pi]. Another reason why it probably doesn't converge is the learning rate you chose is too high. I'd set it to something low like 0.01. As you mentioned in your comments, you already figured that out!
However, if you want to fit non-linear data using linear regression, you're going to have to include higher order terms to account for the variability. As such, try including second order and/or third order terms. This can simply be done by modifying your x matrix like so:
x = [ones(1,length(x)); x; x.^2; x.^3];
If you recall, the hypothesis function can be represented as a summation of linear terms:
h(x) = theta0 + theta1*x1 + theta2*x2 + ... + thetan*xn
In our case, each theta term would build a higher order term of our polynomial. x2 would be x^2 and x3 would be x^3. Therefore, we can still use the definition of gradient descent for linear regression here.
I'm also going to control the random generation seed (via rng) so that you can produce the same results I have gotten:
clear all;
close all;
rng(123123);
total_loss = [];
m = 20;
x = linspace(0,pi,m); %// Change
y = sin(x);
w = rand(1,4); %// Change
rate = 0.01; %// Change
x = [ones(1,length(x)); x; x.^2; x.^3]; %// Change - Second and third order terms
for i=1:500
h = w*x;
loss = sum((h-y).^2)/m/2;
total_loss = [total_loss loss];
% gradient is now in a different expression
gradient = (h-y)*x'./m ; % sum all in each iteration, it's a batch gradient
w = w - rate.*gradient;
end
If we try this, we get for w (your parameters):
>> format long g;
>> w
w =
Columns 1 through 3
0.128369521905694 0.819533906064327 -0.0944622478526915
Column 4
-0.0596638117151464
My final loss after this point is:
loss =
0.00154350916582836
This means that our equation of the line is:
y = 0.12 + 0.819x - 0.094x^2 - 0.059x^3
If we plot this equation of the line with your sinusoidal data, this is what we get:
xval = x(2,:);
plot(xval, y, xval, polyval(fliplr(w), xval))
legend('Original', 'Fitted');

how to get a correct spectrogram of a non-stationary signal?

in the below code i am trying to get the spectrogram of the non-stationary signalx
after running the code, i expected to see some thing like the posted inage "image_2" , frequency vs time representation. but the resut of the posted code is image_1.
can any one please guide me to get the correct spectrogram?
Code
% Time specifications:
Fs = 8000; % samples per second
dt = 1/Fs; % seconds per sample
StopTime = 1; % seconds
t = (0:dt:StopTime-dt); % seconds
t1 = (0:dt:.25);
t2 = (.25:dt:.50);
t3 = (.5:dt:.75);
t4 = (.75:dt:1);
%get a full-length example of each signal component
x1 = (10)*sin(2*pi*100*t);
x2 = (10)*sin(2*pi*200*t);
x3 = (10)*sin(2*pi*300*t);
x4 = (10)*sin(2*pi*400*t);
%construct a composite signal
x = zeros(size(t));
I = find((t >= t1(1)) & (t <= t1(end)));
x(I) = x1(I);
I = find((t >= t2(1)) & (t <= t2(end)));
x(I) = x2(I);
I = find((t >= t3(1)) & (t <= t3(end)));
x(I) = x3(I);
I = find((t >= t4(1)) & (t <= t4(end)));
x(I) = x4(I);
NFFT = 2 ^ nextpow2(length(t)); % Next power of 2 from length of y
Y = fft(x, NFFT);
f = Fs / 2 * linspace(0, 1, NFFT/2 + 1);
figure;
plot(f(1:200), 2 * abs( Y( 1:200) ) );
T = 0:.001:1;
spectrogram(x,10,9);
ylabel('Frequency');
axis(get(gcf,'children'), [0, 1, 1, 100]);
result of the posted code: Spectrogram_Image_1:
what i am trying to get: Image_2:
Update_1, image
Code:
%now call the spectrogram
spectrogram(x, window, noverlap, Nfft, Fs);
ylabel('Frequency');
axis(get(gcf,'children'), [0, 1]);
First, as with the first time that you asked this question, have you plotted your data in the time-domain (ie, plot(t, x)) and zoomed in on the transitions to ensure that your signal is what you think it is? Does it have the four different periods with distinct frequencies as you intend?
Assuming that it does, I'm pretty sure that your problem is that your spectrogram call is not doing what you want. I think that you are only getting an NFFT of 10, which means that your bins are 800 Hz wide, which is insufficient for resolving your frequencies that are only 100 Hz apart.
In my opinion, you should specify more parameters so that you know what it is doing. You'd specify an Nfft that would give the frequency resolution that you need. Something with more resolution than 100 Hz (let's try 25 Hz), but not requiring so many points that it is longer than the duration where you have stable frequencies (so, less than 0.25 sec, which means less than 2000 points).
To see how to specify the length of the FFT, I looked at the documentation: http://www.mathworks.com/help/signal/ref/spectrogram.html
Based on the docs I'd try the five parameter version: spectrogram(x,window,noverlap,nfft,fs)
For you code, where Fs and x are as you have already defined them, the spectrogram call would look like:
%define FFT parameters
des_df_Hz = 25; %desired frequency resolution for the display, Hz
Nfft = round(FS / des_df_Hz); %general rule for FFT resolution
Nfft = 2*Nfft; %double the bins to account for spreading due to windowing
Nfft = 2*round(0.5*Nfft); %make Nfft an even number
window = Nfft; %make your window the same length as your FFT
noverlap = round(0.95); %overlap a lot to make the plot pretty
%now call the spectrogram
spectrogram(x, window, noverlap, Nfft, Fs,'yaxis');