Split timeweighting into different types for each of the possible use cases.
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
9aba6040f7
commit
b61fb7b014
1
.gitignore
vendored
1
.gitignore
vendored
@ -20,3 +20,4 @@ doc
|
|||||||
.spyproject
|
.spyproject
|
||||||
.cache
|
.cache
|
||||||
_skbuild
|
_skbuild
|
||||||
|
acme_log.log
|
||||||
|
@ -13,28 +13,29 @@ using rte = std::runtime_error;
|
|||||||
using std::unique_ptr;
|
using std::unique_ptr;
|
||||||
|
|
||||||
SLM::SLM(const d fs, const d Lref, const us downsampling_fac, const d tau,
|
SLM::SLM(const d fs, const d Lref, const us downsampling_fac, const d tau,
|
||||||
std::unique_ptr<Filter> pre_filter,
|
std::unique_ptr<Filter> pre_filter,
|
||||||
std::vector<std::unique_ptr<Filter>> bandpass)
|
std::vector<std::unique_ptr<Filter>> bandpass)
|
||||||
: _pre_filter(std::move(pre_filter)), _bandpass(std::move(bandpass)),
|
: _pre_filter(std::move(pre_filter)), _bandpass(std::move(bandpass)),
|
||||||
_alpha(exp(-1 / (fs * tau))),
|
_alpha(exp(-1 / (fs * tau))),
|
||||||
_sp_storage(_bandpass.size(), arma::fill::zeros), // Storage for
|
_sp_storage(_bandpass.size(), arma::fill::zeros), // Storage for
|
||||||
// components of
|
// components of
|
||||||
// single pole low pass
|
// single pole low pass
|
||||||
// filter
|
// filter
|
||||||
Lrefsq(Lref*Lref), // Reference level
|
Lrefsq(Lref * Lref), // Reference level
|
||||||
downsampling_fac(downsampling_fac),
|
downsampling_fac(downsampling_fac),
|
||||||
|
|
||||||
// Initalize mean square
|
// Initalize mean square
|
||||||
Pm(_bandpass.size(), arma::fill::zeros),
|
Pm(_bandpass.size(), arma::fill::zeros),
|
||||||
|
|
||||||
// Initalize max
|
// Initalize max
|
||||||
Pmax(_bandpass.size(), arma::fill::zeros),
|
Pmax(_bandpass.size(), arma::fill::zeros),
|
||||||
|
|
||||||
// Initalize peak
|
// Initalize peak
|
||||||
Ppeak(_bandpass.size(), arma::fill::zeros)
|
Ppeak(_bandpass.size(), arma::fill::zeros)
|
||||||
|
|
||||||
{
|
{
|
||||||
DEBUGTRACE_ENTER;
|
DEBUGTRACE_ENTER;
|
||||||
|
DEBUGTRACE_PRINT(_alpha);
|
||||||
|
|
||||||
// Make sure thread pool is running
|
// Make sure thread pool is running
|
||||||
getPool();
|
getPool();
|
||||||
@ -42,7 +43,7 @@ SLM::SLM(const d fs, const d Lref, const us downsampling_fac, const d tau,
|
|||||||
if (Lref <= 0) {
|
if (Lref <= 0) {
|
||||||
throw rte("Invalid reference level");
|
throw rte("Invalid reference level");
|
||||||
}
|
}
|
||||||
if (tau <= 0) {
|
if (tau < 0) {
|
||||||
throw rte("Invalid time constant for Single pole lowpass filter");
|
throw rte("Invalid time constant for Single pole lowpass filter");
|
||||||
}
|
}
|
||||||
if (fs <= 0) {
|
if (fs <= 0) {
|
||||||
@ -66,43 +67,43 @@ std::vector<unique_ptr<Filter>> createBandPass(const dmat &coefs) {
|
|||||||
}
|
}
|
||||||
return bf;
|
return bf;
|
||||||
}
|
}
|
||||||
us SLM::suggestedDownSamplingFac(const d fs,const d tau) {
|
us SLM::suggestedDownSamplingFac(const d fs, const d tau) {
|
||||||
if(tau<0) throw rte("Invalid time weighting time constant");
|
if (fs <= 0)
|
||||||
if(fs<=0) throw rte("Invalid sampling frequency");
|
throw rte("Invalid sampling frequency");
|
||||||
// A reasonable 'framerate' for the sound level meter, based on the
|
// A reasonable 'framerate' for the sound level meter, based on the
|
||||||
// filtering time constant.
|
// filtering time constant.
|
||||||
if (tau > 0) {
|
if (tau > 0) {
|
||||||
d fs_slm = 10 / tau;
|
d fs_slm = 10 / tau;
|
||||||
if(fs_slm < 30) {
|
if (fs_slm < 30) {
|
||||||
fs_slm = 30;
|
fs_slm = 30;
|
||||||
}
|
}
|
||||||
return std::max((us) 1, static_cast<us>(fs / fs_slm));
|
return std::max((us)1, static_cast<us>(fs / fs_slm));
|
||||||
} else {
|
} else {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SLM SLM::fromBiquads(const d fs, const d Lref, const us downsampling_fac,
|
SLM SLM::fromBiquads(const d fs, const d Lref, const us downsampling_fac,
|
||||||
const d tau, const vd &pre_filter_coefs,
|
const d tau, const vd &pre_filter_coefs,
|
||||||
const dmat &bandpass_coefs) {
|
const dmat &bandpass_coefs) {
|
||||||
DEBUGTRACE_ENTER;
|
DEBUGTRACE_ENTER;
|
||||||
|
|
||||||
return SLM(fs, Lref, downsampling_fac, tau,
|
return SLM(fs, Lref, downsampling_fac, tau,
|
||||||
std::make_unique<SeriesBiquad>(pre_filter_coefs),
|
std::make_unique<SeriesBiquad>(pre_filter_coefs),
|
||||||
createBandPass(bandpass_coefs));
|
createBandPass(bandpass_coefs));
|
||||||
}
|
}
|
||||||
SLM SLM::fromBiquads(const d fs, const d Lref, const us downsampling_fac,
|
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;
|
DEBUGTRACE_ENTER;
|
||||||
|
|
||||||
return SLM(fs, Lref, downsampling_fac, tau,
|
return SLM(fs, Lref, downsampling_fac, tau,
|
||||||
nullptr, // Pre-filter
|
nullptr, // Pre-filter
|
||||||
createBandPass(bandpass_coefs) // Bandpass coefficients
|
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
|
// Filter input in-place
|
||||||
_bandpass[i]->filter(work);
|
_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++) {
|
for (us j = 0; j < work.n_rows; j++) {
|
||||||
// Update mean square of signal, work is here still signal power
|
// Update mean square of signal, work is here still signal power
|
||||||
Pm(i) = (Pm(i) * static_cast<d>(N_local) + work(j)) /
|
Pm(i) = (Pm(i) * static_cast<d>(N_local) + work(j)) /
|
||||||
(static_cast<d>(N_local) + 1);
|
(static_cast<d>(N_local) + 1);
|
||||||
|
|
||||||
N_local++;
|
N_local++;
|
||||||
|
|
||||||
@ -142,7 +143,7 @@ vd SLM::run_single(vd work,const us i) {
|
|||||||
Pmax(i) = std::max(Pmax(i), arma::max(work));
|
Pmax(i) = std::max(Pmax(i), arma::max(work));
|
||||||
|
|
||||||
// Convert to levels in dB
|
// 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;
|
return work;
|
||||||
}
|
}
|
||||||
@ -170,8 +171,8 @@ dmat SLM::run(const vd &input_orig) {
|
|||||||
/* DEBUGTRACE_PRINT(res.n_rows); */
|
/* DEBUGTRACE_PRINT(res.n_rows); */
|
||||||
/* DEBUGTRACE_PRINT(futs.size()); */
|
/* DEBUGTRACE_PRINT(futs.size()); */
|
||||||
|
|
||||||
// Update the total number of samples harvested so far. NOTE: *This should be
|
// Update the total number of samples harvested so far. NOTE: *This should
|
||||||
// done AFTER the threads are done!!!*
|
// be done AFTER the threads are done!!!*
|
||||||
}
|
}
|
||||||
N += input.n_rows;
|
N += input.n_rows;
|
||||||
|
|
||||||
|
@ -286,46 +286,57 @@ class this_lasp_shelve(Shelve):
|
|||||||
return os.path.join(lasp_appdir, f'{node}_config.shelve')
|
return os.path.join(lasp_appdir, f'{node}_config.shelve')
|
||||||
|
|
||||||
class TimeWeighting:
|
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')
|
uufast = (1e-4, '0.1 ms')
|
||||||
ufast = (35e-3, 'Impulse (35 ms)')
|
ufast = (35e-3, 'Impulse (35 ms)')
|
||||||
fast = (0.125, 'Fast (0.125 s)')
|
fast = (0.125, 'Fast (0.125 s)')
|
||||||
slow = (1.0, 'Slow (1.0 s)')
|
slow = (1.0, 'Slow (1.0 s)')
|
||||||
tens = (10., '10 s')
|
tens = (10., '10 s')
|
||||||
infinite = (0, 'Infinite')
|
|
||||||
types_realtime = (ufast, fast, slow, tens, infinite)
|
# All-averaging: compute the current average of all history. Kind of the
|
||||||
types_all = (none, uufast, ufast, fast, slow, tens, infinite)
|
# 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 = fast
|
||||||
default_index = 3
|
|
||||||
default_index_realtime = 1
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def fillComboBox(cb, realtime=False):
|
def fillComboBox(cb, types):
|
||||||
"""
|
"""
|
||||||
Fill TimeWeightings to a combobox
|
Fill TimeWeightings to a combobox
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
cb: QComboBox to fill
|
cb: QComboBox to fill
|
||||||
|
types: The types to fill it with
|
||||||
"""
|
"""
|
||||||
cb.clear()
|
cb.clear()
|
||||||
if realtime:
|
|
||||||
types = TimeWeighting.types_realtime
|
logging.debug(f'{types}')
|
||||||
defindex = TimeWeighting.default_index_realtime
|
|
||||||
else:
|
|
||||||
types = TimeWeighting.types_all
|
|
||||||
defindex = TimeWeighting.default_index
|
|
||||||
for tw in types:
|
for tw in types:
|
||||||
cb.addItem(tw[1], tw)
|
cb.addItem(tw[1], tw)
|
||||||
|
if TimeWeighting.default == tw:
|
||||||
cb.setCurrentIndex(defindex)
|
cb.setCurrentIndex(cb.count()-1)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def getCurrent(cb):
|
def getCurrent(cb):
|
||||||
if cb.count() == len(TimeWeighting.types_realtime):
|
for type in TimeWeighting.types_all:
|
||||||
return TimeWeighting.types_realtime[cb.currentIndex()]
|
if cb.currentText() == type[1]:
|
||||||
else:
|
return type
|
||||||
return TimeWeighting.types_all[cb.currentIndex()]
|
|
||||||
|
|
||||||
|
|
||||||
class FreqWeighting:
|
class FreqWeighting:
|
||||||
|
@ -32,10 +32,10 @@ class SLM:
|
|||||||
def __init__(self,
|
def __init__(self,
|
||||||
fs,
|
fs,
|
||||||
fbdesigner=None,
|
fbdesigner=None,
|
||||||
tw: TimeWeighting =TimeWeighting.fast,
|
tw: TimeWeighting = TimeWeighting.fast,
|
||||||
fw: FreqWeighting =FreqWeighting.A,
|
fw: FreqWeighting = FreqWeighting.A,
|
||||||
xmin = None,
|
xmin=None,
|
||||||
xmax = None,
|
xmax=None,
|
||||||
include_overall=True,
|
include_overall=True,
|
||||||
level_ref_value=P_REF,
|
level_ref_value=P_REF,
|
||||||
offset_t=0):
|
offset_t=0):
|
||||||
@ -72,7 +72,8 @@ class SLM:
|
|||||||
self.xs = list(range(xmin, xmax + 1))
|
self.xs = list(range(xmin, xmax + 1))
|
||||||
|
|
||||||
nfilters = len(self.xs)
|
nfilters = len(self.xs)
|
||||||
if include_overall: nfilters +=1
|
if include_overall:
|
||||||
|
nfilters += 1
|
||||||
self.include_overall = include_overall
|
self.include_overall = include_overall
|
||||||
self.offset_t = offset_t
|
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
|
# This is a bit of a hack, as the 5 is hard-encoded here, but should in
|
||||||
# fact be coming from somewhere else..
|
# 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:
|
if fbdesigner is not None:
|
||||||
assert fbdesigner.fs == fs
|
assert fbdesigner.fs == fs
|
||||||
@ -108,14 +109,14 @@ class SLM:
|
|||||||
# Create a unit impulse response filter, every third index equals
|
# Create a unit impulse response filter, every third index equals
|
||||||
# 1, so b0 = 1 and a0 is 1 (by definition)
|
# 1, so b0 = 1 and a0 is 1 (by definition)
|
||||||
# a0 = 1, b0 = 1, rest is zero
|
# a0 = 1, b0 = 1, rest is zero
|
||||||
sos[:,-1] = sos_overall
|
sos[:, -1] = sos_overall
|
||||||
self.nom_txt.append('overall')
|
self.nom_txt.append('overall')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# No filterbank, means we do only compute the overall values. This
|
# No filterbank, means we do only compute the overall values. This
|
||||||
# means that in case of include_overall, it creates two overall
|
# means that in case of include_overall, it creates two overall
|
||||||
# channels. That would be confusing, so we do not allow it.
|
# 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')
|
self.nom_txt.append('overall')
|
||||||
|
|
||||||
# Downsampling factor, determine from single pole low pass filter time
|
# Downsampling factor, determine from single pole low pass filter time
|
||||||
@ -137,7 +138,6 @@ class SLM:
|
|||||||
# Initialize counter to 0
|
# Initialize counter to 0
|
||||||
self.N = 0
|
self.N = 0
|
||||||
|
|
||||||
|
|
||||||
def run(self, data):
|
def run(self, data):
|
||||||
"""
|
"""
|
||||||
Add new fresh timedata to the Sound Level Meter
|
Add new fresh timedata to the Sound Level Meter
|
||||||
@ -169,7 +169,6 @@ class SLM:
|
|||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
|
||||||
def return_as_dict(self, dat):
|
def return_as_dict(self, dat):
|
||||||
"""
|
"""
|
||||||
Helper function used to return resulting data in a proper way.
|
Helper function used to return resulting data in a proper way.
|
||||||
|
Loading…
Reference in New Issue
Block a user