来自麦克风信号 FFT 的 SPL 值

信息处理 fft 声音的
2022-01-03 22:28:52

我正在尝试为录制的声音获取频率响应与 dB SPL 的关系图。我有一个响应平坦的麦克风,灵敏度为 4mV/Pa,1 Pa = 94dB。

要获得 dB SPL,我有以下公式:

dBSPL=94+20log10(AI/0.004)

其中 AI 来自麦克风的输入电压,并假设没有增益。

这似乎可以使用峰值电压作为 AI 来获得信号的整体 dB SPL 水平。

我的问题是,如何将其应用于输入信号的 FFT,以获得每个频率的强度级别?

我看到了这篇文章,但不确定如何使其适应输入电压。

这是一些 python 代码来演示我到目前为止所拥有的,我的目标是将其应用于包含许多不同频率分量的超声波信号。

import numpy as np

fs = 5e5
duration = 1
npts = fs*duration
t = np.arange(npts, dtype=float)/fs
f = 1000
amp = 1.414*0.004   
signal = amp * np.sin((f*duration) * np.linspace(0, 2*np.pi, npts))

rms = np.sqrt(np.mean(signal**2))
dbspl = 94 + 20*np.log10(rms/0.004)

sp = np.fft.fft(signal)
freq = np.arange(npts)/(npts/fs)

sp_db = 94 + 20*np.log10(???)
1个回答

假设您正在分析的信号是幅度为的正弦波:A

x=asin2πf0t

然后,它的幅度 RMS 值是:,正如您在代码中注意到的那样。在执行 DFT 之前,最好对信号进行窗口化,因为它可以减少泄漏等。a2

将此信号转换到频域后,您将获得一个双边频谱。然后我们必须取这些复数值的大小。可能你会注意到它们都非常高。那是因为能量没有标准化。标准化可以通过将幅度值除以窗口的能量来完成。由于人们倾向于“按原样”获取信号(这等于应用矩形窗口),因此频谱会除以样本数对于不同类型的窗口,这个因子等于所有窗口样本的总和。N

此外,我们只对频谱的一半感兴趣,因此所有样本的幅度必须乘以以补偿能量损失,除了 DC 分量(仅出现一次)和 Nyquist bin(如果存在)。2

最后,当您要分析信号的 RMS 时,您必须了解以下内容。单面谱的所有值都是高度,等于,其中是第i 个频率分量的 RMS 幅度幅度。将其转换为您获得的 Python 代码:Ai22(Ai2)2Ai2

import numpy as np
import matplotlib.pyplot as plt

fs = 5e5       # Sampling frequency [Hz]
duration = 10  # Duration of the test signal [s]

N = int(fs * duration)
t = np.arange(N, dtype=float) / fs

f = 1000  # Frequency of the test signal [Hz]
sensitivity = 0.004  # Sensitivity of the microphone [mV/Pa]
p_ref = 2e-5 # Reference acoustic pressure [Pa]
amp = sensitivity * np.sqrt(2)  # Amplitude of sinusoidal signal with RMS of 4 mV (94 dB SPL)
signal = amp * np.sin(2 * np.pi * f * duration * t)  # Signal [V]

# Calculate the level from time domain signal
rms_time = np.sqrt(np.mean(signal**2))
db_time = 20 * np.log10(rms_time / sensitivity / p_ref)

# Apply window to the signal
win = np.hamming(N)
signal = signal * win

# Get the spectrum and shift it so that DC is in the middle
spectrum = np.fft.fftshift( np.fft.fft(signal) )
freq = np.fft.fftshift( np.fft.fftfreq(N, 1 / fs) )

# Take only the positive frequencies
spectrum = spectrum[N//2:]
freq = freq[N//2:]

# Since we just removed the energy in negative frequencies, account for that
spectrum *= 2
# If there is even number of samples, do not normalize the Nyquist bin
if N % 2 == 0:
    spectrum[-1] /= 2

# Scale the magnitude of FFT by window energy
spectrum_mag = np.abs(spectrum) / np.sum(win)

# To obtain RMS values, divide by sqrt(2)
spectrum_rms = spectrum_mag / np.sqrt(2)
# Do not scale the DC component
spectrum_rms[0] *= np.sqrt(2) / 2

# Convert to decibel scale
spectrum_db = 20 * np.log10(spectrum_rms / sensitivity / p_ref)

# Compare the outputs
print("Difference in levels: {} dB".format(db_time - spectrum_db.max()))

plt.semilogx(freq, spectrum_db)
plt.xlim( (1, fs/2)  )
plt.ylim( (0, 120) )
plt.grid('on')
plt.xlabel("Frequency [Hz]")
plt.ylabel("SPL [dB]")

plt.show()

在此处输入图像描述

电平差异:-1.4210854715202004e-14 dB

如您所见,这为时域和频域产生了一致的结果。祝你好运!