acoular 24.10__py3-none-any.whl → 25.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- acoular/__init__.py +5 -2
- acoular/aiaa/__init__.py +12 -0
- acoular/{tools → aiaa}/aiaa.py +23 -28
- acoular/base.py +75 -55
- acoular/calib.py +129 -34
- acoular/configuration.py +11 -9
- acoular/demo/__init__.py +1 -0
- acoular/demo/acoular_demo.py +29 -16
- acoular/deprecation.py +85 -0
- acoular/environments.py +31 -19
- acoular/fastFuncs.py +90 -84
- acoular/fbeamform.py +203 -411
- acoular/fprocess.py +49 -41
- acoular/grids.py +101 -143
- acoular/h5cache.py +29 -40
- acoular/h5files.py +2 -6
- acoular/microphones.py +50 -59
- acoular/process.py +366 -59
- acoular/sdinput.py +23 -20
- acoular/signals.py +116 -109
- acoular/sources.py +201 -240
- acoular/spectra.py +53 -229
- acoular/tbeamform.py +79 -202
- acoular/tfastfuncs.py +21 -21
- acoular/tools/__init__.py +2 -8
- acoular/tools/helpers.py +216 -2
- acoular/tools/metrics.py +4 -4
- acoular/tools/utils.py +106 -200
- acoular/tprocess.py +348 -309
- acoular/traitsviews.py +10 -10
- acoular/trajectory.py +7 -10
- acoular/version.py +2 -2
- {acoular-24.10.dist-info → acoular-25.1.dist-info}/METADATA +38 -17
- acoular-25.1.dist-info/RECORD +56 -0
- {acoular-24.10.dist-info → acoular-25.1.dist-info}/WHEEL +1 -1
- acoular-24.10.dist-info/RECORD +0 -54
- {acoular-24.10.dist-info → acoular-25.1.dist-info}/licenses/AUTHORS.rst +0 -0
- {acoular-24.10.dist-info → acoular-25.1.dist-info}/licenses/LICENSE +0 -0
acoular/spectra.py
CHANGED
|
@@ -8,10 +8,10 @@
|
|
|
8
8
|
|
|
9
9
|
BaseSpectra
|
|
10
10
|
PowerSpectra
|
|
11
|
-
synthetic
|
|
12
11
|
PowerSpectraImport
|
|
13
12
|
"""
|
|
14
13
|
|
|
14
|
+
from abc import abstractmethod
|
|
15
15
|
from warnings import warn
|
|
16
16
|
|
|
17
17
|
from numpy import (
|
|
@@ -25,51 +25,53 @@ from numpy import (
|
|
|
25
25
|
hamming,
|
|
26
26
|
hanning,
|
|
27
27
|
imag,
|
|
28
|
-
isscalar,
|
|
29
28
|
linalg,
|
|
30
29
|
ndarray,
|
|
31
30
|
newaxis,
|
|
32
31
|
ones,
|
|
33
32
|
real,
|
|
34
33
|
searchsorted,
|
|
35
|
-
sum,
|
|
34
|
+
sum, # noqa A004
|
|
36
35
|
zeros,
|
|
37
|
-
zeros_like,
|
|
38
36
|
)
|
|
39
37
|
from scipy import fft
|
|
40
38
|
from traits.api import (
|
|
39
|
+
ABCHasStrictTraits,
|
|
41
40
|
Bool,
|
|
42
41
|
CArray,
|
|
43
42
|
Delegate,
|
|
44
43
|
Enum,
|
|
45
44
|
Float,
|
|
46
|
-
HasPrivateTraits,
|
|
47
45
|
Instance,
|
|
48
46
|
Int,
|
|
47
|
+
Map,
|
|
49
48
|
Property,
|
|
50
|
-
|
|
49
|
+
Union,
|
|
51
50
|
cached_property,
|
|
52
51
|
property_depends_on,
|
|
53
52
|
)
|
|
54
53
|
|
|
54
|
+
# acoular imports
|
|
55
55
|
from .base import SamplesGenerator
|
|
56
|
-
from .calib import Calib
|
|
57
56
|
from .configuration import config
|
|
57
|
+
from .deprecation import deprecated_alias
|
|
58
58
|
from .fastFuncs import calcCSM
|
|
59
59
|
from .h5cache import H5cache
|
|
60
60
|
from .h5files import H5CacheFileBase
|
|
61
61
|
from .internal import digest
|
|
62
|
+
from .tools.utils import find_basename
|
|
62
63
|
|
|
63
64
|
|
|
64
|
-
|
|
65
|
+
@deprecated_alias({'numchannels': 'num_channels'}, read_only=True)
|
|
66
|
+
class BaseSpectra(ABCHasStrictTraits):
|
|
65
67
|
#: Data source; :class:`~acoular.sources.SamplesGenerator` or derived object.
|
|
66
|
-
source =
|
|
68
|
+
source = Instance(SamplesGenerator)
|
|
67
69
|
|
|
68
70
|
#: Sampling frequency of output signal, as given by :attr:`source`.
|
|
69
71
|
sample_freq = Delegate('source')
|
|
70
72
|
|
|
71
73
|
#: Number of time data channels
|
|
72
|
-
|
|
74
|
+
num_channels = Delegate('source')
|
|
73
75
|
|
|
74
76
|
#: Window function for FFT, one of:
|
|
75
77
|
#: * 'Rectangular' (default)
|
|
@@ -77,23 +79,22 @@ class BaseSpectra(HasPrivateTraits):
|
|
|
77
79
|
#: * 'Hamming'
|
|
78
80
|
#: * 'Bartlett'
|
|
79
81
|
#: * 'Blackman'
|
|
80
|
-
window =
|
|
81
|
-
'Rectangular',
|
|
82
|
+
window = Map(
|
|
82
83
|
{'Rectangular': ones, 'Hanning': hanning, 'Hamming': hamming, 'Bartlett': bartlett, 'Blackman': blackman},
|
|
84
|
+
default_value='Rectangular',
|
|
83
85
|
desc='type of window for FFT',
|
|
84
86
|
)
|
|
85
87
|
|
|
86
88
|
#: Overlap factor for averaging: 'None'(default), '50%', '75%', '87.5%'.
|
|
87
|
-
overlap =
|
|
89
|
+
overlap = Map({'None': 1, '50%': 2, '75%': 4, '87.5%': 8}, default_value='None', desc='overlap of FFT blocks')
|
|
88
90
|
|
|
89
91
|
#: FFT block size, one of: 128, 256, 512, 1024, 2048 ... 65536,
|
|
90
92
|
#: defaults to 1024.
|
|
91
|
-
block_size =
|
|
93
|
+
block_size = Enum(
|
|
92
94
|
1024,
|
|
93
95
|
128,
|
|
94
96
|
256,
|
|
95
97
|
512,
|
|
96
|
-
1024,
|
|
97
98
|
2048,
|
|
98
99
|
4096,
|
|
99
100
|
8192,
|
|
@@ -105,14 +106,14 @@ class BaseSpectra(HasPrivateTraits):
|
|
|
105
106
|
|
|
106
107
|
#: The floating-number-precision of the resulting spectra, corresponding to numpy dtypes.
|
|
107
108
|
#: Default is 'complex128'.
|
|
108
|
-
precision =
|
|
109
|
+
precision = Enum('complex128', 'complex64', desc='precision of the fft')
|
|
109
110
|
|
|
110
111
|
# internal identifier
|
|
111
112
|
digest = Property(depends_on=['precision', 'block_size', 'window', 'overlap'])
|
|
112
113
|
|
|
113
|
-
@
|
|
114
|
+
@abstractmethod
|
|
114
115
|
def _get_digest(self):
|
|
115
|
-
|
|
116
|
+
"""Return internal identifier."""
|
|
116
117
|
|
|
117
118
|
def fftfreq(self):
|
|
118
119
|
"""Return the Discrete Fourier Transform sample frequencies.
|
|
@@ -130,7 +131,7 @@ class BaseSpectra(HasPrivateTraits):
|
|
|
130
131
|
# generator that yields the time data blocks for every channel (with optional overlap)
|
|
131
132
|
def _get_source_data(self):
|
|
132
133
|
bs = self.block_size
|
|
133
|
-
temp = empty((2 * bs, self.
|
|
134
|
+
temp = empty((2 * bs, self.num_channels))
|
|
134
135
|
pos = bs
|
|
135
136
|
posinc = bs / self.overlap_
|
|
136
137
|
for data_block in self.source.result(bs):
|
|
@@ -153,7 +154,7 @@ class PowerSpectra(BaseSpectra):
|
|
|
153
154
|
the CSM's eigenvalues and eigenvectors and additional properties.
|
|
154
155
|
|
|
155
156
|
The result is computed only when needed, that is when the :attr:`csm`,
|
|
156
|
-
:attr:`eva`, or :attr:`eve` attributes are
|
|
157
|
+
:attr:`eva`, or :attr:`eve` attributes are actually read.
|
|
157
158
|
Any change in the input data or parameters leads to a new calculation,
|
|
158
159
|
again triggered when an attribute is read. The result may be
|
|
159
160
|
cached on disk in HDF5 files and need not to be recomputed during
|
|
@@ -162,32 +163,14 @@ class PowerSpectra(BaseSpectra):
|
|
|
162
163
|
and the same file name in case of that the data is read from a file.
|
|
163
164
|
"""
|
|
164
165
|
|
|
165
|
-
# Shadow trait, should not be set directly, for internal use.
|
|
166
|
-
_source = Trait(SamplesGenerator)
|
|
167
|
-
|
|
168
166
|
#: Data source; :class:`~acoular.sources.SamplesGenerator` or derived object.
|
|
169
|
-
source =
|
|
170
|
-
|
|
171
|
-
#: The :class:`~acoular.base.SamplesGenerator` object that provides the data.
|
|
172
|
-
time_data = Property(
|
|
173
|
-
_source,
|
|
174
|
-
desc='deprecated attribute holding the time data object. Use PowerSpectra.source instead!',
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
#: The :class:`~acoular.calib.Calib` object that provides the calibration data,
|
|
178
|
-
#: defaults to no calibration, i.e. the raw time data is used.
|
|
179
|
-
#:
|
|
180
|
-
#: **deprecated, will be removed in version 25.01**: use :attr:`~acoular.sources.TimeSamples.calib` property of
|
|
181
|
-
#: :class:`~acoular.sources.TimeSamples` objects
|
|
182
|
-
calib = Property(desc='calibration object (deprecated, will be removed in version 25.01)')
|
|
183
|
-
|
|
184
|
-
_calib = Instance(Calib)
|
|
167
|
+
source = Instance(SamplesGenerator)
|
|
185
168
|
|
|
186
169
|
# Shadow trait, should not be set directly, for internal use.
|
|
187
170
|
_ind_low = Int(1, desc='index of lowest frequency line')
|
|
188
171
|
|
|
189
172
|
# Shadow trait, should not be set directly, for internal use.
|
|
190
|
-
_ind_high =
|
|
173
|
+
_ind_high = Union(Int(-1), None, desc='index of highest frequency line')
|
|
191
174
|
|
|
192
175
|
#: Index of lowest frequency line to compute, integer, defaults to 1,
|
|
193
176
|
#: is used only by objects that fetch the csm, PowerSpectra computes every
|
|
@@ -202,7 +185,7 @@ class PowerSpectra(BaseSpectra):
|
|
|
202
185
|
_freqlc = Float(0)
|
|
203
186
|
|
|
204
187
|
# Stores the set higher frequency, for internal use, should not be set directly.
|
|
205
|
-
_freqhc =
|
|
188
|
+
_freqhc = Union(Float(0), None)
|
|
206
189
|
|
|
207
190
|
# Saves whether the user set indices or frequencies last, for internal use only,
|
|
208
191
|
# not to be set directly, if True (default), indices are used for setting
|
|
@@ -232,10 +215,10 @@ class PowerSpectra(BaseSpectra):
|
|
|
232
215
|
indices = Property(desc='index range')
|
|
233
216
|
|
|
234
217
|
#: Name of the cache file without extension, readonly.
|
|
235
|
-
basename = Property(depends_on='
|
|
218
|
+
basename = Property(depends_on=['source.digest'], desc='basename for cache file')
|
|
236
219
|
|
|
237
220
|
#: The cross spectral matrix,
|
|
238
|
-
#: (number of frequencies,
|
|
221
|
+
#: (number of frequencies, num_channels, num_channels) array of complex;
|
|
239
222
|
#: readonly.
|
|
240
223
|
csm = Property(desc='cross spectral matrix')
|
|
241
224
|
|
|
@@ -244,35 +227,23 @@ class PowerSpectra(BaseSpectra):
|
|
|
244
227
|
eva = Property(desc='eigenvalues of cross spectral matrix')
|
|
245
228
|
|
|
246
229
|
#: Eigenvectors of the cross spectral matrix as an
|
|
247
|
-
#: (number of frequencies,
|
|
230
|
+
#: (number of frequencies, num_channels, num_channels) array of floats,
|
|
248
231
|
#: readonly.
|
|
249
232
|
eve = Property(desc='eigenvectors of cross spectral matrix')
|
|
250
233
|
|
|
251
234
|
# internal identifier
|
|
252
235
|
digest = Property(
|
|
253
|
-
depends_on=['
|
|
236
|
+
depends_on=['source.digest', 'block_size', 'window', 'overlap', 'precision'],
|
|
254
237
|
)
|
|
255
238
|
|
|
256
239
|
# hdf5 cache file
|
|
257
240
|
h5f = Instance(H5CacheFileBase, transient=True)
|
|
258
241
|
|
|
259
|
-
|
|
260
|
-
return self._calib
|
|
261
|
-
|
|
262
|
-
def _set_calib(self, calib):
|
|
263
|
-
msg = (
|
|
264
|
-
"Using 'calib' attribute is deprecated and will be removed in version 25.01. "
|
|
265
|
-
'use :attr:`~acoular.sources.TimeSamples.calib` property of '
|
|
266
|
-
':class:`~acoular.sources.TimeSamples` object instead.'
|
|
267
|
-
)
|
|
268
|
-
warn(msg, DeprecationWarning, stacklevel=2)
|
|
269
|
-
self._calib = calib
|
|
270
|
-
|
|
271
|
-
@property_depends_on('_source.numsamples, block_size, overlap')
|
|
242
|
+
@property_depends_on(['source.num_samples', 'block_size', 'overlap'])
|
|
272
243
|
def _get_num_blocks(self):
|
|
273
|
-
return self.overlap_ * self.
|
|
244
|
+
return self.overlap_ * self.source.num_samples / self.block_size - self.overlap_ + 1
|
|
274
245
|
|
|
275
|
-
@property_depends_on('
|
|
246
|
+
@property_depends_on(['source.sample_freq', 'block_size', 'ind_low', 'ind_high'])
|
|
276
247
|
def _get_freq_range(self):
|
|
277
248
|
fftfreq = self.fftfreq()
|
|
278
249
|
if fftfreq is not None:
|
|
@@ -286,7 +257,7 @@ class PowerSpectra(BaseSpectra):
|
|
|
286
257
|
self._freqlc = freq_range[0]
|
|
287
258
|
self._freqhc = freq_range[1]
|
|
288
259
|
|
|
289
|
-
@property_depends_on('
|
|
260
|
+
@property_depends_on(['source.sample_freq', 'block_size', '_ind_low', '_freqlc'])
|
|
290
261
|
def _get_ind_low(self):
|
|
291
262
|
fftfreq = self.fftfreq()
|
|
292
263
|
if fftfreq is not None:
|
|
@@ -295,7 +266,7 @@ class PowerSpectra(BaseSpectra):
|
|
|
295
266
|
return searchsorted(fftfreq[:-1], self._freqlc)
|
|
296
267
|
return None
|
|
297
268
|
|
|
298
|
-
@property_depends_on('
|
|
269
|
+
@property_depends_on(['source.sample_freq', 'block_size', '_ind_high', '_freqhc'])
|
|
299
270
|
def _get_ind_high(self):
|
|
300
271
|
fftfreq = self.fftfreq()
|
|
301
272
|
if fftfreq is not None:
|
|
@@ -316,24 +287,7 @@ class PowerSpectra(BaseSpectra):
|
|
|
316
287
|
self._index_set_last = True
|
|
317
288
|
self._ind_low = ind_low
|
|
318
289
|
|
|
319
|
-
|
|
320
|
-
msg = (
|
|
321
|
-
"Using 'time_data' attribute is deprecated and will be removed in version 25.01. "
|
|
322
|
-
"Use 'source' attribute instead."
|
|
323
|
-
)
|
|
324
|
-
warn(msg, DeprecationWarning, stacklevel=2)
|
|
325
|
-
self._source = time_data
|
|
326
|
-
|
|
327
|
-
def _set_source(self, source):
|
|
328
|
-
self._source = source
|
|
329
|
-
|
|
330
|
-
def _get_time_data(self):
|
|
331
|
-
return self._source
|
|
332
|
-
|
|
333
|
-
def _get_source(self):
|
|
334
|
-
return self._source
|
|
335
|
-
|
|
336
|
-
@property_depends_on('block_size, ind_low, ind_high')
|
|
290
|
+
@property_depends_on(['block_size', 'ind_low', 'ind_high'])
|
|
337
291
|
def _get_indices(self):
|
|
338
292
|
fftfreq = self.fftfreq()
|
|
339
293
|
if fftfreq is not None:
|
|
@@ -352,9 +306,7 @@ class PowerSpectra(BaseSpectra):
|
|
|
352
306
|
|
|
353
307
|
@cached_property
|
|
354
308
|
def _get_basename(self):
|
|
355
|
-
|
|
356
|
-
return self._source.basename
|
|
357
|
-
return self._source.__class__.__name__ + self._source.digest
|
|
309
|
+
return find_basename(self.source, alternative_basename=self.source.__class__.__name__ + self.source.digest)
|
|
358
310
|
|
|
359
311
|
def calc_csm(self):
|
|
360
312
|
"""Csm calculation."""
|
|
@@ -363,15 +315,8 @@ class PowerSpectra(BaseSpectra):
|
|
|
363
315
|
weight = dot(wind, wind)
|
|
364
316
|
wind = wind[newaxis, :].swapaxes(0, 1)
|
|
365
317
|
numfreq = int(self.block_size / 2 + 1)
|
|
366
|
-
csm_shape = (numfreq, t.
|
|
318
|
+
csm_shape = (numfreq, t.num_channels, t.num_channels)
|
|
367
319
|
csm_upper = zeros(csm_shape, dtype=self.precision)
|
|
368
|
-
# print "num blocks", self.num_blocks
|
|
369
|
-
# for backward compatibility
|
|
370
|
-
if self.calib and self.calib.num_mics > 0:
|
|
371
|
-
if self.calib.num_mics == t.numchannels:
|
|
372
|
-
wind = wind * self.calib.data[newaxis, :]
|
|
373
|
-
else:
|
|
374
|
-
raise ValueError('Calibration data not compatible: %i, %i' % (self.calib.num_mics, t.numchannels))
|
|
375
320
|
# get time data blockwise
|
|
376
321
|
for data in self._get_source_data():
|
|
377
322
|
ft = fft.rfft(data * wind, None, 0).astype(self.precision)
|
|
@@ -405,23 +350,6 @@ class PowerSpectra(BaseSpectra):
|
|
|
405
350
|
"""Calculates eigenvectors of csm."""
|
|
406
351
|
return self.calc_ev()[1]
|
|
407
352
|
|
|
408
|
-
def _handle_dual_calibration(self):
|
|
409
|
-
obj = self.source # start with time_data obj
|
|
410
|
-
while obj:
|
|
411
|
-
if 'calib' in obj.all_trait_names(): # at original source?
|
|
412
|
-
if obj.calib and self.calib:
|
|
413
|
-
if obj.calib.digest == self.calib.digest:
|
|
414
|
-
self.calib = None # ignore it silently
|
|
415
|
-
else:
|
|
416
|
-
msg = 'Non-identical dual calibration for both TimeSamples and PowerSpectra object'
|
|
417
|
-
raise ValueError(msg)
|
|
418
|
-
obj = None
|
|
419
|
-
else:
|
|
420
|
-
try:
|
|
421
|
-
obj = obj.source # traverse down until original data source
|
|
422
|
-
except AttributeError:
|
|
423
|
-
obj = None
|
|
424
|
-
|
|
425
353
|
def _get_filecache(self, traitname):
|
|
426
354
|
"""Function handles result caching of csm, eigenvectors and eigenvalues
|
|
427
355
|
calculation depending on global/local caching behaviour.
|
|
@@ -429,7 +357,7 @@ class PowerSpectra(BaseSpectra):
|
|
|
429
357
|
if traitname == 'csm':
|
|
430
358
|
func = self.calc_csm
|
|
431
359
|
numfreq = int(self.block_size / 2 + 1)
|
|
432
|
-
shape = (numfreq, self.
|
|
360
|
+
shape = (numfreq, self.source.num_channels, self.source.num_channels)
|
|
433
361
|
precision = self.precision
|
|
434
362
|
elif traitname == 'eva':
|
|
435
363
|
func = self.calc_eva
|
|
@@ -465,18 +393,17 @@ class PowerSpectra(BaseSpectra):
|
|
|
465
393
|
self.h5f.flush()
|
|
466
394
|
return ac
|
|
467
395
|
|
|
468
|
-
@property_depends_on('digest')
|
|
396
|
+
@property_depends_on(['digest'])
|
|
469
397
|
def _get_csm(self):
|
|
470
398
|
"""Main work is done here:
|
|
471
399
|
Cross spectral matrix is either loaded from cache file or
|
|
472
400
|
calculated and then additionally stored into cache.
|
|
473
401
|
"""
|
|
474
|
-
self._handle_dual_calibration()
|
|
475
402
|
if config.global_caching == 'none' or (config.global_caching == 'individual' and self.cached is False):
|
|
476
403
|
return self.calc_csm()
|
|
477
404
|
return self._get_filecache('csm')
|
|
478
405
|
|
|
479
|
-
@property_depends_on('digest')
|
|
406
|
+
@property_depends_on(['digest'])
|
|
480
407
|
def _get_eva(self):
|
|
481
408
|
"""Eigenvalues of cross spectral matrix are either loaded from cache file or
|
|
482
409
|
calculated and then additionally stored into cache.
|
|
@@ -485,7 +412,7 @@ class PowerSpectra(BaseSpectra):
|
|
|
485
412
|
return self.calc_eva()
|
|
486
413
|
return self._get_filecache('eva')
|
|
487
414
|
|
|
488
|
-
@property_depends_on('digest')
|
|
415
|
+
@property_depends_on(['digest'])
|
|
489
416
|
def _get_eve(self):
|
|
490
417
|
"""Eigenvectors of cross spectral matrix are either loaded from cache file or
|
|
491
418
|
calculated and then additionally stored into cache.
|
|
@@ -532,101 +459,6 @@ class PowerSpectra(BaseSpectra):
|
|
|
532
459
|
return sum(self.eva[f1:f2], 0)
|
|
533
460
|
|
|
534
461
|
|
|
535
|
-
def synthetic(data, freqs, f, num=3):
|
|
536
|
-
"""Returns synthesized frequency band values of spectral data.
|
|
537
|
-
|
|
538
|
-
If used with :meth:`Beamformer.result()<acoular.fbeamform.BeamformerBase.result>`
|
|
539
|
-
and only one frequency band, the output is identical to the result of the intrinsic
|
|
540
|
-
:meth:`Beamformer.synthetic<acoular.fbeamform.BeamformerBase.synthetic>` method.
|
|
541
|
-
It can, however, also be used with the
|
|
542
|
-
:meth:`Beamformer.integrate<acoular.fbeamform.BeamformerBase.integrate>`
|
|
543
|
-
output and more frequency bands.
|
|
544
|
-
|
|
545
|
-
Parameters
|
|
546
|
-
----------
|
|
547
|
-
data : array of floats
|
|
548
|
-
The spectral data (squared sound pressures in Pa^2) in an array with one value
|
|
549
|
-
per frequency line.
|
|
550
|
-
The number of entries must be identical to the number of
|
|
551
|
-
grid points.
|
|
552
|
-
freq : array of floats
|
|
553
|
-
The frequencies that correspon to the input *data* (as yielded by
|
|
554
|
-
the :meth:`PowerSpectra.fftfreq<acoular.spectra.PowerSpectra.fftfreq>`
|
|
555
|
-
method).
|
|
556
|
-
f : float or list of floats
|
|
557
|
-
Band center frequency/frequencies for which to return the results.
|
|
558
|
-
num : integer
|
|
559
|
-
Controls the width of the frequency bands considered; defaults to
|
|
560
|
-
3 (third-octave band).
|
|
561
|
-
|
|
562
|
-
=== =====================
|
|
563
|
-
num frequency band width
|
|
564
|
-
=== =====================
|
|
565
|
-
0 single frequency line
|
|
566
|
-
1 octave band
|
|
567
|
-
3 third-octave band
|
|
568
|
-
n 1/n-octave band
|
|
569
|
-
=== =====================
|
|
570
|
-
|
|
571
|
-
Returns
|
|
572
|
-
-------
|
|
573
|
-
array of floats
|
|
574
|
-
Synthesized frequency band values of the beamforming result at
|
|
575
|
-
each grid point (the sum of all values that are contained in the band).
|
|
576
|
-
Note that the frequency resolution and therefore the bandwidth
|
|
577
|
-
represented by a single frequency line depends on
|
|
578
|
-
the :attr:`sampling frequency<acoular.base.SamplesGenerator.sample_freq>`
|
|
579
|
-
and used :attr:`FFT block size<acoular.spectra.PowerSpectra.block_size>`.
|
|
580
|
-
|
|
581
|
-
"""
|
|
582
|
-
if isscalar(f):
|
|
583
|
-
f = (f,)
|
|
584
|
-
if num == 0:
|
|
585
|
-
# single frequency lines
|
|
586
|
-
res = []
|
|
587
|
-
for i in f:
|
|
588
|
-
ind = searchsorted(freqs, i)
|
|
589
|
-
if ind >= len(freqs):
|
|
590
|
-
warn(
|
|
591
|
-
'Queried frequency (%g Hz) not in resolved frequency range. Returning zeros.' % i,
|
|
592
|
-
Warning,
|
|
593
|
-
stacklevel=2,
|
|
594
|
-
)
|
|
595
|
-
h = zeros_like(data[0])
|
|
596
|
-
else:
|
|
597
|
-
if freqs[ind] != i:
|
|
598
|
-
warn(
|
|
599
|
-
f'Queried frequency ({i:g} Hz) not in set of '
|
|
600
|
-
'discrete FFT sample frequencies. '
|
|
601
|
-
f'Using frequency {freqs[ind]:g} Hz instead.',
|
|
602
|
-
Warning,
|
|
603
|
-
stacklevel=2,
|
|
604
|
-
)
|
|
605
|
-
h = data[ind]
|
|
606
|
-
res += [h]
|
|
607
|
-
else:
|
|
608
|
-
# fractional octave bands
|
|
609
|
-
res = []
|
|
610
|
-
for i in f:
|
|
611
|
-
f1 = i * 2.0 ** (-0.5 / num)
|
|
612
|
-
f2 = i * 2.0 ** (+0.5 / num)
|
|
613
|
-
ind1 = searchsorted(freqs, f1)
|
|
614
|
-
ind2 = searchsorted(freqs, f2)
|
|
615
|
-
if ind1 == ind2:
|
|
616
|
-
warn(
|
|
617
|
-
f'Queried frequency band ({f1:g} to {f2:g} Hz) does not '
|
|
618
|
-
'include any discrete FFT sample frequencies. '
|
|
619
|
-
'Returning zeros.',
|
|
620
|
-
Warning,
|
|
621
|
-
stacklevel=2,
|
|
622
|
-
)
|
|
623
|
-
h = zeros_like(data[0])
|
|
624
|
-
else:
|
|
625
|
-
h = sum(data[ind1:ind2], 0)
|
|
626
|
-
res += [h]
|
|
627
|
-
return array(res)
|
|
628
|
-
|
|
629
|
-
|
|
630
462
|
class PowerSpectraImport(PowerSpectra):
|
|
631
463
|
"""Provides a dummy class for using pre-calculated cross-spectral
|
|
632
464
|
matrices.
|
|
@@ -636,27 +468,24 @@ class PowerSpectraImport(PowerSpectra):
|
|
|
636
468
|
:attr:`csm` attribute. This can be useful when algorithms shall be
|
|
637
469
|
evaluated with existing CSM matrices.
|
|
638
470
|
The frequency or frequencies contained by the CSM must be set via the
|
|
639
|
-
attr:`frequencies` attribute. The attr:`
|
|
471
|
+
attr:`frequencies` attribute. The attr:`num_channels` attributes
|
|
640
472
|
is determined on the basis of the CSM shape.
|
|
641
473
|
In contrast to the PowerSpectra object, the attributes
|
|
642
|
-
:attr:`sample_freq`, :attr:`
|
|
643
|
-
:attr:`block_size`, :attr:`calib`, :attr:`window`,
|
|
474
|
+
:attr:`sample_freq`, :attr:`source`, :attr:`block_size`, :attr:`window`,
|
|
644
475
|
:attr:`overlap`, :attr:`cached`, and :attr:`num_blocks`
|
|
645
476
|
have no functionality.
|
|
646
477
|
"""
|
|
647
478
|
|
|
648
479
|
#: The cross spectral matrix,
|
|
649
|
-
#: (number of frequencies,
|
|
480
|
+
#: (number of frequencies, num_channels, num_channels) array of complex;
|
|
650
481
|
csm = Property(desc='cross spectral matrix')
|
|
651
482
|
|
|
652
483
|
#: frequencies included in the cross-spectral matrix in ascending order.
|
|
653
484
|
#: Compound trait that accepts arguments of type list, array, and float
|
|
654
|
-
frequencies =
|
|
485
|
+
frequencies = Union(CArray, Float, desc='frequencies included in the cross-spectral matrix')
|
|
655
486
|
|
|
656
487
|
#: Number of time data channels
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
time_data = Enum(None, desc='PowerSpectraImport cannot consume time data')
|
|
488
|
+
num_channels = Property(depends_on=['digest'])
|
|
660
489
|
|
|
661
490
|
source = Enum(None, desc='PowerSpectraImport cannot consume time data')
|
|
662
491
|
|
|
@@ -665,8 +494,6 @@ class PowerSpectraImport(PowerSpectra):
|
|
|
665
494
|
|
|
666
495
|
block_size = Enum(None, desc='PowerSpectraImport does not operate on blocks of time data')
|
|
667
496
|
|
|
668
|
-
calib = Enum(None, desc='PowerSpectraImport cannot calibrate the time data')
|
|
669
|
-
|
|
670
497
|
window = Enum(None, desc='PowerSpectraImport does not perform windowing')
|
|
671
498
|
|
|
672
499
|
overlap = Enum(None, desc='PowerSpectraImport does not consume time data')
|
|
@@ -679,17 +506,13 @@ class PowerSpectraImport(PowerSpectra):
|
|
|
679
506
|
_ind_low = Int(0, desc='index of lowest frequency line')
|
|
680
507
|
|
|
681
508
|
# Shadow trait, should not be set directly, for internal use.
|
|
682
|
-
_ind_high =
|
|
509
|
+
_ind_high = Union(None, Int, desc='index of highest frequency line')
|
|
683
510
|
|
|
684
511
|
# internal identifier
|
|
685
|
-
digest = Property(
|
|
686
|
-
depends_on=[
|
|
687
|
-
'_csmsum',
|
|
688
|
-
],
|
|
689
|
-
)
|
|
512
|
+
digest = Property(depends_on=['_csmsum'])
|
|
690
513
|
|
|
691
514
|
#: Name of the cache file without extension, readonly.
|
|
692
|
-
basename = Property(depends_on='digest', desc='basename for cache file')
|
|
515
|
+
basename = Property(depends_on=['digest'], desc='basename for cache file')
|
|
693
516
|
|
|
694
517
|
# csm shadow trait, only for internal use.
|
|
695
518
|
_csm = CArray()
|
|
@@ -704,7 +527,7 @@ class PowerSpectraImport(PowerSpectra):
|
|
|
704
527
|
def _get_digest(self):
|
|
705
528
|
return digest(self)
|
|
706
529
|
|
|
707
|
-
def
|
|
530
|
+
def _get_num_channels(self):
|
|
708
531
|
return self.csm.shape[1]
|
|
709
532
|
|
|
710
533
|
def _get_csm(self):
|
|
@@ -712,19 +535,20 @@ class PowerSpectraImport(PowerSpectra):
|
|
|
712
535
|
|
|
713
536
|
def _set_csm(self, csm):
|
|
714
537
|
if (len(csm.shape) != 3) or (csm.shape[1] != csm.shape[2]):
|
|
715
|
-
msg = 'The cross spectral matrix must have the following shape:
|
|
538
|
+
msg = 'The cross spectral matrix must have the following shape: \
|
|
539
|
+
(number of frequencies, num_channels, num_channels)!'
|
|
716
540
|
raise ValueError(msg)
|
|
717
541
|
self._csmsum = real(self._csm).sum() + (imag(self._csm) ** 2).sum() # to trigger new digest creation
|
|
718
542
|
self._csm = csm
|
|
719
543
|
|
|
720
|
-
@property_depends_on('digest')
|
|
544
|
+
@property_depends_on(['digest'])
|
|
721
545
|
def _get_eva(self):
|
|
722
546
|
"""Eigenvalues of cross spectral matrix are either loaded from cache file or
|
|
723
547
|
calculated and then additionally stored into cache.
|
|
724
548
|
"""
|
|
725
549
|
return self.calc_eva()
|
|
726
550
|
|
|
727
|
-
@property_depends_on('digest')
|
|
551
|
+
@property_depends_on(['digest'])
|
|
728
552
|
def _get_eve(self):
|
|
729
553
|
"""Eigenvectors of cross spectral matrix are either loaded from cache file or
|
|
730
554
|
calculated and then additionally stored into cache.
|