acoular 25.1__py3-none-any.whl → 25.3.post1__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/fprocess.py CHANGED
@@ -1,7 +1,8 @@
1
1
  # ------------------------------------------------------------------------------
2
2
  # Copyright (c) Acoular Development Team.
3
3
  # ------------------------------------------------------------------------------
4
- """Implements blockwise processing methods in the frequency domain.
4
+ """
5
+ Implements blockwise processing methods in the frequency domain.
5
6
 
6
7
  .. autosummary::
7
8
  :toctree: generated/
@@ -30,46 +31,55 @@ from .spectra import BaseSpectra
30
31
 
31
32
  @deprecated_alias({'numfreqs': 'num_freqs', 'numsamples': 'num_samples'}, read_only=True)
32
33
  class RFFT(BaseSpectra, SpectraOut):
33
- """Provides the one-sided Fast Fourier Transform (FFT) for real-valued multichannel time data.
34
+ """
35
+ Compute the one-sided Fast Fourier Transform (FFT) for real-valued multichannel time data.
34
36
 
35
- The FFT is calculated block-wise, i.e. the input data is divided into blocks of length
36
- :attr:`block_size` and the FFT is calculated for each block. Optionally, a window function
37
- can be applied to the data before the FFT calculation via the :attr:`window` attribute.
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.
38
43
  """
39
44
 
40
- #: 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.
41
47
  source = Instance(SamplesGenerator)
42
48
 
43
- #: Number of workers to use for the FFT calculation. If negative values are used,
44
- #: all available logical CPUs will be considered (``scipy.fft.rfft`` implementation wraps around
45
- #: from ``os.cpu_count()``).
46
- #: Default is `None` (handled by scipy)
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.
47
52
  workers = Union(Int(), None, default_value=None, desc='number of workers to use')
48
53
 
49
- #: Scaling method, either 'amplitude', 'energy' or :code:`none`.
50
- #: Default is :code:`none`.
51
- #: 'energy': compensates for the energy loss due to truncation of the FFT result. The resulting
52
- #: one-sided spectrum is multiplied by 2.0, except for the DC and Nyquist frequency.
53
- #: 'amplitude': scales the one-sided spectrum so that the amplitude of discrete tones does not
54
- #: depend on the block size.
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.
55
61
  scaling = Enum('none', 'energy', 'amplitude')
56
62
 
57
- #: block size of the FFT. Default is 1024.
63
+ #: The length of each block of time-domain data used for the FFT. Must be an even number.
64
+ #: Default is ``1024``.
58
65
  block_size = Property()
59
66
 
60
- #: Number of frequencies in the output.
67
+ #: The number of frequency bins in the FFT result, calculated as ``block_size // 2 + 1``.
61
68
  num_freqs = Property(depends_on=['_block_size'])
62
69
 
63
- #: Number of snapshots in the output.
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.
64
72
  num_samples = Property(depends_on=['source.num_samples', '_block_size'])
65
73
 
66
- #: 1-D array of FFT sample frequencies.
74
+ #: A 1-D array containing the Discrete Fourier Transform
75
+ #: sample frequencies corresponding to the FFT output.
67
76
  freqs = Property()
68
77
 
69
- # internal block size variable
78
+ # Internal representation of the block size for FFT processing.
79
+ # Used for validation and property management.
70
80
  _block_size = Int(1024, desc='block size of the FFT')
71
81
 
72
- # internal identifier
82
+ #: A unique identifier based on the process properties.
73
83
  digest = Property(depends_on=['source.digest', 'scaling', 'precision', '_block_size', 'window', 'overlap'])
74
84
 
75
85
  @cached_property
@@ -96,39 +106,49 @@ class RFFT(BaseSpectra, SpectraOut):
96
106
  self._block_size = value
97
107
 
98
108
  def _scale(self, data, scaling_value):
99
- """Corrects the energy of the one-sided FFT data."""
109
+ # Corrects the energy of the one-sided FFT data.
100
110
  if self.scaling == 'amplitude' or self.scaling == 'energy':
101
111
  data[1:-1] *= 2.0
102
112
  data *= scaling_value
103
113
  return data
104
114
 
105
115
  def _get_freqs(self):
106
- """Return the Discrete Fourier Transform sample frequencies.
107
-
108
- Returns
109
- -------
110
- f : ndarray
111
- 1-D Array of length *block_size/2+1* containing the sample frequencies.
116
+ # Return the Discrete Fourier Transform sample frequencies.
112
117
 
113
- """
118
+ # Returns
119
+ # -------
120
+ # f : ndarray
121
+ # 1-D Array of length *block_size // 2 + 1* containing the sample frequencies.
114
122
  if self.source is not None:
115
123
  return abs(fft.fftfreq(self.block_size, 1.0 / self.source.sample_freq)[: int(self.block_size / 2 + 1)])
116
124
  return np.array([])
117
125
 
118
126
  def result(self, num=1):
119
- """Python generator that yields the output block-wise.
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.
120
133
 
121
134
  Parameters
122
135
  ----------
123
- num : integer
124
- This parameter defines the number of multi-channel spectra (i.e. snapshots) per block
125
- returned by the generator.
126
-
127
- Returns
128
- -------
129
- Spectra block of shape (num, :attr:`num_channels` * :attr:`num_freqs`).
130
- 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``.
131
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.
132
152
  """
133
153
  wind = self.window_(self.block_size)
134
154
  if self.scaling == 'none' or self.scaling == 'energy': # only compensate for the window
@@ -152,28 +172,39 @@ class RFFT(BaseSpectra, SpectraOut):
152
172
 
153
173
  @deprecated_alias({'numsamples': 'num_samples'}, read_only=True)
154
174
  class IRFFT(TimeOut):
155
- """Calculates the inverse Fast Fourier Transform (IFFT) for one-sided multi-channel spectra."""
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
+ """
156
183
 
157
- #: Data source; :class:`~acoular.base.SpectraGenerator` or derived object.
184
+ #: Data source providing one-sided spectra, implemented as an instance of
185
+ # :class:`~acoular.base.SpectraGenerator` or a derived object.
158
186
  source = Instance(SpectraGenerator)
159
187
 
160
- #: Number of workers to use for the FFT calculation. If negative values are used,
161
- #: all available logical CPUs will be considered (``scipy.fft.rfft`` implementation wraps around
162
- #: from ``os.cpu_count()``).
163
- #: Default is `None` (handled by scipy)
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.
164
191
  workers = Union(Int(), None, default_value=None, desc='number of workers to use')
165
192
 
166
- #: The floating-number-precision of the resulting time signals, corresponding to numpy dtypes.
167
- #: Default is 64 bit.
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.
168
196
  precision = Enum('float64', 'float32', desc='precision of the time signal after the ifft')
169
197
 
170
- #: Number of time samples in the output.
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.
171
201
  num_samples = Property(depends_on=['source.num_samples', 'source._block_size'])
172
202
 
173
- # internal time signal buffer to handle arbitrary output block sizes
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.
174
205
  _buffer = CArray(desc='signal buffer')
175
206
 
176
- # internal identifier
207
+ #: A unique identifier based on the process properties.
177
208
  digest = Property(depends_on=['source.digest', 'scaling', 'precision', '_block_size', 'window', 'overlap'])
178
209
 
179
210
  def _get_num_samples(self):
@@ -203,18 +234,32 @@ class IRFFT(TimeOut):
203
234
  raise ValueError(msg)
204
235
 
205
236
  def result(self, num):
206
- """Python generator that yields the output block-wise.
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.
207
243
 
208
244
  Parameters
209
245
  ----------
210
- num : integer
211
- This parameter defines the size of the blocks to be yielded
212
- (i.e. the number of samples per block). The last block may be shorter than num.
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.
213
249
 
214
250
  Yields
215
251
  ------
216
- numpy.ndarray
217
- Yields blocks of shape (num, num_channels).
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.
218
263
  """
219
264
  self._validate()
220
265
  bs = self.source.block_size
@@ -230,22 +275,34 @@ class IRFFT(TimeOut):
230
275
 
231
276
 
232
277
  class AutoPowerSpectra(SpectraOut):
233
- """Calculates the real-valued auto-power spectra."""
278
+ """
279
+ Compute the real-valued auto-power spectra from multi-channel frequency-domain data.
234
280
 
235
- #: Data source; :class:`~acoular.base.SpectraGenerator` or derived object.
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.
236
289
  source = Instance(SpectraGenerator)
237
290
 
238
- #: Scaling method, either 'power' or 'psd' (Power Spectral Density).
239
- #: Only relevant if the source is a :class:`~acoular.fprocess.FreqInOut` object.
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.
240
296
  scaling = Enum('power', 'psd')
241
297
 
242
- #: Determines if the spectra yielded by the source are single-sided spectra.
298
+ #: A Boolean flag indicating whether the input spectra are single-sided. Default is ``True``.
243
299
  single_sided = Bool(True, desc='single sided spectrum')
244
300
 
245
- #: The floating-number-precision of entries, corresponding to numpy dtypes. Default is 64 bit.
301
+ #: Specifies the floating-point precision of the computed auto-power spectra.
302
+ #: Options are ``'float64'`` and ``'float32'``. Default is ``'float64'``.
246
303
  precision = Enum('float64', 'float32', desc='floating-number-precision')
247
304
 
248
- # internal identifier
305
+ #: A unique identifier based on the computation properties.
249
306
  digest = Property(depends_on=['source.digest', 'precision', 'scaling', 'single_sided'])
250
307
 
251
308
  @cached_property
@@ -261,19 +318,32 @@ class AutoPowerSpectra(SpectraOut):
261
318
  return scale
262
319
 
263
320
  def result(self, num=1):
264
- """Python generator that yields the real-valued auto-power spectra.
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.
265
327
 
266
328
  Parameters
267
329
  ----------
268
- num : integer
269
- This parameter defines the number of snapshots within each output data block.
330
+ num : :class:`int`, optional
331
+ Number of snapshots in each output block. Default is ``1``.
270
332
 
271
333
  Yields
272
334
  ------
273
- numpy.ndarray
274
- Yields blocks of shape (num, num_channels * num_freqs).
275
- The last block may be shorter than num.
276
-
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.
277
347
  """
278
348
  scale = self._get_scaling_value()
279
349
  for temp in self.source.result(num):
@@ -282,33 +352,48 @@ class AutoPowerSpectra(SpectraOut):
282
352
 
283
353
  @deprecated_alias({'numchannels': 'num_channels'}, read_only=True)
284
354
  class CrossPowerSpectra(AutoPowerSpectra):
285
- """Calculates the complex-valued auto- and cross-power spectra.
355
+ """
356
+ Compute the complex-valued auto- and cross-power spectra from frequency-domain data.
357
+
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:
361
+
362
+ - The full CSM, which includes all elements.
363
+ - Only the upper triangle of the CSM.
364
+ - Only the lower triangle of the CSM.
286
365
 
287
- Receives the complex-valued spectra from the source and returns the cross-spectral matrix (CSM)
288
- in a flattened representation (i.e. the auto- and cross-power spectra are concatenated along the
289
- last axis). If :attr:`calc_mode` is 'full', the full CSM is calculated, if 'upper', only the
290
- upper triangle is calculated.
366
+ The results are computed block-by-block and scaled according to the specified configuration.
291
367
  """
292
368
 
293
- #: Data source; :class:`~acoular.base.SpectraGenerator` or derived object.
369
+ #: The data source providing the input spectra,
370
+ #: implemented as an instance of :class:`~acoular.base.SpectraGenerator` or a derived object.
294
371
  source = Instance(SpectraGenerator)
295
372
 
296
- #: The floating-number-precision of entries of csm, eigenvalues and
297
- #: eigenvectors, corresponding to numpy dtypes. Default is 64 bit.
373
+ #: Specifies the floating-point precision of the computed cross-spectral matrix (CSM).
374
+ #: Options are ``'complex128'`` and ``'complex64'``. Default is ``'complex128'``.
298
375
  precision = Enum('complex128', 'complex64', desc='precision of the fft')
299
376
 
300
- #: Calculation mode, either 'full' or 'upper'.
301
- #: 'full' calculates the full cross-spectral matrix, 'upper' calculates
302
- # only the upper triangle. Default is 'full'.
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'``.
303
386
  calc_mode = Enum('full', 'upper', 'lower', desc='calculation mode')
304
387
 
305
- #: Number of channels in output. If :attr:`calc_mode` is 'full', then
306
- #: :attr:`num_channels` is :math:`n^2`, where :math:`n` is the number of
307
- #: channels in the input. If :attr:`calc_mode` is 'upper', then
308
- #: :attr:`num_channels` is :math:`n + n(n-1)/2`.
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).
309
394
  num_channels = Property(depends_on=['source.num_channels'])
310
395
 
311
- # internal identifier
396
+ #: A unique identifier based on the computation properties.
312
397
  digest = Property(depends_on=['source.digest', 'precision', 'scaling', 'single_sided', 'calc_mode'])
313
398
 
314
399
  @cached_property
@@ -321,18 +406,28 @@ class CrossPowerSpectra(AutoPowerSpectra):
321
406
  return digest(self)
322
407
 
323
408
  def result(self, num=1):
324
- """Python generator that yields the output block-wise.
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.
325
418
 
326
419
  Parameters
327
420
  ----------
328
- num : integer
329
- This parameter defines the size of the blocks to be yielded
330
- (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``.
331
423
 
332
424
  Yields
333
425
  ------
334
- numpy.ndarray
335
- Yields blocks of shape (num, num_channels * num_freqs).
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.
336
431
  """
337
432
  nc_src = self.source.num_channels
338
433
  nc = self.num_channels
@@ -358,13 +453,20 @@ class CrossPowerSpectra(AutoPowerSpectra):
358
453
 
359
454
 
360
455
  class FFTSpectra(RFFT):
361
- """Provides the one-sided Fast Fourier Transform (FFT) for multichannel time data.
362
-
363
- Alias for :class:`~acoular.fprocess.RFFT`.
456
+ """
457
+ Provide the one-sided Fast Fourier Transform (FFT) for multichannel time data.
364
458
 
365
459
  .. deprecated:: 24.10
366
- Using :class:`~acoular.fprocess.FFTSpectra` is deprecated and will be removed in Acoular
367
- version 25.07. Use :class:`~acoular.fprocess.RFFT` instead.
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.
368
470
  """
369
471
 
370
472
  def __init__(self, *args, **kwargs):