acoular 24.7__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/process.py ADDED
@@ -0,0 +1,464 @@
1
+ # ------------------------------------------------------------------------------
2
+ # Copyright (c) Acoular Development Team.
3
+ # ------------------------------------------------------------------------------
4
+ """Implements general purpose blockwise processing methods independent of the domain (time or frequency).
5
+
6
+ .. autosummary::
7
+ :toctree: generated/
8
+
9
+ Average
10
+ Cache
11
+ SampleSplitter
12
+ TimeAverage
13
+ TimeCache
14
+ """
15
+
16
+ import threading
17
+ from collections import deque
18
+ from inspect import currentframe
19
+ from warnings import warn
20
+
21
+ from traits.api import Bool, Dict, Instance, Int, Property, Trait, cached_property, on_trait_change
22
+
23
+ from .base import Generator, InOut
24
+ from .configuration import config
25
+ from .h5cache import H5cache
26
+ from .h5files import H5CacheFileBase
27
+ from .internal import digest
28
+
29
+
30
+ class LockedGenerator:
31
+ """Creates a Thread Safe Iterator.
32
+ Takes an iterator/generator and makes it thread-safe by
33
+ serializing call to the `next` method of given iterator/generator.
34
+ """
35
+
36
+ def __init__(self, it):
37
+ self.it = it
38
+ self.lock = threading.Lock()
39
+
40
+ def __next__(self):
41
+ with self.lock:
42
+ return self.it.__next__()
43
+
44
+
45
+ class Average(InOut):
46
+ """Calculates the average across consecutive time samples or frequency snapshots.
47
+
48
+ The average operation is performed differently depending on the source type.
49
+ If the source is a time domain source (e.g. derived from :class:`~acoular.base.SamplesGenerator`),
50
+ the average is calculated over a certain number of time samples given by :attr:`naverage`.
51
+ If the source is a frequency domain source (e.g. derived from :class:`~acoular.base.SpectraGenerator`),
52
+ the average is calculated over a certain number of snapshots given by :attr:`naverage`.
53
+
54
+ Examples
55
+ --------
56
+ For estimate the RMS of a white noise (time-domain) signal, the average of the squared signal can be calculated:
57
+
58
+ >>> import acoular as ac
59
+ >>> import numpy as np
60
+ >>>
61
+ >>> signal = ac.WNoiseGenerator(sample_freq=51200, numsamples=51200, rms=2.0).signal()
62
+ >>> ts = ac.TimeSamples(data=signal[:, np.newaxis], sample_freq=51200)
63
+ >>> tp = ac.TimePower(source=ts)
64
+ >>> avg = ac.Average(source=tp, naverage=512)
65
+ >>> mean_squared_value = next(avg.result(num=1))
66
+ >>> rms = np.sqrt(mean_squared_value)[0, 0]
67
+ >>> print(rms)
68
+ 1.9985200025816718
69
+
70
+ Here, each evaluation of the generator created by the :meth:`result` method of the :class:`Average` object
71
+ via the :meth:`next` function returns :code:`num=1` average across a snapshot of 512 samples.
72
+
73
+ If the source is a frequency domain source, the average is calculated over a certain number of
74
+ snapshots, defined by :attr:`naverage`.
75
+
76
+ >>> fft = ac.RFFT(source=ts, block_size=64)
77
+ >>> ps = ac.AutoPowerSpectra(source=fft)
78
+ >>> avg = ac.Average(source=ps, naverage=16)
79
+ >>> mean_power = next(avg.result(num=1))
80
+ >>> print(np.sqrt(mean_power.sum()))
81
+ 2.0024960894399295
82
+
83
+ Here, the generator created by the :meth:`result` method of the :class:`Average` object
84
+ returns the average across 16 snapshots in the frequency domain.
85
+
86
+ """
87
+
88
+ #: Number of samples (time domain source) or snapshots (frequency domain source)
89
+ #: to average over, defaults to 64.
90
+ naverage = Int(64, desc='number of samples to average over')
91
+
92
+ #: Sampling frequency of the output signal, is set automatically.
93
+ sample_freq = Property(depends_on='source.sample_freq, naverage')
94
+
95
+ #: Number of samples (time domain) or snapshots (frequency domain) of the output signal.
96
+ #: Is set automatically.
97
+ numsamples = Property(depends_on='source.numsamples, naverage')
98
+
99
+ # internal identifier
100
+ digest = Property(depends_on=['source.digest', '__class__', 'naverage'])
101
+
102
+ @cached_property
103
+ def _get_digest(self):
104
+ return digest(self)
105
+
106
+ @cached_property
107
+ def _get_sample_freq(self):
108
+ if self.source:
109
+ return 1.0 * self.source.sample_freq / self.naverage
110
+ return None
111
+
112
+ @cached_property
113
+ def _get_numsamples(self):
114
+ if self.source:
115
+ return self.source.numsamples / self.naverage
116
+ return None
117
+
118
+ def result(self, num):
119
+ """Python generator that yields the output block-wise.
120
+
121
+ Parameters
122
+ ----------
123
+ num : integer
124
+ This parameter defines the size of the blocks to be yielded
125
+ (i.e. the number of samples per block).
126
+
127
+ Returns
128
+ -------
129
+ Average of the output of source.
130
+ Yields samples in blocks of shape (num, numchannels).
131
+ The last block may be shorter than num.
132
+
133
+ """
134
+ nav = self.naverage
135
+ for temp in self.source.result(num * nav):
136
+ ns, nc = temp.shape
137
+ nso = int(ns / nav)
138
+ if nso > 0:
139
+ yield temp[: nso * nav].reshape((nso, -1, nc)).mean(axis=1)
140
+
141
+
142
+ class Cache(InOut):
143
+ """Caches source output in cache file.
144
+
145
+ This class is used to cache the output of a :class:`acoular.base.Generator` derived source
146
+ object in a cache file to circumvent time-consuming re-calculation.
147
+ The cache file is created in the Acoular cache directory.
148
+
149
+ Examples
150
+ --------
151
+ >>> import acoular as ac
152
+ >>> import numpy as np
153
+ >>>
154
+ >>> ac.config.h5library = 'tables'
155
+ >>> data = np.random.rand(1024, 1)
156
+ >>> ts = ac.TimeSamples(data=data, sample_freq=51200)
157
+ >>> fft = ac.RFFT(source=ts, block_size=1024)
158
+ >>> cache = ac.Cache(source=fft) # cache the output of the FFT in cache file
159
+ >>> for block in cache.result(num=1): # read the cached data block-wise
160
+ ... print(block.shape)
161
+ [('_cache.h5', 1)]
162
+ (1, 513)
163
+
164
+ The caching behaviour can be controlled by the :class:`~acoular.configuration.Config` instance
165
+ via the :attr:`~acoular.configuration.Config.global_caching` attribute.
166
+ To turn off caching, set :attr:`~acoular.configuration.Config.global_caching` to 'none' before
167
+ running the code. The cache file directory can be obtained (and set) via the
168
+ :attr:`~acoular.configuration.Config.cache_dir`
169
+
170
+ >>> ac.config.global_caching = 'none'
171
+
172
+ """
173
+
174
+ # basename for cache
175
+ basename = Property(depends_on='digest')
176
+
177
+ # hdf5 cache file
178
+ h5f = Instance(H5CacheFileBase, transient=True)
179
+
180
+ # internal identifier
181
+ digest = Property(depends_on=['source.digest', '__class__'])
182
+
183
+ @cached_property
184
+ def _get_digest(self):
185
+ return digest(self)
186
+
187
+ @cached_property
188
+ def _get_basename(self):
189
+ obj = self.source # start with source
190
+ basename = 'void' # if no file source is found
191
+ while obj:
192
+ if 'basename' in obj.all_trait_names(): # at original source?
193
+ basename = obj.basename # get the name
194
+ break
195
+ try:
196
+ obj = obj.source # traverse down until original data source
197
+ except AttributeError:
198
+ obj = None
199
+ return basename
200
+
201
+ def _pass_data(self, num):
202
+ yield from self.source.result(num)
203
+
204
+ def _write_data_to_cache(self, num):
205
+ nodename = 'tc_' + self.digest
206
+ for i, data in enumerate(self.source.result(num)):
207
+ if i == 0:
208
+ self.h5f.create_extendable_array(nodename, (0, data.shape[1]), data.dtype.name)
209
+ ac = self.h5f.get_data_by_reference(nodename)
210
+ self.h5f.set_node_attribute(ac, 'sample_freq', self.sample_freq)
211
+ self.h5f.set_node_attribute(ac, 'complete', False)
212
+ self.h5f.append_data(ac, data)
213
+ self.h5f.flush()
214
+ yield data
215
+ self.h5f.set_node_attribute(ac, 'complete', True)
216
+
217
+ def _get_data_from_cache(self, num):
218
+ nodename = 'tc_' + self.digest
219
+ ac = self.h5f.get_data_by_reference(nodename)
220
+ i = 0
221
+ while i < ac.shape[0]:
222
+ yield ac[i : i + num]
223
+ i += num
224
+
225
+ def _get_data_from_incomplete_cache(self, num):
226
+ nodename = 'tc_' + self.digest
227
+ ac = self.h5f.get_data_by_reference(nodename)
228
+ i = 0
229
+ nblocks = 0
230
+ while i + num <= ac.shape[0]:
231
+ yield ac[i : i + num]
232
+ nblocks += 1
233
+ i += num
234
+ self.h5f.remove_data(nodename)
235
+ for j, data in enumerate(self.source.result(num)):
236
+ if j == 0:
237
+ self.h5f.create_extendable_array(nodename, (0, data.shape[1]), data.dtype.name)
238
+ ac = self.h5f.get_data_by_reference(nodename)
239
+ self.h5f.set_node_attribute(ac, 'sample_freq', self.sample_freq)
240
+ self.h5f.set_node_attribute(ac, 'complete', False)
241
+ self.h5f.append_data(ac, data)
242
+ if j >= nblocks:
243
+ self.h5f.flush()
244
+ yield data
245
+ self.h5f.set_node_attribute(ac, 'complete', True)
246
+
247
+ # result generator: delivers input, possibly from cache
248
+ def result(self, num):
249
+ """Python generator that yields the output from cache block-wise.
250
+
251
+ Parameters
252
+ ----------
253
+ num : integer
254
+ This parameter defines the size of the blocks to be yielded
255
+ (i.e. the number of samples per block).
256
+
257
+ Returns
258
+ -------
259
+ Samples in blocks of shape (num, numchannels).
260
+ The last block may be shorter than num.
261
+ Echos the source output, but reads it from cache
262
+ when available and prevents unnecassary recalculation.
263
+
264
+ """
265
+ if config.global_caching == 'none':
266
+ generator = self._pass_data
267
+ else:
268
+ nodename = 'tc_' + self.digest
269
+ H5cache.get_cache_file(self, self.basename)
270
+ if not self.h5f:
271
+ generator = self._pass_data
272
+ elif self.h5f.is_cached(nodename):
273
+ generator = self._get_data_from_cache
274
+ if config.global_caching == 'overwrite':
275
+ self.h5f.remove_data(nodename)
276
+ generator = self._write_data_to_cache
277
+ elif not self.h5f.get_data_by_reference(nodename).attrs.__contains__('complete'):
278
+ if config.global_caching == 'readonly':
279
+ generator = self._pass_data
280
+ else:
281
+ generator = self._get_data_from_incomplete_cache
282
+ elif not self.h5f.get_data_by_reference(nodename).attrs['complete']:
283
+ if config.global_caching == 'readonly':
284
+ warn(
285
+ "Cache file is incomplete for nodename %s. With config.global_caching='readonly', the cache file will not be used!"
286
+ % str(nodename),
287
+ Warning,
288
+ stacklevel=1,
289
+ )
290
+ generator = self._pass_data
291
+ else:
292
+ generator = self._get_data_from_incomplete_cache
293
+ elif not self.h5f.is_cached(nodename):
294
+ generator = self._write_data_to_cache
295
+ if config.global_caching == 'readonly':
296
+ generator = self._pass_data
297
+ yield from generator(num)
298
+
299
+
300
+ class SampleSplitter(InOut):
301
+ """Distributes data blocks from source to several following objects.
302
+ A separate block buffer is created for each registered object in
303
+ (:attr:`block_buffer`) .
304
+ """
305
+
306
+ #: dictionary with block buffers (dict values) of registered objects (dict
307
+ #: keys).
308
+ block_buffer = Dict(key_trait=Instance(Generator))
309
+
310
+ #: max elements/blocks in block buffers.
311
+ buffer_size = Int(100)
312
+
313
+ #: defines behaviour in case of block_buffer overflow. Can be set individually
314
+ #: for each registered object.
315
+ #:
316
+ #: * 'error': an IOError is thrown by the class
317
+ #: * 'warning': a warning is displayed. Possibly leads to lost blocks of data
318
+ #: * 'none': nothing happens. Possibly leads to lost blocks of data
319
+ buffer_overflow_treatment = Dict(
320
+ key_trait=Instance(Generator),
321
+ value_trait=Trait('error', 'warning', 'none'),
322
+ desc='defines buffer overflow behaviour.',
323
+ )
324
+
325
+ # shadow trait to monitor if source deliver samples or is empty
326
+ _source_generator_exist = Bool(False)
327
+
328
+ # shadow trait to monitor if buffer of objects with overflow treatment = 'error'
329
+ # or warning is overfilled. Error will be raised in all threads.
330
+ _buffer_overflow = Bool(False)
331
+
332
+ # Helper Trait holds source generator
333
+ _source_generator = Trait()
334
+
335
+ def _create_block_buffer(self, obj):
336
+ self.block_buffer[obj] = deque([], maxlen=self.buffer_size)
337
+
338
+ def _create_buffer_overflow_treatment(self, obj):
339
+ self.buffer_overflow_treatment[obj] = 'error'
340
+
341
+ def _clear_block_buffer(self, obj):
342
+ self.block_buffer[obj].clear()
343
+
344
+ def _remove_block_buffer(self, obj):
345
+ del self.block_buffer[obj]
346
+
347
+ def _remove_buffer_overflow_treatment(self, obj):
348
+ del self.buffer_overflow_treatment[obj]
349
+
350
+ def _assert_obj_registered(self, obj):
351
+ if obj not in self.block_buffer:
352
+ raise OSError('calling object %s is not registered.' % obj)
353
+
354
+ def _get_objs_to_inspect(self):
355
+ return [obj for obj in self.buffer_overflow_treatment if self.buffer_overflow_treatment[obj] != 'none']
356
+
357
+ def _inspect_buffer_levels(self, inspect_objs):
358
+ for obj in inspect_objs:
359
+ if len(self.block_buffer[obj]) == self.buffer_size:
360
+ if self.buffer_overflow_treatment[obj] == 'error':
361
+ self._buffer_overflow = True
362
+ elif self.buffer_overflow_treatment[obj] == 'warning':
363
+ warn('overfilled buffer for object: %s data will get lost' % obj, UserWarning, stacklevel=1)
364
+
365
+ def _create_source_generator(self, num):
366
+ for obj in self.block_buffer:
367
+ self._clear_block_buffer(obj)
368
+ self._buffer_overflow = False # reset overflow bool
369
+ self._source_generator = LockedGenerator(self.source.result(num))
370
+ self._source_generator_exist = True # indicates full generator
371
+
372
+ def _fill_block_buffers(self):
373
+ next_block = next(self._source_generator)
374
+ [self.block_buffer[obj].appendleft(next_block) for obj in self.block_buffer]
375
+
376
+ @on_trait_change('buffer_size')
377
+ def _change_buffer_size(self): #
378
+ for obj in self.block_buffer:
379
+ self._remove_block_buffer(obj)
380
+ self._create_block_buffer(obj)
381
+
382
+ def register_object(self, *objects_to_register):
383
+ """Function that can be used to register objects that receive blocks from this class."""
384
+ for obj in objects_to_register:
385
+ if obj not in self.block_buffer:
386
+ self._create_block_buffer(obj)
387
+ self._create_buffer_overflow_treatment(obj)
388
+
389
+ def remove_object(self, *objects_to_remove):
390
+ """Function that can be used to remove registered objects."""
391
+ for obj in objects_to_remove:
392
+ self._remove_block_buffer(obj)
393
+ self._remove_buffer_overflow_treatment(obj)
394
+
395
+ def result(self, num):
396
+ """Python generator that yields the output block-wise from block-buffer.
397
+
398
+ Parameters
399
+ ----------
400
+ num : integer
401
+ This parameter defines the size of the blocks to be yielded
402
+ (i.e. the number of samples per block).
403
+
404
+ Returns
405
+ -------
406
+ Samples in blocks of shape (num, numchannels).
407
+ Delivers a block of samples to the calling object.
408
+ The last block may be shorter than num.
409
+
410
+ """
411
+ calling_obj = currentframe().f_back.f_locals['self']
412
+ self._assert_obj_registered(calling_obj)
413
+ objs_to_inspect = self._get_objs_to_inspect()
414
+
415
+ if not self._source_generator_exist:
416
+ self._create_source_generator(num)
417
+
418
+ while not self._buffer_overflow:
419
+ if self.block_buffer[calling_obj]:
420
+ yield self.block_buffer[calling_obj].pop()
421
+ else:
422
+ self._inspect_buffer_levels(objs_to_inspect)
423
+ try:
424
+ self._fill_block_buffers()
425
+ except StopIteration:
426
+ self._source_generator_exist = False
427
+ return
428
+ else:
429
+ msg = 'Maximum size of block buffer is reached!'
430
+ raise OSError(msg)
431
+
432
+
433
+ class TimeAverage(Average):
434
+ """Calculates average of the signal (Alias for :class:`acoular.process.Average`).
435
+
436
+ .. deprecated:: 24.10
437
+ Using :class:`~acoular.process.TimeAverage` is deprecated and will be removed in Acoular
438
+ version 25.07. Use :class:`~acoular.process.Average` instead.
439
+ """
440
+
441
+ def __init__(self, *args, **kwargs):
442
+ super().__init__(*args, **kwargs)
443
+ warn(
444
+ 'Using TimeAverage is deprecated and will be removed in Acoular version 25.07. Use Average instead.',
445
+ DeprecationWarning,
446
+ stacklevel=2,
447
+ )
448
+
449
+
450
+ class TimeCache(Cache):
451
+ """Caches source signals in cache file (Alias for :class:`acoular.process.Cache`).
452
+
453
+ .. deprecated:: 24.10
454
+ Using :class:`~acoular.process.TimeCache` is deprecated and will be removed in Acoular
455
+ version 25.07. Use :class:`~acoular.process.Cache` instead.
456
+ """
457
+
458
+ def __init__(self, *args, **kwargs):
459
+ super().__init__(*args, **kwargs)
460
+ warn(
461
+ 'Using TimeCache is deprecated and will be removed in Acoular version 25.07. Use Cache instead.',
462
+ DeprecationWarning,
463
+ stacklevel=2,
464
+ )
acoular/sdinput.py CHANGED
@@ -9,11 +9,11 @@
9
9
  SoundDeviceSamplesGenerator
10
10
  """
11
11
 
12
- from traits.api import Any, Bool, Int, Long, Property, cached_property, observe
12
+ from traits.api import Any, Bool, Float, Int, Long, Property, Trait, cached_property, observe
13
13
 
14
+ from .base import SamplesGenerator
14
15
  from .configuration import config
15
16
  from .internal import digest
16
- from .tprocess import SamplesGenerator
17
17
 
18
18
  if config.have_sounddevice:
19
19
  import sounddevice as sd
@@ -49,6 +49,11 @@ class SoundDeviceSamplesGenerator(SamplesGenerator):
49
49
  #: Sampling frequency of the signal, changes with sinusdevices
50
50
  sample_freq = Property(desc='sampling frequency')
51
51
 
52
+ _sample_freq = Float(default_value=None)
53
+
54
+ #: Datatype (resolution) of the signal, used as `dtype` in a sd `Stream` object
55
+ precision = Trait('float32', 'float16', 'int32', 'int16', 'int8', 'uint8', desc='precision (resolution) of signal')
56
+
52
57
  #: Indicates that the sounddevice buffer has overflown
53
58
  overflow = Bool(False, desc='Indicates if sounddevice buffer overflow')
54
59
 
@@ -71,7 +76,12 @@ class SoundDeviceSamplesGenerator(SamplesGenerator):
71
76
  self.numchannels = min(self.numchannels, sd.query_devices(self.device)['max_input_channels'])
72
77
 
73
78
  def _get_sample_freq(self):
74
- return sd.query_devices(self.device)['default_samplerate']
79
+ if self._sample_freq is None:
80
+ self._sample_freq = sd.query_devices(self.device)['default_samplerate']
81
+ return self._sample_freq
82
+
83
+ def _set_sample_freq(self, f):
84
+ self._sample_freq = f
75
85
 
76
86
  def device_properties(self):
77
87
  """Returns
@@ -102,6 +112,7 @@ class SoundDeviceSamplesGenerator(SamplesGenerator):
102
112
  channels=self.numchannels,
103
113
  clip_off=True,
104
114
  samplerate=self.sample_freq,
115
+ dtype=self.precision,
105
116
  )
106
117
 
107
118
  with stream_obj as stream:
acoular/signals.py CHANGED
@@ -23,10 +23,9 @@ from numpy.random import RandomState
23
23
  from scipy.signal import resample, sosfilt, tf2sos
24
24
  from traits.api import Bool, CArray, CLong, Delegate, Float, HasPrivateTraits, Int, Property, Trait, cached_property
25
25
 
26
- from .internal import digest
27
-
28
26
  # acoular imports
29
- from .tprocess import SamplesGenerator
27
+ from .base import SamplesGenerator
28
+ from .internal import digest
30
29
 
31
30
 
32
31
  class SignalGenerator(HasPrivateTraits):
@@ -238,10 +237,11 @@ class SineGenerator(SignalGenerator):
238
237
 
239
238
  def _set_rms(self, rms):
240
239
  warn(
240
+ 'Using rms to set amplitude is deprecated and will be removed in version 25.01. '
241
241
  'Up to Acoular 20.02, rms is interpreted as sine amplitude. '
242
242
  'This has since been corrected (rms now is 1/sqrt(2) of amplitude). '
243
243
  "Use 'amplitude' trait to directly set the ampltiude.",
244
- Warning,
244
+ DeprecationWarning,
245
245
  stacklevel=2,
246
246
  )
247
247
  self._amp = rms * 2**0.5
@@ -279,7 +279,7 @@ class SineGenerator(SignalGenerator):
279
279
 
280
280
 
281
281
  class GenericSignalGenerator(SignalGenerator):
282
- """Generate signal from output of :class:`~acoular.tprocess.SamplesGenerator` object.
282
+ """Generate signal from output of :class:`~acoular.base.SamplesGenerator` object.
283
283
 
284
284
  This class can be used to inject arbitrary signals into Acoular processing
285
285
  chains. For example, it can be used to read signals from a HDF5 file or create any signal
@@ -295,7 +295,7 @@ class GenericSignalGenerator(SignalGenerator):
295
295
 
296
296
  """
297
297
 
298
- #: Data source; :class:`~acoular.tprocess.SamplesGenerator` or derived object.
298
+ #: Data source; :class:`~acoular.base.SamplesGenerator` or derived object.
299
299
  source = Trait(SamplesGenerator)
300
300
 
301
301
  #: Sampling frequency of output signal, as given by :attr:`source`.
acoular/sources.py CHANGED
@@ -51,7 +51,7 @@ from numpy import (
51
51
  )
52
52
  from numpy import min as npmin
53
53
  from numpy.fft import fft, ifft
54
- from numpy.linalg import norm
54
+ from scipy.linalg import norm
55
55
  from scipy.special import sph_harm, spherical_jn, spherical_yn
56
56
  from traits.api import (
57
57
  Any,
@@ -76,6 +76,8 @@ from traits.api import (
76
76
  on_trait_change,
77
77
  )
78
78
 
79
+ from .base import SamplesGenerator
80
+
79
81
  # acoular imports
80
82
  from .calib import Calib
81
83
  from .environments import Environment
@@ -83,7 +85,7 @@ from .h5files import H5FileBase, _get_h5file_class
83
85
  from .internal import digest, ldigest
84
86
  from .microphones import MicGeom
85
87
  from .signals import SignalGenerator
86
- from .tprocess import SamplesGenerator, TimeConvolve
88
+ from .tprocess import TimeConvolve
87
89
  from .trajectory import Trajectory
88
90
 
89
91
 
@@ -252,7 +254,9 @@ class TimeSamples(SamplesGenerator):
252
254
  _datachecksum = Property()
253
255
 
254
256
  # internal identifier
255
- digest = Property(depends_on=['basename', 'calib.digest', '_datachecksum'])
257
+ digest = Property(
258
+ depends_on=['basename', 'calib.digest', '_datachecksum', 'sample_freq', 'numchannels', 'numsamples']
259
+ )
256
260
 
257
261
  def _get__datachecksum(self):
258
262
  return self.data[0, :].sum()
@@ -534,7 +538,11 @@ class PointSource(SamplesGenerator):
534
538
  return self.mics
535
539
 
536
540
  def _set_mpos(self, mpos):
537
- 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)
538
546
  self.mics = mpos
539
547
 
540
548
  # The speed of sound.
@@ -546,7 +554,8 @@ class PointSource(SamplesGenerator):
546
554
  return self.env.c
547
555
 
548
556
  def _set_c(self, c):
549
- 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)
550
559
  self.env.c = c
551
560
 
552
561
  # --- End of backwards compatibility traits --------------------------------------
@@ -1373,7 +1382,11 @@ class UncorrelatedNoiseSource(SamplesGenerator):
1373
1382
  return self.mics
1374
1383
 
1375
1384
  def _set_mpos(self, mpos):
1376
- 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)
1377
1390
  self.mics = mpos
1378
1391
 
1379
1392
  # --- End of backwards compatibility traits --------------------------------------
@@ -1462,7 +1475,7 @@ class UncorrelatedNoiseSource(SamplesGenerator):
1462
1475
  class SourceMixer(SamplesGenerator):
1463
1476
  """Mixes the signals from several sources."""
1464
1477
 
1465
- #: List of :class:`~acoular.tprocess.SamplesGenerator` objects
1478
+ #: List of :class:`~acoular.base.SamplesGenerator` objects
1466
1479
  #: to be mixed.
1467
1480
  sources = List(Instance(SamplesGenerator, ()))
1468
1481