178 lines
4.9 KiB
Python
178 lines
4.9 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
Sound level meter implementation
|
|
@author: J.A. de Jong - ASCEE
|
|
"""
|
|
from .wrappers import SPLowpass
|
|
from .lasp_computewidget import ComputeWidget
|
|
import numpy as np
|
|
from .lasp_config import zeros
|
|
from .lasp_common import (FreqWeighting, sens, calfile,
|
|
TimeWeighting, getTime, P_REF)
|
|
from .lasp_weighcal import WeighCal
|
|
from .lasp_gui_tools import wait_cursor
|
|
from .lasp_figure import FigureDialog, PlotOptions
|
|
from .ui_slmwidget import Ui_SlmWidget
|
|
|
|
__all__ = ['SLM', 'SlmWidget']
|
|
|
|
|
|
class Dummy:
|
|
"""
|
|
Emulate filtering, but does not filter anything at all.
|
|
"""
|
|
|
|
def filter_(self, data):
|
|
return data[:, np.newaxis]
|
|
|
|
|
|
class SLM:
|
|
"""
|
|
Sound Level Meter, implements the single pole lowpass filter
|
|
"""
|
|
|
|
def __init__(self, fs, weighcal,
|
|
tw=TimeWeighting.default,
|
|
):
|
|
"""
|
|
Initialize a sound level meter object. Number of channels comes from
|
|
the weighcal object
|
|
|
|
Args:
|
|
fs: Sampling frequency [Hz]
|
|
weighcal: WeighCal instance used for calibration and frequency
|
|
weighting.
|
|
nchannels: Number of channels to allocate filters for
|
|
"""
|
|
nchannels = weighcal.nchannels
|
|
self.nchannels = nchannels
|
|
self._weighcal = weighcal
|
|
if tw[0] is not TimeWeighting.none[0]:
|
|
self._lps = [SPLowpass(fs, tw[0]) for i in range(nchannels)]
|
|
else:
|
|
self._lps = [Dummy() for i in range(nchannels)]
|
|
self._Lmax = zeros(nchannels)
|
|
|
|
@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:
|
|
"""
|
|
if data.ndim == 1:
|
|
data = data[:, np.newaxis]
|
|
|
|
data_weighted = self._weighcal.filter_(data)
|
|
|
|
# Squared
|
|
sq = data_weighted**2
|
|
if sq.shape[0] == 0:
|
|
return np.array([])
|
|
|
|
tw = []
|
|
|
|
# Time-weight the signal
|
|
for chan, lp in enumerate(self._lps):
|
|
tw.append(lp.filter_(sq[:, chan])[:, 0])
|
|
|
|
tw = np.asarray(tw).transpose()
|
|
|
|
Level = 10*np.log10(tw/P_REF**2)
|
|
|
|
curmax = np.max(Level)
|
|
if curmax > self._Lmax:
|
|
self._Lmax = curmax
|
|
|
|
return Level
|
|
|
|
|
|
class SlmWidget(ComputeWidget, Ui_SlmWidget):
|
|
def __init__(self):
|
|
"""
|
|
Initialize the SlmWidget.
|
|
"""
|
|
super().__init__()
|
|
self.setupUi(self)
|
|
FreqWeighting.fillComboBox(self.freqweighting)
|
|
|
|
self.setMeas(None)
|
|
|
|
def setMeas(self, meas):
|
|
"""
|
|
Set the current measurement for this widget.
|
|
|
|
Args:
|
|
meas: if None, the Widget is disabled
|
|
"""
|
|
self.meas = meas
|
|
if meas is None:
|
|
self.setEnabled(False)
|
|
else:
|
|
self.setEnabled(True)
|
|
rt = meas.recTime
|
|
self.starttime.setRange(0, rt, 0)
|
|
self.stoptime.setRange(0, rt, rt)
|
|
|
|
self.channel.clear()
|
|
for i in range(meas.nchannels):
|
|
self.channel.addItem(str(i))
|
|
self.channel.setCurrentIndex(0)
|
|
|
|
def compute(self):
|
|
"""
|
|
Compute Sound Level using settings. This method is
|
|
called whenever the Compute button is pushed in the SLM tab
|
|
"""
|
|
meas = self.meas
|
|
fs = meas.samplerate
|
|
channel = self.channel.currentIndex()
|
|
tw = TimeWeighting.getCurrent(self.timeweighting)
|
|
fw = FreqWeighting.getCurrent(self.freqweighting)
|
|
|
|
# Downsampling factor of result
|
|
dsf = self.downsampling.value()
|
|
# gb = self.slmFre
|
|
|
|
with wait_cursor():
|
|
# This one exctracts the calfile and sensitivity from global
|
|
# variables defined at the top. # TODO: Change this to a more
|
|
# robust variant.
|
|
weighcal = WeighCal(fw, nchannels=1,
|
|
fs=fs, calfile=calfile,
|
|
sens=sens)
|
|
|
|
slm = SLM(fs, weighcal, tw)
|
|
praw = meas.praw()[:, [channel]]
|
|
|
|
# Filter, downsample data
|
|
filtered = slm.addData(praw)[::dsf, :]
|
|
N = filtered.shape[0]
|
|
time = getTime(float(fs)/dsf, N)
|
|
Lmax = slm.Lmax
|
|
|
|
pto = PlotOptions()
|
|
pto.ylabel = f'L{fw[0]} [dB({fw[0]})]'
|
|
pto.xlim = (time[0], time[-1])
|
|
fig, new = self.getFigure(pto)
|
|
fig.fig.plot(time, filtered)
|
|
fig.show()
|
|
|
|
stats = f"""Statistical results:
|
|
=============================
|
|
Applied frequency weighting: {fw[1]}
|
|
Applied time weighting: {tw[1]}
|
|
Applied Downsampling factor: {dsf}
|
|
Maximum level (L{fw[0]} max): {Lmax:4.4} [dB({fw[0]})]
|
|
|
|
"""
|
|
self.results.setPlainText(stats)
|