Finding specific peaks and valleys

17 views (last 30 days)
I have a periodic signal with 6 peaks and 6 valleys. I am able to find all 6 peaks and all 6 valleys using the code below:
% plot periodic signal
plot(angle,signal)
hold on;
% fit to avoid noisy oscillations and find 6 global peaks
f = fit(angle,signal,'sin9');
yfitted = feval(f,angle);
plot(angle,yfitted,'r')
% find peaks and corresponding angles
[ypk0,idx0] = findpeaks(yfitted);
peaks = signal(idx0);
max_angles = angle(idx0);
% invert fitted signal to find 6 global valleys and corresponding angles
yfittedinv = max(yfitted) - yfitted;
[ypk,idx] = findpeaks(yfittedinv);
valleys = signal(idx);
min_angles = angle(idx);
Furthermore, I am able to find the global minimum (i.e., the lowest valley) using this line:
% find the global minimum
minangle = min_angles(valleys == min(valleys));
Both the arrays "max_angles" and "min_angles" have 6 elements. The question is how to reduce the number of elements in the array "min_angles" from 6 to 3, where I will have only the global minimum and the 2 non-neighbor valleys. The attached pic should help. I am able to find the green circle, but I also need to find the 2 red circles. Note that the global valley (green circle) is not always in the middle. It can be the first element of the array "min_angles", or it can be the last one. In the picture below, it's number 4.
  2 Comments
Image Analyst
Image Analyst on 30 Jan 2025
Make it easy for us to help you, not hard. Attach your variables "angle" and "signal" in a .mat or text file. That way we'll have something to work with and won't have to try to create data on our own.
save('answers.mat', 'angle', 'signal');
If you have any more questions, then attach your data and code to read it in with the paperclip icon after you read this:

Sign in to comment.

Accepted Answer

Star Strider
Star Strider on 30 Jan 2025
Edited: Star Strider on 30 Jan 2025
To find specific valleys, one option might be to use the MinPeakProminence value. The prominences are the tthird output returned by findpeaks, so you can look at those to see iif that option would work.
Another option is to get the peak values and use the mink (or maxk) function to return thee three lowest values (since that appears to be what the green circle and the red circles represent). Return both outputs from mink beecausee the second will be the index values of the minima (or maxima, depending on you you have the results returned), and you can use those indices with the returned values for the peaks and their locations to isolate the ones you want.
This is based on the figure you posted.
.
EDIT — (30 Jan 2025 at 21:33)
Example —
a = linspace(0,360).';
s = sin(2*pi*a/360*6)-sin(2*pi*a/720);
[vlys,vlocs] = findpeaks(-s)
vlys = 6×1
1.3727 1.7946 1.9796 1.9212 1.6080 1.1255
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
vlocs = 6×1
14 30 46 63 79 96
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
[minpks, minpksidx] = mink(-vlys,3)
minpks = 3×1
-1.9796 -1.9212 -1.7946
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
minpksidx = 3×1
3 4 2
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
figure
plot(a, s)
hold on
plot(a(vlocs(minpksidx)), -vlys(minpksidx), 'rs')
hold off
grid
ylim([-2.5 1])
.
  8 Comments
Aram Zeytunyan
Aram Zeytunyan on 1 Feb 2025
Edited: Aram Zeytunyan on 1 Feb 2025
I need to find 3x valleys out of 6. The starting/reference point is the lowest valley (the large red dot in the plot, which is easy to find). The other two cannot be the neighboring valleys as that's forbidden by physics. mink will find valleys labeled 2, 4, and 5. I need to find valleys 2,4, and 6.
Star Strider
Star Strider on 1 Feb 2025
I am still not certain what you want, since I am still uncertain of the criteria.
See if this works —
loaddata = readmatrix('testdata.csv');
angle = loaddata(:,1);
signal = loaddata(:,2);
[pks,plocs,pprm] = findpeaks(signal, MinPeakProminence=10);
[vys,vlocs,vprm] = findpeaks(-signal, MinPeakProminence=10);
[minvly,minidx] = min(-vys); % Minimum Valley & Associated Valley Index
idxrng = [max(1,(minidx-2)) minidx min((minidx+2),numel(vlocs))] % Valley Indices ±2 Positions From Minimum
idxrng = 1×3
2 4 6
<mw-icon class=""></mw-icon>
<mw-icon class=""></mw-icon>
Results = table(angle(vlocs(idxrng)), signal(vlocs(idxrng)), VariableNames=["Angle","Value"])
Results = 3x2 table
Angle Value ______ ______ 102.16 83.776 226.39 77.624 343.3 81.332
figure
plot(angle, signal, DisplayName='Signal')
hold on
plot(angle(plocs), pks, 'vg', DisplayName='Peaks')
plot(angle(vlocs), -vys, '^r', DisplayName='Valleys')
plot(angle(vlocs(idxrng)), signal(vlocs(idxrng)), 'ms', MarkerSize=12, DisplayName='Selected')
hold off
grid
xlabel('Angle (°)')
ylabel('Signal (mV)')
legend(Location='best')
The ‘idxrng’ calculation contains a ‘fail safe’ to prevent finding indices outside the allotted range.
.

Sign in to comment.

More Answers (0)

Categories

Find more on Descriptive Statistics in Help Center and File Exchange

Products


Release

R2024b

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!