acoular 24.10__py3-none-any.whl → 25.1__py3-none-any.whl

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