Matlab findpeaks unable to detect peak - matlab

I have a 1d signal with a very clear peak, which I wish to characterise. For this, Matlab recommends the findpeak() function. I used it, but it could not detect any peaks.
Can anyone tell me why the peak detection fails, and how I could correct this? Is it because of the slight asymmetry, or because of its location close to the origin?
Here is the signal:
-0.749429547824983,-1.48893649456578,-3.69142404184161,-5.90518397007934,-8.94050442864937,-13.9397113416771,-18.6485000060921,-26.171050819591,-33.8321611238473,-42.6887187100784,-52.7848046259627,-64.0885716722416,-76.4819754427361,-89.7534943093904,-103.596636684011,-117.616848525362,-137.728485864984,-149.968644032202,-165.760526765682,-173.438446162355,-177.794367330021,-180.665504657482,-181.587837865151,-182.05669036563,-181.953081445756,-181.224964655595,-179.466956009378,-174.935326783301,-166.108729320377,-154.8188727578,-138.989450390713,-124.745660884327,-110.75406648731,-97.5101283485409,-85.3020579114654,-75.9579975036876,-67.4601854156705,-61.2940646311251,-55.6332658221936,-50.4524017554082,-45.7226429946525,-41.4134661940384,-37.4939178270878,-33.9335009233627,-30.7027744357005,-27.7737375478043,-25.120055867709,-22.7171735026633,-20.5423444708846,-18.1299037240041,-16.0034032566806,-13.4349709052746,-11.2814814601965,-9.22383728236694,-7.54259823223305,-6.16891326304915,-5.04643005083767,-4.12908680572436,-3.3792629187093,-2.76624567764036,-2.26496715909135,-1.85496794618472,-1.51954915870186,-1.24507971658957,-1.02043099016049,-0.836515683557349,-0.685911850401105,-0.562556362193813,-0.461494999843215,-0.378678691801432,-0.31079735465677,-0.255144373245673,-0.209506048358245,-0.172071392676599,-0.141358513015601,-0.116154515064569,-0.0954664351129079,-0.0784811657953865,-0.0645327193959676,-0.053075478741892,-0.0436623352584338,-0.035926816959731,-0.0295684746597058,-0.0243409295220307,-0.0200420949390602,-0.0165061752797916,-0.0135971170520871,-0.0112032475556222,-0.00923288465794644,-0.00761074093974044,-0.00627497778144994,-0.00517479134910001,-0.00426843397916662,-0.00352159205421904,-0.00290605583052415,-0.00239862841900826,-0.00198023071526613,-0.00163516691682862,-0.00135052167802519,-0.00111566519662662,-0.000881935744953948,-0.000697915251838727,-0.000524637598946218,-0.000394567839006708,-0.000296885161988861,-0.00020976002143167,-0.000148545955794115,-9.72008185405489e-05,-6.36690385791491e-05,-4.17477119966825e-05,-2.74018483759325e-05,-1.8003902287259e-05,-1.18410413188178e-05,-7.7955452552938e-06,-5.13728164003599e-06,-3.38881214609516e-06,-2.2376175188872e-06,-1.47892273125427e-06,-9.78415790827604e-07,-6.47913246914071e-07,-4.29460109665109e-07,-2.84930223745026e-07,-1.89217408323062e-07,-1.25773079886823e-07,-8.36787965589242e-08,-5.57238587861215e-08,-3.71416697587417e-08,-2.47783039314225e-08,-1.6544922828963e-08,-1.10567854116822e-08,-7.39503946153151e-09,-4.94938072790637e-09,-3.31402364108024e-09,-2.2188483621877e-09,-1.48377653147047e-09,-9.88495439963603e-10,-6.52325826840569e-10,-4.20789022022039e-10,-2.56587161450693e-10,-1.50808306057169e-10,-6.34502662515917e-11,-3.11362883724889e-11
and here is the x-data:
0.7,0.700231481481481,0.700462962962963,0.700925925925926,0.701388888888889,0.701851851851852,0.702777777777778,0.703472222222222,0.704166666666667,0.704861111111111,0.705555555555555,0.70625,0.706944444444444,0.707638888888889,0.708333333333333,0.709027777777778,0.709722222222222,0.711111111111111,0.711805555555555,0.7125,0.713194444444444,0.713541666666667,0.713888888888889,0.71412037037037,0.714351851851852,0.714814814814815,0.715277777777778,0.715740740740741,0.716666666666667,0.718055555555556,0.719097222222222,0.720138888888889,0.721180555555555,0.722222222222222,0.723263888888889,0.724305555555556,0.725,0.725694444444444,0.726388888888889,0.727083333333333,0.727777777777778,0.728472222222222,0.729166666666667,0.729861111111111,0.730555555555556,0.73125,0.731944444444444,0.732638888888889,0.733333333333333,0.734027777777778,0.735069444444444,0.736111111111111,0.7375,0.738888888888889,0.740277777777778,0.741666666666667,0.743055555555555,0.744444444444444,0.745833333333333,0.747222222222222,0.748611111111111,0.75,0.751388888888889,0.752777777777778,0.754166666666667,0.755555555555556,0.756944444444444,0.758333333333333,0.759722222222222,0.761111111111111,0.7625,0.763888888888889,0.765277777777778,0.766666666666667,0.768055555555555,0.769444444444444,0.770833333333333,0.772222222222222,0.773611111111111,0.775,0.776388888888889,0.777777777777778,0.779166666666667,0.780555555555555,0.781944444444444,0.783333333333333,0.784722222222222,0.786111111111111,0.7875,0.788888888888889,0.790277777777778,0.791666666666667,0.793055555555555,0.794444444444444,0.795833333333333,0.797222222222222,0.798611111111111,0.8,0.801388888888889,0.802777777777778,0.804166666666667,0.80625,0.808333333333333,0.810416666666667,0.8125,0.814583333333333,0.817708333333333,0.820833333333333,0.823958333333333,0.827083333333333,0.830208333333333,0.833333333333333,0.836458333333333,0.839583333333333,0.842708333333333,0.845833333333333,0.848958333333333,0.852083333333333,0.855208333333333,0.858333333333333,0.861458333333333,0.864583333333333,0.867708333333333,0.870833333333333,0.873958333333333,0.877083333333333,0.880208333333333,0.883333333333333,0.886458333333333,0.889583333333333,0.892708333333333,0.895833333333333,0.898958333333333,0.902083333333333,0.905208333333333,0.908333333333333,0.911458333333333,0.914583333333333,0.917708333333333,0.920833333333333,0.922916666666667,0.925
I copy-pasted them from a comma-delimited .txt file.

If you look at the help you can see:
PKS = findpeaks(Y) finds local peaks in the data vector Y. A local peak
is defined as a data sample which is either larger than the two
neighboring samples or is equal to Inf.
So by default it is looking for a positive peak, you can supply the -ve of the signal and it will find it.

I was able to find the peak by fitting the posted data to a three-parameter Extreme Value peak equation, "y = a * exp(-exp(-((x-b)/c))-((x-b)/c)+1.0)", with parameter b as the peak at approximately x = 0.714. Fitted parameters of a = -1.7877846561352214E+02, b = 7.1375869130522429E-01, and c = 6.4029302224962413E-03 yielded RMSE = 2.64 and R-squared = 0.997.

Related

Store maximum frequency

I am working on a script which performs a FFT of given short audio file in a loop. I also want to store the peak frequency but I do not know how to do that.
The code looks similar to this:
n = ...
Frequencies = zeros(1,n); % Allocating memory for the peak frequencies
for k = 1:n
str(k)
textFileName = [num2str(k) '.m4a'];
[data,fs] = audioread(textFileName);
%...
% Fast Fourier transform and plotting part works ok
%...
[peaks,frequencies] = findpeaks(abs(cutP2),cutf,'MinPeakHeight',10e-3);
% Here starts the problem
maximum_Peak = max(peaks);
Frequencies(k) = ... % I need to store the frequency which is coupled
% with the maximum amplitude but I do not know how
end
close(figure(n)) %The loop opens one redundant blank plot, I could not
%find out any other way to close it
I do not want to store the amplitudes of peak frequencies, but frequencies of peak amplitudes. If you could help me with the redundant figure, I would be happy. I tried to implement an if statement but did not work.
max contains a second output which returns the index of the maximum value. Use this second value to stores the value of interest.
[maximum_Peak,I] = max(peaks); %Note I Use 'I' for index - personal habit
Frequencies(k) = frequencies(I);
Also, if your goal is only to find the max point findpeaks may be overkill and you could potentially use:
[maximum_Peak,I] = max(abs(cutP2));
%Might want to check that max is high enough
Frequencies(k) = cutf(I);
Note although the code is similar it is not the same and depends on what you want to do.
Finally, some unsolicited advice, your use of frequencies and Frequencies is a bit of a red flag. Generally differences based on capitalization are not a good idea. Consider renaming the latter to freq_of_max_amp

Fourier Analysis - MATLAB

Good evening guys,
I wanna ask you a question regarding the analysis of a function in the domain of frequencies (Fourier). I have two vectors: one containing 7700 values for pressure, and the other one containing 7700 values (same number) for time.
For example, I call the firt vector "a" and the second one "b". With the command "figure(1),plot(a,b)" I obtain the curve in the domain of time.
How can I do to plot this curve in the domain of frequency, to make Fourier transform?
I've read about the function "fft", but I've not understood very well how it can be used...can anyone help me?
Thanks in advance for your attention!
fft returns spectrum as complex numbers. In order to analyze it you have to use its absolute value or phase. In general, it should look like this (let's assume that t is vector containing time and y is the one with actual signal, N is the number of samples):
fY = fft(y) / (N/2) % scale it to amplitude, typically by N/2
amp_fY = abs(fY)
phs_fY = angle(fY)
Additionally, it would be nice to have FFT with known frequency resolution. For that, you need sampling period/frequency. Let's call that frequency fs:
fs = 1/(t(1) - t(0))
and the vector of frequencies for FFT (F)
should be:
F = (0:fs/N:(N-1)*fs/N)
and finally plots:
plot(F, amp_fY)
% or plot(F, phs_fy) according to what you need
I you can use stem instead of plot to get some other type of chart.
Note that the DC component (the average value) will be doubled on the plot.
Hope it helps

Define the width of peak in Matlab

I'm trying to find some peaks in Matlab, but the function findpeaks.m doesn't have the width option. The peaks I want to be detected are in the balls. All the detected are in the red squares. As you can see they have a low width. Any help?
here's the code I use:
[pk,lo] = findpeaks(ecg);
lo2 = zeros(size(lo));
for m = 1:length(lo) - 1
if (ecg(m) - ecg(m+1)) > 0.025
lo2(m) = lo(m);
end
end
p = find(lo2 == 0);
lo2(p) = [];
figure, plot(ecg);
hold on
plot(lo, ecg(lo), 'rs');
By the looks of it you want to characterise each peak in terms of amplitude and width, so that you can apply thresholds (or simmilar) to these values to select only those meeting your criteria (tall and thin).
One way you could do this is to fit a normal distribution to each peak, pegging the mean and amplitude to the value you have found already, and using an optimisation function to find the standard deviation (width of normal distribution).
So, you would need a function which calculates a representation of your data based on the sum of all the gaussian distributions you have, and an error function (mean squared error perhaps) then you just need to throw this into one of matlabs inbuilt optimisation/minimisation functions.
The optimal set of standard deviation parameters would give you the widths of each peak, or at least a good approximation.
Another method, based on Adiel's comment and which is perhaps more appropriate since it looks like you are working on ecg data, would be to also find the local minima (troughs) as well as the peaks. From this you could construct an approximate measure of 'thinness' by taking the x-axis distance between the troughs on either side of a given peak.
You need to define a peak width first, determine how narrow you want your peaks to be and then select them accordingly.
For instance, you can define the width of a peak as the difference between the x-coordinates at which the y-coordinates equal to half of the peak's value (see here). Another approach, (which seems more appropriate here) is to measure the gradient at fixed distances from the peak itself, and selecting the peaks accordingly. In MATLAB, you'll probably use a gradient filter for that :
g = conv(ecg, [-1 0 1], 'same'); %// Gradient filter
idx = g(lo) > thr); %// Indices of narrow peaks
lo = lo(idx);
where thr is the threshold value that you need to determine for yourself. Lower threshold values mean more tolerance for wider peaks.
You need to define what it means to be a peak of interest, and what you mean by the width of that peak. Once you do those things, you are a step ahead.
Perhaps you might locate each peak using find peaks. Then locate the troughs, one of which should lie between each pair of peaks. A trough is simply a peak of -y. Make sure you worry about the first and last peaks/troughs.
Next, define the half height points as the location midway in height between each peak and trough. This can be done using a reverse linear interpolation on the curve.
Finally, the width at half height might be simply the distance (on the x axis) between those two half height points.
Thinking pragmatically, I suppose you could use something along the lines of this simple brute-force approach:
[peaks , peakLocations] = findpeaks(+X);
[troughs, troughLocations] = findpeaks(-X);
width = zeros(size(peaks));
for ii = 1:numel(peaks)
trough_before = troughLocations( ...
find(troughLocations < peakLocations(ii), 1,'last') );
trough_after = troughLocations( ...
find(troughLocations > peakLocations(ii), 1,'first') );
width(ii) = trough_after - trough_before;
end
This will find the distance between the two troughs surrounding a peak of interest.
Use the 'MinPeakHeight' option in findpeaks() to pre-prune your data. By the looks of it, there is no automatic way to extract the peaks you want (unless you somehow have explicit indices to them). Meaning, you'll have to select them manually.
Now of course, there will be many more details that will have to be dealt with, but given the shape of your data set, I think the underlying idea here can nicely solve your problem.

Given an array of data, extract possible frequencies with fft, how to?

I am fairly new to vibrations and using matalb fft.I am given a set of data (1D array) of length 15000 (not sure if this is relevant) and I am trying to figure out if there is any wave buried in this data at all. I was instructed to possibly use matlab fft. Is that the correct way to do it? what shall I expect to see? I am really not sure how to interpret any results I would get.
Please let me know what you all think.
Thanks and if more details are needed I will provide them.
Example:
% Df=[array is given to me and it is of size 15000];
% t=[time used for the above array, and if it is of the same size, also provided to me]
N_0= length(t);
fs_0=length(Dfxz);
Y_0=fft(Dfxz,N_0);
k_0=-N_0/2:N_0/2-1;
%Find the phase angle
p_0 = (angle(Y_0));
R_0 = norm(Y_0);
ff_0 = (0:length(Y_0)-1)'/length(Y_0)*100; % Frequency vector
FT_power1_0 = abs(Y_0);
plot(k_0*fs_0/N_0,fftshift(abs(Y_0)))
I only see 1 peek at frequency = 0 but I am sure that there are non zero frequencies, what am I doing wrong?
Thanks!
PS:I am not sure how to pick the sampling frequency either? any tips please (keep in mind that I do not know the original frequency)
Try my version. It looks to me like you have all the information you need to find frequency peaks in your data if they exist. If all you can see is a big peak at zero frequency, you probably have a massive DC offset that is drowning out all your other data. I have put a remedy in my code . .
x = randn(15000,1); %//This is the data you were given - I'll use noise for demonstration - replace x with your Df data
%//If youre getting a massive peak at zero that dwarfs everything else, you
%//probably have a large DC offset. Easily removed in the time domain using
%//the following ..
x = x-mean(x);
tAxis = linspace(3/15000,3,15000); %//You said you have this too - I'll make up something for demonstration - make sure you replace this with your t data
dt = diff(tAxis(1:2)); %//sample period from time axis
fs = 1/dt;%//sample rate from sample period
NFFT = numel(x); %//number of fft bins - change if you like
Y = abs(fft(x, NFFT)).^2; %power spectrum
%//Calculate frequency axis
df = fs/NFFT;
fAxis = 0:df:(fs-df);
%//Plot it all
figure; plot(fAxis(1:NFFT/2), Y(1:NFFT/2))
xlabel('Frequency in Hz')
ylabel('Power')
If that works out OK, you can go into more depth by checking out another FFT answer on stackoverflow.

the Length of signal in calculating FFT

I want to ask some questions related to the last question of mine so I don't want to post in another thread. My question contains a code, I therefore can't post it as a comment. So I have to edit my old question into a new one. Please take a look and help. Thank you.
I'm new to FFT and DSP and I want to ask you some questions about calculating FFT in Matlab. The following code is from Matlab help, I just removed the noise.
Can I choose the length of signal L different from NFFT?
I'm not sure if I used window correctly. But when I use window (hanning in the following code), I can't get the exact values of amplitudes?
When L and NFFT get different values, then the values of amplitudes were different too. How can I get the exact value of amplitude of input signal? (in the following code, I used a already known signal to check if the code work correctly. But in case, I got the signal from a sensor and I dont know ahead its amplitude, how can I check?)
I thank you very much and look forward to hearing from you :)
Fs = 1000; % Sampling frequency
T = 1/Fs; % Sample time
L = 512; % Length of signal
NFFT=1024; % number of fft points
t = (0:L-1)*T; % Time vector
x = 0.7*sin(2*pi*50*t) + sin(2*pi*120*t); input signal
X = fft(hann(L).*x', NFFT)/L;
f = Fs/2*linspace(0,1,NFFT/2+1);
plot(f,2*abs(X(1:NFFT/2+1))) % Plot single-sided amplitude spectrum.
L is the number of samples in your input signal. If L < NFFT then the difference is zero-padded.
I would recommend you do some reading on the effect of zero-padding on FFTs. Typically it is best to use L = NFFT as this will give you the best representation of your data.
An excepted answer on the use of zero-padding and FFTs is given here:
https://dsp.stackexchange.com/questions/741/why-should-i-zero-pad-a-signal-before-taking-the-fourier-transform
In your experiment you are seeing different amplitudes because you will have different amount of spectral leakage with each different L.
You need to apply a window function prior to the FFT to get consistent results with frequency components that have non-integral number of periods within your sampling window.
You might also want to consider using periodogram instead of using the FFT directly - it takes care of window functions and a lot of the other housekeeping for you.