acoular 24.3__py3-none-any.whl → 24.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- acoular/__init__.py +119 -54
- acoular/calib.py +29 -38
- acoular/configuration.py +132 -82
- acoular/demo/__init__.py +10 -4
- acoular/demo/acoular_demo.py +73 -55
- acoular/environments.py +270 -264
- acoular/fastFuncs.py +366 -196
- acoular/fbeamform.py +1797 -1934
- acoular/grids.py +504 -548
- acoular/h5cache.py +74 -83
- acoular/h5files.py +159 -142
- acoular/internal.py +13 -14
- acoular/microphones.py +57 -53
- acoular/sdinput.py +57 -53
- acoular/signals.py +180 -178
- acoular/sources.py +920 -724
- acoular/spectra.py +353 -363
- acoular/tbeamform.py +416 -416
- acoular/tfastfuncs.py +180 -104
- acoular/tools/__init__.py +25 -0
- acoular/tools/aiaa.py +185 -0
- acoular/tools/helpers.py +189 -0
- acoular/tools/metrics.py +165 -0
- acoular/tprocess.py +1240 -1182
- acoular/traitsviews.py +513 -501
- acoular/trajectory.py +50 -52
- acoular/version.py +5 -6
- acoular/xml/minidsp_uma-16.xml +20 -0
- acoular/xml/{minidsp_uma16.xml → minidsp_uma-16_mirrored.xml} +3 -0
- {acoular-24.3.dist-info → acoular-24.7.dist-info}/METADATA +58 -39
- acoular-24.7.dist-info/RECORD +50 -0
- {acoular-24.3.dist-info → acoular-24.7.dist-info}/WHEEL +1 -1
- acoular-24.7.dist-info/licenses/LICENSE +28 -0
- acoular/fileimport.py +0 -380
- acoular/nidaqimport.py +0 -273
- acoular/tests/reference_data/BeamformerBase.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse1.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse2.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse3.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseFalse4.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue1.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue2.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue3.npy +0 -0
- acoular/tests/reference_data/BeamformerBaseTrue4.npy +0 -0
- acoular/tests/reference_data/BeamformerCMFLassoLarsBIC.npy +0 -0
- acoular/tests/reference_data/BeamformerCMFNNLS.npy +0 -0
- acoular/tests/reference_data/BeamformerCapon.npy +0 -0
- acoular/tests/reference_data/BeamformerClean.npy +0 -0
- acoular/tests/reference_data/BeamformerCleansc.npy +0 -0
- acoular/tests/reference_data/BeamformerCleant.npy +0 -0
- acoular/tests/reference_data/BeamformerCleantSq.npy +0 -0
- acoular/tests/reference_data/BeamformerCleantSqTraj.npy +0 -0
- acoular/tests/reference_data/BeamformerCleantTraj.npy +0 -0
- acoular/tests/reference_data/BeamformerDamas.npy +0 -0
- acoular/tests/reference_data/BeamformerDamasPlus.npy +0 -0
- acoular/tests/reference_data/BeamformerEig.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse1.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse2.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse3.npy +0 -0
- acoular/tests/reference_data/BeamformerEigFalse4.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue1.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue2.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue3.npy +0 -0
- acoular/tests/reference_data/BeamformerEigTrue4.npy +0 -0
- acoular/tests/reference_data/BeamformerFunctional.npy +0 -0
- acoular/tests/reference_data/BeamformerGIB.npy +0 -0
- acoular/tests/reference_data/BeamformerGridlessOrth.npy +0 -0
- acoular/tests/reference_data/BeamformerMusic.npy +0 -0
- acoular/tests/reference_data/BeamformerOrth.npy +0 -0
- acoular/tests/reference_data/BeamformerSODIX.npy +0 -0
- acoular/tests/reference_data/BeamformerTime.npy +0 -0
- acoular/tests/reference_data/BeamformerTimeSq.npy +0 -0
- acoular/tests/reference_data/BeamformerTimeSqTraj.npy +0 -0
- acoular/tests/reference_data/BeamformerTimeTraj.npy +0 -0
- acoular/tests/reference_data/Environment.npy +0 -0
- acoular/tests/reference_data/Example1_numerical_values_testsum.h5 +0 -0
- acoular/tests/reference_data/FiltFiltOctave__.npy +0 -0
- acoular/tests/reference_data/FiltFiltOctave_band_100_0_fraction_Thirdoctave_.npy +0 -0
- acoular/tests/reference_data/FiltFreqWeight_weight_A_.npy +0 -0
- acoular/tests/reference_data/FiltFreqWeight_weight_C_.npy +0 -0
- acoular/tests/reference_data/FiltFreqWeight_weight_Z_.npy +0 -0
- acoular/tests/reference_data/FiltOctave__.npy +0 -0
- acoular/tests/reference_data/FiltOctave_band_100_0_fraction_Thirdoctave_.npy +0 -0
- acoular/tests/reference_data/Filter__.npy +0 -0
- acoular/tests/reference_data/GeneralFlowEnvironment.npy +0 -0
- acoular/tests/reference_data/OctaveFilterBank__.npy +0 -0
- acoular/tests/reference_data/OpenJet.npy +0 -0
- acoular/tests/reference_data/PointSource.npy +0 -0
- acoular/tests/reference_data/PowerSpectra_csm.npy +0 -0
- acoular/tests/reference_data/PowerSpectra_ev.npy +0 -0
- acoular/tests/reference_data/RotatingFlow.npy +0 -0
- acoular/tests/reference_data/SlotJet.npy +0 -0
- acoular/tests/reference_data/TimeAverage__.npy +0 -0
- acoular/tests/reference_data/TimeCumAverage__.npy +0 -0
- acoular/tests/reference_data/TimeExpAverage_weight_F_.npy +0 -0
- acoular/tests/reference_data/TimeExpAverage_weight_I_.npy +0 -0
- acoular/tests/reference_data/TimeExpAverage_weight_S_.npy +0 -0
- acoular/tests/reference_data/TimeInOut__.npy +0 -0
- acoular/tests/reference_data/TimePower__.npy +0 -0
- acoular/tests/reference_data/TimeReverse__.npy +0 -0
- acoular/tests/reference_data/UniformFlowEnvironment.npy +0 -0
- acoular/tests/reference_data/beamformer_traj_time_data.h5 +0 -0
- acoular/tests/run_tests.sh +0 -18
- acoular/tests/run_tests_osx.sh +0 -16
- acoular/tests/test.npy +0 -0
- acoular/tests/test_beamformer_results.py +0 -213
- acoular/tests/test_classes.py +0 -60
- acoular/tests/test_digest.py +0 -125
- acoular/tests/test_environments.py +0 -73
- acoular/tests/test_example1.py +0 -124
- acoular/tests/test_grid.py +0 -92
- acoular/tests/test_integrate.py +0 -102
- acoular/tests/test_signals.py +0 -60
- acoular/tests/test_sources.py +0 -65
- acoular/tests/test_spectra.py +0 -38
- acoular/tests/test_timecache.py +0 -35
- acoular/tests/test_tprocess.py +0 -90
- acoular/tests/test_traj_beamformer_results.py +0 -164
- acoular/tests/unsupported/SpeedComparison/OvernightTestcasesBeamformer_nMics32_nGridPoints100_nFreqs4_nTrials10.png +0 -0
- acoular/tests/unsupported/SpeedComparison/cythonBeamformer.pyx +0 -237
- acoular/tests/unsupported/SpeedComparison/mainForCython.py +0 -103
- acoular/tests/unsupported/SpeedComparison/mainForParallelJit.py +0 -143
- acoular/tests/unsupported/SpeedComparison/setupCythonOpenMP.py +0 -63
- acoular/tests/unsupported/SpeedComparison/sharedFunctions.py +0 -153
- acoular/tests/unsupported/SpeedComparison/timeOverNMics_AllImportantMethods.png +0 -0
- acoular/tests/unsupported/SpeedComparison/timeOverNMics_faverage.png +0 -0
- acoular/tests/unsupported/SpeedComparison/vglOptimierungFAverage.py +0 -204
- acoular/tests/unsupported/SpeedComparison/vglOptimierungGaussSeidel.py +0 -182
- acoular/tests/unsupported/SpeedComparison/vglOptimierungR_BEAMFULL_INVERSE.py +0 -764
- acoular/tests/unsupported/SpeedComparison/vglOptimierungR_BEAM_OS.py +0 -231
- acoular/tests/unsupported/SpeedComparison/whatsFastestWayFor_absASquared.py +0 -48
- acoular/tests/unsupported/functionalBeamformer.py +0 -123
- acoular/tests/unsupported/precisionTest.py +0 -153
- acoular/tests/unsupported/validationOfBeamformerFuncsPOSTAcoularIntegration.py +0 -254
- acoular/tests/unsupported/validationOfBeamformerFuncsPREeAcoularIntegration.py +0 -531
- acoular/tools.py +0 -422
- acoular-24.3.dist-info/RECORD +0 -148
- acoular-24.3.dist-info/licenses/LICENSE +0 -29
- {acoular-24.3.dist-info → acoular-24.7.dist-info}/licenses/AUTHORS.rst +0 -0
acoular/fastFuncs.py
CHANGED
|
@@ -1,41 +1,47 @@
|
|
|
1
|
-
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
#------------------------------------------------------------------------------
|
|
1
|
+
# ------------------------------------------------------------------------------
|
|
4
2
|
# Copyright (c) Acoular Development Team.
|
|
5
|
-
|
|
6
|
-
"""
|
|
7
|
-
This file contains all the functionalities which are very expansive, regarding
|
|
3
|
+
# ------------------------------------------------------------------------------
|
|
4
|
+
"""Contains all the functionalities which are very expansive, regarding
|
|
8
5
|
computational costs. All functionalities are optimized via NUMBA.
|
|
9
6
|
"""
|
|
10
|
-
|
|
7
|
+
|
|
11
8
|
import numba as nb
|
|
9
|
+
import numpy as np
|
|
12
10
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
CACHED_OPTION = True # if True: saves the numba func as compiled func in sub directory
|
|
12
|
+
PARALLEL_OPTION = 'parallel' # if numba.guvectorize is used: 'CPU' for single threading; 'parallel' for multithreading; 'cuda' for calculating on GPU
|
|
13
|
+
FAST_OPTION = True # fastmath options
|
|
16
14
|
|
|
17
15
|
|
|
18
16
|
# Formerly known as 'faverage'
|
|
19
|
-
@nb.njit(
|
|
20
|
-
|
|
17
|
+
@nb.njit(
|
|
18
|
+
[
|
|
19
|
+
nb.complex128[:, :, ::1](nb.complex128[:, :, ::1], nb.complex128[:, ::1]),
|
|
20
|
+
nb.complex64[:, :, ::1](nb.complex64[:, :, ::1], nb.complex64[:, ::1]),
|
|
21
|
+
],
|
|
22
|
+
cache=CACHED_OPTION,
|
|
23
|
+
parallel=True,
|
|
24
|
+
fastmath=FAST_OPTION,
|
|
25
|
+
)
|
|
21
26
|
def calcCSM(csm, SpecAllMics):
|
|
22
|
-
"""
|
|
27
|
+
"""Adds a given spectrum to the Cross-Spectral-Matrix (CSM).
|
|
23
28
|
Here only the upper triangular matrix of the CSM is calculated. After
|
|
24
|
-
averaging over the various ensembles, the whole CSM is created via complex
|
|
25
|
-
conjugation transposing. This happens outside
|
|
26
|
-
(in :class:`PowerSpectra<acoular.spectra.PowerSpectra>`).
|
|
29
|
+
averaging over the various ensembles, the whole CSM is created via complex
|
|
30
|
+
conjugation transposing. This happens outside
|
|
31
|
+
(in :class:`PowerSpectra<acoular.spectra.PowerSpectra>`).
|
|
27
32
|
This method was called 'faverage' in acoular versions <= 16.5.
|
|
28
|
-
|
|
33
|
+
|
|
29
34
|
Parameters
|
|
30
35
|
----------
|
|
31
|
-
csm : complex128[nFreqs, nMics, nMics]
|
|
36
|
+
csm : complex128[nFreqs, nMics, nMics]
|
|
32
37
|
The cross spectral matrix which gets updated with the spectrum of the ensemble.
|
|
33
|
-
SpecAllMics : complex128[nFreqs, nMics]
|
|
38
|
+
SpecAllMics : complex128[nFreqs, nMics]
|
|
34
39
|
Spectrum of the added ensemble at all Mics.
|
|
35
|
-
|
|
40
|
+
|
|
36
41
|
Returns
|
|
37
42
|
-------
|
|
38
43
|
None : as the input csm gets overwritten.
|
|
44
|
+
|
|
39
45
|
"""
|
|
40
46
|
nFreqs = csm.shape[0]
|
|
41
47
|
nMics = csm.shape[1]
|
|
@@ -46,9 +52,9 @@ def calcCSM(csm, SpecAllMics):
|
|
|
46
52
|
csm[cntFreq, cntRow, cntColumn] += temp * SpecAllMics[cntFreq, cntRow]
|
|
47
53
|
return csm
|
|
48
54
|
|
|
49
|
-
|
|
55
|
+
|
|
50
56
|
def beamformerFreq(steerVecType, boolRemovedDiagOfCSM, normFactor, inputTupleSteer, inputTupleCsm):
|
|
51
|
-
"""
|
|
57
|
+
r"""Conventional beamformer in frequency domain. Use either a predefined
|
|
52
58
|
steering vector formulation (see Sarradj 2012) or pass your own
|
|
53
59
|
steering vector.
|
|
54
60
|
|
|
@@ -83,72 +89,76 @@ def beamformerFreq(steerVecType, boolRemovedDiagOfCSM, normFactor, inputTupleSte
|
|
|
83
89
|
perform beamformer on eigenvalue decomposition of csm:
|
|
84
90
|
inputTupleCsm = (eigValues, eigVectors) , with
|
|
85
91
|
eigValues : float64[nEV]
|
|
86
|
-
nEV is the number of eigenvalues which should be taken into account.
|
|
92
|
+
nEV is the number of eigenvalues which should be taken into account.
|
|
87
93
|
All passed eigenvalues will be evaluated.
|
|
88
94
|
eigVectors : complex128[nMics, nEV]
|
|
89
95
|
Eigen vectors corresponding to eigValues. All passed eigenvector slices will be evaluated.
|
|
90
96
|
|
|
91
97
|
Returns
|
|
92
98
|
-------
|
|
93
|
-
*Autopower spectrum beamforming map [nGridPoints]
|
|
94
|
-
|
|
95
|
-
*steer normalization factor [nGridPoints]... contains the values the autopower needs to be multiplied with, in order to
|
|
96
|
-
fullfill 'steer^H * steer = 1' as needed for functional beamforming.
|
|
97
|
-
|
|
99
|
+
*Autopower spectrum beamforming map [nGridPoints]
|
|
100
|
+
|
|
101
|
+
*steer normalization factor [nGridPoints]... contains the values the autopower needs to be multiplied with, in order to
|
|
102
|
+
fullfill 'steer^H * steer = 1' as needed for functional beamforming.
|
|
103
|
+
|
|
98
104
|
Some Notes on the optimization of all subroutines
|
|
99
105
|
-------------------------------------------------
|
|
100
106
|
Reducing beamforming equation:
|
|
101
|
-
Let the csm be C and the steering vector be h, than, using Linear Albegra, the conventional beamformer can be written as
|
|
102
|
-
|
|
107
|
+
Let the csm be C and the steering vector be h, than, using Linear Albegra, the conventional beamformer can be written as
|
|
108
|
+
|
|
103
109
|
.. math:: B = h^H \\cdot C \\cdot h,
|
|
104
110
|
with ^H meaning the complex conjugated transpose.
|
|
105
111
|
When using that C is a hermitian matrix one can reduce the equation to
|
|
106
|
-
|
|
112
|
+
|
|
107
113
|
.. math:: B = h^H \\cdot C_D \\cdot h + 2 \\cdot Real(h^H \\cdot C_U \\cdot h),
|
|
108
114
|
where C_D and C_U are the diagonal part and upper part of C respectively.
|
|
109
115
|
Steering vector:
|
|
110
|
-
Theoretically the steering vector always includes the term "exp(distMicsGrid - distArrayCenterGrid)",
|
|
111
|
-
but as the steering vector gets multplied with its complex conjugation in all beamformer routines,
|
|
116
|
+
Theoretically the steering vector always includes the term "exp(distMicsGrid - distArrayCenterGrid)",
|
|
117
|
+
but as the steering vector gets multplied with its complex conjugation in all beamformer routines,
|
|
112
118
|
the constant "distArrayCenterGrid" cancels out --> In order to save operations, it is not implemented.
|
|
113
119
|
Spectral decomposition of the CSM:
|
|
114
120
|
In Linear Algebra the spectral decomposition of the CSM matrix would be:
|
|
115
|
-
|
|
121
|
+
|
|
116
122
|
.. math:: CSM = \\sum_{i=1}^{nEigenvalues} \\lambda_i (v_i \\cdot v_i^H) ,
|
|
117
|
-
where lambda_i is the i-th eigenvalue and
|
|
118
|
-
v_i is the eigenvector[nEigVal,1] belonging to lambda_i and ^H denotes the complex conjug transpose.
|
|
119
|
-
Using this, one must not build the whole CSM (which would be time consuming), but can drag the
|
|
123
|
+
where lambda_i is the i-th eigenvalue and
|
|
124
|
+
v_i is the eigenvector[nEigVal,1] belonging to lambda_i and ^H denotes the complex conjug transpose.
|
|
125
|
+
Using this, one must not build the whole CSM (which would be time consuming), but can drag the
|
|
120
126
|
steering vector into the sum of the spectral decomp. This saves a lot of operations.
|
|
121
127
|
Squares:
|
|
122
128
|
Seemingly "a * a" is slightly faster than "a**2" in numba
|
|
123
129
|
Square of abs():
|
|
124
|
-
Even though "a.real**2 + a.imag**2" would have fewer operations, modern processors seem to be optimized
|
|
130
|
+
Even though "a.real**2 + a.imag**2" would have fewer operations, modern processors seem to be optimized
|
|
125
131
|
for "a * a.conj" and are slightly faster the latter way. Both Versions are much faster than "abs(a)**2".
|
|
126
132
|
Using Cascading Sums:
|
|
127
|
-
When using the Spectral-Decomposition-Beamformer one could use numpys cascading sums for the scalar product
|
|
128
|
-
"eigenVec.conj * steeringVector". BUT (at the moment) this only brings benefits in comp-time for a very
|
|
133
|
+
When using the Spectral-Decomposition-Beamformer one could use numpys cascading sums for the scalar product
|
|
134
|
+
"eigenVec.conj * steeringVector". BUT (at the moment) this only brings benefits in comp-time for a very
|
|
129
135
|
small range of nMics (approx 250) --> Therefor it is not implemented here.
|
|
136
|
+
|
|
130
137
|
"""
|
|
131
|
-
boolIsEigValProb = isinstance(inputTupleCsm, tuple)# len(inputTupleCsm) > 1
|
|
138
|
+
boolIsEigValProb = isinstance(inputTupleCsm, tuple) # len(inputTupleCsm) > 1
|
|
132
139
|
# get the beamformer type (key-tuple = (isEigValProblem, formulationOfSteeringVector, RemovalOfCSMDiag))
|
|
133
|
-
beamformerDict = {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
140
|
+
beamformerDict = {
|
|
141
|
+
(False, 'custom', False): _freqBeamformer_SpecificSteerVec_FullCSM,
|
|
142
|
+
(False, 'custom', True): _freqBeamformer_SpecificSteerVec_CsmRemovedDiag,
|
|
143
|
+
(True, 'custom', False): _freqBeamformer_EigValProb_SpecificSteerVec_FullCSM,
|
|
144
|
+
(True, 'custom', True): _freqBeamformer_EigValProb_SpecificSteerVec_CsmRemovedDiag,
|
|
145
|
+
}
|
|
146
|
+
sth = {'classic': 1, 'inverse': 2, 'true level': 3, 'true location': 4}
|
|
147
|
+
|
|
139
148
|
# prepare Input
|
|
140
149
|
if steerVecType == 'custom': # beamformer with custom steering vector
|
|
141
150
|
steerVec = inputTupleSteer
|
|
142
151
|
nGridPoints = steerVec.shape[0]
|
|
143
152
|
else: # predefined beamformers (Formulation I - IV)
|
|
144
153
|
distGridToArrayCenter, distGridToAllMics, waveNumber = inputTupleSteer
|
|
145
|
-
if not isinstance(waveNumber, np.ndarray):
|
|
154
|
+
if not isinstance(waveNumber, np.ndarray):
|
|
155
|
+
waveNumber = np.array([waveNumber]) # for backward compatibility
|
|
146
156
|
nGridPoints = distGridToAllMics.shape[0]
|
|
147
157
|
if boolIsEigValProb:
|
|
148
|
-
eigVal, eigVec = inputTupleCsm#[0], inputTupleCsm[1]
|
|
158
|
+
eigVal, eigVec = inputTupleCsm # [0], inputTupleCsm[1]
|
|
149
159
|
else:
|
|
150
160
|
csm = inputTupleCsm
|
|
151
|
-
|
|
161
|
+
|
|
152
162
|
# beamformer routine: parallelized over Gridpoints
|
|
153
163
|
beamformOutput = np.zeros(nGridPoints, np.float64)
|
|
154
164
|
steerNormalizeOutput = np.zeros_like(beamformOutput)
|
|
@@ -162,16 +172,34 @@ def beamformerFreq(steerVecType, boolRemovedDiagOfCSM, normFactor, inputTupleSte
|
|
|
162
172
|
coreFunc(csm, steerVec, normFactor, result, normalHelp)
|
|
163
173
|
else: # predefined beamformers (Formulation I - IV)
|
|
164
174
|
if boolIsEigValProb:
|
|
165
|
-
_freqBeamformer_EigValues(
|
|
166
|
-
|
|
167
|
-
|
|
175
|
+
_freqBeamformer_EigValues(
|
|
176
|
+
eigVal,
|
|
177
|
+
np.ascontiguousarray(eigVec),
|
|
178
|
+
distGridToArrayCenter,
|
|
179
|
+
distGridToAllMics,
|
|
180
|
+
waveNumber[0],
|
|
181
|
+
normFactor,
|
|
182
|
+
boolRemovedDiagOfCSM,
|
|
183
|
+
sth[steerVecType],
|
|
184
|
+
result,
|
|
185
|
+
normalHelp,
|
|
186
|
+
)
|
|
168
187
|
else:
|
|
169
|
-
_freqBeamformer_FullCSM(
|
|
170
|
-
|
|
171
|
-
|
|
188
|
+
_freqBeamformer_FullCSM(
|
|
189
|
+
csm,
|
|
190
|
+
distGridToArrayCenter,
|
|
191
|
+
distGridToAllMics,
|
|
192
|
+
waveNumber[0],
|
|
193
|
+
normFactor,
|
|
194
|
+
boolRemovedDiagOfCSM,
|
|
195
|
+
sth[steerVecType],
|
|
196
|
+
result,
|
|
197
|
+
normalHelp,
|
|
198
|
+
)
|
|
172
199
|
beamformOutput = result
|
|
173
|
-
steerNormalizeOutput = normalHelp
|
|
174
|
-
return beamformOutput, steerNormalizeOutput
|
|
200
|
+
steerNormalizeOutput = normalHelp
|
|
201
|
+
return beamformOutput, steerNormalizeOutput
|
|
202
|
+
|
|
175
203
|
|
|
176
204
|
# fast implementation of full matrix beamformers
|
|
177
205
|
@nb.njit(
|
|
@@ -186,11 +214,11 @@ def beamformerFreq(steerVecType, boolRemovedDiagOfCSM, normFactor, inputTupleSte
|
|
|
186
214
|
nb.int64,
|
|
187
215
|
nb.float64[::1],
|
|
188
216
|
nb.float64[::1],
|
|
189
|
-
)
|
|
217
|
+
),
|
|
190
218
|
],
|
|
191
|
-
cache=
|
|
219
|
+
cache=CACHED_OPTION,
|
|
192
220
|
parallel=True,
|
|
193
|
-
error_model=
|
|
221
|
+
error_model='numpy',
|
|
194
222
|
)
|
|
195
223
|
def _freqBeamformer_FullCSM(
|
|
196
224
|
csm,
|
|
@@ -205,54 +233,48 @@ def _freqBeamformer_FullCSM(
|
|
|
205
233
|
):
|
|
206
234
|
# see bottom of information header of 'beamformerFreq' for information on which steps are taken, in order to gain speed improvements.
|
|
207
235
|
nMics = csm.shape[0]
|
|
208
|
-
st2 =
|
|
209
|
-
st34 =
|
|
210
|
-
helpNormalize = 0.0
|
|
236
|
+
st2 = steer_type == 2
|
|
237
|
+
st34 = steer_type == 3 or steer_type == 4
|
|
238
|
+
helpNormalize = 0.0 # just a hint for the compiler
|
|
211
239
|
for gi in nb.prange(distGridToArrayCenter.shape[0]):
|
|
212
240
|
steerVec = np.empty((nMics), np.complex128)
|
|
213
241
|
# building steering vector: in order to save some operation -> some normalization steps are applied after mat-vec-multipl.
|
|
214
242
|
for cntMics in range(nMics):
|
|
215
243
|
expArg = np.float32(waveNumber * distGridToAllMics[gi, cntMics])
|
|
216
244
|
steerVec[cntMics] = np.cos(expArg) - 1j * np.sin(expArg)
|
|
217
|
-
if st2:
|
|
245
|
+
if st2:
|
|
218
246
|
helpNormalize = 0.0
|
|
219
247
|
for cntMics in range(nMics):
|
|
220
|
-
helpNormalize += distGridToAllMics[gi,cntMics] * distGridToAllMics[gi,cntMics]
|
|
221
|
-
steerVec[cntMics] *= distGridToAllMics[gi,cntMics] # r_{t,i}-normalization is handled here
|
|
248
|
+
helpNormalize += distGridToAllMics[gi, cntMics] * distGridToAllMics[gi, cntMics]
|
|
249
|
+
steerVec[cntMics] *= distGridToAllMics[gi, cntMics] # r_{t,i}-normalization is handled here
|
|
222
250
|
if st34:
|
|
223
251
|
helpNormalize = 0.0
|
|
224
252
|
for cntMics in range(nMics):
|
|
225
|
-
helpNormalize += 1.0 / (distGridToAllMics[gi,cntMics] * distGridToAllMics[gi,cntMics])
|
|
226
|
-
steerVec[cntMics] /= distGridToAllMics[gi,cntMics] # r_{t,i}-normalization is handled here
|
|
253
|
+
helpNormalize += 1.0 / (distGridToAllMics[gi, cntMics] * distGridToAllMics[gi, cntMics])
|
|
254
|
+
steerVec[cntMics] /= distGridToAllMics[gi, cntMics] # r_{t,i}-normalization is handled here
|
|
227
255
|
|
|
228
256
|
# performing matrix-vector-multiplication (see bottom of information header of 'beamformerFreq)
|
|
229
257
|
scalarProd = 0.0
|
|
230
258
|
for cntMics in range(nMics):
|
|
231
259
|
leftVecMatrixProd = 0.0 + 0.0j
|
|
232
260
|
for cntMics2 in range(
|
|
233
|
-
cntMics
|
|
261
|
+
cntMics,
|
|
234
262
|
): # calculate 'steer^H * CSM' of upper-triangular-part of csm (without diagonal)
|
|
235
|
-
leftVecMatrixProd += (
|
|
236
|
-
csm[cntMics2, cntMics] * steerVec[cntMics2].conjugate()
|
|
237
|
-
)
|
|
263
|
+
leftVecMatrixProd += csm[cntMics2, cntMics] * steerVec[cntMics2].conjugate()
|
|
238
264
|
scalarProd += (
|
|
239
265
|
2 * (leftVecMatrixProd * steerVec[cntMics]).real
|
|
240
266
|
) # use that csm is Hermitian (lower triangular of csm can be reduced to factor '2')
|
|
241
267
|
if not r_diag:
|
|
242
268
|
for cntMics in range(nMics):
|
|
243
269
|
scalarProd += (
|
|
244
|
-
csm[cntMics, cntMics]
|
|
245
|
-
* steerVec[cntMics].conjugate()
|
|
246
|
-
* steerVec[cntMics]
|
|
270
|
+
csm[cntMics, cntMics] * steerVec[cntMics].conjugate() * steerVec[cntMics]
|
|
247
271
|
).real # include diagonal of csm
|
|
248
272
|
|
|
249
273
|
# specific normalzation for different steering vector formulations
|
|
250
274
|
if steer_type == 1:
|
|
251
275
|
normalizeFactor = nMics
|
|
252
276
|
normalizeSteer[gi] = 1.0 / nMics
|
|
253
|
-
result[gi] = (
|
|
254
|
-
scalarProd / (normalizeFactor * normalizeFactor) * signalLossNormalization
|
|
255
|
-
)
|
|
277
|
+
result[gi] = scalarProd / (normalizeFactor * normalizeFactor) * signalLossNormalization
|
|
256
278
|
elif steer_type == 2:
|
|
257
279
|
normalizeFactor = nMics * distGridToArrayCenter[gi]
|
|
258
280
|
normalizeFactorSquared = normalizeFactor * normalizeFactor
|
|
@@ -267,6 +289,7 @@ def _freqBeamformer_FullCSM(
|
|
|
267
289
|
normalizeSteer[gi] = 1.0 / nMics
|
|
268
290
|
result[gi] = scalarProd / normalizeFactor * signalLossNormalization
|
|
269
291
|
|
|
292
|
+
|
|
270
293
|
# fast implementation of eigenvalue beamformers
|
|
271
294
|
@nb.njit(
|
|
272
295
|
[
|
|
@@ -281,11 +304,11 @@ def _freqBeamformer_FullCSM(
|
|
|
281
304
|
nb.int64,
|
|
282
305
|
nb.float64[::1],
|
|
283
306
|
nb.float64[::1],
|
|
284
|
-
)
|
|
307
|
+
),
|
|
285
308
|
],
|
|
286
|
-
cache=
|
|
309
|
+
cache=CACHED_OPTION,
|
|
287
310
|
parallel=True,
|
|
288
|
-
error_model=
|
|
311
|
+
error_model='numpy',
|
|
289
312
|
)
|
|
290
313
|
def _freqBeamformer_EigValues(
|
|
291
314
|
eigVal,
|
|
@@ -302,36 +325,36 @@ def _freqBeamformer_EigValues(
|
|
|
302
325
|
# see bottom of information header of 'beamformerFreq' for information on which steps are taken, in order to gain speed improvements.
|
|
303
326
|
nMics = eigVec.shape[0]
|
|
304
327
|
nEigs = len(eigVal)
|
|
305
|
-
st2 =
|
|
306
|
-
st34 =
|
|
307
|
-
helpNormalize = 0.0
|
|
328
|
+
st2 = steer_type == 2
|
|
329
|
+
st34 = steer_type == 3 or steer_type == 4
|
|
330
|
+
helpNormalize = 0.0 # just a hint for the compiler
|
|
308
331
|
for gi in nb.prange(distGridToArrayCenter.shape[0]):
|
|
309
332
|
steerVec = np.empty((nMics), np.complex128)
|
|
310
333
|
# building steering vector: in order to save some operation -> some normalization steps are applied after mat-vec-multipl.
|
|
311
334
|
for cntMics in range(nMics):
|
|
312
335
|
expArg = np.float32(waveNumber * distGridToAllMics[gi, cntMics])
|
|
313
336
|
steerVec[cntMics] = np.cos(expArg) - 1j * np.sin(expArg)
|
|
314
|
-
if st2:
|
|
337
|
+
if st2:
|
|
315
338
|
helpNormalize = 0.0
|
|
316
339
|
for cntMics in range(nMics):
|
|
317
|
-
helpNormalize += distGridToAllMics[gi,cntMics] * distGridToAllMics[gi,cntMics]
|
|
318
|
-
steerVec[cntMics] *= distGridToAllMics[gi,cntMics] # r_{t,i}-normalization is handled here
|
|
340
|
+
helpNormalize += distGridToAllMics[gi, cntMics] * distGridToAllMics[gi, cntMics]
|
|
341
|
+
steerVec[cntMics] *= distGridToAllMics[gi, cntMics] # r_{t,i}-normalization is handled here
|
|
319
342
|
if st34:
|
|
320
343
|
helpNormalize = 0.0
|
|
321
344
|
for cntMics in range(nMics):
|
|
322
|
-
helpNormalize += 1.0 / (distGridToAllMics[gi,cntMics] * distGridToAllMics[gi,cntMics])
|
|
323
|
-
steerVec[cntMics] /= distGridToAllMics[gi,cntMics] # r_{t,i}-normalization is handled here
|
|
345
|
+
helpNormalize += 1.0 / (distGridToAllMics[gi, cntMics] * distGridToAllMics[gi, cntMics])
|
|
346
|
+
steerVec[cntMics] /= distGridToAllMics[gi, cntMics] # r_{t,i}-normalization is handled here
|
|
324
347
|
|
|
325
|
-
# eigenvalue beamforming
|
|
348
|
+
# eigenvalue beamforming
|
|
326
349
|
scalarProd = 0.0
|
|
327
350
|
if r_diag:
|
|
328
351
|
for cntEigVal in range(len(eigVal)):
|
|
329
352
|
scalarProdFullCSMperEigVal = 0.0 + 0.0j
|
|
330
353
|
scalarProdDiagCSMperEigVal = 0.0
|
|
331
354
|
for cntMics in range(nMics):
|
|
332
|
-
temp1 = eigVec[cntMics, cntEigVal].conjugate() * steerVec[cntMics]
|
|
355
|
+
temp1 = eigVec[cntMics, cntEigVal].conjugate() * steerVec[cntMics]
|
|
333
356
|
scalarProdFullCSMperEigVal += temp1
|
|
334
|
-
scalarProdDiagCSMperEigVal += (temp1 * temp1.conjugate()).real
|
|
357
|
+
scalarProdDiagCSMperEigVal += (temp1 * temp1.conjugate()).real
|
|
335
358
|
scalarProdFullCSMAbsSquared = (scalarProdFullCSMperEigVal * scalarProdFullCSMperEigVal.conjugate()).real
|
|
336
359
|
scalarProd += (scalarProdFullCSMAbsSquared - scalarProdDiagCSMperEigVal) * eigVal[cntEigVal]
|
|
337
360
|
else:
|
|
@@ -339,16 +362,14 @@ def _freqBeamformer_EigValues(
|
|
|
339
362
|
scalarProdFullCSMperEigVal = 0.0 + 0.0j
|
|
340
363
|
for cntMics in range(nMics):
|
|
341
364
|
scalarProdFullCSMperEigVal += eigVec[cntMics, cntEigVal].conjugate() * steerVec[cntMics]
|
|
342
|
-
scalarProdFullCSMAbsSquared = (scalarProdFullCSMperEigVal * scalarProdFullCSMperEigVal.conjugate()).real
|
|
365
|
+
scalarProdFullCSMAbsSquared = (scalarProdFullCSMperEigVal * scalarProdFullCSMperEigVal.conjugate()).real
|
|
343
366
|
scalarProd += scalarProdFullCSMAbsSquared * eigVal[cntEigVal]
|
|
344
367
|
|
|
345
368
|
# specific normalzation for different steering vector formulations
|
|
346
369
|
if steer_type == 1:
|
|
347
370
|
normalizeFactor = nMics
|
|
348
371
|
normalizeSteer[gi] = 1.0 / nMics
|
|
349
|
-
result[gi] = (
|
|
350
|
-
scalarProd / (normalizeFactor * normalizeFactor) * signalLossNormalization
|
|
351
|
-
)
|
|
372
|
+
result[gi] = scalarProd / (normalizeFactor * normalizeFactor) * signalLossNormalization
|
|
352
373
|
elif steer_type == 2:
|
|
353
374
|
normalizeFactor = nMics * distGridToArrayCenter[gi]
|
|
354
375
|
normalizeFactorSquared = normalizeFactor * normalizeFactor
|
|
@@ -363,8 +384,15 @@ def _freqBeamformer_EigValues(
|
|
|
363
384
|
normalizeSteer[gi] = 1.0 / nMics
|
|
364
385
|
result[gi] = scalarProd / normalizeFactor * signalLossNormalization
|
|
365
386
|
|
|
366
|
-
|
|
367
|
-
|
|
387
|
+
|
|
388
|
+
@nb.guvectorize(
|
|
389
|
+
[(nb.complex128[:, :], nb.complex128[:], nb.float64[:], nb.float64[:], nb.float64[:])],
|
|
390
|
+
'(m,m),(m),()->(),()',
|
|
391
|
+
nopython=True,
|
|
392
|
+
target=PARALLEL_OPTION,
|
|
393
|
+
cache=CACHED_OPTION,
|
|
394
|
+
fastmath=FAST_OPTION,
|
|
395
|
+
)
|
|
368
396
|
def _freqBeamformer_SpecificSteerVec_FullCSM(csm, steerVec, signalLossNormalization, result, normalizeSteer):
|
|
369
397
|
# see bottom of information header of 'beamformerFreq' for information on which steps are taken, in order to gain speed improvements.
|
|
370
398
|
nMics = csm.shape[0]
|
|
@@ -377,14 +405,24 @@ def _freqBeamformer_SpecificSteerVec_FullCSM(csm, steerVec, signalLossNormalizat
|
|
|
377
405
|
leftVecMatrixProd = 0.0 + 0.0j
|
|
378
406
|
for cntMics2 in range(cntMics): # calculate 'steer^H * CSM' of upper-triangular-part of csm (without diagonal)
|
|
379
407
|
leftVecMatrixProd += csm[cntMics2, cntMics] * steerVec[cntMics2].conjugate()
|
|
380
|
-
scalarProd +=
|
|
381
|
-
|
|
408
|
+
scalarProd += (
|
|
409
|
+
2 * (leftVecMatrixProd * steerVec[cntMics]).real
|
|
410
|
+
) # use that csm is Hermitian (lower triangular of csm can be reduced to factor '2')
|
|
411
|
+
scalarProd += (
|
|
412
|
+
csm[cntMics, cntMics] * steerVec[cntMics].conjugate() * steerVec[cntMics]
|
|
413
|
+
).real # include diagonal of csm
|
|
382
414
|
normalizeSteer[0] = helpNormalize.real
|
|
383
415
|
result[0] = scalarProd * signalLossNormalization[0]
|
|
384
416
|
|
|
385
417
|
|
|
386
|
-
@nb.guvectorize(
|
|
387
|
-
|
|
418
|
+
@nb.guvectorize(
|
|
419
|
+
[(nb.complex128[:, :], nb.complex128[:], nb.float64[:], nb.float64[:], nb.float64[:])],
|
|
420
|
+
'(m,m),(m),()->(),()',
|
|
421
|
+
nopython=True,
|
|
422
|
+
target=PARALLEL_OPTION,
|
|
423
|
+
cache=CACHED_OPTION,
|
|
424
|
+
fastmath=FAST_OPTION,
|
|
425
|
+
)
|
|
388
426
|
def _freqBeamformer_SpecificSteerVec_CsmRemovedDiag(csm, steerVec, signalLossNormalization, result, normalizeSteer):
|
|
389
427
|
# see bottom of information header of 'beamformerFreq' for information on which steps are taken, in order to gain speed improvements.
|
|
390
428
|
nMics = csm.shape[0]
|
|
@@ -397,15 +435,32 @@ def _freqBeamformer_SpecificSteerVec_CsmRemovedDiag(csm, steerVec, signalLossNor
|
|
|
397
435
|
leftVecMatrixProd = 0.0 + 0.0j
|
|
398
436
|
for cntMics2 in range(cntMics): # calculate 'steer^H * CSM' of upper-triangular-part of csm (without diagonal)
|
|
399
437
|
leftVecMatrixProd += csm[cntMics2, cntMics] * steerVec[cntMics2].conjugate()
|
|
400
|
-
scalarProd +=
|
|
438
|
+
scalarProd += (
|
|
439
|
+
2 * (leftVecMatrixProd * steerVec[cntMics]).real
|
|
440
|
+
) # use that csm is Hermitian (lower triangular of csm can be reduced to factor '2')
|
|
401
441
|
normalizeSteer[0] = helpNormalize.real
|
|
402
442
|
result[0] = scalarProd * signalLossNormalization[0]
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
@nb.guvectorize(
|
|
446
|
+
[(nb.float64[:], nb.complex128[:, :], nb.complex128[:], nb.float64[:], nb.float64[:], nb.float64[:])],
|
|
447
|
+
'(e),(m,e),(m),()->(),()',
|
|
448
|
+
nopython=True,
|
|
449
|
+
target=PARALLEL_OPTION,
|
|
450
|
+
cache=CACHED_OPTION,
|
|
451
|
+
fastmath=FAST_OPTION,
|
|
452
|
+
)
|
|
453
|
+
def _freqBeamformer_EigValProb_SpecificSteerVec_FullCSM(
|
|
454
|
+
eigVal,
|
|
455
|
+
eigVec,
|
|
456
|
+
steerVec,
|
|
457
|
+
signalLossNormalization,
|
|
458
|
+
result,
|
|
459
|
+
normalizeSteer,
|
|
460
|
+
):
|
|
406
461
|
# see bottom of information header of 'beamformerFreq' for information on which steps are taken, in order to gain speed improvements.
|
|
407
462
|
nMics = eigVec.shape[0]
|
|
408
|
-
|
|
463
|
+
|
|
409
464
|
# get h^H * h for normalization
|
|
410
465
|
helpNormalize = 0.0
|
|
411
466
|
for cntMics in range(nMics):
|
|
@@ -417,18 +472,31 @@ def _freqBeamformer_EigValProb_SpecificSteerVec_FullCSM(eigVal, eigVec, steerVec
|
|
|
417
472
|
scalarProdFullCSMperEigVal = 0.0 + 0.0j
|
|
418
473
|
for cntMics in range(nMics):
|
|
419
474
|
scalarProdFullCSMperEigVal += eigVec[cntMics, cntEigVal].conjugate() * steerVec[cntMics]
|
|
420
|
-
scalarProdFullCSMAbsSquared = (scalarProdFullCSMperEigVal * scalarProdFullCSMperEigVal.conjugate()).real
|
|
475
|
+
scalarProdFullCSMAbsSquared = (scalarProdFullCSMperEigVal * scalarProdFullCSMperEigVal.conjugate()).real
|
|
421
476
|
scalarProdFullCSM += scalarProdFullCSMAbsSquared * eigVal[cntEigVal]
|
|
422
477
|
normalizeSteer[0] = helpNormalize.real
|
|
423
478
|
result[0] = scalarProdFullCSM * signalLossNormalization[0]
|
|
424
479
|
|
|
425
480
|
|
|
426
|
-
@nb.guvectorize(
|
|
427
|
-
|
|
428
|
-
|
|
481
|
+
@nb.guvectorize(
|
|
482
|
+
[(nb.float64[:], nb.complex128[:, :], nb.complex128[:], nb.float64[:], nb.float64[:], nb.float64[:])],
|
|
483
|
+
'(e),(m,e),(m),()->(),()',
|
|
484
|
+
nopython=True,
|
|
485
|
+
target=PARALLEL_OPTION,
|
|
486
|
+
cache=CACHED_OPTION,
|
|
487
|
+
fastmath=FAST_OPTION,
|
|
488
|
+
)
|
|
489
|
+
def _freqBeamformer_EigValProb_SpecificSteerVec_CsmRemovedDiag(
|
|
490
|
+
eigVal,
|
|
491
|
+
eigVec,
|
|
492
|
+
steerVec,
|
|
493
|
+
signalLossNormalization,
|
|
494
|
+
result,
|
|
495
|
+
normalizeSteer,
|
|
496
|
+
):
|
|
429
497
|
# see bottom of information header of 'beamformerFreq' for information on which steps are taken, in order to gain speed improvements.
|
|
430
498
|
nMics = eigVec.shape[0]
|
|
431
|
-
|
|
499
|
+
|
|
432
500
|
# get h^H * h for normalization
|
|
433
501
|
helpNormalize = 0.0
|
|
434
502
|
for cntMics in range(nMics):
|
|
@@ -442,15 +510,16 @@ def _freqBeamformer_EigValProb_SpecificSteerVec_CsmRemovedDiag(eigVal, eigVec, s
|
|
|
442
510
|
for cntMics in range(nMics):
|
|
443
511
|
temp1 = eigVec[cntMics, cntEigVal].conjugate() * steerVec[cntMics]
|
|
444
512
|
scalarProdFullCSMperEigVal += temp1
|
|
445
|
-
scalarProdDiagCSMperEigVal += (temp1 * temp1.conjugate()).real
|
|
513
|
+
scalarProdDiagCSMperEigVal += (temp1 * temp1.conjugate()).real
|
|
446
514
|
scalarProdFullCSMAbsSquared = (scalarProdFullCSMperEigVal * scalarProdFullCSMperEigVal.conjugate()).real
|
|
447
515
|
scalarProdReducedCSM += (scalarProdFullCSMAbsSquared - scalarProdDiagCSMperEigVal) * eigVal[cntEigVal]
|
|
448
516
|
normalizeSteer[0] = helpNormalize.real
|
|
449
517
|
result[0] = scalarProdReducedCSM * signalLossNormalization[0]
|
|
450
518
|
|
|
451
|
-
|
|
519
|
+
|
|
520
|
+
# %% Point - Spread - Function
|
|
452
521
|
def calcPointSpreadFunction(steerVecType, distGridToArrayCenter, distGridToAllMics, waveNumber, indSource, dtype):
|
|
453
|
-
"""
|
|
522
|
+
r"""Calculates the Point-Spread-Functions. Use either a predefined steering vector
|
|
454
523
|
formulation (see :ref:`Sarradj, 2012<Sarradj2012>`) or pass it your own steering vector.
|
|
455
524
|
|
|
456
525
|
Parameters
|
|
@@ -464,7 +533,7 @@ def calcPointSpreadFunction(steerVecType, distGridToArrayCenter, distGridToAllMi
|
|
|
464
533
|
waveNumber : float64
|
|
465
534
|
The free field wave number.
|
|
466
535
|
indSource : a LIST of int (e.g. indSource=[5] is fine; indSource=5 doesn't work):
|
|
467
|
-
specifies which gridpoints should be assumed to be sources
|
|
536
|
+
specifies which gridpoints should be assumed to be sources
|
|
468
537
|
--> a seperate psf will be calculated for each source
|
|
469
538
|
dtype : either 'float64' or 'float32'
|
|
470
539
|
Determines the precision of the result. For big maps this could be worth downgrading.
|
|
@@ -472,177 +541,271 @@ def calcPointSpreadFunction(steerVecType, distGridToArrayCenter, distGridToAllMi
|
|
|
472
541
|
Returns
|
|
473
542
|
-------
|
|
474
543
|
Autopower spectrum PSF map : [nFreqs, nGridPoints, nSources]
|
|
475
|
-
|
|
544
|
+
|
|
476
545
|
Some Notes on the optimization of all subroutines
|
|
477
546
|
-------------------------------------------------
|
|
478
547
|
Reducing beamforming equation:
|
|
479
548
|
Let the steering vector be h, than, using Linear Albegra, the PSF of a SourcePoint S would be
|
|
480
|
-
|
|
549
|
+
|
|
481
550
|
.. math:: B = h^H \\cdot (a_S \\cdot a_S^H) \\cdot h,
|
|
482
551
|
with ^H meaning the complex conjugated transpose and a_s the transfer function from source to gridpoint.
|
|
483
|
-
The (...)-part equals the CSM that the source would produce via the chosen steering vec formulation.
|
|
552
|
+
The (...)-part equals the CSM that the source would produce via the chosen steering vec formulation.
|
|
484
553
|
Using (for example) tensor calculus, one can reduce the equation to:
|
|
485
|
-
|
|
554
|
+
|
|
486
555
|
.. math:: B = \\left| h^H \\cdot a_S \\right| ^ 2.
|
|
487
556
|
Steering vector:
|
|
488
|
-
Theoretically the steering vector always includes the term "exp(distMicsGrid - distArrayCenterGrid)", but as the steering vector gets multplied with its complex conjugation in
|
|
557
|
+
Theoretically the steering vector always includes the term "exp(distMicsGrid - distArrayCenterGrid)", but as the steering vector gets multplied with its complex conjugation in
|
|
489
558
|
all beamformer routines, the constant "distArrayCenterGrid" cancels out --> In order to save operations, it is not implemented.
|
|
490
559
|
Squares:
|
|
491
560
|
Seemingly "a * a" is slightly faster than "a**2" in numba
|
|
492
561
|
Square of abs():
|
|
493
562
|
Even though "a.real**2 + a.imag**^2" would have fewer operations, modern processors seem to be optimized for "a * a.conj" and are slightly faster the latter way.
|
|
494
563
|
Both Versions are much faster than "abs(a)**2".
|
|
564
|
+
|
|
495
565
|
"""
|
|
496
566
|
# get the steering vector formulation
|
|
497
|
-
psfDict = {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
567
|
+
psfDict = {
|
|
568
|
+
'classic': _psf_Formulation1AkaClassic,
|
|
569
|
+
'inverse': _psf_Formulation2AkaInverse,
|
|
570
|
+
'true level': _psf_Formulation3AkaTrueLevel,
|
|
571
|
+
'true location': _psf_Formulation4AkaTrueLocation,
|
|
572
|
+
}
|
|
501
573
|
coreFunc = psfDict[steerVecType]
|
|
502
574
|
|
|
503
575
|
# prepare input
|
|
504
576
|
nGridPoints = distGridToAllMics.shape[0]
|
|
505
577
|
nSources = len(indSource)
|
|
506
|
-
if not isinstance(waveNumber, np.ndarray):
|
|
507
|
-
|
|
578
|
+
if not isinstance(waveNumber, np.ndarray):
|
|
579
|
+
waveNumber = np.array([waveNumber])
|
|
580
|
+
|
|
508
581
|
# psf routine: parallelized over Gridpoints
|
|
509
582
|
psfOutput = np.zeros((nGridPoints, nSources), dtype=dtype)
|
|
510
|
-
coreFunc(
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
583
|
+
coreFunc(
|
|
584
|
+
distGridToArrayCenter,
|
|
585
|
+
distGridToAllMics,
|
|
586
|
+
distGridToArrayCenter[indSource],
|
|
587
|
+
distGridToAllMics[indSource, :],
|
|
588
|
+
waveNumber,
|
|
589
|
+
psfOutput,
|
|
590
|
+
)
|
|
516
591
|
|
|
517
592
|
return psfOutput
|
|
518
593
|
|
|
519
594
|
|
|
520
|
-
@nb.guvectorize(
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
595
|
+
@nb.guvectorize(
|
|
596
|
+
[
|
|
597
|
+
(nb.float64[:], nb.float64[:], nb.float64[:], nb.float64[:, :], nb.float64[:], nb.float64[:]),
|
|
598
|
+
(nb.float64[:], nb.float64[:], nb.float64[:], nb.float64[:, :], nb.float64[:], nb.float32[:]),
|
|
599
|
+
],
|
|
600
|
+
'(),(m),(s),(s,m),()->(s)',
|
|
601
|
+
nopython=True,
|
|
602
|
+
target=PARALLEL_OPTION,
|
|
603
|
+
cache=CACHED_OPTION,
|
|
604
|
+
fastmath=FAST_OPTION,
|
|
605
|
+
)
|
|
606
|
+
def _psf_Formulation1AkaClassic(
|
|
607
|
+
distGridToArrayCenter, # noqa ARG001
|
|
608
|
+
distGridToAllMics,
|
|
609
|
+
distSourcesToArrayCenter,
|
|
610
|
+
distSourcesToAllMics,
|
|
611
|
+
waveNumber,
|
|
612
|
+
result,
|
|
613
|
+
): # noqa ARG001
|
|
524
614
|
nMics = distGridToAllMics.shape[0]
|
|
525
615
|
for cntSources in range(len(distSourcesToArrayCenter)):
|
|
526
616
|
# see bottom of information header of 'calcPointSpreadFunction' for infos on the PSF calculation and speed improvements.
|
|
527
617
|
scalarProd = 0.0 + 0.0j
|
|
528
618
|
for cntMics in range(nMics):
|
|
529
|
-
expArg = np.float32(
|
|
619
|
+
expArg = np.float32(
|
|
620
|
+
waveNumber[0] * (distGridToAllMics[cntMics] - distSourcesToAllMics[cntSources, cntMics]),
|
|
621
|
+
)
|
|
530
622
|
scalarProd += (np.cos(expArg) - 1j * np.sin(expArg)) / distSourcesToAllMics[cntSources, cntMics]
|
|
531
623
|
normalizeFactor = distSourcesToArrayCenter[cntSources] / nMics
|
|
532
624
|
scalarProdAbsSquared = (scalarProd * scalarProd.conjugate()).real
|
|
533
625
|
result[cntSources] = scalarProdAbsSquared * (normalizeFactor * normalizeFactor)
|
|
534
626
|
|
|
535
627
|
|
|
536
|
-
@nb.guvectorize(
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
628
|
+
@nb.guvectorize(
|
|
629
|
+
[
|
|
630
|
+
(nb.float64[:], nb.float64[:], nb.float64[:], nb.float64[:, :], nb.float64[:], nb.float64[:]),
|
|
631
|
+
(nb.float64[:], nb.float64[:], nb.float64[:], nb.float64[:, :], nb.float64[:], nb.float32[:]),
|
|
632
|
+
],
|
|
633
|
+
'(),(m),(s),(s,m),()->(s)',
|
|
634
|
+
nopython=True,
|
|
635
|
+
target=PARALLEL_OPTION,
|
|
636
|
+
cache=CACHED_OPTION,
|
|
637
|
+
fastmath=FAST_OPTION,
|
|
638
|
+
)
|
|
639
|
+
def _psf_Formulation2AkaInverse(
|
|
640
|
+
distGridToArrayCenter,
|
|
641
|
+
distGridToAllMics,
|
|
642
|
+
distSourcesToArrayCenter,
|
|
643
|
+
distSourcesToAllMics,
|
|
644
|
+
waveNumber,
|
|
645
|
+
result,
|
|
646
|
+
):
|
|
540
647
|
nMics = distGridToAllMics.shape[0]
|
|
541
648
|
for cntSources in range(len(distSourcesToArrayCenter)):
|
|
542
649
|
# see bottom of information header of 'calcPointSpreadFunction' for infos on the PSF calculation and speed improvements.
|
|
543
650
|
scalarProd = 0.0 + 0.0j
|
|
544
651
|
for cntMics in range(nMics):
|
|
545
|
-
expArg = np.float32(
|
|
546
|
-
|
|
652
|
+
expArg = np.float32(
|
|
653
|
+
waveNumber[0] * (distGridToAllMics[cntMics] - distSourcesToAllMics[cntSources, cntMics]),
|
|
654
|
+
)
|
|
655
|
+
scalarProd += (
|
|
656
|
+
(np.cos(expArg) - 1j * np.sin(expArg))
|
|
657
|
+
/ distSourcesToAllMics[cntSources, cntMics]
|
|
658
|
+
* distGridToAllMics[cntMics]
|
|
659
|
+
)
|
|
547
660
|
normalizeFactor = distSourcesToArrayCenter[cntSources] / distGridToArrayCenter[0] / nMics
|
|
548
|
-
scalarProdAbsSquared = (scalarProd * scalarProd.conjugate()).real
|
|
549
|
-
result[cntSources] = scalarProdAbsSquared * (normalizeFactor * normalizeFactor)
|
|
661
|
+
scalarProdAbsSquared = (scalarProd * scalarProd.conjugate()).real
|
|
662
|
+
result[cntSources] = scalarProdAbsSquared * (normalizeFactor * normalizeFactor)
|
|
550
663
|
|
|
551
664
|
|
|
552
|
-
@nb.guvectorize(
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
665
|
+
@nb.guvectorize(
|
|
666
|
+
[
|
|
667
|
+
(nb.float64[:], nb.float64[:], nb.float64[:], nb.float64[:, :], nb.float64[:], nb.float64[:]),
|
|
668
|
+
(nb.float64[:], nb.float64[:], nb.float64[:], nb.float64[:, :], nb.float64[:], nb.float32[:]),
|
|
669
|
+
],
|
|
670
|
+
'(),(m),(s),(s,m),()->(s)',
|
|
671
|
+
nopython=True,
|
|
672
|
+
target=PARALLEL_OPTION,
|
|
673
|
+
cache=CACHED_OPTION,
|
|
674
|
+
fastmath=FAST_OPTION,
|
|
675
|
+
)
|
|
676
|
+
def _psf_Formulation3AkaTrueLevel(
|
|
677
|
+
distGridToArrayCenter,
|
|
678
|
+
distGridToAllMics,
|
|
679
|
+
distSourcesToArrayCenter,
|
|
680
|
+
distSourcesToAllMics,
|
|
681
|
+
waveNumber,
|
|
682
|
+
result,
|
|
683
|
+
):
|
|
556
684
|
nMics = distGridToAllMics.shape[0]
|
|
557
685
|
for cntSources in range(len(distSourcesToArrayCenter)):
|
|
558
686
|
# see bottom of information header of 'calcPointSpreadFunction' for infos on the PSF calculation and speed improvements.
|
|
559
687
|
scalarProd = 0.0 + 0.0j
|
|
560
688
|
helpNormalizeGrid = 0.0
|
|
561
689
|
for cntMics in range(nMics):
|
|
562
|
-
expArg = np.float32(
|
|
563
|
-
|
|
690
|
+
expArg = np.float32(
|
|
691
|
+
waveNumber[0] * (distGridToAllMics[cntMics] - distSourcesToAllMics[cntSources, cntMics]),
|
|
692
|
+
)
|
|
693
|
+
scalarProd += (
|
|
694
|
+
(np.cos(expArg) - 1j * np.sin(expArg))
|
|
695
|
+
/ distSourcesToAllMics[cntSources, cntMics]
|
|
696
|
+
/ distGridToAllMics[cntMics]
|
|
697
|
+
)
|
|
564
698
|
helpNormalizeGrid += 1.0 / (distGridToAllMics[cntMics] * distGridToAllMics[cntMics])
|
|
565
699
|
normalizeFactor = distSourcesToArrayCenter[cntSources] / distGridToArrayCenter[0] / helpNormalizeGrid
|
|
566
700
|
scalarProdAbsSquared = (scalarProd * scalarProd.conjugate()).real
|
|
567
701
|
result[cntSources] = scalarProdAbsSquared * (normalizeFactor * normalizeFactor)
|
|
568
702
|
|
|
569
703
|
|
|
570
|
-
@nb.guvectorize(
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
704
|
+
@nb.guvectorize(
|
|
705
|
+
[
|
|
706
|
+
(nb.float64[:], nb.float64[:], nb.float64[:], nb.float64[:, :], nb.float64[:], nb.float64[:]),
|
|
707
|
+
(nb.float64[:], nb.float64[:], nb.float64[:], nb.float64[:, :], nb.float64[:], nb.float32[:]),
|
|
708
|
+
],
|
|
709
|
+
'(),(m),(s),(s,m),()->(s)',
|
|
710
|
+
nopython=True,
|
|
711
|
+
target=PARALLEL_OPTION,
|
|
712
|
+
cache=CACHED_OPTION,
|
|
713
|
+
fastmath=FAST_OPTION,
|
|
714
|
+
)
|
|
715
|
+
def _psf_Formulation4AkaTrueLocation(
|
|
716
|
+
distGridToArrayCenter, # noqa ARG001
|
|
717
|
+
distGridToAllMics,
|
|
718
|
+
distSourcesToArrayCenter,
|
|
719
|
+
distSourcesToAllMics,
|
|
720
|
+
waveNumber,
|
|
721
|
+
result,
|
|
722
|
+
): # noqa ARG001
|
|
574
723
|
nMics = distGridToAllMics.shape[0]
|
|
575
724
|
for cntSources in range(len(distSourcesToArrayCenter)):
|
|
576
725
|
# see bottom of information header of 'calcPointSpreadFunction' for infos on the PSF calculation and speed improvements.
|
|
577
726
|
scalarProd = 0.0 + 0.0j
|
|
578
727
|
helpNormalizeGrid = 0.0
|
|
579
728
|
for cntMics in range(nMics):
|
|
580
|
-
expArg = np.float32(
|
|
581
|
-
|
|
729
|
+
expArg = np.float32(
|
|
730
|
+
waveNumber[0] * (distGridToAllMics[cntMics] - distSourcesToAllMics[cntSources, cntMics]),
|
|
731
|
+
)
|
|
732
|
+
scalarProd += (
|
|
733
|
+
(np.cos(expArg) - 1j * np.sin(expArg))
|
|
734
|
+
/ distSourcesToAllMics[cntSources, cntMics]
|
|
735
|
+
/ distGridToAllMics[cntMics]
|
|
736
|
+
)
|
|
582
737
|
helpNormalizeGrid += 1.0 / (distGridToAllMics[cntMics] * distGridToAllMics[cntMics])
|
|
583
738
|
normalizeFactor = distSourcesToArrayCenter[cntSources]
|
|
584
739
|
scalarProdAbsSquared = (scalarProd * scalarProd.conjugate()).real
|
|
585
740
|
result[cntSources] = scalarProdAbsSquared * (normalizeFactor * normalizeFactor) / nMics / helpNormalizeGrid
|
|
586
741
|
|
|
742
|
+
|
|
587
743
|
# CURRENTLY NOT NEEDED, AS CUSTOM PSF WILL BE CALCULATED IN fbeamform.SteeringVector WITH THE USE OF Trait transfer
|
|
588
|
-
|
|
744
|
+
# @nb.guvectorize([(nb.float64[:], nb.float64[:], nb.float64[:], nb.float64[:,:], nb.float64[:], nb.float64[:]),
|
|
589
745
|
# (nb.float64[:], nb.float64[:], nb.float64[:], nb.float64[:,:], nb.float64[:], nb.float32[:])],
|
|
590
|
-
# '(),(m),(s),(s,m),()->(s)', nopython=True, target=
|
|
591
|
-
#def _psf_SpecificSteerVec(steerVec, steerVecSources, result):
|
|
746
|
+
# '(),(m),(s),(s,m),()->(s)', nopython=True, target=PARALLEL_OPTION, cache=CACHED_OPTION)
|
|
747
|
+
# def _psf_SpecificSteerVec(steerVec, steerVecSources, result):
|
|
592
748
|
# nMics = len(steerVec)
|
|
593
749
|
# for cntSources in range(steerVecSources.shape[0]):
|
|
594
750
|
# # see bottom of information header of 'calcPointSpreadFunction' for infos on the PSF calculation and speed improvements.
|
|
595
751
|
# scalarProd = 0.0 + 0.0j
|
|
596
752
|
# for cntMics in range(nMics):
|
|
597
753
|
# scalarProd += steerVec[cntMics].conjugate() * steerVecSources[cntSources, cntMics]
|
|
598
|
-
# scalarProdAbsSquared = (scalarProd * scalarProd.conjugate()).real
|
|
754
|
+
# scalarProdAbsSquared = (scalarProd * scalarProd.conjugate()).real
|
|
599
755
|
# result[cntSources] = scalarProdAbsSquared
|
|
600
756
|
|
|
601
757
|
|
|
602
|
-
|
|
758
|
+
# %% Damas - Gauss Seidel
|
|
603
759
|
# Formerly known as 'gseidel'
|
|
604
|
-
@nb.guvectorize(
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
760
|
+
@nb.guvectorize(
|
|
761
|
+
[ # (nb.float32[:,:], nb.float32[:], nb.int64[:], nb.float64[:], nb.float32[:]),
|
|
762
|
+
(nb.float64[:, :], nb.float64[:], nb.int64[:], nb.float64[:], nb.float64[:]),
|
|
763
|
+
# (nb.float32[:,:], nb.float64[:], nb.int64[:], nb.float64[:], nb.float64[:]),
|
|
764
|
+
# (nb.float64[:,:], nb.float32[:], nb.int64[:], nb.float64[:], nb.float32[:])
|
|
765
|
+
],
|
|
766
|
+
'(g,g),(g),(),()->(g)',
|
|
767
|
+
nopython=True,
|
|
768
|
+
target=PARALLEL_OPTION,
|
|
769
|
+
cache=CACHED_OPTION,
|
|
770
|
+
fastmath=FAST_OPTION,
|
|
771
|
+
)
|
|
610
772
|
def damasSolverGaussSeidel(A, dirtyMap, nIterations, relax, damasSolution):
|
|
611
|
-
"""
|
|
773
|
+
"""Solves the DAMAS inverse problem via modified gauss seidel.
|
|
612
774
|
This is the original formulation from :ref:`Brooks and Humphreys, 2006<BrooksHumphreys2006>`.
|
|
613
|
-
|
|
775
|
+
|
|
614
776
|
Parameters
|
|
615
777
|
----------
|
|
616
778
|
A : float32/float64[nFreqs, nGridpoints, nGridpoints] (or float64[...])
|
|
617
779
|
The PSF build matrix (see :ref:`Brooks and Humphreys, 2006<BrooksHumphreys2006>`)
|
|
618
780
|
dirtyMap : float32/float64[nFreqs, nGridpoints] (or float64[...])
|
|
619
781
|
The conventional beamformer map
|
|
620
|
-
nIterations : int64[scalar]
|
|
782
|
+
nIterations : int64[scalar]
|
|
621
783
|
number of Iterations the damas solver has to go through
|
|
622
|
-
relax : int64[scalar]
|
|
784
|
+
relax : int64[scalar]
|
|
623
785
|
relaxation parameter (=1.0 in :ref:`Brooks and Humphreys, 2006<BrooksHumphreys2006>`)
|
|
624
|
-
damasSolution : float32/float64[nFreqs, nGridpoints] (or float64[...])
|
|
786
|
+
damasSolution : float32/float64[nFreqs, nGridpoints] (or float64[...])
|
|
625
787
|
starting solution
|
|
626
|
-
|
|
788
|
+
|
|
627
789
|
Returns
|
|
628
790
|
-------
|
|
629
791
|
None : as damasSolution is overwritten with end result of the damas iterative solver.
|
|
792
|
+
|
|
630
793
|
"""
|
|
631
|
-
# nGridPoints = len(dirtyMap)
|
|
632
|
-
# for cntIter in range(nIterations[0]):
|
|
633
|
-
# for cntGrid in range(nGridPoints):
|
|
634
|
-
# solHelp = np.float32(0)
|
|
635
|
-
# for cntGridHelp in range(cntGrid): # lower sum
|
|
636
|
-
# solHelp += A[cntGrid, cntGridHelp] * damasSolution[cntGridHelp]
|
|
637
|
-
# for cntGridHelp in range(cntGrid + 1, nGridPoints): # upper sum
|
|
638
|
-
# solHelp += A[cntGrid, cntGridHelp] * damasSolution[cntGridHelp]
|
|
639
|
-
# solHelp = (1 - relax[0]) * damasSolution[cntGrid] + relax[0] * (dirtyMap[cntGrid] - solHelp)
|
|
640
|
-
# if solHelp > 0.0:
|
|
641
|
-
# damasSolution[cntGrid] = solHelp
|
|
642
|
-
# else:
|
|
643
|
-
# damasSolution[cntGrid] = 0.0
|
|
794
|
+
# nGridPoints = len(dirtyMap)
|
|
795
|
+
# for cntIter in range(nIterations[0]):
|
|
796
|
+
# for cntGrid in range(nGridPoints):
|
|
797
|
+
# solHelp = np.float32(0)
|
|
798
|
+
# for cntGridHelp in range(cntGrid): # lower sum
|
|
799
|
+
# solHelp += A[cntGrid, cntGridHelp] * damasSolution[cntGridHelp]
|
|
800
|
+
# for cntGridHelp in range(cntGrid + 1, nGridPoints): # upper sum
|
|
801
|
+
# solHelp += A[cntGrid, cntGridHelp] * damasSolution[cntGridHelp]
|
|
802
|
+
# solHelp = (1 - relax[0]) * damasSolution[cntGrid] + relax[0] * (dirtyMap[cntGrid] - solHelp)
|
|
803
|
+
# if solHelp > 0.0:
|
|
804
|
+
# damasSolution[cntGrid] = solHelp
|
|
805
|
+
# else:
|
|
806
|
+
# damasSolution[cntGrid] = 0.0
|
|
644
807
|
nGridPoints = len(dirtyMap)
|
|
645
|
-
for
|
|
808
|
+
for _cntIter in range(nIterations[0]):
|
|
646
809
|
for cntGrid in range(nGridPoints):
|
|
647
810
|
solHelp = 0.0
|
|
648
811
|
for cntGridHelp in range(nGridPoints): # full sum
|
|
@@ -655,10 +818,10 @@ def damasSolverGaussSeidel(A, dirtyMap, nIterations, relax, damasSolution):
|
|
|
655
818
|
damasSolution[cntGrid] = 0.0
|
|
656
819
|
|
|
657
820
|
|
|
658
|
-
|
|
821
|
+
# %% Transfer - Function
|
|
659
822
|
def calcTransfer(distGridToArrayCenter, distGridToAllMics, waveNumber):
|
|
660
|
-
"""
|
|
661
|
-
|
|
823
|
+
"""Calculates the transfer functions between the various mics and gridpoints.
|
|
824
|
+
|
|
662
825
|
Parameters
|
|
663
826
|
----------
|
|
664
827
|
distGridToArrayCenter : float64[nGridpoints]
|
|
@@ -671,6 +834,7 @@ def calcTransfer(distGridToArrayCenter, distGridToAllMics, waveNumber):
|
|
|
671
834
|
Returns
|
|
672
835
|
-------
|
|
673
836
|
The Transferfunctions in format complex128[nGridPoints, nMics].
|
|
837
|
+
|
|
674
838
|
"""
|
|
675
839
|
nGridPoints, nMics = distGridToAllMics.shape[0], distGridToAllMics.shape[1]
|
|
676
840
|
result = np.zeros((nGridPoints, nMics), np.complex128)
|
|
@@ -678,11 +842,17 @@ def calcTransfer(distGridToArrayCenter, distGridToAllMics, waveNumber):
|
|
|
678
842
|
_transferCoreFunc(distGridToArrayCenter, distGridToAllMics, np.array([waveNumber]), result)
|
|
679
843
|
return result
|
|
680
844
|
|
|
681
|
-
|
|
682
|
-
|
|
845
|
+
|
|
846
|
+
@nb.guvectorize(
|
|
847
|
+
[(nb.float64[:], nb.float64[:], nb.float64[:], nb.complex128[:])],
|
|
848
|
+
'(),(m),()->(m)',
|
|
849
|
+
nopython=True,
|
|
850
|
+
target=PARALLEL_OPTION,
|
|
851
|
+
cache=CACHED_OPTION,
|
|
852
|
+
fastmath=FAST_OPTION,
|
|
853
|
+
)
|
|
683
854
|
def _transferCoreFunc(distGridToArrayCenter, distGridToAllMics, waveNumber, result):
|
|
684
855
|
nMics = distGridToAllMics.shape[0]
|
|
685
856
|
for cntMics in range(nMics):
|
|
686
857
|
expArg = np.float32(waveNumber[0] * (distGridToAllMics[cntMics] - distGridToArrayCenter[0]))
|
|
687
858
|
result[cntMics] = (np.cos(expArg) - 1j * np.sin(expArg)) * distGridToArrayCenter[0] / distGridToAllMics[cntMics]
|
|
688
|
-
|