acoular 24.7__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
@@ -7,12 +7,11 @@
7
7
  :toctree: generated/
8
8
 
9
9
  BaseSpectra
10
- FFTSpectra
11
10
  PowerSpectra
12
- synthetic
13
11
  PowerSpectraImport
14
12
  """
15
13
 
14
+ from abc import abstractmethod
16
15
  from warnings import warn
17
16
 
18
17
  from numpy import (
@@ -26,52 +25,53 @@ from numpy import (
26
25
  hamming,
27
26
  hanning,
28
27
  imag,
29
- isscalar,
30
28
  linalg,
31
29
  ndarray,
32
30
  newaxis,
33
31
  ones,
34
32
  real,
35
33
  searchsorted,
36
- sqrt,
37
- sum,
34
+ sum, # noqa A004
38
35
  zeros,
39
- zeros_like,
40
36
  )
41
37
  from scipy import fft
42
38
  from traits.api import (
39
+ ABCHasStrictTraits,
43
40
  Bool,
44
41
  CArray,
45
42
  Delegate,
46
43
  Enum,
47
44
  Float,
48
- HasPrivateTraits,
49
45
  Instance,
50
46
  Int,
47
+ Map,
51
48
  Property,
52
- Trait,
49
+ Union,
53
50
  cached_property,
54
51
  property_depends_on,
55
52
  )
56
53
 
57
- from .calib import Calib
54
+ # acoular imports
55
+ from .base import SamplesGenerator
58
56
  from .configuration import config
57
+ from .deprecation import deprecated_alias
59
58
  from .fastFuncs import calcCSM
60
59
  from .h5cache import H5cache
61
60
  from .h5files import H5CacheFileBase
62
61
  from .internal import digest
63
- from .tprocess import SamplesGenerator, TimeInOut
62
+ from .tools.utils import find_basename
64
63
 
65
64
 
66
- class BaseSpectra(HasPrivateTraits):
65
+ @deprecated_alias({'numchannels': 'num_channels'}, read_only=True)
66
+ class BaseSpectra(ABCHasStrictTraits):
67
67
  #: Data source; :class:`~acoular.sources.SamplesGenerator` or derived object.
68
- source = Trait(SamplesGenerator)
68
+ source = Instance(SamplesGenerator)
69
69
 
70
70
  #: Sampling frequency of output signal, as given by :attr:`source`.
71
71
  sample_freq = Delegate('source')
72
72
 
73
73
  #: Number of time data channels
74
- numchannels = Delegate('source')
74
+ num_channels = Delegate('source')
75
75
 
76
76
  #: Window function for FFT, one of:
77
77
  #: * 'Rectangular' (default)
@@ -79,23 +79,22 @@ class BaseSpectra(HasPrivateTraits):
79
79
  #: * 'Hamming'
80
80
  #: * 'Bartlett'
81
81
  #: * 'Blackman'
82
- window = Trait(
83
- 'Rectangular',
82
+ window = Map(
84
83
  {'Rectangular': ones, 'Hanning': hanning, 'Hamming': hamming, 'Bartlett': bartlett, 'Blackman': blackman},
84
+ default_value='Rectangular',
85
85
  desc='type of window for FFT',
86
86
  )
87
87
 
88
88
  #: Overlap factor for averaging: 'None'(default), '50%', '75%', '87.5%'.
89
- 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')
90
90
 
91
91
  #: FFT block size, one of: 128, 256, 512, 1024, 2048 ... 65536,
92
92
  #: defaults to 1024.
93
- block_size = Trait(
93
+ block_size = Enum(
94
94
  1024,
95
95
  128,
96
96
  256,
97
97
  512,
98
- 1024,
99
98
  2048,
100
99
  4096,
101
100
  8192,
@@ -105,16 +104,16 @@ class BaseSpectra(HasPrivateTraits):
105
104
  desc='number of samples per FFT block',
106
105
  )
107
106
 
108
- #: The floating-number-precision of entries of csm, eigenvalues and
109
- #: eigenvectors, corresponding to numpy dtypes. Default is 64 bit.
110
- precision = Trait('complex128', 'complex64', desc='precision of the fft')
107
+ #: The floating-number-precision of the resulting spectra, corresponding to numpy dtypes.
108
+ #: Default is 'complex128'.
109
+ precision = Enum('complex128', 'complex64', desc='precision of the fft')
111
110
 
112
111
  # internal identifier
113
112
  digest = Property(depends_on=['precision', 'block_size', 'window', 'overlap'])
114
113
 
115
- @cached_property
114
+ @abstractmethod
116
115
  def _get_digest(self):
117
- return digest(self)
116
+ """Return internal identifier."""
118
117
 
119
118
  def fftfreq(self):
120
119
  """Return the Discrete Fourier Transform sample frequencies.
@@ -130,9 +129,9 @@ class BaseSpectra(HasPrivateTraits):
130
129
  return None
131
130
 
132
131
  # generator that yields the time data blocks for every channel (with optional overlap)
133
- def get_source_data(self):
132
+ def _get_source_data(self):
134
133
  bs = self.block_size
135
- temp = empty((2 * bs, self.numchannels))
134
+ temp = empty((2 * bs, self.num_channels))
136
135
  pos = bs
137
136
  posinc = bs / self.overlap_
138
137
  for data_block in self.source.result(bs):
@@ -146,52 +145,16 @@ class BaseSpectra(HasPrivateTraits):
146
145
  pos -= bs
147
146
 
148
147
 
149
- class FFTSpectra(BaseSpectra, TimeInOut):
150
- """Provides the spectra of multichannel time data.
151
-
152
- Returns Spectra per block over a Generator.
153
- """
154
-
155
- # internal identifier
156
- digest = Property(depends_on=['source.digest', 'precision', 'block_size', 'window', 'overlap'])
157
-
158
- @cached_property
159
- def _get_digest(self):
160
- return digest(self)
161
-
162
- # generator that yields the fft for every channel
163
- def result(self):
164
- """Python generator that yields the output block-wise.
165
-
166
- Parameters
167
- ----------
168
- num : integer
169
- This parameter defines the size of the blocks to be yielded
170
- (i.e. the number of samples per block).
171
-
172
- Returns
173
- -------
174
- Samples in blocks of shape (numfreq, :attr:`numchannels`).
175
- The last block may be shorter than num.
176
-
177
- """
178
- wind = self.window_(self.block_size)
179
- weight = sqrt(2) / self.block_size * sqrt(self.block_size / dot(wind, wind)) * wind[:, newaxis]
180
- for data in self.get_source_data():
181
- ft = fft.rfft(data * weight, None, 0).astype(self.precision)
182
- yield ft
183
-
184
-
185
148
  class PowerSpectra(BaseSpectra):
186
149
  """Provides the cross spectral matrix of multichannel time data
187
150
  and its eigen-decomposition.
188
151
 
189
152
  This class includes the efficient calculation of the full cross spectral
190
- matrix using the Welch method with windows and overlap. It also contains
153
+ matrix using the Welch method with windows and overlap (:cite:`Welch1967`). It also contains
191
154
  the CSM's eigenvalues and eigenvectors and additional properties.
192
155
 
193
156
  The result is computed only when needed, that is when the :attr:`csm`,
194
- :attr:`eva`, or :attr:`eve` attributes are acturally read.
157
+ :attr:`eva`, or :attr:`eve` attributes are actually read.
195
158
  Any change in the input data or parameters leads to a new calculation,
196
159
  again triggered when an attribute is read. The result may be
197
160
  cached on disk in HDF5 files and need not to be recomputed during
@@ -200,30 +163,14 @@ class PowerSpectra(BaseSpectra):
200
163
  and the same file name in case of that the data is read from a file.
201
164
  """
202
165
 
203
- # Shadow trait, should not be set directly, for internal use.
204
- _source = Trait(SamplesGenerator)
205
-
206
166
  #: Data source; :class:`~acoular.sources.SamplesGenerator` or derived object.
207
- source = Property(_source, desc='time data object')
208
-
209
- #: The :class:`~acoular.tprocess.SamplesGenerator` object that provides the data.
210
- time_data = Property(
211
- _source,
212
- desc='deprecated attribute holding the time data object. Use PowerSpectra.source instead!',
213
- )
214
-
215
- #: The :class:`~acoular.calib.Calib` object that provides the calibration data,
216
- #: defaults to no calibration, i.e. the raw time data is used.
217
- #:
218
- #: **deprecated**: use :attr:`~acoular.sources.TimeSamples.calib` property of
219
- #: :class:`~acoular.sources.TimeSamples` objects
220
- calib = Instance(Calib)
167
+ source = Instance(SamplesGenerator)
221
168
 
222
169
  # Shadow trait, should not be set directly, for internal use.
223
170
  _ind_low = Int(1, desc='index of lowest frequency line')
224
171
 
225
172
  # Shadow trait, should not be set directly, for internal use.
226
- _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')
227
174
 
228
175
  #: Index of lowest frequency line to compute, integer, defaults to 1,
229
176
  #: is used only by objects that fetch the csm, PowerSpectra computes every
@@ -238,7 +185,7 @@ class PowerSpectra(BaseSpectra):
238
185
  _freqlc = Float(0)
239
186
 
240
187
  # Stores the set higher frequency, for internal use, should not be set directly.
241
- _freqhc = Trait(0, (Float, None))
188
+ _freqhc = Union(Float(0), None)
242
189
 
243
190
  # Saves whether the user set indices or frequencies last, for internal use only,
244
191
  # not to be set directly, if True (default), indices are used for setting
@@ -268,10 +215,10 @@ class PowerSpectra(BaseSpectra):
268
215
  indices = Property(desc='index range')
269
216
 
270
217
  #: Name of the cache file without extension, readonly.
271
- basename = Property(depends_on='_source.digest', desc='basename for cache file')
218
+ basename = Property(depends_on=['source.digest'], desc='basename for cache file')
272
219
 
273
220
  #: The cross spectral matrix,
274
- #: (number of frequencies, numchannels, numchannels) array of complex;
221
+ #: (number of frequencies, num_channels, num_channels) array of complex;
275
222
  #: readonly.
276
223
  csm = Property(desc='cross spectral matrix')
277
224
 
@@ -280,23 +227,23 @@ class PowerSpectra(BaseSpectra):
280
227
  eva = Property(desc='eigenvalues of cross spectral matrix')
281
228
 
282
229
  #: Eigenvectors of the cross spectral matrix as an
283
- #: (number of frequencies, numchannels, numchannels) array of floats,
230
+ #: (number of frequencies, num_channels, num_channels) array of floats,
284
231
  #: readonly.
285
232
  eve = Property(desc='eigenvectors of cross spectral matrix')
286
233
 
287
234
  # internal identifier
288
235
  digest = Property(
289
- depends_on=['_source.digest', 'calib.digest', 'block_size', 'window', 'overlap', 'precision'],
236
+ depends_on=['source.digest', 'block_size', 'window', 'overlap', 'precision'],
290
237
  )
291
238
 
292
239
  # hdf5 cache file
293
240
  h5f = Instance(H5CacheFileBase, transient=True)
294
241
 
295
- @property_depends_on('_source.numsamples, block_size, overlap')
242
+ @property_depends_on(['source.num_samples', 'block_size', 'overlap'])
296
243
  def _get_num_blocks(self):
297
- 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
298
245
 
299
- @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'])
300
247
  def _get_freq_range(self):
301
248
  fftfreq = self.fftfreq()
302
249
  if fftfreq is not None:
@@ -310,7 +257,7 @@ class PowerSpectra(BaseSpectra):
310
257
  self._freqlc = freq_range[0]
311
258
  self._freqhc = freq_range[1]
312
259
 
313
- @property_depends_on('_source.sample_freq, block_size, _ind_low, _freqlc')
260
+ @property_depends_on(['source.sample_freq', 'block_size', '_ind_low', '_freqlc'])
314
261
  def _get_ind_low(self):
315
262
  fftfreq = self.fftfreq()
316
263
  if fftfreq is not None:
@@ -319,7 +266,7 @@ class PowerSpectra(BaseSpectra):
319
266
  return searchsorted(fftfreq[:-1], self._freqlc)
320
267
  return None
321
268
 
322
- @property_depends_on('_source.sample_freq, block_size, _ind_high, _freqhc')
269
+ @property_depends_on(['source.sample_freq', 'block_size', '_ind_high', '_freqhc'])
323
270
  def _get_ind_high(self):
324
271
  fftfreq = self.fftfreq()
325
272
  if fftfreq is not None:
@@ -340,19 +287,7 @@ class PowerSpectra(BaseSpectra):
340
287
  self._index_set_last = True
341
288
  self._ind_low = ind_low
342
289
 
343
- def _set_time_data(self, time_data):
344
- self._source = time_data
345
-
346
- def _set_source(self, source):
347
- self._source = source
348
-
349
- def _get_time_data(self):
350
- return self._source
351
-
352
- def _get_source(self):
353
- return self._source
354
-
355
- @property_depends_on('block_size, ind_low, ind_high')
290
+ @property_depends_on(['block_size', 'ind_low', 'ind_high'])
356
291
  def _get_indices(self):
357
292
  fftfreq = self.fftfreq()
358
293
  if fftfreq is not None:
@@ -371,9 +306,7 @@ class PowerSpectra(BaseSpectra):
371
306
 
372
307
  @cached_property
373
308
  def _get_basename(self):
374
- if 'basename' in self._source.all_trait_names():
375
- return self._source.basename
376
- return self._source.__class__.__name__ + self._source.digest
309
+ return find_basename(self.source, alternative_basename=self.source.__class__.__name__ + self.source.digest)
377
310
 
378
311
  def calc_csm(self):
379
312
  """Csm calculation."""
@@ -382,17 +315,10 @@ class PowerSpectra(BaseSpectra):
382
315
  weight = dot(wind, wind)
383
316
  wind = wind[newaxis, :].swapaxes(0, 1)
384
317
  numfreq = int(self.block_size / 2 + 1)
385
- csm_shape = (numfreq, t.numchannels, t.numchannels)
318
+ csm_shape = (numfreq, t.num_channels, t.num_channels)
386
319
  csm_upper = zeros(csm_shape, dtype=self.precision)
387
- # print "num blocks", self.num_blocks
388
- # for backward compatibility
389
- if self.calib and self.calib.num_mics > 0:
390
- if self.calib.num_mics == t.numchannels:
391
- wind = wind * self.calib.data[newaxis, :]
392
- else:
393
- raise ValueError('Calibration data not compatible: %i, %i' % (self.calib.num_mics, t.numchannels))
394
320
  # get time data blockwise
395
- for data in self.get_source_data():
321
+ for data in self._get_source_data():
396
322
  ft = fft.rfft(data * wind, None, 0).astype(self.precision)
397
323
  calcCSM(csm_upper, ft) # only upper triangular part of matrix is calculated (for speed reasons)
398
324
  # create the full csm matrix via transposing and complex conj.
@@ -424,23 +350,6 @@ class PowerSpectra(BaseSpectra):
424
350
  """Calculates eigenvectors of csm."""
425
351
  return self.calc_ev()[1]
426
352
 
427
- def _handle_dual_calibration(self):
428
- obj = self.source # start with time_data obj
429
- while obj:
430
- if 'calib' in obj.all_trait_names(): # at original source?
431
- if obj.calib and self.calib:
432
- if obj.calib.digest == self.calib.digest:
433
- self.calib = None # ignore it silently
434
- else:
435
- msg = 'Non-identical dual calibration for both TimeSamples and PowerSpectra object'
436
- raise ValueError(msg)
437
- obj = None
438
- else:
439
- try:
440
- obj = obj.source # traverse down until original data source
441
- except AttributeError:
442
- obj = None
443
-
444
353
  def _get_filecache(self, traitname):
445
354
  """Function handles result caching of csm, eigenvectors and eigenvalues
446
355
  calculation depending on global/local caching behaviour.
@@ -448,7 +357,7 @@ class PowerSpectra(BaseSpectra):
448
357
  if traitname == 'csm':
449
358
  func = self.calc_csm
450
359
  numfreq = int(self.block_size / 2 + 1)
451
- shape = (numfreq, self._source.numchannels, self._source.numchannels)
360
+ shape = (numfreq, self.source.num_channels, self.source.num_channels)
452
361
  precision = self.precision
453
362
  elif traitname == 'eva':
454
363
  func = self.calc_eva
@@ -484,18 +393,17 @@ class PowerSpectra(BaseSpectra):
484
393
  self.h5f.flush()
485
394
  return ac
486
395
 
487
- @property_depends_on('digest')
396
+ @property_depends_on(['digest'])
488
397
  def _get_csm(self):
489
398
  """Main work is done here:
490
399
  Cross spectral matrix is either loaded from cache file or
491
400
  calculated and then additionally stored into cache.
492
401
  """
493
- self._handle_dual_calibration()
494
402
  if config.global_caching == 'none' or (config.global_caching == 'individual' and self.cached is False):
495
403
  return self.calc_csm()
496
404
  return self._get_filecache('csm')
497
405
 
498
- @property_depends_on('digest')
406
+ @property_depends_on(['digest'])
499
407
  def _get_eva(self):
500
408
  """Eigenvalues of cross spectral matrix are either loaded from cache file or
501
409
  calculated and then additionally stored into cache.
@@ -504,7 +412,7 @@ class PowerSpectra(BaseSpectra):
504
412
  return self.calc_eva()
505
413
  return self._get_filecache('eva')
506
414
 
507
- @property_depends_on('digest')
415
+ @property_depends_on(['digest'])
508
416
  def _get_eve(self):
509
417
  """Eigenvectors of cross spectral matrix are either loaded from cache file or
510
418
  calculated and then additionally stored into cache.
@@ -551,101 +459,6 @@ class PowerSpectra(BaseSpectra):
551
459
  return sum(self.eva[f1:f2], 0)
552
460
 
553
461
 
554
- def synthetic(data, freqs, f, num=3):
555
- """Returns synthesized frequency band values of spectral data.
556
-
557
- If used with :meth:`Beamformer.result()<acoular.fbeamform.BeamformerBase.result>`
558
- and only one frequency band, the output is identical to the result of the intrinsic
559
- :meth:`Beamformer.synthetic<acoular.fbeamform.BeamformerBase.synthetic>` method.
560
- It can, however, also be used with the
561
- :meth:`Beamformer.integrate<acoular.fbeamform.BeamformerBase.integrate>`
562
- output and more frequency bands.
563
-
564
- Parameters
565
- ----------
566
- data : array of floats
567
- The spectral data (squared sound pressures in Pa^2) in an array with one value
568
- per frequency line.
569
- The number of entries must be identical to the number of
570
- grid points.
571
- freq : array of floats
572
- The frequencies that correspon to the input *data* (as yielded by
573
- the :meth:`PowerSpectra.fftfreq<acoular.spectra.PowerSpectra.fftfreq>`
574
- method).
575
- f : float or list of floats
576
- Band center frequency/frequencies for which to return the results.
577
- num : integer
578
- Controls the width of the frequency bands considered; defaults to
579
- 3 (third-octave band).
580
-
581
- === =====================
582
- num frequency band width
583
- === =====================
584
- 0 single frequency line
585
- 1 octave band
586
- 3 third-octave band
587
- n 1/n-octave band
588
- === =====================
589
-
590
- Returns
591
- -------
592
- array of floats
593
- Synthesized frequency band values of the beamforming result at
594
- each grid point (the sum of all values that are contained in the band).
595
- Note that the frequency resolution and therefore the bandwidth
596
- represented by a single frequency line depends on
597
- the :attr:`sampling frequency<acoular.tprocess.SamplesGenerator.sample_freq>`
598
- and used :attr:`FFT block size<acoular.spectra.PowerSpectra.block_size>`.
599
-
600
- """
601
- if isscalar(f):
602
- f = (f,)
603
- if num == 0:
604
- # single frequency lines
605
- res = []
606
- for i in f:
607
- ind = searchsorted(freqs, i)
608
- if ind >= len(freqs):
609
- warn(
610
- 'Queried frequency (%g Hz) not in resolved frequency range. Returning zeros.' % i,
611
- Warning,
612
- stacklevel=2,
613
- )
614
- h = zeros_like(data[0])
615
- else:
616
- if freqs[ind] != i:
617
- warn(
618
- f'Queried frequency ({i:g} Hz) not in set of '
619
- 'discrete FFT sample frequencies. '
620
- f'Using frequency {freqs[ind]:g} Hz instead.',
621
- Warning,
622
- stacklevel=2,
623
- )
624
- h = data[ind]
625
- res += [h]
626
- else:
627
- # fractional octave bands
628
- res = []
629
- for i in f:
630
- f1 = i * 2.0 ** (-0.5 / num)
631
- f2 = i * 2.0 ** (+0.5 / num)
632
- ind1 = searchsorted(freqs, f1)
633
- ind2 = searchsorted(freqs, f2)
634
- if ind1 == ind2:
635
- warn(
636
- f'Queried frequency band ({f1:g} to {f2:g} Hz) does not '
637
- 'include any discrete FFT sample frequencies. '
638
- 'Returning zeros.',
639
- Warning,
640
- stacklevel=2,
641
- )
642
- h = zeros_like(data[0])
643
- else:
644
- h = sum(data[ind1:ind2], 0)
645
- res += [h]
646
- return array(res)
647
-
648
-
649
462
  class PowerSpectraImport(PowerSpectra):
650
463
  """Provides a dummy class for using pre-calculated cross-spectral
651
464
  matrices.
@@ -655,27 +468,24 @@ class PowerSpectraImport(PowerSpectra):
655
468
  :attr:`csm` attribute. This can be useful when algorithms shall be
656
469
  evaluated with existing CSM matrices.
657
470
  The frequency or frequencies contained by the CSM must be set via the
658
- attr:`frequencies` attribute. The attr:`numchannels` attributes
471
+ attr:`frequencies` attribute. The attr:`num_channels` attributes
659
472
  is determined on the basis of the CSM shape.
660
473
  In contrast to the PowerSpectra object, the attributes
661
- :attr:`sample_freq`, :attr:`time_data`, :attr:`source`,
662
- :attr:`block_size`, :attr:`calib`, :attr:`window`,
474
+ :attr:`sample_freq`, :attr:`source`, :attr:`block_size`, :attr:`window`,
663
475
  :attr:`overlap`, :attr:`cached`, and :attr:`num_blocks`
664
476
  have no functionality.
665
477
  """
666
478
 
667
479
  #: The cross spectral matrix,
668
- #: (number of frequencies, numchannels, numchannels) array of complex;
480
+ #: (number of frequencies, num_channels, num_channels) array of complex;
669
481
  csm = Property(desc='cross spectral matrix')
670
482
 
671
483
  #: frequencies included in the cross-spectral matrix in ascending order.
672
484
  #: Compound trait that accepts arguments of type list, array, and float
673
- 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')
674
486
 
675
487
  #: Number of time data channels
676
- numchannels = Property(depends_on=['digest'])
677
-
678
- time_data = Enum(None, desc='PowerSpectraImport cannot consume time data')
488
+ num_channels = Property(depends_on=['digest'])
679
489
 
680
490
  source = Enum(None, desc='PowerSpectraImport cannot consume time data')
681
491
 
@@ -684,8 +494,6 @@ class PowerSpectraImport(PowerSpectra):
684
494
 
685
495
  block_size = Enum(None, desc='PowerSpectraImport does not operate on blocks of time data')
686
496
 
687
- calib = Enum(None, desc='PowerSpectraImport cannot calibrate the time data')
688
-
689
497
  window = Enum(None, desc='PowerSpectraImport does not perform windowing')
690
498
 
691
499
  overlap = Enum(None, desc='PowerSpectraImport does not consume time data')
@@ -698,17 +506,13 @@ class PowerSpectraImport(PowerSpectra):
698
506
  _ind_low = Int(0, desc='index of lowest frequency line')
699
507
 
700
508
  # Shadow trait, should not be set directly, for internal use.
701
- _ind_high = Trait(None, (Int, None), desc='index of highest frequency line')
509
+ _ind_high = Union(None, Int, desc='index of highest frequency line')
702
510
 
703
511
  # internal identifier
704
- digest = Property(
705
- depends_on=[
706
- '_csmsum',
707
- ],
708
- )
512
+ digest = Property(depends_on=['_csmsum'])
709
513
 
710
514
  #: Name of the cache file without extension, readonly.
711
- basename = Property(depends_on='digest', desc='basename for cache file')
515
+ basename = Property(depends_on=['digest'], desc='basename for cache file')
712
516
 
713
517
  # csm shadow trait, only for internal use.
714
518
  _csm = CArray()
@@ -723,7 +527,7 @@ class PowerSpectraImport(PowerSpectra):
723
527
  def _get_digest(self):
724
528
  return digest(self)
725
529
 
726
- def _get_numchannels(self):
530
+ def _get_num_channels(self):
727
531
  return self.csm.shape[1]
728
532
 
729
533
  def _get_csm(self):
@@ -731,19 +535,20 @@ class PowerSpectraImport(PowerSpectra):
731
535
 
732
536
  def _set_csm(self, csm):
733
537
  if (len(csm.shape) != 3) or (csm.shape[1] != csm.shape[2]):
734
- 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)!'
735
540
  raise ValueError(msg)
736
541
  self._csmsum = real(self._csm).sum() + (imag(self._csm) ** 2).sum() # to trigger new digest creation
737
542
  self._csm = csm
738
543
 
739
- @property_depends_on('digest')
544
+ @property_depends_on(['digest'])
740
545
  def _get_eva(self):
741
546
  """Eigenvalues of cross spectral matrix are either loaded from cache file or
742
547
  calculated and then additionally stored into cache.
743
548
  """
744
549
  return self.calc_eva()
745
550
 
746
- @property_depends_on('digest')
551
+ @property_depends_on(['digest'])
747
552
  def _get_eve(self):
748
553
  """Eigenvectors of cross spectral matrix are either loaded from cache file or
749
554
  calculated and then additionally stored into cache.