Source code for mojo.analysis.acousticPressureScales

"""
acousticPressureScales.py
=========================

Module with methods to process 1D data sample with extension especially for acoustic applications.
"""

import math
import numpy as np

from .windowedFFT import WindowedFFT

# @todo: implement output in 1/3 octave bands


def get_a_filter(frequencies):
    """
    returns the A-weighting for given frequencies
    """
    freq_square = np.square(frequencies)  # pylint: disable=assignment-from-no-return
    a_filter = math.pow(12000, 2) * np.power(frequencies, 4) / ((freq_square + math.pow(20.6, 2)) *
                                                                (freq_square + math.pow(12200, 2)) * np.sqrt(freq_square + math.pow(107.7, 2)) *
                                                                np.sqrt(freq_square + math.pow(737.9, 2)))

    a_filter = np.ma.masked_less_equal(a_filter, 0., copy=False)
    a_filter = 20 * np.ma.log10(a_filter) + 2.

    return a_filter.filled(fill_value=0.)


def get_b_filter(frequencies):
    """
    returns the B-weighting for given frequencies
    """

    freq_square = np.square(frequencies)  # pylint: disable=assignment-from-no-return
    b_filter = math.pow(12000, 2) * np.power(frequencies, 3) / ((freq_square + math.pow(20.6, 2)) * (freq_square +
                                                                                                     math.pow(12200, 2)) * np.sqrt(freq_square + math.pow(158.5, 2)))

    b_filter = np.ma.masked_less_equal(b_filter, 0., copy=False)
    b_filter = 20 * np.ma.log10(b_filter) + 0.17

    return b_filter.filled(fill_value=0.)


def get_c_filter(frequencies):
    """
    returns the C-weighting for given frequencies
    """
    freq_square = np.square(frequencies)  # pylint: disable=assignment-from-no-return
    c_filter = math.pow(12000, 2) * freq_square / ((freq_square + math.pow(20.6, 2)) *
                                                   (freq_square + math.pow(12200, 2)))

    c_filter = np.ma.masked_less_equal(c_filter, 0., copy=False)
    c_filter = 20 * np.ma.log10(c_filter) + 0.06

    return c_filter.filled(fill_value=0.)


[docs]class AcousticPressureScales: """ Class to process 1D data sample with extension especially for acoustic applications. """ def __init__(self, pressure_time, d_time, pressure_reference=0.00002): self.__frequency = None self.__pressure_time = pressure_time self.__pressure_frq = None self.__pressure_frq_log = None self.__pref = pressure_reference self.__d_time = d_time self.__window_type = "" self.__window_size = 0 self.__window_overlap = 0 self.set_windowing_params("FLAT", self.__pressure_time.size, self.__window_overlap)
[docs] def set_windowing_params(self, window_type, window_size, window_overlap): """ set or change the parameter for the FFT """ self.__window_type = window_type self.__window_size = window_size self.__window_overlap = window_overlap
[docs] def get_autocorrelation(self): """ calculate the auto correlation of a signal """ signal_length = self.__pressure_time.size variance = np.var(self.__pressure_time) auto_corr = np.correlate(self.__pressure_time, self.__pressure_time, mode='full')[-signal_length:] auto_corr_norm = auto_corr / (variance * (np.arange(signal_length, 0, -1))) return auto_corr_norm
def __perform_fft(self, pressure): """ perform and store the FFT """ if self.__pressure_frq is None: data_fft = WindowedFFT(pressure, self.__d_time, self.__window_type, self.__window_size) self.__frequency = data_fft.getSingleSidedFrequencies() self.__pressure_frq = data_fft.getSingleSidedSpectrum() def __compute_log_scale(self): """ convert a equidistant linear frequency array into a equidistant log-scale array """ if self.__pressure_frq_log is None: self.__pressure_frq_log = 10 * np.log10(np.square(np.abs(self.__pressure_frq) / self.__pref))
[docs] def get_spl(self, filter_name=None): """ Convert the pressure amplitudes to SPL based unit """ self.__perform_fft(self.__pressure_time) self.__compute_log_scale() if filter_name is None: spl = self.__pressure_frq_log else: if filter_name.upper() == "A": filter_func = get_a_filter(self.__frequency) elif filter_name.upper() == "B": filter_func = get_b_filter(self.__frequency) elif filter_name.upper() == "C": filter_func = get_c_filter(self.__frequency) else: raise ValueError("No filter function defined with name '%s'." % filter_name) spl = filter_func * self.__pressure_frq_log return self.__frequency, spl
[docs] def get_psd(self): """ Convert the pressure amplitudes to PSD based unit """ auto_corr = self.get_autocorrelation() self.__perform_fft(auto_corr) self.__compute_log_scale() return self.__frequency, self.__pressure_frq_log