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/microphones.py
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# ------------------------------------------------------------------------------
|
|
2
2
|
# Copyright (c) Acoular Development Team.
|
|
3
3
|
# ------------------------------------------------------------------------------
|
|
4
|
-
"""
|
|
4
|
+
"""
|
|
5
|
+
Implements support for array microphone arrangements.
|
|
5
6
|
|
|
6
7
|
.. autosummary::
|
|
7
8
|
:toctree: generated/
|
|
8
9
|
|
|
9
10
|
MicGeom
|
|
10
|
-
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
13
|
# imports from other packages
|
|
@@ -33,39 +33,127 @@ from .internal import digest
|
|
|
33
33
|
|
|
34
34
|
@deprecated_alias({'mpos_tot': 'pos_total', 'mpos': 'pos', 'from_file': 'file'}, read_only=['mpos'])
|
|
35
35
|
class MicGeom(HasStrictTraits):
|
|
36
|
-
"""
|
|
36
|
+
"""
|
|
37
|
+
Provide the geometric arrangement of microphones in an array.
|
|
38
|
+
|
|
39
|
+
This class allows you to define, import, and manage the spatial positions of microphones in a
|
|
40
|
+
microphone array. The positions can be read from an XML file or set programmatically. Invalid
|
|
41
|
+
microphones can be excluded by specifying their indices via :attr:`invalid_channels`.
|
|
42
|
+
|
|
43
|
+
Notes
|
|
44
|
+
-----
|
|
45
|
+
- The microphone geometry as in :attr:`total_pos` is automatically changed if the :attr:`file`
|
|
46
|
+
attribute is updated.
|
|
47
|
+
- Small numerical values in the computed :attr:`center` are set to zero for numerical stability.
|
|
48
|
+
|
|
49
|
+
Examples
|
|
50
|
+
--------
|
|
51
|
+
To set a microphone geomerty for ``n`` programmatically, first a ``(3,n)`` array is needed. In
|
|
52
|
+
this case we'll use ``n=9`` and generate an array containing the positional data.
|
|
53
|
+
|
|
54
|
+
>>> import numpy as np
|
|
55
|
+
>>>
|
|
56
|
+
>>> # Generate a (3,3) grid of points in the x-y plane
|
|
57
|
+
>>> x = np.linspace(-1, 1, 3) # Generate 3 points for x, from -1 to 1
|
|
58
|
+
>>> y = np.linspace(-1, 1, 3) # Generate 3 points for y, from -1 to 1
|
|
59
|
+
>>>
|
|
60
|
+
>>> # Create a meshgrid for 3D coordinates, with z=0 for all points
|
|
61
|
+
>>> X, Y = np.meshgrid(x, y)
|
|
62
|
+
>>> Z = np.zeros_like(X) # Set all z-values to 0
|
|
63
|
+
>>>
|
|
64
|
+
>>> # Stack the coordinates into a single (3,9) array
|
|
65
|
+
>>> points = np.vstack([X.ravel(), Y.ravel(), Z.ravel()])
|
|
66
|
+
>>> points
|
|
67
|
+
array([[-1., 0., 1., -1., 0., 1., -1., 0., 1.],
|
|
68
|
+
[-1., -1., -1., 0., 0., 0., 1., 1., 1.],
|
|
69
|
+
[ 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
|
|
70
|
+
|
|
71
|
+
Now, to implement this array as a microphone geomertry, create a :class:`MicGeom` object and
|
|
72
|
+
assign the array to it the by using the :attr:`pos_total` attribute:
|
|
73
|
+
|
|
74
|
+
>>> from acoular import MicGeom
|
|
75
|
+
>>> mg = MicGeom(pos_total=points)
|
|
76
|
+
>>> mg.pos
|
|
77
|
+
array([[-1., 0., 1., -1., 0., 1., -1., 0., 1.],
|
|
78
|
+
[-1., -1., -1., 0., 0., 0., 1., 1., 1.],
|
|
79
|
+
[ 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
|
|
80
|
+
|
|
81
|
+
The microphones along the diagonal can be removed by setting their indices in the
|
|
82
|
+
:attr:`invalid_channels` attribute:
|
|
83
|
+
|
|
84
|
+
>>> mg.invalid_channels = [0, 4, 9]
|
|
85
|
+
>>> mg.pos
|
|
86
|
+
array([[ 0., 1., -1., 1., -1., 0., 1.],
|
|
87
|
+
[-1., -1., 0., 0., 1., 1., 1.],
|
|
88
|
+
[ 0., 0., 0., 0., 0., 0., 0.]])
|
|
89
|
+
|
|
90
|
+
But they will still be included in :attr:`pos_total`:
|
|
91
|
+
|
|
92
|
+
>>> mg.pos_total
|
|
93
|
+
array([[-1., 0., 1., -1., 0., 1., -1., 0., 1.],
|
|
94
|
+
[-1., -1., -1., 0., 0., 0., 1., 1., 1.],
|
|
95
|
+
[ 0., 0., 0., 0., 0., 0., 0., 0., 0.]])
|
|
37
96
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
97
|
+
To export this microphone geometry, use the :meth:`export_mpos` method. Note that the
|
|
98
|
+
microphones marked as invalid in :attr:`invalid_channels` will not be exported.
|
|
99
|
+
|
|
100
|
+
>>> mg.export_mpos('micgeom.xml') # doctest: +SKIP
|
|
101
|
+
|
|
102
|
+
The newly generated ``micgeom.xml`` file looks like this:
|
|
103
|
+
|
|
104
|
+
.. code-block:: xml
|
|
105
|
+
|
|
106
|
+
<?xml version="1.1" encoding="utf-8"?><MicArray name="micgeom">
|
|
107
|
+
<pos Name="Point 1" x="0.0" y="-1.0" z="0.0"/>
|
|
108
|
+
<pos Name="Point 2" x="1.0" y="-1.0" z="0.0"/>
|
|
109
|
+
<pos Name="Point 3" x="-1.0" y="0.0" z="0.0"/>
|
|
110
|
+
<pos Name="Point 4" x="1.0" y="0.0" z="0.0"/>
|
|
111
|
+
<pos Name="Point 5" x="-1.0" y="1.0" z="0.0"/>
|
|
112
|
+
<pos Name="Point 6" x="0.0" y="1.0" z="0.0"/>
|
|
113
|
+
<pos Name="Point 7" x="1.0" y="1.0" z="0.0"/>
|
|
114
|
+
</MicArray>
|
|
115
|
+
|
|
116
|
+
Note that when importing a microphone geometry, the XML file needs to look similar to this one:
|
|
117
|
+
There must be ``<pos>`` elements with ``Name``, ``x``, ``y``, and ``z`` attributes.
|
|
118
|
+
|
|
119
|
+
To load this same file as a new :class:`MicGeom` object, the ``micgeom.xml`` file can be
|
|
120
|
+
assigned to the :attr:`file` attribute:
|
|
121
|
+
|
|
122
|
+
>>> new_mg = MicGeom(file='micgeom.xml') # doctest: +SKIP
|
|
123
|
+
>>> new_mg.pos # doctest: +SKIP
|
|
124
|
+
array([[ 0., 1., -1., 1., -1., 0., 1.],
|
|
125
|
+
[-1., -1., 0., 0., 1., 1., 1.],
|
|
126
|
+
[ 0., 0., 0., 0., 0., 0., 0.]])
|
|
41
127
|
"""
|
|
42
128
|
|
|
43
|
-
#:
|
|
129
|
+
#: Path to the XML file containing microphone positions. The XML file should have elements with
|
|
130
|
+
#: the tag ``pos`` and attributes ``Name``, ``x``, ``y``, and ``z``.
|
|
44
131
|
file = File(filter=['*.xml'], exists=True, desc='name of the xml file to import')
|
|
45
132
|
|
|
46
|
-
#:
|
|
47
|
-
#:
|
|
48
|
-
#:
|
|
133
|
+
#: Array containing the ``x, y, z`` positions of all microphones, including invalid ones, shape
|
|
134
|
+
#: ``(3,`` :attr:`num_mics` ``)``. This is set automatically when :attr:`file` changes or
|
|
135
|
+
#: explicitly by assigning an array of floats.
|
|
49
136
|
pos_total = CArray(dtype=float, shape=(3, None), desc='x, y, z position of all microphones')
|
|
50
137
|
|
|
51
|
-
#:
|
|
52
|
-
#:
|
|
138
|
+
#: Array containing the ``x, y, z`` positions of valid microphones (i.e., excluding those in
|
|
139
|
+
#: :attr:`invalid_channels`), shape ``(3,`` :attr:`num_mics` ``)``. (read-only)
|
|
53
140
|
pos = Property(depends_on=['pos_total', 'invalid_channels'], desc='x, y, z position of used microphones')
|
|
54
141
|
|
|
55
|
-
#: List
|
|
56
|
-
#:
|
|
142
|
+
#: List of indices indicating microphones to be excluded from calculations and results.
|
|
143
|
+
#: Default is ``[]``.
|
|
57
144
|
invalid_channels = List(int, desc='list of invalid channels')
|
|
58
145
|
|
|
59
|
-
#: Number of
|
|
146
|
+
#: Number of valid microphones in the array. (read-only)
|
|
60
147
|
num_mics = Property(depends_on=['pos'], desc='number of microphones in the geometry')
|
|
61
148
|
|
|
62
|
-
#:
|
|
149
|
+
#: The geometric center of the array, calculated as the arithmetic mean of the positions of all
|
|
150
|
+
#: valid microphones. (read-only)
|
|
63
151
|
center = Property(depends_on=['pos'], desc='array center')
|
|
64
152
|
|
|
65
|
-
#:
|
|
153
|
+
#: The maximum distance between any two valid microphones in the array. (read-only)
|
|
66
154
|
aperture = Property(depends_on=['pos'], desc='array aperture')
|
|
67
155
|
|
|
68
|
-
|
|
156
|
+
#: A unique identifier for the geometry, based on its properties. (read-only)
|
|
69
157
|
digest = Property(depends_on=['pos'])
|
|
70
158
|
|
|
71
159
|
@cached_property
|
|
@@ -99,10 +187,34 @@ class MicGeom(HasStrictTraits):
|
|
|
99
187
|
return None
|
|
100
188
|
|
|
101
189
|
@on_trait_change('file')
|
|
102
|
-
def
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
190
|
+
def _import_mpos(self):
|
|
191
|
+
# Import the microphone positions from an XML file.
|
|
192
|
+
#
|
|
193
|
+
# This method parses the XML file specified in :attr:`file` and extracts the ``x``, ``y``,
|
|
194
|
+
# and ``z`` positions of microphones. The data is stored in :attr:`pos_total` attribute as
|
|
195
|
+
# an array of shape ``(3,`` :attr:`num_mics` ``)``.
|
|
196
|
+
#
|
|
197
|
+
# This method is called when :attr:`file` changes.
|
|
198
|
+
#
|
|
199
|
+
# Raises
|
|
200
|
+
# ------
|
|
201
|
+
# xml.parsers.expat.ExpatError
|
|
202
|
+
# If the XML file is malformed or cannot be parsed.
|
|
203
|
+
# ValueError
|
|
204
|
+
# If the attributes ``x``, ``y``, or ``z`` in any ``<pos>`` element are missing or
|
|
205
|
+
# cannot be converted to a float.
|
|
206
|
+
#
|
|
207
|
+
# Examples
|
|
208
|
+
# --------
|
|
209
|
+
# The microphone geometry changes by changing the :attr:`file` attribute.
|
|
210
|
+
#
|
|
211
|
+
# >>> from acoular import MicGeom # doctest: +SKIP
|
|
212
|
+
# >>> mg = MicGeom(file='/path/to/geom1.xml') # doctest: +SKIP
|
|
213
|
+
# >>> mg.center # doctest: +SKIP
|
|
214
|
+
# array([-0.25, 0. , 0.25]) # doctest: +SKIP
|
|
215
|
+
# >>> mg.file = '/path/to/geom2.xml' # doctest: +SKIP
|
|
216
|
+
# >>> mg.center # doctest: +SKIP
|
|
217
|
+
# array([0. , 0.33333333, 0.66666667]) # doctest: +SKIP
|
|
106
218
|
doc = xml.dom.minidom.parse(self.file)
|
|
107
219
|
names = []
|
|
108
220
|
xyz = []
|
|
@@ -112,12 +224,32 @@ class MicGeom(HasStrictTraits):
|
|
|
112
224
|
self.pos_total = array(xyz, 'd').swapaxes(0, 1)
|
|
113
225
|
|
|
114
226
|
def export_mpos(self, filename):
|
|
115
|
-
"""
|
|
227
|
+
"""
|
|
228
|
+
Export the microphone positions to an XML file.
|
|
229
|
+
|
|
230
|
+
This method generates an XML file containing the positions of all valid microphones in the
|
|
231
|
+
array. Each microphone is represented by a ``<pos>`` element with ``Name``, ``x``, ``y``,
|
|
232
|
+
and ``z`` attributes. The generated XML is formatted to match the structure required for
|
|
233
|
+
importing into the :class:`MicGeom` class.
|
|
116
234
|
|
|
117
235
|
Parameters
|
|
118
236
|
----------
|
|
119
|
-
filename : str
|
|
120
|
-
|
|
237
|
+
filename : :class:`str`
|
|
238
|
+
The path to the file to which the microphone positions will be written. The file
|
|
239
|
+
extension must be ``.xml``.
|
|
240
|
+
|
|
241
|
+
Raises
|
|
242
|
+
------
|
|
243
|
+
:obj:`OSError`
|
|
244
|
+
If the file cannot be written due to permissions issues or invalid file paths.
|
|
245
|
+
|
|
246
|
+
Notes
|
|
247
|
+
-----
|
|
248
|
+
- The file will be saved in UTF-8 encoding.
|
|
249
|
+
- The ``Name`` attribute for each microphone is set as ``"Point {i+1}"``, where ``i`` is the
|
|
250
|
+
index of the microphone.
|
|
251
|
+
- This method only exports the positions of the valid microphones (those not listed in
|
|
252
|
+
:attr:`invalid_channels`).
|
|
121
253
|
"""
|
|
122
254
|
filepath = Path(filename)
|
|
123
255
|
basename = filepath.stem
|