acoular 24.5__py3-none-any.whl → 24.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- acoular/__init__.py +17 -11
- acoular/base.py +312 -0
- acoular/configuration.py +23 -16
- acoular/demo/acoular_demo.py +28 -35
- acoular/environments.py +20 -15
- acoular/fastFuncs.py +40 -40
- acoular/fbeamform.py +1100 -1130
- acoular/fprocess.py +368 -0
- acoular/grids.py +36 -22
- acoular/h5cache.py +34 -34
- acoular/h5files.py +13 -13
- acoular/internal.py +3 -3
- acoular/process.py +464 -0
- acoular/sdinput.py +24 -4
- acoular/signals.py +20 -6
- acoular/sources.py +140 -56
- acoular/spectra.py +34 -53
- acoular/tbeamform.py +264 -142
- acoular/tfastfuncs.py +17 -18
- acoular/tools/__init__.py +2 -0
- acoular/tools/aiaa.py +7 -8
- acoular/tools/helpers.py +2 -2
- acoular/tools/metrics.py +1 -1
- acoular/tools/utils.py +210 -0
- acoular/tprocess.py +168 -532
- acoular/traitsviews.py +5 -3
- acoular/version.py +2 -2
- {acoular-24.5.dist-info → acoular-24.10.dist-info}/METADATA +49 -8
- acoular-24.10.dist-info/RECORD +54 -0
- {acoular-24.5.dist-info → acoular-24.10.dist-info}/WHEEL +1 -1
- acoular-24.5.dist-info/RECORD +0 -50
- {acoular-24.5.dist-info → acoular-24.10.dist-info}/licenses/AUTHORS.rst +0 -0
- {acoular-24.5.dist-info → acoular-24.10.dist-info}/licenses/LICENSE +0 -0
acoular/tfastfuncs.py
CHANGED
|
@@ -6,9 +6,6 @@
|
|
|
6
6
|
import numba as nb
|
|
7
7
|
import numpy as np
|
|
8
8
|
|
|
9
|
-
cachedOption = True # if True: saves the numba func as compiled func in sub directory
|
|
10
|
-
fastOption = True # fastmath options
|
|
11
|
-
|
|
12
9
|
|
|
13
10
|
@nb.njit(
|
|
14
11
|
[
|
|
@@ -46,11 +43,11 @@ def _delayandsum4(data, offsets, ifactor2, steeramp, out, autopower):
|
|
|
46
43
|
"""
|
|
47
44
|
gridsize, numchannels = offsets.shape
|
|
48
45
|
num = out.shape[0]
|
|
49
|
-
|
|
46
|
+
zero_constant = data.dtype.type(0.0)
|
|
50
47
|
for n in nb.prange(num):
|
|
51
48
|
for gi in nb.prange(gridsize):
|
|
52
|
-
out[n, gi] =
|
|
53
|
-
autopower[n, gi] =
|
|
49
|
+
out[n, gi] = zero_constant
|
|
50
|
+
autopower[n, gi] = zero_constant
|
|
54
51
|
for mi in range(numchannels):
|
|
55
52
|
ind = (gi, mi)
|
|
56
53
|
r = (
|
|
@@ -105,14 +102,16 @@ def _delayandsum5(data, offsets, ifactor2, steeramp, out, autopower):
|
|
|
105
102
|
num, gridsize, numchannels = offsets.shape
|
|
106
103
|
num = out.shape[0]
|
|
107
104
|
# ZERO = data.dtype.type(0.)
|
|
108
|
-
|
|
105
|
+
one_constant = data.dtype.type(1.0)
|
|
109
106
|
for n in nb.prange(num):
|
|
110
107
|
for gi in nb.prange(gridsize):
|
|
111
108
|
out[n, gi] = 0
|
|
112
109
|
autopower[n, gi] = 0
|
|
113
110
|
for mi in range(numchannels):
|
|
114
111
|
ind = offsets[n, gi, mi] + n
|
|
115
|
-
r = (
|
|
112
|
+
r = (
|
|
113
|
+
data[ind, mi] * (one_constant - ifactor2[n, gi, mi]) + data[ind + 1, mi] * ifactor2[n, gi, mi]
|
|
114
|
+
) * steeramp[
|
|
116
115
|
n,
|
|
117
116
|
gi,
|
|
118
117
|
mi,
|
|
@@ -130,14 +129,14 @@ def _delayandsum5(data, offsets, ifactor2, steeramp, out, autopower):
|
|
|
130
129
|
parallel=True,
|
|
131
130
|
fastmath=True,
|
|
132
131
|
)
|
|
133
|
-
def _steer_I(rm, r0, amp): # noqa: ARG001
|
|
132
|
+
def _steer_I(rm, r0, amp): # noqa: ARG001, N802
|
|
134
133
|
num, gridsize, numchannels = rm.shape
|
|
135
134
|
amp[0, 0, 0] = 1.0 / numchannels # to get the same type for rm2 as for rm
|
|
136
|
-
|
|
135
|
+
nr = amp[0, 0, 0]
|
|
137
136
|
for n in nb.prange(num):
|
|
138
137
|
for gi in nb.prange(gridsize):
|
|
139
138
|
for mi in nb.prange(numchannels):
|
|
140
|
-
amp[n, gi, mi] =
|
|
139
|
+
amp[n, gi, mi] = nr
|
|
141
140
|
|
|
142
141
|
|
|
143
142
|
@nb.njit(
|
|
@@ -149,13 +148,13 @@ def _steer_I(rm, r0, amp): # noqa: ARG001
|
|
|
149
148
|
parallel=True,
|
|
150
149
|
fastmath=True,
|
|
151
150
|
)
|
|
152
|
-
def _steer_II(rm, r0, amp):
|
|
151
|
+
def _steer_II(rm, r0, amp): # noqa: N802
|
|
153
152
|
num, gridsize, numchannels = rm.shape
|
|
154
153
|
amp[0, 0, 0] = 1.0 / numchannels # to get the same type for rm2 as for rm
|
|
155
|
-
|
|
154
|
+
nr = amp[0, 0, 0]
|
|
156
155
|
for n in nb.prange(num):
|
|
157
156
|
for gi in nb.prange(gridsize):
|
|
158
|
-
rm2 = np.divide(
|
|
157
|
+
rm2 = np.divide(nr, r0[n, gi])
|
|
159
158
|
for mi in nb.prange(numchannels):
|
|
160
159
|
amp[n, gi, mi] = rm[n, gi, mi] * rm2
|
|
161
160
|
|
|
@@ -169,7 +168,7 @@ def _steer_II(rm, r0, amp):
|
|
|
169
168
|
parallel=True,
|
|
170
169
|
fastmath=True,
|
|
171
170
|
)
|
|
172
|
-
def _steer_III(rm, r0, amp):
|
|
171
|
+
def _steer_III(rm, r0, amp): # noqa: N802
|
|
173
172
|
num, gridsize, numchannels = rm.shape
|
|
174
173
|
rm20 = rm[0, 0, 0] - rm[0, 0, 0] # to get the same type for rm2 as for rm
|
|
175
174
|
rm1 = rm[0, 0, 0] / rm[0, 0, 0]
|
|
@@ -192,10 +191,10 @@ def _steer_III(rm, r0, amp):
|
|
|
192
191
|
parallel=True,
|
|
193
192
|
fastmath=True,
|
|
194
193
|
)
|
|
195
|
-
def _steer_IV(rm, r0, amp): # noqa: ARG001
|
|
194
|
+
def _steer_IV(rm, r0, amp): # noqa: ARG001, N802
|
|
196
195
|
num, gridsize, numchannels = rm.shape
|
|
197
196
|
amp[0, 0, 0] = np.sqrt(1.0 / numchannels) # to get the same type for rm2 as for rm
|
|
198
|
-
|
|
197
|
+
nr = amp[0, 0, 0]
|
|
199
198
|
rm1 = rm[0, 0, 0] / rm[0, 0, 0]
|
|
200
199
|
rm20 = rm[0, 0, 0] - rm[0, 0, 0] # to get the same type for rm2 as for rm
|
|
201
200
|
for n in nb.prange(num):
|
|
@@ -205,7 +204,7 @@ def _steer_IV(rm, r0, amp): # noqa: ARG001
|
|
|
205
204
|
rm2 += np.divide(rm1, np.square(rm[n, gi, mi]))
|
|
206
205
|
rm2 = np.sqrt(rm2)
|
|
207
206
|
for mi in nb.prange(numchannels):
|
|
208
|
-
amp[n, gi, mi] = np.divide(
|
|
207
|
+
amp[n, gi, mi] = np.divide(nr, rm[n, gi, mi] * rm2)
|
|
209
208
|
|
|
210
209
|
|
|
211
210
|
@nb.njit(
|
acoular/tools/__init__.py
CHANGED
acoular/tools/aiaa.py
CHANGED
|
@@ -12,8 +12,8 @@ the framework.
|
|
|
12
12
|
|
|
13
13
|
Examples
|
|
14
14
|
--------
|
|
15
|
-
>>> micgeom = MicAIAABenchmark(name='some_benchmarkdata.h5')
|
|
16
|
-
>>> timedata = TimeSamplesAIAABenchmark(name='some_benchmarkdata.h5')
|
|
15
|
+
>>> micgeom = MicAIAABenchmark(name='some_benchmarkdata.h5') # doctest: +SKIP
|
|
16
|
+
>>> timedata = TimeSamplesAIAABenchmark(name='some_benchmarkdata.h5') # doctest: +SKIP
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
.. autosummary::
|
|
@@ -25,6 +25,7 @@ Examples
|
|
|
25
25
|
MicAIAABenchmark
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
|
+
import contextlib
|
|
28
29
|
from os import path
|
|
29
30
|
|
|
30
31
|
from numpy import array
|
|
@@ -53,13 +54,13 @@ class TimeSamplesAIAABenchmark(TimeSamples):
|
|
|
53
54
|
objects.
|
|
54
55
|
"""
|
|
55
56
|
|
|
56
|
-
def
|
|
57
|
+
def _load_timedata(self):
|
|
57
58
|
"""Loads timedata from .h5 file. Only for internal use."""
|
|
58
59
|
self.data = self.h5f.get_data_by_reference('MicrophoneData/microphoneDataPa')
|
|
59
60
|
self.sample_freq = self.h5f.get_node_attribute(self.data, 'sampleRateHz')
|
|
60
61
|
(self.numsamples, self.numchannels) = self.data.shape
|
|
61
62
|
|
|
62
|
-
def
|
|
63
|
+
def _load_metadata(self):
|
|
63
64
|
"""Loads metadata from .h5 file. Only for internal use."""
|
|
64
65
|
self.metadata = {}
|
|
65
66
|
if '/MetaData' in self.h5f:
|
|
@@ -75,7 +76,7 @@ class TriggerAIAABenchmark(TimeSamplesAIAABenchmark):
|
|
|
75
76
|
and and provides information about this data.
|
|
76
77
|
"""
|
|
77
78
|
|
|
78
|
-
def
|
|
79
|
+
def _load_timedata(self):
|
|
79
80
|
"""Loads timedata from .h5 file. Only for internal use."""
|
|
80
81
|
self.data = self.h5f.get_data_by_reference('TachoData/tachoDataV')
|
|
81
82
|
self.sample_freq = self.h5f.get_node_attribute(self.data, 'sampleRateHz')
|
|
@@ -118,10 +119,8 @@ class CsmAIAABenchmark(PowerSpectraImport):
|
|
|
118
119
|
# no file there
|
|
119
120
|
raise OSError('No such file: %s' % self.name)
|
|
120
121
|
if self.h5f is not None:
|
|
121
|
-
|
|
122
|
+
with contextlib.suppress(OSError):
|
|
122
123
|
self.h5f.close()
|
|
123
|
-
except OSError:
|
|
124
|
-
pass
|
|
125
124
|
file = _get_h5file_class()
|
|
126
125
|
self.h5f = file(self.name)
|
|
127
126
|
|
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.
|
|
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.
|
|
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
|
|
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)
|