acoular 24.10__py3-none-any.whl → 25.3__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 +31 -18
- acoular/deprecation.py +85 -0
- acoular/environments.py +481 -229
- acoular/fastFuncs.py +90 -84
- acoular/fbeamform.py +203 -411
- acoular/fprocess.py +233 -123
- acoular/grids.py +793 -424
- acoular/h5cache.py +29 -40
- acoular/h5files.py +2 -6
- acoular/microphones.py +197 -74
- acoular/process.py +660 -149
- acoular/sdinput.py +23 -20
- acoular/signals.py +461 -159
- acoular/sources.py +1311 -489
- acoular/spectra.py +328 -352
- 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 +126 -53
- acoular/version.py +2 -2
- {acoular-24.10.dist-info → acoular-25.3.dist-info}/METADATA +39 -17
- acoular-25.3.dist-info/RECORD +56 -0
- {acoular-24.10.dist-info → acoular-25.3.dist-info}/WHEEL +1 -1
- acoular-24.10.dist-info/RECORD +0 -54
- {acoular-24.10.dist-info → acoular-25.3.dist-info}/licenses/AUTHORS.rst +0 -0
- {acoular-24.10.dist-info → acoular-25.3.dist-info}/licenses/LICENSE +0 -0
acoular/fprocess.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# ------------------------------------------------------------------------------
|
|
2
2
|
# Copyright (c) Acoular Development Team.
|
|
3
3
|
# ------------------------------------------------------------------------------
|
|
4
|
-
"""
|
|
4
|
+
"""
|
|
5
|
+
Implements blockwise processing methods in the frequency domain.
|
|
5
6
|
|
|
6
7
|
.. autosummary::
|
|
7
8
|
:toctree: generated/
|
|
@@ -17,55 +18,68 @@ from warnings import warn
|
|
|
17
18
|
|
|
18
19
|
import numpy as np
|
|
19
20
|
from scipy import fft
|
|
20
|
-
from traits.api import Bool, CArray, Enum, Instance, Int, Property,
|
|
21
|
+
from traits.api import Bool, CArray, Enum, Instance, Int, Property, Union, cached_property
|
|
21
22
|
|
|
23
|
+
# acoular imports
|
|
22
24
|
from .base import SamplesGenerator, SpectraGenerator, SpectraOut, TimeOut
|
|
25
|
+
from .deprecation import deprecated_alias
|
|
23
26
|
from .fastFuncs import calcCSM
|
|
24
27
|
from .internal import digest
|
|
28
|
+
from .process import SamplesBuffer
|
|
25
29
|
from .spectra import BaseSpectra
|
|
26
|
-
from .tools.utils import SamplesBuffer
|
|
27
30
|
|
|
28
31
|
|
|
32
|
+
@deprecated_alias({'numfreqs': 'num_freqs', 'numsamples': 'num_samples'}, read_only=True)
|
|
29
33
|
class RFFT(BaseSpectra, SpectraOut):
|
|
30
|
-
"""
|
|
34
|
+
"""
|
|
35
|
+
Compute the one-sided Fast Fourier Transform (FFT) for real-valued multichannel time data.
|
|
31
36
|
|
|
32
|
-
The FFT is
|
|
33
|
-
:attr:`block_size`
|
|
34
|
-
|
|
37
|
+
The FFT is performed block-wise, dividing the input data into blocks of length specified
|
|
38
|
+
by the :attr:`block_size` attribute. A window function can be optionally
|
|
39
|
+
applied to each block before the FFT calculation, controlled via the :attr:`window` attribute.
|
|
40
|
+
|
|
41
|
+
This class provides flexibility in scaling the FFT results for different use cases, such as
|
|
42
|
+
preserving amplitude or energy, by setting the :attr:`scaling` attribute.
|
|
35
43
|
"""
|
|
36
44
|
|
|
37
|
-
#: Data source; :class:`~acoular.base.SamplesGenerator` or derived object.
|
|
45
|
+
#: Data source; an instance of :class:`~acoular.base.SamplesGenerator` or a derived object.
|
|
46
|
+
#: This provides the input time-domain data for FFT processing.
|
|
38
47
|
source = Instance(SamplesGenerator)
|
|
39
48
|
|
|
40
|
-
#:
|
|
41
|
-
#:
|
|
42
|
-
#: Default is
|
|
49
|
+
#: The number of workers to use for FFT calculation.
|
|
50
|
+
#: If set to a negative value, all available logical CPUs are used.
|
|
51
|
+
#: Default is ``None``, which relies on the :func:`scipy.fft.rfft` implementation.
|
|
43
52
|
workers = Union(Int(), None, default_value=None, desc='number of workers to use')
|
|
44
53
|
|
|
45
|
-
#:
|
|
46
|
-
#:
|
|
47
|
-
#: '
|
|
48
|
-
#:
|
|
49
|
-
#:
|
|
50
|
-
#:
|
|
54
|
+
#: Defines the scaling method for the FFT result. Options are:
|
|
55
|
+
#:
|
|
56
|
+
#: - ``'none'``: No scaling is applied.
|
|
57
|
+
#: - ``'energy'``: Compensates for energy loss in the FFT result due to truncation,
|
|
58
|
+
#: doubling the values for frequencies other than DC and the Nyquist frequency.
|
|
59
|
+
#: - ``'amplitude'``: Scales the result so that the amplitude
|
|
60
|
+
#: of discrete tones is independent of the block size.
|
|
51
61
|
scaling = Enum('none', 'energy', 'amplitude')
|
|
52
62
|
|
|
53
|
-
#:
|
|
63
|
+
#: The length of each block of time-domain data used for the FFT. Must be an even number.
|
|
64
|
+
#: Default is ``1024``.
|
|
54
65
|
block_size = Property()
|
|
55
66
|
|
|
56
|
-
#:
|
|
57
|
-
|
|
67
|
+
#: The number of frequency bins in the FFT result, calculated as ``block_size // 2 + 1``.
|
|
68
|
+
num_freqs = Property(depends_on=['_block_size'])
|
|
58
69
|
|
|
59
|
-
#:
|
|
60
|
-
|
|
70
|
+
#: The total number of snapshots (blocks) available in the FFT result,
|
|
71
|
+
#: determined by the size of the input data and the block size.
|
|
72
|
+
num_samples = Property(depends_on=['source.num_samples', '_block_size'])
|
|
61
73
|
|
|
62
|
-
#: 1-D array
|
|
74
|
+
#: A 1-D array containing the Discrete Fourier Transform
|
|
75
|
+
#: sample frequencies corresponding to the FFT output.
|
|
63
76
|
freqs = Property()
|
|
64
77
|
|
|
65
|
-
#
|
|
78
|
+
# Internal representation of the block size for FFT processing.
|
|
79
|
+
# Used for validation and property management.
|
|
66
80
|
_block_size = Int(1024, desc='block size of the FFT')
|
|
67
81
|
|
|
68
|
-
|
|
82
|
+
#: A unique identifier based on the process properties.
|
|
69
83
|
digest = Property(depends_on=['source.digest', 'scaling', 'precision', '_block_size', 'window', 'overlap'])
|
|
70
84
|
|
|
71
85
|
@cached_property
|
|
@@ -73,13 +87,13 @@ class RFFT(BaseSpectra, SpectraOut):
|
|
|
73
87
|
return digest(self)
|
|
74
88
|
|
|
75
89
|
@cached_property
|
|
76
|
-
def
|
|
90
|
+
def _get_num_freqs(self):
|
|
77
91
|
return int(self.block_size / 2 + 1)
|
|
78
92
|
|
|
79
93
|
@cached_property
|
|
80
|
-
def
|
|
81
|
-
if self.source.
|
|
82
|
-
return int(np.floor(self.source.
|
|
94
|
+
def _get_num_samples(self):
|
|
95
|
+
if self.source.num_samples >= 0:
|
|
96
|
+
return int(np.floor(self.source.num_samples / self.block_size))
|
|
83
97
|
return -1
|
|
84
98
|
|
|
85
99
|
def _get_block_size(self):
|
|
@@ -92,39 +106,49 @@ class RFFT(BaseSpectra, SpectraOut):
|
|
|
92
106
|
self._block_size = value
|
|
93
107
|
|
|
94
108
|
def _scale(self, data, scaling_value):
|
|
95
|
-
|
|
109
|
+
# Corrects the energy of the one-sided FFT data.
|
|
96
110
|
if self.scaling == 'amplitude' or self.scaling == 'energy':
|
|
97
111
|
data[1:-1] *= 2.0
|
|
98
112
|
data *= scaling_value
|
|
99
113
|
return data
|
|
100
114
|
|
|
101
115
|
def _get_freqs(self):
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
Returns
|
|
105
|
-
-------
|
|
106
|
-
f : ndarray
|
|
107
|
-
1-D Array of length *block_size/2+1* containing the sample frequencies.
|
|
116
|
+
# Return the Discrete Fourier Transform sample frequencies.
|
|
108
117
|
|
|
109
|
-
|
|
118
|
+
# Returns
|
|
119
|
+
# -------
|
|
120
|
+
# f : ndarray
|
|
121
|
+
# 1-D Array of length *block_size // 2 + 1* containing the sample frequencies.
|
|
110
122
|
if self.source is not None:
|
|
111
123
|
return abs(fft.fftfreq(self.block_size, 1.0 / self.source.sample_freq)[: int(self.block_size / 2 + 1)])
|
|
112
124
|
return np.array([])
|
|
113
125
|
|
|
114
126
|
def result(self, num=1):
|
|
115
|
-
"""
|
|
127
|
+
"""
|
|
128
|
+
Yield the FFT results block-wise as multi-channel spectra.
|
|
129
|
+
|
|
130
|
+
This generator processes the input data block-by-block, applying the specified
|
|
131
|
+
window function and FFT parameters. The output consists of the FFT spectra for
|
|
132
|
+
each block, scaled according to the selected :attr:`scaling` method.
|
|
116
133
|
|
|
117
134
|
Parameters
|
|
118
135
|
----------
|
|
119
|
-
num :
|
|
120
|
-
|
|
121
|
-
returned by the generator.
|
|
122
|
-
|
|
123
|
-
Returns
|
|
124
|
-
-------
|
|
125
|
-
Spectra block of shape (num, :attr:`numchannels`*:attr:`numfreqs`).
|
|
126
|
-
The last block may be shorter than num.
|
|
136
|
+
num : :class:`int`, optional
|
|
137
|
+
Number of multi-channel spectra (snapshots) per block to return. Default is ``1``.
|
|
127
138
|
|
|
139
|
+
Yields
|
|
140
|
+
------
|
|
141
|
+
:class:`numpy.ndarray`
|
|
142
|
+
A block of FFT spectra with shape (num, :attr:`num_channels` ``*`` :attr:`num_freqs`).
|
|
143
|
+
The final block may contain fewer than ``num`` spectra if the input data is insufficient
|
|
144
|
+
to fill the last block.
|
|
145
|
+
|
|
146
|
+
Notes
|
|
147
|
+
-----
|
|
148
|
+
- The generator compensates for energy or amplitude loss based on the :attr:`scaling`
|
|
149
|
+
attribute.
|
|
150
|
+
- If the input data source provides fewer samples than required for a complete block,
|
|
151
|
+
the remaining spectra are padded or adjusted accordingly.
|
|
128
152
|
"""
|
|
129
153
|
wind = self.window_(self.block_size)
|
|
130
154
|
if self.scaling == 'none' or self.scaling == 'energy': # only compensate for the window
|
|
@@ -132,7 +156,7 @@ class RFFT(BaseSpectra, SpectraOut):
|
|
|
132
156
|
elif self.scaling == 'amplitude': # compensates for the window and the energy loss
|
|
133
157
|
svalue = 1 / wind.sum()
|
|
134
158
|
wind = wind[:, np.newaxis]
|
|
135
|
-
fftdata = np.zeros((num, self.
|
|
159
|
+
fftdata = np.zeros((num, self.num_channels * self.num_freqs), dtype=self.precision)
|
|
136
160
|
j = 0
|
|
137
161
|
for i, data in enumerate(self._get_source_data()): # yields one block of time data
|
|
138
162
|
j = i % num
|
|
@@ -146,33 +170,46 @@ class RFFT(BaseSpectra, SpectraOut):
|
|
|
146
170
|
yield fftdata[: j + 1]
|
|
147
171
|
|
|
148
172
|
|
|
173
|
+
@deprecated_alias({'numsamples': 'num_samples'}, read_only=True)
|
|
149
174
|
class IRFFT(TimeOut):
|
|
150
|
-
"""
|
|
175
|
+
"""
|
|
176
|
+
Perform the inverse Fast Fourier Transform (IFFT) for one-sided multi-channel spectra.
|
|
177
|
+
|
|
178
|
+
This class converts spectral data from the frequency domain back into time-domain signals.
|
|
179
|
+
The IFFT is calculated block-wise, where the block size is defined by the spectral data
|
|
180
|
+
source. The output time-domain signals are scaled and processed according to the precision
|
|
181
|
+
defined by the :attr:`precision` attribute.
|
|
182
|
+
"""
|
|
151
183
|
|
|
152
|
-
#: Data source
|
|
184
|
+
#: Data source providing one-sided spectra, implemented as an instance of
|
|
185
|
+
# :class:`~acoular.base.SpectraGenerator` or a derived object.
|
|
153
186
|
source = Instance(SpectraGenerator)
|
|
154
187
|
|
|
155
|
-
#:
|
|
156
|
-
#: all available logical CPUs
|
|
157
|
-
#: Default is
|
|
188
|
+
#: The number of workers (threads) to use for the IFFT calculation.
|
|
189
|
+
#: A negative value utilizes all available logical CPUs.
|
|
190
|
+
#: Default is ``None``, which relies on the :func:`scipy.fft.irfft` implementation.
|
|
158
191
|
workers = Union(Int(), None, default_value=None, desc='number of workers to use')
|
|
159
192
|
|
|
160
|
-
#:
|
|
161
|
-
#:
|
|
162
|
-
|
|
193
|
+
#: Determines the floating-point precision of the resulting time-domain signals.
|
|
194
|
+
#: Options include ``'float64'`` and ``'float32'``.
|
|
195
|
+
#: Default is ``'float64'``, ensuring high precision.
|
|
196
|
+
precision = Enum('float64', 'float32', desc='precision of the time signal after the ifft')
|
|
163
197
|
|
|
164
|
-
#:
|
|
165
|
-
|
|
198
|
+
#: The total number of time-domain samples in the output.
|
|
199
|
+
#: Computed as the product of the number of input samples and the block size.
|
|
200
|
+
#: Returns ``-1`` if the number of input samples is negative.
|
|
201
|
+
num_samples = Property(depends_on=['source.num_samples', 'source._block_size'])
|
|
166
202
|
|
|
167
|
-
#
|
|
203
|
+
# Internal signal buffer used for handling arbitrary output block sizes. Optimizes
|
|
204
|
+
# processing when the requested output block size does not match the source block size.
|
|
168
205
|
_buffer = CArray(desc='signal buffer')
|
|
169
206
|
|
|
170
|
-
|
|
207
|
+
#: A unique identifier based on the process properties.
|
|
171
208
|
digest = Property(depends_on=['source.digest', 'scaling', 'precision', '_block_size', 'window', 'overlap'])
|
|
172
209
|
|
|
173
|
-
def
|
|
174
|
-
if self.source.
|
|
175
|
-
return int(self.source.
|
|
210
|
+
def _get_num_samples(self):
|
|
211
|
+
if self.source.num_samples >= 0:
|
|
212
|
+
return int(self.source.num_samples * self.source.block_size)
|
|
176
213
|
return -1
|
|
177
214
|
|
|
178
215
|
@cached_property
|
|
@@ -186,9 +223,9 @@ class IRFFT(TimeOut):
|
|
|
186
223
|
'This is likely due to incomplete spectral data from which the inverse FFT cannot be calculated.'
|
|
187
224
|
)
|
|
188
225
|
raise ValueError(msg)
|
|
189
|
-
if (self.source.
|
|
226
|
+
if (self.source.num_freqs - 1) * 2 != self.source.block_size:
|
|
190
227
|
msg = (
|
|
191
|
-
f'Block size must be 2*(
|
|
228
|
+
f'Block size must be 2*(num_freqs-1) but is {self.source.block_size}.'
|
|
192
229
|
'This is likely due to incomplete spectral data from which the inverse FFT cannot be calculated.'
|
|
193
230
|
)
|
|
194
231
|
raise ValueError(msg)
|
|
@@ -197,18 +234,32 @@ class IRFFT(TimeOut):
|
|
|
197
234
|
raise ValueError(msg)
|
|
198
235
|
|
|
199
236
|
def result(self, num):
|
|
200
|
-
"""
|
|
237
|
+
"""
|
|
238
|
+
Generate time-domain signal blocks from spectral data.
|
|
239
|
+
|
|
240
|
+
This generator processes spectral data block-by-block, performing an inverse Fast
|
|
241
|
+
Fourier Transform (IFFT) to convert the input spectra into time-domain signals.
|
|
242
|
+
The output is yielded in blocks of the specified size.
|
|
201
243
|
|
|
202
244
|
Parameters
|
|
203
245
|
----------
|
|
204
|
-
num :
|
|
205
|
-
|
|
206
|
-
|
|
246
|
+
num : :class:`int`
|
|
247
|
+
The number of time samples per output block. If ``num`` differs from the
|
|
248
|
+
source block size, an internal buffer is used to assemble the required output.
|
|
207
249
|
|
|
208
250
|
Yields
|
|
209
251
|
------
|
|
210
|
-
numpy.ndarray
|
|
211
|
-
|
|
252
|
+
:class:`numpy.ndarray`
|
|
253
|
+
Blocks of time-domain signals with shape (num, :attr:`num_channels`). The last block may
|
|
254
|
+
contain fewer samples if the input data is insufficient to fill the requested size.
|
|
255
|
+
|
|
256
|
+
Notes
|
|
257
|
+
-----
|
|
258
|
+
- The method ensures that the source block size and frequency data are compatible for IFFT.
|
|
259
|
+
- If the requested block size does not match the source block size, a buffer is used
|
|
260
|
+
to assemble the output, allowing arbitrary block sizes to be generated.
|
|
261
|
+
- For performance optimization, the number of workers (threads) can be specified via
|
|
262
|
+
the :attr:`workers` attribute.
|
|
212
263
|
"""
|
|
213
264
|
self._validate()
|
|
214
265
|
bs = self.source.block_size
|
|
@@ -219,27 +270,39 @@ class IRFFT(TimeOut):
|
|
|
219
270
|
else:
|
|
220
271
|
for spectra in self.source.result(1):
|
|
221
272
|
yield fft.irfft(
|
|
222
|
-
spectra.reshape(self.source.
|
|
273
|
+
spectra.reshape(self.source.num_freqs, self.num_channels), n=num, axis=0, workers=self.workers
|
|
223
274
|
)
|
|
224
275
|
|
|
225
276
|
|
|
226
277
|
class AutoPowerSpectra(SpectraOut):
|
|
227
|
-
"""
|
|
278
|
+
"""
|
|
279
|
+
Compute the real-valued auto-power spectra from multi-channel frequency-domain data.
|
|
228
280
|
|
|
229
|
-
|
|
281
|
+
The auto-power spectra provide a measure of the power contained in each frequency bin
|
|
282
|
+
for each channel. This class processes spectral data from the source block-by-block,
|
|
283
|
+
applying scaling and precision adjustments as configured by the :attr:`scaling` and
|
|
284
|
+
:attr:`precision` attributes.
|
|
285
|
+
"""
|
|
286
|
+
|
|
287
|
+
#: The data source that provides frequency-domain spectra,
|
|
288
|
+
#: implemented as an instance of :class:`~acoular.base.SpectraGenerator` or a derived object.
|
|
230
289
|
source = Instance(SpectraGenerator)
|
|
231
290
|
|
|
232
|
-
#:
|
|
233
|
-
#:
|
|
291
|
+
#: Specifies the scaling method for the auto-power spectra. Options are:
|
|
292
|
+
#:
|
|
293
|
+
#: - ``'power'``: Outputs the raw power of the spectra.
|
|
294
|
+
#: - ``'psd'``: Outputs the Power Spectral Density (PSD),
|
|
295
|
+
#: normalized by the block size and sampling frequency.
|
|
234
296
|
scaling = Enum('power', 'psd')
|
|
235
297
|
|
|
236
|
-
#:
|
|
298
|
+
#: A Boolean flag indicating whether the input spectra are single-sided. Default is ``True``.
|
|
237
299
|
single_sided = Bool(True, desc='single sided spectrum')
|
|
238
300
|
|
|
239
|
-
#:
|
|
240
|
-
|
|
301
|
+
#: Specifies the floating-point precision of the computed auto-power spectra.
|
|
302
|
+
#: Options are ``'float64'`` and ``'float32'``. Default is ``'float64'``.
|
|
303
|
+
precision = Enum('float64', 'float32', desc='floating-number-precision')
|
|
241
304
|
|
|
242
|
-
|
|
305
|
+
#: A unique identifier based on the computation properties.
|
|
243
306
|
digest = Property(depends_on=['source.digest', 'precision', 'scaling', 'single_sided'])
|
|
244
307
|
|
|
245
308
|
@cached_property
|
|
@@ -255,57 +318,87 @@ class AutoPowerSpectra(SpectraOut):
|
|
|
255
318
|
return scale
|
|
256
319
|
|
|
257
320
|
def result(self, num=1):
|
|
258
|
-
"""
|
|
321
|
+
"""
|
|
322
|
+
Generate real-valued auto-power spectra blocks.
|
|
323
|
+
|
|
324
|
+
This generator computes the auto-power spectra by taking the element-wise squared
|
|
325
|
+
magnitude of the input spectra and applying the appropriate scaling. The results
|
|
326
|
+
are yielded block-by-block with the specified number of snapshots.
|
|
259
327
|
|
|
260
328
|
Parameters
|
|
261
329
|
----------
|
|
262
|
-
num :
|
|
263
|
-
|
|
330
|
+
num : :class:`int`, optional
|
|
331
|
+
Number of snapshots in each output block. Default is ``1``.
|
|
264
332
|
|
|
265
333
|
Yields
|
|
266
334
|
------
|
|
267
|
-
numpy.ndarray
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
335
|
+
:class:`numpy.ndarray`
|
|
336
|
+
(num, :attr:`num_channels` ``*`` :attr:`num_freqs`). The last block may contain fewer
|
|
337
|
+
snapshots if the input data does not completely fill the requested block size.
|
|
338
|
+
|
|
339
|
+
Notes
|
|
340
|
+
-----
|
|
341
|
+
- The auto-power spectra are computed as the squared magnitude of the spectra
|
|
342
|
+
:math:`|S(f)|^2`, where :math:`S(f)` is the frequency-domain signal.
|
|
343
|
+
- Scaling is applied based on the configuration of the :attr:`scaling` and
|
|
344
|
+
:attr:`single_sided` attributes.
|
|
345
|
+
- The floating-point precision of the output is determined by the
|
|
346
|
+
:attr:`precision` attribute.
|
|
271
347
|
"""
|
|
272
348
|
scale = self._get_scaling_value()
|
|
273
349
|
for temp in self.source.result(num):
|
|
274
350
|
yield ((temp * temp.conjugate()).real * scale).astype(self.precision)
|
|
275
351
|
|
|
276
352
|
|
|
353
|
+
@deprecated_alias({'numchannels': 'num_channels'}, read_only=True)
|
|
277
354
|
class CrossPowerSpectra(AutoPowerSpectra):
|
|
278
|
-
"""Calculates the complex-valued auto- and cross-power spectra.
|
|
279
|
-
|
|
280
|
-
Receives the complex-valued spectra from the source and returns the cross-spectral matrix (CSM) in a flattened
|
|
281
|
-
representation (i.e. the auto- and cross-power spectra are concatenated along the last axis).
|
|
282
|
-
If :attr:`calc_mode` is 'full', the full CSM is calculated, if 'upper', only the upper triangle is calculated.
|
|
283
355
|
"""
|
|
356
|
+
Compute the complex-valued auto- and cross-power spectra from frequency-domain data.
|
|
284
357
|
|
|
285
|
-
|
|
286
|
-
|
|
358
|
+
This class generates the cross-spectral matrix (CSM) in a flattened representation, which
|
|
359
|
+
includes the auto-power spectra (diagonal elements) and cross-power spectra (off-diagonal
|
|
360
|
+
elements). Depending on the :attr:`calc_mode`, the class can compute:
|
|
287
361
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
362
|
+
- The full CSM, which includes all elements.
|
|
363
|
+
- Only the upper triangle of the CSM.
|
|
364
|
+
- Only the lower triangle of the CSM.
|
|
291
365
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
# only the upper triangle. Default is 'full'.
|
|
295
|
-
calc_mode = Trait('full', 'upper', 'lower', desc='calculation mode')
|
|
366
|
+
The results are computed block-by-block and scaled according to the specified configuration.
|
|
367
|
+
"""
|
|
296
368
|
|
|
297
|
-
#:
|
|
298
|
-
#:
|
|
299
|
-
|
|
300
|
-
#: :attr:`numchannels` is :math:`n + n(n-1)/2`.
|
|
301
|
-
numchannels = Property(depends_on='source.numchannels')
|
|
369
|
+
#: The data source providing the input spectra,
|
|
370
|
+
#: implemented as an instance of :class:`~acoular.base.SpectraGenerator` or a derived object.
|
|
371
|
+
source = Instance(SpectraGenerator)
|
|
302
372
|
|
|
303
|
-
|
|
373
|
+
#: Specifies the floating-point precision of the computed cross-spectral matrix (CSM).
|
|
374
|
+
#: Options are ``'complex128'`` and ``'complex64'``. Default is ``'complex128'``.
|
|
375
|
+
precision = Enum('complex128', 'complex64', desc='precision of the fft')
|
|
376
|
+
|
|
377
|
+
#: Defines the calculation mode for the cross-spectral matrix:
|
|
378
|
+
#:
|
|
379
|
+
#: - ``'full'``: Computes the full CSM, including all auto- and cross-power spectra.
|
|
380
|
+
#: - ``'upper'``: Computes only the upper triangle of the CSM,
|
|
381
|
+
# excluding redundant lower-triangle elements.
|
|
382
|
+
#: - ``'lower'``: Computes only the lower triangle of the CSM,
|
|
383
|
+
#: excluding redundant upper-triangle elements.
|
|
384
|
+
#:
|
|
385
|
+
#: Default is ``'full'``.
|
|
386
|
+
calc_mode = Enum('full', 'upper', 'lower', desc='calculation mode')
|
|
387
|
+
|
|
388
|
+
#: The number of channels in the output data. The value depends on the number of input channels
|
|
389
|
+
#: :math:`n` and the selected :attr:`calc_mode`:
|
|
390
|
+
#:
|
|
391
|
+
#: - ``'full'``: :math:`n^2` (all elements in the CSM).
|
|
392
|
+
#: - ``'upper'``: :math:`n + n(n-1)/2` (diagonal + upper triangle elements).
|
|
393
|
+
#: - ``'lower'``: :math:`n + n(n-1)/2` (diagonal + lower triangle elements).
|
|
394
|
+
num_channels = Property(depends_on=['source.num_channels'])
|
|
395
|
+
|
|
396
|
+
#: A unique identifier based on the computation properties.
|
|
304
397
|
digest = Property(depends_on=['source.digest', 'precision', 'scaling', 'single_sided', 'calc_mode'])
|
|
305
398
|
|
|
306
399
|
@cached_property
|
|
307
|
-
def
|
|
308
|
-
n = self.source.
|
|
400
|
+
def _get_num_channels(self):
|
|
401
|
+
n = self.source.num_channels
|
|
309
402
|
return n**2 if self.calc_mode == 'full' else int(n + n * (n - 1) / 2)
|
|
310
403
|
|
|
311
404
|
@cached_property
|
|
@@ -313,22 +406,32 @@ class CrossPowerSpectra(AutoPowerSpectra):
|
|
|
313
406
|
return digest(self)
|
|
314
407
|
|
|
315
408
|
def result(self, num=1):
|
|
316
|
-
"""
|
|
409
|
+
"""
|
|
410
|
+
Generate blocks of complex-valued auto- and cross-power spectra.
|
|
411
|
+
|
|
412
|
+
This generator computes the cross-spectral matrix (CSM) for input spectra block-by-block.
|
|
413
|
+
Depending on the :attr:`calc_mode`, the resulting CSM is flattened in one of three ways:
|
|
414
|
+
|
|
415
|
+
- ``'full'``: Includes all elements of the CSM.
|
|
416
|
+
- ``'upper'``: Includes only the diagonal and upper triangle.
|
|
417
|
+
- ``'lower'``: Includes only the diagonal and lower triangle.
|
|
317
418
|
|
|
318
419
|
Parameters
|
|
319
420
|
----------
|
|
320
|
-
num :
|
|
321
|
-
|
|
322
|
-
(i.e. the number of samples per block).
|
|
421
|
+
num : :class:`int`, optional
|
|
422
|
+
Number of snapshots (blocks) in each output data block. Default is ``1``.
|
|
323
423
|
|
|
324
424
|
Yields
|
|
325
425
|
------
|
|
326
|
-
numpy.ndarray
|
|
327
|
-
|
|
426
|
+
:class:`numpy.ndarray`
|
|
427
|
+
Blocks of complex-valued auto- and cross-power spectra with shape
|
|
428
|
+
``(num, :attr:`num_channels` * :attr:`num_freqs`)``.
|
|
429
|
+
The last block may contain fewer than ``num`` elements if the input data
|
|
430
|
+
does not completely fill the requested block size.
|
|
328
431
|
"""
|
|
329
|
-
nc_src = self.source.
|
|
330
|
-
nc = self.
|
|
331
|
-
nf = self.
|
|
432
|
+
nc_src = self.source.num_channels
|
|
433
|
+
nc = self.num_channels
|
|
434
|
+
nf = self.num_freqs
|
|
332
435
|
scale = self._get_scaling_value()
|
|
333
436
|
|
|
334
437
|
csm_flat = np.zeros((num, nc * nf), dtype=self.precision)
|
|
@@ -345,18 +448,25 @@ class CrossPowerSpectra(AutoPowerSpectra):
|
|
|
345
448
|
else: # lower
|
|
346
449
|
csm_lower = csm_upper.conj().transpose(0, 2, 1)
|
|
347
450
|
csm_flat[i] = csm_lower[:, :nc].reshape(-1)
|
|
348
|
-
csm_upper[...] = 0 # calcCSM adds
|
|
451
|
+
csm_upper[...] = 0 # calcCSM adds cumulative
|
|
349
452
|
yield csm_flat[: i + 1] * scale
|
|
350
453
|
|
|
351
454
|
|
|
352
455
|
class FFTSpectra(RFFT):
|
|
353
|
-
"""
|
|
354
|
-
|
|
355
|
-
Alias for :class:`~acoular.fprocess.RFFT`.
|
|
456
|
+
"""
|
|
457
|
+
Provide the one-sided Fast Fourier Transform (FFT) for multichannel time data.
|
|
356
458
|
|
|
357
459
|
.. deprecated:: 24.10
|
|
358
|
-
|
|
359
|
-
version 25.07.
|
|
460
|
+
The :class:`~acoular.fprocess.FFTSpectra` class is deprecated and will be removed
|
|
461
|
+
in Acoular version 25.07. Please use :class:`~acoular.fprocess.RFFT` instead.
|
|
462
|
+
|
|
463
|
+
Alias for the :class:`~acoular.fprocess.RFFT` class, which computes the one-sided
|
|
464
|
+
Fast Fourier Transform (FFT) for multichannel time data.
|
|
465
|
+
|
|
466
|
+
Warnings
|
|
467
|
+
--------
|
|
468
|
+
This class remains temporarily available for backward compatibility but should not be used in
|
|
469
|
+
new implementations.
|
|
360
470
|
"""
|
|
361
471
|
|
|
362
472
|
def __init__(self, *args, **kwargs):
|