1
$\begingroup$

My goal is to do spectrum analysis of a gear motor. I used Python with the Pyaudio package and made a crude spectrum analyzer that displays the FFT as the sound is being recorded. I recorded 10 seconds of sound just for test. Sample rate is 44100, each frame is 1024 samples. For each frame I plotted the FFT, and made a dynamic plot thru the 10 seconds.

I also downloaded a FFT analyzer to my smartphone and compared the results as I turned on a motor. The smartphone app is able to display relatively stable spectrum. In my plot, the magnitudes are very high upon turning on the motor, and only "settled" after a couple of seconds.

I wonder why that is. Is it due to my sound card or laptop mic? I tested using another laptop and got similar result. Is it because I recorded in Mono mode? The spectrum changes too much as I move the motor around, whereas on the smartphone the spectrum is more stable.

Or is it because I didn't use a Window function? Here is my code:

import numpy as np
import os
import time
import pyaudio
import matplotlib.pyplot as plt
from scipy.fftpack import fft

CHUNK = 1024
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
RECORD_SECONDS = 10
WAVE_OUTPUT_FILENAME = "test.wav"

p = pyaudio.PyAudio()

stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK)

input('Press key to start recording:\n')
print('1 sec delay started') #delay so keystroke doesn't get recorded
time.sleep(1)
print("* recording")


frames = []
n = 1024
k=np.arange(n)
T = n/RATE
frq = k/T
frq = frq[range(int(n/2))] # one side frequency range
for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
    data = stream.read(CHUNK)
    frames.append(data)
    decoded = np.fromstring(data, dtype=np.int16) #grab the data in stream
    fft_decode=fft(decoded)/(len(decoded)/2) #normalized FFT
    mags=np.absolute(fft(decoded)) #
    plt.ylim(top=55000)
    plt.xlabel('Freq (Hz)')
    plt.ylabel('|Y(freq)|')
    plt.plot(frq, mags[range(int(n/2))],'b')
    plt.pause(.01)
    plt.gcf().clear()

print("* done recording")

plt.close()

How can I improve my analyzer? Is there anything wrong with my code or the way I approached this?

Edit: Nevermind. The reason turns out to be the boost function of my microphone, and disabling it solved my problem.

$\endgroup$
2
  • 2
    $\begingroup$ While the parameters of your program are stated here, we don’t know what they are on your cell phone app. it is probably averaging a few frames prior to displaying. this is a guess and remotely guessing is not efficient. i suggest you do something like using an app like audacity to capture the data in real time and process the wave file separately as your first version. once satisfied, develop a real time version. a separate microphone, away from your computer might be helpful $\endgroup$
    – user28715
    Jan 15, 2019 at 7:28
  • $\begingroup$ the rate that you are processing data is about 40 frames per second which is a bit too fast to display it. 20 frames a second for display is a practical display rate. your phone app probably averages fft chunks for a reasonable display rate and it smooths the fft magnitudes that see. you should expect to see start up transients that quickly subside $\endgroup$
    – user28715
    Jan 15, 2019 at 16:49

2 Answers 2

2
$\begingroup$

Yes, you should be using a window function. I recommend the VonHann (Also know as Hann or Hanning). The effect in the frequency domain is that each bin gets the average of its neighbors subtracted from it. This is basically the equivalent of a discrete second derivative. It squashes constants and linear trends.

Why is this helpful? Because of the nature of the DFT. If you throw a sine wave at it, if there is a whole number of cycles it will fall in a single frequency bin (considering only sub-Nyquist bins). If it is off an integer, there will be leakage. The worst case being a whole number plus half a cycle in your frame. Then the signal gets smeared across the DFT in a big hump centered at the two bins surrounding the frequency.

In the former case, the VonHann will turn a spike into a little triangle. In the latter case, it will squash most of the side lobes leaving values near the bin of interest. The resulting heights are roughly the same. This makes for a nice display in your spectrogram.

You should test your program with a frequency sweep sine tone. It should make a fairly consistent solid line.

$\endgroup$
2
$\begingroup$

Are you appending new data to your FFT input vector? If so, then every FFT result will have a different length; and it's likely the early ones are too short for the frequency resolution you desire. The smartphone app may also be averaging multiple successive FFT results to reduce noise.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.