67 Calculate integral of (part of) Hann window.
68 If the args are vectors, the return value will match those.
71 x1: lower bound [-0.5, 0.5]
72 x2: upper bound [-0.5, 0.5]
74 Integral of Hann window between x1 and x2
76 x1 = np.clip(x1, -0.5, 0.5)
77 x2 = np.clip(x2, -0.5, 0.5)
78 return (sin(2*pi*x2) - sin(2*pi*x1))/(2*pi) + (x2-x1)
84 freq: array of frequencies of data points [Hz] - equally spaced
88 freq: array frequencies of data points [Hz]
89 Q: matrix to smooth, power: {fsm} = [Q] * {fraw}
91 Warning: this method does not work on levels (dB)
93 According to Tylka_JAES_SmoothingWeights.pdf
94 "A Generalized Method for Fractional-Octave Smoothing of Transfer Functions
95 that Preserves Log-Frequency Symmetry"
96 https://doi.org/10.17743/jaes.2016.0053
102 assert Noct > 0,
"'Noct' must be absolute positive"
103 assert np.isclose(freq[-1]-freq[-2], freq[1]-freq[0]),
"Input data must "\
104 "have a linear frequency spacing"
106 raise Warning(
'Check if \'Noct\' is entered correctly')
110 Q = np.zeros(shape=(L, L), dtype=np.float16)
112 x0 = 1
if freq[0] == 0
else 0
115 ifreq = freq/(freq[1]-freq[0])
116 ifreq = np.array(ifreq.astype(int))
118 ifreqMax = ifreq[L-1]
119 sfact = 2**((1/Noct)/2)
121 kpmin = np.floor(ifreq/sfact).astype(int)
122 kpmax = np.ceil(ifreq*sfact).astype(int)
124 for ff
in range(x0, L):
127 if kpmin[ff] < ifreqMin:
129 kpmax[ff] = np.ceil(ifreq[ff]**2/ifreqMin)
130 if np.isclose(kpmin[ff], kpmax[ff]):
132 NoctAct = 1/log2(kpmax[ff]/kpmin[ff])
133 elif kpmax[ff] > ifreqMax:
134 kpmin[ff] = np.floor(ifreq[ff]**2/ifreqMax)
136 if np.isclose(kpmin[ff], kpmax[ff]):
138 NoctAct = 1/log2(kpmax[ff]/kpmin[ff])
142 kp = np.arange(kpmin[ff], kpmax[ff]+1)
143 Phi1 = log2((kp - 0.5)/ifreq[ff]) * NoctAct
144 Phi2 = log2((kp + 0.5)/ifreq[ff]) * NoctAct
146 Q[ff, kpmin[ff]-ifreq[0]:kpmax[ff]-ifreq[0]+1] = W
149 Qpower = np.sum(Q, axis=0)
150 Q = Q / Qpower[np.newaxis, :]
156 st: SmoothingType = SmoothingType.levels):
158 Apply fractional octave smoothing to data in the frequency domain.
161 freq: array of frequencies of data points [Hz] - equally spaced
162 M: array of data, either power or dB
163 the smoothing type `st`, the smoothing is applied.
165 st: smoothing type = data type of input data
168 freq : array frequencies of data points [Hz]
169 Msm : float smoothed magnitude of data points
172 MM = copy.deepcopy(M)
174 assert len(MM) > 0,
"Smoothing function: input array is empty"
175 assert Noct > 0,
"'Noct' must be absolute positive"
177 raise Warning(
'Check if \'Noct\' is entered correctly')
178 assert len(freq) == len(MM),
"f and M should have equal length"
180 if st == SmoothingType.ps:
181 assert np.min(MM) >= 0,
'Power spectrum values cannot be negative'
182 if st == SmoothingType.levels
and isinstance(MM.dtype, complex):
183 raise RuntimeError(
'Decibel input should be real-valued')
186 if st == SmoothingType.levels:
188 elif st == SmoothingType.ps:
191 raise RuntimeError(f
"Incorrect SmoothingType: {st}")
194 Psm = np.zeros_like(P)
201 nfft = int(2*(len(freq)-1))
202 key = f
"nfft{nfft}_Noct{Noct}"
204 if 'Qdict' not in globals():
215 Psm = np.matmul(Q, P)
218 if st == SmoothingType.levels:
219 Psm = 10*np.log10(Psm)