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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
acoular/signals.py CHANGED
@@ -1,7 +1,8 @@
1
1
  # ------------------------------------------------------------------------------
2
2
  # Copyright (c) Acoular Development Team.
3
3
  # ------------------------------------------------------------------------------
4
- """Implements signal generators for the simulation of acoustic sources.
4
+ """
5
+ Implements signal generators for the simulation of acoustic sources.
5
6
 
6
7
  .. autosummary::
7
8
  :toctree: generated/
@@ -14,7 +15,6 @@
14
15
  FiltWNoiseGenerator
15
16
  SineGenerator
16
17
  GenericSignalGenerator
17
-
18
18
  """
19
19
 
20
20
  # imports from other packages
@@ -45,45 +45,78 @@ from .internal import digest
45
45
 
46
46
  @deprecated_alias({'numsamples': 'num_samples'})
47
47
  class SignalGenerator(ABCHasStrictTraits):
48
- """Virtual base class for a simple one-channel signal generator.
48
+ """
49
+ ABC for a simple one-channel signal generator.
50
+
51
+ This ABC defines the common interface and attributes for all signal generator implementations.
52
+ It provides a template for generating one-channel signals with specified amplitude,
53
+ sampling frequency, and duration. Subclasses should implement the core functionality,
54
+ including signal generation and computation of the internal identifier.
55
+
56
+ See Also
57
+ --------
58
+ :func:`scipy.signal.resample` : Used for resampling signals in the :meth:`usignal` method.
49
59
 
50
- Defines the common interface for all SignalGenerator classes. This class
51
- may be used as a base for specialized SignalGenerator implementations. It
52
- should not be used directly as it contains no real functionality.
60
+ Notes
61
+ -----
62
+ This class should not be instantiated directly. Instead, use a subclass that
63
+ implements the required methods for signal generation.
53
64
  """
54
65
 
55
- #: Sampling frequency of the signal.
66
+ #: Sampling frequency of the signal in Hz. Default is ``1.0``.
56
67
  sample_freq = Float(1.0, desc='sampling frequency')
57
68
 
58
- #: Number of samples to generate.
69
+ #: The number of samples to generate for the signal.
59
70
  num_samples = CInt
60
71
 
61
- # internal identifier
72
+ #: A unique checksum identifier based on the object properties. (read-only)
62
73
  digest = Property(depends_on=['sample_freq', 'num_samples'])
63
74
 
64
75
  @abstractmethod
65
76
  def _get_digest(self):
66
- """Returns the internal identifier."""
77
+ """Return the internal identifier."""
67
78
 
68
79
  @abstractmethod
69
80
  def signal(self):
70
- """Deliver the signal."""
81
+ """
82
+ Generate and return the signal.
83
+
84
+ This method must be implemented by subclasses to provide the generated signal
85
+ as a 1D array of samples.
86
+ """
71
87
 
72
88
  def usignal(self, factor):
73
- """Delivers the signal resampled with a multiple of the sampling freq.
89
+ """
90
+ Resample the signal at a higher sampling frequency.
74
91
 
75
- Uses fourier transform method for resampling (from scipy.signal).
92
+ This method uses Fourier transform-based resampling to deliver the signal at a
93
+ sampling frequency that is a multiple of the original :attr:`sample_freq`.
94
+ The resampled signal has a length of ``factor * num_samples``.
76
95
 
77
96
  Parameters
78
97
  ----------
79
- factor : integer
80
- The factor defines how many times the new sampling frequency is
81
- larger than :attr:`sample_freq`.
98
+ factor : int
99
+ The resampling factor. Defines how many times larger the new sampling frequency is
100
+ compared to the original :attr:`sample_freq`.
82
101
 
83
102
  Returns
84
103
  -------
85
- array of floats
86
- The resulting signal of length `factor` * :attr:`num_samples`.
104
+ :class:`numpy.ndarray`
105
+ The resampled signal as a 1D array of floats.
106
+
107
+ Notes
108
+ -----
109
+ This method relies on the :func:`scipy.signal.resample` function for resampling.
110
+
111
+ Examples
112
+ --------
113
+ Resample a signal by a factor of 4:
114
+
115
+ >>> from acoular import SineGenerator # Class extending SignalGenerator
116
+ >>> sg = SineGenerator(sample_freq=100.0, num_samples=1000)
117
+ >>> resampled_signal = sg.usignal(4)
118
+ >>> len(resampled_signal)
119
+ 4000
87
120
  """
88
121
  return resample(self.signal(), factor * self.num_samples)
89
122
 
@@ -92,27 +125,33 @@ class PeriodicSignalGenerator(SignalGenerator):
92
125
  """
93
126
  Abstract base class for periodic signal generators.
94
127
 
95
- Defines the common interface for all :class:`SignalGenerator`-derived classes with periodic
96
- signals. This class may be used as a base for class handling periodic signals that can be
97
- characterized by their frequency, phase and amplitude. It should not be used directly as it
98
- contains no real functionality.
128
+ The :class:`PeriodicSignalGenerator` class defines the common interface
129
+ for all :class:`SignalGenerator`-derived classes with periodic signals.
130
+ This class may be used as a base for class handling periodic signals
131
+ that can be characterized by their frequency, phase and amplitude.
132
+
133
+ It should not be used directly as it contains no real functionality.
134
+
135
+ See Also
136
+ --------
137
+ SineGenerator : Generate a sine signal.
99
138
  """
100
139
 
101
- #: Frequency of the signal, float, defaults to 1000.0.
140
+ #: The frequency of the signal. Default is ``1000.0``.
102
141
  freq = Float(1000.0, desc='Frequency')
103
142
 
104
- #: Phase of the signal (in radians), float, defaults to 0.0.
143
+ #: The phase of the signal (in radians). Default is ``0.0``.
105
144
  phase = Float(0.0, desc='Phase')
106
145
 
107
- #: Amplitude of the signal. Defaults to 1.0.
146
+ #: The amplitude of the signal. Default is ``1.0``.
108
147
  amplitude = Float(1.0)
109
148
 
110
- # internal identifier
149
+ #: Internal identifier based on generator properties. (read-only)
111
150
  digest = Property(depends_on=['amplitude', 'num_samples', 'sample_freq', 'freq', 'phase'])
112
151
 
113
152
  @abstractmethod
114
153
  def _get_digest(self):
115
- """Returns the internal identifier."""
154
+ """Return the internal identifier."""
116
155
 
117
156
  @abstractmethod
118
157
  def signal(self):
@@ -120,35 +159,86 @@ class PeriodicSignalGenerator(SignalGenerator):
120
159
 
121
160
 
122
161
  class NoiseGenerator(SignalGenerator):
123
- """Abstract base class for noise signal generators.
162
+ """
163
+ Abstract base class for noise signal generators.
124
164
 
125
- Defines the common interface for all :class:`SignalGenerator` classes with noise signals. This
126
- class may be used as a base for class handling noise signals that can be characterized by their
127
- RMS amplitude. It should not be used directly as it contains no real functionality.
165
+ The :class:`NoiseGenerator` class defines the common interface for all :class:`SignalGenerator`
166
+ classes with noise signals. This class may be used as a base for class handling noise signals
167
+ that can be characterized by their RMS amplitude.
168
+
169
+ It should not be used directly as it contains no real functionality.
170
+
171
+ See Also
172
+ --------
173
+ :class:`acoular.signals.PNoiseGenerator` : For pink noise generation.
174
+ :class:`acoular.signals.WNoiseGenerator` : For pink white generation.
175
+ :class:`acoular.sources.UncorrelatedNoiseSource` : For per-channel noise generation.
128
176
  """
129
177
 
130
- #: RMS amplitude of the signal.
178
+ #: Root mean square (RMS) amplitude of the signal. For a point source,
179
+ #: this corresponds to the RMS amplitude at a distance of 1 meter. Default is ``1.0``.
131
180
  rms = Float(1.0, desc='rms amplitude')
132
181
 
133
- #: Seed for random number generator, defaults to 0.
182
+ #: Seed for random number generator. Default is ``0``.
134
183
  #: This parameter should be set differently for different instances
135
184
  #: to guarantee statistically independent (non-correlated) outputs.
136
185
  seed = Int(0, desc='random seed value')
137
186
 
138
- # internal identifier
187
+ #: Internal identifier based on generator properties. (read-only)
139
188
  digest = Property(depends_on=['rms', 'seed', 'sample_freq', 'num_samples'])
140
189
 
141
190
  @abstractmethod
142
191
  def _get_digest(self):
143
- """Returns the internal identifier."""
192
+ """Return the internal identifier."""
144
193
 
145
194
  @abstractmethod
146
195
  def signal(self):
147
- """Deliver the signal."""
196
+ """Generate and deliver the periodic signal."""
148
197
 
149
198
 
150
199
  class WNoiseGenerator(NoiseGenerator):
151
- """White noise signal generator."""
200
+ """
201
+ White noise signal generator.
202
+
203
+ This class generates white noise signals with a specified
204
+ :attr:`root mean square (RMS)<SignalGenerator.rms>` amplitude,
205
+ :attr:`number of samples<SignalGenerator.num_samples>`, and
206
+ :attr:`sampling frequency<SignalGenerator.sample_freq>`. The white noise is generated using a
207
+ :obj:`random number generator<numpy.random.RandomState.standard_normal>` initialized with a
208
+ :attr:`user-defined seed<seed>` for reproducibility.
209
+
210
+ See Also
211
+ --------
212
+ :obj:`numpy.random.RandomState.standard_normal` :
213
+ Used here to generate normally distributed noise.
214
+ :class:`acoular.signals.PNoiseGenerator` : For pink noise generation.
215
+ :class:`acoular.sources.UncorrelatedNoiseSource` : For per-channel noise generation.
216
+
217
+ Examples
218
+ --------
219
+ Generate white noise with an RMS amplitude of 1.0 and 0.5:
220
+
221
+ >>> from acoular import WNoiseGenerator
222
+ >>> from numpy import mean
223
+ >>>
224
+ >>> # White noise with RMS of 1.0
225
+ >>> gen1 = WNoiseGenerator(rms=1.0, num_samples=1000, seed=42)
226
+ >>> signal1 = gen1.signal()
227
+ >>>
228
+ >>> # White noise with RMS of 0.5
229
+ >>> gen2 = WNoiseGenerator(rms=0.5, num_samples=1000, seed=24)
230
+ >>> signal2 = gen2.signal()
231
+ >>>
232
+ >>> mean(signal1) > mean(signal2)
233
+ np.True_
234
+
235
+ Ensure different outputs with different seeds:
236
+
237
+ >>> gen1 = WNoiseGenerator(num_samples=3, seed=42)
238
+ >>> gen2 = WNoiseGenerator(num_samples=3, seed=73)
239
+ >>> gen1.signal() == gen2.signal()
240
+ array([False, False, False])
241
+ """
152
242
 
153
243
  # internal identifier
154
244
  digest = Property(depends_on=['rms', 'seed', 'sample_freq', 'num_samples'])
@@ -158,35 +248,52 @@ class WNoiseGenerator(NoiseGenerator):
158
248
  return digest(self)
159
249
 
160
250
  def signal(self):
161
- """Deliver the signal.
251
+ """
252
+ Generate and deliver the white noise signal.
253
+
254
+ The signal is created using a Gaussian distribution with mean 0 and variance 1,
255
+ scaled by the :attr:`RMS<SignalGenerator.rms>` amplitude of the object.
162
256
 
163
257
  Returns
164
258
  -------
165
- Array of floats
166
- The resulting signal as an array of length :attr:`~SignalGenerator.num_samples`.
259
+ :class:`numpy.ndarray`
260
+ A 1D array of floats containing the generated white noise signal.
261
+ The length of the array is equal to :attr:`~SignalGenerator.num_samples`.
167
262
  """
168
263
  rnd_gen = RandomState(self.seed)
169
264
  return self.rms * rnd_gen.standard_normal(self.num_samples)
170
265
 
171
266
 
172
267
  class PNoiseGenerator(NoiseGenerator):
173
- """Pink noise signal generator.
268
+ """
269
+ Generate pink noise signal.
174
270
 
175
- Simulation of pink noise is based on the Voss-McCartney algorithm.
176
- Ref.:
271
+ The :class:`PNoiseGenerator` class generates pink noise signals,
272
+ which exhibit a :math:`1/f` power spectral density. Pink noise is characterized by
273
+ equal energy per octave, making it useful in various applications such as audio testing,
274
+ sound synthesis, and environmental noise simulations.
177
275
 
178
- * S.J. Orfanidis: Signal Processing (2010), pp. 729-733
179
- * online discussion: http://www.firstpr.com.au/dsp/pink-noise/
276
+ The pink noise simulation is based on the Voss-McCartney algorithm, which iteratively adds
277
+ noise with increasing wavelength to achieve the desired :math:`1/f` characteristic.
180
278
 
181
- The idea is to iteratively add larger-wavelength noise to get 1/f
182
- characteristic.
279
+ See Also
280
+ --------
281
+ :class:`acoular.signals.WNoiseGenerator` : For white noise generation.
282
+ :class:`acoular.sources.UncorrelatedNoiseSource` : For per-channel noise generation.
283
+
284
+ References
285
+ ----------
286
+ - S.J. Orfanidis: Signal Processing (2010), pp. 729-733 :cite:`Orfanidis2010`
287
+ - Online discussion: http://www.firstpr.com.au/dsp/pink-noise/
183
288
  """
184
289
 
185
- #: "Octave depth" -- higher values for 1/f spectrum at low frequencies,
186
- #: but longer calculation, defaults to 16.
290
+ #: "Octave depth" of the pink noise generation. Higher values result in a better approximation
291
+ #: of the :math:`1/f` spectrum at low frequencies but increase computation time. The maximum
292
+ #: allowable value depends on the :attr:`number of samples<SignalGenerator.num_samples>`.
293
+ #: Default is ``16``.
187
294
  depth = Int(16, desc='octave depth')
188
295
 
189
- # internal identifier
296
+ #: A unique checksum identifier based on the object properties. (read-only)
190
297
  digest = Property(depends_on=['rms', 'seed', 'sample_freq', 'num_samples', 'depth'])
191
298
 
192
299
  @cached_property
@@ -194,6 +301,27 @@ class PNoiseGenerator(NoiseGenerator):
194
301
  return digest(self)
195
302
 
196
303
  def signal(self):
304
+ """
305
+ Generate and deliver the pink noise signal.
306
+
307
+ The signal is computed using the Voss-McCartney algorithm, which generates noise
308
+ with a :math:`1/f` power spectral density. The method ensures that the output has the
309
+ desired :attr:`RMS<SignalGenerator.rms>` amplitude and spectrum.
310
+
311
+ Returns
312
+ -------
313
+ :class:`numpy.ndarray`
314
+ A 1D array of floats containing the generated pink noise signal. The length
315
+ of the array is equal to :attr:`~SignalGenerator.num_samples`.
316
+
317
+ Notes
318
+ -----
319
+ - The "depth" parameter controls the number of octaves included in the pink noise
320
+ simulation. If the specified depth exceeds the maximum possible value based on
321
+ the number of samples, it is automatically adjusted, and a warning is printed.
322
+ - The output signal is scaled to have the same overall level as white noise by dividing
323
+ the result by ``sqrt(depth + 1.5)``.
324
+ """
197
325
  nums = self.num_samples
198
326
  depth = self.depth
199
327
  # maximum depth depending on number of samples
@@ -215,22 +343,69 @@ class PNoiseGenerator(NoiseGenerator):
215
343
 
216
344
 
217
345
  class FiltWNoiseGenerator(WNoiseGenerator):
218
- """Filtered white noise signal following an autoregressive (AR), moving-average
219
- (MA) or autoregressive moving-average (ARMA) process.
220
-
221
- The desired frequency response of the filter can be defined by specifying
222
- the filter coefficients :attr:`ar` and :attr:`ma`.
223
- The RMS value specified via the :attr:`rms` attribute belongs to the white noise
224
- signal and differs from the RMS value of the filtered signal.
225
- For numerical stability at high orders, the filter is a combination of second order
226
- sections (sos).
346
+ """
347
+ Generate filtered white noise using an **AR**, **MA**, or **ARMA** process.
348
+
349
+ - **AR:** autoregressive (:attr:`ar`)
350
+ - **MA:** moving-average (:attr:`ma`)
351
+ - **ARMA:** autoregressive moving-average
352
+
353
+ This class extends the :class:`WNoiseGenerator` class to apply a digital filter to white noise,
354
+ producing a signal with specific characteristics based on the provided filter coefficients.
355
+ The desired filter is defined using the autoregressive coefficients (:attr:`ar`) and
356
+ moving-average coefficients (:attr:`ma`).
357
+
358
+ The filter is implemented as a series of second-order sections (sos) for numerical stability,
359
+ especially at high filter orders.
360
+
361
+ See Also
362
+ --------
363
+ :class:`WNoiseGenerator` : For white noise generation.
364
+
365
+ Notes
366
+ -----
367
+ - The output signal is adjusted for the group delay introduced by the filter, ensuring
368
+ proper alignment of the filtered signal.
369
+ - The RMS value specified in the :attr:`~NoiseGenerator.rms` attribute corresponds to the
370
+ original white noise signal and not the filtered output.
371
+
372
+ Examples
373
+ --------
374
+ Generate filtered white noise using :attr:`AR<ar>` and :attr:`MA<ma>` coefficients:
375
+
376
+ >>> import acoular as ac
377
+ >>> import numpy as np
378
+ >>>
379
+ >>> # Define AR and MA coefficients
380
+ >>> ar = np.array([1.0, -0.5])
381
+ >>> ma = np.array([0.5, 0.5])
382
+ >>>
383
+ >>> # Create generator
384
+ >>> gen = ac.FiltWNoiseGenerator(
385
+ ... rms=1.0,
386
+ ... seed=42,
387
+ ... sample_freq=1000,
388
+ ... num_samples=10000,
389
+ ... ar=ar,
390
+ ... ma=ma,
391
+ ... )
392
+ >>>
393
+ >>> # Generate signal
394
+ >>> signal = gen.signal()
395
+ >>> print(signal[:10]) # Print the first 10 samples
396
+ [0.24835708 0.30340346 0.40641385 1.28856612 1.2887213 0.41021549
397
+ 0.87764567 1.61214661 0.95505348 0.51406957]
227
398
  """
228
399
 
400
+ #: A :class:`numpy.ndarray` of autoregressive coefficients (denominator). Default is ``[]``,
401
+ #: which results in no AR filtering (i.e., all-pole filter is ``[1.0]``).
229
402
  ar = CArray(value=array([]), dtype=float, desc='autoregressive coefficients (coefficients of the denominator)')
230
403
 
404
+ #: A :class:`numpy.ndarray` of moving-average coefficients (numerator). Default is ``[]``,
405
+ #: which results in no MA filtering (i.e., all-zero filter is ``[1.0]``).
231
406
  ma = CArray(value=array([]), dtype=float, desc='moving-average coefficients (coefficients of the numerator)')
232
407
 
233
- # internal identifier
408
+ #: A unique checksum identifier based on the object properties. (read-only)
234
409
  digest = Property(depends_on=['rms', 'seed', 'sample_freq', 'num_samples', 'ar', 'ma'])
235
410
 
236
411
  @cached_property
@@ -238,17 +413,42 @@ class FiltWNoiseGenerator(WNoiseGenerator):
238
413
  return digest(self)
239
414
 
240
415
  def handle_empty_coefficients(self, coefficients):
416
+ """
417
+ Handle empty filter coefficient arrays by returning a default value.
418
+
419
+ This method ensures that both the autoregressive (:attr:`ar`) and moving-average
420
+ (:attr:`ma`) coefficients are non-empty before filtering. If a coefficient array is empty,
421
+ it is replaced with a default array containing a single value of ``1.0``.
422
+
423
+ Parameters
424
+ ----------
425
+ coefficients : :class:`numpy.ndarray`
426
+ Array of filter coefficients to check.
427
+
428
+ Returns
429
+ -------
430
+ :class:`numpy.ndarray`
431
+ The original array if it is non-empty, or a default array containing ``[1.0]``
432
+ if the input array is empty.
433
+ """
241
434
  if coefficients.size == 0:
242
435
  return array([1.0])
243
436
  return coefficients
244
437
 
245
438
  def signal(self):
246
- """Deliver the signal.
439
+ """
440
+ Generate and return the filtered white noise signal.
441
+
442
+ This method creates a white noise signal with the specified
443
+ :attr:`RMS value<NoiseGenerator.rms>` and :attr:`~NoiseGenerator.seed`, then filters it
444
+ using the autoregressive (:attr:`ar`) and moving-average (:attr:`ma`) coefficients.
445
+ The filtering process compensates for group delay introduced by the filter.
247
446
 
248
447
  Returns
249
448
  -------
250
- Array of floats
251
- The resulting signal as an array of length :attr:`~SignalGenerator.num_samples`.
449
+ :class:`numpy.ndarray` of :class:`floats<float>`
450
+ An array representing the filtered white noise signal. The length of the returned array
451
+ is equal to :attr:`the number of samples<SignalGenerator.num_samples>`.
252
452
  """
253
453
  rnd_gen = RandomState(self.seed)
254
454
  ma = self.handle_empty_coefficients(self.ma)
@@ -263,9 +463,68 @@ class FiltWNoiseGenerator(WNoiseGenerator):
263
463
 
264
464
 
265
465
  class SineGenerator(PeriodicSignalGenerator):
266
- """Sine signal generator with adjustable amplitude, frequency and phase."""
466
+ r"""
467
+ Generate a sine signal.
267
468
 
268
- # internal identifier
469
+ The :class:`SineGenerator` class extends the :class:`PeriodicSignalGenerator` class and
470
+ generates a sinusoidal signal based on specified :attr:`~PeriodicSignalGenerator.amplitude`,
471
+ :attr:`frequency<PeriodicSignalGenerator.freq>`, and :attr:`~PeriodicSignalGenerator.phase`.
472
+
473
+ This generator is commonly used for creating test signals in signal processing, acoustics,
474
+ and control systems.
475
+
476
+ The signal is defined as
477
+
478
+ .. math::
479
+
480
+ s(t) = A \sin(2 \pi f t + \phi)
481
+
482
+ where:
483
+ - :math:`A` is the amplitude,
484
+ - :math:`f` is the frequency,
485
+ - :math:`\phi` is the phase,
486
+ - :math:`t` is the time (computed from the
487
+ :attr:`sampling frequency<SignalGenerator.sample_freq>` and the
488
+ :attr:`number of samples<SignalGenerator.num_samples>`).
489
+
490
+ See Also
491
+ --------
492
+ PeriodicSignalGenerator : Base class for periodic signal generators.
493
+
494
+ Examples
495
+ --------
496
+ Generate a sine wave signal:
497
+
498
+ >>> import acoular as ac
499
+ >>>
500
+ >>> gen = ac.SineGenerator(
501
+ ... amplitude=2.0,
502
+ ... freq=50.0,
503
+ ... phase=0.0,
504
+ ... num_samples=1000,
505
+ ... sample_freq=1000,
506
+ ... )
507
+ >>> signal = gen.signal()
508
+ >>> signal[:5] # The first 5 samples
509
+ array([0. , 0.61803399, 1.1755705 , 1.61803399, 1.90211303])
510
+
511
+ Generate a sine wave with a phase shift (arguably a cosine wave):
512
+
513
+ >>> import numpy as np
514
+ >>>
515
+ >>> gen = ac.SineGenerator(
516
+ ... amplitude=1.0,
517
+ ... freq=100.0,
518
+ ... phase=np.pi / 2,
519
+ ... num_samples=500,
520
+ ... sample_freq=2000,
521
+ ... )
522
+ >>> signal = gen.signal()
523
+ >>> signal[:5] # The first 5 samples
524
+ array([1. , 0.95105652, 0.80901699, 0.58778525, 0.30901699])
525
+ """
526
+
527
+ #: A unique checksum identifier based on the object properties. (read-only)
269
528
  digest = Property(depends_on=['num_samples', 'sample_freq', 'amplitude', 'freq', 'phase'])
270
529
 
271
530
  @cached_property
@@ -273,12 +532,25 @@ class SineGenerator(PeriodicSignalGenerator):
273
532
  return digest(self)
274
533
 
275
534
  def signal(self):
276
- """Deliver the signal.
535
+ r"""
536
+ Generate and return the sine wave signal.
537
+
538
+ The method computes the sine wave based on the specified
539
+ :attr:`~PeriodicSignalGenerator.amplitude`, :attr:`frequency<PeriodicSignalGenerator.freq>`,
540
+ and :attr:`~PeriodicSignalGenerator.phase`. The time values are determined by the
541
+ :attr:`sampling frequency<SignalGenerator.sample_freq>` and the
542
+ :attr:`number of samples<SignalGenerator.num_samples>`.
277
543
 
278
544
  Returns
279
545
  -------
280
- array of floats
281
- The resulting signal as an array of length :attr:`~SignalGenerator.num_samples`.
546
+ :class:`numpy.ndarray` of :class:`floats<float>`
547
+ A 1D array representing the sine wave signal.
548
+ The length of the array is equal to :attr:`~SignalGenerator.num_samples`.
549
+
550
+ Notes
551
+ -----
552
+ The generator supports high-frequency and high-resolution signals,
553
+ limited by the Nyquist criterion.
282
554
  """
283
555
  t = arange(self.num_samples, dtype=float) / self.sample_freq
284
556
  return self.amplitude * sin(2 * pi * self.freq * t + self.phase)
@@ -286,33 +558,51 @@ class SineGenerator(PeriodicSignalGenerator):
286
558
 
287
559
  @deprecated_alias({'rms': 'amplitude'})
288
560
  class GenericSignalGenerator(SignalGenerator):
289
- """Generate signal from output of :class:`~acoular.base.SamplesGenerator` object.
561
+ """
562
+ Generate signals from a :class:`~acoular.base.SamplesGenerator` or derived object.
563
+
564
+ The :class:`GenericSignalGenerator` class enables the integration of arbitrary signals into
565
+ Acoular processing chains. The signal is fetched from a specified data source and optionally
566
+ scaled by an amplitude factor. It supports looping the signal to match the desired number of
567
+ samples and can handle signals with multiple channels (only the first channel is used).
290
568
 
291
- This class can be used to inject arbitrary signals into Acoular processing
292
- chains. For example, it can be used to read signals from a HDF5 file or create any signal
293
- by using the :class:`acoular.sources.TimeSamples` class.
569
+ Common use cases include:
570
+ - Injecting custom or pre-recorded signals from HDF5 files.
571
+ - Creating signals using the :class:`~acoular.sources.TimeSamples` class.
572
+ - Generating a continuous or repeated signal for simulations.
294
573
 
295
- Example
296
- -------
574
+ Notes
575
+ -----
576
+ If the signal source has more than one channel, only channel 0 is used.
577
+
578
+ Examples
579
+ --------
580
+ Inject a random signal into a processing chain:
581
+
582
+ >>> import acoular as ac
297
583
  >>> import numpy as np
298
- >>> from acoular import TimeSamples, GenericSignalGenerator
584
+ >>>
299
585
  >>> data = np.random.rand(1000, 1)
300
- >>> ts = TimeSamples(data=data, sample_freq=51200)
301
- >>> sig = GenericSignalGenerator(source=ts)
586
+ >>> ts = ac.TimeSamples(data=data, sample_freq=51200)
587
+ >>> sig = ac.GenericSignalGenerator(source=ts)
588
+ >>> output_signal = sig.signal()
302
589
  """
303
590
 
304
- #: Data source; :class:`~acoular.base.SamplesGenerator` or derived object.
591
+ #: The data source from which the signal is fetched.
592
+ #: This can be any object derived from :class:`SamplesGenerator`.
305
593
  source = Instance(SamplesGenerator)
306
594
 
307
- #: Amplitude of the signal. Defaults to 1.0.
595
+ #: Scaling factor applied to the generated signal. Defaults to ``1.0``.
308
596
  amplitude = Float(1.0)
309
597
 
310
- #: Sampling frequency of output signal, as given by :attr:`source`.
598
+ #: Sampling frequency of the output signal, as provided by the :attr:`source` object.
311
599
  sample_freq = Delegate('source')
312
600
 
313
601
  _num_samples = CInt(0)
314
602
 
315
- #: Number of samples to generate. Is set to source.num_samples by default.
603
+ #: The number of samples to generate. Default is the number of samples available in the
604
+ #: :attr:`source` (``source.num_samples``). If set explicitly, it can exceed the source length,
605
+ #: in which case the signal will loop if :attr:`loop_signal` is ``True``.
316
606
  num_samples = Property()
317
607
 
318
608
  def _get_num_samples(self):
@@ -323,11 +613,11 @@ class GenericSignalGenerator(SignalGenerator):
323
613
  def _set_num_samples(self, num_samples):
324
614
  self._num_samples = num_samples
325
615
 
326
- #: Boolean flag, if 'True' (default), signal track is repeated if requested
327
- #: :attr:`num_samples` is higher than available sample number
616
+ #: If ``True`` (default), the signal is repeated to meet the requested :attr:`num_samples`.
617
+ #: If ``False``, the signal stops once the source data is exhausted.
328
618
  loop_signal = Bool(True)
329
619
 
330
- # internal identifier
620
+ #: A unique checksum identifier based on the object properties. (read-only)
331
621
  digest = Property(
332
622
  depends_on=['source.digest', 'loop_signal', 'num_samples', 'amplitude'],
333
623
  )
@@ -337,13 +627,18 @@ class GenericSignalGenerator(SignalGenerator):
337
627
  return digest(self)
338
628
 
339
629
  def signal(self):
340
- """Deliver the signal.
630
+ """
631
+ Deliver the signal from the specified source.
341
632
 
342
633
  Returns
343
634
  -------
344
- array of floats
345
- The resulting signal as an array of length :attr:`~GenericSignalGenerator.num_samples`.
635
+ :class:`numpy.array` of :class:`floats<float>`
636
+ The resulting signal, scaled by the :attr:`amplitude` attribute, with a length
637
+ matching :attr:`~GenericSignalGenerator.num_samples`.
346
638
 
639
+ Warnings
640
+ --------
641
+ A warning is raised if the source has more than one channel.
347
642
  """
348
643
  block = 1024
349
644
  if self.source.num_channels > 1: