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