acoular 25.7__py3-none-any.whl → 26.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/aiaa/aiaa.py +8 -10
- acoular/base.py +13 -16
- acoular/calib.py +25 -24
- acoular/configuration.py +2 -2
- acoular/demo/__init__.py +97 -9
- acoular/demo/__main__.py +37 -0
- acoular/environments.py +119 -130
- acoular/fbeamform.py +438 -440
- acoular/fprocess.py +18 -13
- acoular/grids.py +122 -301
- acoular/h5cache.py +5 -1
- acoular/h5files.py +96 -9
- acoular/microphones.py +30 -35
- acoular/process.py +14 -25
- acoular/sdinput.py +9 -14
- acoular/signals.py +36 -34
- acoular/sources.py +263 -380
- acoular/spectra.py +60 -80
- acoular/tbeamform.py +242 -224
- acoular/tools/helpers.py +25 -33
- acoular/tools/metrics.py +5 -10
- acoular/tools/utils.py +168 -0
- acoular/tprocess.py +248 -271
- acoular/trajectory.py +5 -6
- acoular/version.py +2 -2
- {acoular-25.7.dist-info → acoular-26.1.dist-info}/METADATA +54 -105
- acoular-26.1.dist-info/RECORD +56 -0
- {acoular-25.7.dist-info → acoular-26.1.dist-info}/WHEEL +1 -1
- acoular/demo/acoular_demo.py +0 -135
- acoular-25.7.dist-info/RECORD +0 -56
- {acoular-25.7.dist-info → acoular-26.1.dist-info}/licenses/AUTHORS.rst +0 -0
- {acoular-25.7.dist-info → acoular-26.1.dist-info}/licenses/LICENSE +0 -0
acoular/spectra.py
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
# ------------------------------------------------------------------------------
|
|
4
4
|
"""Estimation of power spectra and related tools.
|
|
5
5
|
|
|
6
|
+
.. inheritance-diagram::
|
|
7
|
+
acoular.spectra
|
|
8
|
+
:top-classes:
|
|
9
|
+
acoular.spectra.BaseSpectra
|
|
10
|
+
:parts: 1
|
|
11
|
+
|
|
6
12
|
.. autosummary::
|
|
7
13
|
:toctree: generated/
|
|
8
14
|
|
|
@@ -13,26 +19,7 @@
|
|
|
13
19
|
|
|
14
20
|
from abc import abstractmethod
|
|
15
21
|
|
|
16
|
-
|
|
17
|
-
arange,
|
|
18
|
-
array,
|
|
19
|
-
bartlett,
|
|
20
|
-
blackman,
|
|
21
|
-
dot,
|
|
22
|
-
empty,
|
|
23
|
-
fill_diagonal,
|
|
24
|
-
hamming,
|
|
25
|
-
hanning,
|
|
26
|
-
imag,
|
|
27
|
-
linalg,
|
|
28
|
-
ndarray,
|
|
29
|
-
newaxis,
|
|
30
|
-
ones,
|
|
31
|
-
real,
|
|
32
|
-
searchsorted,
|
|
33
|
-
sum, # noqa A004
|
|
34
|
-
zeros,
|
|
35
|
-
)
|
|
22
|
+
import numpy as np
|
|
36
23
|
from scipy import fft
|
|
37
24
|
from traits.api import (
|
|
38
25
|
ABCHasStrictTraits,
|
|
@@ -53,7 +40,6 @@ from traits.api import (
|
|
|
53
40
|
# acoular imports
|
|
54
41
|
from .base import SamplesGenerator
|
|
55
42
|
from .configuration import config
|
|
56
|
-
from .deprecation import deprecated_alias
|
|
57
43
|
from .fastFuncs import calcCSM
|
|
58
44
|
from .h5cache import H5cache
|
|
59
45
|
from .h5files import H5CacheFileBase
|
|
@@ -61,9 +47,6 @@ from .internal import digest
|
|
|
61
47
|
from .tools.utils import find_basename
|
|
62
48
|
|
|
63
49
|
|
|
64
|
-
@deprecated_alias(
|
|
65
|
-
{'numchannels': 'num_channels', 'time_data': 'source'}, read_only=['numchannels'], removal_version='25.10'
|
|
66
|
-
)
|
|
67
50
|
class BaseSpectra(ABCHasStrictTraits):
|
|
68
51
|
"""
|
|
69
52
|
Base class for handling spectral data in Acoular.
|
|
@@ -94,9 +77,14 @@ class BaseSpectra(ABCHasStrictTraits):
|
|
|
94
77
|
#:
|
|
95
78
|
#: - ``'Blackman'``
|
|
96
79
|
window = Map(
|
|
97
|
-
{
|
|
80
|
+
{
|
|
81
|
+
'Rectangular': np.ones,
|
|
82
|
+
'Hanning': np.hanning,
|
|
83
|
+
'Hamming': np.hamming,
|
|
84
|
+
'Bartlett': np.bartlett,
|
|
85
|
+
'Blackman': np.blackman,
|
|
86
|
+
},
|
|
98
87
|
default_value='Rectangular',
|
|
99
|
-
desc='type of window for FFT',
|
|
100
88
|
)
|
|
101
89
|
|
|
102
90
|
#: Overlap factor for FFT block averaging. One of:
|
|
@@ -108,7 +96,7 @@ class BaseSpectra(ABCHasStrictTraits):
|
|
|
108
96
|
#: - ``'75%'``
|
|
109
97
|
#:
|
|
110
98
|
#: - ``'87.5%'``
|
|
111
|
-
overlap = Map({'None': 1, '50%': 2, '75%': 4, '87.5%': 8}, default_value='None'
|
|
99
|
+
overlap = Map({'None': 1, '50%': 2, '75%': 4, '87.5%': 8}, default_value='None')
|
|
112
100
|
|
|
113
101
|
#: FFT block size. Must be one of: ``128``, ``256``, ``512``, ``1024``, ... ``65536``.
|
|
114
102
|
#: Default is ``1024``.
|
|
@@ -123,11 +111,10 @@ class BaseSpectra(ABCHasStrictTraits):
|
|
|
123
111
|
16384,
|
|
124
112
|
32768,
|
|
125
113
|
65536,
|
|
126
|
-
desc='number of samples per FFT block',
|
|
127
114
|
)
|
|
128
115
|
|
|
129
116
|
#: Precision of the FFT, corresponding to NumPy dtypes. Default is ``'complex128'``.
|
|
130
|
-
precision = Enum('complex128', 'complex64'
|
|
117
|
+
precision = Enum('complex128', 'complex64')
|
|
131
118
|
|
|
132
119
|
#: A unique identifier for the spectra, based on its properties. (read-only)
|
|
133
120
|
digest = Property(depends_on=['precision', 'block_size', 'window', 'overlap'])
|
|
@@ -181,7 +168,7 @@ class BaseSpectra(ABCHasStrictTraits):
|
|
|
181
168
|
# generator that yields the time data blocks for every channel (with optional overlap)
|
|
182
169
|
def _get_source_data(self):
|
|
183
170
|
bs = self.block_size
|
|
184
|
-
temp = empty((2 * bs, self.num_channels))
|
|
171
|
+
temp = np.empty((2 * bs, self.num_channels))
|
|
185
172
|
pos = bs
|
|
186
173
|
posinc = bs / self.overlap_
|
|
187
174
|
for data_block in self.source.result(bs):
|
|
@@ -218,19 +205,16 @@ class PowerSpectra(BaseSpectra):
|
|
|
218
205
|
#: :class:`SamplesGenerator<acoular.base.SamplesGenerator>` or a derived class.
|
|
219
206
|
source = Instance(SamplesGenerator)
|
|
220
207
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
# Shadow trait, should not be set directly, for internal use.
|
|
225
|
-
_ind_high = Union(Int(-1), None, desc='index of highest frequency line')
|
|
208
|
+
_ind_low = Int(1)
|
|
209
|
+
_ind_high = Union(Int(-1), None)
|
|
226
210
|
|
|
227
211
|
#: Index of lowest frequency line to compute. Default is ``1``. Only used by objects that fetch
|
|
228
212
|
#: the CSM. PowerSpectra computes every frequency line.
|
|
229
|
-
ind_low = Property(_ind_low
|
|
213
|
+
ind_low = Property(_ind_low)
|
|
230
214
|
|
|
231
215
|
#: Index of highest frequency line to compute. Default is ``-1``
|
|
232
216
|
#: (last possible line for default :attr:`~BaseSpectra.block_size`).
|
|
233
|
-
ind_high = Property(_ind_high
|
|
217
|
+
ind_high = Property(_ind_high)
|
|
234
218
|
|
|
235
219
|
# Stores the set lower frequency, for internal use, should not be set directly.
|
|
236
220
|
_freqlc = Float(0)
|
|
@@ -244,37 +228,37 @@ class PowerSpectra(BaseSpectra):
|
|
|
244
228
|
_index_set_last = Bool(True)
|
|
245
229
|
|
|
246
230
|
#: A flag indicating whether the result should be cached in HDF5 files. Default is ``True``.
|
|
247
|
-
cached = Bool(True
|
|
231
|
+
cached = Bool(True)
|
|
248
232
|
|
|
249
233
|
#: The number of FFT blocks used for averaging. This is derived from the
|
|
250
234
|
#: :attr:`~BaseSpectra.block_size` and :attr:`~BaseSpectra.overlap` parameters. (read-only)
|
|
251
|
-
num_blocks = Property(
|
|
235
|
+
num_blocks = Property()
|
|
252
236
|
|
|
253
237
|
#: 2-element array with the lowest and highest frequency. If the higher frequency is larger than
|
|
254
238
|
#: the max frequency, the max frequency will be the upper bound.
|
|
255
|
-
freq_range = Property(
|
|
239
|
+
freq_range = Property()
|
|
256
240
|
# If set, will overwrite :attr:`_freqlc` and :attr:`_freqhc` according to the range. The
|
|
257
241
|
# freq_range interval will be the smallest discrete frequency inside the half-open interval
|
|
258
242
|
# [_freqlc, _freqhc[ and the smallest upper frequency outside of the interval.
|
|
259
243
|
|
|
260
244
|
#: The sequence of frequency indices between :attr:`ind_low` and :attr:`ind_high`. (read-only)
|
|
261
|
-
indices = Property(
|
|
245
|
+
indices = Property()
|
|
262
246
|
|
|
263
247
|
#: The name of the cache file (without the file extension) used for storing results. (read-only)
|
|
264
|
-
basename = Property(depends_on=['source.digest']
|
|
248
|
+
basename = Property(depends_on=['source.digest'])
|
|
265
249
|
|
|
266
250
|
#: The cross-spectral matrix, represented as an array of shape ``(n, m, m)`` of complex values
|
|
267
251
|
#: for ``n`` frequencies and ``m`` channels as in :attr:`~BaseSpectra.num_channels`. (read-only)
|
|
268
|
-
csm = Property(
|
|
252
|
+
csm = Property()
|
|
269
253
|
|
|
270
254
|
#: The eigenvalues of the CSM, stored as an array of shape ``(n,)`` of floats for ``n``
|
|
271
255
|
#: frequencies. (read-only)
|
|
272
|
-
eva = Property(
|
|
256
|
+
eva = Property()
|
|
273
257
|
|
|
274
258
|
#: The eigenvectors of the cross spectral matrix, stored as an array of shape ``(n, m, m)`` of
|
|
275
259
|
#: floats for ``n`` frequencies and ``m`` channels as in :attr:`~BaseSpectra.num_channels`.
|
|
276
260
|
#: (read-only)
|
|
277
|
-
eve = Property(
|
|
261
|
+
eve = Property()
|
|
278
262
|
|
|
279
263
|
#: A unique identifier for the spectra, based on its properties. (read-only)
|
|
280
264
|
digest = Property(
|
|
@@ -293,7 +277,7 @@ class PowerSpectra(BaseSpectra):
|
|
|
293
277
|
fftfreq = self.fftfreq()
|
|
294
278
|
if fftfreq is not None:
|
|
295
279
|
if self._ind_high is None:
|
|
296
|
-
return array([fftfreq[self.ind_low], None])
|
|
280
|
+
return np.array([fftfreq[self.ind_low], None])
|
|
297
281
|
return fftfreq[[self.ind_low, self.ind_high]]
|
|
298
282
|
return None
|
|
299
283
|
|
|
@@ -309,7 +293,7 @@ class PowerSpectra(BaseSpectra):
|
|
|
309
293
|
if fftfreq is not None:
|
|
310
294
|
if self._index_set_last:
|
|
311
295
|
return min(self._ind_low, fftfreq.shape[0] - 1)
|
|
312
|
-
return searchsorted(fftfreq[:-1], self._freqlc)
|
|
296
|
+
return np.searchsorted(fftfreq[:-1], self._freqlc)
|
|
313
297
|
return 0
|
|
314
298
|
|
|
315
299
|
@property_depends_on(['source.sample_freq', 'block_size', '_ind_high', '_freqhc'])
|
|
@@ -322,7 +306,7 @@ class PowerSpectra(BaseSpectra):
|
|
|
322
306
|
return min(self._ind_high, fftfreq.shape[0] - 1)
|
|
323
307
|
if self._freqhc is None:
|
|
324
308
|
return None
|
|
325
|
-
return searchsorted(fftfreq[:-1], self._freqhc)
|
|
309
|
+
return np.searchsorted(fftfreq[:-1], self._freqhc)
|
|
326
310
|
return None
|
|
327
311
|
|
|
328
312
|
def _set_ind_high(self, ind_high): # by setting this the user sets the lower index
|
|
@@ -338,7 +322,7 @@ class PowerSpectra(BaseSpectra):
|
|
|
338
322
|
fftfreq = self.fftfreq()
|
|
339
323
|
if fftfreq is not None:
|
|
340
324
|
try:
|
|
341
|
-
indices = arange(fftfreq.shape[0], dtype=int)
|
|
325
|
+
indices = np.arange(fftfreq.shape[0], dtype=int)
|
|
342
326
|
if self.ind_high is None:
|
|
343
327
|
return indices[self.ind_low :]
|
|
344
328
|
return indices[self.ind_low : self.ind_high]
|
|
@@ -385,18 +369,18 @@ class PowerSpectra(BaseSpectra):
|
|
|
385
369
|
"""
|
|
386
370
|
t = self.source
|
|
387
371
|
wind = self.window_(self.block_size)
|
|
388
|
-
weight = dot(wind, wind)
|
|
389
|
-
wind = wind[newaxis, :].swapaxes(0, 1)
|
|
372
|
+
weight = np.dot(wind, wind)
|
|
373
|
+
wind = wind[np.newaxis, :].swapaxes(0, 1)
|
|
390
374
|
numfreq = int(self.block_size / 2 + 1)
|
|
391
375
|
csm_shape = (numfreq, t.num_channels, t.num_channels)
|
|
392
|
-
csm_upper = zeros(csm_shape, dtype=self.precision)
|
|
376
|
+
csm_upper = np.zeros(csm_shape, dtype=self.precision)
|
|
393
377
|
# get time data blockwise
|
|
394
378
|
for data in self._get_source_data():
|
|
395
379
|
ft = fft.rfft(data * wind, None, 0).astype(self.precision)
|
|
396
380
|
calcCSM(csm_upper, ft) # only upper triangular part of matrix is calculated (for speed reasons)
|
|
397
381
|
# create the full csm matrix via transposing and complex conj.
|
|
398
382
|
csm_lower = csm_upper.conj().transpose(0, 2, 1)
|
|
399
|
-
[fill_diagonal(csm_lower[cntFreq, :, :], 0) for cntFreq in range(csm_lower.shape[0])]
|
|
383
|
+
[np.fill_diagonal(csm_lower[cntFreq, :, :], 0) for cntFreq in range(csm_lower.shape[0])]
|
|
400
384
|
csm = csm_lower + csm_upper
|
|
401
385
|
# onesided spectrum: multiplication by 2.0=sqrt(2)^2
|
|
402
386
|
return csm * (2.0 / self.block_size / weight / self.num_blocks)
|
|
@@ -445,10 +429,10 @@ class PowerSpectra(BaseSpectra):
|
|
|
445
429
|
eva_dtype = 'float32'
|
|
446
430
|
# csm = self.csm #trigger calculation
|
|
447
431
|
csm_shape = self.csm.shape
|
|
448
|
-
eva = empty(csm_shape[0:2], dtype=eva_dtype)
|
|
449
|
-
eve = empty(csm_shape, dtype=self.precision)
|
|
432
|
+
eva = np.empty(csm_shape[0:2], dtype=eva_dtype)
|
|
433
|
+
eve = np.empty(csm_shape, dtype=self.precision)
|
|
450
434
|
for i in range(csm_shape[0]):
|
|
451
|
-
(eva[i], eve[i]) = linalg.eigh(self.csm[i])
|
|
435
|
+
(eva[i], eve[i]) = np.linalg.eigh(self.csm[i])
|
|
452
436
|
return (eva, eve)
|
|
453
437
|
|
|
454
438
|
def calc_eva(self):
|
|
@@ -605,12 +589,12 @@ class PowerSpectra(BaseSpectra):
|
|
|
605
589
|
f = self.fftfreq()
|
|
606
590
|
if num == 0:
|
|
607
591
|
# single frequency line
|
|
608
|
-
return self.eva[searchsorted(f, freq)]
|
|
609
|
-
f1 = searchsorted(f, freq * 2.0 ** (-0.5 / num))
|
|
610
|
-
f2 = searchsorted(f, freq * 2.0 ** (0.5 / num))
|
|
592
|
+
return self.eva[np.searchsorted(f, freq)]
|
|
593
|
+
f1 = np.searchsorted(f, freq * 2.0 ** (-0.5 / num))
|
|
594
|
+
f2 = np.searchsorted(f, freq * 2.0 ** (0.5 / num))
|
|
611
595
|
if f1 == f2:
|
|
612
596
|
return self.eva[f1]
|
|
613
|
-
return sum(self.eva[f1:f2], 0)
|
|
597
|
+
return np.sum(self.eva[f1:f2], 0)
|
|
614
598
|
|
|
615
599
|
|
|
616
600
|
class PowerSpectraImport(PowerSpectra):
|
|
@@ -628,50 +612,46 @@ class PowerSpectraImport(PowerSpectra):
|
|
|
628
612
|
|
|
629
613
|
#: The cross-spectral matrix stored in an array of shape ``(n, m, m)`` of complex for ``n``
|
|
630
614
|
#: frequencies and ``m`` channels.
|
|
631
|
-
csm = Property(
|
|
615
|
+
csm = Property()
|
|
632
616
|
|
|
633
617
|
#: The frequencies included in the CSM in ascending order. Accepts list, array, or a single
|
|
634
618
|
#: float value.
|
|
635
|
-
frequencies = Union(None, CArray, Float
|
|
619
|
+
frequencies = Union(None, CArray, Float)
|
|
636
620
|
|
|
637
621
|
#: Number of time data channels, inferred from the shape of the CSM.
|
|
638
622
|
num_channels = Property(depends_on=['digest'])
|
|
639
623
|
|
|
640
624
|
#: :class:`PowerSpectraImport` does not consume time data; source is always ``None``.
|
|
641
|
-
source = Enum(None
|
|
625
|
+
source = Enum(None)
|
|
642
626
|
|
|
643
627
|
#: Sampling frequency of the signal. Default is ``None``
|
|
644
|
-
sample_freq = Enum(None
|
|
628
|
+
sample_freq = Enum(None)
|
|
645
629
|
|
|
646
630
|
#: Block size for FFT, non-functional in this class.
|
|
647
|
-
block_size = Enum(None
|
|
631
|
+
block_size = Enum(None)
|
|
648
632
|
|
|
649
633
|
#: Windowing method, non-functional in this class.
|
|
650
|
-
window = Enum(None
|
|
634
|
+
window = Enum(None)
|
|
651
635
|
|
|
652
636
|
#: Overlap between blocks, non-functional in this class.
|
|
653
|
-
overlap = Enum(None
|
|
637
|
+
overlap = Enum(None)
|
|
654
638
|
|
|
655
639
|
#: Caching capability, always disabled.
|
|
656
|
-
cached = Enum(False
|
|
640
|
+
cached = Enum(False)
|
|
657
641
|
|
|
658
642
|
#: Number of FFT blocks, always ``None``.
|
|
659
|
-
num_blocks = Enum(None
|
|
660
|
-
|
|
661
|
-
# Shadow trait, should not be set directly, for internal use.
|
|
662
|
-
_ind_low = Int(0, desc='index of lowest frequency line')
|
|
643
|
+
num_blocks = Enum(None)
|
|
663
644
|
|
|
664
|
-
|
|
665
|
-
_ind_high = Union(None, Int
|
|
645
|
+
_ind_low = Int(0)
|
|
646
|
+
_ind_high = Union(None, Int)
|
|
666
647
|
|
|
667
648
|
#: A unique identifier for the spectra, based on its properties. (read-only)
|
|
668
649
|
digest = Property(depends_on=['_csmsum'])
|
|
669
650
|
|
|
670
651
|
#: Name of the cache file without extension. (read-only)
|
|
671
|
-
basename = Property(depends_on=['digest']
|
|
652
|
+
basename = Property(depends_on=['digest'])
|
|
672
653
|
|
|
673
|
-
|
|
674
|
-
_csm = Union(None, CArray(shape=(None, None, None)), desc='cross spectral matrix')
|
|
654
|
+
_csm = Union(None, CArray(shape=(None, None, None)))
|
|
675
655
|
|
|
676
656
|
# Checksum for the CSM to trigger digest calculation, for internal use only.
|
|
677
657
|
_csmsum = Float()
|
|
@@ -696,7 +676,7 @@ class PowerSpectraImport(PowerSpectra):
|
|
|
696
676
|
(number of frequencies, num_channels, num_channels)!'
|
|
697
677
|
raise ValueError(msg)
|
|
698
678
|
self._csm = csm
|
|
699
|
-
self._csmsum = real(self._csm).sum() + (imag(self._csm) ** 2).sum() # to trigger new digest creation
|
|
679
|
+
self._csmsum = np.real(self._csm).sum() + (np.imag(self._csm) ** 2).sum() # to trigger new digest creation
|
|
700
680
|
|
|
701
681
|
@property_depends_on(['digest'])
|
|
702
682
|
def _get_eva(self):
|
|
@@ -719,7 +699,7 @@ class PowerSpectraImport(PowerSpectra):
|
|
|
719
699
|
Array containing the frequencies.
|
|
720
700
|
"""
|
|
721
701
|
if isinstance(self.frequencies, float):
|
|
722
|
-
return array([self.frequencies])
|
|
723
|
-
if isinstance(self.frequencies, ndarray):
|
|
702
|
+
return np.array([self.frequencies])
|
|
703
|
+
if isinstance(self.frequencies, np.ndarray):
|
|
724
704
|
return self.frequencies
|
|
725
705
|
return self.frequencies
|