Extract segments from signal
25 Ansichten (letzte 30 Tage)
Ältere Kommentare anzeigen
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
Akzeptierte Antwort
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:
- Identify 3% drops.
- Identify 3% rises.
- Identify drop-to-rise runs.
- 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
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')
Weitere Antworten (1)
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.
0 Kommentare
Siehe auch
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!