acoular 24.5__py3-none-any.whl → 24.10__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/sources.py CHANGED
@@ -1,7 +1,7 @@
1
1
  # ------------------------------------------------------------------------------
2
2
  # Copyright (c) Acoular Development Team.
3
3
  # ------------------------------------------------------------------------------
4
- """Measured multichannel data managment and simulation of acoustic sources.
4
+ """Measured multichannel data management and simulation of acoustic sources.
5
5
 
6
6
  .. autosummary::
7
7
  :toctree: generated/
@@ -22,6 +22,7 @@
22
22
 
23
23
  # imports from other packages
24
24
 
25
+ import contextlib
25
26
  from os import path
26
27
  from warnings import warn
27
28
 
@@ -50,7 +51,7 @@ from numpy import (
50
51
  )
51
52
  from numpy import min as npmin
52
53
  from numpy.fft import fft, ifft
53
- from numpy.linalg import norm
54
+ from scipy.linalg import norm
54
55
  from scipy.special import sph_harm, spherical_jn, spherical_yn
55
56
  from traits.api import (
56
57
  Any,
@@ -75,6 +76,8 @@ from traits.api import (
75
76
  on_trait_change,
76
77
  )
77
78
 
79
+ from .base import SamplesGenerator
80
+
78
81
  # acoular imports
79
82
  from .calib import Calib
80
83
  from .environments import Environment
@@ -82,7 +85,7 @@ from .h5files import H5FileBase, _get_h5file_class
82
85
  from .internal import digest, ldigest
83
86
  from .microphones import MicGeom
84
87
  from .signals import SignalGenerator
85
- from .tprocess import SamplesGenerator, TimeConvolve
88
+ from .tprocess import TimeConvolve
86
89
  from .trajectory import Trajectory
87
90
 
88
91
 
@@ -104,7 +107,7 @@ def _fill_mic_signal_block(out, signal, rm, ind, blocksize, numchannels, up, pre
104
107
  return out
105
108
 
106
109
 
107
- def spherical_hn1(n, z, derivativearccos=False):
110
+ def spherical_hn1(n, z):
108
111
  """Spherical Hankel Function of the First Kind."""
109
112
  return spherical_jn(n, z, derivative=False) + 1j * spherical_yn(n, z, derivative=False)
110
113
 
@@ -146,7 +149,7 @@ def get_radiation_angles(direction, mpos, sourceposition):
146
149
  return azi, ele
147
150
 
148
151
 
149
- def get_modes(lOrder, direction, mpos, sourceposition=None):
152
+ def get_modes(lOrder, direction, mpos, sourceposition=None): # noqa: N803
150
153
  """Returns Spherical Harmonic Radiation Pattern at the Microphones.
151
154
 
152
155
  Parameters
@@ -170,9 +173,9 @@ def get_modes(lOrder, direction, mpos, sourceposition=None):
170
173
  azi, ele = get_radiation_angles(direction, mpos, sourceposition) # angles between source and mics
171
174
  modes = zeros((azi.shape[0], (lOrder + 1) ** 2), dtype=complex128)
172
175
  i = 0
173
- for l in range(lOrder + 1):
174
- for m in range(-l, l + 1):
175
- modes[:, i] = sph_harm(m, l, azi, ele)
176
+ for lidx in range(lOrder + 1):
177
+ for m in range(-lidx, lidx + 1):
178
+ modes[:, i] = sph_harm(m, lidx, azi, ele)
176
179
  if m < 0:
177
180
  modes[:, i] = modes[:, i].conj() * 1j
178
181
  i += 1
@@ -180,12 +183,44 @@ def get_modes(lOrder, direction, mpos, sourceposition=None):
180
183
 
181
184
 
182
185
  class TimeSamples(SamplesGenerator):
183
- """Container for time data in `*.h5` format.
184
-
185
- This class loads measured data from h5 files and
186
- and provides information about this data.
187
- It also serves as an interface where the data can be accessed
188
- (e.g. for use in a block chain) via the :meth:`result` generator.
186
+ """Container for processing time data in `*.h5` or NumPy array format.
187
+
188
+ This class loads measured data from HDF5 files and provides information about this data.
189
+ It also serves as an interface where the data can be accessed (e.g. for use in a block chain) via the
190
+ :meth:`result` generator.
191
+
192
+ Examples
193
+ --------
194
+ Data can be loaded from a HDF5 file as follows:
195
+
196
+ >>> from acoular import TimeSamples
197
+ >>> name = <some_h5_file.h5> # doctest: +SKIP
198
+ >>> ts = TimeSamples(name=name) # doctest: +SKIP
199
+ >>> print(f'number of channels: {ts.numchannels}') # doctest: +SKIP
200
+ number of channels: 56 # doctest: +SKIP
201
+
202
+ Alternatively, the time data can be specified directly as a numpy array.
203
+ In this case, the :attr:`data` and :attr:`sample_freq` attributes must be set manually.
204
+
205
+ >>> import numpy as np
206
+ >>> data = np.random.rand(1000, 4)
207
+ >>> ts = TimeSamples(data=data, sample_freq=51200)
208
+
209
+ Chunks of the time data can be accessed iteratively via the :meth:`result` generator.
210
+ The last block will be shorter than the block size if the number of samples is not a multiple of the block size.
211
+
212
+ >>> blocksize = 512
213
+ >>> generator = ts.result(num=blocksize)
214
+ >>> for block in generator:
215
+ ... print(block.shape)
216
+ (512, 4)
217
+ (488, 4)
218
+
219
+ See Also
220
+ --------
221
+ acoular.sources.MaskedTimeSamples :
222
+ Extends the functionality of class :class:`TimeSamples` by enabling the definition of start and stop samples
223
+ as well as the specification of invalid channels.
189
224
  """
190
225
 
191
226
  #: Full name of the .h5 file with data.
@@ -219,7 +254,9 @@ class TimeSamples(SamplesGenerator):
219
254
  _datachecksum = Property()
220
255
 
221
256
  # internal identifier
222
- digest = Property(depends_on=['basename', 'calib.digest', '_datachecksum'])
257
+ digest = Property(
258
+ depends_on=['basename', 'calib.digest', '_datachecksum', 'sample_freq', 'numchannels', 'numsamples']
259
+ )
223
260
 
224
261
  def _get__datachecksum(self):
225
262
  return self.data[0, :].sum()
@@ -233,31 +270,31 @@ class TimeSamples(SamplesGenerator):
233
270
  return path.splitext(path.basename(self.name))[0]
234
271
 
235
272
  @on_trait_change('basename')
236
- def load_data(self):
273
+ def _load_data(self):
237
274
  """Open the .h5 file and set attributes."""
238
275
  if not path.isfile(self.name):
239
- # no file there
240
- self.numsamples = 0
241
- self.numchannels = 0
242
276
  self.sample_freq = 0
243
277
  raise OSError('No such file: %s' % self.name)
244
278
  if self.h5f is not None:
245
- try:
279
+ with contextlib.suppress(OSError):
246
280
  self.h5f.close()
247
- except OSError:
248
- pass
249
281
  file = _get_h5file_class()
250
282
  self.h5f = file(self.name)
251
- self.load_timedata()
252
- self.load_metadata()
283
+ self._load_timedata()
284
+ self._load_metadata()
253
285
 
254
- def load_timedata(self):
286
+ @on_trait_change('data')
287
+ def _load_shapes(self):
288
+ """Set numchannels and numsamples from data."""
289
+ if self.data is not None:
290
+ self.numsamples, self.numchannels = self.data.shape
291
+
292
+ def _load_timedata(self):
255
293
  """Loads timedata from .h5 file. Only for internal use."""
256
294
  self.data = self.h5f.get_data_by_reference('time_data')
257
295
  self.sample_freq = self.h5f.get_node_attribute(self.data, 'sample_freq')
258
- (self.numsamples, self.numchannels) = self.data.shape
259
296
 
260
- def load_metadata(self):
297
+ def _load_metadata(self):
261
298
  """Loads metadata from .h5 file. Only for internal use."""
262
299
  self.metadata = {}
263
300
  if '/metadata' in self.h5f:
@@ -266,15 +303,20 @@ class TimeSamples(SamplesGenerator):
266
303
  def result(self, num=128):
267
304
  """Python generator that yields the output block-wise.
268
305
 
306
+ Reads the time data either from a HDF5 file or from a numpy array given
307
+ by :attr:`data` and iteratively returns a block of size `num` samples.
308
+ Calibrated data is returned if a calibration object is given by :attr:`calib`.
309
+
269
310
  Parameters
270
311
  ----------
271
312
  num : integer, defaults to 128
272
313
  This parameter defines the size of the blocks to be yielded
273
- (i.e. the number of samples per block) .
314
+ (i.e. the number of samples per block).
274
315
 
275
- Returns
276
- -------
277
- Samples in blocks of shape (num, numchannels).
316
+ Yields
317
+ ------
318
+ numpy.ndarray
319
+ Samples in blocks of shape (num, numchannels).
278
320
  The last block may be shorter than num.
279
321
 
280
322
  """
@@ -298,14 +340,40 @@ class TimeSamples(SamplesGenerator):
298
340
 
299
341
 
300
342
  class MaskedTimeSamples(TimeSamples):
301
- """Container for time data in `*.h5` format.
302
-
303
- This class loads measured data from h5 files
304
- and provides information about this data.
305
- It supports storing information about (in)valid samples and (in)valid channels
306
- It also serves as an interface where the data can be accessed
307
- (e.g. for use in a block chain) via the :meth:`result` generator.
308
-
343
+ """Container for processing time data in `*.h5` or NumPy array format.
344
+
345
+ This class loads measured data from HDF5 files and provides information about this data.
346
+ It supports storing information about (in)valid samples and (in)valid channels and allows
347
+ to specify a start and stop index for the valid samples.
348
+ It also serves as an interface where the data can be accessed (e.g. for use in a block chain) via the
349
+ :meth:`result` generator.
350
+
351
+ Examples
352
+ --------
353
+ Data can be loaded from a HDF5 file and invalid channels can be specified as follows:
354
+
355
+ >>> from acoular import MaskedTimeSamples
356
+ >>> name = <some_h5_file.h5> # doctest: +SKIP
357
+ >>> ts = MaskedTimeSamples(name=name, invalid_channels=[0, 1]) # doctest: +SKIP
358
+ >>> print(f'number of valid channels: {ts.numchannels}') # doctest: +SKIP
359
+ number of valid channels: 54 # doctest: +SKIP
360
+
361
+ Alternatively, the time data can be specified directly as a numpy array.
362
+ In this case, the :attr:`data` and :attr:`sample_freq` attributes must be set manually.
363
+
364
+ >>> from acoular import MaskedTimeSamples
365
+ >>> import numpy as np
366
+ >>> data = np.random.rand(1000, 4)
367
+ >>> ts = MaskedTimeSamples(data=data, sample_freq=51200)
368
+
369
+ Chunks of the time data can be accessed iteratively via the :meth:`result` generator:
370
+
371
+ >>> blocksize = 512
372
+ >>> generator = ts.result(num=blocksize)
373
+ >>> for block in generator:
374
+ ... print(block.shape)
375
+ (512, 4)
376
+ (488, 4)
309
377
  """
310
378
 
311
379
  #: Index of the first sample to be considered valid.
@@ -362,26 +430,28 @@ class MaskedTimeSamples(TimeSamples):
362
430
  return sli[1] - sli[0]
363
431
 
364
432
  @on_trait_change('basename')
365
- def load_data(self):
433
+ def _load_data(self):
366
434
  # """ open the .h5 file and set attributes
367
435
  # """
368
436
  if not path.isfile(self.name):
369
437
  # no file there
370
- self.numsamples_total = 0
371
- self.numchannels_total = 0
372
438
  self.sample_freq = 0
373
439
  raise OSError('No such file: %s' % self.name)
374
440
  if self.h5f is not None:
375
- try:
441
+ with contextlib.suppress(OSError):
376
442
  self.h5f.close()
377
- except OSError:
378
- pass
379
443
  file = _get_h5file_class()
380
444
  self.h5f = file(self.name)
381
- self.load_timedata()
382
- self.load_metadata()
445
+ self._load_timedata()
446
+ self._load_metadata()
447
+
448
+ @on_trait_change('data')
449
+ def _load_shapes(self):
450
+ """Set numchannels and numsamples from data."""
451
+ if self.data is not None:
452
+ self.numsamples_total, self.numchannels_total = self.data.shape
383
453
 
384
- def load_timedata(self):
454
+ def _load_timedata(self):
385
455
  """Loads timedata from .h5 file. Only for internal use."""
386
456
  self.data = self.h5f.get_data_by_reference('time_data')
387
457
  self.sample_freq = self.h5f.get_node_attribute(self.data, 'sample_freq')
@@ -390,15 +460,20 @@ class MaskedTimeSamples(TimeSamples):
390
460
  def result(self, num=128):
391
461
  """Python generator that yields the output block-wise.
392
462
 
463
+ Reads the time data either from a HDF5 file or from a numpy array given
464
+ by :attr:`data` and iteratively returns a block of size `num` samples.
465
+ Calibrated data is returned if a calibration object is given by :attr:`calib`.
466
+
393
467
  Parameters
394
468
  ----------
395
469
  num : integer, defaults to 128
396
470
  This parameter defines the size of the blocks to be yielded
397
471
  (i.e. the number of samples per block).
398
472
 
399
- Returns
400
- -------
401
- Samples in blocks of shape (num, numchannels).
473
+ Yields
474
+ ------
475
+ numpy.ndarray
476
+ Samples in blocks of shape (num, numchannels).
402
477
  The last block may be shorter than num.
403
478
 
404
479
  """
@@ -463,7 +538,11 @@ class PointSource(SamplesGenerator):
463
538
  return self.mics
464
539
 
465
540
  def _set_mpos(self, mpos):
466
- warn("Deprecated use of 'mpos' trait. ", Warning, stacklevel=2)
541
+ msg = (
542
+ "Deprecated use of 'mpos' trait. Use 'mics' trait instead."
543
+ "The 'mpos' trait will be removed in version 25.01."
544
+ )
545
+ warn(msg, DeprecationWarning, stacklevel=2)
467
546
  self.mics = mpos
468
547
 
469
548
  # The speed of sound.
@@ -475,7 +554,8 @@ class PointSource(SamplesGenerator):
475
554
  return self.env.c
476
555
 
477
556
  def _set_c(self, c):
478
- warn("Deprecated use of 'c' trait. ", Warning, stacklevel=2)
557
+ msg = "Deprecated use of 'c' trait. Use 'env' trait instead." "The 'c' trait will be removed in version 25.01."
558
+ warn(msg, DeprecationWarning, stacklevel=2)
479
559
  self.env.c = c
480
560
 
481
561
  # --- End of backwards compatibility traits --------------------------------------
@@ -588,7 +668,7 @@ class SphericalHarmonicSource(PointSource):
588
668
  """
589
669
 
590
670
  #: Order of spherical harmonic source
591
- lOrder = Int(0, desc='Order of spherical harmonic')
671
+ lOrder = Int(0, desc='Order of spherical harmonic') # noqa: N815
592
672
 
593
673
  alpha = CArray(desc='coefficients of the (lOrder,) spherical harmonic mode')
594
674
 
@@ -1302,7 +1382,11 @@ class UncorrelatedNoiseSource(SamplesGenerator):
1302
1382
  return self.mics
1303
1383
 
1304
1384
  def _set_mpos(self, mpos):
1305
- warn("Deprecated use of 'mpos' trait. ", Warning, stacklevel=2)
1385
+ msg = (
1386
+ "Deprecated use of 'mpos' trait. Use 'mics' trait instead."
1387
+ "The 'mpos' trait will be removed in version 25.01."
1388
+ )
1389
+ warn(msg, DeprecationWarning, stacklevel=2)
1306
1390
  self.mics = mpos
1307
1391
 
1308
1392
  # --- End of backwards compatibility traits --------------------------------------
@@ -1391,7 +1475,7 @@ class UncorrelatedNoiseSource(SamplesGenerator):
1391
1475
  class SourceMixer(SamplesGenerator):
1392
1476
  """Mixes the signals from several sources."""
1393
1477
 
1394
- #: List of :class:`~acoular.tprocess.SamplesGenerator` objects
1478
+ #: List of :class:`~acoular.base.SamplesGenerator` objects
1395
1479
  #: to be mixed.
1396
1480
  sources = List(Instance(SamplesGenerator, ()))
1397
1481
 
acoular/spectra.py CHANGED
@@ -7,7 +7,6 @@
7
7
  :toctree: generated/
8
8
 
9
9
  BaseSpectra
10
- FFTSpectra
11
10
  PowerSpectra
12
11
  synthetic
13
12
  PowerSpectraImport
@@ -33,7 +32,6 @@ from numpy import (
33
32
  ones,
34
33
  real,
35
34
  searchsorted,
36
- sqrt,
37
35
  sum,
38
36
  zeros,
39
37
  zeros_like,
@@ -54,13 +52,13 @@ from traits.api import (
54
52
  property_depends_on,
55
53
  )
56
54
 
55
+ from .base import SamplesGenerator
57
56
  from .calib import Calib
58
57
  from .configuration import config
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
64
62
 
65
63
 
66
64
  class BaseSpectra(HasPrivateTraits):
@@ -105,8 +103,8 @@ class BaseSpectra(HasPrivateTraits):
105
103
  desc='number of samples per FFT block',
106
104
  )
107
105
 
108
- #: The floating-number-precision of entries of csm, eigenvalues and
109
- #: eigenvectors, corresponding to numpy dtypes. Default is 64 bit.
106
+ #: The floating-number-precision of the resulting spectra, corresponding to numpy dtypes.
107
+ #: Default is 'complex128'.
110
108
  precision = Trait('complex128', 'complex64', desc='precision of the fft')
111
109
 
112
110
  # internal identifier
@@ -130,7 +128,7 @@ class BaseSpectra(HasPrivateTraits):
130
128
  return None
131
129
 
132
130
  # generator that yields the time data blocks for every channel (with optional overlap)
133
- def get_source_data(self):
131
+ def _get_source_data(self):
134
132
  bs = self.block_size
135
133
  temp = empty((2 * bs, self.numchannels))
136
134
  pos = bs
@@ -146,48 +144,12 @@ class BaseSpectra(HasPrivateTraits):
146
144
  pos -= bs
147
145
 
148
146
 
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
147
  class PowerSpectra(BaseSpectra):
186
148
  """Provides the cross spectral matrix of multichannel time data
187
149
  and its eigen-decomposition.
188
150
 
189
151
  This class includes the efficient calculation of the full cross spectral
190
- matrix using the Welch method with windows and overlap. It also contains
152
+ matrix using the Welch method with windows and overlap (:cite:`Welch1967`). It also contains
191
153
  the CSM's eigenvalues and eigenvectors and additional properties.
192
154
 
193
155
  The result is computed only when needed, that is when the :attr:`csm`,
@@ -206,7 +168,7 @@ class PowerSpectra(BaseSpectra):
206
168
  #: Data source; :class:`~acoular.sources.SamplesGenerator` or derived object.
207
169
  source = Property(_source, desc='time data object')
208
170
 
209
- #: The :class:`~acoular.tprocess.SamplesGenerator` object that provides the data.
171
+ #: The :class:`~acoular.base.SamplesGenerator` object that provides the data.
210
172
  time_data = Property(
211
173
  _source,
212
174
  desc='deprecated attribute holding the time data object. Use PowerSpectra.source instead!',
@@ -215,9 +177,11 @@ class PowerSpectra(BaseSpectra):
215
177
  #: The :class:`~acoular.calib.Calib` object that provides the calibration data,
216
178
  #: defaults to no calibration, i.e. the raw time data is used.
217
179
  #:
218
- #: **deprecated**: use :attr:`~acoular.sources.TimeSamples.calib` property of
180
+ #: **deprecated, will be removed in version 25.01**: use :attr:`~acoular.sources.TimeSamples.calib` property of
219
181
  #: :class:`~acoular.sources.TimeSamples` objects
220
- calib = Instance(Calib)
182
+ calib = Property(desc='calibration object (deprecated, will be removed in version 25.01)')
183
+
184
+ _calib = Instance(Calib)
221
185
 
222
186
  # Shadow trait, should not be set directly, for internal use.
223
187
  _ind_low = Int(1, desc='index of lowest frequency line')
@@ -292,6 +256,18 @@ class PowerSpectra(BaseSpectra):
292
256
  # hdf5 cache file
293
257
  h5f = Instance(H5CacheFileBase, transient=True)
294
258
 
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
+
295
271
  @property_depends_on('_source.numsamples, block_size, overlap')
296
272
  def _get_num_blocks(self):
297
273
  return self.overlap_ * self._source.numsamples / self.block_size - self.overlap_ + 1
@@ -341,6 +317,11 @@ class PowerSpectra(BaseSpectra):
341
317
  self._ind_low = ind_low
342
318
 
343
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)
344
325
  self._source = time_data
345
326
 
346
327
  def _set_source(self, source):
@@ -383,7 +364,7 @@ class PowerSpectra(BaseSpectra):
383
364
  wind = wind[newaxis, :].swapaxes(0, 1)
384
365
  numfreq = int(self.block_size / 2 + 1)
385
366
  csm_shape = (numfreq, t.numchannels, t.numchannels)
386
- csmUpper = zeros(csm_shape, dtype=self.precision)
367
+ csm_upper = zeros(csm_shape, dtype=self.precision)
387
368
  # print "num blocks", self.num_blocks
388
369
  # for backward compatibility
389
370
  if self.calib and self.calib.num_mics > 0:
@@ -392,13 +373,13 @@ class PowerSpectra(BaseSpectra):
392
373
  else:
393
374
  raise ValueError('Calibration data not compatible: %i, %i' % (self.calib.num_mics, t.numchannels))
394
375
  # get time data blockwise
395
- for data in self.get_source_data():
376
+ for data in self._get_source_data():
396
377
  ft = fft.rfft(data * wind, None, 0).astype(self.precision)
397
- calcCSM(csmUpper, ft) # only upper triangular part of matrix is calculated (for speed reasons)
378
+ calcCSM(csm_upper, ft) # only upper triangular part of matrix is calculated (for speed reasons)
398
379
  # create the full csm matrix via transposing and complex conj.
399
- csmLower = csmUpper.conj().transpose(0, 2, 1)
400
- [fill_diagonal(csmLower[cntFreq, :, :], 0) for cntFreq in range(csmLower.shape[0])]
401
- csm = csmLower + csmUpper
380
+ csm_lower = csm_upper.conj().transpose(0, 2, 1)
381
+ [fill_diagonal(csm_lower[cntFreq, :, :], 0) for cntFreq in range(csm_lower.shape[0])]
382
+ csm = csm_lower + csm_upper
402
383
  # onesided spectrum: multiplication by 2.0=sqrt(2)^2
403
384
  return csm * (2.0 / self.block_size / weight / self.num_blocks)
404
385
 
@@ -594,7 +575,7 @@ def synthetic(data, freqs, f, num=3):
594
575
  each grid point (the sum of all values that are contained in the band).
595
576
  Note that the frequency resolution and therefore the bandwidth
596
577
  represented by a single frequency line depends on
597
- the :attr:`sampling frequency<acoular.tprocess.SamplesGenerator.sample_freq>`
578
+ the :attr:`sampling frequency<acoular.base.SamplesGenerator.sample_freq>`
598
579
  and used :attr:`FFT block size<acoular.spectra.PowerSpectra.block_size>`.
599
580
 
600
581
  """