acoular 25.1__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/spectra.py CHANGED
@@ -63,33 +63,55 @@ from .tools.utils import find_basename
63
63
 
64
64
 
65
65
  @deprecated_alias({'numchannels': 'num_channels'}, read_only=True)
66
+ @deprecated_alias({'time_data': 'source'}, read_only=False)
66
67
  class BaseSpectra(ABCHasStrictTraits):
67
- #: Data source; :class:`~acoular.sources.SamplesGenerator` or derived object.
68
+ """
69
+ Base class for handling spectral data in Acoular.
70
+
71
+ This class defines the basic structure and functionality for computing and managing spectral
72
+ data derived from time-domain signals. It includes properties for configuring the Fast Fourier
73
+ Transformation (FFT), including overlap, and other parameters critical for spectral analysis.
74
+ """
75
+
76
+ #: Data source; an instance of :class:`~acoular.base.SamplesGenerator` or derived object.
68
77
  source = Instance(SamplesGenerator)
69
78
 
70
- #: Sampling frequency of output signal, as given by :attr:`source`.
79
+ #: Sampling frequency of the output signal, delegated from :attr:`source`.
71
80
  sample_freq = Delegate('source')
72
81
 
73
- #: Number of time data channels
82
+ #: Number of time microphones, delegated from :attr:`source`.
74
83
  num_channels = Delegate('source')
75
84
 
76
- #: Window function for FFT, one of:
77
- #: * 'Rectangular' (default)
78
- #: * 'Hanning'
79
- #: * 'Hamming'
80
- #: * 'Bartlett'
81
- #: * 'Blackman'
85
+ #: Window function applied during FFT. Can be one of:
86
+ #:
87
+ #: - ``'Rectangular'`` (default)
88
+ #:
89
+ #: - ``'Hanning'``
90
+ #:
91
+ #: - ``'Hamming'``
92
+ #:
93
+ #: - ``'Bartlett'``
94
+ #:
95
+ #: - ``'Blackman'``
82
96
  window = Map(
83
97
  {'Rectangular': ones, 'Hanning': hanning, 'Hamming': hamming, 'Bartlett': bartlett, 'Blackman': blackman},
84
98
  default_value='Rectangular',
85
99
  desc='type of window for FFT',
86
100
  )
87
101
 
88
- #: Overlap factor for averaging: 'None'(default), '50%', '75%', '87.5%'.
102
+ #: Overlap factor for FFT block averaging. One of:
103
+ #:
104
+ #: - ``'None'`` (default)
105
+ #:
106
+ #: - ``'50%'``
107
+ #:
108
+ #: - ``'75%'``
109
+ #:
110
+ #: - ``'87.5%'``
89
111
  overlap = Map({'None': 1, '50%': 2, '75%': 4, '87.5%': 8}, default_value='None', desc='overlap of FFT blocks')
90
112
 
91
- #: FFT block size, one of: 128, 256, 512, 1024, 2048 ... 65536,
92
- #: defaults to 1024.
113
+ #: FFT block size. Must be one of: ``128``, ``256``, ``512``, ``1024``, ... ``65536``.
114
+ #: Default is ``1024``.
93
115
  block_size = Enum(
94
116
  1024,
95
117
  128,
@@ -104,11 +126,10 @@ class BaseSpectra(ABCHasStrictTraits):
104
126
  desc='number of samples per FFT block',
105
127
  )
106
128
 
107
- #: The floating-number-precision of the resulting spectra, corresponding to numpy dtypes.
108
- #: Default is 'complex128'.
129
+ #: Precision of the FFT, corresponding to NumPy dtypes. Default is ``'complex128'``.
109
130
  precision = Enum('complex128', 'complex64', desc='precision of the fft')
110
131
 
111
- # internal identifier
132
+ #: A unique identifier for the spectra, based on its properties. (read-only)
112
133
  digest = Property(depends_on=['precision', 'block_size', 'window', 'overlap'])
113
134
 
114
135
  @abstractmethod
@@ -116,13 +137,42 @@ class BaseSpectra(ABCHasStrictTraits):
116
137
  """Return internal identifier."""
117
138
 
118
139
  def fftfreq(self):
119
- """Return the Discrete Fourier Transform sample frequencies.
140
+ """
141
+ Compute and return the Discrete Fourier Transform sample frequencies.
142
+
143
+ This method generates the frequency values corresponding to the FFT bins for the
144
+ configured :attr:`block_size` and sampling frequency from the data source.
120
145
 
121
146
  Returns
122
147
  -------
123
- f : ndarray
124
- Array of length *block_size/2+1* containing the sample frequencies.
125
-
148
+ :obj:`numpy.ndarray` or :obj:`None`
149
+ Array of shape ``(`` :attr:`block_size` ``/ 2 + 1,)`` containing the sample frequencies.
150
+ If :attr:`source` is not set, returns ``None``.
151
+
152
+ Examples
153
+ --------
154
+ Using normally distributed data for time samples as in
155
+ :class:`~acoular.sources.TimeSamples`.
156
+
157
+ >>> import numpy as np
158
+ >>> from acoular import TimeSamples
159
+ >>> from acoular.spectra import PowerSpectra
160
+ >>>
161
+ >>> data = np.random.rand(1000, 4)
162
+ >>> ts = TimeSamples(data=data, sample_freq=51200)
163
+ >>> print(ts.num_channels, ts.num_samples, ts.sample_freq)
164
+ 4 1000 51200.0
165
+ >>> ps = PowerSpectra(source=ts, block_size=128, window='Blackman')
166
+ >>> ps.fftfreq()
167
+ array([ 0., 400., 800., 1200., 1600., 2000., 2400., 2800.,
168
+ 3200., 3600., 4000., 4400., 4800., 5200., 5600., 6000.,
169
+ 6400., 6800., 7200., 7600., 8000., 8400., 8800., 9200.,
170
+ 9600., 10000., 10400., 10800., 11200., 11600., 12000., 12400.,
171
+ 12800., 13200., 13600., 14000., 14400., 14800., 15200., 15600.,
172
+ 16000., 16400., 16800., 17200., 17600., 18000., 18400., 18800.,
173
+ 19200., 19600., 20000., 20400., 20800., 21200., 21600., 22000.,
174
+ 22400., 22800., 23200., 23600., 24000., 24400., 24800., 25200.,
175
+ 25600.])
126
176
  """
127
177
  if self.source is not None:
128
178
  return abs(fft.fftfreq(self.block_size, 1.0 / self.source.sample_freq)[: int(self.block_size / 2 + 1)])
@@ -146,24 +196,26 @@ class BaseSpectra(ABCHasStrictTraits):
146
196
 
147
197
 
148
198
  class PowerSpectra(BaseSpectra):
149
- """Provides the cross spectral matrix of multichannel time data
150
- and its eigen-decomposition.
151
-
152
- This class includes the efficient calculation of the full cross spectral
153
- matrix using the Welch method with windows and overlap (:cite:`Welch1967`). It also contains
154
- the CSM's eigenvalues and eigenvectors and additional properties.
155
-
156
- The result is computed only when needed, that is when the :attr:`csm`,
157
- :attr:`eva`, or :attr:`eve` attributes are actually read.
158
- Any change in the input data or parameters leads to a new calculation,
159
- again triggered when an attribute is read. The result may be
160
- cached on disk in HDF5 files and need not to be recomputed during
161
- subsequent program runs with identical input data and parameters. The
162
- input data is taken to be identical if the source has identical parameters
163
- and the same file name in case of that the data is read from a file.
199
+ """
200
+ Provides the cross-spectral matrix of multichannel time-domain data and its eigen-decomposition.
201
+
202
+ This class is designed to compute the cross-spectral matrix (CSM) efficiently using the Welch
203
+ method :cite:`Welch1967` with support for windowing and overlapping data segments. It also
204
+ calculates the eigenvalues and eigenvectors of the CSM, allowing for spectral analysis and
205
+ advanced signal processing tasks.
206
+
207
+ Key features:
208
+ - **Efficient Calculation**: Computes the CSM using FFT-based methods.
209
+ - **Caching**: Results can be cached in HDF5 files to avoid redundant calculations for
210
+ identical inputs and parameters.
211
+ - **Lazy Evaluation**: Calculations are triggered only when attributes like :attr:`csm`,
212
+ :attr:`eva`, or :attr:`eve` are accessed.
213
+ - **Dynamic Input Handling**: Automatically recomputes results when the input data or
214
+ parameters change.
164
215
  """
165
216
 
166
- #: Data source; :class:`~acoular.sources.SamplesGenerator` or derived object.
217
+ #: The data source for the time-domain samples. It must be an instance of
218
+ #: :class:`SamplesGenerator<acoular.base.SamplesGenerator>` or a derived class.
167
219
  source = Instance(SamplesGenerator)
168
220
 
169
221
  # Shadow trait, should not be set directly, for internal use.
@@ -172,13 +224,12 @@ class PowerSpectra(BaseSpectra):
172
224
  # Shadow trait, should not be set directly, for internal use.
173
225
  _ind_high = Union(Int(-1), None, desc='index of highest frequency line')
174
226
 
175
- #: Index of lowest frequency line to compute, integer, defaults to 1,
176
- #: is used only by objects that fetch the csm, PowerSpectra computes every
177
- #: frequency line.
227
+ #: Index of lowest frequency line to compute. Default is ``1``. Only used by objects that fetch
228
+ #: the CSM. PowerSpectra computes every frequency line.
178
229
  ind_low = Property(_ind_low, desc='index of lowest frequency line')
179
230
 
180
- #: Index of highest frequency line to compute, integer,
181
- #: defaults to -1 (last possible line for default block_size).
231
+ #: Index of highest frequency line to compute. Default is ``-1``
232
+ #: (last possible line for default :attr:`~BaseSpectra.block_size`).
182
233
  ind_high = Property(_ind_high, desc='index of lowest frequency line')
183
234
 
184
235
  # Stores the set lower frequency, for internal use, should not be set directly.
@@ -187,56 +238,50 @@ class PowerSpectra(BaseSpectra):
187
238
  # Stores the set higher frequency, for internal use, should not be set directly.
188
239
  _freqhc = Union(Float(0), None)
189
240
 
190
- # Saves whether the user set indices or frequencies last, for internal use only,
191
- # not to be set directly, if True (default), indices are used for setting
192
- # the freq_range interval.
241
+ # Saves whether the user set indices or frequencies last, for internal use only, not to be set
242
+ # directly, if ``True``, indices are used for setting the :attr:`freq_range` interval.
243
+ # Default is ``True``.
193
244
  _index_set_last = Bool(True)
194
245
 
195
- #: Flag, if true (default), the result is cached in h5 files and need not
196
- #: to be recomputed during subsequent program runs.
246
+ #: A flag indicating whether the result should be cached in HDF5 files. Default is ``True``.
197
247
  cached = Bool(True, desc='cached flag')
198
248
 
199
- #: Number of FFT blocks to average, readonly
200
- #: (set from block_size and overlap).
249
+ #: The number of FFT blocks used for averaging. This is derived from the
250
+ #: :attr:`~BaseSpectra.block_size` and :attr:`~BaseSpectra.overlap` parameters. (read-only)
201
251
  num_blocks = Property(desc='overall number of FFT blocks')
202
252
 
203
- #: 2-element array with the lowest and highest frequency. If set,
204
- #: will overwrite :attr:`_freqlc` and :attr:`_freqhc` according to
205
- #: the range.
206
- #: The freq_range interval will be the smallest discrete frequency
207
- #: inside the half-open interval [_freqlc, _freqhc[ and the smallest
208
- #: upper frequency outside of the interval.
209
- #: If user chooses the higher frequency larger than the max frequency,
210
- #: the max frequency will be the upper bound.
253
+ #: 2-element array with the lowest and highest frequency. If the higher frequency is larger than
254
+ #: the max frequency, the max frequency will be the upper bound.
211
255
  freq_range = Property(desc='frequency range')
256
+ # If set, will overwrite :attr:`_freqlc` and :attr:`_freqhc` according to the range. The
257
+ # freq_range interval will be the smallest discrete frequency inside the half-open interval
258
+ # [_freqlc, _freqhc[ and the smallest upper frequency outside of the interval.
212
259
 
213
- #: Array with a sequence of indices for all frequencies
214
- #: between :attr:`ind_low` and :attr:`ind_high` within the result, readonly.
260
+ #: The sequence of frequency indices between :attr:`ind_low` and :attr:`ind_high`. (read-only)
215
261
  indices = Property(desc='index range')
216
262
 
217
- #: Name of the cache file without extension, readonly.
263
+ #: The name of the cache file (without the file extension) used for storing results. (read-only)
218
264
  basename = Property(depends_on=['source.digest'], desc='basename for cache file')
219
265
 
220
- #: The cross spectral matrix,
221
- #: (number of frequencies, num_channels, num_channels) array of complex;
222
- #: readonly.
266
+ #: The cross-spectral matrix, represented as an array of shape ``(n, m, m)`` of complex values
267
+ #: for ``n`` frequencies and ``m`` channels as in :attr:`~BaseSpectra.num_channels`. (read-only)
223
268
  csm = Property(desc='cross spectral matrix')
224
269
 
225
- #: Eigenvalues of the cross spectral matrix as an
226
- #: (number of frequencies) array of floats, readonly.
270
+ #: The eigenvalues of the CSM, stored as an array of shape ``(n,)`` of floats for ``n``
271
+ #: frequencies. (read-only)
227
272
  eva = Property(desc='eigenvalues of cross spectral matrix')
228
273
 
229
- #: Eigenvectors of the cross spectral matrix as an
230
- #: (number of frequencies, num_channels, num_channels) array of floats,
231
- #: readonly.
274
+ #: The eigenvectors of the cross spectral matrix, stored as an array of shape ``(n, m, m)`` of
275
+ #: floats for ``n`` frequencies and ``m`` channels as in :attr:`~BaseSpectra.num_channels`.
276
+ #: (read-only)
232
277
  eve = Property(desc='eigenvectors of cross spectral matrix')
233
278
 
234
- # internal identifier
279
+ #: A unique identifier for the spectra, based on its properties. (read-only)
235
280
  digest = Property(
236
281
  depends_on=['source.digest', 'block_size', 'window', 'overlap', 'precision'],
237
282
  )
238
283
 
239
- # hdf5 cache file
284
+ #: The HDF5 cache file used for storing the results if :attr:`cached` is set to ``True``.
240
285
  h5f = Instance(H5CacheFileBase, transient=True)
241
286
 
242
287
  @property_depends_on(['source.num_samples', 'block_size', 'overlap'])
@@ -309,7 +354,34 @@ class PowerSpectra(BaseSpectra):
309
354
  return find_basename(self.source, alternative_basename=self.source.__class__.__name__ + self.source.digest)
310
355
 
311
356
  def calc_csm(self):
312
- """Csm calculation."""
357
+ """
358
+ Calculate the CSM for the given source data.
359
+
360
+ This method computes the CSM by performing a block-wise Fast Fourier Transform (FFT) on the
361
+ source data, applying a window function, and averaging the results. Only the upper
362
+ triangular part of the matrix is computed for efficiency, and the lower triangular part is
363
+ constructed via transposition and complex conjugation.
364
+
365
+ Returns
366
+ -------
367
+ :obj:`numpy.ndarray`
368
+ The computed cross spectral matrix as an array of shape ``(n, m, m)`` of complex values
369
+ for ``n`` frequencies and ``m`` channels as in :attr:`~BaseSpectra.num_channels`.
370
+
371
+ Examples
372
+ --------
373
+ >>> import numpy as np
374
+ >>> from acoular import TimeSamples
375
+ >>> from acoular.spectra import PowerSpectra
376
+ >>>
377
+ >>> data = np.random.rand(1000, 4)
378
+ >>> ts = TimeSamples(data=data, sample_freq=51200)
379
+ >>> print(ts.num_channels, ts.num_samples, ts.sample_freq)
380
+ 4 1000 51200.0
381
+ >>> ps = PowerSpectra(source=ts, block_size=128, window='Blackman')
382
+ >>> ps.csm.shape
383
+ (65, 4, 4)
384
+ """
313
385
  t = self.source
314
386
  wind = self.window_(self.block_size)
315
387
  weight = dot(wind, wind)
@@ -329,7 +401,43 @@ class PowerSpectra(BaseSpectra):
329
401
  return csm * (2.0 / self.block_size / weight / self.num_blocks)
330
402
 
331
403
  def calc_ev(self):
332
- """Eigenvalues / eigenvectors calculation."""
404
+ """
405
+ Calculate eigenvalues and eigenvectors of the CSM for each frequency.
406
+
407
+ The eigenvalues represent the spectral power, and the eigenvectors correspond to the
408
+ principal components of the matrix. This calculation is performed for all frequency slices
409
+ of the CSM.
410
+
411
+ Returns
412
+ -------
413
+ :class:`tuple` of :obj:`numpy.ndarray`
414
+ A tuple containing:
415
+ - :attr:`eva` (:obj:`numpy.ndarray`): Eigenvalues as a 2D array of shape ``(n, m)``,
416
+ where ``n`` is the number of frequencies and ``m`` is the number of channels. The
417
+ datatype depends on the precision.
418
+ - :attr:`eve` (:obj:`numpy.ndarray`): Eigenvectors as a 3D array of shape
419
+ ``(n, m, m)``. The datatype is consistent with the precision of the input data.
420
+
421
+ Notes
422
+ -----
423
+ - The precision of the eigenvalues is determined by :attr:`~BaseSpectra.precision`
424
+ (``'float64'`` for ``complex128`` precision and ``'float32'`` for ``complex64``
425
+ precision).
426
+ - This method assumes the CSM is already computed and accessible via :attr:`csm`.
427
+
428
+ Examples
429
+ --------
430
+ >>> import numpy as np
431
+ >>> from acoular import TimeSamples
432
+ >>> from acoular.spectra import PowerSpectra
433
+ >>>
434
+ >>> data = np.random.rand(1000, 4)
435
+ >>> ts = TimeSamples(data=data, sample_freq=51200)
436
+ >>> ps = PowerSpectra(source=ts, block_size=128, window='Hanning')
437
+ >>> eva, eve = ps.calc_ev()
438
+ >>> print(eva.shape, eve.shape)
439
+ (65, 4) (65, 4, 4)
440
+ """
333
441
  if self.precision == 'complex128':
334
442
  eva_dtype = 'float64'
335
443
  elif self.precision == 'complex64':
@@ -343,17 +451,47 @@ class PowerSpectra(BaseSpectra):
343
451
  return (eva, eve)
344
452
 
345
453
  def calc_eva(self):
346
- """Calculates eigenvalues of csm."""
454
+ """
455
+ Calculate eigenvalues of the CSM.
456
+
457
+ This method computes and returns the eigenvalues of the CSM for all frequency slices.
458
+
459
+ Returns
460
+ -------
461
+ :obj:`numpy.ndarray`
462
+ A 2D array of shape ``(n, m)`` containing the eigenvalues for ``n`` frequencies and
463
+ ``m`` channels. The datatype depends on :attr:`~BaseSpectra.precision` (``'float64'``
464
+ for ``complex128`` precision and ``'float32'`` for ``complex64`` precision).
465
+
466
+ Notes
467
+ -----
468
+ This method internally calls :meth:`calc_ev` and extracts only the eigenvalues.
469
+ """
347
470
  return self.calc_ev()[0]
348
471
 
349
472
  def calc_eve(self):
350
- """Calculates eigenvectors of csm."""
473
+ """
474
+ Calculate eigenvectors of the Cross Spectral Matrix (CSM).
475
+
476
+ This method computes and returns the eigenvectors of the CSM for all frequency slices.
477
+
478
+ Returns
479
+ -------
480
+ :obj:`numpy.ndarray`
481
+ A 3D array of shape ``(n, m, m)`` containing the eigenvectors for ``n`` frequencies and
482
+ ``m`` channels. Each slice ``eve[f]`` represents an ``(m, m)`` matrix of eigenvectors
483
+ for frequency ``f``. The datatype matches the :attr:`~BaseSpectra.precision` of the CSM
484
+ (``complex128`` or ``complex64``).
485
+
486
+ Notes
487
+ -----
488
+ This method internally calls :meth:`calc_ev()` and extracts only the eigenvectors.
489
+ """
351
490
  return self.calc_ev()[1]
352
491
 
353
492
  def _get_filecache(self, traitname):
354
- """Function handles result caching of csm, eigenvectors and eigenvalues
355
- calculation depending on global/local caching behaviour.
356
- """
493
+ # Handle caching of results for CSM, eigenvalues, and eigenvectors.
494
+ # Returns the requested data (``csm``, ``eva``, or ``eve``) as a NumPy array.
357
495
  if traitname == 'csm':
358
496
  func = self.calc_csm
359
497
  numfreq = int(self.block_size / 2 + 1)
@@ -395,42 +533,42 @@ class PowerSpectra(BaseSpectra):
395
533
 
396
534
  @property_depends_on(['digest'])
397
535
  def _get_csm(self):
398
- """Main work is done here:
399
- Cross spectral matrix is either loaded from cache file or
400
- calculated and then additionally stored into cache.
401
- """
402
536
  if config.global_caching == 'none' or (config.global_caching == 'individual' and self.cached is False):
403
537
  return self.calc_csm()
404
538
  return self._get_filecache('csm')
405
539
 
406
540
  @property_depends_on(['digest'])
407
541
  def _get_eva(self):
408
- """Eigenvalues of cross spectral matrix are either loaded from cache file or
409
- calculated and then additionally stored into cache.
410
- """
411
542
  if config.global_caching == 'none' or (config.global_caching == 'individual' and self.cached is False):
412
543
  return self.calc_eva()
413
544
  return self._get_filecache('eva')
414
545
 
415
546
  @property_depends_on(['digest'])
416
547
  def _get_eve(self):
417
- """Eigenvectors of cross spectral matrix are either loaded from cache file or
418
- calculated and then additionally stored into cache.
419
- """
420
548
  if config.global_caching == 'none' or (config.global_caching == 'individual' and self.cached is False):
421
549
  return self.calc_eve()
422
550
  return self._get_filecache('eve')
423
551
 
424
552
  def synthetic_ev(self, freq, num=0):
425
- """Return synthesized frequency band values of the eigenvalues.
553
+ """
554
+ Retrieve synthetic eigenvalues for a specified frequency or frequency range.
555
+
556
+ This method calculates the eigenvalues of the CSM for a single frequency or a synthetic
557
+ frequency range. If ``num`` is set to ``0``, it retrieves the eigenvalues at the exact
558
+ frequency. Otherwise, it averages eigenvalues across a range determined by ``freq`` and
559
+ ``num``.
426
560
 
427
561
  Parameters
428
562
  ----------
429
- freq : float
430
- Band center frequency for which to return the results.
431
- num : integer
432
- Controls the width of the frequency bands considered; defaults to
433
- 3 (third-octave band).
563
+ freq : :class:`float`
564
+ The target frequency for which the eigenvalues are calculated. This is the center
565
+ frequency for synthetic averaging.
566
+ num : :class:`int`, optional
567
+ The number of subdivisions in the logarithmic frequency space around the center
568
+ frequency ``freq``.
569
+
570
+ - ``0`` (default): Only the eigenvalues for the exact frequency line are returned.
571
+ - Non-zero:
434
572
 
435
573
  === =====================
436
574
  num frequency band width
@@ -443,10 +581,25 @@ class PowerSpectra(BaseSpectra):
443
581
 
444
582
  Returns
445
583
  -------
446
- float
447
- Synthesized frequency band value of the eigenvalues (the sum of
448
- all values that are contained in the band).
449
-
584
+ :obj:`numpy.ndarray`
585
+ An array of eigenvalues. If ``num == 0``, the eigenvalues for the single frequency are
586
+ returned. For ``num > 0``, a summed array of eigenvalues across the synthetic frequency
587
+ range is returned.
588
+
589
+ Examples
590
+ --------
591
+ >>> import numpy as np
592
+ >>> from acoular import TimeSamples
593
+ >>> from acoular.spectra import PowerSpectra
594
+ >>> np.random.seed(0)
595
+ >>>
596
+ >>> data = np.random.rand(1000, 4)
597
+ >>> ts = TimeSamples(data=data, sample_freq=51200)
598
+ >>> ps = PowerSpectra(source=ts, block_size=128, window='Hamming')
599
+ >>> ps.synthetic_ev(freq=5000, num=5)
600
+ array([0.00048803, 0.0010141 , 0.00234248, 0.00457097])
601
+ >>> ps.synthetic_ev(freq=5000)
602
+ array([0.00022468, 0.0004589 , 0.00088059, 0.00245989])
450
603
  """
451
604
  f = self.fftfreq()
452
605
  if num == 0:
@@ -460,46 +613,48 @@ class PowerSpectra(BaseSpectra):
460
613
 
461
614
 
462
615
  class PowerSpectraImport(PowerSpectra):
463
- """Provides a dummy class for using pre-calculated cross-spectral
464
- matrices.
465
-
466
- This class does not calculate the cross-spectral matrix. Instead,
467
- the user can inject one or multiple existing CSMs by setting the
468
- :attr:`csm` attribute. This can be useful when algorithms shall be
469
- evaluated with existing CSM matrices.
470
- The frequency or frequencies contained by the CSM must be set via the
471
- attr:`frequencies` attribute. The attr:`num_channels` attributes
472
- is determined on the basis of the CSM shape.
473
- In contrast to the PowerSpectra object, the attributes
474
- :attr:`sample_freq`, :attr:`source`, :attr:`block_size`, :attr:`window`,
475
- :attr:`overlap`, :attr:`cached`, and :attr:`num_blocks`
476
- have no functionality.
616
+ """
617
+ Provides a dummy class for using pre-calculated CSMs.
618
+
619
+ This class does not calculate the CSM. Instead, the user can inject one or multiple existing
620
+ CSMs by setting the :attr:`csm` attribute. This can be useful when algorithms shall be
621
+ evaluated with existing CSMs. The frequency or frequencies contained by the CSM must be set via
622
+ the :attr:`frequencies` attribute. The attr:`num_channels` attributes is determined on the basis
623
+ of the CSM shape. In contrast to the :class:`PowerSpectra` object, the attributes
624
+ :attr:`sample_freq`, :attr:`source`, :attr:`block_size`, :attr:`window`, :attr:`overlap`,
625
+ :attr:`cached`, and :attr:`num_blocks` have no functionality.
477
626
  """
478
627
 
479
- #: The cross spectral matrix,
480
- #: (number of frequencies, num_channels, num_channels) array of complex;
628
+ #: The cross-spectral matrix stored in an array of shape ``(n, m, m)`` of complex for ``n``
629
+ #: frequencies and ``m`` channels.
481
630
  csm = Property(desc='cross spectral matrix')
482
631
 
483
- #: frequencies included in the cross-spectral matrix in ascending order.
484
- #: Compound trait that accepts arguments of type list, array, and float
632
+ #: The frequencies included in the CSM in ascending order. Accepts list, array, or a single
633
+ #: float value.
485
634
  frequencies = Union(CArray, Float, desc='frequencies included in the cross-spectral matrix')
486
635
 
487
- #: Number of time data channels
636
+ #: Number of time data channels, inferred from the shape of the CSM.
488
637
  num_channels = Property(depends_on=['digest'])
489
638
 
639
+ #: :class:`PowerSpectraImport` does not consume time data; source is always ``None``.
490
640
  source = Enum(None, desc='PowerSpectraImport cannot consume time data')
491
641
 
492
- # Sampling frequency of the signal, defaults to None
642
+ #: Sampling frequency of the signal. Default is ``None``
493
643
  sample_freq = Enum(None, desc='sampling frequency')
494
644
 
645
+ #: Block size for FFT, non-functional in this class.
495
646
  block_size = Enum(None, desc='PowerSpectraImport does not operate on blocks of time data')
496
647
 
648
+ #: Windowing method, non-functional in this class.
497
649
  window = Enum(None, desc='PowerSpectraImport does not perform windowing')
498
650
 
651
+ #: Overlap between blocks, non-functional in this class.
499
652
  overlap = Enum(None, desc='PowerSpectraImport does not consume time data')
500
653
 
654
+ #: Caching capability, always disabled.
501
655
  cached = Enum(False, desc='PowerSpectraImport has no caching capabilities')
502
656
 
657
+ #: Number of FFT blocks, always ``None``.
503
658
  num_blocks = Enum(None, desc='PowerSpectraImport cannot determine the number of blocks')
504
659
 
505
660
  # Shadow trait, should not be set directly, for internal use.
@@ -508,16 +663,16 @@ class PowerSpectraImport(PowerSpectra):
508
663
  # Shadow trait, should not be set directly, for internal use.
509
664
  _ind_high = Union(None, Int, desc='index of highest frequency line')
510
665
 
511
- # internal identifier
666
+ #: A unique identifier for the spectra, based on its properties. (read-only)
512
667
  digest = Property(depends_on=['_csmsum'])
513
668
 
514
- #: Name of the cache file without extension, readonly.
669
+ #: Name of the cache file without extension. (read-only)
515
670
  basename = Property(depends_on=['digest'], desc='basename for cache file')
516
671
 
517
- # csm shadow trait, only for internal use.
672
+ # Shadow trait for storing the CSM, for internal use only.
518
673
  _csm = CArray()
519
674
 
520
- # CSM checksum to trigger digest calculation, only for internal use.
675
+ # Checksum for the CSM to trigger digest calculation, for internal use only.
521
676
  _csmsum = Float()
522
677
 
523
678
  def _get_basename(self):
@@ -543,26 +698,23 @@ class PowerSpectraImport(PowerSpectra):
543
698
 
544
699
  @property_depends_on(['digest'])
545
700
  def _get_eva(self):
546
- """Eigenvalues of cross spectral matrix are either loaded from cache file or
547
- calculated and then additionally stored into cache.
548
- """
549
701
  return self.calc_eva()
550
702
 
551
703
  @property_depends_on(['digest'])
552
704
  def _get_eve(self):
553
- """Eigenvectors of cross spectral matrix are either loaded from cache file or
554
- calculated and then additionally stored into cache.
555
- """
556
705
  return self.calc_eve()
557
706
 
558
707
  def fftfreq(self):
559
- """Return the Discrete Fourier Transform sample frequencies.
708
+ """
709
+ Return the Discrete Fourier Transform sample frequencies.
710
+
711
+ The method checks the type of :attr:`frequencies` and returns the corresponding frequency
712
+ array. If :attr:`frequencies` is not defined, a warning is raised.
560
713
 
561
714
  Returns
562
715
  -------
563
- f : ndarray
716
+ :obj:`numpy.ndarray`
564
717
  Array containing the frequencies.
565
-
566
718
  """
567
719
  if isinstance(self.frequencies, float):
568
720
  return array([self.frequencies])