lasp/lasp/lasp_slm.py

102 lines
2.4 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Sound level meter implementation
@author: J.A. de Jong - ASCEE
"""
from .wrappers import Slm as pyxSlm
import numpy as np
from .lasp_common import (TimeWeighting, P_REF)
__all__ = ['SLM', 'Dummy']
class Dummy:
"""
Emulate filtering, but does not filter anything at all.
"""
def filter_(self, data):
return data[:, np.newaxis]
class SLM:
"""
Multi-channel Sound Level Meter. Input data: time data with a certain
sampling frequency. Output: time-weighted (fast/slow) sound pressure
levels in dB(A/C/Z). Possibly in octave bands.
"""
def __init__(self, fs, tw=TimeWeighting.default, ):
"""
Initialize a sound level meter object.
Number of channels comes from the weighcal object.
Args:
fs: Sampling frequency [Hz]
tw: Time Weighting to apply
"""
if tw[0] is not TimeWeighting.none[0]:
# Initialize the single-pole low-pass filter for given time-
# weighting value.
self._lp = SPLowpass(fs, tw[0])
else:
self._lp = Dummy()
self._Lmax = 0.
# Storage for computing the equivalent level
self._sq = 0. # Square of the level data, storage
self._N = 0
self._Leq = 0.
@property
def Leq(self):
"""
Returns the equivalent level of the recorded signal so far
"""
return self._Leq
@property
def Lmax(self):
"""
Returns the currently maximum recorded level
"""
return self._Lmax
def addData(self, data):
"""
Add new fresh timedata to the Sound Level Meter
Args:
data:
returns:
level values as a function of time
"""
assert data.ndim == 2
assert data.shape[1] == 1
P_REFsq = P_REF**2
# Squared
sq = data**2
# Update equivalent level
N1 = sq.shape[0]
self._sq = (np.sum(sq) + self._sq*self._N)/(N1+self._N)
self._N += N1
self._Leq = 10*np.log10(self._sq/P_REFsq)
# Time-weight the signal
tw = self._lp.filter_(sq)
Level = 10*np.log10(tw/P_REFsq)
# Update maximum level
curmax = np.max(Level)
if curmax > self._Lmax:
self._Lmax = curmax
return Level