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/demo/acoular_demo.py +5 -5
- acoular/environments.py +458 -218
- acoular/fprocess.py +199 -97
- acoular/grids.py +714 -303
- acoular/microphones.py +157 -25
- acoular/process.py +405 -201
- acoular/signals.py +382 -87
- acoular/sources.py +1147 -286
- acoular/spectra.py +280 -128
- acoular/trajectory.py +119 -43
- acoular/version.py +2 -2
- {acoular-25.1.dist-info → acoular-25.3.dist-info}/METADATA +6 -5
- {acoular-25.1.dist-info → acoular-25.3.dist-info}/RECORD +16 -16
- {acoular-25.1.dist-info → acoular-25.3.dist-info}/WHEEL +0 -0
- {acoular-25.1.dist-info → acoular-25.3.dist-info}/licenses/AUTHORS.rst +0 -0
- {acoular-25.1.dist-info → acoular-25.3.dist-info}/licenses/LICENSE +0 -0
acoular/process.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# ------------------------------------------------------------------------------
|
|
2
2
|
# Copyright (c) Acoular Development Team.
|
|
3
3
|
# ------------------------------------------------------------------------------
|
|
4
|
-
"""
|
|
4
|
+
"""
|
|
5
|
+
General purpose blockwise processing methods independent of the domain (time or frequency).
|
|
5
6
|
|
|
6
7
|
.. autosummary::
|
|
7
8
|
:toctree: generated/
|
|
@@ -33,9 +34,25 @@ from .tools.utils import find_basename
|
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
class LockedGenerator:
|
|
36
|
-
"""
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
"""
|
|
38
|
+
Thread-safe wrapper for an iterator or generator.
|
|
39
|
+
|
|
40
|
+
The :class:`LockedGenerator` class ensures that calls to the ``__next__`` method of the
|
|
41
|
+
given iterator or generator are thread-safe, preventing race conditions when accessed by
|
|
42
|
+
multiple threads simultaneously.
|
|
43
|
+
|
|
44
|
+
It achieves thread safety by using a lock to serialize access to the underlying
|
|
45
|
+
iterator or generator.
|
|
46
|
+
|
|
47
|
+
Parameters
|
|
48
|
+
----------
|
|
49
|
+
it : iterator or generator
|
|
50
|
+
The iterator or generator to be made thread-safe.
|
|
51
|
+
|
|
52
|
+
See Also
|
|
53
|
+
--------
|
|
54
|
+
:class:`acoular.process.SampleSplitter` :
|
|
55
|
+
Distribute data from a source to several following objects in a block-wise manner.
|
|
39
56
|
"""
|
|
40
57
|
|
|
41
58
|
def __init__(self, it):
|
|
@@ -43,70 +60,78 @@ class LockedGenerator:
|
|
|
43
60
|
self.lock = threading.Lock()
|
|
44
61
|
|
|
45
62
|
def __next__(self):
|
|
63
|
+
"""Fetch the next item from the iterator or generator in a thread-safe manner."""
|
|
46
64
|
with self.lock:
|
|
47
65
|
return self.it.__next__()
|
|
48
66
|
|
|
49
67
|
|
|
50
68
|
@deprecated_alias({'naverage': 'num_per_average', 'numsamples': 'num_samples'}, read_only=['numsamples'])
|
|
51
69
|
class Average(InOut):
|
|
52
|
-
"""
|
|
70
|
+
"""
|
|
71
|
+
Calculate the average across consecutive time samples or frequency snapshots.
|
|
53
72
|
|
|
54
73
|
The average operation is performed differently depending on the source type.
|
|
55
|
-
If the source is a time domain source
|
|
56
|
-
|
|
57
|
-
|
|
74
|
+
If the source is a time domain source (e.g. derived from
|
|
75
|
+
:class:`~acoular.base.SamplesGenerator`), the average is calculated
|
|
76
|
+
over a certain number of time samples given by :attr:`num_per_average`.
|
|
58
77
|
If the source is a frequency domain source (e.g. derived from
|
|
59
|
-
:class:`~acoular.base.SpectraGenerator`), the average is calculated
|
|
60
|
-
number of snapshots given by :attr:`num_per_average`.
|
|
78
|
+
:class:`~acoular.base.SpectraGenerator`), the average is calculated
|
|
79
|
+
over a certain number of frequency snapshots given by :attr:`num_per_average`.
|
|
80
|
+
|
|
81
|
+
See Also
|
|
82
|
+
--------
|
|
83
|
+
:class:`acoular.base.InOut` :
|
|
84
|
+
Receive data from any source domain and return signals in the same domain.
|
|
61
85
|
|
|
62
86
|
Examples
|
|
63
87
|
--------
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
>>> import acoular as ac
|
|
68
|
-
>>> import numpy as np
|
|
69
|
-
>>>
|
|
70
|
-
>>> signal = ac.WNoiseGenerator(sample_freq=51200, num_samples=51200, rms=2.0).signal()
|
|
71
|
-
>>> ts = ac.TimeSamples(data=signal[:, np.newaxis], sample_freq=51200)
|
|
72
|
-
>>> tp = ac.TimePower(source=ts)
|
|
73
|
-
>>> avg = ac.Average(source=tp, num_per_average=512)
|
|
74
|
-
>>> mean_squared_value = next(avg.result(num=1))
|
|
75
|
-
>>> rms = np.sqrt(mean_squared_value)[0, 0]
|
|
76
|
-
>>> print(rms)
|
|
77
|
-
1.9985200025816718
|
|
78
|
-
|
|
79
|
-
Here, each evaluation of the generator created by the :meth:`result` method of the
|
|
80
|
-
:class:`Average` object via the :meth:`next` function returns :code:`num=1` average across a
|
|
81
|
-
snapshot of 512 samples.
|
|
82
|
-
|
|
83
|
-
If the source is a frequency domain source, the average is calculated over a certain number
|
|
84
|
-
of snapshots, defined by :attr:`num_per_average`.
|
|
85
|
-
|
|
86
|
-
>>> fft = ac.RFFT(source=ts, block_size=64)
|
|
87
|
-
>>> ps = ac.AutoPowerSpectra(source=fft)
|
|
88
|
-
>>> avg = ac.Average(source=ps, num_per_average=16)
|
|
89
|
-
>>> mean_power = next(avg.result(num=1))
|
|
90
|
-
>>> print(np.sqrt(mean_power.sum()))
|
|
91
|
-
2.0024960894399295
|
|
92
|
-
|
|
93
|
-
Here, the generator created by the :meth:`result` method of the :class:`Average` object
|
|
94
|
-
returns the average across 16 snapshots in the frequency domain.
|
|
88
|
+
To estimate the RMS of a white noise (time-domain) signal, the average of the squared
|
|
89
|
+
signal can be calculated:
|
|
95
90
|
|
|
91
|
+
>>> import acoular as ac
|
|
92
|
+
>>> import numpy as np
|
|
93
|
+
>>>
|
|
94
|
+
>>> signal = ac.WNoiseGenerator(sample_freq=51200, num_samples=51200, rms=2.0).signal()
|
|
95
|
+
>>> ts = ac.TimeSamples(data=signal[:, np.newaxis], sample_freq=51200)
|
|
96
|
+
>>> tp = ac.TimePower(source=ts)
|
|
97
|
+
>>> avg = ac.Average(source=tp, num_per_average=512)
|
|
98
|
+
>>> mean_squared_value = next(avg.result(num=1))
|
|
99
|
+
>>> rms = np.sqrt(mean_squared_value)[0, 0]
|
|
100
|
+
>>> print(rms)
|
|
101
|
+
1.9985200025816718
|
|
102
|
+
|
|
103
|
+
Here, each evaluation of the generator created by the :meth:`result` method of the
|
|
104
|
+
:class:`Average` object via the :meth:`next` function returns :code:`num=1` average across a
|
|
105
|
+
snapshot of 512 time samples.
|
|
106
|
+
|
|
107
|
+
If the source is a frequency domain source, the average is calculated over a certain number
|
|
108
|
+
of frequency snapshots, defined by :attr:`num_per_average`.
|
|
109
|
+
|
|
110
|
+
>>> fft = ac.RFFT(source=ts, block_size=64)
|
|
111
|
+
>>> ps = ac.AutoPowerSpectra(source=fft)
|
|
112
|
+
>>> avg = ac.Average(source=ps, num_per_average=16)
|
|
113
|
+
>>> mean_power = next(avg.result(num=1))
|
|
114
|
+
>>> print(np.sqrt(mean_power.sum()))
|
|
115
|
+
2.0024960894399295
|
|
116
|
+
|
|
117
|
+
Here, the generator created by the :meth:`result` method of the :class:`Average` object
|
|
118
|
+
returns the average across 16 snapshots in the frequency domain.
|
|
96
119
|
"""
|
|
97
120
|
|
|
98
|
-
#:
|
|
99
|
-
#: to average over
|
|
121
|
+
#: The number of samples (time domain source) or snapshots (frequency domain source)
|
|
122
|
+
#: to average over. Default is ``64``.
|
|
100
123
|
num_per_average = Int(64, desc='number of samples/snapshots to average over')
|
|
101
124
|
|
|
102
|
-
#:
|
|
125
|
+
#: The sampling frequency of the output signal. It is set automatically as
|
|
126
|
+
#: (:attr:`~acoular.base.Generator.sample_freq` ``/`` :attr:`num_per_average`).
|
|
103
127
|
sample_freq = Property(depends_on=['source.sample_freq', 'num_per_average'])
|
|
104
128
|
|
|
105
|
-
#:
|
|
106
|
-
#:
|
|
129
|
+
#: The number of samples (time domain) or snapshots (frequency domain) of the output signal.
|
|
130
|
+
#: It is set automatically as
|
|
131
|
+
#: (:attr:`~acoular.base.Generator.num_samples` ``/`` :attr:`num_per_average`).
|
|
107
132
|
num_samples = Property(depends_on=['source.num_samples', 'num_per_average'])
|
|
108
133
|
|
|
109
|
-
|
|
134
|
+
#: A unique identifier based on the class properties.
|
|
110
135
|
digest = Property(depends_on=['source.digest', 'num_per_average'])
|
|
111
136
|
|
|
112
137
|
@cached_property
|
|
@@ -126,20 +151,40 @@ class Average(InOut):
|
|
|
126
151
|
return None
|
|
127
152
|
|
|
128
153
|
def result(self, num):
|
|
129
|
-
"""
|
|
154
|
+
"""
|
|
155
|
+
Generate averaged output blocks from the source data.
|
|
156
|
+
|
|
157
|
+
This method implements a Python generator that yields blocks of averaged data
|
|
158
|
+
from the source. The averaging is performed over :attr:`num_per_average` samples
|
|
159
|
+
(for time-domain sources) or snapshots (for frequency-domain sources).
|
|
160
|
+
The size of the blocks yielded is defined by the ``num`` parameter.
|
|
130
161
|
|
|
131
162
|
Parameters
|
|
132
163
|
----------
|
|
133
|
-
num :
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
Returns
|
|
138
|
-
-------
|
|
139
|
-
Average of the output of source.
|
|
140
|
-
Yields samples in blocks of shape (num, num_channels).
|
|
141
|
-
The last block may be shorter than num.
|
|
164
|
+
num : :class:`int`
|
|
165
|
+
The number of averaged blocks to yield at a time. Each block contains the average over
|
|
166
|
+
:attr:`num_per_average` time samples or frequency snapshots. The last block may be
|
|
167
|
+
shorter than the specified size if the remaining data is insufficient.
|
|
142
168
|
|
|
169
|
+
Yields
|
|
170
|
+
------
|
|
171
|
+
:class:`numpy.ndarray`
|
|
172
|
+
A 2D NumPy array of shape ``(num, num_channels)``, where ``num`` is the number
|
|
173
|
+
of averaged blocks requested, and ``num_channels`` corresponds to the number of channels
|
|
174
|
+
in the source, as specified by :attr:`~acoular.base.Generator.num_channels`.
|
|
175
|
+
Each entry in the array is the average over :attr:`num_per_average` samples/snapshots.
|
|
176
|
+
|
|
177
|
+
Notes
|
|
178
|
+
-----
|
|
179
|
+
- The averaging operation depends on the source type:
|
|
180
|
+
- For time-domain sources (e.g., derived from :class:`~acoular.base.SamplesGenerator`),
|
|
181
|
+
the average is calculated over :attr:`num_per_average` time samples.
|
|
182
|
+
- For frequency-domain sources (e.g., derived from
|
|
183
|
+
:class:`~acoular.base.SpectraGenerator`), the average is calculated over
|
|
184
|
+
:attr:`num_per_average` frequency snapshots.
|
|
185
|
+
- The generator will stop yielding when the source data is exhausted.
|
|
186
|
+
- If the source provides fewer than ``num * num_per_average`` samples,
|
|
187
|
+
the final block may be smaller than the requested ``num`` size.
|
|
143
188
|
"""
|
|
144
189
|
nav = self.num_per_average
|
|
145
190
|
for temp in self.source.result(num * nav):
|
|
@@ -150,14 +195,32 @@ class Average(InOut):
|
|
|
150
195
|
|
|
151
196
|
|
|
152
197
|
class Cache(InOut):
|
|
153
|
-
"""
|
|
198
|
+
"""
|
|
199
|
+
Cache the output of a source in a file to avoid redundant computations.
|
|
200
|
+
|
|
201
|
+
The :class:`Cache` class stores the output of a source (derived from
|
|
202
|
+
:class:`~acoular.base.Generator`) in a cache file within the Acoular cache directory.
|
|
203
|
+
This enables faster reuse of precomputed data by avoiding time-consuming recalculations.
|
|
204
|
+
The cache behavior is managed through the :class:`~acoular.configuration.Config` class by
|
|
205
|
+
setting the :attr:`~acoular.configuration.Config.global_caching` attribute.
|
|
154
206
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
The
|
|
207
|
+
The class intelligently determines whether to use the cached data, update it,
|
|
208
|
+
or bypass caching based on the global caching configuration and the state of the cache file.
|
|
209
|
+
The caching mechanism supports scenarios such as:
|
|
210
|
+
|
|
211
|
+
- Reading from a complete or incomplete cache.
|
|
212
|
+
- Overwriting an existing cache.
|
|
213
|
+
- Operating in a read-only or no-cache mode.
|
|
214
|
+
|
|
215
|
+
See Also
|
|
216
|
+
--------
|
|
217
|
+
:class:`acoular.base.InOut` : Receive data from any source domain and return signals in the same
|
|
218
|
+
domain.
|
|
158
219
|
|
|
159
220
|
Examples
|
|
160
221
|
--------
|
|
222
|
+
Caching the output of an FFT computation:
|
|
223
|
+
|
|
161
224
|
>>> import acoular as ac
|
|
162
225
|
>>> import numpy as np
|
|
163
226
|
>>>
|
|
@@ -171,23 +234,24 @@ class Cache(InOut):
|
|
|
171
234
|
[('void_cache.h5', 1)]
|
|
172
235
|
(1, 513)
|
|
173
236
|
|
|
174
|
-
|
|
175
|
-
via the :attr:`~acoular.configuration.Config.global_caching` attribute.
|
|
176
|
-
To turn off caching, set :attr:`~acoular.configuration.Config.global_caching` to 'none' before
|
|
177
|
-
running the code. The cache file directory can be obtained (and set) via the
|
|
178
|
-
:attr:`~acoular.configuration.Config.cache_dir`
|
|
237
|
+
Disabling caching globally:
|
|
179
238
|
|
|
180
239
|
>>> ac.config.global_caching = 'none'
|
|
181
240
|
|
|
241
|
+
Changing the cache directory:
|
|
242
|
+
|
|
243
|
+
>>> ac.config.cache_dir = '/path/to/cache_dir' # doctest: +SKIP
|
|
182
244
|
"""
|
|
183
245
|
|
|
184
|
-
# basename for cache
|
|
246
|
+
# The basename for the cache file.
|
|
247
|
+
# Derived from the :attr:`digest` property and used to uniquely identify the cache file.
|
|
185
248
|
basename = Property(depends_on=['digest'])
|
|
186
249
|
|
|
187
|
-
#
|
|
250
|
+
# The HDF5 cache file instance.
|
|
251
|
+
# This is used to store or retrieve cached data in the Acoular cache directory.
|
|
188
252
|
h5f = Instance(H5CacheFileBase, transient=True)
|
|
189
253
|
|
|
190
|
-
|
|
254
|
+
#: A unique identifier based on the cache properties.
|
|
191
255
|
digest = Property(depends_on=['source.digest'])
|
|
192
256
|
|
|
193
257
|
@cached_property
|
|
@@ -246,21 +310,40 @@ class Cache(InOut):
|
|
|
246
310
|
|
|
247
311
|
# result generator: delivers input, possibly from cache
|
|
248
312
|
def result(self, num):
|
|
249
|
-
"""
|
|
313
|
+
"""
|
|
314
|
+
Generate data blocks from the source, using cache when available.
|
|
315
|
+
|
|
316
|
+
This method acts as a Python generator that yields blocks of output data from the source,
|
|
317
|
+
reading from the cache file when possible. The size of the data blocks is determined by the
|
|
318
|
+
``num`` parameter. The caching mechanism helps prevent redundant calculations by storing and
|
|
319
|
+
reusing the source's output.
|
|
250
320
|
|
|
251
321
|
Parameters
|
|
252
322
|
----------
|
|
253
|
-
num :
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
Returns
|
|
258
|
-
-------
|
|
259
|
-
Samples in blocks of shape (num, num_channels).
|
|
260
|
-
The last block may be shorter than num.
|
|
261
|
-
Echos the source output, but reads it from cache
|
|
262
|
-
when available and prevents unnecessary recalculation.
|
|
323
|
+
num : :class:`int`
|
|
324
|
+
The number of time samples or frequency snapshots per block to yield.
|
|
325
|
+
The final block may be smaller if there is insufficient data.
|
|
263
326
|
|
|
327
|
+
Yields
|
|
328
|
+
------
|
|
329
|
+
:class:`numpy.ndarray`
|
|
330
|
+
A 2D NumPy array of shape ``(num, num_channels)`` representing the output data.
|
|
331
|
+
Each block is either retrieved from the cache file or generated by the source
|
|
332
|
+
and cached dynamically during processing.
|
|
333
|
+
|
|
334
|
+
Notes
|
|
335
|
+
-----
|
|
336
|
+
- The behavior of the caching mechanism depends on the
|
|
337
|
+
:attr:`~acoular.configuration.Config.global_caching` setting:
|
|
338
|
+
|
|
339
|
+
- ``'none'``: Bypasses caching and directly retrieves data from the source.
|
|
340
|
+
- ``'readonly'``: Reads data from the cache if available; otherwise,
|
|
341
|
+
retrieves data from the source without caching.
|
|
342
|
+
- ``'overwrite'``: Replaces any existing cache with newly computed data.
|
|
343
|
+
|
|
344
|
+
- If the cache file is incomplete or corrupted, the method may generate new data
|
|
345
|
+
from the source to update the cache unless the caching mode is ``'readonly'``.
|
|
346
|
+
- The cache node name is based on the source's :attr:`digest` attribute.
|
|
264
347
|
"""
|
|
265
348
|
if config.global_caching == 'none':
|
|
266
349
|
generator = self._pass_data
|
|
@@ -299,30 +382,45 @@ class Cache(InOut):
|
|
|
299
382
|
|
|
300
383
|
class SampleSplitter(InOut):
|
|
301
384
|
"""
|
|
302
|
-
|
|
385
|
+
Distribute data from a source to multiple connected objects in a block-wise manner.
|
|
386
|
+
|
|
387
|
+
The :class:`SampleSplitter` class is designed to manage the distribution of data blocks from a
|
|
388
|
+
single source object, derived from :class:`~acoular.base.Generator`, to multiple target
|
|
389
|
+
objects, also derived from :class:`~acoular.base.Generator`. Each connected target object
|
|
390
|
+
is assigned a dedicated buffer to hold incoming data blocks. These buffers operate in a
|
|
391
|
+
first-in-first-out (FIFO) manner, ensuring efficient and parallelized data handling.
|
|
392
|
+
|
|
393
|
+
This class is particularly useful when distributing data blocks from a streaming source
|
|
394
|
+
to multiple downstream processing objects.
|
|
395
|
+
|
|
396
|
+
Each registered target object maintains its own dedicated block buffer, allowing for independent
|
|
397
|
+
data management. The buffer size can be customized per object, and different overflow handling
|
|
398
|
+
strategies can be configured, such as raising an error, issuing a warning, or discarding old
|
|
399
|
+
data. This ensures efficient parallel data processing, making it well-suited for complex
|
|
400
|
+
workflows.
|
|
303
401
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
first-in-first-out (FIFO) manner. This allows for efficient data handling and processing in
|
|
309
|
-
parallel.
|
|
402
|
+
Notes
|
|
403
|
+
-----
|
|
404
|
+
- Buffers are dynamically created and managed for each registered object.
|
|
405
|
+
- Buffer overflow behavior can be set individually for each target object.
|
|
310
406
|
|
|
311
407
|
Examples
|
|
312
408
|
--------
|
|
313
|
-
Consider a time
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
generator.
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
409
|
+
Consider a time-domain signal stream where the FFT spectra and signal power are calculated
|
|
410
|
+
block-by-block and in parallel using the :class:`~acoular.fprocess.RFFT`,
|
|
411
|
+
:class:`~acoular.tprocess.TimePower`, and :class:`~acoular.process.Average` objects.
|
|
412
|
+
The :class:`SampleSplitter` is responsible for distributing incoming data blocks to the buffers
|
|
413
|
+
of the :class:`~acoular.fprocess.RFFT` and :class:`~acoular.tprocess.TimePower` objects whenever
|
|
414
|
+
either object requests data via the :meth:`result` generator.
|
|
415
|
+
|
|
416
|
+
For the :class:`~acoular.tprocess.TimePower` object, the buffer size is set to 10 blocks.
|
|
417
|
+
If the buffer is full, an error is raised, as the buffer overflow treatment is set to
|
|
418
|
+
``'error'``. For the :class:`~acoular.fprocess.RFFT` object, the buffer size is limited to 1
|
|
419
|
+
block, and the overflow treatment is set to ``'none'``. This setup helps reduce latency in FFT
|
|
420
|
+
calculations, which may take longer than signal power calculations. If new data arrives and the
|
|
421
|
+
:class:`~acoular.fprocess.RFFT` buffer is full, the :class:`SampleSplitter` will discard the
|
|
422
|
+
oldest block, ensuring that the :class:`~acoular.fprocess.RFFT`
|
|
423
|
+
object always receives the most recent block of data.
|
|
326
424
|
|
|
327
425
|
>>> import acoular as ac
|
|
328
426
|
>>> import numpy as np
|
|
@@ -342,9 +440,9 @@ class SampleSplitter(InOut):
|
|
|
342
440
|
>>> ss.register_object(fft, buffer_size=1, buffer_overflow_treatment='none')
|
|
343
441
|
>>> ss.register_object(pow, buffer_size=10, buffer_overflow_treatment='error')
|
|
344
442
|
|
|
345
|
-
After object registration, the
|
|
346
|
-
object buffers. The block buffers can be accessed via the
|
|
347
|
-
|
|
443
|
+
After object registration, the ``SampleSplitter`` object is ready to distribute the data to the
|
|
444
|
+
object buffers. The block buffers can be accessed via the :attr:`block_buffer` attribute of the
|
|
445
|
+
``SampleSplitter`` object.
|
|
348
446
|
|
|
349
447
|
>>> ss.block_buffer.values()
|
|
350
448
|
dict_values([deque([], maxlen=1), deque([], maxlen=10)])
|
|
@@ -361,42 +459,37 @@ class SampleSplitter(InOut):
|
|
|
361
459
|
>>> print(len(ss.block_buffer[pow]))
|
|
362
460
|
1
|
|
363
461
|
|
|
364
|
-
To remove registered objects from the
|
|
462
|
+
To remove registered objects from the :class:`SampleSplitter`, use the :meth:`remove_object`
|
|
463
|
+
method.
|
|
365
464
|
|
|
366
465
|
>>> ss.remove_object(pow)
|
|
367
466
|
>>> print(len(ss.block_buffer))
|
|
368
467
|
1
|
|
369
468
|
"""
|
|
370
469
|
|
|
371
|
-
#: dictionary
|
|
372
|
-
#:
|
|
470
|
+
#: A dictionary containing block buffers for registered objects.
|
|
471
|
+
#: Keys are the registered objects, and values are deque structures holding data blocks.
|
|
373
472
|
block_buffer = Dict(key_trait=Instance(Generator))
|
|
374
473
|
|
|
375
|
-
#:
|
|
376
|
-
#: Can be set
|
|
377
|
-
#: Default is 100 blocks for each registered object.
|
|
474
|
+
#: The maximum number of blocks each buffer can hold.
|
|
475
|
+
#: Can be set globally for all objects or individually using a dictionary.
|
|
378
476
|
buffer_size = Union(
|
|
379
477
|
Int,
|
|
380
478
|
Dict(key_trait=Instance(Generator), value_trait=Int),
|
|
381
479
|
default_value=100,
|
|
382
480
|
)
|
|
383
481
|
|
|
384
|
-
#:
|
|
385
|
-
#: for each registered object.
|
|
386
|
-
#:
|
|
387
|
-
#: * 'error': an IOError is thrown by the class
|
|
388
|
-
#: * 'warning': a warning is displayed. Possibly leads to lost blocks of data
|
|
389
|
-
#: * 'none': nothing happens. Possibly leads to lost blocks of data
|
|
482
|
+
#: Defines behavior when a buffer exceeds its maximum size.
|
|
390
483
|
buffer_overflow_treatment = Dict(
|
|
391
484
|
key_trait=Instance(Generator),
|
|
392
485
|
value_trait=Enum('error', 'warning', 'none'),
|
|
393
486
|
desc='defines buffer overflow behaviour.',
|
|
394
487
|
)
|
|
395
488
|
|
|
396
|
-
# shadow trait to monitor if source deliver samples or is empty
|
|
489
|
+
# A shadow trait to monitor if source deliver samples or is empty.
|
|
397
490
|
_source_generator_exist = Bool(False)
|
|
398
491
|
|
|
399
|
-
# shadow trait to monitor if buffer of objects with overflow treatment = 'error'
|
|
492
|
+
# A shadow trait to monitor if buffer of objects with overflow treatment = 'error'
|
|
400
493
|
# or warning is overfilled. Error will be raised in all threads.
|
|
401
494
|
_buffer_overflow = Bool(False)
|
|
402
495
|
|
|
@@ -456,23 +549,35 @@ class SampleSplitter(InOut):
|
|
|
456
549
|
self._create_block_buffer(obj)
|
|
457
550
|
|
|
458
551
|
def register_object(self, *objects_to_register, buffer_size=None, buffer_overflow_treatment=None):
|
|
459
|
-
"""
|
|
552
|
+
"""
|
|
553
|
+
Register one or more target objects to the :class:`SampleSplitter` object.
|
|
460
554
|
|
|
461
|
-
|
|
462
|
-
|
|
555
|
+
This method creates and configures block buffers for the specified target objects, enabling
|
|
556
|
+
them to receive data blocks from the :class:`SampleSplitter`. Each registered object is
|
|
557
|
+
assigned a dedicated buffer with customizable size and overflow behavior.
|
|
463
558
|
|
|
464
559
|
Parameters
|
|
465
560
|
----------
|
|
466
|
-
objects_to_register : Generator
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
of
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
561
|
+
objects_to_register : :class:`~acoular.base.Generator` or list of :class:`~acoular.base.Generator`
|
|
562
|
+
A single object or a list of objects derived from :class:`~acoular.base.Generator` to be
|
|
563
|
+
registered as targets for data distribution.
|
|
564
|
+
buffer_size : :class:`int`, optional
|
|
565
|
+
The maximum number of data blocks each object's buffer can hold. If not specified,
|
|
566
|
+
the default buffer size (100 blocks) is used, or a globally defined size if
|
|
567
|
+
``buffer_size`` is a dictionary.
|
|
568
|
+
buffer_overflow_treatment : :attr:`str`, optional
|
|
569
|
+
Defines the behavior when a buffer exceeds its maximum size. Options are:
|
|
570
|
+
|
|
571
|
+
- ``'error'``: Raises an :obj:`IOError` when the buffer overflows.
|
|
572
|
+
- ``'warning'``: Issues a warning and may result in data loss.
|
|
573
|
+
- ``'none'``: Silently discards the oldest data blocks to make room for new ones.
|
|
574
|
+
If not specified, the default behavior is ``'error'``.
|
|
575
|
+
|
|
576
|
+
Raises
|
|
577
|
+
------
|
|
578
|
+
:obj:`OSError`
|
|
579
|
+
If any of the specified objects is already registered.
|
|
580
|
+
""" # noqa: W505
|
|
476
581
|
for obj in objects_to_register:
|
|
477
582
|
if obj not in self.block_buffer:
|
|
478
583
|
self._create_block_buffer(obj, buffer_size)
|
|
@@ -482,16 +587,31 @@ class SampleSplitter(InOut):
|
|
|
482
587
|
raise OSError(msg)
|
|
483
588
|
|
|
484
589
|
def remove_object(self, *objects_to_remove):
|
|
485
|
-
"""
|
|
590
|
+
"""
|
|
591
|
+
Unregister one or more objects from the :class:`SampleSplitter`.
|
|
486
592
|
|
|
487
|
-
|
|
593
|
+
This method removes the specified objects and their associated block buffers from the
|
|
594
|
+
:class:`SampleSplitter`. If no objects are specified, all currently registered objects
|
|
595
|
+
are unregistered, effectively clearing all buffers.
|
|
488
596
|
|
|
489
597
|
Parameters
|
|
490
598
|
----------
|
|
491
|
-
objects_to_remove : list
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
599
|
+
objects_to_remove : :class:`~acoular.base.Generator` or list of :class:`~acoular.base.Generator`, optional
|
|
600
|
+
A single object or a list of objects derived from :class:`~acoular.base.Generator` to be
|
|
601
|
+
removed from the :class:`SampleSplitter`.
|
|
602
|
+
If no objects are provided, all registered objects will be removed.
|
|
603
|
+
|
|
604
|
+
Raises
|
|
605
|
+
------
|
|
606
|
+
:obj:`KeyError`
|
|
607
|
+
If any of the specified objects are not currently registered.
|
|
608
|
+
|
|
609
|
+
Notes
|
|
610
|
+
-----
|
|
611
|
+
- Once an object is removed, it will no longer receive data from the
|
|
612
|
+
:class:`SampleSplitter`.
|
|
613
|
+
- Removing an object also clears its associated buffer.
|
|
614
|
+
""" # noqa: W505
|
|
495
615
|
if not objects_to_remove:
|
|
496
616
|
objects_to_remove = list(self.block_buffer.keys())
|
|
497
617
|
for obj in objects_to_remove:
|
|
@@ -499,20 +619,39 @@ class SampleSplitter(InOut):
|
|
|
499
619
|
self._remove_buffer_overflow_treatment(obj)
|
|
500
620
|
|
|
501
621
|
def result(self, num):
|
|
502
|
-
"""
|
|
622
|
+
"""
|
|
623
|
+
Yield data blocks from the buffer to the calling object.
|
|
624
|
+
|
|
625
|
+
This generator method retrieves blocks of data for the calling object, either
|
|
626
|
+
from its dedicated block buffer or by processing new data from the source.
|
|
627
|
+
If the buffer is empty, new data blocks are generated and distributed to
|
|
628
|
+
all registered objects in a block-wise manner.
|
|
503
629
|
|
|
504
630
|
Parameters
|
|
505
631
|
----------
|
|
506
|
-
num :
|
|
507
|
-
|
|
508
|
-
(i.e. the number of samples per block).
|
|
632
|
+
num : :class:`int`
|
|
633
|
+
The size of each block to be yielded, defined as the number of samples per block.
|
|
509
634
|
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
The last block may be shorter than num.
|
|
635
|
+
Yields
|
|
636
|
+
------
|
|
637
|
+
:class:`numpy.ndarray`
|
|
638
|
+
Blocks of data with shape ``(num, num_channels)``.
|
|
639
|
+
The last block may be shorter than ``num`` if the source data is exhausted.
|
|
515
640
|
|
|
641
|
+
Raises
|
|
642
|
+
------
|
|
643
|
+
:obj:`OSError`
|
|
644
|
+
If the calling object is not registered with the :class:`SampleSplitter`.
|
|
645
|
+
:obj:`OSError`
|
|
646
|
+
If the block buffer reaches its maximum size and the overflow handling
|
|
647
|
+
policy is set to ``'error'``.
|
|
648
|
+
|
|
649
|
+
Notes
|
|
650
|
+
-----
|
|
651
|
+
- If the block buffer is empty, new data is fetched from the source and distributed to all
|
|
652
|
+
registered objects.
|
|
653
|
+
- Buffer overflow behavior is controlled by the :attr:`buffer_overflow_treatment` attribute,
|
|
654
|
+
which can be set to ``'error'``, ``'warning'``, or ``'none'``.
|
|
516
655
|
"""
|
|
517
656
|
calling_obj = currentframe().f_back.f_locals['self']
|
|
518
657
|
self._assert_obj_registered(calling_obj)
|
|
@@ -537,11 +676,15 @@ class SampleSplitter(InOut):
|
|
|
537
676
|
|
|
538
677
|
|
|
539
678
|
class TimeAverage(Average):
|
|
540
|
-
"""
|
|
679
|
+
"""
|
|
680
|
+
Calculate the average of the signal.
|
|
541
681
|
|
|
542
682
|
.. deprecated:: 24.10
|
|
543
|
-
|
|
544
|
-
version 25.07.
|
|
683
|
+
The use of :class:`~acoular.process.TimeAverage` is deprecated
|
|
684
|
+
and will be removed in Acoular version 25.07.
|
|
685
|
+
Please use :class:`~acoular.process.Average` instead for future compatibility.
|
|
686
|
+
|
|
687
|
+
Alias for :class:`~acoular.process.Average`.
|
|
545
688
|
"""
|
|
546
689
|
|
|
547
690
|
def __init__(self, *args, **kwargs):
|
|
@@ -554,11 +697,15 @@ class TimeAverage(Average):
|
|
|
554
697
|
|
|
555
698
|
|
|
556
699
|
class TimeCache(Cache):
|
|
557
|
-
"""
|
|
700
|
+
"""
|
|
701
|
+
Cache source signals in cache file.
|
|
558
702
|
|
|
559
703
|
.. deprecated:: 24.10
|
|
560
|
-
|
|
561
|
-
version 25.07.
|
|
704
|
+
The use of :class:`~acoular.process.TimeCache` is deprecated
|
|
705
|
+
and will be removed in Acoular version 25.07.
|
|
706
|
+
Please use :class:`~acoular.process.Cache` instead for future compatibility.
|
|
707
|
+
|
|
708
|
+
Alias for :class:`~acoular.process.Cache`.
|
|
562
709
|
"""
|
|
563
710
|
|
|
564
711
|
def __init__(self, *args, **kwargs):
|
|
@@ -571,17 +718,17 @@ class TimeCache(Cache):
|
|
|
571
718
|
|
|
572
719
|
|
|
573
720
|
class SamplesBuffer(InOut):
|
|
574
|
-
"""
|
|
721
|
+
"""
|
|
722
|
+
Handle buffering of samples from a source.
|
|
575
723
|
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
724
|
+
The :class:`SamplesBuffer` class buffers samples from a source and provides them in blocks of a
|
|
725
|
+
specified size. It supports various use cases for efficient handling of sample data.
|
|
726
|
+
Below is an example demonstrating its functionality.
|
|
579
727
|
|
|
580
728
|
Examples
|
|
581
729
|
--------
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
behaviour by using the following code:
|
|
730
|
+
Suppose we want to draw blocks of 16 samples from the source, while ensuring that the buffer
|
|
731
|
+
always holds twice that number (32 samples). The following code achieves this behavior:
|
|
585
732
|
|
|
586
733
|
>>> import acoular as ac
|
|
587
734
|
>>> import numpy as np
|
|
@@ -599,20 +746,20 @@ class SamplesBuffer(InOut):
|
|
|
599
746
|
>>> block = next(buffer.result(num=16))
|
|
600
747
|
>>> np.testing.assert_array_equal(block, source.data[:16])
|
|
601
748
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
749
|
+
In the example above, the buffer initially collects blocks of the specified size from the
|
|
750
|
+
source. It then returns the first block of 16 samples. With subsequent calls to the
|
|
751
|
+
:meth:`result` method, the buffer refills and returns additional blocks of 16 samples.
|
|
752
|
+
|
|
753
|
+
In some cases, you may wish to retrieve a different number of samples from the source than you
|
|
754
|
+
want to return. This can be achieved by setting the :attr:`source_num` attribute. For example,
|
|
755
|
+
in the :class:`~acoular.tbeamform.BeamformerTimeTraj` class, the number of time samples varies
|
|
756
|
+
based on the expected delay for moving sources, while still adhering to the desired block size
|
|
757
|
+
for the buffer.
|
|
758
|
+
|
|
759
|
+
The :attr:`shift_index_by` attribute controls how the buffer updates its index when retrieving
|
|
760
|
+
data. If set to ``'num'``, the buffer returns :attr:`result_num` samples but forgets ``'num'``
|
|
761
|
+
samples from the buffer.
|
|
762
|
+
If set to :attr:`result_num`, the buffer will return and forget the same number of samples.
|
|
616
763
|
|
|
617
764
|
>>> buffer = ac.process.SamplesBuffer(source=source, length=32, result_num=20, shift_index_by='num')
|
|
618
765
|
>>> block_sizes = []
|
|
@@ -626,11 +773,11 @@ class SamplesBuffer(InOut):
|
|
|
626
773
|
>>> np.testing.assert_array_equal(block_sizes, [20, 24])
|
|
627
774
|
""" # noqa: W505
|
|
628
775
|
|
|
629
|
-
#: number of samples that
|
|
776
|
+
#: The number of samples that the buffer can hold.
|
|
630
777
|
length = Int(desc='number of samples that fit in the buffer')
|
|
631
778
|
|
|
632
|
-
#: number of samples per block to obtain from the source. If
|
|
633
|
-
#: result method
|
|
779
|
+
#: The number of samples per block to obtain from the source. If set to ``None``, the number of
|
|
780
|
+
#: samples will be determined by the ``num`` argument of the :meth:`result` method.
|
|
634
781
|
source_num = Union(
|
|
635
782
|
None,
|
|
636
783
|
Int(),
|
|
@@ -638,7 +785,8 @@ class SamplesBuffer(InOut):
|
|
|
638
785
|
desc='number of samples to return from the source. If "None", use "num" argument of result method',
|
|
639
786
|
)
|
|
640
787
|
|
|
641
|
-
#: number of samples to return from the buffer. If
|
|
788
|
+
#: The number of samples to return from the buffer. If set to ``None``, the number of
|
|
789
|
+
#: samples will be determined by the ``num`` argument of the :meth:`result` method.
|
|
642
790
|
result_num = Union(
|
|
643
791
|
None,
|
|
644
792
|
Int(),
|
|
@@ -646,8 +794,11 @@ class SamplesBuffer(InOut):
|
|
|
646
794
|
desc="number of samples to return from the buffer. If 'None', use 'num' argument of result method",
|
|
647
795
|
)
|
|
648
796
|
|
|
649
|
-
#:
|
|
650
|
-
#:
|
|
797
|
+
#: Index shift value for the buffer.
|
|
798
|
+
#:
|
|
799
|
+
#: - If set to ``'result_num'``, the buffer will return and forget :attr:`result_num` samples.
|
|
800
|
+
#: - If set to ``'num'``, the buffer will return :attr:`result_num` samples but forget ``num``
|
|
801
|
+
#: samples.
|
|
651
802
|
shift_index_by = Enum(
|
|
652
803
|
('result_num', 'num'),
|
|
653
804
|
desc=(
|
|
@@ -656,19 +807,19 @@ class SamplesBuffer(InOut):
|
|
|
656
807
|
),
|
|
657
808
|
)
|
|
658
809
|
|
|
659
|
-
#: current filling level of buffer
|
|
810
|
+
#: The current filling level of the buffer, i.e., how many samples are currently available.
|
|
660
811
|
level = Property(desc='current filling level of buffer')
|
|
661
812
|
|
|
662
|
-
#: data type of the buffer
|
|
813
|
+
#: The data type of the elements in the buffer.
|
|
663
814
|
dtype = Any(desc='data type of the buffer')
|
|
664
815
|
|
|
665
|
-
#
|
|
816
|
+
# Flag indicating if the source is empty (for internal use).
|
|
666
817
|
_empty_source = Bool(False, desc='flag to indicate that the source is empty')
|
|
667
818
|
|
|
668
|
-
#
|
|
819
|
+
# The actual buffer holding the samples for processing.
|
|
669
820
|
_buffer = Array(shape=(None, None), desc='buffer for block processing')
|
|
670
821
|
|
|
671
|
-
# current index in buffer
|
|
822
|
+
# The current index position in the buffer.
|
|
672
823
|
_index = Int(desc='current index in buffer')
|
|
673
824
|
|
|
674
825
|
def _get_level(self):
|
|
@@ -686,11 +837,17 @@ class SamplesBuffer(InOut):
|
|
|
686
837
|
self._index -= ns
|
|
687
838
|
|
|
688
839
|
def increase_buffer(self, num):
|
|
689
|
-
"""
|
|
840
|
+
"""
|
|
841
|
+
Increase the size of the buffer by a specified number of samples.
|
|
690
842
|
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
843
|
+
This method expands the buffer by appending additional samples, effectively increasing
|
|
844
|
+
its capacity. The new samples are initialized to zero. The index of the buffer is adjusted
|
|
845
|
+
accordingly to accommodate the increase.
|
|
846
|
+
|
|
847
|
+
Parameters
|
|
848
|
+
----------
|
|
849
|
+
num : :class:`int`
|
|
850
|
+
The number of samples by which to increase the buffer size.
|
|
694
851
|
"""
|
|
695
852
|
ar = np.zeros((num, self.num_channels), dtype=self._buffer.dtype)
|
|
696
853
|
self._buffer = np.concatenate((ar, self._buffer), axis=0)
|
|
@@ -698,18 +855,32 @@ class SamplesBuffer(InOut):
|
|
|
698
855
|
self.length += num
|
|
699
856
|
|
|
700
857
|
def read_from_buffer(self, num):
|
|
701
|
-
"""
|
|
858
|
+
"""
|
|
859
|
+
Read a specified number of samples from the buffer.
|
|
860
|
+
|
|
861
|
+
This method retrieves samples from the buffer, ensuring that the requested number of samples
|
|
862
|
+
is returned. If the buffer contains fewer samples than requested, the method will return all
|
|
863
|
+
available samples. The index of the buffer is updated based on the :attr:`shift_index_by`
|
|
864
|
+
setting.
|
|
702
865
|
|
|
703
866
|
Parameters
|
|
704
867
|
----------
|
|
705
|
-
num : int
|
|
706
|
-
number of samples to read from the buffer.
|
|
868
|
+
num : :class:`int`
|
|
869
|
+
The number of samples to read from the buffer.
|
|
707
870
|
|
|
708
871
|
Returns
|
|
709
872
|
-------
|
|
710
|
-
numpy.ndarray
|
|
711
|
-
block of samples from the buffer
|
|
873
|
+
:class:`numpy.ndarray`
|
|
874
|
+
A block of samples (array) from the buffer.
|
|
875
|
+
|
|
876
|
+
Notes
|
|
877
|
+
-----
|
|
878
|
+
- If the :attr:`result_num` attribute is set, it determines the number of samples to return.
|
|
879
|
+
- The method ensures the buffer index is adjusted according to the :attr:`shift_index_by`
|
|
880
|
+
setting. Options are:
|
|
712
881
|
|
|
882
|
+
- ``'result_num'``: The index will shift by the number of samples returned.
|
|
883
|
+
- ``'num'``: The index will shift by the number of samples requested (``num``).
|
|
713
884
|
"""
|
|
714
885
|
rnum = num if self.result_num is None else self.result_num
|
|
715
886
|
rnum = rnum if self.level >= rnum else self.level
|
|
@@ -721,16 +892,32 @@ class SamplesBuffer(InOut):
|
|
|
721
892
|
return data
|
|
722
893
|
|
|
723
894
|
def fill_buffer(self, snum):
|
|
724
|
-
"""
|
|
895
|
+
"""
|
|
896
|
+
Fill the buffer with samples from the source.
|
|
897
|
+
|
|
898
|
+
The :meth:`fill_buffer` method collects samples from the source and writes them to the
|
|
899
|
+
buffer. It continues to fill the buffer until there are enough samples available, or the
|
|
900
|
+
source runs out of data. If the buffer reaches its maximum capacity, additional samples are
|
|
901
|
+
discarded. The buffer will only contain the most recent data, and its index will be updated
|
|
902
|
+
accordingly.
|
|
725
903
|
|
|
726
904
|
Parameters
|
|
727
905
|
----------
|
|
728
|
-
snum : int
|
|
729
|
-
number of samples to
|
|
906
|
+
snum : :class:`int`
|
|
907
|
+
The number of samples to retrieve from the source in each iteration.
|
|
730
908
|
|
|
731
909
|
Yields
|
|
732
910
|
------
|
|
733
|
-
None
|
|
911
|
+
:obj:`None`
|
|
912
|
+
This method is a generator and yields control back after filling the buffer.
|
|
913
|
+
|
|
914
|
+
Notes
|
|
915
|
+
-----
|
|
916
|
+
- The method ensures that the buffer is filled with the required number of samples,
|
|
917
|
+
adjusting the buffer size if necessary (via the :meth:`increase_buffer` method) when more
|
|
918
|
+
space is needed.
|
|
919
|
+
- Once the buffer is filled, it yields control and resumes only when the buffer is ready for
|
|
920
|
+
more data.
|
|
734
921
|
"""
|
|
735
922
|
source_generator = self.source.result(snum)
|
|
736
923
|
while not self._empty_source:
|
|
@@ -746,17 +933,34 @@ class SamplesBuffer(InOut):
|
|
|
746
933
|
yield
|
|
747
934
|
|
|
748
935
|
def result(self, num):
|
|
749
|
-
"""
|
|
936
|
+
"""
|
|
937
|
+
Return blocks of samples from the buffer.
|
|
938
|
+
|
|
939
|
+
The :meth:`result` method retrieves blocks of samples from the buffer and yields them to the
|
|
940
|
+
calling process. The number of samples per block is determined by the ``num`` argument, but
|
|
941
|
+
can also be influenced by other attributes like `result_num` (if set). If the buffer is not
|
|
942
|
+
yet filled, it will continue to collect samples from the source until the buffer contains
|
|
943
|
+
enough data. Once the buffer is full, it will return the requested blocks of samples.
|
|
750
944
|
|
|
751
945
|
Parameters
|
|
752
946
|
----------
|
|
753
|
-
num : int
|
|
754
|
-
number of samples to return.
|
|
947
|
+
num : :class:`int`
|
|
948
|
+
The number of samples to return in each block.
|
|
949
|
+
This value specifies the size of the blocks to be yielded from the buffer.
|
|
755
950
|
|
|
756
951
|
Yields
|
|
757
952
|
------
|
|
758
|
-
numpy.ndarray
|
|
759
|
-
block of samples from the buffer
|
|
953
|
+
:class:`numpy.ndarray`
|
|
954
|
+
A block of samples from the buffer. The size of the block is determined by the ``num``
|
|
955
|
+
parameter or the :attr:`result_num` attribute, depending on the buffer's configuration.
|
|
956
|
+
|
|
957
|
+
Notes
|
|
958
|
+
-----
|
|
959
|
+
- If :attr:`result_num` is set, the method will use it to determine the number of samples
|
|
960
|
+
returned instead of the ``num`` parameter.
|
|
961
|
+
- If the buffer is empty or does not have enough samples, it will attempt to fill the buffer
|
|
962
|
+
by collecting data from the source. If there are not enough samples available from the
|
|
963
|
+
source, the method will yield whatever samples are left in the buffer.
|
|
760
964
|
"""
|
|
761
965
|
self._create_new_buffer()
|
|
762
966
|
snum = num
|