Extract segments from signal

25 Ansichten (letzte 30 Tage)
Samuel
Samuel am 24 Mai 2023
Kommentiert: Daniel am 25 Mai 2023
Hi,
I have a signal in form of a 6608x1 double and I'd like to extract segments from that signal.
I want to extract every segment of the signal where the signal drops by at least 3% compared to the prior max value. Also, the segments are only valid if the duration is at least 150 seconds. As soon as the signal goes up again, the segment is over. I want to store these segments in a seperate array, so I can plot these segments on top of the signal to highlight where the signal drops by at least 3% for at least 150 seconds.
Can someone help me define the criteria for this task in Matlab? I'm not quite sure how to approach this.
This is a drawing of how I would like this to look when I plot the segments on top of the signal.
Thanks in advance!
  4 Kommentare
Daniel
Daniel am 24 Mai 2023
What do you mean by "prior max value"? How is the prior max defined?
Samuel
Samuel am 24 Mai 2023
The last value before the signal drops. And everytime the signal goes up again, it reaches a new max before it drops again. I'm looking for a drop by at least 3% between the lowest point of the current drop and the last max value before the drop began.

Melden Sie sich an, um zu kommentieren.

Akzeptierte Antwort

Daniel
Daniel am 24 Mai 2023
As a caution, I'm not at all sure that this is the most efficient way to do what you're asking. That said, it looks like you want to do a couple of things:
  1. Identify 3% drops.
  2. Identify 3% rises.
  3. Identify drop-to-rise runs.
  4. Filter these runs based on length.
At some point you'll also need to associate time values with your data. You can do this by creating a time vector, such as
t=(0:length(signal-1))'*Ts;
Based on your plot, it looks like you're taking samples once a second, so Ts would be 1.
To identify drops, you'll need to take the difference and compare it to the former value. You can take the difference with the diff function.
signal_difference = [diff(signal);0]; % I'm adding an element so sigdiff has the same size as signal
signal_ratio = signal_difference./signal; % Element-wise normalization gives you the relative differences
Then you'll want to identify falling and rising edges by comparing signal_ratio to 0.03 or -0.03. (By the way, your provided signal data contains no 3% drops or rises.)
Next, you'll have to iterate over the drops and rises to collect them. You'll often see multiple drops before a single rise, or multiple rises before a single drop. So you'll need to write some sort of state machine to pair them, looking for a drop and then looking for a rise and so on. If you store the index associated with each qualified drop and each qualified rise, that will allow you to extract the signal data, and also give you the run length for each run. I'm not aware of anything prefabricated that does this automatically.
Finally, scan that list of indices and reject any run length less than your desired time.
If you run into problems doing that, we can provide more targeted advice. If anyone else has a better or more clever idea for how to do this I'd be interested to hear.
  2 Kommentare
Samuel
Samuel am 24 Mai 2023
Thanks for your input. I've come up with another idea.
I've used the findpeaks() function to get every local max and min. Now my task is to identify which neighbouring min and max are at least 3% apart in Y values and at least 150 seconds apart in X values. But I'm only interested in the 3% drops. Not the rises.
Daniel
Daniel am 25 Mai 2023
Whoops, I misunderstood as far as rises, sorry!
Neither of the qualification tasks should be too hard, I think.
For the 3% difference, find the first min after each max, divide the min by the max, compare to threshold.
For the 150 seconds apart, find the first min after each max, compute the difference in sample indices (therefore in sample times once you multiply by your sampling period Ts), compare to threshold.
Keep any region that passes both tests.
The only remaining divergence: If you use the output of findpeaks as is, you'll get a run going from the max value itself to the min. From the plot in your original post, it looks like you want to exclude the max itself, and only include values below the max. Luca has a nice intuitive solution for that, scanning through the signal until the signal falls below the observed max. If you want to be more obfuscated, you could do something like the "Code to find the latest-occurring peak value" below.
% Extra code to generate a waveform to test
x = repmat(rand(1,20),5,1); x = reshape(x,1,[])';
% Code to find the latest-occurring peak value
[p,peakIdxsFlipped] = findpeaks(x(end:-1:1));
peakIdxs = length(x) + 1 - peakIdxsFlipped(end:-1:1);
% Extra code to plot the results
plot(x); hold on; plot(peakIdxs,x(peakIdxs),'o')

Melden Sie sich an, um zu kommentieren.

Weitere Antworten (1)

Luca Ferro
Luca Ferro am 24 Mai 2023
Bearbeitet: Luca Ferro am 24 Mai 2023
This is the closest i could go as of right now, i'll revise it in the next days. But i'll share it so that someone else can build on it to reach the final solution.
Basically what it does is analyzing the signal flat section per flat section.
flat=diff(signal);
[peaks ,peaksIdx]=findpeaks(signal); %find peaks
highlight=nan(size(signal,1),1);
peaksIdx=[peaksIdx ; inf];
for pp=1:size(peaks,1)-1 %loopsthrough each peak
ss=peaksIdx(pp);
peakLen=1;
startPeak=ss;
while ss<peaksIdx(pp+1) && signal(ss)==peaks(pp) && flat(ss)==0
peakLen=peakLen+1;
ss=ss+1;
end %detects peak and moves to the end of it
while flat(ss)~=0 && ss<peaksIdx(pp+1)
ss=ss+1;
end %moves to the next flat area
while ss<peaksIdx(pp+1) && flat(ss)==0
startFlat=ss;
flatLen=1;
if signal(ss)/peaks(pp) > 0.3
flatLen=flatLen+1;
end
highlight(startFlat:startFlat+flatLen-1)=signal(startFlat:startFlat+flatLen-1);
ss=ss+1;
end %if the flat area has the proper specifics highlights it
end
plot(signal,'color','blue');
hold on
plot(highlight,'color','red','LineWidth',3)
The issue that this has is that it only highlights the first drop and not the following ones.
Disclaimer: is not the best approach, is not the cleanest code. I'll revise the abuse of loops later to find a better structure.

Produkte


Version

R2022b

Community Treasure Hunt

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

Start Hunting!

Translated by