Skip to content

Instantly share code, notes, and snippets.

@endolith
Last active September 16, 2020 18:15
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save endolith/129445 to your computer and use it in GitHub Desktop.
Save endolith/129445 to your computer and use it in GitHub Desktop.
Count zero crossings to determine frequency
function [frequency, period, crossings, seconds, samples]=freq(wavefile, siz)
% FREQ Measures frequency of mono .WAV files by counting zero crossings
%
% [frequency, period, crossings, seconds, samples]=freq(wavefile, siz, fmt24bit)
%
% frequency : in hertz
% period : in seconds
% crossings : the total number of zero crossings
% seconds : total amount of time between first and last zero crossing
% samples : total number of samples between first and last zero crossing
%
% wavfile : file name
% (The ".wav" extension is appended if no extension is given.)
% siz : 'size' returns the size of the audio data
% : [N1 N2] returns only samples N1 through N2
% : N1 returns the first N1 samples
% : default is all the data
% endolith@gmail.com July 12th, 2005
% Meant to measure frequency very accurately, for comparing ADC clocks. Only useful in the specific circumstance I was using it, with long wav files and signal much larger than noise, so that noise doesn't cause spurious zero crossings
% Show help
if nargin < 1
help freq;
return;
end
%Load WAV file
if nargin < 2
[sig, sf, bits] = wavread (wavefile);
else
if nargin < 3
[sig, sf, bits] = wavread (wavefile, siz);
end
end
%We need high precision for these numbers
output_precision(9)
%Find all zero crossings
c=[(sig(1:size(sig,1)-1)<=0).*(sig(2:size(sig,1))>0);0];
%Find locations of the rising edge zero crossings
a=find(c);
%Calculate number of samples between first and last zero crossing (relevant data)
samples=(a(size(a,1)))-a(1)
% Should specify the precision of the measurement (1000.0 Hz for a short sample, 1000.00000 Hz for a long sample?)
%Calculate time in seconds of relevant section
seconds=samples/sf
%Number of crossings in relevant section
crossings=size(a,1)-1
%Frequency is crossings per second
frequency=crossings*sf/samples
period=1/frequency
%Plot
%closeplot
%d=zeros(size(sig,1),1);
%d(a(1):a(size(a,1))-1)=1;
%dsize = size(d(d>0),1)
%t = [1:size(sig,1)];
%plot(t,sig,t,c,t,d)
%allcs = size(c(c>0),1)
from __future__ import division
from numpy import logical_and, average, diff
from matplotlib.mlab import find
def freq_from_crossings(sig, fs):
"""Estimate frequency by counting zero crossings
Doesn't work if there are multiple zero crossings per cycle.
"""
indices = find(logical_and(sig[1:] >= 0, sig[:-1] < 0))
# Linear interpolation to find truer zero crossings
crossings = [i - sig[i] / (sig[i+1] - sig[i]) for i in indices]
return fs / average(diff(crossings))
@appetrosyan
Copy link

Indices = find(...) is always empty.

@russellb23
Copy link

russellb23 commented Mar 22, 2018

import numpy as np
indices = np.nonzero(np.diff(sig > 0))[0]

This might work!

@laurentbenaroya
Copy link

I'm amazed by the latter Python code which doesn't seem optimized at all.
Don't we have sum(diff(crossings)) = crossings[-1] - crossings[0]?
By the way, I used this function on an Arduino, in C. Of course, the reference point is not 0. but 512 if the signal is quantized on 10 bits.

@endolith
Copy link
Author

endolith commented Jul 3, 2020

@LaurentNorbert Yeah, that's right, though it's only going to take microseconds either way.

average(diff(crossings))

can be replaced by

(crossings[-1]-crossings[0])/(len(crossings)-1)

The average method would allow you to do a trimmed mean to throw away outliers, etc.

@lewham97
Copy link

I'm new to python. Can someone tell me what sig and fs mean? Thanks.

@endolith
Copy link
Author

@lewham97 those are just variable names, not specific to python. sig = signal, fs = sample rate

@lewham97
Copy link

@endolith Thanks for clearing that up. Would this code be suitable for calculating voltage zero crossings from the output of a comparator circuit?

@endolith
Copy link
Author

@lewham97 I'm not sure what you mean. It counts zero crossings of a constant frequency and calculates the frequency

@lewham97
Copy link

@endolith I’m looking to count zero crossings of a square wave with a peak of 3.3v using a Raspberry Pi to calculate the frequency. I’m hoping that this code will be suitable for the job!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment