acoular 25.1__py3-none-any.whl → 25.3.post1__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/microphones.py CHANGED
@@ -1,13 +1,13 @@
1
1
  # ------------------------------------------------------------------------------
2
2
  # Copyright (c) Acoular Development Team.
3
3
  # ------------------------------------------------------------------------------
4
- """Implements support for array microphone arrangements.
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
- """Provides the geometric arrangement of microphones in the array.
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
- The geometric arrangement of microphones is read in from an
39
- xml-source with element tag names `pos` and attributes Name, `x`, `y` and `z`.
40
- Can also be used with programmatically generated arrangements.
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
- #: Name of the .xml-file from which to read the data.
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
- #: Positions as (3, :attr:`num_mics`) array of floats, may include also invalid
47
- #: microphones (if any). Set either automatically on change of the
48
- #: :attr:`file` argument or explicitly by assigning an array of floats.
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
- #: Positions as (3, :attr:`num_mics`) array of floats, without invalid
52
- #: microphones; readonly.
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 that gives the indices of channels that should not be considered.
56
- #: Defaults to a blank list.
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 used microphones in the array; readonly.
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
- #: Center of the array (arithmetic mean of all used array positions); readonly.
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
- #: Aperture of the array (greatest extent between two microphones); readonly.
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
- # internal identifier
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 import_mpos(self):
103
- """Import the microphone positions from .xml file.
104
- Called when :attr:`file` changes.
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
- """Export the microphone positions to .xml file.
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
- Name of the file to which the microphone positions are written.
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