0

Here is a graph that shows a "control" (blue) and "recorded" (orange) signals.

enter image description here

Both are numpy arrays with 10,756 items, acquired from a .wav file with the code:

from wave import open as open_wave

waveFile = open_wave(filename,'rb')
nframes = waveFile.getnframes()
wavFrames = waveFile.readframes(nframes)
ys = np.fromstring(wavFrames, dtype=np.int16)

I have 26 control signals (all letters of alphabet), and I'd like to take a recorded wave and figure out which control signal it is most similar too.

My first approach was using scipy.signal.find_peaks() which works perfectly for control signals, and sometimes for recorded signals, but not good enough. I understand the shortcoming here to be a) possible clipping of signal at beginning/end, or b) noise in the recorded signal can create false peaks.

My second approach was subtracting the recorded array from all controls, hoping the most similar would result in the smallest diff. This didn't work well either (though still interested in this approach...).

What I'm hoping to do now is:

  • continue to identify peaks with scipy.signal.find_peaks()
  • get average distance between peaks across signal
  • look for a control signal where this average peak distance is similar

Where, of course, "peak distance" is the frequency of the sin wave.

Any suggestions or streamlines appreciated! I realize I'm bumbling into a very rich world of signal processing, using this toy / fun example to dip my toes.

ghukill
  • 1,136
  • 17
  • 42
  • The so-called naive way is-do an FFT. Will be trivially easy with data like this. Can probably trust the result of an `argmax` on the spectrum. – Reinderien Feb 24 '23 at 04:18
  • What is the frequency range over your 26 options? – Reinderien Feb 24 '23 at 04:19
  • @Reinderien, the numpy arrays are 10k long, and the **mean** distance between peaks (in the clean, control signals) range from 28 to 336 apart. – ghukill Feb 24 '23 at 13:31
  • Forget about peaks (or at least, forget about temporal peaks). Run [`rfft`](https://numpy.org/doc/stable/reference/generated/numpy.fft.rfft.html) and compare the single highest spectral peak in both. – Reinderien Feb 24 '23 at 14:35
  • 1
    @Reinderien, I think this might work great! I was a bit lost with what to do with the results until I reread your comment, "compare the single highest spectral peak in both" and then graphed the results. Looks like the peak does align fairly closely. Going to write up an answer, with graphs, thanks! – ghukill Feb 24 '23 at 15:25

1 Answers1

1

Thanks to @Reinderien for the comment suggestions, think using Numpy's "one-dimensional discrete Fourier Transform for real input" is a great approach.

Code snippet used:

control_tone =  array([...], dtype=int16)
recorded_tone =  array([...], dtype=int16)
plt.close
plt.plot(np.fft.rfft(control_tone))
plt.plot(np.fft.rfft(recorded_tone))
plt.show()

Two examples of tones that should align: enter image description here enter image description here

And then, an example of tones that should not align (can see the peaks are off): enter image description here

By comparing the "the single highest spectral peak in both" I can see a clear path towards determining which control signal the recorded signal is most similar to.

UPDATE What was not immediately obvious to me, from the output of np.fft.rfft(), was using np.argmax() to get the index of the highest peak:

np.argmax(control_fft) # output: 71
np.argmax(recorded_fft) # output: 72, which is closer than others, so use
ghukill
  • 1,136
  • 17
  • 42