Zero detection in online signal

Hi.. I am acquiring a signal from an EEG from a matlab script. I know approximatly the frequency of the signal (0.5Hz to 4 Hz).
I need to play a sound when the signal does a negative zero crossing. So I need to detect the correct instant to fire the audio in real ltime.
500 samples make 1 second of the signal.
I would have to band pass the signal to remove high frequencies (otherwhise, it could incur spurious zero crossing). For example, I could band pass 10 samples (which would take me 20 ms ) .
%s - > acquired signal (10 samples)
x = filter (s) % x is the bandpassed signal
for ii = 1:10
if (x(ii-1) > 0 && x(ii) < 0 ) % lets say that this happend for the fifth element of x (ii = 5)
sound('pinknoise.wav')
end
end
I tought about using the following as a filter because it has linear delay.
%%%%%
h = fdesign.bandpass('fst1,fp1,fp2,fst2,ast1,ap,ast2', Fstop1, Fpass1, Fpass2, Fstop2, Astop1, Apass, Astop2, Fs);
Hd = design(h, 'equiripple', 'MinOrder', 'any');
y = filter(Hd,x);
%%%%%
However, I am not sure about the delay that the filter would introduce. It Introduces a phase shift (delay) of 2 samples. Therefore is it correct to say that the negative zero crossing happened at the (5 - 2) third element of s?
How to get this delay? Is this zero cossing algorithm reasonable? Are there otherS best than this one?
Best

Answers (1)

Jon
Jon on 8 Nov 2023
Edited: Jon on 8 Nov 2023
Your filtering idea, could probably be made to work. As an alternative, you might consider the use of a Schmidt trigger, to "debounce" the signal. It is extremely simple. You would adjust the lower threshold to be large enough to avoid false triggers.
Even with this approach there will be a tradeoff between false alarms, and the delay to detect them. With a large threshold and a slowly descending signal there will be a some samples of delay between the true zero crossing and when the lower threshold is crossed, and the trigger event occurs.
If you aren't familiar with Schmidt triggers you can Google them, and find lots of references.
Here's a rough example:
% Parameters
aNoise = 0.1; % noise amplitude
f = 1; % example signal frequency [Hz]
a = 3; % example signal amplitude
numCycles = 10; % number of signal cycles
fsmpl = 100; % sampling frequency [Hz]
xLower = 0.3; % Lower Schmidt Threshold
xUpper = 0; % Upper Schmidt Threshold
% Generate noisy signal
T = 1/f; % signal period
Tsmpl = 1/fsmpl;
t = 0:Tsmpl:numCycles*T;
x = a*sin(2*pi/T*t) + aNoise*randn(size(t));
% Loop through signal values one sample at a time to simulate real time
% application
val = zeros(size(t));
soundOn = zeros(size(t));
prevVal = 0;
for k = 1:numel(x)
% Pass signal through Schmidt Trigger
val(k) = schmidt(x(k),xLower,xUpper);
% Check if this was a zero crossing (schmidt changed state)
if (val(k) - prevVal) > 0
soundOn(k) = 1;
end
prevVal = val(k);
end
% Plot results
plot(t,x,t,val,t,soundOn,'o')
grid
legend('signal','schmidtState','soundOn')
function val = schmidt(x,xLower,xUpper)
persistent isNeg % trigger state
% initialize
if isempty(isNeg)
isNeg = false;
end
if x <= xLower && ~isNeg
isNeg = true;
end
if x > xUpper && isNeg
isNeg = false;
end
% Compute output signal, as zero or 1 numerical value
val = double(isNeg);
end

16 Comments

Thanks, for your answer.
Now, I am having the problem of filtering the signal.
bandpassdata = bandpass(x,[0.25 4], 500)
x is 1x10 vector containing 10 datapoints.
Matlab complains : Warning: Signal length is not large enough to filter data with a filter that
meets the specifications.
A filter with a smaller steepness has been designed instead. Relax the
steepness and stopband attenuation values or increase the signal length.
I used the smallest steepness and stopband and it still complains.
Jon
Jon on 8 Nov 2023
Edited: Jon on 8 Nov 2023
I would first suggest that you evaluate the Schmidt trigger idea as an alternative to filtering. It is a very simple and widely used approach. If it works well enough for you, then you don't have to worry about your problems with the filter.
Regarding your problems with implementing a filter. First, why are you bandpass filtering the signal? Do you have some reason to believe the noise is only in a narrow range? I would think you would just want to low pass filter the data to get rid of the relatively high frequency noise.
If you still need detailed help with the filter implementation, please attach an example data file with your signal, and the code that you have written so far.
I need to filter because the zero crossing detection and sound trigger should be done in the signal without the DC component and without noise.
I know rougly the frequency of the signal of interest: 0.25 to 4 Hz
EEG.x = rand(1,10)
bandpassdata = bandpass(EEG.x,[0.25 4] , 500);
Oh I see, you want to bandpass to just keep the frequencies of interest. Sorry I was somehow thinking you were using a notch filter to get rid of noise at a specific frequency.
Regardless, it would be good to be clear about why you "need" a filter. I would turn the question around and specify what you really need to find out from the signal, and then figure out what you need to do to accomplish that.
To get rid of the dc component, a very simple high pass filter can be used. for example. Essentially you are implementing the equivalent of "ac coupling" on a scope. For this you could for example just subtract the current value from the previous n-point moving average. Another similar idea is to subtract the current value from the output of a first order lag.
If you could further specify what it is you want to extract from the signal and attach an exemplar signal, and your files so far I could take a look.
For your application, do you have the entire signal available, and you are postprocessing it, or do you only get one value every 1/500 sec and you have to decide based on the current and previous values whether it has crossed zero or not.
In order to use MATLAB's bandpass function you would need to have the whole signal available. It can not be applied one value at a time, for a real time application. Because it post processes the values it is able to shift the signal back in time to compensate for the filter delay.
Here is a little example to illustrate. Note how the whole filtered signal is shifted back in time, and is clearly non-causal, as it anticipates the change in the signal value. I also plot the result of applying the filter, without the backward time shift using MATLAB's filter function. In the example I used a fir filter as you said you were interested in a linear phase response. Note however that the filter length, and corresponding delay are quite long.
% Make a signal with a dc component and a single zero crossing
Ts = 1/500; % sampling period [s]
t = 0:Ts:200;
N = numel(t);
x = zeros(N,1);
x(round(N/2):end) = -3;
x = x + 2;
% Band pass filter the signal, with passband from 0.25 to 4 Hz
[xf,d] = bandpass(x,[0.25,4],1/Ts,'ImpulseResponse','fir');
% Plot output of "bandpass function"
plot(t,x,t,xf)
xlabel('time [s]')
legend('original signal','filtered signal')
title('Output of MATLAB bandpass function')
% Generate output of signal that would be generated in real time
xfrt = filter(d,x);
plot(t,x,t,xfrt)
xlabel('time [s]')
legend('original signal','filtered signal')
title('Output of MATLAB filter function')
Hi @Jon, but a moving average would introduce delay, right?
How would I solve that .
L
L on 9 Nov 2023
Edited: L on 9 Nov 2023
@Jon So, I am doing it real time.
I receive 10 points every 1/500 s
I need to take off the DC and check for zero crossing. The signal might also have some high frequency noise coming in.
Here is some example points .. but this is after recording. i would have to do this processing at real time.
The signal shown in your attached file is steadily increasing with time. How then do you define a zero crossing. Are you perhaps thinking that you want to subtract off the trend line (assuming it is linearly increasing) and see where it crosses the trend line?
Note, you say that the signal has a dc component. A "dc" component would be a constant added to your signal.
You have a growing value added to your signal. Here is a plot of what you sent.
Or - are you trying to find the zero crossings of the first derivative of this signal?
load data2
N = numel(data2);
Ts = 1/500;
idx = 1:N;
t = (idx-1)*Ts;
plot(t,data2)
xlabel('time [seconds]')
I want to find the zero crossings of the [0.25Hz to 4 Hz content] of this signal
That is why I was trying to band pass it.
If you band pass the entire signal you will see that these increading offset is taken out.
OK, I will have to think about this a little. I'll let you know if I have a good approach.
@Jon, I was wondering if we could use a dsp function from simulink, such as :
I guess such blocks are meant to work in real time , right?
I did quickly try this but wasn't able to get any meaningful results.
I'm sorry I'm stuck at this point. Maybe someone else can help.
I'm having problems even conceptually with thinking about how to deal with this ramping function, and exactly how you are defining a zero crossing. Obviously the raw signal itself never crosses zero. So somehow we must remove the ramp and produce a signal that crosses zero.
While I can roughly understand your idea of bandpassing to detrend (remove the linear growth) I'm not sure how to think about frequency response of the filter in the context of this ramping signal.
I'm sorry, but I can't really put much more time into this right now, but here are some references I found that might be of interest
https://www.sciencedirect.com/science/article/pii/S1053811919301545
Hi @Jon. I am having a error on the schimidt trigger. I need my threshold not to be zero, but to be -60. So, I consider zero crossing when actually the signal went smaller then -60.
I tried to modify the code. But then it dosent work.
Before getting to the details of that, what is the signal that you are applying the Schmidt trigger to?
In my example of applying a Schmidt trigger, I have assumed that the input signal is varying about zero, and you are looking for where it crosses zero. You could also have a signal that varied about some other constant and subtract off that constant to transform it to a zero crossing problem. In any case it was not intended for a ramp signal such as the one you attached. Have you found a way to first "detrend" the data, so you now have a signal that varies above and below some constant value, rather than a ramp signal as you now have?

Sign in to comment.

Products

Asked:

L
L
on 7 Nov 2023

Commented:

Jon
on 14 Nov 2023

Community Treasure Hunt

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

Start Hunting!