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/tools/__init__.py CHANGED
@@ -9,6 +9,7 @@
9
9
  aiaa
10
10
  helpers
11
11
  metrics
12
+ utils
12
13
  """
13
14
 
14
15
  from .aiaa import (
@@ -23,3 +24,4 @@ from .helpers import (
23
24
  return_result,
24
25
  )
25
26
  from .metrics import MetricEvaluator
27
+ from .utils import SamplesBuffer
acoular/tools/aiaa.py CHANGED
@@ -54,13 +54,13 @@ class TimeSamplesAIAABenchmark(TimeSamples):
54
54
  objects.
55
55
  """
56
56
 
57
- def load_timedata(self):
57
+ def _load_timedata(self):
58
58
  """Loads timedata from .h5 file. Only for internal use."""
59
59
  self.data = self.h5f.get_data_by_reference('MicrophoneData/microphoneDataPa')
60
60
  self.sample_freq = self.h5f.get_node_attribute(self.data, 'sampleRateHz')
61
61
  (self.numsamples, self.numchannels) = self.data.shape
62
62
 
63
- def load_metadata(self):
63
+ def _load_metadata(self):
64
64
  """Loads metadata from .h5 file. Only for internal use."""
65
65
  self.metadata = {}
66
66
  if '/MetaData' in self.h5f:
@@ -76,7 +76,7 @@ class TriggerAIAABenchmark(TimeSamplesAIAABenchmark):
76
76
  and and provides information about this data.
77
77
  """
78
78
 
79
- def load_timedata(self):
79
+ def _load_timedata(self):
80
80
  """Loads timedata from .h5 file. Only for internal use."""
81
81
  self.data = self.h5f.get_data_by_reference('TachoData/tachoDataV')
82
82
  self.sample_freq = self.h5f.get_node_attribute(self.data, 'sampleRateHz')
acoular/tools/helpers.py CHANGED
@@ -24,13 +24,13 @@ from acoular.spectra import synthetic
24
24
 
25
25
  def return_result(source, nmax=-1, num=128):
26
26
  """Collects the output from a
27
- :meth:`SamplesGenerator.result()<acoular.tprocess.SamplesGenerator.result>`
27
+ :meth:`SamplesGenerator.result()<acoular.base.SamplesGenerator.result>`
28
28
  generator and returns an assembled array with all the data.
29
29
 
30
30
  Parameters
31
31
  ----------
32
32
  source: SamplesGenerator or derived object.
33
- This is the :class:`SamplesGenerator<acoular.tprocess.SamplesGenerator>` data source.
33
+ This is the :class:`SamplesGenerator<acoular.base.SamplesGenerator>` data source.
34
34
  nmax: integer
35
35
  With this parameter, a maximum number of output samples can be set
36
36
  (first dimension of array). If set to -1 (default), samples are
acoular/tools/metrics.py CHANGED
@@ -28,7 +28,7 @@ class MetricEvaluator(HasPrivateTraits):
28
28
  """Evaluate the reconstruction performance of source mapping methods.
29
29
 
30
30
  This class can be used to calculate the following performance metrics
31
- according to Herold and Sarradj (2017):
31
+ according :cite:`Herold2017`:
32
32
  * Specific level error
33
33
  * Overall level error
34
34
  * Inverse level error
acoular/tools/utils.py ADDED
@@ -0,0 +1,210 @@
1
+ """Utility classes intended for internal use in Acoular.
2
+
3
+ .. autosummary::
4
+ :toctree: generated/
5
+
6
+ SamplesBuffer
7
+ """
8
+
9
+ import numpy as np
10
+ from traits.api import Any, Array, Bool, Enum, Int, Property, Union
11
+
12
+ from acoular.process import InOut
13
+
14
+
15
+ class SamplesBuffer(InOut):
16
+ """Handles buffering of samples from a source.
17
+
18
+ This class is used to buffer samples from a source and provide them in blocks
19
+ of a specified size. There are several usecases for this class, as demonstrated in
20
+ the following.
21
+
22
+ Examples
23
+ --------
24
+ Let us assume we want to draw blocks of 16 samples from our source, but we want to make sure
25
+ that we always have twice the number of samples in the buffer. We can achieve this simple behaviour
26
+ by using the following code:
27
+
28
+ >>> import acoular as ac
29
+ >>> import numpy as np
30
+ >>> # create a white noise source with 512 samples
31
+ >>> source = ac.TimeSamples(
32
+ ... data=ac.WNoiseGenerator(
33
+ ... sample_freq=64,
34
+ ... numsamples=512,
35
+ ... ).signal()[:, np.newaxis],
36
+ ... sample_freq=64,
37
+ ... )
38
+ >>> # create a buffer with a size of 32 samples
39
+ >>> buffer = ac.tools.SamplesBuffer(source=source, length=32)
40
+ >>> # get the first block of 16 samples
41
+ >>> block = next(buffer.result(num=16))
42
+ >>> np.testing.assert_array_equal(block, source.data[:16])
43
+
44
+ Here, on the first call to the result method, the buffer will fill up by collecting blocks with same size
45
+ from the source. The buffer will then return the first block of 16 samples. On the next call to the result
46
+ method, the buffer will be filled again and returns the next block of 16 samples.
47
+
48
+ In some cases, we might want to draw a different number of samples from the source than we want to return.
49
+ This can be achieved by setting the `source_num` trait of the buffer. A special case is the return of a variable
50
+ number of samples. This is the case, for example, in the class :class:`~acoular.tbeamform.BeamformerTimeTraj`,
51
+ in which a different number of time samples is required from the buffer for further delay-and-sum processing
52
+ depending on the expected delay, which can be vary for moving sources. At the same time, however, only 'num'
53
+ samples should be written to and removed from the buffer. This behavior can be achieved by setting the
54
+ `shift_index_by` trait to 'num' and by setting the `result_num` trait to the number of samples that should be
55
+ returned by the result function.
56
+
57
+ >>> buffer = ac.tools.SamplesBuffer(source=source, length=32, result_num=20, shift_index_by='num')
58
+ >>> block_sizes = []
59
+ >>> block_sizes.append(
60
+ ... next(buffer.result(num=16)).shape[0]
61
+ ... ) # this time, the buffer will return 20 samples, but the buffer will only forget the first 16 samples
62
+ >>> buffer.result_num = 24
63
+ >>> block_sizes.append(
64
+ ... next(buffer.result(num=16)).shape[0]
65
+ ... ) # this time, the buffer will return 24 samples, but the buffer will only forget the first 16 samples
66
+ >>> np.testing.assert_array_equal(block_sizes, [20, 24])
67
+ """
68
+
69
+ #: number of samples that fit in the buffer
70
+ length = Int(desc='number of samples that fit in the buffer')
71
+
72
+ #: number of samples per block to obtain from the source. If 'None', use 'num' argument of result method
73
+ source_num = Union(
74
+ None,
75
+ Int(),
76
+ default_value=None,
77
+ desc='number of samples to return from the source. If "None", use "num" argument of result method',
78
+ )
79
+
80
+ #: number of samples to return from the buffer. If 'None', use 'num' argument of result method
81
+ result_num = Union(
82
+ None,
83
+ Int(),
84
+ default_value=None,
85
+ desc="number of samples to return from the buffer. If 'None', use 'num' argument of result method",
86
+ )
87
+
88
+ #: index shift value for the buffer. If "result_num", buffer will return and forget 'result_num' samples.
89
+ #: If "num", buffer will return 'result_num' samples but will forget 'num' samples
90
+ shift_index_by = Enum(
91
+ ('result_num', 'num'),
92
+ desc=(
93
+ 'index shift value for the buffer. If "result_num", use "result_num" trait.'
94
+ ' If "num", use "num" argument of result method'
95
+ ),
96
+ )
97
+
98
+ #: current filling level of buffer
99
+ level = Property(desc='current filling level of buffer')
100
+
101
+ #: data type of the buffer elements
102
+ dtype = Any(desc='data type of the buffer')
103
+
104
+ # flag to indicate that the source is empty, for internal use
105
+ _empty_source = Bool(False, desc='flag to indicate that the source is empty')
106
+
107
+ # the buffer for processing
108
+ _buffer = Array(shape=(None, None), desc='buffer for block processing')
109
+
110
+ # current index in buffer
111
+ _index = Int(desc='current index in buffer')
112
+
113
+ def _get_level(self):
114
+ return self._buffer.shape[0] - self._index
115
+
116
+ def _create_new_buffer(self):
117
+ self._buffer = np.zeros((self.length, self.numchannels), dtype=self.dtype)
118
+ self._index = self.length
119
+ self._empty_source = False
120
+
121
+ def _write_to_buffer(self, data):
122
+ ns = data.shape[0]
123
+ self._buffer[0 : (self.length - ns)] = self._buffer[-(self.length - ns) :]
124
+ self._buffer[-ns:, :] = data.astype(self.dtype)
125
+ self._index -= ns
126
+
127
+ def increase_buffer(self, num):
128
+ """Increase the buffer by 'num' samples.
129
+
130
+ Returns
131
+ -------
132
+ None
133
+ """
134
+ ar = np.zeros((num, self.numchannels), dtype=self._buffer.dtype)
135
+ self._buffer = np.concatenate((ar, self._buffer), axis=0)
136
+ self._index += num
137
+ self.length += num
138
+
139
+ def read_from_buffer(self, num):
140
+ """Read samples from the buffer.
141
+
142
+ Parameters
143
+ ----------
144
+ num : int
145
+ number of samples to read from the buffer.
146
+
147
+ Returns
148
+ -------
149
+ numpy.ndarray
150
+ block of samples from the buffer
151
+
152
+ """
153
+ rnum = num if self.result_num is None else self.result_num
154
+ rnum = rnum if self.level >= rnum else self.level
155
+ data = self._buffer[self._index : self._index + rnum]
156
+ if self.shift_index_by == 'result_num':
157
+ self._index += rnum
158
+ else:
159
+ self._index += num
160
+ return data
161
+
162
+ def fill_buffer(self, snum):
163
+ """Fill the buffer with samples from the source.
164
+
165
+ Parameters
166
+ ----------
167
+ snum : int
168
+ number of samples to return from the source.
169
+
170
+ Yields
171
+ ------
172
+ None
173
+ """
174
+ source_generator = self.source.result(snum)
175
+ while not self._empty_source:
176
+ while self._index >= snum:
177
+ if self.result_num is not None:
178
+ while self.result_num > self.length:
179
+ self.increase_buffer(snum)
180
+ try:
181
+ self._write_to_buffer(next(source_generator))
182
+ except StopIteration:
183
+ self._empty_source = True
184
+ break
185
+ yield
186
+
187
+ def result(self, num):
188
+ """Return blocks of samples from the buffer.
189
+
190
+ Parameters
191
+ ----------
192
+ num : int
193
+ number of samples to return.
194
+
195
+ Yields
196
+ ------
197
+ numpy.ndarray
198
+ block of samples from the buffer
199
+ """
200
+ self._create_new_buffer()
201
+ snum = num
202
+ if self.source_num is not None:
203
+ snum = self.source_num
204
+ for _ in self.fill_buffer(snum):
205
+ if self.level > 0:
206
+ yield self.read_from_buffer(num)
207
+ else:
208
+ break
209
+ while self.level > 0:
210
+ yield self.read_from_buffer(num)