I have an acoustic setup. A sender, emitting a clean sine signal. The receiver is far away. In the time domain, the sine is hardly visible because the noise is nearly as big as the signal. In the frequency domain, the noise is distributed across all frequencies.
I need to extract the amplitude at the target frequency, I do not care about the phase.
Currently, I calculate the length of my fft-window to contain exactly N periods of my target frequency (n_bins=N * samplerate / frequency
) to avoid a loss in magnitude due to spectral leakage.
To remove the noise at this frequency, I do the same with a measurement w/o signal to estimate the noise background and subtract it.
My question now is, should I still use a window-function like blackman? From my understanding, I could now have leakage from noise in adjacent bins into my signal?
I have tried using a window-function in a python-example for this, with simulated data. Here, when using a window, the amplitude at my target frequency was actually higher than expected, while the results without the window were closer to reality.
EDIT: Python source code:
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import blackman as w_function
target_freq = 12000
samplerate = 1000000.
window = True
# generate data
x = np.array(range(0,17166)) / samplerate
y = 290*np.sin(2*np.pi*target_freq*x) # signal
y += 150*np.sin(2*np.pi*(target_freq - 451)*x) # noise near the signal
y += 232*np.sin(2*np.pi*200185*x) # noise far away
# find closest multiple
reduced_len = len(y)
while not float(reduced_len * target_freq / samplerate).is_integer():
reduced_len -= 1
def calc_fft(window):
# prepare window
if window:
wndw = w_function(len(y))
else:
wndw = np.array([1] * len(y))
wndw_correction = float(len(y)) / float(np.sum(wndw)) # amplitude correction factor for window
# calculate fft, apply corrections for correct amplitude representation
rft = np.abs(np.fft.fft(y * wndw, reduced_len, norm=None))
rft *= 2 * wndw_correction / float(len(rft))
# calculate corresponding frequencies, show only positive frequencies
fft_freq = np.fft.fftfreq(len(rft), 1 / samplerate)
rft = rft[np.argsort(fft_freq)]
fft_freq = fft_freq[np.argsort(fft_freq)]
low_ind = (np.abs(fft_freq-0)).argmin()
fft_freq = fft_freq[low_ind:]
rft = rft[low_ind:]
return fft_freq, rft
fft_freq, rft_window = calc_fft(True)
fft_freq, rft_no_window = calc_fft(False)
plt.plot(fft_freq, rft_window, label="window")
plt.plot(fft_freq, rft_no_window, "-.", label="no window")
plt.legend()
plt.show()
Without any noise (just comment lines in python out), the fft without window gives the exact amplitude (290), with window the amplitude is to high:
With noise, the amplitude with window is still to high, and the amplitude without window is somewhat to low: