Total sound pressure level calculation and 1/3 octave band sound pressure level calculation

    Recently, I have been studying the calculation algorithm of sound pressure level. To be honest, I have searched a lot of information on the Internet, most of which are formulas for converting a certain sound pressure value into DB. This formula can be found in any book, so you need to explain it in detail. eh?

    Without further ado, I suggest that those who want to understand my blog first look for information to study what 1/3 octave is and the basic formula for sound pressure level calculation. When calculating the sound pressure level of the 1/3 octave band, it is necessary to figure out the future life of the Fourier transform, and the purpose is to make the amplitude of the frequency domain correspond accurately. This blog chose base 2 to determine the upper and lower frequencies of the octave band when doing the octave calculations. The most important sentence for calculating the sound pressure level: the mean square value of the sound pressure in each octave band is the sum of the mean square values ​​of the amplitudes of the spectral lines in the frequency band.

    Explain that the mean square value is a statistic. What is the mean square value of a spectral line? This is to correspond this spectral line to the time-domain signal to find its mean square value, that is, divide the amplitude of this spectral line by the root sign 2. The mean square value of the total sound pressure is the sum of the mean square values ​​in each octave band. Attached below is my calculation code:

import numpy as np
from matplotlib import pyplot as plt
from numpy.fft import fft
import math


def fft_custom(sig, Fs, zero_padding=False, window=None):
    if zero_padding:
        fft_num = np.power(2, math.ceil(np.log2(len(sig))))
        if window:
            mag = np.fft.fft(sig * np.hanning(len(sig)), fft_num) * 2 / fft_num
            f = np.arange(fft_num) * (Fs / fft_num)
        else:
            mag = np.fft.fft(sig, fft_num) * 2 / fft_num
            f = np.arange(fft_num) * (Fs / fft_num)
    else:
        fft_num = len(sig)
        if window:
            mag = np.fft.fft(sig * np.hanning(len(sig)), fft_num) * 2 / fft_num
            f = np.arange(fft_num) * (Fs / fft_num)
        else:
            mag = np.fft.fft(sig, fft_num) * 2 / fft_num
            f = np.arange(fft_num) * (Fs / fft_num)
    mag[0] /= 2
    return f[:int(fft_num / 2)], abs(mag[:int(fft_num / 2)])


def get_octave_band_base_2(start=-23, end=14):
    """以2为基数返回1/3倍频程带"""
    bands = list()
    for index, i in enumerate(range(start, end)):
        central_frequency = 1000 * (2 ** (1 / 3)) ** i
        if index == 0:
            bands.append([0, central_frequency * np.power(2, 1 / 6)])
        else:
            bands.append([central_frequency / np.power(2, 1 / 6), central_frequency * np.power(2, 1 / 6)])
    return np.asarray(bands)


if __name__ == '__main__':
    # 仿真信号
    # Fs = 10000
    # N = 50000
    # n = list(np.arange(N))
    # t = np.asarray([temp / Fs for temp in n])
    # sig = np.sqrt(2) * np.sin(2 * np.pi * 5 * t) + np.sin(2 * np.pi * 18 * t) + np.sin(2 * np.pi * 53 * t) + np.sin(
    #     2 * np.pi * 197 * t)

    # 实际信号
    path = r'C:\Users\Administrator\Desktop\yourfilepath.txt'
    Fs = 20833
    sig = np.loadtxt(path)
    N = len(sig)
    n = list(np.arange(N))
    t = np.asarray([temp/Fs for temp in n])

    # 1 时域内计算声压级
    temp = [temp * temp for temp in sig]
    p_square_time = np.sum(temp) / len(temp)  # 时域内计算的声压总均方值
    print("p_square_time: ", p_square_time)

    f, mag = fft_custom(sig, Fs)
    # 频域内计算声压总均方值
    temp = [(temp / np.sqrt(2)) * (temp / np.sqrt(2)) for temp in mag]
    p_square_frequency = np.sum(temp)
    print("p_square_frequency: ", p_square_frequency)

    spl_overall = 10 * np.log10(p_square_time / 0.0000000004)

    print("声压级(DB):", spl_overall)

    bands = get_octave_band_base_2(start=-21, end=15)  # start 和 end 控制带宽
    spl_of_bands = list()
    f = list(f)
    index_start = 0
    for band in bands:
        index_stop = np.where(f < band[1])[0][-1]
        band_frequencies_mag = mag[index_start:index_stop]
        temp = [(temp / np.sqrt(2)) * (temp / np.sqrt(2)) for temp in band_frequencies_mag]
        p_square_frequency_band = np.sum(temp)
        spl_ = 10 * np.log10(p_square_frequency_band / 0.0000000004)
        if band[0] <= Fs/2:
            spl_of_bands.append([band[0], band[1], spl_])
        else:
            break
        # print("index_start: %d index_stop: %d" % (index_start, index_stop))
        index_start = index_stop
        # print(band)

    plt.figure(1)
    plt.subplot(211)
    plt.plot(t, sig)
    plt.subplot(212)
    plt.plot(f, mag)
    # plt.show()

    spl_values = list()
    xticks = list()

    for temp in spl_of_bands:
        spl_values.append(temp[2])
        xticks.append(str(int(temp[1])))

    plt.figure(2)
    plt.bar(range(len(spl_values)), spl_values, facecolor='b', width=0.8)
    plt.title("1/3 octave SPL || Overall SPL is %f " % np.round(spl_overall, 3))
    plt.xticks(list(range(len(spl_values))), xticks,rotation=45)
    plt.show()

    Just show you the effect of running this code:

    

       You can also use the simulation signal I provided to verify the accuracy of the sound pressure level calculation algorithm, welcome to communicate~

Guess you like

Origin blog.csdn.net/D344896224/article/details/108675377