acoular 25.7__py3-none-any.whl → 26.1__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/aiaa/aiaa.py CHANGED
@@ -27,18 +27,17 @@ Examples
27
27
 
28
28
  import contextlib
29
29
 
30
- from numpy import array
30
+ import numpy as np
31
31
  from traits.api import (
32
32
  File,
33
33
  Instance,
34
34
  Property,
35
35
  Union,
36
36
  cached_property,
37
- on_trait_change,
37
+ observe,
38
38
  property_depends_on,
39
39
  )
40
40
 
41
- from acoular.deprecation import deprecated_alias
42
41
  from acoular.h5files import H5FileBase, _get_h5file_class
43
42
  from acoular.internal import digest
44
43
  from acoular.microphones import MicGeom
@@ -85,7 +84,6 @@ class TriggerAIAABenchmark(TimeSamplesAIAABenchmark):
85
84
  (self.num_samples, self.num_channels) = self.data.shape
86
85
 
87
86
 
88
- @deprecated_alias({'name': 'file'}, removal_version='25.10')
89
87
  class CsmAIAABenchmark(PowerSpectraImport):
90
88
  """Class to load the CSM that is stored in AIAA Benchmark HDF5 file."""
91
89
 
@@ -104,7 +102,7 @@ class CsmAIAABenchmark(PowerSpectraImport):
104
102
  #: HDF5 file object
105
103
  h5f = Instance(H5FileBase, transient=True)
106
104
 
107
- # internal identifier
105
+ #: A unique identifier for the CSM importer, based on its properties. (read-only)
108
106
  digest = Property(depends_on=['basename', '_csmsum'])
109
107
 
110
108
  @cached_property
@@ -115,8 +113,8 @@ class CsmAIAABenchmark(PowerSpectraImport):
115
113
  def _get_basename(self):
116
114
  return get_file_basename(self.file)
117
115
 
118
- @on_trait_change('basename')
119
- def load_data(self):
116
+ @observe('basename')
117
+ def _load_data(self, event): # noqa ARG002
120
118
  """Open the .h5 file and set attributes."""
121
119
  if self.h5f is not None:
122
120
  with contextlib.suppress(OSError):
@@ -156,7 +154,7 @@ class CsmAIAABenchmark(PowerSpectraImport):
156
154
  ndarray
157
155
  Array of length *block_size/2+1* containing the sample frequencies.
158
156
  """
159
- return array(self.h5f.get_data_by_reference('/CsmData/binCenterFrequenciesHz')[:].flatten(), dtype=float)
157
+ return np.array(self.h5f.get_data_by_reference('/CsmData/binCenterFrequenciesHz')[:].flatten(), dtype=float)
160
158
 
161
159
 
162
160
  class MicAIAABenchmark(MicGeom):
@@ -172,8 +170,8 @@ class MicAIAABenchmark(MicGeom):
172
170
  None, File(filter=['*.h5'], exists=True), desc='name of the h5 file containing the microphone geometry'
173
171
  )
174
172
 
175
- @on_trait_change('file')
176
- def _import_mpos(self):
173
+ @observe('file')
174
+ def _import_mpos(self, event): # noqa ARG002
177
175
  """
178
176
  Import the microphone positions from .h5 file.
179
177
 
acoular/base.py CHANGED
@@ -7,6 +7,12 @@ The classes in this module are abstract base classes that provide a common inter
7
7
  that generate an output via the generator :meth:`result` in block-wise manner. They are not intended
8
8
  to be used directly, but to be subclassed by classes that implement the actual signal processing.
9
9
 
10
+ .. inheritance-diagram::
11
+ acoular.base
12
+ :top-classes:
13
+ acoular.base.Generator
14
+ :parts: 1
15
+
10
16
  .. autosummary::
11
17
  :toctree: generated/
12
18
 
@@ -32,11 +38,9 @@ from traits.api import (
32
38
  )
33
39
 
34
40
  # acoular imports
35
- from .deprecation import deprecated_alias
36
41
  from .internal import digest
37
42
 
38
43
 
39
- @deprecated_alias({'numchannels': 'num_channels', 'numsamples': 'num_samples'}, removal_version='25.10')
40
44
  class Generator(ABCHasStrictTraits):
41
45
  """Interface for any generating signal processing block.
42
46
 
@@ -51,7 +55,7 @@ class Generator(ABCHasStrictTraits):
51
55
  """
52
56
 
53
57
  #: Sampling frequency of the signal, defaults to 1.0
54
- sample_freq = Float(1.0, desc='sampling frequency')
58
+ sample_freq = Float(1.0)
55
59
 
56
60
  #: Number of signal samples
57
61
  num_samples = CInt
@@ -59,7 +63,7 @@ class Generator(ABCHasStrictTraits):
59
63
  #: Number of channels
60
64
  num_channels = CInt
61
65
 
62
- # internal identifier
66
+ #: A unique identifier for the generator, based on its properties. (read-only)
63
67
  digest = Property(depends_on=['sample_freq', 'num_samples', 'num_channels'])
64
68
 
65
69
  def _get_digest(self):
@@ -92,7 +96,7 @@ class SamplesGenerator(Generator):
92
96
 
93
97
  """
94
98
 
95
- # internal identifier
99
+ #: A unique identifier for the generator, based on its properties. (read-only)
96
100
  digest = Property(depends_on=['sample_freq', 'num_samples', 'num_channels'])
97
101
 
98
102
  def _get_digest(self):
@@ -133,7 +137,7 @@ class SpectraGenerator(Generator):
133
137
  #: The length of the block used to calculate the spectra
134
138
  block_size = CInt
135
139
 
136
- # internal identifier
140
+ #: A unique identifier for the generator, based on its properties. (read-only)
137
141
  digest = Property(depends_on=['sample_freq', 'num_samples', 'num_channels', 'num_freqs', 'block_size'])
138
142
 
139
143
  def _get_digest(self):
@@ -156,7 +160,6 @@ class SpectraGenerator(Generator):
156
160
  """
157
161
 
158
162
 
159
- @deprecated_alias({'numchannels': 'num_channels', 'numsamples': 'num_samples'}, read_only=True, removal_version='25.10')
160
163
  class TimeOut(SamplesGenerator):
161
164
  """
162
165
  Abstract base class receiving from a :attr:`source` and returning time domain signals.
@@ -178,7 +181,7 @@ class TimeOut(SamplesGenerator):
178
181
  #: Number of samples in output, as given by :attr:`source`.
179
182
  num_samples = Delegate('source')
180
183
 
181
- # internal identifier
184
+ #: A unique identifier for the generator, based on its properties. (read-only)
182
185
  digest = Property(depends_on=['source.digest'])
183
186
 
184
187
  @cached_property
@@ -204,11 +207,6 @@ class TimeOut(SamplesGenerator):
204
207
  """
205
208
 
206
209
 
207
- @deprecated_alias(
208
- {'numchannels': 'num_channels', 'numsamples': 'num_samples', 'numfreqs': 'num_freqs'},
209
- read_only=True,
210
- removal_version='25.10',
211
- )
212
210
  class SpectraOut(SpectraGenerator):
213
211
  """
214
212
  Abstract base class receiving from a :attr:`source` and returning frequency domain signals.
@@ -239,7 +237,7 @@ class SpectraOut(SpectraGenerator):
239
237
  #: The size of the block used to calculate the spectra
240
238
  block_size = Delegate('source')
241
239
 
242
- # internal identifier
240
+ #: A unique identifier for the generator, based on its properties. (read-only)
243
241
  digest = Property(depends_on=['source.digest'])
244
242
 
245
243
  @cached_property
@@ -263,7 +261,6 @@ class SpectraOut(SpectraGenerator):
263
261
  """
264
262
 
265
263
 
266
- @deprecated_alias({'numchannels': 'num_channels', 'numsamples': 'num_samples'}, read_only=True, removal_version='25.10')
267
264
  class InOut(SamplesGenerator, SpectraGenerator):
268
265
  """
269
266
  Abstract base class receiving from a :attr:`source` and returning signals in the same domain.
@@ -288,7 +285,7 @@ class InOut(SamplesGenerator, SpectraGenerator):
288
285
  #: Number of samples / snapshots in output, as given by :attr:`source`.
289
286
  num_samples = Delegate('source')
290
287
 
291
- # internal identifier
288
+ #: A unique identifier for the generator, based on its properties. (read-only)
292
289
  digest = Property(depends_on=['source.digest'])
293
290
 
294
291
  @cached_property
acoular/calib.py CHANGED
@@ -3,6 +3,12 @@
3
3
  # ------------------------------------------------------------------------------
4
4
  """Implements calibration of multichannel time signals.
5
5
 
6
+ .. inheritance-diagram::
7
+ acoular.calib
8
+ :top-classes:
9
+ acoular.base.InOut
10
+ :parts: 1
11
+
6
12
  .. autosummary::
7
13
  :toctree: generated/
8
14
 
@@ -12,19 +18,14 @@
12
18
  # imports from other packages
13
19
  import xml.dom.minidom
14
20
 
15
- from numpy import array, newaxis
16
- from traits.api import CArray, CInt, File, List, Property, Union, cached_property, on_trait_change
17
-
18
- import acoular as ac
19
-
20
- from .base import InOut
21
+ import numpy as np
22
+ from traits.api import CArray, CInt, File, List, Property, Union, cached_property, observe
21
23
 
22
24
  # acoular imports
23
- from .deprecation import deprecated_alias
25
+ from .base import InOut, SamplesGenerator, SpectraGenerator
24
26
  from .internal import digest
25
27
 
26
28
 
27
- @deprecated_alias({'from_file': 'file'}, removal_version='25.10')
28
29
  class Calib(InOut):
29
30
  """Processing block for handling calibration data in `*.xml` or NumPy format.
30
31
 
@@ -77,30 +78,30 @@ class Calib(InOut):
77
78
  """
78
79
 
79
80
  #: Name of the .xml file to be imported.
80
- file = Union(None, File(filter=['*.xml'], exists=True), desc='name of the xml file to import')
81
+ file = Union(None, File(filter=['*.xml'], exists=True))
81
82
 
82
83
  #: Number of microphones in the calibration data,
83
84
  #: is set automatically when read from file or when data is set.
84
- num_mics = CInt(0, desc='number of microphones in the geometry')
85
+ num_mics = CInt(0)
85
86
 
86
87
  #: Array of calibration factors,
87
88
  #: is set automatically when read from file.
88
89
  #: Can be set manually by specifying a NumPy array with shape (num_channels, ) if
89
90
  #: :attr:`source` yields time domain signals. For frequency domain signals, the expected
90
91
  #: shape is (num_channels * num_freqs).
91
- data = CArray(desc='calibration data')
92
+ data = CArray()
92
93
 
93
94
  #: Channels that are to be treated as invalid.
94
- invalid_channels = List(int, desc='list of invalid channels')
95
+ invalid_channels = List(int)
95
96
 
96
97
  #: Channel mask to serve as an index for all valid channels, is set automatically.
97
- channels = Property(depends_on=['invalid_channels', 'num_mics'], desc='channel mask')
98
+ channels = Property(depends_on=['invalid_channels', 'num_mics'])
98
99
 
99
- # Internal identifier
100
+ #: A unique identifier for the object, based on its properties. (read-only)
100
101
  digest = Property(depends_on=['source.digest', 'data'])
101
102
 
102
- @on_trait_change('data')
103
- def set_num_mics(self):
103
+ @observe('data')
104
+ def _update_num_mics(self, event): # noqa ARG002
104
105
  """Sets the number of microphones based on the shape of the data array."""
105
106
  self.num_mics = self.data.shape[0]
106
107
 
@@ -109,14 +110,14 @@ class Calib(InOut):
109
110
  if len(self.invalid_channels) == 0:
110
111
  return slice(0, None, None)
111
112
  allr = [i for i in range(self.num_mics) if i not in self.invalid_channels]
112
- return array(allr)
113
+ return np.array(allr)
113
114
 
114
115
  @cached_property
115
116
  def _get_digest(self):
116
117
  return digest(self)
117
118
 
118
- @on_trait_change('file')
119
- def import_data(self):
119
+ @observe('file')
120
+ def _import_data(self, event): # noqa ARG002
120
121
  """Loads the calibration data from `*.xml` file ."""
121
122
  doc = xml.dom.minidom.parse(self.file)
122
123
  names = []
@@ -124,7 +125,7 @@ class Calib(InOut):
124
125
  for element in doc.getElementsByTagName('pos'):
125
126
  names.append(element.getAttribute('Name'))
126
127
  data.append(float(element.getAttribute('factor')))
127
- self.data = array(data, 'd')
128
+ self.data = np.array(data, 'd')
128
129
  self.num_mics = self.data.shape[0]
129
130
 
130
131
  def __validate_data(self):
@@ -136,13 +137,13 @@ class Calib(InOut):
136
137
  msg = 'No source data available.'
137
138
  raise ValueError(msg)
138
139
  tobj = self.source
139
- while isinstance(tobj, ac.InOut):
140
+ while isinstance(tobj, InOut):
140
141
  tobj = tobj.source
141
- if isinstance(tobj, ac.SamplesGenerator) and (self.data[self.channels].shape[0] != tobj.num_channels):
142
+ if isinstance(tobj, SamplesGenerator) and (self.data[self.channels].shape[0] != tobj.num_channels):
142
143
  msg = f'calibration data shape {self.data[self.channels].shape[0]} does not match \
143
144
  source data shape {tobj.num_channels}'
144
145
  raise ValueError(msg)
145
- if isinstance(tobj, ac.SpectraGenerator) and (
146
+ if isinstance(tobj, SpectraGenerator) and (
146
147
  self.data[self.channels].shape[0] != tobj.num_channels * tobj.num_freqs
147
148
  ):
148
149
  msg = f'calibration data shape {self.data[self.channels].shape[0]} does not match \
@@ -170,4 +171,4 @@ class Calib(InOut):
170
171
  """
171
172
  self.__validate_data()
172
173
  for block in self.source.result(num):
173
- yield block * self.data[self.channels][newaxis]
174
+ yield block * self.data[self.channels][np.newaxis]
acoular/configuration.py CHANGED
@@ -39,9 +39,9 @@ if 'numpy' in sys.modules:
39
39
  # check if it uses OpenBLAS or another library
40
40
  if 'openblas' in temp_stdout.getvalue().lower() and environ.get('OPENBLAS_NUM_THREADS') != '1':
41
41
  # it's OpenBLAS, set numba threads=1 to avoid overcommittment
42
- import numba
42
+ import numba as nb
43
43
 
44
- numba.set_num_threads(1)
44
+ nb.set_num_threads(1)
45
45
  warn(
46
46
  'We detected that Numpy is already loaded and uses OpenBLAS. Because '
47
47
  'this conflicts with Numba parallel execution, we disable parallel '
acoular/demo/__init__.py CHANGED
@@ -5,15 +5,103 @@
5
5
 
6
6
  .. autosummary::
7
7
  :toctree: generated/
8
+ """
8
9
 
9
- acoular_demo
10
10
 
11
- """
11
+ def create_three_sources(mg, h5savefile='three_sources.h5'):
12
+ """
13
+ Create three noise sources and return them as Mixer.
14
+
15
+ Alias for :func:`create_three_sources_2d`.
16
+ """
17
+ return create_three_sources_2d(mg, h5savefile=h5savefile)
18
+
19
+
20
+ def _create_three_sources(mg, locs, h5savefile='', sfreq=51200, duration=1):
21
+ """Create three noise sources with custom locations and return them as Mixer."""
22
+ import acoular as ac
23
+
24
+ nsamples = duration * sfreq
25
+
26
+ n1 = ac.WNoiseGenerator(sample_freq=sfreq, num_samples=nsamples, seed=1)
27
+ n2 = ac.WNoiseGenerator(sample_freq=sfreq, num_samples=nsamples, seed=2, rms=0.7)
28
+ n3 = ac.WNoiseGenerator(sample_freq=sfreq, num_samples=nsamples, seed=3, rms=0.5)
29
+
30
+ noises = [n1, n2, n3]
31
+ ps = [ac.PointSource(signal=n, mics=mg, loc=loc) for n, loc in list(zip(noises, locs, strict=True))]
32
+ pa = ac.Mixer(source=ps[0], sources=ps[1:])
33
+
34
+ if h5savefile:
35
+ wh5 = ac.WriteH5(source=pa, file=h5savefile)
36
+ wh5.save()
37
+ return pa
38
+
39
+
40
+ def create_three_sources_1d(mg, h5savefile='three_sources_1d.h5'):
41
+ """Create three noise sources on a 1D line and return them as Mixer."""
42
+ locs = [(-0.1, 0, -0.3), (0.15, 0, -0.3), (0, 0, -0.3)]
43
+ return _create_three_sources(mg, locs, h5savefile=h5savefile)
44
+
45
+
46
+ def create_three_sources_2d(mg, h5savefile='three_sources_2d.h5'):
47
+ """Create three noise sources in a 2D plane and return them as Mixer."""
48
+ locs = [(-0.1, -0.1, -0.3), (0.15, 0, -0.3), (0, 0.1, -0.3)]
49
+ return _create_three_sources(mg, locs, h5savefile=h5savefile)
50
+
51
+
52
+ def create_three_sources_3d(mg, h5savefile='three_sources_3d.h5'):
53
+ """Create three noise sources in 3D space and return them as Mixer."""
54
+ locs = [(-0.1, -0.1, -0.3), (0.15, 0, -0.17), (0, 0.1, -0.25)]
55
+ return _create_three_sources(mg, locs, h5savefile=h5savefile)
56
+
57
+
58
+ def run():
59
+ """Run the Acoular demo."""
60
+ from pathlib import Path
61
+
62
+ import acoular as ac
63
+
64
+ ac.config.global_caching = 'none'
65
+
66
+ # set up microphone geometry
67
+
68
+ micgeofile = Path(ac.__file__).parent / 'xml' / 'array_64.xml'
69
+ mg = ac.MicGeom(file=micgeofile)
70
+
71
+ # generate test data, in real life this would come from an array measurement
72
+
73
+ pa = create_three_sources(mg)
74
+
75
+ # analyze the data and generate map
76
+
77
+ ps = ac.PowerSpectra(source=pa, block_size=128, window='Hanning')
78
+
79
+ rg = ac.RectGrid(x_min=-0.2, x_max=0.2, y_min=-0.2, y_max=0.2, z=-0.3, increment=0.01)
80
+ st = ac.SteeringVector(grid=rg, mics=mg)
81
+
82
+ bb = ac.BeamformerBase(freq_data=ps, steer=st)
83
+ pm = bb.synthetic(8000, 3)
84
+ spl = ac.L_p(pm)
85
+
86
+ if ac.config.have_matplotlib:
87
+ from matplotlib.pyplot import axis, colorbar, figure, imshow, plot, show
88
+
89
+ # show map
90
+ imshow(spl.T, origin='lower', vmin=spl.max() - 10, extent=rg.extent, interpolation='bicubic')
91
+ colorbar()
92
+
93
+ # plot microphone geometry
94
+ figure(2)
95
+ plot(mg.pos[0], mg.pos[1], 'o')
96
+ axis('equal')
97
+
98
+ show()
12
99
 
13
- from . import acoular_demo
14
- from .acoular_demo import (
15
- create_three_sources,
16
- create_three_sources_1d,
17
- create_three_sources_2d,
18
- create_three_sources_3d,
19
- )
100
+ else:
101
+ print('Matplotlib not found! Please install matplotlib if you want to plot the results.')
102
+ print('For consolation we do an ASCII map plot of the results here.')
103
+ grayscale = '@%#*+=-:. '[::-1]
104
+ ind = ((spl.T - spl.max() + 9).clip(0, 9)).astype(int)[::-1]
105
+ print(78 * '-')
106
+ print('|\n'.join([' '.join(['|'] + [grayscale[i] for i in row[2:-1]]) for row in ind]) + '|')
107
+ print(7 * '-', ''.join([f'{grayscale[i]}={int(spl.max()) - 9 + i}dB ' for i in range(1, 10)]), 6 * '-')
@@ -0,0 +1,37 @@
1
+ # ------------------------------------------------------------------------------
2
+ # Copyright (c) Acoular Development Team.
3
+ # ------------------------------------------------------------------------------
4
+ """Demo for Acoular.
5
+
6
+ To run the demo, execute the following commands:
7
+
8
+ .. code-block:: python
9
+
10
+ import acoular
11
+
12
+ acoular.demo.run()
13
+
14
+
15
+ Generates a test data set for three sources, analyzes them and generates a
16
+ map of the three sources.
17
+
18
+ The simulation generates the sound pressure at 64 microphones that are
19
+ arrangend in the 'array64' geometry, which is part of the package. The sound
20
+ pressure signals are sampled at 51200 Hz for a duration of 1 second.
21
+
22
+ Source location (relative to array center) and RMS in 1 m distance:
23
+
24
+ ====== =============== ======
25
+ Source Location RMS
26
+ ====== =============== ======
27
+ 1 (-0.1,-0.1,0.3) 1.0 Pa
28
+ 2 (0.15,0,0.3) 0.7 Pa
29
+ 3 (0,0.1,0.3) 0.5 Pa
30
+ ====== =============== ======
31
+
32
+ """
33
+
34
+ if __name__ == '__main__':
35
+ from . import run
36
+
37
+ run()