Matlab cos signal from defined frequency course - matlab

how come the spectrogram of this code is at its max at approximately 4400Hz instead of 2400Hz in the last timestep of the stft? (frequencyCourse(end) = 100, meshingOrder = 24 -> f = 2400)
startTime = 0; %s
endTime = 30; %s
startIAS = 15; %Hz
endIAS = 100; %Hz
meshingOrder = 24;
fs = 100000; %Hz
t = startTime:1/fs:endTime-1/fs;
frequencyCourse = linspace(startIAS, endIAS, length(t));
signal = cos(2*pi*meshingOrder*frequencyCourse.*t);
spectrogram(signal, hanning(2^13), 0, 2^14, fs, 'yaxis')
Here's a picture:
It works fine as long as I use chirp instead of my self constructed signal, it's not an option though, since more specific courses are to come.

Summary
The problem is that the instantaneous phase is the integral of the instantaneaous frequency with respect to time, not the instantaneous frequency multiplied by time.
You should compute the signal as
signal = cos(2*pi*meshingOrder*cumtrapz(t, frequencyCourse));
What your code does
In your example, you seem to want to generate a linear chirp with initial frequency meshingOrder*startIAS and final frequency meshingOrder*endIAS. But that's not the code is doing.
In your computed signal, the instantaneous phase is the argument to the cos function:
2*pi*meshingOrder*frequencyCourse.*t
Since the variable frequencyCourse increases from meshingOrder*startIAS at start time (which is 0) to meshingOrder*endIAS at end time, this can be expressed as
2*pi*(A+B*t).*t
where A = meshingOrder*startIAS and B = meshingOrder*(endIAS-startIAS)/endTime. Differentiating the instantanous phase with respect to t gives an instantaneous frequency
A + 2*B*t
that is
meshingOrder*startIAS + 2*meshingOrder*(endIAS-startIAS)/endTime * t
As you can see, the problem is the factor 2 here. At end time the instantaneous frequency is
meshingOrder*startIAS + 2*meshingOrder*(endIAS-startIAS)
that is
2*meshingOrder*endIAS - meshingOrder*startIAS
In your example this is 4440 Hz, which is in accordance with your observed value.
What the code should do
For a linear chirp (or a chirp with any other simple frequency variation, such as a quadratic or exponential) you could compute the correct instantaneous phase that gives rise to the desired instantaneous frequency. See for example here. This is also what the chirp function internally does.
But you seem to be want to deal with arbitrary frequency courses. To do that, given arbitrary t, just compute the argument of the cos as the cumulative integral of frequencyCourse with respect to t. This is easily done with cumtrapz:
signal = cos(2*pi*meshingOrder*cumtrapz(t, frequencyCourse));
Changing this line in your example gives the following figure, which has the expected frequency variation from 360 Hz to 2400 Hz:

Related

How to get the full Fourier spectrum in MATLAB?

I created a 6-bit quantizer and passed a signal through it, but when I plot the DFT, it peaks at 200 MHz and then stops; I'm not seeing the whole spectrum. What's preventing me in my code from getting the rest of the points at the higher frequencies?
Here is my code:
bits = 6; %6-bit
fs = 400e6; %sampling frequency
amp = 1; %amplitude
f = 200e6; %actual frequency
vpp = 2; peak-to-peak voltage
LSB = vpp/(2^bits); %least-significant bit
cycles = 1000;
duration = cycles/f;
values = 0:1/fs:duration;
party = LSB:LSB:(vpp-LSB); %partition
blocker = 0:1:(2^bits - 1); %codebook
biblocker = fliplr(decimaltobinary(blocker)); %I created a function that converts decimal to binary
qtone = amp + amp*sin(2*pi*f*values); %tone
[index, q] = quantization(qtone,party,blocker); %I created a quantizing function
ftq = fft(q)/length(q); % Fourier Transform (Scaled)
qf = linspace(0, 1, fix(length(q))/2+1)*(fs/2); % Frequency Vector
qi = 1:length(qf); % Index Vector
qa = abs(ftq(qi))*2/.7562;
figure
plot(qf/1e6, qa) % One-Sided Amplitude Plot
xlim([100 500]);
xlabel('Frequency [MHz]')
ylabel('Amplitude')
Here is what I get:
Since you selected your sampling frequency, fs = 400e6 i.e. 400 MHz, you can only observe the spectrum up to 200e6, half of the sampling frequency. You can read the theory behind it using Nyquist Sampling Theorem.
As a solution you need to set your twice the frequency you want to observe on the spectrum. It is impossible to observe whole frequency, you need to set a finite frequency limit.
For base-band sampled data, everything in the spectrum above half the sample rate is redundant, just aliases for the spectrum below half the sample rate. So there's no need to display the same spectrum repeated.
When sampling for a finite length of time (less than the age of the Earth, etc.), you have to sample at a rate higher than twice the highest frequency in the signal. 2X (400Msps for a 200MHz signal) often won't work.
qf = linspace(0, 1, fix(length(q)))*(fs);

FFT: extract amplitude ratio when signal is not "straight"

I need to do frequency analysis on an integrating process where input is torque and output is position. If the input is a sinusoid, the output becomes something like this:
The code I use to extract amplitude ratio and phase looks like this:
freq = 40;
freq_rad = freq * 2 * pi
phase_offset_rad = 30 * pi / 180
gain = 0
fs = 500;
L = 100;
t = (0:L-1)*(1/fs);
in = 2 * sin(freq * 2 * pi * t);
pos_in = [];
vel = 0;
pos = 0;
for i = 1:length(t)
vel = vel + in(i);
pos = pos + vel;
pos_in = [pos_in; pos];
end
out = pos_in;
%out = (2 + gain) * sin(freq * 2 * pi * t + phase_offset_rad);
fft_in = fft(in);
fft_out = fft(out);
[mag_in idx_in] = max(abs(fft_in));
[mag_out idx_out] = max(abs(fft_out));
phase = angle(fft_out(idx_out)) - angle(fft_in(idx_in))
phase_deg = phase / (pi / 180)
ratio = mag_out / mag_in
If I run it on perfectly straight sinusoidal signals then it works perfectly. But as soon as I add a distortion like above, both phase and amplitude values are not right. I think I need to somehow "flatten" the signal. But I'm not sure how to extract correct amplitude from it. What is the amplitude? I would say in the output it is ~45 measured from one "plateau" to the next since that's how far the thing moves. That would be ratio of ~22.5. However the result of the calculation is 196.
Maybe I'm thinking about it wrong? I want to eventually derive a transfer function from the torque input to the positional output using experimental data. Perhaps someone can show how to do that instead?
I have been thinking that what I could do is record amplitude ratio and phase and then make a bode plot and from that easily extract the transfer function. I have so far not been able to get as far as getting a bode plot out of running tests with varying input frequencies.
Because FFT assumes you performing frequency analysis of perfectly periodical signal (exactly one period of the signal) your fft(out) will contain very big power disturbance (see the Periodicity and Shift theorem).
I believe, your case you can avoid of FFT analysis artifacts by performing some system modification. Instead of estimating the transfer function of the system, you can estimate the transfer function of the system + filter. I.e. you have to pass the out signal of your system through the high-pass filter:
out = filter([1 -1], 1, out);
Then, you can perform your analysis.
The frequency response of the filter you can estimate trough the freqz function (or just H = fft([1 -1], length(out)); in my case). Then you can eliminate the filter influence in the frequency domain by inverse response applying fft_out = fft_out ./ H(:);. Also, do not forget to nullify the 0-th frequency fft_out(1) = 0; before maximum estimation.
By the way, the phase difference estimation of the different frequencies looks strange (in your code phase = angle(fft_out(idx_out)) - angle(fft_in(idx_in))). Looks like you must use idx_in (or idx_out, depends what estimation is more reliable) for bout angles.
Note: this answer is not the complete guide and some real life enhancements possible will be required.
P.S. Try applying windowing for frequency response estimation in real world applications (for example the Hamming window).
P.P.S Try ask your question at the https://dsp.stackexchange.com/
Update:
In some cases, instead of neglecting filter influence, you can perform same linear input signal transformation of the input signal: in = filter([1 -1], 1, in);

Generating a sine signal with time dependent frequency in Matlab

I want to generate a sine signal y(t) with a time dependent frequency f(t) in Matlab.
I've already tried to realise this using the sine function of Matlab:
h = 0.0001;
npoints = 150/h;
for i = 1:1:npoints
f(i) = 2 - 0.01*i*h;
y(i) = 0.5*sin(2*3.1415*f(i)*i*h)+0.5;
end
where the frequency is decreasing with time and h is the time step width.
My problem:
The signal y(t) doesn't look I expected it to look like. There appears a bump in the amplitude at a distinct time (have a look at the plot below).
Does anyone know why this happens and how to generate this sine signal correctly?
what about
y(i) = 0.5*real(exp(1i*2*pi*f(i)*i*h))+0.5;
You will get the plot below
If you just need a chirp signal starting from 2Hz down to 0.5Hz, the following should do the job
f_start = 2; % start frequency
f_end = 0.5; % end frequency
endtime = 150; % seconds
timestep = 0.0001;
times = timestep:timestep:endtime;
y = chirp(times,f_start,endtime,f_end);
and if you plot it you get
figure(2);plot(times,y);
You can achieve the same manually using below
f_start = 2; % start frequency
f_end = 0.5; % end frequency
timestep = 0.0001;
T = 150;
rate_of_change = (f_start - f_end)/T;
times = timestep:timestep:T;
y = sin(2*pi*(f_start*times - times.^2*rate_of_change/2));
It might be useful to read the following Wikipedia page on Chirp signal.
At 100 you have sin(2*pi*N), which is 0. Change f a little bit, say to 2.0123-... and it goes to the top.
As for the general probably unexpected shape, consider what function you are using in the end (= substitute f back in the formula). You see that you have something of the form y = ...sin(Ai-B*i^2)..., which has a minimum at 100.
The easiest solution here is to simply offset frequency a little more, and use something like f(i) = 3.1 - ..., which has a minimum outside of your considered range.
It looks like there isn't actually a "bump in frequency", but at the 100 value on the x-axis the entire signal is shifted by 180 degrees. Bear in mind that the amplitude still reaches 0 and does not become smaller (e.g. from 0.25 to 0.75)
This is because the i value becomes so high that the value of f(i) changes sign.
Another indicator of this is that the frequency starts to increase again after the shift instead of gradually becoming even lower.
Why do you start off with the value of 2 in f(i)?
Sorry for asking for clarification here, but I cannot post it as a comment.

identifying phase shift between signals

I have generated three identical waves with a phase shift in each. For example:
t = 1:10800; % generate time vector
fs = 1; % sampling frequency (seconds)
A = 2; % amplitude
P = 1000; % period (seconds), the time it takes for the signal to repeat itself
f1 = 1/P; % number of cycles per second (i.e. how often the signal repeats itself every second).
y1 = A*sin(2*pi*f1*t); % signal 1
phi = 10; % phase shift
y2 = A*sin(2*pi*f1*t + phi); % signal 2
phi = 15; % phase shift
y3 = A*sin(2*pi*f1*t + phi); % signal 3
YY = [y1',y2',y3'];
plot(t,YY)
I would now like to use a method for detecting this phase shift between the waves. The point of doing this is so that I can eventually apply the method to real data and identify phase shifts between signals.
So far I have been thinking of computing the cross spectra between each wave and the first wave (i.e. without the phase shift):
for i = 1:3;
[Pxy,Freq] = cpsd(YY(:,1),YY(:,i));
coP = real(Pxy);
quadP = imag(Pxy);
phase(:,i) = atan2(coP,quadP);
end
but I'm not sure if this makes any sense.
Has anyone else done something similar to this? The desired outcome should show a phase shift at 10 and 15 for waves 2 and 3 respectively.
Any advice would be appreciated.
There are several ways that you can measure the phase shift between signals. Between your response, the comments below your response, and the other answers, you've gotten most of the options. The specific choice of technique is usually based on issues such as:
Noisy or Clean: Is there noise in your signal?
Multi-Component or Single-Component: Are there more than one type of signal within your recording (multiple tones at multiple frequencies moving in different directions)? Or, is there just a single signal, like in your sine-wave example?
Instantaneous or Averaged: Are you looking for the average phase lag across your entire recording, or are you looking to track how the phase changes throughout the recording?
Depending on your answer to these questions, you could consider the following techniques:
Cross-Correlation: Use the a command like [c,lag]=xcorr(y1,y2); to get the cross-correlation between the two signals. This works on the original time-domain signals. You look for the index where c is maximum ([maxC,I]=max(c);) and then you get your lag value in units of samples lag = lag(I);. This approach gives you the average phase lag for the entire recording. It requires that your signal of interest in the recording be stronger than anything else in your recording...in other words, it is sensitive to noise and other interference.
Frequency Domain: Here you convert your signals into the frequency domain (using fft or cpsd or whatever). Then, you'd find the bin that corresponds to the frequency that you care about and get the angle between the two signals. So, for example, if bin #18 corresponds to your signal's frequency, you'd get the phase lag in radians via phase_rad = angle(fft_y1(18)/fft_y2(18));. If your signals have a constant frequency, this is an excellent approach because it naturally rejects all noise and interference at other frequencies. You can have really strong interference at one frequency, but you can still cleanly get your signal at another frequency. This technique is not the best for signals that change frequency during the fft analysis window.
Hilbert Transform: A third technique, often overlooked, is to convert your time-domain signal into an analytic signal via the Hilbert transform: y1_h = hilbert(y1);. Once you do this, your signal is a vector of complex numbers. A vector holding a simple sine wave in the time domain will now be a vector of complex numbers whose magnitude is constant and whose phase is changing in sync with your original sine wave. This technique allows you to get the instantaneous phase lag between two signals...it's powerful: phase_rad = angle(y1_h ./ y2_h); or phase_rad = wrap(angle(y1_h) - angle(y2_h));. The major limitation to this approach is that your signal needs to be mono-component, meaning that your signal of interest must dominate your recording. Therefore, you may have to filter out any substantial interference that might exist.
For two sinusoidal signal the phase of the complex correlation coefficient gives you what you want. I can only give you an python example (using scipy) as I don't have a matlab to test it.
x1 = sin( 0.1*arange(1024) )
x2 = sin( 0.1*arange(1024) + 0.456)
x1h = hilbert(x1)
x2h = hilbert(x2)
c = inner( x1h, conj(x2h) ) / sqrt( inner(x1h,conj(x1h)) * inner(x2h,conj(x2h)) )
phase_diff = angle(c)
There is a function corrcoeff in matlab, that should work, too (The python one discard the imaginary part). I.e. c = corrcoeff(x1h,x2h) should work in matlab.
The Matlab code to find relative phase using cross-correlation:
fr = 20; % input signal freq
timeStep = 1e-4;
t = 0:timeStep:50; % time vector
y1 = sin(2*pi*t); % reference signal
ph = 0.5; % phase difference to be detected in radians
y2 = 0.9 * sin(2*pi*t + ph); % signal, the phase of which, is to be measured relative to the reference signal
[c,lag]=xcorr(y1,y2); % calc. cross-corel-n
[maxC,I]=max(c); % find max
PH = (lag(I) * timeStep) * 2 * pi; % calculated phase in radians
>> PH
PH =
0.4995
With the correct signals:
t = 1:10800; % generate time vector
fs = 1; % sampling frequency (seconds)
A = 2; % amplitude
P = 1000; % period (seconds), the time it takes for the signal to repeat itself
f1 = 1/P; % number of cycles per second (i.e. how often the signal repeats itself every second).
y1 = A*sin(2*pi*f1*t); % signal 1
phi = 10*pi/180; % phase shift in radians
y2 = A*sin(2*pi*f1*t + phi); % signal 2
phi = 15*pi/180; % phase shift in radians
y3 = A*sin(2*pi*f1*t + phi); % signal 3
The following should work:
>> acos(dot(y1,y2)/(norm(y1)*norm(y2)))
>> ans*180/pi
ans = 9.9332
>> acos(dot(y1,y3)/(norm(y1)*norm(y3)))
ans = 0.25980
>> ans*180/pi
ans = 14.885
Whether or not that's good enough for your "real" signals, only you can tell.
Here is the little modification of your code: phi = 10 is actually in degree, then in sine function, phase information is mostly expressed in radian,so you need to change deg2rad(phi) as following:
t = 1:10800; % generate time vector
fs = 1; % sampling frequency (seconds)
A = 2; % amplitude
P = 1000; % period (seconds), the time it takes for the signal to repeat itself
f1 = 1/P; % number of cycles per second (i.e. how often the signal repeats itself every second).
y1 = A*sin(2*pi*f1*t); % signal 1
phi = deg2rad(10); % phase shift
y2 = A*sin(2*pi*f1*t + phi); % signal 2
phi = deg2rad(15); % phase shift
y3 = A*sin(2*pi*f1*t + phi); % signal 3
YY = [y1',y2',y3'];
plot(t,YY)
then using frequency domain method as mentioned chipaudette
fft_y1 = fft(y1);
fft_y2 = fft(y2);
phase_rad = angle(fft_y1(1:end/2)/fft_y2(1:end/2));
phase_deg = rad2deg(angle(fft_y1(1:end/2)/fft_y2(1:end/2)));
now this will give you a phase shift estimate with error = +-0.2145
If you know the frequency and just want to find the phase, rather than use a full FFT, you might want to consider the Goertzel algorithm, which is a more efficient way to calculate the DFT for a single frequency (an FFT will calculate it for all frequencies).
For a good implementation, see: https://www.mathworks.com/matlabcentral/fileexchange/35103-generalized-goertzel-algorithm and https://asp-eurasipjournals.springeropen.com/track/pdf/10.1186/1687-6180-2012-56.pdf
If you use an AWGN signal with delay and apply your method it works, but if you are using a single tone frequency estimation will not help you. because there is no energy in any other frequency but the tone. You better use cross-correlation in the time domain for this - it will work better for a fixed delay. If you have a wideband signal you can use subbands domain and estimate the phase from that (it is better than FFT due to low cross-frequency dependencies).

Difficulty understanding the phase calculated by the FFT. Short matlab demo to illustrate

I'm testing the phase output of an fft of a sin signal and a cos signal.
The script below creates the signals and performs an FFT on them. Bins who's amplitude is below a threshold are zeroed for the phase spectrum because I am only interested in the phase of the signals.
% 10khz 10 second long time interval
t = 0:1 / 10000:10;
%1khz cos
c = cos(2 * pi * 1000 .* t);
%1khz sin
s = sin(2 * pi * 1000 .* t);
%ffts
C = fft(c)/length(c);
S = fft(s)/length(s);
%magnitude and phases of ffts
CA = abs(C); %cos magnitude
SA = abs(S); %sin magnitude
Cthresh = max(CA) * 0.5;
Sthresh = max(SA) * 0.5;
%find all indeces below the threshold
Crange = find(CA < Cthresh);
Srange = find(SA < Sthresh);
%set the indeces below the threshold to 0 - phase will be meaningless for
%noise values
CP = angle(C);
CP(Crange) = 0;
SP = angle(S);
SP(Srange) = 0;
If you plot CP - the phase of the cos - you will get a phase of 0.3142 in the bins corresponding to the frequency of the cos signal and zeros elsewhere. This is pi/10. I'm expecting to get pi. Why is this?
If you plot SP you get values of 1.2566. I'm expecting to get pi/2 or 1.5708. 80% of the expected value. What is causing these errors?
If your input signal is not perfectly periodic in the FFT aperture length (an exact integer number of full periods), the sinusoids will be discontinuous across the ends of the FFT aperture. Thus you will get a phase that is the average of the two different phases at both ends of the FFT input vector.
If you want a more sensible phase, reference the phase of your sinusoids to the center of the FFT input vector, and do an fft shift before the FFT. This will result in a continuous sinusoid at the zero phase reference position, with a single phase instead of a weird average value.
Also note that matlab may reference the phase to the second point in a sampled sinusoid, e.g. vectorElement[i=1], not the first, vectorElement[i=0]. This will have a phase of pi/10 for a sinusoid of period = 20 samples.
The issue you have is exactly what hotpaw2 has stated. You have 100001 samples in t, so you do not have a perfectly periodic signal, therefore you have leakage. That means that you've got a sin()/sin() function from your implicit rectangular window convolved with your solution. That's what changes your phase.
If instead you try the following:
t = 0:1 / 10000:9.9999;
c = cos(2 * pi * 1000 .* t);
%1khz sin
s = sin(2 * pi * 1000 .* t);
%ffts
C = fft(c)/length(c);
S = fft(s)/length(s);
you would find that the phase of the cosine is zero (which is what you would expect) and that the phase of sine is pi/2.
Performing a linear shift in the time domain (using fftshift) will merely introduce a linear phase term in the frequency domain, and will not resolve the original problem.
In practice, rather than trying to set the length of the sequence precisely to match the period of the signal, windowing should be applied if the signal is to be examined in the frequency domain. In that case you really should make sure that your signals are appropriately aligned so that the window attenuates the end points, thus smoothing out the discontinuity. This has the effect of broadening the main lobe of the FFT, but it also controls the leakage.