From b61fb7b0149411d5dd697692b4b0d879190e5eae Mon Sep 17 00:00:00 2001 From: "J.A. de Jong - Redu-Sone B.V., ASCEE V.O.F" Date: Fri, 3 Feb 2023 20:41:59 +0100 Subject: [PATCH] Split timeweighting into different types for each of the possible use cases. --- .gitignore | 1 + src/lasp/dsp/lasp_slm.cpp | 71 ++++++++++++++++++++------------------- src/lasp/lasp_common.py | 49 ++++++++++++++++----------- src/lasp/lasp_slm.py | 19 +++++------ 4 files changed, 76 insertions(+), 64 deletions(-) diff --git a/.gitignore b/.gitignore index c6e64f4..6caaa0c 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ doc .spyproject .cache _skbuild +acme_log.log diff --git a/src/lasp/dsp/lasp_slm.cpp b/src/lasp/dsp/lasp_slm.cpp index 81fec5a..e934800 100644 --- a/src/lasp/dsp/lasp_slm.cpp +++ b/src/lasp/dsp/lasp_slm.cpp @@ -13,28 +13,29 @@ using rte = std::runtime_error; using std::unique_ptr; SLM::SLM(const d fs, const d Lref, const us downsampling_fac, const d tau, - std::unique_ptr pre_filter, - std::vector> bandpass) - : _pre_filter(std::move(pre_filter)), _bandpass(std::move(bandpass)), - _alpha(exp(-1 / (fs * tau))), - _sp_storage(_bandpass.size(), arma::fill::zeros), // Storage for - // components of - // single pole low pass - // filter - Lrefsq(Lref*Lref), // Reference level - downsampling_fac(downsampling_fac), + std::unique_ptr pre_filter, + std::vector> bandpass) + : _pre_filter(std::move(pre_filter)), _bandpass(std::move(bandpass)), + _alpha(exp(-1 / (fs * tau))), + _sp_storage(_bandpass.size(), arma::fill::zeros), // Storage for + // components of + // single pole low pass + // filter + Lrefsq(Lref * Lref), // Reference level + downsampling_fac(downsampling_fac), - // Initalize mean square - Pm(_bandpass.size(), arma::fill::zeros), + // Initalize mean square + Pm(_bandpass.size(), arma::fill::zeros), - // Initalize max - Pmax(_bandpass.size(), arma::fill::zeros), + // Initalize max + Pmax(_bandpass.size(), arma::fill::zeros), - // Initalize peak - Ppeak(_bandpass.size(), arma::fill::zeros) + // Initalize peak + Ppeak(_bandpass.size(), arma::fill::zeros) { DEBUGTRACE_ENTER; + DEBUGTRACE_PRINT(_alpha); // Make sure thread pool is running getPool(); @@ -42,7 +43,7 @@ SLM::SLM(const d fs, const d Lref, const us downsampling_fac, const d tau, if (Lref <= 0) { throw rte("Invalid reference level"); } - if (tau <= 0) { + if (tau < 0) { throw rte("Invalid time constant for Single pole lowpass filter"); } if (fs <= 0) { @@ -66,43 +67,43 @@ std::vector> createBandPass(const dmat &coefs) { } return bf; } -us SLM::suggestedDownSamplingFac(const d fs,const d tau) { - if(tau<0) throw rte("Invalid time weighting time constant"); - if(fs<=0) throw rte("Invalid sampling frequency"); +us SLM::suggestedDownSamplingFac(const d fs, const d tau) { + if (fs <= 0) + throw rte("Invalid sampling frequency"); // A reasonable 'framerate' for the sound level meter, based on the // filtering time constant. if (tau > 0) { d fs_slm = 10 / tau; - if(fs_slm < 30) { + if (fs_slm < 30) { fs_slm = 30; } - return std::max((us) 1, static_cast(fs / fs_slm)); + return std::max((us)1, static_cast(fs / fs_slm)); } else { return 1; } } SLM SLM::fromBiquads(const d fs, const d Lref, const us downsampling_fac, - const d tau, const vd &pre_filter_coefs, - const dmat &bandpass_coefs) { + const d tau, const vd &pre_filter_coefs, + const dmat &bandpass_coefs) { DEBUGTRACE_ENTER; return SLM(fs, Lref, downsampling_fac, tau, - std::make_unique(pre_filter_coefs), - createBandPass(bandpass_coefs)); + std::make_unique(pre_filter_coefs), + createBandPass(bandpass_coefs)); } SLM SLM::fromBiquads(const d fs, const d Lref, const us downsampling_fac, - const d tau, const dmat &bandpass_coefs) { + const d tau, const dmat &bandpass_coefs) { DEBUGTRACE_ENTER; return SLM(fs, Lref, downsampling_fac, tau, - nullptr, // Pre-filter - createBandPass(bandpass_coefs) // Bandpass coefficients - ); + nullptr, // Pre-filter + createBandPass(bandpass_coefs) // Bandpass coefficients + ); } -vd SLM::run_single(vd work,const us i) { +vd SLM::run_single(vd work, const us i) { // Filter input in-place _bandpass[i]->filter(work); @@ -126,7 +127,7 @@ vd SLM::run_single(vd work,const us i) { for (us j = 0; j < work.n_rows; j++) { // Update mean square of signal, work is here still signal power Pm(i) = (Pm(i) * static_cast(N_local) + work(j)) / - (static_cast(N_local) + 1); + (static_cast(N_local) + 1); N_local++; @@ -142,7 +143,7 @@ vd SLM::run_single(vd work,const us i) { Pmax(i) = std::max(Pmax(i), arma::max(work)); // Convert to levels in dB - work = 10*arma::log10((work+arma::datum::eps)/Lrefsq); + work = 10 * arma::log10((work + arma::datum::eps) / Lrefsq); return work; } @@ -170,8 +171,8 @@ dmat SLM::run(const vd &input_orig) { /* DEBUGTRACE_PRINT(res.n_rows); */ /* DEBUGTRACE_PRINT(futs.size()); */ - // Update the total number of samples harvested so far. NOTE: *This should be - // done AFTER the threads are done!!!* + // Update the total number of samples harvested so far. NOTE: *This should + // be done AFTER the threads are done!!!* } N += input.n_rows; diff --git a/src/lasp/lasp_common.py b/src/lasp/lasp_common.py index 4bc7e59..696ac76 100644 --- a/src/lasp/lasp_common.py +++ b/src/lasp/lasp_common.py @@ -286,46 +286,57 @@ class this_lasp_shelve(Shelve): return os.path.join(lasp_appdir, f'{node}_config.shelve') class TimeWeighting: - none = (-1, 'Raw (no time weighting)') + # This is not actually a time weighting + none = (0, 'Raw (no time weighting)') + uufast = (1e-4, '0.1 ms') ufast = (35e-3, 'Impulse (35 ms)') fast = (0.125, 'Fast (0.125 s)') slow = (1.0, 'Slow (1.0 s)') tens = (10., '10 s') - infinite = (0, 'Infinite') - types_realtime = (ufast, fast, slow, tens, infinite) - types_all = (none, uufast, ufast, fast, slow, tens, infinite) + + # All-averaging: compute the current average of all history. Kind of the + # equivalent level. Only useful for power spectra. For Sound Level Meter, + # equivalent levels does that thing. + averaging = (-1, 'All-averaging') + + types_all = (none, uufast, ufast, fast, slow, tens, averaging) + + # Used for combobox of realt time power spectra + types_realtimeaps = (ufast, fast, slow, tens, averaging) + # Used for combobox of realt time sound level meter + types_realtimeslm = (ufast, fast, slow, tens) + + # Used for combobox of sound level meter - time dependent + types_slmt = (none, uufast, ufast, fast, slow, tens) + + # Used for combobox of sound level meter - Lmax statistics + types_slmstats = (uufast, ufast, fast, slow, tens) default = fast - default_index = 3 - default_index_realtime = 1 @staticmethod - def fillComboBox(cb, realtime=False): + def fillComboBox(cb, types): """ Fill TimeWeightings to a combobox Args: cb: QComboBox to fill + types: The types to fill it with """ cb.clear() - if realtime: - types = TimeWeighting.types_realtime - defindex = TimeWeighting.default_index_realtime - else: - types = TimeWeighting.types_all - defindex = TimeWeighting.default_index + + logging.debug(f'{types}') for tw in types: cb.addItem(tw[1], tw) - - cb.setCurrentIndex(defindex) + if TimeWeighting.default == tw: + cb.setCurrentIndex(cb.count()-1) @staticmethod def getCurrent(cb): - if cb.count() == len(TimeWeighting.types_realtime): - return TimeWeighting.types_realtime[cb.currentIndex()] - else: - return TimeWeighting.types_all[cb.currentIndex()] + for type in TimeWeighting.types_all: + if cb.currentText() == type[1]: + return type class FreqWeighting: diff --git a/src/lasp/lasp_slm.py b/src/lasp/lasp_slm.py index 6dbc135..9b605dc 100644 --- a/src/lasp/lasp_slm.py +++ b/src/lasp/lasp_slm.py @@ -32,10 +32,10 @@ class SLM: def __init__(self, fs, fbdesigner=None, - tw: TimeWeighting =TimeWeighting.fast, - fw: FreqWeighting =FreqWeighting.A, - xmin = None, - xmax = None, + tw: TimeWeighting = TimeWeighting.fast, + fw: FreqWeighting = FreqWeighting.A, + xmin=None, + xmax=None, include_overall=True, level_ref_value=P_REF, offset_t=0): @@ -72,7 +72,8 @@ class SLM: self.xs = list(range(xmin, xmax + 1)) nfilters = len(self.xs) - if include_overall: nfilters +=1 + if include_overall: + nfilters += 1 self.include_overall = include_overall self.offset_t = offset_t @@ -91,7 +92,7 @@ class SLM: # This is a bit of a hack, as the 5 is hard-encoded here, but should in # fact be coming from somewhere else.. - sos_overall = np.array([1,0,0,1,0,0]*5, dtype=float) + sos_overall = np.array([1, 0, 0, 1, 0, 0]*5, dtype=float) if fbdesigner is not None: assert fbdesigner.fs == fs @@ -108,14 +109,14 @@ class SLM: # Create a unit impulse response filter, every third index equals # 1, so b0 = 1 and a0 is 1 (by definition) # a0 = 1, b0 = 1, rest is zero - sos[:,-1] = sos_overall + sos[:, -1] = sos_overall self.nom_txt.append('overall') else: # No filterbank, means we do only compute the overall values. This # means that in case of include_overall, it creates two overall # channels. That would be confusing, so we do not allow it. - sos = sos_overall[:,np.newaxis] + sos = sos_overall[:, np.newaxis] self.nom_txt.append('overall') # Downsampling factor, determine from single pole low pass filter time @@ -137,7 +138,6 @@ class SLM: # Initialize counter to 0 self.N = 0 - def run(self, data): """ Add new fresh timedata to the Sound Level Meter @@ -169,7 +169,6 @@ class SLM: return output - def return_as_dict(self, dat): """ Helper function used to return resulting data in a proper way.