zero crossing plot

Hi all,

I am trying to set up a crude sound card means of datalogging the zero crossing times for a (somewhat) changing frequency sine (somewhat) wave. I have a homebrew voltage to frequency converter that puts out a sinewave, around 2 volts peak to peak. The input to the voltage to frequency to voltage converter is the analog signal I am trying to plot. It is a slow moving signal that is an output of a sensor. So, when the sensor input increases and decreases in amplitude, the voltage to frequency converter changes frequency.

My overall project is to find a very fast ‘wakeup’ sensor that can sit idle drawing nearly zero battery power, then wake the mouse when the sensor determines the mouse has moved. In this manner, a high power gaming mouse can be used without sucking up battery power when the mouse is not being used.

I would like to have the length of time between each zero crossing of the analog output of the voltage to frequency converter, in a text file if possible. Technically, I’d like to plot the delta T of each successive zero crossing, but if I can get the raw data containing only the zero crossing time, I can handle the plotting.

I can recover the raw data of the sensor output by using Analyze>Sample Data Export. But, I might be able to enter the raw data into a spreadsheet and find each zero crossing manually, but with 30 seconds of data at 44 Khz, it would take forever to manually recover all the zero crossing times.

I know I can use Analyze>Silence Finder, but it only gives me a graphical representation of multiple zero crossings without the exact time that a zero crossing happens.

I’m good with analog hardware, but not so good with software/digital.

I have no fancy hardware, an old core duo processor in an HP laptop using the onboard sound card. And, I do not need extreme precision or a fast sampling rate (8 KHz sampling is more than adequate because these are not fast sensors).

Is there anyway that Audacity can output the change in period for each zero crossing and plot it or give me a text file output showing the duration between each zero crossing event? I can handle fabrication of hardware if needed, but my budget is low and I am hoping the sound card/Audacity can handle the task without additional hardware.

Aloha,

AG

Audacity does not do “real-time” processing, but so long as that is not a problem, then you could use something like this in the “Nyquist Prompt” effect:

;; Print a list of positive going zero crossing points
(do ((i 0 (1+ i))
     (flag (> (snd-fetch (snd-copy *track*)) 0))
     (val (snd-fetch *track*)(snd-fetch *track*)))
    ((not val) (print "done"))
  (cond
    ((and (not flag) (> val 0))
      (print (+ (get '*selection* 'start)
                (/ i *sound-srate*)))
      (setf flag t))
    ((and flag (< val 0))
      (setf flag nil))))

Copy and paste this code snippet into the Nyquist Prompt effect, then click the “Debug” button to run the effect. The printed output will appear in the “debug window”, and you can select and copy the data from there.

This code is for mono tracks only. It would need to be modified for stereo tracks.

The above code can of course be modified to output the data differently as needed.

See here for more information about the Nyquist Prompt: http://manual.audacityteam.org/o/man/nyquist_prompt.html
and here for more information about writing Nyquist scripts and plug-ins: http://wiki.audacityteam.org/wiki/Nyquist_Plug-ins_Reference

Do you mean that you want the time interval between successive zero crossings? If so, then the code may easily be modified to give that.

The code in my previous post produced a list of values, one per line. That too could be modified, for example if CSV are more convenient, then that would be a simple modification.

This gives a list of intervals between successive positive going zero crossing points:

(do ((i 0 (1+ i))
     (output "")
     previous ; previous zero crossing time (default nil)
     period   ; duration between successive zero crossings
     (flag (> (snd-fetch (snd-copy *track*)) 0))
     (val (snd-fetch *track*)(snd-fetch *track*)))
    ((not val) (print "done"))
  (cond
    ((and (not flag) (> val 0))
      (if previous
          (let ((now (/ i *sound-srate*)))
            (print (- now previous))
            (setf previous now))
          (setf previous (/ i *sound-srate*)))
      (setf flag t))
    ((and flag (< val 0))
      (setf flag nil))))

Here is another solution. The code here is much harder to follow, but is much faster than the previous examples. This code is derived from a code snippet that Robert J H posted to the forum some time ago.

In this version, the output is as comma separated values (CSV).

(setf *float-format* "%.3f")  ; format to 3 decimal places

(let* ((beh (snd-from-array 0 *sound-srate* #(1)))
       (sr *sound-srate*)
       (z (mult sr (integrate (trigger *track* (cue beh)))))
       (final (round (peak z ny:all)))
       (out "0.0"))
  (do ((i 1 (1+ i)))
      ((= i (1+ final)) (format t "~a" out))
    (setf out (format nil "~a, ~a" out (sref-inverse z i))))
  (format nil "Done."))

Do you mean that you want the time interval between successive zero crossings? If so, then the code may easily be modified to give that.

Yes, that’s exactly what I mean. If the input to my voltage to frequency causes its output to be 1 KHz for 10 mS, I’d like the output to be 20 lines, each line being .5 milliseconds more than the line before it.

It would look something like:

0
0.0005
0.001
0.0015
0.002
0.0025
0.003
0.0035
0.004
0.0045
0.005
0.0055
0.006
0.0065
0.007
0.0075
0.008
0.0085
0.009
0.0095

OR

0.0005
0.0005
0.0005
0.0005
0.0005
0.0005
0.0005
0.0005
0.0005
0.0005
0.0005
0.0005
0.0005
0.0005
0.0005
0.0005
0.0005
0.0005
0.0005
0.0005
0.0005

I’ll try it tonight, right now the house is a madhouse:) It will be much quieter around 11PM or later.

Aloha and many thanks.

AG

PS:There’s nothing sacred about the zero crossing point, I can use the data if it gives the elapsed time between every .1 volt point.

So you want positive going zero crossings AND negative going zero crossings?

In that case, try this version. I’ve added a few comments to the code that may help you to see what it is doing (anything starting with a semicolon is a “comment” which is ignored when running the code)

(setf *float-format* "%.4f")  ; format to 4 decimal places

(do ((i 0 (1+ i))
     previous ; previous zero crossing time (default nil).
     period   ; duration between successive zero crossings.
     ; 'flag' is 'true' when sample value is above zero.
     (flag (> (snd-fetch (snd-copy *track*)) 0))
     (val (snd-fetch *track*)(snd-fetch *track*)))
    ((not val) (print "done"))
  (cond
    ;; Crossing from negative to positive.
    ((and (not flag) (> val 0))
      (if previous
          (let ((now (/ i *sound-srate*)))
            (print (- now previous))
            (setf previous now))
          (setf previous (/ i *sound-srate*)))
      (setf flag t))
    ;; Crosssing from positive to negative
    ((and flag (< val 0))
      (if previous
          (let ((now (/ i *sound-srate*)))
            (print (- now previous))
            (setf previous now))
          (setf previous (/ i *sound-srate*)))
      (setf flag nil))))

Just for interest, this code uses the same detection method as the previous example, but rather than printing the output to the Debug window, it creates labels at each zero crossing point. The labels are held in the variable “output” in the form of a “list” of labels, where each label is a “list” containing a “time” and the label text. In this case the label text is “Z+” for positive going zero crossings, and “Z-” for negative going zero crossings.

(do ((i 0 (1+ i))
     (output ())
     period   ; duration between successive zero crossings.
     ; 'flag' is 'true' when sample value is above zero.
     (flag (> (snd-fetch (snd-copy *track*)) 0))
     (val (snd-fetch *track*)(snd-fetch *track*)))
    ((not val) output)
  (cond
    ;; Crossing from negative to positive.
    ((and (not flag) (> val 0))
      (push (list (/ i *sound-srate*) "Z+") output)
      (setf flag t))
    ;; Crosssing from positive to negative
    ((and flag (< val 0))
      (push (list (/ i *sound-srate*) "Z-") output)
      (setf flag nil))))

and the quicker (but a little less accurate) version:

(let* ((beh (snd-from-array 0 *sound-srate* #(1)))
       (sr *sound-srate*)
       (z1 (mult sr (integrate (trigger *track* (cue beh)))))
       (z2 (mult sr (integrate (trigger (mult -1 *track*) (cue beh)))))
       (final1 (round (peak z1 ny:all)))
       (final2 (round (peak z2 ny:all)))
       (out ()))
  (do ((i 1 (1+ i)))
      ((= i (1+ final1)))
    (setf now (sref-inverse z1 (- i 0.1)))
    (push (list now "Z+") out))
  (do ((i 1 (1+ i)))
      ((= i (1+ final2)))
    (setf now (sref-inverse z2 (- i 0.1)))
    (push (list now "Z-") out))
  out)

The labels can be exported as a text file using “File menu > Export Labels”