Smoothing: minor bug fix
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Casper Jansen 2023-02-23 16:37:08 +01:00
parent cacfc7fe6c
commit b3fb7ddb6d

View File

@ -5,13 +5,10 @@ Author: C. Jansen, J.A. de Jong - ASCEE V.O.F.
Smooth data in the frequency domain. Smooth data in the frequency domain.
TODO: This function is rather slow as it TODO: This function is rather slow as it uses [for loops] in Python. Speed up.
used Python for loops. The implementations should be speed up in the near NOTE: function requires lin frequency spaced input data
future. TODO: accept input data that is not lin spaced in frequency
TODO: check if the smoothing is correct: depending on whether the data points TODO: it makes more sense to output data that is log spaced in frequency
are spaced lin of log in frequency, they should be given different weights.
TODO: accept data that is not equally spaced in frequency
TODO: output data that is log spaced in frequency
Cutoff frequencies of window taken from Cutoff frequencies of window taken from
http://www.huennebeck-online.de/software/download/src/index.html 15-10-2021 http://www.huennebeck-online.de/software/download/src/index.html 15-10-2021
@ -91,17 +88,20 @@ def smoothCalcMatrix(freq, sw: SmoothingWidth):
Warning: this method does not work on levels (dB) Warning: this method does not work on levels (dB)
""" """
# Settings # Settings
tr = 2 # truncate window after 2x std; shorter is faster and less accurate tr = 3 # truncate window after 2x std; shorter is faster and less accurate
Noct = sw.value[0] Noct = sw.value[0]
assert Noct > 0, "'Noct' must be absolute positive" assert Noct > 0, "'Noct' must be absolute positive"
assert np.isclose(freq[-1]-freq[-2], freq[1]-freq[0]), "Input data must "\
"have a linear frequency spacing"
if Noct < 1: if Noct < 1:
raise Warning('Check if \'Noct\' is entered correctly') raise Warning('Check if \'Noct\' is entered correctly')
# Initialize # Initialize
L = len(freq) L = len(freq)
Q = np.zeros(shape=(L, L), dtype=np.float16) # float16: keep size small Q = np.zeros(shape=(L, L), dtype=np.float16) # float16: keep size small
Q[0, 0] = 1 # in case first point is skipped
x0 = 1 if freq[0] == 0 else 0 # Skip first data point if zero frequency x0 = 1 if freq[0] == 0 else 0 # Skip first data point if zero frequency
# Loop over indices of raw frequency vector # Loop over indices of raw frequency vector
for x in range(x0, L): for x in range(x0, L):
# Find indices of data points to calculate current (smoothed) magnitude # Find indices of data points to calculate current (smoothed) magnitude
@ -128,9 +128,9 @@ def smoothCalcMatrix(freq, sw: SmoothingWidth):
# Find indices corresponding to frequencies # Find indices corresponding to frequencies
xl = bisect.bisect_left(freq, fl) # index corresponding to fl xl = bisect.bisect_left(freq, fl) # index corresponding to fl
xu = bisect.bisect_left(freq, fu) xu = bisect.bisect_right(freq, fu)
xl = xu-1 if xu-xl <= 0 else xl # Guarantee window length of at least 1 xl = xu-1 if xu-xl <= 0 else xl # Guarantee window length >= 1
# Calculate window # Calculate window
xg = np.arange(xl, xu) # indices xg = np.arange(xl, xu) # indices
@ -139,6 +139,10 @@ def smoothCalcMatrix(freq, sw: SmoothingWidth):
gs /= np.sum(gs) # normalize: integral=1 gs /= np.sum(gs) # normalize: integral=1
Q[x, xl:xu] = gs # add to matrix Q[x, xl:xu] = gs # add to matrix
# Normalize to conserve input power
Qpower = np.sum(Q, axis=0)
Q = Q / Qpower[np.newaxis, :]
return Q return Q
@ -147,13 +151,13 @@ def smoothSpectralData(freq, M, sw: SmoothingWidth,
""" """
Apply fractional octave smoothing to magnitude data in frequency domain. Apply fractional octave smoothing to magnitude data in frequency domain.
Smoothing is performed to power, using a sliding Gaussian window with Smoothing is performed to power, using a sliding Gaussian window with
variable length. The window is truncated after 2x std at either side. variable length. The window is truncated after 3x std at either side.
The implementation is not exact, because f is linearly spaced and The implementation is not exact, because f is linearly spaced and
fractional octave smoothing is related to log spaced data. In this fractional octave smoothing is related to log spaced data. In this
implementation, the window extends with a fixed frequency step to either implementation, the window extends with a fixed frequency step to either
side. The deviation is largest when Noct is small (e.g. coarse smoothing). side. The deviation is largest when Noct is small (e.g. coarse smoothing).
Casper Jansen, 07-05-2021 07-05-2021
Update 16-01-2023: speed up algorithm Update 16-01-2023: speed up algorithm
- Smoothing is performed using matrix multiplication - Smoothing is performed using matrix multiplication
@ -243,7 +247,8 @@ if __name__ == "__main__":
fmax = 24e3 # [Hz] max freq fmax = 24e3 # [Hz] max freq
Ndata = 200 # number of data points Ndata = 200 # number of data points
freq = np.linspace(fmin, fmax, Ndata) # frequency points freq = np.linspace(fmin, fmax, Ndata) # frequency points
M = abs(0.4*np.random.normal(size=(Ndata,)))+0.01 # # freq = np.hstack((0, freq))
M = abs(0.4*np.random.normal(size=(len(freq),)))+0.01 #
M = 20*np.log10(M) M = 20*np.log10(M)
# # Create dummy data set 2: dirac delta # # Create dummy data set 2: dirac delta