acoular 25.7__py3-none-any.whl → 25.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/aiaa/aiaa.py +7 -9
- acoular/base.py +6 -9
- acoular/calib.py +19 -18
- acoular/configuration.py +2 -2
- acoular/environments.py +102 -113
- acoular/fbeamform.py +296 -301
- acoular/fprocess.py +7 -4
- acoular/grids.py +98 -111
- acoular/h5cache.py +5 -1
- acoular/h5files.py +96 -9
- acoular/microphones.py +22 -27
- acoular/process.py +7 -11
- acoular/sdinput.py +0 -5
- acoular/signals.py +29 -27
- acoular/sources.py +189 -322
- acoular/spectra.py +33 -44
- acoular/tbeamform.py +217 -199
- acoular/tools/helpers.py +25 -33
- acoular/tools/metrics.py +5 -10
- acoular/tprocess.py +173 -209
- acoular/trajectory.py +5 -5
- acoular/version.py +2 -2
- {acoular-25.7.dist-info → acoular-25.10.dist-info}/METADATA +6 -2
- {acoular-25.7.dist-info → acoular-25.10.dist-info}/RECORD +27 -27
- {acoular-25.7.dist-info → acoular-25.10.dist-info}/WHEEL +0 -0
- {acoular-25.7.dist-info → acoular-25.10.dist-info}/licenses/AUTHORS.rst +0 -0
- {acoular-25.7.dist-info → acoular-25.10.dist-info}/licenses/LICENSE +0 -0
acoular/fbeamform.py
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
# ------------------------------------------------------------------------------
|
|
2
2
|
# Copyright (c) Acoular Development Team.
|
|
3
3
|
# ------------------------------------------------------------------------------
|
|
4
|
-
"""
|
|
4
|
+
"""
|
|
5
|
+
Implements beamformers in the frequency domain.
|
|
6
|
+
|
|
7
|
+
.. inheritance-diagram::
|
|
8
|
+
acoular.fbeamform.BeamformerBase
|
|
9
|
+
:include-subclasses:
|
|
10
|
+
:top-classes:
|
|
11
|
+
acoular.grids.Grid,
|
|
12
|
+
acoular.fbeamform.BeamformerBase
|
|
13
|
+
:parts: 1
|
|
5
14
|
|
|
6
15
|
.. autosummary::
|
|
7
16
|
:toctree: generated/
|
|
@@ -28,7 +37,6 @@
|
|
|
28
37
|
PointSpreadFunction
|
|
29
38
|
L_p
|
|
30
39
|
integrate
|
|
31
|
-
|
|
32
40
|
"""
|
|
33
41
|
|
|
34
42
|
# imports from other packages
|
|
@@ -36,53 +44,12 @@
|
|
|
36
44
|
import warnings
|
|
37
45
|
from warnings import warn
|
|
38
46
|
|
|
47
|
+
import numpy as np
|
|
48
|
+
import scipy.linalg as spla
|
|
49
|
+
|
|
39
50
|
# check for sklearn version to account for incompatible behavior
|
|
40
51
|
import sklearn
|
|
41
|
-
from numpy import (
|
|
42
|
-
absolute,
|
|
43
|
-
arange,
|
|
44
|
-
argsort,
|
|
45
|
-
array,
|
|
46
|
-
atleast_2d,
|
|
47
|
-
clip,
|
|
48
|
-
delete,
|
|
49
|
-
diag,
|
|
50
|
-
dot,
|
|
51
|
-
einsum,
|
|
52
|
-
einsum_path,
|
|
53
|
-
eye,
|
|
54
|
-
fill_diagonal,
|
|
55
|
-
full,
|
|
56
|
-
hsplit,
|
|
57
|
-
hstack,
|
|
58
|
-
index_exp,
|
|
59
|
-
inf,
|
|
60
|
-
integer,
|
|
61
|
-
invert,
|
|
62
|
-
isscalar,
|
|
63
|
-
log10,
|
|
64
|
-
ndarray,
|
|
65
|
-
newaxis,
|
|
66
|
-
ones,
|
|
67
|
-
pi,
|
|
68
|
-
real,
|
|
69
|
-
reshape,
|
|
70
|
-
round, # noqa: A004
|
|
71
|
-
searchsorted,
|
|
72
|
-
sign,
|
|
73
|
-
size,
|
|
74
|
-
sqrt,
|
|
75
|
-
sum, # noqa: A004
|
|
76
|
-
tile,
|
|
77
|
-
trace,
|
|
78
|
-
tril,
|
|
79
|
-
unique,
|
|
80
|
-
vstack,
|
|
81
|
-
zeros,
|
|
82
|
-
zeros_like,
|
|
83
|
-
)
|
|
84
52
|
from packaging.version import parse
|
|
85
|
-
from scipy.linalg import eigh, eigvals, fractional_matrix_power, inv, norm
|
|
86
53
|
from scipy.optimize import fmin_l_bfgs_b, linprog, nnls, shgo
|
|
87
54
|
from sklearn.linear_model import LassoLars, LassoLarsCV, LassoLarsIC, LinearRegression, OrthogonalMatchingPursuitCV
|
|
88
55
|
from traits.api import (
|
|
@@ -100,14 +67,13 @@ from traits.api import (
|
|
|
100
67
|
Range,
|
|
101
68
|
Tuple,
|
|
102
69
|
cached_property,
|
|
103
|
-
|
|
70
|
+
observe,
|
|
104
71
|
property_depends_on,
|
|
105
72
|
)
|
|
106
73
|
from traits.trait_errors import TraitError
|
|
107
74
|
|
|
108
75
|
# acoular imports
|
|
109
76
|
from .configuration import config
|
|
110
|
-
from .deprecation import deprecated_alias
|
|
111
77
|
from .environments import Environment
|
|
112
78
|
from .fastFuncs import beamformerFreq, calcPointSpreadFunction, calcTransfer, damasSolverGaussSeidel
|
|
113
79
|
from .grids import Grid, Sector
|
|
@@ -126,7 +92,8 @@ BEAMFORMER_BASE_DIGEST_DEPENDENCIES = ['freq_data.digest', 'r_diag', 'r_diag_nor
|
|
|
126
92
|
|
|
127
93
|
|
|
128
94
|
class SteeringVector(HasStrictTraits):
|
|
129
|
-
"""
|
|
95
|
+
"""
|
|
96
|
+
Basic class for implementing steering vectors with monopole source transfer models.
|
|
130
97
|
|
|
131
98
|
Handles four different steering vector formulations. See :cite:`Sarradj2012` for details.
|
|
132
99
|
"""
|
|
@@ -154,7 +121,7 @@ class SteeringVector(HasStrictTraits):
|
|
|
154
121
|
rm = Property(desc='all array mics to grid distances')
|
|
155
122
|
|
|
156
123
|
# mirror trait for ref
|
|
157
|
-
_ref = Any(array([0.0, 0.0, 0.0]), desc='reference position or distance')
|
|
124
|
+
_ref = Any(np.array([0.0, 0.0, 0.0]), desc='reference position or distance')
|
|
158
125
|
|
|
159
126
|
#: Reference position or distance at which to evaluate the sound pressure
|
|
160
127
|
#: of a grid point.
|
|
@@ -165,10 +132,10 @@ class SteeringVector(HasStrictTraits):
|
|
|
165
132
|
|
|
166
133
|
_steer_funcs_freq = Dict(
|
|
167
134
|
{
|
|
168
|
-
'classic': lambda x: x /
|
|
135
|
+
'classic': lambda x: x / np.abs(x) / x.shape[-1],
|
|
169
136
|
'inverse': lambda x: 1.0 / x.conj() / x.shape[-1],
|
|
170
|
-
'true level': lambda x: x / einsum('ij,ij->i', x, x.conj())[:, newaxis],
|
|
171
|
-
'true location': lambda x: x / sqrt(einsum('ij,ij->i', x, x.conj()) * x.shape[-1])[:, newaxis],
|
|
137
|
+
'true level': lambda x: x / np.einsum('ij,ij->i', x, x.conj())[:, np.newaxis],
|
|
138
|
+
'true location': lambda x: x / np.sqrt(np.einsum('ij,ij->i', x, x.conj()) * x.shape[-1])[:, np.newaxis],
|
|
172
139
|
},
|
|
173
140
|
transient=True,
|
|
174
141
|
desc='dictionary of frequency domain steering vector functions',
|
|
@@ -186,13 +153,13 @@ class SteeringVector(HasStrictTraits):
|
|
|
186
153
|
)
|
|
187
154
|
|
|
188
155
|
def _set_ref(self, ref):
|
|
189
|
-
if isscalar(ref):
|
|
156
|
+
if np.isscalar(ref):
|
|
190
157
|
try:
|
|
191
|
-
self._ref =
|
|
158
|
+
self._ref = np.abs(float(ref))
|
|
192
159
|
except ValueError as ve:
|
|
193
160
|
raise TraitError(args=self, name='ref', info='Float or CArray(3,)', value=ref) from ve
|
|
194
161
|
elif len(ref) == 3:
|
|
195
|
-
self._ref = array(ref, dtype=float)
|
|
162
|
+
self._ref = np.array(ref, dtype=float)
|
|
196
163
|
else:
|
|
197
164
|
raise TraitError(args=self, name='ref', info='Float or CArray(3,)', value=ref)
|
|
198
165
|
|
|
@@ -207,15 +174,15 @@ class SteeringVector(HasStrictTraits):
|
|
|
207
174
|
|
|
208
175
|
@property_depends_on(['grid.digest', 'env.digest', '_ref'])
|
|
209
176
|
def _get_r0(self):
|
|
210
|
-
if isscalar(self.ref):
|
|
177
|
+
if np.isscalar(self.ref):
|
|
211
178
|
if self.ref > 0:
|
|
212
|
-
return full((self.grid.size,), self.ref)
|
|
179
|
+
return np.full((self.grid.size,), self.ref)
|
|
213
180
|
return self.env._r(self.grid.pos())
|
|
214
|
-
return self.env._r(self.grid.pos, self.ref[:, newaxis])
|
|
181
|
+
return self.env._r(self.grid.pos, self.ref[:, np.newaxis])
|
|
215
182
|
|
|
216
183
|
@property_depends_on(['grid.digest', 'mics.digest', 'env.digest'])
|
|
217
184
|
def _get_rm(self):
|
|
218
|
-
return atleast_2d(self.env._r(self.grid.pos, self.mics.pos))
|
|
185
|
+
return np.atleast_2d(self.env._r(self.grid.pos, self.mics.pos))
|
|
219
186
|
|
|
220
187
|
@cached_property
|
|
221
188
|
def _get_digest(self):
|
|
@@ -226,7 +193,8 @@ class SteeringVector(HasStrictTraits):
|
|
|
226
193
|
return digest(self)
|
|
227
194
|
|
|
228
195
|
def transfer(self, f, ind=None):
|
|
229
|
-
"""
|
|
196
|
+
"""
|
|
197
|
+
Calculates the transfer matrix for one frequency.
|
|
230
198
|
|
|
231
199
|
Parameters
|
|
232
200
|
----------
|
|
@@ -241,22 +209,22 @@ class SteeringVector(HasStrictTraits):
|
|
|
241
209
|
-------
|
|
242
210
|
array of complex128
|
|
243
211
|
array of shape (ngridpts, nmics) containing the transfer matrix for the given frequency
|
|
244
|
-
|
|
245
212
|
"""
|
|
246
213
|
# if self.cached:
|
|
247
214
|
# warn('Caching of transfer function is not yet supported!', Warning)
|
|
248
215
|
# self.cached = False
|
|
249
216
|
|
|
250
217
|
if ind is None:
|
|
251
|
-
trans = calcTransfer(self.r0, self.rm, array(2 * pi * f / self.env.c))
|
|
252
|
-
elif not isinstance(ind, ndarray):
|
|
253
|
-
trans = calcTransfer(self.r0[ind], self.rm[ind, :][newaxis], array(2 * pi * f / self.env.c))
|
|
218
|
+
trans = calcTransfer(self.r0, self.rm, np.array(2 * np.pi * f / self.env.c))
|
|
219
|
+
elif not isinstance(ind, np.ndarray):
|
|
220
|
+
trans = calcTransfer(self.r0[ind], self.rm[ind, :][np.newaxis], np.array(2 * np.pi * f / self.env.c))
|
|
254
221
|
else:
|
|
255
|
-
trans = calcTransfer(self.r0[ind], self.rm[ind, :], array(2 * pi * f / self.env.c))
|
|
222
|
+
trans = calcTransfer(self.r0[ind], self.rm[ind, :], np.array(2 * np.pi * f / self.env.c))
|
|
256
223
|
return trans
|
|
257
224
|
|
|
258
225
|
def steer_vector(self, f, ind=None):
|
|
259
|
-
"""
|
|
226
|
+
"""
|
|
227
|
+
Calculates the steering vectors based on the transfer function.
|
|
260
228
|
|
|
261
229
|
See also :cite:`Sarradj2012`.
|
|
262
230
|
|
|
@@ -273,7 +241,6 @@ class SteeringVector(HasStrictTraits):
|
|
|
273
241
|
-------
|
|
274
242
|
array of complex128
|
|
275
243
|
array of shape (ngridpts, nmics) containing the steering vectors for the given frequency
|
|
276
|
-
|
|
277
244
|
"""
|
|
278
245
|
func = self._steer_funcs_freq[self.steer_type]
|
|
279
246
|
return func(self.transfer(f, ind))
|
|
@@ -293,11 +260,11 @@ class LazyBfResult:
|
|
|
293
260
|
|
|
294
261
|
def __getitem__(self, key):
|
|
295
262
|
"""'intelligent' [] operator, checks if results are available and triggers calculation."""
|
|
296
|
-
sl = index_exp[key][0]
|
|
297
|
-
if isinstance(sl, (int, integer)):
|
|
263
|
+
sl = np.index_exp[key][0]
|
|
264
|
+
if isinstance(sl, (int, np.integer)):
|
|
298
265
|
sl = slice(sl, sl + 1)
|
|
299
266
|
# indices which are missing
|
|
300
|
-
missingind = arange(*sl.indices(self.bf._numfreq))[self.bf._fr[sl] == 0]
|
|
267
|
+
missingind = np.arange(*sl.indices(self.bf._numfreq))[self.bf._fr[sl] == 0]
|
|
301
268
|
# calc if needed
|
|
302
269
|
if missingind.size:
|
|
303
270
|
self.bf._calc(missingind)
|
|
@@ -362,9 +329,10 @@ class BeamformerBase(HasStrictTraits):
|
|
|
362
329
|
return digest(self)
|
|
363
330
|
|
|
364
331
|
def _get_filecache(self):
|
|
365
|
-
"""
|
|
366
|
-
|
|
367
|
-
|
|
332
|
+
"""
|
|
333
|
+
Function collects cached results from file depending on global/local caching behaviour.
|
|
334
|
+
|
|
335
|
+
Returns (None, None) if no cachefile/data exist and global caching mode is 'readonly'.
|
|
368
336
|
"""
|
|
369
337
|
# print("get cachefile:", self.freq_data.basename)
|
|
370
338
|
H5cache.get_cache_file(self, self.freq_data.basename)
|
|
@@ -419,7 +387,9 @@ class BeamformerBase(HasStrictTraits):
|
|
|
419
387
|
|
|
420
388
|
@property_depends_on(['digest'])
|
|
421
389
|
def _get_result(self):
|
|
422
|
-
"""
|
|
390
|
+
"""
|
|
391
|
+
Implements the :attr:`result` getter routine.
|
|
392
|
+
|
|
423
393
|
The beamforming result is either loaded or calculated.
|
|
424
394
|
"""
|
|
425
395
|
# store locally for performance
|
|
@@ -439,20 +409,22 @@ class BeamformerBase(HasStrictTraits):
|
|
|
439
409
|
else:
|
|
440
410
|
# no caching or not activated, init numpy arrays
|
|
441
411
|
if isinstance(self, BeamformerAdaptiveGrid):
|
|
442
|
-
self._gpos = zeros((3, self.size), dtype=self.precision)
|
|
443
|
-
ac = zeros((self._numfreq, self.size), dtype=self.precision)
|
|
412
|
+
self._gpos = np.zeros((3, self.size), dtype=self.precision)
|
|
413
|
+
ac = np.zeros((self._numfreq, self.size), dtype=self.precision)
|
|
444
414
|
elif isinstance(self, BeamformerSODIX):
|
|
445
|
-
ac = zeros((self._numfreq, self.steer.grid.size * self.steer.mics.num_mics), dtype=self.precision)
|
|
415
|
+
ac = np.zeros((self._numfreq, self.steer.grid.size * self.steer.mics.num_mics), dtype=self.precision)
|
|
446
416
|
else:
|
|
447
|
-
ac = zeros((self._numfreq, self.steer.grid.size), dtype=self.precision)
|
|
448
|
-
fr = zeros(self._numfreq, dtype='int8')
|
|
417
|
+
ac = np.zeros((self._numfreq, self.steer.grid.size), dtype=self.precision)
|
|
418
|
+
fr = np.zeros(self._numfreq, dtype='int8')
|
|
449
419
|
self._ac = ac
|
|
450
420
|
self._fr = fr
|
|
451
421
|
return LazyBfResult(self)
|
|
452
422
|
|
|
453
423
|
def sig_loss_norm(self):
|
|
454
|
-
"""
|
|
455
|
-
of
|
|
424
|
+
"""
|
|
425
|
+
If the diagonal of the CSM is removed one has to handle the loss of signal energy.
|
|
426
|
+
|
|
427
|
+
Done via a normalization factor.
|
|
456
428
|
"""
|
|
457
429
|
if not self.r_diag: # Full CSM --> no normalization needed
|
|
458
430
|
normfactor = 1.0
|
|
@@ -464,28 +436,29 @@ class BeamformerBase(HasStrictTraits):
|
|
|
464
436
|
return normfactor
|
|
465
437
|
|
|
466
438
|
def _beamformer_params(self):
|
|
467
|
-
"""
|
|
468
|
-
|
|
469
|
-
|
|
439
|
+
"""
|
|
440
|
+
Manages the parameters for calling of the core beamformer functionality.
|
|
441
|
+
|
|
442
|
+
This is a workaround to allow faster calculation and may change in the future.
|
|
470
443
|
|
|
471
444
|
Returns
|
|
472
445
|
-------
|
|
473
446
|
- String containing the steering vector type
|
|
474
447
|
- Function for frequency-dependent steering vector calculation
|
|
475
|
-
|
|
476
448
|
"""
|
|
477
449
|
if type(self.steer) is SteeringVector: # for simple steering vector, use faster method
|
|
478
450
|
param_type = self.steer.steer_type
|
|
479
451
|
|
|
480
452
|
def param_steer_func(f):
|
|
481
|
-
return (self.steer.r0, self.steer.rm, 2 * pi * f / self.steer.env.c)
|
|
453
|
+
return (self.steer.r0, self.steer.rm, 2 * np.pi * f / self.steer.env.c)
|
|
482
454
|
else:
|
|
483
455
|
param_type = 'custom'
|
|
484
456
|
param_steer_func = self.steer.steer_vector
|
|
485
457
|
return param_type, param_steer_func
|
|
486
458
|
|
|
487
459
|
def _calc(self, ind):
|
|
488
|
-
"""
|
|
460
|
+
"""
|
|
461
|
+
Calculates the result for the frequencies defined by :attr:`freq_data`.
|
|
489
462
|
|
|
490
463
|
This is an internal helper function that is automatically called when
|
|
491
464
|
accessing the beamformer's :attr:`result` or calling
|
|
@@ -500,14 +473,13 @@ class BeamformerBase(HasStrictTraits):
|
|
|
500
473
|
Returns
|
|
501
474
|
-------
|
|
502
475
|
This method only returns values through :attr:`_ac` and :attr:`_fr`
|
|
503
|
-
|
|
504
476
|
"""
|
|
505
477
|
f = self._f
|
|
506
478
|
normfactor = self.sig_loss_norm()
|
|
507
479
|
param_steer_type, steer_vector = self._beamformer_params()
|
|
508
480
|
for i in ind:
|
|
509
481
|
# print(f'compute{i}')
|
|
510
|
-
csm = array(self.freq_data.csm[i], dtype='complex128')
|
|
482
|
+
csm = np.array(self.freq_data.csm[i], dtype='complex128')
|
|
511
483
|
beamformerOutput = beamformerFreq(
|
|
512
484
|
param_steer_type,
|
|
513
485
|
self.r_diag,
|
|
@@ -516,13 +488,14 @@ class BeamformerBase(HasStrictTraits):
|
|
|
516
488
|
csm,
|
|
517
489
|
)[0]
|
|
518
490
|
if self.r_diag: # set (unphysical) negative output values to 0
|
|
519
|
-
indNegSign = sign(beamformerOutput) < 0
|
|
491
|
+
indNegSign = np.sign(beamformerOutput) < 0
|
|
520
492
|
beamformerOutput[indNegSign] = 0.0
|
|
521
493
|
self._ac[i] = beamformerOutput
|
|
522
494
|
self._fr[i] = 1
|
|
523
495
|
|
|
524
496
|
def synthetic(self, f, num=0):
|
|
525
|
-
"""
|
|
497
|
+
"""
|
|
498
|
+
Evaluates the beamforming result for an arbitrary frequency band.
|
|
526
499
|
|
|
527
500
|
Parameters
|
|
528
501
|
----------
|
|
@@ -550,7 +523,6 @@ class BeamformerBase(HasStrictTraits):
|
|
|
550
523
|
represented by a single frequency line depends on
|
|
551
524
|
the :attr:`sampling frequency<acoular.base.SamplesGenerator.sample_freq>` and
|
|
552
525
|
used :attr:`FFT block size<acoular.spectra.PowerSpectra.block_size>`.
|
|
553
|
-
|
|
554
526
|
"""
|
|
555
527
|
res = self.result # trigger calculation
|
|
556
528
|
freq = self.freq_data.fftfreq()
|
|
@@ -559,14 +531,14 @@ class BeamformerBase(HasStrictTraits):
|
|
|
559
531
|
|
|
560
532
|
if num == 0:
|
|
561
533
|
# single frequency line
|
|
562
|
-
ind = searchsorted(freq, f)
|
|
534
|
+
ind = np.searchsorted(freq, f)
|
|
563
535
|
if ind >= len(freq):
|
|
564
536
|
warn(
|
|
565
537
|
f'Queried frequency ({f:g} Hz) not in resolved frequency range. Returning zeros.',
|
|
566
538
|
Warning,
|
|
567
539
|
stacklevel=2,
|
|
568
540
|
)
|
|
569
|
-
h = zeros_like(res[0])
|
|
541
|
+
h = np.zeros_like(res[0])
|
|
570
542
|
else:
|
|
571
543
|
if freq[ind] != f:
|
|
572
544
|
warn(
|
|
@@ -585,8 +557,8 @@ class BeamformerBase(HasStrictTraits):
|
|
|
585
557
|
else:
|
|
586
558
|
f1 = f * 2.0 ** (-0.5 / num)
|
|
587
559
|
f2 = f * 2.0 ** (+0.5 / num)
|
|
588
|
-
ind1 = searchsorted(freq, f1)
|
|
589
|
-
ind2 = searchsorted(freq, f2)
|
|
560
|
+
ind1 = np.searchsorted(freq, f1)
|
|
561
|
+
ind2 = np.searchsorted(freq, f2)
|
|
590
562
|
if ind1 == ind2:
|
|
591
563
|
warn(
|
|
592
564
|
f'Queried frequency band ({f1:g} to {f2:g} Hz) does not '
|
|
@@ -595,9 +567,9 @@ class BeamformerBase(HasStrictTraits):
|
|
|
595
567
|
Warning,
|
|
596
568
|
stacklevel=2,
|
|
597
569
|
)
|
|
598
|
-
h = zeros_like(res[0])
|
|
570
|
+
h = np.zeros_like(res[0])
|
|
599
571
|
else:
|
|
600
|
-
h = sum(res[ind1:ind2], 0)
|
|
572
|
+
h = np.sum(res[ind1:ind2], 0)
|
|
601
573
|
if isinstance(self, BeamformerAdaptiveGrid):
|
|
602
574
|
return h
|
|
603
575
|
if isinstance(self, BeamformerSODIX):
|
|
@@ -605,7 +577,8 @@ class BeamformerBase(HasStrictTraits):
|
|
|
605
577
|
return h.reshape(self.steer.grid.shape)
|
|
606
578
|
|
|
607
579
|
def integrate(self, sector, frange=None, num=0):
|
|
608
|
-
"""
|
|
580
|
+
"""
|
|
581
|
+
Integrates result map over a given sector.
|
|
609
582
|
|
|
610
583
|
Parameters
|
|
611
584
|
----------
|
|
@@ -674,13 +647,13 @@ class BeamformerBase(HasStrictTraits):
|
|
|
674
647
|
irange = (ind_low, ind_high)
|
|
675
648
|
num = 0
|
|
676
649
|
elif len(frange) == 2:
|
|
677
|
-
irange = (searchsorted(self._f, frange[0]), searchsorted(self._f, frange[1]))
|
|
650
|
+
irange = (np.searchsorted(self._f, frange[0]), np.searchsorted(self._f, frange[1]))
|
|
678
651
|
else:
|
|
679
652
|
msg = 'Only a tuple of length 2 is allowed for frange if num==0'
|
|
680
653
|
raise TypeError(
|
|
681
654
|
msg,
|
|
682
655
|
)
|
|
683
|
-
h = zeros(num_freqs, dtype=float)
|
|
656
|
+
h = np.zeros(num_freqs, dtype=float)
|
|
684
657
|
sl = slice(*irange)
|
|
685
658
|
r = self.result[sl]
|
|
686
659
|
for i in range(num_freqs)[sl]:
|
|
@@ -690,14 +663,15 @@ class BeamformerBase(HasStrictTraits):
|
|
|
690
663
|
return h
|
|
691
664
|
return self._f[sl], h[sl]
|
|
692
665
|
|
|
693
|
-
h = zeros(len(frange), dtype=float)
|
|
666
|
+
h = np.zeros(len(frange), dtype=float)
|
|
694
667
|
for i, f in enumerate(frange):
|
|
695
668
|
h[i] = self.synthetic(f, num).reshape(gshape)[ind].sum()
|
|
696
669
|
return h
|
|
697
670
|
|
|
698
671
|
|
|
699
672
|
class BeamformerFunctional(BeamformerBase):
|
|
700
|
-
"""
|
|
673
|
+
"""
|
|
674
|
+
Functional beamforming algorithm.
|
|
701
675
|
|
|
702
676
|
See :cite:`Dougherty2014` for details.
|
|
703
677
|
"""
|
|
@@ -723,7 +697,8 @@ class BeamformerFunctional(BeamformerBase):
|
|
|
723
697
|
return digest(self)
|
|
724
698
|
|
|
725
699
|
def _calc(self, ind):
|
|
726
|
-
"""
|
|
700
|
+
"""
|
|
701
|
+
Calculates the result for the frequencies defined by :attr:`freq_data`.
|
|
727
702
|
|
|
728
703
|
This is an internal helper function that is automatically called when
|
|
729
704
|
accessing the beamformer's :attr:`result` or calling
|
|
@@ -738,7 +713,6 @@ class BeamformerFunctional(BeamformerBase):
|
|
|
738
713
|
Returns
|
|
739
714
|
-------
|
|
740
715
|
This method only returns values through :attr:`_ac` and :attr:`_fr`
|
|
741
|
-
|
|
742
716
|
"""
|
|
743
717
|
f = self._f
|
|
744
718
|
normfactor = self.sig_loss_norm()
|
|
@@ -756,8 +730,8 @@ class BeamformerFunctional(BeamformerBase):
|
|
|
756
730
|
# WATCH OUT: This doesn't really produce good results.
|
|
757
731
|
# ==============================================================================
|
|
758
732
|
csm = self.freq_data.csm[i]
|
|
759
|
-
fill_diagonal(csm, 0)
|
|
760
|
-
csmRoot = fractional_matrix_power(csm, 1.0 / self.gamma)
|
|
733
|
+
np.fill_diagonal(csm, 0)
|
|
734
|
+
csmRoot = spla.fractional_matrix_power(csm, 1.0 / self.gamma)
|
|
761
735
|
beamformerOutput, steerNorm = beamformerFreq(
|
|
762
736
|
param_steer_type,
|
|
763
737
|
self.r_diag,
|
|
@@ -768,11 +742,11 @@ class BeamformerFunctional(BeamformerBase):
|
|
|
768
742
|
beamformerOutput /= steerNorm # take normalized steering vec
|
|
769
743
|
|
|
770
744
|
# set (unphysical) negative output values to 0
|
|
771
|
-
indNegSign = sign(beamformerOutput) < 0
|
|
745
|
+
indNegSign = np.sign(beamformerOutput) < 0
|
|
772
746
|
beamformerOutput[indNegSign] = 0.0
|
|
773
747
|
else:
|
|
774
|
-
eva = array(self.freq_data.eva[i], dtype='float64') ** (1.0 / self.gamma)
|
|
775
|
-
eve = array(self.freq_data.eve[i], dtype='complex128')
|
|
748
|
+
eva = np.array(self.freq_data.eva[i], dtype='float64') ** (1.0 / self.gamma)
|
|
749
|
+
eve = np.array(self.freq_data.eve[i], dtype='complex128')
|
|
776
750
|
beamformerOutput, steerNorm = beamformerFreq(
|
|
777
751
|
param_steer_type,
|
|
778
752
|
self.r_diag,
|
|
@@ -788,7 +762,8 @@ class BeamformerFunctional(BeamformerBase):
|
|
|
788
762
|
|
|
789
763
|
|
|
790
764
|
class BeamformerCapon(BeamformerBase):
|
|
791
|
-
"""
|
|
765
|
+
"""
|
|
766
|
+
Beamforming using the Capon (Mininimum Variance) algorithm.
|
|
792
767
|
|
|
793
768
|
See :cite:`Capon1969` for details.
|
|
794
769
|
"""
|
|
@@ -805,7 +780,8 @@ class BeamformerCapon(BeamformerBase):
|
|
|
805
780
|
)
|
|
806
781
|
|
|
807
782
|
def _calc(self, ind):
|
|
808
|
-
"""
|
|
783
|
+
"""
|
|
784
|
+
Calculates the result for the frequencies defined by :attr:`freq_data`.
|
|
809
785
|
|
|
810
786
|
This is an internal helper function that is automatically called when
|
|
811
787
|
accessing the beamformer's :attr:`result` or calling
|
|
@@ -820,21 +796,21 @@ class BeamformerCapon(BeamformerBase):
|
|
|
820
796
|
Returns
|
|
821
797
|
-------
|
|
822
798
|
This method only returns values through :attr:`_ac` and :attr:`_fr`
|
|
823
|
-
|
|
824
799
|
"""
|
|
825
800
|
f = self._f
|
|
826
801
|
nMics = self.freq_data.num_channels
|
|
827
802
|
normfactor = self.sig_loss_norm() * nMics**2
|
|
828
803
|
param_steer_type, steer_vector = self._beamformer_params()
|
|
829
804
|
for i in ind:
|
|
830
|
-
csm = array(inv(array(self.freq_data.csm[i], dtype='complex128')), order='C')
|
|
805
|
+
csm = np.array(spla.inv(np.array(self.freq_data.csm[i], dtype='complex128')), order='C')
|
|
831
806
|
beamformerOutput = beamformerFreq(param_steer_type, self.r_diag, normfactor, steer_vector(f[i]), csm)[0]
|
|
832
807
|
self._ac[i] = 1.0 / beamformerOutput
|
|
833
808
|
self._fr[i] = 1
|
|
834
809
|
|
|
835
810
|
|
|
836
811
|
class BeamformerEig(BeamformerBase):
|
|
837
|
-
"""
|
|
812
|
+
"""
|
|
813
|
+
Beamforming using eigenvalue and eigenvector techniques.
|
|
838
814
|
|
|
839
815
|
See :cite:`Sarradj2005` for details.
|
|
840
816
|
"""
|
|
@@ -863,7 +839,8 @@ class BeamformerEig(BeamformerBase):
|
|
|
863
839
|
return min(nm - 1, na)
|
|
864
840
|
|
|
865
841
|
def _calc(self, ind):
|
|
866
|
-
"""
|
|
842
|
+
"""
|
|
843
|
+
Calculates the result for the frequencies defined by :attr:`freq_data`.
|
|
867
844
|
|
|
868
845
|
This is an internal helper function that is automatically called when
|
|
869
846
|
accessing the beamformer's :attr:`result` or calling
|
|
@@ -878,15 +855,14 @@ class BeamformerEig(BeamformerBase):
|
|
|
878
855
|
Returns
|
|
879
856
|
-------
|
|
880
857
|
This method only returns values through :attr:`_ac` and :attr:`_fr`
|
|
881
|
-
|
|
882
858
|
"""
|
|
883
859
|
f = self._f
|
|
884
860
|
na = int(self.na) # eigenvalue taken into account
|
|
885
861
|
normfactor = self.sig_loss_norm()
|
|
886
862
|
param_steer_type, steer_vector = self._beamformer_params()
|
|
887
863
|
for i in ind:
|
|
888
|
-
eva = array(self.freq_data.eva[i], dtype='float64')
|
|
889
|
-
eve = array(self.freq_data.eve[i], dtype='complex128')
|
|
864
|
+
eva = np.array(self.freq_data.eva[i], dtype='float64')
|
|
865
|
+
eve = np.array(self.freq_data.eve[i], dtype='complex128')
|
|
890
866
|
beamformerOutput = beamformerFreq(
|
|
891
867
|
param_steer_type,
|
|
892
868
|
self.r_diag,
|
|
@@ -895,14 +871,15 @@ class BeamformerEig(BeamformerBase):
|
|
|
895
871
|
(eva[na : na + 1], eve[:, na : na + 1]),
|
|
896
872
|
)[0]
|
|
897
873
|
if self.r_diag: # set (unphysical) negative output values to 0
|
|
898
|
-
indNegSign = sign(beamformerOutput) < 0
|
|
874
|
+
indNegSign = np.sign(beamformerOutput) < 0
|
|
899
875
|
beamformerOutput[indNegSign] = 0
|
|
900
876
|
self._ac[i] = beamformerOutput
|
|
901
877
|
self._fr[i] = 1
|
|
902
878
|
|
|
903
879
|
|
|
904
880
|
class BeamformerMusic(BeamformerEig):
|
|
905
|
-
"""
|
|
881
|
+
"""
|
|
882
|
+
Beamforming using the MUSIC algorithm.
|
|
906
883
|
|
|
907
884
|
See :cite:`Schmidt1986` for details.
|
|
908
885
|
"""
|
|
@@ -923,7 +900,8 @@ class BeamformerMusic(BeamformerEig):
|
|
|
923
900
|
n = Int(1, desc='assumed number of sources')
|
|
924
901
|
|
|
925
902
|
def _calc(self, ind):
|
|
926
|
-
"""
|
|
903
|
+
"""
|
|
904
|
+
Calculates the result for the frequencies defined by :attr:`freq_data`.
|
|
927
905
|
|
|
928
906
|
This is an internal helper function that is automatically called when
|
|
929
907
|
accessing the beamformer's :attr:`result` or calling
|
|
@@ -938,7 +916,6 @@ class BeamformerMusic(BeamformerEig):
|
|
|
938
916
|
Returns
|
|
939
917
|
-------
|
|
940
918
|
This method only returns values through :attr:`_ac` and :attr:`_fr`
|
|
941
|
-
|
|
942
919
|
"""
|
|
943
920
|
f = self._f
|
|
944
921
|
nMics = self.freq_data.num_channels
|
|
@@ -946,8 +923,8 @@ class BeamformerMusic(BeamformerEig):
|
|
|
946
923
|
normfactor = self.sig_loss_norm() * nMics**2
|
|
947
924
|
param_steer_type, steer_vector = self._beamformer_params()
|
|
948
925
|
for i in ind:
|
|
949
|
-
eva = array(self.freq_data.eva[i], dtype='float64')
|
|
950
|
-
eve = array(self.freq_data.eve[i], dtype='complex128')
|
|
926
|
+
eva = np.array(self.freq_data.eva[i], dtype='float64')
|
|
927
|
+
eve = np.array(self.freq_data.eve[i], dtype='complex128')
|
|
951
928
|
beamformerOutput = beamformerFreq(
|
|
952
929
|
param_steer_type,
|
|
953
930
|
self.r_diag,
|
|
@@ -960,7 +937,8 @@ class BeamformerMusic(BeamformerEig):
|
|
|
960
937
|
|
|
961
938
|
|
|
962
939
|
class PointSpreadFunction(HasStrictTraits):
|
|
963
|
-
"""
|
|
940
|
+
"""
|
|
941
|
+
The point spread function.
|
|
964
942
|
|
|
965
943
|
This class provides tools to calculate the PSF depending on the used
|
|
966
944
|
microphone geometry, focus grid, flow environment, etc.
|
|
@@ -976,7 +954,7 @@ class PointSpreadFunction(HasStrictTraits):
|
|
|
976
954
|
#: Indices of grid points to calculate the PSF for.
|
|
977
955
|
grid_indices = CArray(
|
|
978
956
|
dtype=int,
|
|
979
|
-
value=array([]),
|
|
957
|
+
value=np.array([]),
|
|
980
958
|
desc='indices of grid points for psf',
|
|
981
959
|
) # value=array([]), value=self.steer.grid.pos(),
|
|
982
960
|
|
|
@@ -1013,9 +991,10 @@ class PointSpreadFunction(HasStrictTraits):
|
|
|
1013
991
|
return digest(self)
|
|
1014
992
|
|
|
1015
993
|
def _get_filecache(self):
|
|
1016
|
-
"""
|
|
1017
|
-
|
|
1018
|
-
|
|
994
|
+
"""
|
|
995
|
+
Function collects cached results from file depending on global/local caching behaviour.
|
|
996
|
+
|
|
997
|
+
Returns (None, None) if no cachefile/data exist and global caching mode is 'readonly'.
|
|
1019
998
|
"""
|
|
1020
999
|
filename = 'psf' + self.digest
|
|
1021
1000
|
nodename = (f'Hz_{self.freq:.2f}').replace('.', '_')
|
|
@@ -1047,12 +1026,14 @@ class PointSpreadFunction(HasStrictTraits):
|
|
|
1047
1026
|
return (ac, gp)
|
|
1048
1027
|
|
|
1049
1028
|
def _get_psf(self):
|
|
1050
|
-
"""
|
|
1029
|
+
"""
|
|
1030
|
+
Implements the :attr:`psf` getter routine.
|
|
1031
|
+
|
|
1051
1032
|
The point spread function is either loaded or calculated.
|
|
1052
1033
|
"""
|
|
1053
1034
|
gs = self.steer.grid.size
|
|
1054
1035
|
if not self.grid_indices.size:
|
|
1055
|
-
self.grid_indices = arange(gs)
|
|
1036
|
+
self.grid_indices = np.arange(gs)
|
|
1056
1037
|
|
|
1057
1038
|
if config.global_caching != 'none':
|
|
1058
1039
|
# print("get filecache..")
|
|
@@ -1075,13 +1056,13 @@ class PointSpreadFunction(HasStrictTraits):
|
|
|
1075
1056
|
# print("cached results are complete! return.")
|
|
1076
1057
|
return ac[:, self.grid_indices]
|
|
1077
1058
|
# print("no caching, calculate result")
|
|
1078
|
-
ac = zeros((gs, gs), dtype=self.precision)
|
|
1079
|
-
gp = zeros((gs,), dtype='int8')
|
|
1059
|
+
ac = np.zeros((gs, gs), dtype=self.precision)
|
|
1060
|
+
gp = np.zeros((gs,), dtype='int8')
|
|
1080
1061
|
self.calc_psf(ac, gp)
|
|
1081
1062
|
else: # no caching activated
|
|
1082
1063
|
# print("no caching activated, calculate result")
|
|
1083
|
-
ac = zeros((gs, gs), dtype=self.precision)
|
|
1084
|
-
gp = zeros((gs,), dtype='int8')
|
|
1064
|
+
ac = np.zeros((gs, gs), dtype=self.precision)
|
|
1065
|
+
gp = np.zeros((gs,), dtype='int8')
|
|
1085
1066
|
self.calc_psf(ac, gp)
|
|
1086
1067
|
return ac[:, self.grid_indices]
|
|
1087
1068
|
|
|
@@ -1090,7 +1071,7 @@ class PointSpreadFunction(HasStrictTraits):
|
|
|
1090
1071
|
if self.calcmode != 'full':
|
|
1091
1072
|
# calc_ind has the form [True, True, False, True], except
|
|
1092
1073
|
# when it has only 1 entry (value True/1 would be ambiguous)
|
|
1093
|
-
calc_ind = [0] if self.grid_indices.size == 1 else invert(gp[:][self.grid_indices])
|
|
1074
|
+
calc_ind = [0] if self.grid_indices.size == 1 else np.invert(gp[:][self.grid_indices])
|
|
1094
1075
|
|
|
1095
1076
|
# get indices which have the value True = not yet calculated
|
|
1096
1077
|
g_ind_calc = self.grid_indices[calc_ind]
|
|
@@ -1101,7 +1082,7 @@ class PointSpreadFunction(HasStrictTraits):
|
|
|
1101
1082
|
gp[ind] = 1
|
|
1102
1083
|
elif self.calcmode == 'full': # calculate all psfs in one go
|
|
1103
1084
|
gp[:] = 1
|
|
1104
|
-
ac[:] = self._psf_call(arange(self.steer.grid.size))
|
|
1085
|
+
ac[:] = self._psf_call(np.arange(self.steer.grid.size))
|
|
1105
1086
|
else: # 'block' # calculate selected psfs in one go
|
|
1106
1087
|
hh = self._psf_call(g_ind_calc)
|
|
1107
1088
|
for indh, ind in enumerate(g_ind_calc):
|
|
@@ -1110,7 +1091,8 @@ class PointSpreadFunction(HasStrictTraits):
|
|
|
1110
1091
|
indh += 1
|
|
1111
1092
|
|
|
1112
1093
|
def _psf_call(self, ind):
|
|
1113
|
-
"""
|
|
1094
|
+
"""
|
|
1095
|
+
Manages the calling of the core psf functionality.
|
|
1114
1096
|
|
|
1115
1097
|
Parameters
|
|
1116
1098
|
----------
|
|
@@ -1127,20 +1109,21 @@ class PointSpreadFunction(HasStrictTraits):
|
|
|
1127
1109
|
self.steer.steer_type,
|
|
1128
1110
|
self.steer.r0,
|
|
1129
1111
|
self.steer.rm,
|
|
1130
|
-
2 * pi * self.freq / self.steer.env.c,
|
|
1112
|
+
2 * np.pi * self.freq / self.steer.env.c,
|
|
1131
1113
|
ind,
|
|
1132
1114
|
self.precision,
|
|
1133
1115
|
)
|
|
1134
1116
|
else:
|
|
1135
1117
|
# for arbitrary steering sectors, use general calculation. there is a version of this in
|
|
1136
1118
|
# fastFuncs, may be used later after runtime testing and debugging
|
|
1137
|
-
product = dot(self.steer.steer_vector(self.freq).conj(), self.steer.transfer(self.freq, ind).T)
|
|
1119
|
+
product = np.dot(self.steer.steer_vector(self.freq).conj(), self.steer.transfer(self.freq, ind).T)
|
|
1138
1120
|
result = (product * product.conj()).real
|
|
1139
1121
|
return result
|
|
1140
1122
|
|
|
1141
1123
|
|
|
1142
1124
|
class BeamformerDamas(BeamformerBase):
|
|
1143
|
-
"""
|
|
1125
|
+
"""
|
|
1126
|
+
DAMAS deconvolution algorithm.
|
|
1144
1127
|
|
|
1145
1128
|
See :cite:`Brooks2006` for details.
|
|
1146
1129
|
"""
|
|
@@ -1168,7 +1151,8 @@ class BeamformerDamas(BeamformerBase):
|
|
|
1168
1151
|
return digest(self)
|
|
1169
1152
|
|
|
1170
1153
|
def _calc(self, ind):
|
|
1171
|
-
"""
|
|
1154
|
+
"""
|
|
1155
|
+
Calculates the result for the frequencies defined by :attr:`freq_data`.
|
|
1172
1156
|
|
|
1173
1157
|
This is an internal helper function that is automatically called when
|
|
1174
1158
|
accessing the beamformer's :attr:`result` or calling
|
|
@@ -1183,14 +1167,13 @@ class BeamformerDamas(BeamformerBase):
|
|
|
1183
1167
|
Returns
|
|
1184
1168
|
-------
|
|
1185
1169
|
This method only returns values through :attr:`_ac` and :attr:`_fr`
|
|
1186
|
-
|
|
1187
1170
|
"""
|
|
1188
1171
|
f = self._f
|
|
1189
1172
|
normfactor = self.sig_loss_norm()
|
|
1190
1173
|
p = PointSpreadFunction(steer=self.steer, calcmode=self.calcmode, precision=self.psf_precision)
|
|
1191
1174
|
param_steer_type, steer_vector = self._beamformer_params()
|
|
1192
1175
|
for i in ind:
|
|
1193
|
-
csm = array(self.freq_data.csm[i], dtype='complex128')
|
|
1176
|
+
csm = np.array(self.freq_data.csm[i], dtype='complex128')
|
|
1194
1177
|
y = beamformerFreq(
|
|
1195
1178
|
param_steer_type,
|
|
1196
1179
|
self.r_diag,
|
|
@@ -1199,7 +1182,7 @@ class BeamformerDamas(BeamformerBase):
|
|
|
1199
1182
|
csm,
|
|
1200
1183
|
)[0]
|
|
1201
1184
|
if self.r_diag: # set (unphysical) negative output values to 0
|
|
1202
|
-
indNegSign = sign(y) < 0
|
|
1185
|
+
indNegSign = np.sign(y) < 0
|
|
1203
1186
|
y[indNegSign] = 0.0
|
|
1204
1187
|
x = y.copy()
|
|
1205
1188
|
p.freq = f[i]
|
|
@@ -1209,12 +1192,13 @@ class BeamformerDamas(BeamformerBase):
|
|
|
1209
1192
|
self._fr[i] = 1
|
|
1210
1193
|
|
|
1211
1194
|
|
|
1212
|
-
@deprecated_alias({'max_iter': 'n_iter'}, removal_version='25.10')
|
|
1213
1195
|
class BeamformerDamasPlus(BeamformerDamas):
|
|
1214
|
-
"""
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1196
|
+
"""
|
|
1197
|
+
DAMAS deconvolution :cite:`Brooks2006` for solving the system of equations.
|
|
1198
|
+
|
|
1199
|
+
Instead of the original Gauss-Seidel iterations, this class employs the NNLS or linear
|
|
1200
|
+
programming solvers from scipy.optimize or one of several optimization algorithms from the
|
|
1201
|
+
scikit-learn module. Needs a-priori delay-and-sum beamforming (:class:`BeamformerBase`).
|
|
1218
1202
|
"""
|
|
1219
1203
|
|
|
1220
1204
|
#: Type of fit method to be used ('LassoLars',
|
|
@@ -1250,7 +1234,8 @@ class BeamformerDamasPlus(BeamformerDamas):
|
|
|
1250
1234
|
return digest(self)
|
|
1251
1235
|
|
|
1252
1236
|
def _calc(self, ind):
|
|
1253
|
-
"""
|
|
1237
|
+
"""
|
|
1238
|
+
Calculates the result for the frequencies defined by :attr:`freq_data`.
|
|
1254
1239
|
|
|
1255
1240
|
This is an internal helper function that is automatically called when
|
|
1256
1241
|
accessing the beamformer's :attr:`result` or calling
|
|
@@ -1265,7 +1250,6 @@ class BeamformerDamasPlus(BeamformerDamas):
|
|
|
1265
1250
|
Returns
|
|
1266
1251
|
-------
|
|
1267
1252
|
This method only returns values through :attr:`_ac` and :attr:`_fr`
|
|
1268
|
-
|
|
1269
1253
|
"""
|
|
1270
1254
|
f = self._f
|
|
1271
1255
|
p = PointSpreadFunction(steer=self.steer, calcmode=self.calcmode, precision=self.psf_precision)
|
|
@@ -1273,7 +1257,7 @@ class BeamformerDamasPlus(BeamformerDamas):
|
|
|
1273
1257
|
normfactor = self.sig_loss_norm()
|
|
1274
1258
|
param_steer_type, steer_vector = self._beamformer_params()
|
|
1275
1259
|
for i in ind:
|
|
1276
|
-
csm = array(self.freq_data.csm[i], dtype='complex128')
|
|
1260
|
+
csm = np.array(self.freq_data.csm[i], dtype='complex128')
|
|
1277
1261
|
y = beamformerFreq(
|
|
1278
1262
|
param_steer_type,
|
|
1279
1263
|
self.r_diag,
|
|
@@ -1282,7 +1266,7 @@ class BeamformerDamasPlus(BeamformerDamas):
|
|
|
1282
1266
|
csm,
|
|
1283
1267
|
)[0]
|
|
1284
1268
|
if self.r_diag: # set (unphysical) negative output values to 0
|
|
1285
|
-
indNegSign = sign(y) < 0
|
|
1269
|
+
indNegSign = np.sign(y) < 0
|
|
1286
1270
|
y[indNegSign] = 0.0
|
|
1287
1271
|
y *= unit
|
|
1288
1272
|
p.freq = f[i]
|
|
@@ -1313,7 +1297,7 @@ class BeamformerDamasPlus(BeamformerDamas):
|
|
|
1313
1297
|
# pipeline approach with StandardScaler does scale in a different way, thus we
|
|
1314
1298
|
# monkeypatch the code and normalize ourselves to make results the same over
|
|
1315
1299
|
# different sklearn versions
|
|
1316
|
-
norms = norm(psf, axis=0)
|
|
1300
|
+
norms = spla.norm(psf, axis=0)
|
|
1317
1301
|
# get rid of annoying sklearn warnings that appear
|
|
1318
1302
|
# for sklearn<1.2 despite any settings
|
|
1319
1303
|
with warnings.catch_warnings():
|
|
@@ -1326,7 +1310,8 @@ class BeamformerDamasPlus(BeamformerDamas):
|
|
|
1326
1310
|
|
|
1327
1311
|
|
|
1328
1312
|
class BeamformerOrth(BeamformerBase):
|
|
1329
|
-
"""
|
|
1313
|
+
"""
|
|
1314
|
+
Orthogonal deconvolution algorithm.
|
|
1330
1315
|
|
|
1331
1316
|
See :cite:`Sarradj2010` for details.
|
|
1332
1317
|
New faster implementation without explicit (:class:`BeamformerEig`).
|
|
@@ -1334,7 +1319,7 @@ class BeamformerOrth(BeamformerBase):
|
|
|
1334
1319
|
|
|
1335
1320
|
#: List of components to consider, use this to directly set the eigenvalues
|
|
1336
1321
|
#: used in the beamformer. Alternatively, set :attr:`n`.
|
|
1337
|
-
eva_list = CArray(dtype=int, value=array([-1]), desc='components')
|
|
1322
|
+
eva_list = CArray(dtype=int, value=np.array([-1]), desc='components')
|
|
1338
1323
|
|
|
1339
1324
|
#: Number of components to consider, defaults to 1. If set,
|
|
1340
1325
|
#: :attr:`eva_list` will contain
|
|
@@ -1351,13 +1336,14 @@ class BeamformerOrth(BeamformerBase):
|
|
|
1351
1336
|
def _get_digest(self):
|
|
1352
1337
|
return digest(self)
|
|
1353
1338
|
|
|
1354
|
-
@
|
|
1355
|
-
def
|
|
1339
|
+
@observe('n')
|
|
1340
|
+
def _update_eva_list(self, event): # noqa ARG002
|
|
1356
1341
|
"""Sets the list of eigenvalues to consider."""
|
|
1357
|
-
self.eva_list = arange(-1, -1 - self.n, -1)
|
|
1342
|
+
self.eva_list = np.arange(-1, -1 - self.n, -1)
|
|
1358
1343
|
|
|
1359
1344
|
def _calc(self, ind):
|
|
1360
|
-
"""
|
|
1345
|
+
"""
|
|
1346
|
+
Calculates the result for the frequencies defined by :attr:`freq_data`.
|
|
1361
1347
|
|
|
1362
1348
|
This is an internal helper function that is automatically called when
|
|
1363
1349
|
accessing the beamformer's :attr:`result` or calling
|
|
@@ -1372,30 +1358,29 @@ class BeamformerOrth(BeamformerBase):
|
|
|
1372
1358
|
Returns
|
|
1373
1359
|
-------
|
|
1374
1360
|
This method only returns values through :attr:`_ac` and :attr:`_fr`
|
|
1375
|
-
|
|
1376
1361
|
"""
|
|
1377
1362
|
f = self._f
|
|
1378
1363
|
num_channels = self.freq_data.num_channels
|
|
1379
1364
|
normfactor = self.sig_loss_norm()
|
|
1380
1365
|
param_steer_type, steer_vector = self._beamformer_params()
|
|
1381
1366
|
for i in ind:
|
|
1382
|
-
eva = array(self.freq_data.eva[i], dtype='float64')
|
|
1383
|
-
eve = array(self.freq_data.eve[i], dtype='complex128')
|
|
1367
|
+
eva = np.array(self.freq_data.eva[i], dtype='float64')
|
|
1368
|
+
eve = np.array(self.freq_data.eve[i], dtype='complex128')
|
|
1384
1369
|
for n in self.eva_list:
|
|
1385
1370
|
beamformerOutput = beamformerFreq(
|
|
1386
1371
|
param_steer_type,
|
|
1387
1372
|
self.r_diag,
|
|
1388
1373
|
normfactor,
|
|
1389
1374
|
steer_vector(f[i]),
|
|
1390
|
-
(ones(1), eve[:, n].reshape((-1, 1))),
|
|
1375
|
+
(np.ones(1), eve[:, n].reshape((-1, 1))),
|
|
1391
1376
|
)[0]
|
|
1392
1377
|
self._ac[i, beamformerOutput.argmax()] += eva[n] / num_channels
|
|
1393
1378
|
self._fr[i] = 1
|
|
1394
1379
|
|
|
1395
1380
|
|
|
1396
|
-
@deprecated_alias({'n': 'n_iter'}, removal_version='25.10')
|
|
1397
1381
|
class BeamformerCleansc(BeamformerBase):
|
|
1398
|
-
"""
|
|
1382
|
+
"""
|
|
1383
|
+
CLEAN-SC deconvolution algorithm.
|
|
1399
1384
|
|
|
1400
1385
|
See :cite:`Sijtsma2007` for details.
|
|
1401
1386
|
Classic delay-and-sum beamforming is already included.
|
|
@@ -1422,7 +1407,8 @@ class BeamformerCleansc(BeamformerBase):
|
|
|
1422
1407
|
return digest(self)
|
|
1423
1408
|
|
|
1424
1409
|
def _calc(self, ind):
|
|
1425
|
-
"""
|
|
1410
|
+
"""
|
|
1411
|
+
Calculates the result for the frequencies defined by :attr:`freq_data`.
|
|
1426
1412
|
|
|
1427
1413
|
This is an internal helper function that is automatically called when
|
|
1428
1414
|
accessing the beamformer's :attr:`result` or calling
|
|
@@ -1437,18 +1423,17 @@ class BeamformerCleansc(BeamformerBase):
|
|
|
1437
1423
|
Returns
|
|
1438
1424
|
-------
|
|
1439
1425
|
This method only returns values through :attr:`_ac` and :attr:`_fr`
|
|
1440
|
-
|
|
1441
1426
|
"""
|
|
1442
1427
|
f = self._f
|
|
1443
1428
|
normfactor = self.sig_loss_norm()
|
|
1444
1429
|
num_channels = self.freq_data.num_channels
|
|
1445
|
-
result = zeros((self.steer.grid.size), 'f')
|
|
1430
|
+
result = np.zeros((self.steer.grid.size), 'f')
|
|
1446
1431
|
J = num_channels * 2 if not self.n_iter else self.n_iter
|
|
1447
|
-
powers = zeros(J, 'd')
|
|
1432
|
+
powers = np.zeros(J, 'd')
|
|
1448
1433
|
|
|
1449
1434
|
param_steer_type, steer_vector = self._beamformer_params()
|
|
1450
1435
|
for i in ind:
|
|
1451
|
-
csm = array(self.freq_data.csm[i], dtype='complex128', copy=
|
|
1436
|
+
csm = np.array(self.freq_data.csm[i], dtype='complex128', copy=True)
|
|
1452
1437
|
# h = self.steer._beamformerCall(f[i], self.r_diag, normfactor, (csm,))[0]
|
|
1453
1438
|
h = beamformerFreq(param_steer_type, self.r_diag, normfactor, steer_vector(f[i]), csm)[0]
|
|
1454
1439
|
# CLEANSC Iteration
|
|
@@ -1459,25 +1444,25 @@ class BeamformerCleansc(BeamformerBase):
|
|
|
1459
1444
|
result[xi_max] += self.damp * hmax
|
|
1460
1445
|
if j > self.stopn and hmax > powers[j - self.stopn]:
|
|
1461
1446
|
break
|
|
1462
|
-
wmax = self.steer.steer_vector(f[i], xi_max) * sqrt(normfactor)
|
|
1447
|
+
wmax = self.steer.steer_vector(f[i], xi_max) * np.sqrt(normfactor)
|
|
1463
1448
|
wmax = wmax[0].conj() # as old code worked with conjugated csm..should be updated
|
|
1464
1449
|
hh = wmax.copy()
|
|
1465
|
-
D1 = dot(csm.T - diag(diag(csm)), wmax) / hmax
|
|
1450
|
+
D1 = np.dot(csm.T - np.diag(np.diag(csm)), wmax) / hmax
|
|
1466
1451
|
ww = wmax.conj() * wmax
|
|
1467
|
-
for
|
|
1452
|
+
for _ in range(20):
|
|
1468
1453
|
H = hh.conj() * hh
|
|
1469
|
-
hh = (D1 + H * wmax) / sqrt(1 + dot(ww, H))
|
|
1470
|
-
hh = hh[:, newaxis]
|
|
1454
|
+
hh = (D1 + H * wmax) / np.sqrt(1 + np.dot(ww, H))
|
|
1455
|
+
hh = hh[:, np.newaxis]
|
|
1471
1456
|
csm1 = hmax * (hh * hh.conj().T)
|
|
1472
1457
|
|
|
1473
1458
|
# h1 = self.steer._beamformerCall(f[i], self.r_diag, normfactor, \
|
|
1474
|
-
# (array((hmax, ))[newaxis, :], hh[newaxis, :].conjugate()))[0]
|
|
1459
|
+
# (np.array((hmax, ))[np.newaxis, :], hh[np.newaxis, :].conjugate()))[0]
|
|
1475
1460
|
h1 = beamformerFreq(
|
|
1476
1461
|
param_steer_type,
|
|
1477
1462
|
self.r_diag,
|
|
1478
1463
|
normfactor,
|
|
1479
1464
|
steer_vector(f[i]),
|
|
1480
|
-
(array((hmax,)), hh.conj()),
|
|
1465
|
+
(np.array((hmax,)), hh.conj()),
|
|
1481
1466
|
)[0]
|
|
1482
1467
|
h -= self.damp * h1
|
|
1483
1468
|
csm -= self.damp * csm1.T # transpose(0,2,1)
|
|
@@ -1486,7 +1471,8 @@ class BeamformerCleansc(BeamformerBase):
|
|
|
1486
1471
|
|
|
1487
1472
|
|
|
1488
1473
|
class BeamformerClean(BeamformerBase):
|
|
1489
|
-
"""
|
|
1474
|
+
"""
|
|
1475
|
+
CLEAN deconvolution algorithm.
|
|
1490
1476
|
|
|
1491
1477
|
See :cite:`Hoegbom1974` for details.
|
|
1492
1478
|
"""
|
|
@@ -1529,7 +1515,6 @@ class BeamformerClean(BeamformerBase):
|
|
|
1529
1515
|
Returns
|
|
1530
1516
|
-------
|
|
1531
1517
|
This method only returns values through :attr:`_ac` and :attr:`_fr`
|
|
1532
|
-
|
|
1533
1518
|
"""
|
|
1534
1519
|
f = self._f
|
|
1535
1520
|
gs = self.steer.grid.size
|
|
@@ -1545,7 +1530,7 @@ class BeamformerClean(BeamformerBase):
|
|
|
1545
1530
|
param_steer_type, steer_vector = self._beamformer_params()
|
|
1546
1531
|
for i in ind:
|
|
1547
1532
|
p.freq = f[i]
|
|
1548
|
-
csm = array(self.freq_data.csm[i], dtype='complex128')
|
|
1533
|
+
csm = np.array(self.freq_data.csm[i], dtype='complex128')
|
|
1549
1534
|
dirty = beamformerFreq(
|
|
1550
1535
|
param_steer_type,
|
|
1551
1536
|
self.r_diag,
|
|
@@ -1554,16 +1539,16 @@ class BeamformerClean(BeamformerBase):
|
|
|
1554
1539
|
csm,
|
|
1555
1540
|
)[0]
|
|
1556
1541
|
if self.r_diag: # set (unphysical) negative output values to 0
|
|
1557
|
-
indNegSign = sign(dirty) < 0
|
|
1542
|
+
indNegSign = np.sign(dirty) < 0
|
|
1558
1543
|
dirty[indNegSign] = 0.0
|
|
1559
1544
|
|
|
1560
|
-
clean = zeros(gs, dtype=dirty.dtype)
|
|
1545
|
+
clean = np.zeros(gs, dtype=dirty.dtype)
|
|
1561
1546
|
i_iter = 0
|
|
1562
1547
|
flag = True
|
|
1563
1548
|
while flag:
|
|
1564
1549
|
dirty_sum = abs(dirty).sum(0)
|
|
1565
1550
|
next_max = dirty.argmax(0)
|
|
1566
|
-
p.grid_indices = array([next_max])
|
|
1551
|
+
p.grid_indices = np.array([next_max])
|
|
1567
1552
|
psf = p.psf.reshape(gs)
|
|
1568
1553
|
new_amp = self.damp * dirty[next_max] # / psf[next_max]
|
|
1569
1554
|
clean[next_max] += new_amp
|
|
@@ -1575,9 +1560,9 @@ class BeamformerClean(BeamformerBase):
|
|
|
1575
1560
|
self._fr[i] = 1
|
|
1576
1561
|
|
|
1577
1562
|
|
|
1578
|
-
@deprecated_alias({'max_iter': 'n_iter'}, removal_version='25.10')
|
|
1579
1563
|
class BeamformerCMF(BeamformerBase):
|
|
1580
|
-
"""
|
|
1564
|
+
"""
|
|
1565
|
+
Covariance Matrix Fitting algorithm.
|
|
1581
1566
|
|
|
1582
1567
|
This is not really a beamformer, but an inverse method.
|
|
1583
1568
|
See :cite:`Yardibi2008` for details.
|
|
@@ -1644,8 +1629,8 @@ class BeamformerCMF(BeamformerBase):
|
|
|
1644
1629
|
def _get_digest(self):
|
|
1645
1630
|
return digest(self)
|
|
1646
1631
|
|
|
1647
|
-
@
|
|
1648
|
-
def _validate(self):
|
|
1632
|
+
@observe('method')
|
|
1633
|
+
def _validate(self, event): # noqa ARG002
|
|
1649
1634
|
if self.method in ['FISTA', 'Split_Bregman'] and not config.have_pylops:
|
|
1650
1635
|
msg = (
|
|
1651
1636
|
'Cannot import Pylops package. No Pylops installed.'
|
|
@@ -1654,7 +1639,8 @@ class BeamformerCMF(BeamformerBase):
|
|
|
1654
1639
|
raise ImportError(msg)
|
|
1655
1640
|
|
|
1656
1641
|
def _calc(self, ind):
|
|
1657
|
-
"""
|
|
1642
|
+
"""
|
|
1643
|
+
Calculates the result for the frequencies defined by :attr:`freq_data`.
|
|
1658
1644
|
|
|
1659
1645
|
This is an internal helper function that is automatically called when
|
|
1660
1646
|
accessing the beamformer's :attr:`result` or calling
|
|
@@ -1669,13 +1655,12 @@ class BeamformerCMF(BeamformerBase):
|
|
|
1669
1655
|
Returns
|
|
1670
1656
|
-------
|
|
1671
1657
|
This method only returns values through :attr:`_ac` and :attr:`_fr`
|
|
1672
|
-
|
|
1673
1658
|
"""
|
|
1674
1659
|
f = self._f
|
|
1675
1660
|
|
|
1676
1661
|
# function to repack complex matrices to deal with them in real number space
|
|
1677
1662
|
def realify(matrix):
|
|
1678
|
-
return vstack([matrix.real, matrix.imag])
|
|
1663
|
+
return np.vstack([matrix.real, matrix.imag])
|
|
1679
1664
|
|
|
1680
1665
|
# prepare calculation
|
|
1681
1666
|
nc = self.freq_data.num_channels
|
|
@@ -1683,29 +1668,29 @@ class BeamformerCMF(BeamformerBase):
|
|
|
1683
1668
|
unit = self.unit_mult
|
|
1684
1669
|
|
|
1685
1670
|
for i in ind:
|
|
1686
|
-
csm = array(self.freq_data.csm[i], dtype='complex128', copy=
|
|
1671
|
+
csm = np.array(self.freq_data.csm[i], dtype='complex128', copy=True)
|
|
1687
1672
|
|
|
1688
1673
|
h = self.steer.transfer(f[i]).T
|
|
1689
1674
|
|
|
1690
1675
|
# reduced Kronecker product (only where solution matrix != 0)
|
|
1691
|
-
Bc = (h[:, :, newaxis] * h.conjugate().T[newaxis, :, :]).transpose(2, 0, 1)
|
|
1676
|
+
Bc = (h[:, :, np.newaxis] * h.conjugate().T[np.newaxis, :, :]).transpose(2, 0, 1)
|
|
1692
1677
|
Ac = Bc.reshape(nc * nc, num_points)
|
|
1693
1678
|
|
|
1694
|
-
# get indices for upper triangular matrices (use tril b/c transposed)
|
|
1695
|
-
ind = reshape(tril(ones((nc, nc))), (nc * nc,)) > 0
|
|
1679
|
+
# get indices for upper triangular matrices (use np.tril b/c transposed)
|
|
1680
|
+
ind = np.reshape(np.tril(np.ones((nc, nc))), (nc * nc,)) > 0
|
|
1696
1681
|
|
|
1697
|
-
ind_im0 = (reshape(eye(nc), (nc * nc,)) == 0)[ind]
|
|
1682
|
+
ind_im0 = (np.reshape(np.eye(nc), (nc * nc,)) == 0)[ind]
|
|
1698
1683
|
if self.r_diag:
|
|
1699
1684
|
# omit main diagonal for noise reduction
|
|
1700
|
-
ind_reim = hstack([ind_im0, ind_im0])
|
|
1685
|
+
ind_reim = np.hstack([ind_im0, ind_im0])
|
|
1701
1686
|
else:
|
|
1702
1687
|
# take all real parts -- also main diagonal
|
|
1703
|
-
ind_reim = hstack([ones(size(ind_im0)) > 0, ind_im0])
|
|
1688
|
+
ind_reim = np.hstack([np.ones(np.size(ind_im0)) > 0, ind_im0])
|
|
1704
1689
|
ind_reim[0] = True # why this ?
|
|
1705
1690
|
|
|
1706
1691
|
A = realify(Ac[ind, :])[ind_reim, :]
|
|
1707
1692
|
# use csm.T for column stacking reshape!
|
|
1708
|
-
R = realify(reshape(csm.T, (nc * nc, 1))[ind, :])[ind_reim, :] * unit
|
|
1693
|
+
R = realify(np.reshape(csm.T, (nc * nc, 1))[ind, :])[ind_reim, :] * unit
|
|
1709
1694
|
# choose method
|
|
1710
1695
|
if self.method == 'LassoLars':
|
|
1711
1696
|
model = LassoLars(alpha=self.alpha * unit, max_iter=self.n_iter, positive=True, **sklearn_ndict)
|
|
@@ -1759,13 +1744,13 @@ class BeamformerCMF(BeamformerBase):
|
|
|
1759
1744
|
# function
|
|
1760
1745
|
func = x.T @ A.T @ A @ x - 2 * R.T @ A @ x + R.T @ R
|
|
1761
1746
|
# derivitaive
|
|
1762
|
-
der = 2 * A.T @ A @ x.T[:, newaxis] - 2 * A.T @ R
|
|
1747
|
+
der = 2 * A.T @ A @ x.T[:, np.newaxis] - 2 * A.T @ R
|
|
1763
1748
|
return func[0].T, der[:, 0]
|
|
1764
1749
|
|
|
1765
1750
|
# initial guess
|
|
1766
|
-
x0 = ones([num_points])
|
|
1751
|
+
x0 = np.ones([num_points])
|
|
1767
1752
|
# boundaries - set to non negative
|
|
1768
|
-
boundaries = tile((0,
|
|
1753
|
+
boundaries = np.tile((0, np.inf), (len(x0), 1))
|
|
1769
1754
|
|
|
1770
1755
|
# optimize
|
|
1771
1756
|
self._ac[i], yval, dicts = fmin_l_bfgs_b(
|
|
@@ -1791,7 +1776,7 @@ class BeamformerCMF(BeamformerBase):
|
|
|
1791
1776
|
# pipeline approach with StandardScaler does scale in a different way, thus we
|
|
1792
1777
|
# monkeypatch the code and normalize ourselves to make results the same over
|
|
1793
1778
|
# different sklearn versions
|
|
1794
|
-
norms = norm(A, axis=0)
|
|
1779
|
+
norms = spla.norm(A, axis=0)
|
|
1795
1780
|
# get rid of sklearn warnings that appear for sklearn<1.2 despite any settings
|
|
1796
1781
|
with warnings.catch_warnings():
|
|
1797
1782
|
warnings.simplefilter('ignore', category=FutureWarning)
|
|
@@ -1802,9 +1787,9 @@ class BeamformerCMF(BeamformerBase):
|
|
|
1802
1787
|
self._fr[i] = 1
|
|
1803
1788
|
|
|
1804
1789
|
|
|
1805
|
-
@deprecated_alias({'max_iter': 'n_iter'}, removal_version='25.10')
|
|
1806
1790
|
class BeamformerSODIX(BeamformerBase):
|
|
1807
|
-
"""
|
|
1791
|
+
"""
|
|
1792
|
+
Source directivity modeling in the cross-spectral matrix (SODIX) algorithm.
|
|
1808
1793
|
|
|
1809
1794
|
See :cite:`Funke2017` and :cite:`Oertwig2019` for details.
|
|
1810
1795
|
"""
|
|
@@ -1854,7 +1839,8 @@ class BeamformerSODIX(BeamformerBase):
|
|
|
1854
1839
|
return digest(self)
|
|
1855
1840
|
|
|
1856
1841
|
def _calc(self, ind):
|
|
1857
|
-
"""
|
|
1842
|
+
"""
|
|
1843
|
+
Calculates the result for the frequencies defined by :attr:`freq_data`.
|
|
1858
1844
|
|
|
1859
1845
|
This is an internal helper function that is automatically called when
|
|
1860
1846
|
accessing the beamformer's :attr:`result` or calling
|
|
@@ -1869,7 +1855,6 @@ class BeamformerSODIX(BeamformerBase):
|
|
|
1869
1855
|
Returns
|
|
1870
1856
|
-------
|
|
1871
1857
|
This method only returns values through :attr:`_ac` and :attr:`_fr`
|
|
1872
|
-
|
|
1873
1858
|
"""
|
|
1874
1859
|
# prepare calculation
|
|
1875
1860
|
f = self._f
|
|
@@ -1884,14 +1869,17 @@ class BeamformerSODIX(BeamformerBase):
|
|
|
1884
1869
|
for i in range(1, ind.max() + 1):
|
|
1885
1870
|
if not self._fr[i]:
|
|
1886
1871
|
# measured csm
|
|
1887
|
-
csm = array(self.freq_data.csm[i], dtype='complex128', copy=
|
|
1872
|
+
csm = np.array(self.freq_data.csm[i], dtype='complex128', copy=True)
|
|
1888
1873
|
# transfer function
|
|
1889
1874
|
h = self.steer.transfer(f[i]).T
|
|
1890
1875
|
|
|
1891
1876
|
if self.method == 'fmin_l_bfgs_b':
|
|
1892
1877
|
# function to minimize
|
|
1893
1878
|
def function(directions):
|
|
1894
|
-
"""
|
|
1879
|
+
"""
|
|
1880
|
+
Calculates SODIX objective function and derivatives for optimization.
|
|
1881
|
+
|
|
1882
|
+
Parameters
|
|
1895
1883
|
----------
|
|
1896
1884
|
directions
|
|
1897
1885
|
[num_points*num_mics]
|
|
@@ -1902,41 +1890,43 @@ class BeamformerSODIX(BeamformerBase):
|
|
|
1902
1890
|
[1]
|
|
1903
1891
|
derdrl - derivitaives in direction of D
|
|
1904
1892
|
[num_mics*num_points].
|
|
1905
|
-
|
|
1906
1893
|
"""
|
|
1907
1894
|
#### the sodix function ####
|
|
1908
1895
|
Djm = directions.reshape([num_points, num_mics])
|
|
1909
1896
|
p = h.T * Djm
|
|
1910
|
-
csm_mod = dot(p.T, p.conj())
|
|
1897
|
+
csm_mod = np.dot(p.T, p.conj())
|
|
1911
1898
|
Q = csm - csm_mod
|
|
1912
|
-
func = sum((
|
|
1899
|
+
func = np.sum((np.abs(Q)) ** 2)
|
|
1913
1900
|
|
|
1914
1901
|
# subscripts and operands for numpy einsum and einsum_path
|
|
1915
1902
|
subscripts = 'rl,rm,ml->rl'
|
|
1916
1903
|
operands = (h.T, h.T.conj() * Djm, Q)
|
|
1917
|
-
es_path = einsum_path(subscripts, *operands, optimize='greedy')[0]
|
|
1904
|
+
es_path = np.einsum_path(subscripts, *operands, optimize='greedy')[0]
|
|
1918
1905
|
|
|
1919
1906
|
#### the sodix derivative ####
|
|
1920
|
-
derdrl = einsum(subscripts, *operands, optimize=es_path)
|
|
1921
|
-
derdrl = -4 * real(derdrl)
|
|
1907
|
+
derdrl = np.einsum(subscripts, *operands, optimize=es_path)
|
|
1908
|
+
derdrl = -4 * np.real(derdrl)
|
|
1922
1909
|
return func, derdrl.ravel()
|
|
1923
1910
|
|
|
1924
1911
|
##### initial guess ####
|
|
1925
1912
|
if not self._fr[(i - 1)]:
|
|
1926
|
-
D0 = ones([num_points, num_mics])
|
|
1913
|
+
D0 = np.ones([num_points, num_mics])
|
|
1927
1914
|
else:
|
|
1928
|
-
D0 = sqrt(
|
|
1915
|
+
D0 = np.sqrt(
|
|
1929
1916
|
self._ac[(i - 1)]
|
|
1930
|
-
* real(
|
|
1917
|
+
* np.real(
|
|
1918
|
+
np.trace(csm)
|
|
1919
|
+
/ np.trace(np.array(self.freq_data.csm[i - 1], dtype='complex128', copy=True))
|
|
1920
|
+
),
|
|
1931
1921
|
)
|
|
1932
1922
|
|
|
1933
1923
|
# boundaries - set to non negative [2*(num_points*num_mics)]
|
|
1934
|
-
boundaries = tile((0,
|
|
1924
|
+
boundaries = np.tile((0, np.inf), (num_points * num_mics, 1))
|
|
1935
1925
|
|
|
1936
1926
|
# optimize with gradient solver
|
|
1937
1927
|
# see https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fmin_l_bfgs_b.html
|
|
1938
1928
|
|
|
1939
|
-
qi = ones([num_points, num_mics])
|
|
1929
|
+
qi = np.ones([num_points, num_mics])
|
|
1940
1930
|
qi, yval, dicts = fmin_l_bfgs_b(
|
|
1941
1931
|
function,
|
|
1942
1932
|
D0,
|
|
@@ -1959,9 +1949,9 @@ class BeamformerSODIX(BeamformerBase):
|
|
|
1959
1949
|
self._fr[i] = 1
|
|
1960
1950
|
|
|
1961
1951
|
|
|
1962
|
-
@deprecated_alias({'max_iter': 'n_iter'}, removal_version='25.10')
|
|
1963
1952
|
class BeamformerGIB(BeamformerEig): # BeamformerEig #BeamformerBase
|
|
1964
|
-
"""
|
|
1953
|
+
"""
|
|
1954
|
+
Beamforming GIB methods with different normalizations.
|
|
1965
1955
|
|
|
1966
1956
|
See :cite:`Suzuki2011` for details.
|
|
1967
1957
|
"""
|
|
@@ -2052,7 +2042,8 @@ class BeamformerGIB(BeamformerEig): # BeamformerEig #BeamformerBase
|
|
|
2052
2042
|
return min(nm - 1, na)
|
|
2053
2043
|
|
|
2054
2044
|
def _calc(self, ind):
|
|
2055
|
-
"""
|
|
2045
|
+
"""
|
|
2046
|
+
Calculates the result for the frequencies defined by :attr:`freq_data`.
|
|
2056
2047
|
|
|
2057
2048
|
This is an internal helper function that is automatically called when
|
|
2058
2049
|
accessing the beamformer's :attr:`result` or calling
|
|
@@ -2067,14 +2058,13 @@ class BeamformerGIB(BeamformerEig): # BeamformerEig #BeamformerBase
|
|
|
2067
2058
|
Returns
|
|
2068
2059
|
-------
|
|
2069
2060
|
This method only returns values through :attr:`_ac` and :attr:`_fr`
|
|
2070
|
-
|
|
2071
2061
|
"""
|
|
2072
2062
|
f = self._f
|
|
2073
2063
|
n = int(self.na) # number of eigenvalues
|
|
2074
2064
|
m = int(self.m) # number of first eigenvalue
|
|
2075
2065
|
num_channels = self.freq_data.num_channels # number of channels
|
|
2076
2066
|
num_points = self.steer.grid.size
|
|
2077
|
-
hh = zeros((1, num_points, num_channels), dtype='D')
|
|
2067
|
+
hh = np.zeros((1, num_points, num_channels), dtype='D')
|
|
2078
2068
|
|
|
2079
2069
|
# Generate a cross spectral matrix, and perform the eigenvalue decomposition
|
|
2080
2070
|
for i in ind:
|
|
@@ -2083,79 +2073,83 @@ class BeamformerGIB(BeamformerEig): # BeamformerEig #BeamformerBase
|
|
|
2083
2073
|
hh = self.steer.transfer(f[i])
|
|
2084
2074
|
A = hh.T
|
|
2085
2075
|
# eigenvalues and vectors
|
|
2086
|
-
csm = array(self.freq_data.csm[i], dtype='complex128', copy=
|
|
2087
|
-
eva, eve = eigh(csm)
|
|
2076
|
+
csm = np.array(self.freq_data.csm[i], dtype='complex128', copy=True)
|
|
2077
|
+
eva, eve = spla.eigh(csm)
|
|
2088
2078
|
eva = eva[::-1]
|
|
2089
2079
|
eve = eve[:, ::-1]
|
|
2090
2080
|
# set small values zo 0, lowers numerical errors in simulated data
|
|
2091
2081
|
eva[eva < max(eva) / 1e12] = 0
|
|
2092
2082
|
# init sources
|
|
2093
|
-
qi = zeros([n + m, num_points], dtype='complex128')
|
|
2083
|
+
qi = np.zeros([n + m, num_points], dtype='complex128')
|
|
2094
2084
|
# Select the number of coherent modes to be processed referring to the eigenvalue
|
|
2095
2085
|
# distribution.
|
|
2096
2086
|
for s in list(range(m, n + m)):
|
|
2097
2087
|
if eva[s] > 0:
|
|
2098
2088
|
# Generate the corresponding eigenmodes
|
|
2099
|
-
emode = array(sqrt(eva[s]) * eve[:, s], dtype='complex128')
|
|
2089
|
+
emode = np.array(np.sqrt(eva[s]) * eve[:, s], dtype='complex128')
|
|
2100
2090
|
# choose method for computation
|
|
2101
2091
|
if self.method == 'Suzuki':
|
|
2102
2092
|
leftpoints = num_points
|
|
2103
|
-
locpoints = arange(num_points)
|
|
2104
|
-
weights = diag(ones(num_points))
|
|
2105
|
-
epsilon = arange(self.n_iter)
|
|
2106
|
-
for it in arange(self.n_iter):
|
|
2093
|
+
locpoints = np.arange(num_points)
|
|
2094
|
+
weights = np.diag(np.ones(num_points))
|
|
2095
|
+
epsilon = np.arange(self.n_iter)
|
|
2096
|
+
for it in np.arange(self.n_iter):
|
|
2107
2097
|
if num_channels <= leftpoints:
|
|
2108
|
-
AWA = dot(dot(A[:, locpoints], weights), A[:, locpoints].conj().T)
|
|
2109
|
-
epsilon[it] = max(
|
|
2110
|
-
qi[s, locpoints] = dot(
|
|
2111
|
-
dot(
|
|
2112
|
-
dot(weights, A[:, locpoints].conj().T),
|
|
2113
|
-
inv(AWA + eye(num_channels) * epsilon[it]),
|
|
2098
|
+
AWA = np.dot(np.dot(A[:, locpoints], weights), A[:, locpoints].conj().T)
|
|
2099
|
+
epsilon[it] = max(np.abs(spla.eigvals(AWA))) * self.eps_perc
|
|
2100
|
+
qi[s, locpoints] = np.dot(
|
|
2101
|
+
np.dot(
|
|
2102
|
+
np.dot(weights, A[:, locpoints].conj().T),
|
|
2103
|
+
spla.inv(AWA + np.eye(num_channels) * epsilon[it]),
|
|
2114
2104
|
),
|
|
2115
2105
|
emode,
|
|
2116
2106
|
)
|
|
2117
2107
|
elif num_channels > leftpoints:
|
|
2118
|
-
AA = dot(A[:, locpoints].conj().T, A[:, locpoints])
|
|
2119
|
-
epsilon[it] = max(
|
|
2120
|
-
qi[s, locpoints] = dot(
|
|
2121
|
-
dot(inv(AA + inv(weights) * epsilon[it]), A[:, locpoints].conj().T),
|
|
2108
|
+
AA = np.dot(A[:, locpoints].conj().T, A[:, locpoints])
|
|
2109
|
+
epsilon[it] = max(np.abs(spla.eigvals(AA))) * self.eps_perc
|
|
2110
|
+
qi[s, locpoints] = np.dot(
|
|
2111
|
+
np.dot(spla.inv(AA + spla.inv(weights) * epsilon[it]), A[:, locpoints].conj().T),
|
|
2122
2112
|
emode,
|
|
2123
2113
|
)
|
|
2124
2114
|
if self.beta < 1 and it > 1:
|
|
2125
2115
|
# Reorder from the greatest to smallest magnitude to define a
|
|
2126
2116
|
# reduced-point source distribution, and reform a reduced transfer
|
|
2127
2117
|
# matrix
|
|
2128
|
-
leftpoints = int(round(num_points * self.beta ** (it + 1)))
|
|
2129
|
-
idx = argsort(abs(qi[s, locpoints]))[::-1]
|
|
2118
|
+
leftpoints = int(np.round(num_points * self.beta ** (it + 1)))
|
|
2119
|
+
idx = np.argsort(abs(qi[s, locpoints]))[::-1]
|
|
2130
2120
|
# print(it, leftpoints, locpoints, idx )
|
|
2131
|
-
locpoints = delete(locpoints, [idx[leftpoints::]])
|
|
2132
|
-
qix = zeros([n + m, leftpoints], dtype='complex128')
|
|
2121
|
+
locpoints = np.delete(locpoints, [idx[leftpoints::]])
|
|
2122
|
+
qix = np.zeros([n + m, leftpoints], dtype='complex128')
|
|
2133
2123
|
qix[s, :] = qi[s, locpoints]
|
|
2134
2124
|
# calc weights for next iteration
|
|
2135
|
-
weights = diag(
|
|
2125
|
+
weights = np.diag(np.abs(qix[s, :]) ** (2 - self.pnorm))
|
|
2136
2126
|
else:
|
|
2137
|
-
weights = diag(
|
|
2127
|
+
weights = np.diag(np.abs(qi[s, :]) ** (2 - self.pnorm))
|
|
2138
2128
|
|
|
2139
2129
|
elif self.method == 'InverseIRLS':
|
|
2140
|
-
weights = eye(num_points)
|
|
2141
|
-
locpoints = arange(num_points)
|
|
2142
|
-
for _it in arange(self.n_iter):
|
|
2130
|
+
weights = np.eye(num_points)
|
|
2131
|
+
locpoints = np.arange(num_points)
|
|
2132
|
+
for _it in np.arange(self.n_iter):
|
|
2143
2133
|
if num_channels <= num_points:
|
|
2144
|
-
wtwi = inv(dot(weights.T, weights))
|
|
2134
|
+
wtwi = spla.inv(np.dot(weights.T, weights))
|
|
2145
2135
|
aH = A.conj().T
|
|
2146
|
-
qi[s, :] = dot(
|
|
2147
|
-
|
|
2148
|
-
|
|
2136
|
+
qi[s, :] = np.dot(
|
|
2137
|
+
np.dot(wtwi, aH), np.dot(spla.inv(np.dot(A, np.dot(wtwi, aH))), emode)
|
|
2138
|
+
)
|
|
2139
|
+
weights = np.diag(np.abs(qi[s, :]) ** ((2 - self.pnorm) / 2))
|
|
2140
|
+
weights = weights / np.sum(np.abs(weights))
|
|
2149
2141
|
elif num_channels > num_points:
|
|
2150
|
-
wtw = dot(weights.T, weights)
|
|
2151
|
-
qi[s, :] =
|
|
2152
|
-
|
|
2153
|
-
|
|
2142
|
+
wtw = np.dot(weights.T, weights)
|
|
2143
|
+
qi[s, :] = np.dot(
|
|
2144
|
+
np.dot(spla.inv(np.dot(np.dot(A.conj.T, wtw), A)), np.dot(A.conj().T, wtw)), emode
|
|
2145
|
+
)
|
|
2146
|
+
weights = np.diag(np.abs(qi[s, :]) ** ((2 - self.pnorm) / 2))
|
|
2147
|
+
weights = weights / np.sum(np.abs(weights))
|
|
2154
2148
|
else:
|
|
2155
|
-
locpoints = arange(num_points)
|
|
2149
|
+
locpoints = np.arange(num_points)
|
|
2156
2150
|
unit = self.unit_mult
|
|
2157
|
-
AB = vstack([hstack([A.real, -A.imag]), hstack([A.imag, A.real])])
|
|
2158
|
-
R = hstack([emode.real.T, emode.imag.T]) * unit
|
|
2151
|
+
AB = np.vstack([np.hstack([A.real, -A.imag]), np.hstack([A.imag, A.real])])
|
|
2152
|
+
R = np.hstack([emode.real.T, emode.imag.T]) * unit
|
|
2159
2153
|
if self.method == 'LassoLars':
|
|
2160
2154
|
model = LassoLars(alpha=self.alpha * unit, max_iter=self.n_iter, positive=True)
|
|
2161
2155
|
elif self.method == 'LassoLarsBIC':
|
|
@@ -2173,7 +2167,7 @@ class BeamformerGIB(BeamformerEig): # BeamformerEig #BeamformerBase
|
|
|
2173
2167
|
# way, thus we monkeypatch the code and normalize
|
|
2174
2168
|
# ourselves to make results the same over different
|
|
2175
2169
|
# sklearn versions
|
|
2176
|
-
norms = norm(AB, axis=0)
|
|
2170
|
+
norms = spla.norm(AB, axis=0)
|
|
2177
2171
|
# get rid of annoying sklearn warnings that appear
|
|
2178
2172
|
# for sklearn<1.2 despite any settings
|
|
2179
2173
|
with warnings.catch_warnings():
|
|
@@ -2181,7 +2175,7 @@ class BeamformerGIB(BeamformerEig): # BeamformerEig #BeamformerBase
|
|
|
2181
2175
|
# normalized A
|
|
2182
2176
|
model.fit(AB / norms, R)
|
|
2183
2177
|
# recover normalization in the coef's
|
|
2184
|
-
qi_real, qi_imag = hsplit(model.coef_[:] / norms / unit, 2)
|
|
2178
|
+
qi_real, qi_imag = np.hsplit(model.coef_[:] / norms / unit, 2)
|
|
2185
2179
|
# print(s,qi.size)
|
|
2186
2180
|
qi[s, locpoints] = qi_real + qi_imag * 1j
|
|
2187
2181
|
else:
|
|
@@ -2192,8 +2186,8 @@ class BeamformerGIB(BeamformerEig): # BeamformerEig #BeamformerBase
|
|
|
2192
2186
|
)
|
|
2193
2187
|
# Generate source maps of all selected eigenmodes, and superpose source intensity
|
|
2194
2188
|
# for each source type.
|
|
2195
|
-
temp = zeros(num_points)
|
|
2196
|
-
temp[locpoints] = sum(
|
|
2189
|
+
temp = np.zeros(num_points)
|
|
2190
|
+
temp[locpoints] = np.sum(np.abs(qi[:, locpoints]) ** 2, axis=0)
|
|
2197
2191
|
self._ac[i] = temp
|
|
2198
2192
|
self._fr[i] = 1
|
|
2199
2193
|
|
|
@@ -2211,7 +2205,8 @@ class BeamformerAdaptiveGrid(BeamformerBase, Grid):
|
|
|
2211
2205
|
return self._gpos
|
|
2212
2206
|
|
|
2213
2207
|
def integrate(self, sector):
|
|
2214
|
-
"""
|
|
2208
|
+
"""
|
|
2209
|
+
Integrates result map over a given sector.
|
|
2215
2210
|
|
|
2216
2211
|
Parameters
|
|
2217
2212
|
----------
|
|
@@ -2222,7 +2217,6 @@ class BeamformerAdaptiveGrid(BeamformerBase, Grid):
|
|
|
2222
2217
|
-------
|
|
2223
2218
|
array of floats
|
|
2224
2219
|
The spectrum (all calculated frequency bands) for the integrated sector.
|
|
2225
|
-
|
|
2226
2220
|
"""
|
|
2227
2221
|
if not isinstance(sector, Sector):
|
|
2228
2222
|
msg = (
|
|
@@ -2235,21 +2229,22 @@ class BeamformerAdaptiveGrid(BeamformerBase, Grid):
|
|
|
2235
2229
|
|
|
2236
2230
|
ind = self.subdomain(sector)
|
|
2237
2231
|
r = self.result
|
|
2238
|
-
h = zeros(r.shape[0])
|
|
2232
|
+
h = np.zeros(r.shape[0])
|
|
2239
2233
|
for i in range(r.shape[0]):
|
|
2240
2234
|
h[i] = r[i][ind].sum()
|
|
2241
2235
|
return h
|
|
2242
2236
|
|
|
2243
2237
|
|
|
2244
2238
|
class BeamformerGridlessOrth(BeamformerAdaptiveGrid):
|
|
2245
|
-
"""
|
|
2239
|
+
"""
|
|
2240
|
+
Orthogonal beamforming without predefined grid.
|
|
2246
2241
|
|
|
2247
2242
|
See :cite:`Sarradj2022` for details.
|
|
2248
2243
|
"""
|
|
2249
2244
|
|
|
2250
2245
|
#: List of components to consider, use this to directly set the eigenvalues
|
|
2251
2246
|
#: used in the beamformer. Alternatively, set :attr:`n`.
|
|
2252
|
-
eva_list = CArray(dtype=int, value=array([-1]), desc='components')
|
|
2247
|
+
eva_list = CArray(dtype=int, value=np.array([-1]), desc='components')
|
|
2253
2248
|
|
|
2254
2249
|
#: Number of components to consider, defaults to 1. If set,
|
|
2255
2250
|
#: :attr:`eva_list` will contain
|
|
@@ -2286,17 +2281,18 @@ class BeamformerGridlessOrth(BeamformerAdaptiveGrid):
|
|
|
2286
2281
|
def _get_digest(self):
|
|
2287
2282
|
return digest(self)
|
|
2288
2283
|
|
|
2289
|
-
@
|
|
2290
|
-
def
|
|
2284
|
+
@observe('n')
|
|
2285
|
+
def _update_eva_list(self, event): # noqa ARG002
|
|
2291
2286
|
"""Sets the list of eigenvalues to consider."""
|
|
2292
|
-
self.eva_list = arange(-1, -1 - self.n, -1)
|
|
2287
|
+
self.eva_list = np.arange(-1, -1 - self.n, -1)
|
|
2293
2288
|
|
|
2294
2289
|
@property_depends_on('n')
|
|
2295
2290
|
def _get_size(self):
|
|
2296
2291
|
return self.n * self.freq_data.fftfreq().shape[0]
|
|
2297
2292
|
|
|
2298
2293
|
def _calc(self, ind):
|
|
2299
|
-
"""
|
|
2294
|
+
"""
|
|
2295
|
+
Calculates the result for the frequencies defined by :attr:`freq_data`.
|
|
2300
2296
|
|
|
2301
2297
|
This is an internal helper function that is automatically called when
|
|
2302
2298
|
accessing the beamformer's :attr:`result` or calling
|
|
@@ -2311,13 +2307,12 @@ class BeamformerGridlessOrth(BeamformerAdaptiveGrid):
|
|
|
2311
2307
|
Returns
|
|
2312
2308
|
-------
|
|
2313
2309
|
This method only returns values through :attr:`_ac` and :attr:`_fr`
|
|
2314
|
-
|
|
2315
2310
|
"""
|
|
2316
2311
|
f = self._f
|
|
2317
2312
|
normfactor = self.sig_loss_norm()
|
|
2318
2313
|
num_channels = self.freq_data.num_channels
|
|
2319
2314
|
# eigenvalue number list in standard form from largest to smallest
|
|
2320
|
-
eva_list = unique(self.eva_list % self.steer.mics.num_mics)[::-1]
|
|
2315
|
+
eva_list = np.unique(self.eva_list % self.steer.mics.num_mics)[::-1]
|
|
2321
2316
|
steer_type = self.steer.steer_type
|
|
2322
2317
|
if steer_type == 'custom':
|
|
2323
2318
|
msg = 'custom steer_type is not implemented'
|
|
@@ -2336,27 +2331,27 @@ class BeamformerGridlessOrth(BeamformerAdaptiveGrid):
|
|
|
2336
2331
|
for y in self.bounds[1]:
|
|
2337
2332
|
for z in self.bounds[2]:
|
|
2338
2333
|
roi.append((x, y, z))
|
|
2339
|
-
self.steer.env.roi = array(roi).T
|
|
2340
|
-
bmin = array(tuple(map(min, self.bounds)))
|
|
2341
|
-
bmax = array(tuple(map(max, self.bounds)))
|
|
2334
|
+
self.steer.env.roi = np.array(roi).T
|
|
2335
|
+
bmin = np.array(tuple(map(min, self.bounds)))
|
|
2336
|
+
bmax = np.array(tuple(map(max, self.bounds)))
|
|
2342
2337
|
for i in ind:
|
|
2343
|
-
eva = array(self.freq_data.eva[i], dtype='float64')
|
|
2344
|
-
eve = array(self.freq_data.eve[i], dtype='complex128')
|
|
2345
|
-
k = 2 * pi * f[i] / env.c
|
|
2338
|
+
eva = np.array(self.freq_data.eva[i], dtype='float64')
|
|
2339
|
+
eve = np.array(self.freq_data.eve[i], dtype='complex128')
|
|
2340
|
+
k = 2 * np.pi * f[i] / env.c
|
|
2346
2341
|
for j, n in enumerate(eva_list):
|
|
2347
2342
|
# print(f[i],n)
|
|
2348
2343
|
|
|
2349
2344
|
def func(xy):
|
|
2350
2345
|
# function to minimize globally
|
|
2351
|
-
xy = clip(xy, bmin, bmax)
|
|
2352
|
-
r0 = env._r(xy[:, newaxis])
|
|
2353
|
-
rm = env._r(xy[:, newaxis], mpos)
|
|
2346
|
+
xy = np.clip(xy, bmin, bmax)
|
|
2347
|
+
r0 = env._r(xy[:, np.newaxis])
|
|
2348
|
+
rm = env._r(xy[:, np.newaxis], mpos)
|
|
2354
2349
|
return -beamformerFreq(
|
|
2355
2350
|
steer_type,
|
|
2356
2351
|
self.r_diag,
|
|
2357
2352
|
normfactor,
|
|
2358
2353
|
(r0, rm, k),
|
|
2359
|
-
(ones(1), eve[:, n : n + 1]),
|
|
2354
|
+
(np.ones(1), eve[:, n : n + 1]),
|
|
2360
2355
|
)[0][0] # noqa: B023
|
|
2361
2356
|
|
|
2362
2357
|
# simplical global homotopy optimizer
|
|
@@ -2372,7 +2367,8 @@ class BeamformerGridlessOrth(BeamformerAdaptiveGrid):
|
|
|
2372
2367
|
|
|
2373
2368
|
|
|
2374
2369
|
def L_p(x): # noqa: N802
|
|
2375
|
-
r"""
|
|
2370
|
+
r"""
|
|
2371
|
+
Calculates the sound pressure level from the squared sound pressure.
|
|
2376
2372
|
|
|
2377
2373
|
:math:`L_p = 10 \lg ( x / 4\cdot 10^{-10})`
|
|
2378
2374
|
|
|
@@ -2386,17 +2382,17 @@ def L_p(x): # noqa: N802
|
|
|
2386
2382
|
array of floats
|
|
2387
2383
|
The corresponding sound pressure levels in dB.
|
|
2388
2384
|
If `x<0`, -350.0 dB is returned.
|
|
2389
|
-
|
|
2390
2385
|
"""
|
|
2391
2386
|
# new version to prevent division by zero warning for float32 arguments
|
|
2392
|
-
return 10 * log10(clip(x / 4e-10, 1e-35, None))
|
|
2387
|
+
return 10 * np.log10(np.clip(x / 4e-10, 1e-35, None))
|
|
2393
2388
|
|
|
2394
2389
|
|
|
2395
|
-
# return where(x>0, 10*log10(x/4e-10), -1000.)
|
|
2390
|
+
# return where(x>0, 10*np.log10(x/4e-10), -1000.)
|
|
2396
2391
|
|
|
2397
2392
|
|
|
2398
2393
|
def integrate(data, grid, sector):
|
|
2399
|
-
"""
|
|
2394
|
+
"""
|
|
2395
|
+
Integrates a sound pressure map over a given sector.
|
|
2400
2396
|
|
|
2401
2397
|
This function can be applied on beamforming results to
|
|
2402
2398
|
quantitatively analyze the sound pressure in a given sector.
|
|
@@ -2424,8 +2420,8 @@ def integrate(data, grid, sector):
|
|
|
2424
2420
|
of a :class:`~acoular.grids.Grid`-derived class
|
|
2425
2421
|
(e.g. :meth:`RectGrid.indices<acoular.grids.RectGrid.indices>`
|
|
2426
2422
|
or :meth:`RectGrid3D.indices<acoular.grids.RectGrid3D.indices>`).
|
|
2427
|
-
Possible sectors would be `array([xmin, ymin, xmax, ymax])`
|
|
2428
|
-
or `array([x, y, radius])`.
|
|
2423
|
+
Possible sectors would be `np.array([xmin, ymin, xmax, ymax])`
|
|
2424
|
+
or `np.array([x, y, radius])`.
|
|
2429
2425
|
Alternatively, a :class:`~acoular.grids.Sector`-derived object
|
|
2430
2426
|
can be used.
|
|
2431
2427
|
|
|
@@ -2433,7 +2429,6 @@ def integrate(data, grid, sector):
|
|
|
2433
2429
|
-------
|
|
2434
2430
|
array of floats
|
|
2435
2431
|
The spectrum (all calculated frequency bands) for the integrated sector.
|
|
2436
|
-
|
|
2437
2432
|
"""
|
|
2438
2433
|
if isinstance(sector, Sector):
|
|
2439
2434
|
ind = grid.subdomain(sector)
|
|
@@ -2451,10 +2446,10 @@ def integrate(data, grid, sector):
|
|
|
2451
2446
|
|
|
2452
2447
|
gshape = grid.shape
|
|
2453
2448
|
gsize = grid.size
|
|
2454
|
-
if size(data) == gsize: # one value per grid point
|
|
2449
|
+
if np.size(data) == gsize: # one value per grid point
|
|
2455
2450
|
h = data.reshape(gshape)[ind].sum()
|
|
2456
2451
|
elif data.ndim == 2 and data.shape[1] == gsize:
|
|
2457
|
-
h = zeros(data.shape[0])
|
|
2452
|
+
h = np.zeros(data.shape[0])
|
|
2458
2453
|
for i in range(data.shape[0]):
|
|
2459
2454
|
h[i] = data[i].reshape(gshape)[ind].sum()
|
|
2460
2455
|
return h
|