pythonnumpyscipymodulation

Extract frequency of sin wav from clean and noisy numpy arrays


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:

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.


Solution

  • 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