acoular 24.3__py3-none-any.whl → 24.5__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 +118 -50
- acoular/calib.py +29 -38
- acoular/configuration.py +116 -73
- acoular/demo/__init__.py +10 -4
- acoular/demo/acoular_demo.py +78 -53
- acoular/environments.py +265 -262
- acoular/fastFuncs.py +361 -191
- acoular/fbeamform.py +1460 -1404
- acoular/grids.py +501 -545
- acoular/h5cache.py +50 -59
- acoular/h5files.py +154 -137
- acoular/internal.py +10 -11
- acoular/microphones.py +57 -53
- acoular/sdinput.py +47 -52
- acoular/signals.py +167 -179
- acoular/sources.py +818 -693
- acoular/spectra.py +349 -359
- acoular/tbeamform.py +414 -413
- acoular/tfastfuncs.py +178 -101
- acoular/tools/__init__.py +25 -0
- acoular/tools/aiaa.py +186 -0
- acoular/tools/helpers.py +189 -0
- acoular/tools/metrics.py +165 -0
- acoular/tprocess.py +1201 -1143
- 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.5.dist-info}/METADATA +45 -46
- acoular-24.5.dist-info/RECORD +50 -0
- {acoular-24.3.dist-info → acoular-24.5.dist-info}/WHEEL +1 -1
- acoular-24.5.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.5.dist-info}/licenses/AUTHORS.rst +0 -0
acoular/tbeamform.py
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
#
|
|
2
|
-
#pylint: disable-msg=E0611, E1101, C0103, R0901, R0902, R0903, R0904, W0232
|
|
3
|
-
#------------------------------------------------------------------------------
|
|
1
|
+
# ------------------------------------------------------------------------------
|
|
4
2
|
# Copyright (c) Acoular Development Team.
|
|
5
|
-
|
|
3
|
+
# ------------------------------------------------------------------------------
|
|
6
4
|
"""Implements beamformers in the time domain.
|
|
7
5
|
|
|
8
6
|
.. autosummary::
|
|
@@ -20,245 +18,257 @@
|
|
|
20
18
|
"""
|
|
21
19
|
|
|
22
20
|
# imports from other packages
|
|
23
|
-
from
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
from warnings import warn
|
|
22
|
+
|
|
23
|
+
from numpy import (
|
|
24
|
+
arange,
|
|
25
|
+
argmax,
|
|
26
|
+
array,
|
|
27
|
+
ceil,
|
|
28
|
+
concatenate,
|
|
29
|
+
dot,
|
|
30
|
+
empty,
|
|
31
|
+
float32,
|
|
32
|
+
float64,
|
|
33
|
+
histogram,
|
|
34
|
+
int32,
|
|
35
|
+
int64,
|
|
36
|
+
interp,
|
|
37
|
+
isscalar,
|
|
38
|
+
newaxis,
|
|
39
|
+
r_,
|
|
40
|
+
s_,
|
|
41
|
+
sqrt,
|
|
42
|
+
sum,
|
|
43
|
+
unique,
|
|
44
|
+
where,
|
|
45
|
+
zeros,
|
|
46
|
+
)
|
|
27
47
|
from numpy.linalg import norm
|
|
28
|
-
from traits.api import
|
|
29
|
-
cached_property, List, Instance, Range, Int, Enum
|
|
48
|
+
from traits.api import Bool, CArray, Delegate, Enum, Float, Instance, Int, List, Property, Range, Trait, cached_property
|
|
30
49
|
from traits.trait_errors import TraitError
|
|
31
|
-
|
|
50
|
+
|
|
51
|
+
from .fbeamform import SteeringVector
|
|
52
|
+
from .grids import RectGrid
|
|
32
53
|
|
|
33
54
|
# acoular imports
|
|
34
55
|
from .internal import digest
|
|
35
|
-
from .
|
|
36
|
-
from .trajectory import Trajectory
|
|
56
|
+
from .tfastfuncs import _delayandsum4, _delayandsum5, _delays, _steer_I, _steer_II, _steer_III, _steer_IV
|
|
37
57
|
from .tprocess import TimeInOut
|
|
38
|
-
from .
|
|
39
|
-
from .tfastfuncs import _delayandsum4, _delayandsum5, \
|
|
40
|
-
_steer_I, _steer_II, _steer_III, _steer_IV, _delays, _modf
|
|
58
|
+
from .trajectory import Trajectory
|
|
41
59
|
|
|
42
60
|
|
|
43
|
-
def const_power_weight(
|
|
44
|
-
"""
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
Provides microphone weighting
|
|
61
|
+
def const_power_weight(bf):
|
|
62
|
+
"""Internal helper function for :class:`BeamformerTime`.
|
|
63
|
+
|
|
64
|
+
Provides microphone weighting
|
|
48
65
|
to make the power per unit area of the
|
|
49
66
|
microphone array geometry constant.
|
|
50
|
-
|
|
67
|
+
|
|
51
68
|
Parameters
|
|
52
69
|
----------
|
|
53
70
|
bf: :class:`BeamformerTime` object
|
|
54
|
-
|
|
55
|
-
|
|
71
|
+
|
|
72
|
+
|
|
56
73
|
Returns
|
|
57
74
|
-------
|
|
58
75
|
array of floats
|
|
59
76
|
The weight factors.
|
|
60
|
-
"""
|
|
61
77
|
|
|
62
|
-
|
|
78
|
+
"""
|
|
79
|
+
r = bf.steer.env._r(zeros((3, 1)), bf.steer.mics.mpos) # distances to center
|
|
63
80
|
# round the relative distances to one decimal place
|
|
64
|
-
r = (r/r.max()).round(decimals=1)
|
|
81
|
+
r = (r / r.max()).round(decimals=1)
|
|
65
82
|
ru, ind = unique(r, return_inverse=True)
|
|
66
|
-
ru = (ru[1:]+ru[:-1])/2
|
|
67
|
-
count, bins = histogram(r, r_[0, ru, 1.5*r.max()-0.5*ru[-1]])
|
|
83
|
+
ru = (ru[1:] + ru[:-1]) / 2
|
|
84
|
+
count, bins = histogram(r, r_[0, ru, 1.5 * r.max() - 0.5 * ru[-1]])
|
|
68
85
|
bins *= bins
|
|
69
|
-
weights = sqrt((bins[1:]-bins[:-1])/count)
|
|
86
|
+
weights = sqrt((bins[1:] - bins[:-1]) / count)
|
|
70
87
|
weights /= weights.mean()
|
|
71
88
|
return weights[ind]
|
|
72
89
|
|
|
90
|
+
|
|
73
91
|
# possible choices for spatial weights
|
|
74
|
-
possible_weights = {'none':None,
|
|
75
|
-
'power':const_power_weight}
|
|
92
|
+
possible_weights = {'none': None, 'power': const_power_weight}
|
|
76
93
|
|
|
77
94
|
|
|
78
|
-
class BeamformerTime(
|
|
79
|
-
"""
|
|
80
|
-
Provides a basic time domain beamformer with time signal output
|
|
95
|
+
class BeamformerTime(TimeInOut):
|
|
96
|
+
"""Provides a basic time domain beamformer with time signal output
|
|
81
97
|
for a spatially fixed grid.
|
|
82
98
|
"""
|
|
83
99
|
|
|
84
|
-
|
|
85
100
|
# Instance of :class:`~acoular.fbeamform.SteeringVector` or its derived classes
|
|
86
101
|
# that contains information about the steering vector. This is a private trait.
|
|
87
102
|
# Do not set this directly, use `steer` trait instead.
|
|
88
|
-
_steer_obj = Instance(SteeringVector(), SteeringVector)
|
|
89
|
-
|
|
90
|
-
#: :class:`~acoular.fbeamform.SteeringVector` or derived object.
|
|
103
|
+
_steer_obj = Instance(SteeringVector(), SteeringVector)
|
|
104
|
+
|
|
105
|
+
#: :class:`~acoular.fbeamform.SteeringVector` or derived object.
|
|
91
106
|
#: Defaults to :class:`~acoular.fbeamform.SteeringVector` object.
|
|
92
|
-
steer = Property(desc=
|
|
93
|
-
|
|
107
|
+
steer = Property(desc='steering vector object')
|
|
108
|
+
|
|
94
109
|
def _get_steer(self):
|
|
95
110
|
return self._steer_obj
|
|
96
|
-
|
|
111
|
+
|
|
97
112
|
def _set_steer(self, steer):
|
|
98
113
|
if type(steer) == SteeringVector:
|
|
99
114
|
# This condition may be replaced at a later time by: isinstance(steer, SteeringVector): -- (derived classes allowed)
|
|
100
115
|
self._steer_obj = steer
|
|
101
116
|
elif steer in ('true level', 'true location', 'classic', 'inverse'):
|
|
102
117
|
# Type of steering vectors, see also :ref:`Sarradj, 2012<Sarradj2012>`.
|
|
103
|
-
warn(
|
|
104
|
-
|
|
105
|
-
|
|
118
|
+
warn(
|
|
119
|
+
"Deprecated use of 'steer' trait. Please use object of class 'SteeringVector' in the future.",
|
|
120
|
+
Warning,
|
|
121
|
+
stacklevel=2,
|
|
122
|
+
)
|
|
106
123
|
self._steer_obj.steer_type = steer
|
|
107
124
|
else:
|
|
108
|
-
raise(TraitError(args=self,
|
|
109
|
-
name='steer',
|
|
110
|
-
info='SteeringVector',
|
|
111
|
-
value=steer))
|
|
125
|
+
raise (TraitError(args=self, name='steer', info='SteeringVector', value=steer))
|
|
112
126
|
|
|
113
127
|
# --- List of backwards compatibility traits and their setters/getters -----------
|
|
114
|
-
|
|
115
|
-
# :class:`~acoular.environments.Environment` or derived object.
|
|
116
|
-
# Deprecated! Only kept for backwards compatibility.
|
|
128
|
+
|
|
129
|
+
# :class:`~acoular.environments.Environment` or derived object.
|
|
130
|
+
# Deprecated! Only kept for backwards compatibility.
|
|
117
131
|
# Now governed by :attr:`steer` trait.
|
|
118
132
|
env = Property()
|
|
119
|
-
|
|
133
|
+
|
|
120
134
|
def _get_env(self):
|
|
121
|
-
return self._steer_obj.env
|
|
122
|
-
|
|
135
|
+
return self._steer_obj.env
|
|
136
|
+
|
|
123
137
|
def _set_env(self, env):
|
|
124
|
-
warn("Deprecated use of 'env' trait. ", Warning, stacklevel
|
|
138
|
+
warn("Deprecated use of 'env' trait. ", Warning, stacklevel=2)
|
|
125
139
|
self._steer_obj.env = env
|
|
126
|
-
|
|
140
|
+
|
|
127
141
|
# The speed of sound.
|
|
128
|
-
# Deprecated! Only kept for backwards compatibility.
|
|
142
|
+
# Deprecated! Only kept for backwards compatibility.
|
|
129
143
|
# Now governed by :attr:`steer` trait.
|
|
130
144
|
c = Property()
|
|
131
|
-
|
|
145
|
+
|
|
132
146
|
def _get_c(self):
|
|
133
147
|
return self._steer_obj.env.c
|
|
134
|
-
|
|
148
|
+
|
|
135
149
|
def _set_c(self, c):
|
|
136
|
-
warn("Deprecated use of 'c' trait. ", Warning, stacklevel
|
|
150
|
+
warn("Deprecated use of 'c' trait. ", Warning, stacklevel=2)
|
|
137
151
|
self._steer_obj.env.c = c
|
|
138
|
-
|
|
152
|
+
|
|
139
153
|
# :class:`~acoular.grids.Grid`-derived object that provides the grid locations.
|
|
140
|
-
# Deprecated! Only kept for backwards compatibility.
|
|
154
|
+
# Deprecated! Only kept for backwards compatibility.
|
|
141
155
|
# Now governed by :attr:`steer` trait.
|
|
142
156
|
grid = Property()
|
|
143
157
|
|
|
144
158
|
def _get_grid(self):
|
|
145
159
|
return self._steer_obj.grid
|
|
146
|
-
|
|
160
|
+
|
|
147
161
|
def _set_grid(self, grid):
|
|
148
|
-
warn("Deprecated use of 'grid' trait. ", Warning, stacklevel
|
|
162
|
+
warn("Deprecated use of 'grid' trait. ", Warning, stacklevel=2)
|
|
149
163
|
self._steer_obj.grid = grid
|
|
150
|
-
|
|
164
|
+
|
|
151
165
|
# :class:`~acoular.microphones.MicGeom` object that provides the microphone locations.
|
|
152
|
-
# Deprecated! Only kept for backwards compatibility.
|
|
166
|
+
# Deprecated! Only kept for backwards compatibility.
|
|
153
167
|
# Now governed by :attr:`steer` trait
|
|
154
168
|
mpos = Property()
|
|
155
|
-
|
|
169
|
+
|
|
156
170
|
def _get_mpos(self):
|
|
157
171
|
return self._steer_obj.mics
|
|
158
|
-
|
|
172
|
+
|
|
159
173
|
def _set_mpos(self, mpos):
|
|
160
|
-
warn("Deprecated use of 'mpos' trait. ", Warning, stacklevel
|
|
174
|
+
warn("Deprecated use of 'mpos' trait. ", Warning, stacklevel=2)
|
|
161
175
|
self._steer_obj.mics = mpos
|
|
162
|
-
|
|
163
|
-
|
|
176
|
+
|
|
164
177
|
# Sound travel distances from microphone array center to grid points (r0)
|
|
165
178
|
# and all array mics to grid points (rm). Readonly.
|
|
166
|
-
# Deprecated! Only kept for backwards compatibility.
|
|
179
|
+
# Deprecated! Only kept for backwards compatibility.
|
|
167
180
|
# Now governed by :attr:`steer` trait
|
|
168
181
|
r0 = Property()
|
|
182
|
+
|
|
169
183
|
def _get_r0(self):
|
|
170
184
|
return self._steer_obj.r0
|
|
171
|
-
|
|
185
|
+
|
|
172
186
|
rm = Property()
|
|
187
|
+
|
|
173
188
|
def _get_rm(self):
|
|
174
189
|
return self._steer_obj.rm
|
|
175
|
-
|
|
190
|
+
|
|
176
191
|
# --- End of backwards compatibility traits --------------------------------------
|
|
177
192
|
|
|
178
193
|
#: Number of channels in output (=number of grid points).
|
|
179
194
|
numchannels = Delegate('grid', 'size')
|
|
180
195
|
|
|
181
196
|
#: Spatial weighting function.
|
|
182
|
-
weights = Trait('none', possible_weights,
|
|
183
|
-
desc="spatial weighting function")
|
|
197
|
+
weights = Trait('none', possible_weights, desc='spatial weighting function')
|
|
184
198
|
# (from timedomain.possible_weights)
|
|
185
199
|
|
|
186
|
-
# buffer with microphone time signals used for processing. Internal use
|
|
187
|
-
buffer = CArray(desc=
|
|
188
|
-
|
|
200
|
+
# buffer with microphone time signals used for processing. Internal use
|
|
201
|
+
buffer = CArray(desc='buffer containing microphone signals')
|
|
202
|
+
|
|
189
203
|
# index indicating position of current processing sample. Internal use.
|
|
190
|
-
bufferIndex = Int(desc=
|
|
204
|
+
bufferIndex = Int(desc='index indicating position in buffer')
|
|
191
205
|
|
|
192
206
|
# internal identifier
|
|
193
|
-
digest = Property(
|
|
194
|
-
depends_on
|
|
195
|
-
|
|
207
|
+
digest = Property(
|
|
208
|
+
depends_on=['_steer_obj.digest', 'source.digest', 'weights', '__class__'],
|
|
209
|
+
)
|
|
196
210
|
|
|
197
211
|
@cached_property
|
|
198
|
-
def _get_digest(
|
|
212
|
+
def _get_digest(self):
|
|
199
213
|
return digest(self)
|
|
200
|
-
|
|
214
|
+
|
|
201
215
|
def _get_weights(self):
|
|
202
|
-
if self.weights_
|
|
203
|
-
w = self.weights_(self)[newaxis]
|
|
204
|
-
else:
|
|
205
|
-
w = 1.0
|
|
206
|
-
return w
|
|
216
|
+
return self.weights_(self)[newaxis] if self.weights_ else 1.0
|
|
207
217
|
|
|
208
|
-
def _fill_buffer(self,num):
|
|
209
|
-
"""
|
|
218
|
+
def _fill_buffer(self, num):
|
|
219
|
+
"""Generator that fills the signal buffer."""
|
|
210
220
|
weights = self._get_weights()
|
|
211
221
|
for block in self.source.result(num):
|
|
212
222
|
block *= weights
|
|
213
223
|
ns = block.shape[0]
|
|
214
224
|
bufferSize = self.buffer.shape[0]
|
|
215
|
-
self.buffer[0:(bufferSize-ns)] = self.buffer[-(bufferSize-ns):]
|
|
216
|
-
self.buffer[-ns
|
|
225
|
+
self.buffer[0 : (bufferSize - ns)] = self.buffer[-(bufferSize - ns) :]
|
|
226
|
+
self.buffer[-ns:, :] = block
|
|
217
227
|
self.bufferIndex -= ns
|
|
218
228
|
yield
|
|
219
|
-
|
|
220
|
-
def result(
|
|
221
|
-
"""
|
|
222
|
-
Python generator that yields the *squared* deconvolved beamformer
|
|
229
|
+
|
|
230
|
+
def result(self, num=2048):
|
|
231
|
+
"""Python generator that yields the *squared* deconvolved beamformer
|
|
223
232
|
output with optional removal of autocorrelation block-wise.
|
|
224
|
-
|
|
233
|
+
|
|
225
234
|
Parameters
|
|
226
235
|
----------
|
|
227
236
|
num : integer, defaults to 2048
|
|
228
237
|
This parameter defines the size of the blocks to be yielded
|
|
229
238
|
(i.e. the number of samples per block).
|
|
230
|
-
|
|
239
|
+
|
|
231
240
|
Returns
|
|
232
241
|
-------
|
|
233
242
|
Samples in blocks of shape \
|
|
234
|
-
(num, :attr:`~BeamformerTime.numchannels`).
|
|
243
|
+
(num, :attr:`~BeamformerTime.numchannels`).
|
|
235
244
|
:attr:`~BeamformerTime.numchannels` is usually very \
|
|
236
245
|
large (number of grid points).
|
|
237
246
|
The last block may be shorter than num. \
|
|
238
|
-
The output starts for signals that were emitted
|
|
247
|
+
The output starts for signals that were emitted
|
|
239
248
|
from the grid at `t=0`.
|
|
249
|
+
|
|
240
250
|
"""
|
|
241
251
|
# initialize values
|
|
242
252
|
fdtype = float64
|
|
243
253
|
idtype = int64
|
|
244
254
|
numMics = self.steer.mics.num_mics
|
|
245
|
-
n_index = arange(0,num+1)[:,newaxis]
|
|
246
|
-
c = self.steer.env.c/self.source.sample_freq
|
|
247
|
-
amp = empty((1,self.grid.size,numMics),dtype=fdtype)
|
|
248
|
-
#delays = empty((1,self.grid.size,numMics),dtype=fdtype)
|
|
249
|
-
d_index = empty((1,self.grid.size,numMics),dtype=idtype)
|
|
250
|
-
d_interp2 = empty((1,self.grid.size,numMics),dtype=fdtype)
|
|
251
|
-
_steer_III(self.rm[newaxis
|
|
252
|
-
_delays(self.rm[newaxis
|
|
255
|
+
n_index = arange(0, num + 1)[:, newaxis]
|
|
256
|
+
c = self.steer.env.c / self.source.sample_freq
|
|
257
|
+
amp = empty((1, self.grid.size, numMics), dtype=fdtype)
|
|
258
|
+
# delays = empty((1,self.grid.size,numMics),dtype=fdtype)
|
|
259
|
+
d_index = empty((1, self.grid.size, numMics), dtype=idtype)
|
|
260
|
+
d_interp2 = empty((1, self.grid.size, numMics), dtype=fdtype)
|
|
261
|
+
_steer_III(self.rm[newaxis, :, :], self.r0[newaxis, :], amp)
|
|
262
|
+
_delays(self.rm[newaxis, :, :], c, d_interp2, d_index)
|
|
253
263
|
amp.shape = amp.shape[1:]
|
|
254
|
-
#delays.shape = delays.shape[1:]
|
|
264
|
+
# delays.shape = delays.shape[1:]
|
|
255
265
|
d_index.shape = d_index.shape[1:]
|
|
256
266
|
d_interp2.shape = d_interp2.shape[1:]
|
|
257
|
-
maxdelay = int((self.rm/c).max())+2 + num
|
|
258
|
-
initialNumberOfBlocks = int(ceil(maxdelay/num))
|
|
259
|
-
bufferSize=initialNumberOfBlocks*num
|
|
260
|
-
self.buffer = zeros((bufferSize,numMics), dtype=float)
|
|
261
|
-
self.bufferIndex = bufferSize
|
|
267
|
+
maxdelay = int((self.rm / c).max()) + 2 + num # +2 because of interpolation
|
|
268
|
+
initialNumberOfBlocks = int(ceil(maxdelay / num))
|
|
269
|
+
bufferSize = initialNumberOfBlocks * num
|
|
270
|
+
self.buffer = zeros((bufferSize, numMics), dtype=float)
|
|
271
|
+
self.bufferIndex = bufferSize # indexing current time sample in buffer
|
|
262
272
|
fill_buffer_generator = self._fill_buffer(num)
|
|
263
273
|
for _ in range(initialNumberOfBlocks):
|
|
264
274
|
next(fill_buffer_generator)
|
|
@@ -266,264 +276,254 @@ class BeamformerTime( TimeInOut ):
|
|
|
266
276
|
# start processing
|
|
267
277
|
flag = True
|
|
268
278
|
while flag:
|
|
269
|
-
samplesleft = self.buffer.shape[0]-self.bufferIndex
|
|
270
|
-
if samplesleft-maxdelay <= 0:
|
|
271
|
-
num += samplesleft-maxdelay
|
|
272
|
-
maxdelay += samplesleft-maxdelay
|
|
273
|
-
n_index = arange(0,num+1)[:,newaxis]
|
|
274
|
-
flag=False
|
|
279
|
+
samplesleft = self.buffer.shape[0] - self.bufferIndex
|
|
280
|
+
if samplesleft - maxdelay <= 0:
|
|
281
|
+
num += samplesleft - maxdelay
|
|
282
|
+
maxdelay += samplesleft - maxdelay
|
|
283
|
+
n_index = arange(0, num + 1)[:, newaxis]
|
|
284
|
+
flag = False
|
|
275
285
|
# init step
|
|
276
|
-
p_res = array(
|
|
277
|
-
|
|
278
|
-
Phi, autopow = self.delay_and_sum(num,p_res,d_interp2,d_index,amp)
|
|
286
|
+
p_res = array(self.buffer[self.bufferIndex : self.bufferIndex + maxdelay, :])
|
|
287
|
+
Phi, autopow = self.delay_and_sum(num, p_res, d_interp2, d_index, amp)
|
|
279
288
|
if 'Cleant' not in self.__class__.__name__:
|
|
280
289
|
if 'Sq' not in self.__class__.__name__:
|
|
281
290
|
yield Phi[:num]
|
|
282
|
-
elif self.r_diag:
|
|
283
|
-
yield (Phi[:num]**2 - autopow[:num]).clip(min=0)
|
|
291
|
+
elif self.r_diag:
|
|
292
|
+
yield (Phi[:num] ** 2 - autopow[:num]).clip(min=0)
|
|
284
293
|
else:
|
|
285
|
-
yield Phi[:num]**2
|
|
294
|
+
yield Phi[:num] ** 2
|
|
286
295
|
else:
|
|
287
296
|
Gamma = zeros(Phi.shape)
|
|
288
297
|
Gamma_autopow = zeros(Phi.shape)
|
|
289
298
|
J = 0
|
|
290
|
-
# deconvolution
|
|
291
|
-
while
|
|
299
|
+
# deconvolution
|
|
300
|
+
while self.n_iter > J:
|
|
292
301
|
# print(f"start clean iteration {J+1} of max {self.n_iter}")
|
|
293
|
-
if self.r_diag:
|
|
294
|
-
powPhi = (Phi[:num]**2-autopow).sum(0).clip(min=0)
|
|
295
|
-
else:
|
|
296
|
-
powPhi = (Phi[:num]**2).sum(0)
|
|
302
|
+
powPhi = (Phi[:num] ** 2 - autopow).sum(0).clip(min=0) if self.r_diag else (Phi[:num] ** 2).sum(0)
|
|
297
303
|
imax = argmax(powPhi)
|
|
298
304
|
t_float = d_interp2[imax] + d_index[imax] + n_index
|
|
299
305
|
t_ind = t_float.astype(int64)
|
|
300
|
-
for m in range(numMics):
|
|
301
|
-
p_res[t_ind[:num+1,m],m] -= self.damp*interp(
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
+
for m in range(numMics):
|
|
307
|
+
p_res[t_ind[: num + 1, m], m] -= self.damp * interp(
|
|
308
|
+
t_ind[: num + 1, m],
|
|
309
|
+
t_float[:num, m],
|
|
310
|
+
Phi[:num, imax] * self.r0[imax] / self.rm[imax, m],
|
|
311
|
+
)
|
|
312
|
+
nextPhi, nextAutopow = self.delay_and_sum(num, p_res, d_interp2, d_index, amp)
|
|
306
313
|
if self.r_diag:
|
|
307
|
-
pownextPhi = (nextPhi[:num]**2-nextAutopow).sum(0).clip(min=0)
|
|
314
|
+
pownextPhi = (nextPhi[:num] ** 2 - nextAutopow).sum(0).clip(min=0)
|
|
308
315
|
else:
|
|
309
|
-
pownextPhi = (nextPhi[:num]**2).sum(0)
|
|
316
|
+
pownextPhi = (nextPhi[:num] ** 2).sum(0)
|
|
310
317
|
# print(f"total signal power: {powPhi.sum()}")
|
|
311
|
-
if pownextPhi.sum() < powPhi.sum():
|
|
312
|
-
Gamma[:num,imax] += self.damp*Phi[:num,imax]
|
|
313
|
-
Gamma_autopow[:num,imax] = autopow[:num,imax].copy()
|
|
314
|
-
Phi=nextPhi
|
|
315
|
-
autopow=nextAutopow
|
|
318
|
+
if pownextPhi.sum() < powPhi.sum(): # stopping criterion
|
|
319
|
+
Gamma[:num, imax] += self.damp * Phi[:num, imax]
|
|
320
|
+
Gamma_autopow[:num, imax] = autopow[:num, imax].copy()
|
|
321
|
+
Phi = nextPhi
|
|
322
|
+
autopow = nextAutopow
|
|
316
323
|
# print(f"clean max: {L_p((Gamma**2).sum(0)/num).max()} dB")
|
|
317
324
|
J += 1
|
|
318
325
|
else:
|
|
319
326
|
break
|
|
320
327
|
if 'Sq' not in self.__class__.__name__:
|
|
321
328
|
yield Gamma[:num]
|
|
322
|
-
elif self.r_diag:
|
|
323
|
-
yield Gamma[:num]**2 - (self.damp**2)*Gamma_autopow[:num]
|
|
329
|
+
elif self.r_diag:
|
|
330
|
+
yield Gamma[:num] ** 2 - (self.damp**2) * Gamma_autopow[:num]
|
|
324
331
|
else:
|
|
325
|
-
yield Gamma[:num]**2
|
|
332
|
+
yield Gamma[:num] ** 2
|
|
326
333
|
self.bufferIndex += num
|
|
327
334
|
try:
|
|
328
335
|
next(fill_buffer_generator)
|
|
329
|
-
except:
|
|
336
|
+
except:
|
|
330
337
|
pass
|
|
331
338
|
|
|
332
|
-
def delay_and_sum(self,num,p_res,d_interp2,d_index,amp):
|
|
333
|
-
|
|
334
|
-
result = empty((num, self.grid.size), dtype=float)
|
|
335
|
-
autopow = empty((num, self.grid.size), dtype=float)
|
|
339
|
+
def delay_and_sum(self, num, p_res, d_interp2, d_index, amp):
|
|
340
|
+
"""Standard delay-and-sum method."""
|
|
341
|
+
result = empty((num, self.grid.size), dtype=float) # output array
|
|
342
|
+
autopow = empty((num, self.grid.size), dtype=float) # output array
|
|
336
343
|
_delayandsum4(p_res, d_index, d_interp2, amp, result, autopow)
|
|
337
|
-
return result, autopow
|
|
338
|
-
|
|
344
|
+
return result, autopow
|
|
339
345
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
Provides a time domain beamformer with time-dependend
|
|
346
|
+
|
|
347
|
+
class BeamformerTimeSq(BeamformerTime):
|
|
348
|
+
"""Provides a time domain beamformer with time-dependend
|
|
343
349
|
power signal output and possible autopower removal
|
|
344
350
|
for a spatially fixed grid.
|
|
345
351
|
"""
|
|
346
|
-
|
|
352
|
+
|
|
347
353
|
#: Boolean flag, if 'True' (default), the main diagonal is removed before beamforming.
|
|
348
|
-
r_diag = Bool(True,
|
|
349
|
-
desc="removal of diagonal")
|
|
354
|
+
r_diag = Bool(True, desc='removal of diagonal')
|
|
350
355
|
|
|
351
356
|
# internal identifier
|
|
352
|
-
digest = Property(
|
|
353
|
-
depends_on
|
|
354
|
-
|
|
355
|
-
)
|
|
357
|
+
digest = Property(
|
|
358
|
+
depends_on=['_steer_obj.digest', 'source.digest', 'r_diag', 'weights', '__class__'],
|
|
359
|
+
)
|
|
356
360
|
|
|
357
361
|
@cached_property
|
|
358
|
-
def _get_digest(
|
|
362
|
+
def _get_digest(self):
|
|
359
363
|
return digest(self)
|
|
360
|
-
|
|
361
364
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
Provides a basic time domain beamformer with time signal output
|
|
365
|
+
|
|
366
|
+
class BeamformerTimeTraj(BeamformerTime):
|
|
367
|
+
"""Provides a basic time domain beamformer with time signal output
|
|
365
368
|
for a grid moving along a trajectory.
|
|
366
369
|
"""
|
|
367
370
|
|
|
368
|
-
|
|
369
371
|
#: :class:`~acoular.trajectory.Trajectory` or derived object.
|
|
370
372
|
#: Start time is assumed to be the same as for the samples.
|
|
371
|
-
trajectory = Trait(Trajectory,
|
|
372
|
-
desc="trajectory of the grid center")
|
|
373
|
+
trajectory = Trait(Trajectory, desc='trajectory of the grid center')
|
|
373
374
|
|
|
374
375
|
#: Reference vector, perpendicular to the y-axis of moving grid.
|
|
375
|
-
rvec = CArray(
|
|
376
|
-
|
|
377
|
-
|
|
376
|
+
rvec = CArray(dtype=float, shape=(3,), value=array((0, 0, 0)), desc='reference vector')
|
|
377
|
+
|
|
378
378
|
#: Considering of convective amplification in beamforming formula.
|
|
379
|
-
conv_amp = Bool(False,
|
|
380
|
-
desc="determines if convective amplification of source is considered")
|
|
379
|
+
conv_amp = Bool(False, desc='determines if convective amplification of source is considered')
|
|
381
380
|
|
|
382
381
|
#: Floating point and integer precision
|
|
383
|
-
precision = Trait(64, [32,64],
|
|
384
|
-
desc="numeric precision")
|
|
382
|
+
precision = Trait(64, [32, 64], desc='numeric precision')
|
|
385
383
|
|
|
386
384
|
# internal identifier
|
|
387
|
-
digest = Property(
|
|
388
|
-
depends_on
|
|
389
|
-
|
|
390
|
-
|
|
385
|
+
digest = Property(
|
|
386
|
+
depends_on=[
|
|
387
|
+
'_steer_obj.digest',
|
|
388
|
+
'source.digest',
|
|
389
|
+
'weights',
|
|
390
|
+
'precision',
|
|
391
|
+
'rvec',
|
|
392
|
+
'conv_amp',
|
|
393
|
+
'trajectory.digest',
|
|
394
|
+
'__class__',
|
|
395
|
+
],
|
|
396
|
+
)
|
|
391
397
|
|
|
392
398
|
@cached_property
|
|
393
|
-
def _get_digest(
|
|
399
|
+
def _get_digest(self):
|
|
394
400
|
return digest(self)
|
|
395
|
-
|
|
401
|
+
|
|
396
402
|
def get_moving_gpos(self):
|
|
397
|
-
"""
|
|
398
|
-
|
|
399
|
-
"""
|
|
403
|
+
"""Python generator that yields the moving grid coordinates samplewise."""
|
|
404
|
+
|
|
400
405
|
def cross(a, b):
|
|
401
|
-
"""
|
|
402
|
-
|
|
406
|
+
"""Cross product for fast computation
|
|
407
|
+
because numpy.cross is ultra slow in this case.
|
|
403
408
|
"""
|
|
404
|
-
return array([a[1]*b[2] - a[2]*b[1],
|
|
405
|
-
a[2]*b[0] - a[0]*b[2],
|
|
406
|
-
a[0]*b[1] - a[1]*b[0]])
|
|
409
|
+
return array([a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]])
|
|
407
410
|
|
|
408
411
|
start_t = 0.0
|
|
409
412
|
gpos = self.grid.pos()
|
|
410
|
-
trajg = self.trajectory.traj(
|
|
411
|
-
trajg1 = self.trajectory.traj(
|
|
412
|
-
|
|
413
|
-
rflag = (self.rvec == 0).all() #flag translation vs. rotation
|
|
413
|
+
trajg = self.trajectory.traj(start_t, delta_t=1 / self.source.sample_freq)
|
|
414
|
+
trajg1 = self.trajectory.traj(start_t, delta_t=1 / self.source.sample_freq, der=1)
|
|
415
|
+
rflag = (self.rvec == 0).all() # flag translation vs. rotation
|
|
414
416
|
if rflag:
|
|
415
417
|
for g in trajg:
|
|
416
418
|
# grid is only translated, not rotated
|
|
417
419
|
tpos = gpos + array(g)[:, newaxis]
|
|
418
420
|
yield tpos
|
|
419
421
|
else:
|
|
420
|
-
for
|
|
422
|
+
for g, g1 in zip(trajg, trajg1):
|
|
421
423
|
# grid is both translated and rotated
|
|
422
|
-
loc = array(g)
|
|
423
|
-
dx = array(g1)
|
|
424
|
-
dy = cross(self.rvec, dx)
|
|
425
|
-
dz = cross(dx, dy)
|
|
426
|
-
RM = array((dx, dy, dz)).T
|
|
427
|
-
RM /= sqrt((RM*RM).sum(0))
|
|
428
|
-
tpos = dot(RM, gpos)+loc[:, newaxis]
|
|
429
|
-
# print(loc[:])
|
|
424
|
+
loc = array(g) # translation array([0., 0.4, 1.])
|
|
425
|
+
dx = array(g1) # direction vector (new x-axis)
|
|
426
|
+
dy = cross(self.rvec, dx) # new y-axis
|
|
427
|
+
dz = cross(dx, dy) # new z-axis
|
|
428
|
+
RM = array((dx, dy, dz)).T # rotation matrix
|
|
429
|
+
RM /= sqrt((RM * RM).sum(0)) # column normalized
|
|
430
|
+
tpos = dot(RM, gpos) + loc[:, newaxis] # rotation+translation
|
|
431
|
+
# print(loc[:])
|
|
430
432
|
yield tpos
|
|
431
433
|
|
|
432
|
-
def get_macostheta(self,g1,tpos,rm):
|
|
433
|
-
vvec = array(g1)
|
|
434
|
-
ma = norm(vvec)/self.steer.env.c
|
|
435
|
-
fdv = (vvec/sqrt((vvec*vvec).sum()))[:, newaxis]
|
|
434
|
+
def get_macostheta(self, g1, tpos, rm):
|
|
435
|
+
vvec = array(g1) # velocity vector
|
|
436
|
+
ma = norm(vvec) / self.steer.env.c # machnumber
|
|
437
|
+
fdv = (vvec / sqrt((vvec * vvec).sum()))[:, newaxis] # unit vecor velocity
|
|
436
438
|
mpos = self.steer.mics.mpos[:, newaxis, :]
|
|
437
|
-
rmv = tpos[:, :, newaxis]-mpos
|
|
438
|
-
return (ma*sum(rmv.reshape((3, -1))*fdv, 0)
|
|
439
|
-
/rm.reshape(-1)).reshape(rm.shape)
|
|
439
|
+
rmv = tpos[:, :, newaxis] - mpos
|
|
440
|
+
return (ma * sum(rmv.reshape((3, -1)) * fdv, 0) / rm.reshape(-1)).reshape(rm.shape)
|
|
440
441
|
|
|
441
|
-
def get_r0(
|
|
442
|
+
def get_r0(self, tpos):
|
|
442
443
|
if isscalar(self.steer.ref) and self.steer.ref > 0:
|
|
443
|
-
return self.steer.ref#full((self.steer.grid.size,), self.steer.ref)
|
|
444
|
-
|
|
445
|
-
return self.env._r(tpos)
|
|
444
|
+
return self.steer.ref # full((self.steer.grid.size,), self.steer.ref)
|
|
445
|
+
return self.env._r(tpos)
|
|
446
446
|
|
|
447
|
-
def increase_buffer(
|
|
448
|
-
ar = zeros((num,self.steer.mics.num_mics), dtype=self.buffer.dtype)
|
|
449
|
-
self.buffer = concatenate((ar,self.buffer), axis=0)
|
|
447
|
+
def increase_buffer(self, num):
|
|
448
|
+
ar = zeros((num, self.steer.mics.num_mics), dtype=self.buffer.dtype)
|
|
449
|
+
self.buffer = concatenate((ar, self.buffer), axis=0)
|
|
450
450
|
self.bufferIndex += num
|
|
451
451
|
|
|
452
|
-
def result(
|
|
453
|
-
"""
|
|
454
|
-
|
|
455
|
-
|
|
452
|
+
def result(self, num=2048):
|
|
453
|
+
"""Python generator that yields the deconvolved output block-wise.
|
|
454
|
+
|
|
456
455
|
Parameters
|
|
457
456
|
----------
|
|
458
457
|
num : integer, defaults to 2048
|
|
459
458
|
This parameter defines the size of the blocks to be yielded
|
|
460
459
|
(i.e. the number of samples per block).
|
|
461
|
-
|
|
460
|
+
|
|
462
461
|
Returns
|
|
463
462
|
-------
|
|
464
463
|
Samples in blocks of shape \
|
|
465
|
-
(num, :attr:`~BeamformerTime.numchannels`).
|
|
464
|
+
(num, :attr:`~BeamformerTime.numchannels`).
|
|
466
465
|
:attr:`~BeamformerTime.numchannels` is usually very \
|
|
467
466
|
large (number of grid points).
|
|
468
467
|
The last block may be shorter than num. \
|
|
469
|
-
The output starts for signals that were emitted
|
|
468
|
+
The output starts for signals that were emitted
|
|
470
469
|
from the grid at `t=0`.
|
|
470
|
+
|
|
471
471
|
"""
|
|
472
472
|
# initialize values
|
|
473
|
-
if self.precision==64:
|
|
473
|
+
if self.precision == 64:
|
|
474
474
|
fdtype = float64
|
|
475
475
|
idtype = int64
|
|
476
476
|
else:
|
|
477
477
|
fdtype = float32
|
|
478
478
|
idtype = int32
|
|
479
479
|
w = self._get_weights()
|
|
480
|
-
c = self.steer.env.c/self.source.sample_freq
|
|
480
|
+
c = self.steer.env.c / self.source.sample_freq
|
|
481
481
|
numMics = self.steer.mics.num_mics
|
|
482
482
|
mpos = self.steer.mics.mpos.astype(fdtype)
|
|
483
483
|
m_index = arange(numMics, dtype=idtype)
|
|
484
|
-
n_index = arange(num,dtype=idtype)[:,newaxis]
|
|
485
|
-
blockrm = empty((num,self.grid.size,numMics),dtype=fdtype)
|
|
486
|
-
blockrmconv = empty((num,self.grid.size,numMics),dtype=fdtype)
|
|
487
|
-
amp = empty((num,self.grid.size,numMics),dtype=fdtype)
|
|
488
|
-
#delays = empty((num,self.grid.size,numMics),dtype=fdtype)
|
|
489
|
-
d_index = empty((num,self.grid.size,numMics),dtype=idtype)
|
|
490
|
-
d_interp2 = empty((num,self.grid.size,numMics),dtype=fdtype)
|
|
491
|
-
blockr0 = empty((num,self.grid.size),dtype=fdtype)
|
|
492
|
-
self.buffer = zeros((2*num,numMics), dtype=fdtype)
|
|
493
|
-
self.bufferIndex = self.buffer.shape[0]
|
|
494
|
-
movgpos = self.get_moving_gpos()
|
|
495
|
-
movgspeed = self.trajectory.traj(0.0, delta_t=1/self.source.sample_freq,
|
|
496
|
-
der=1)
|
|
484
|
+
n_index = arange(num, dtype=idtype)[:, newaxis]
|
|
485
|
+
blockrm = empty((num, self.grid.size, numMics), dtype=fdtype)
|
|
486
|
+
blockrmconv = empty((num, self.grid.size, numMics), dtype=fdtype)
|
|
487
|
+
amp = empty((num, self.grid.size, numMics), dtype=fdtype)
|
|
488
|
+
# delays = empty((num,self.grid.size,numMics),dtype=fdtype)
|
|
489
|
+
d_index = empty((num, self.grid.size, numMics), dtype=idtype)
|
|
490
|
+
d_interp2 = empty((num, self.grid.size, numMics), dtype=fdtype)
|
|
491
|
+
blockr0 = empty((num, self.grid.size), dtype=fdtype)
|
|
492
|
+
self.buffer = zeros((2 * num, numMics), dtype=fdtype)
|
|
493
|
+
self.bufferIndex = self.buffer.shape[0]
|
|
494
|
+
movgpos = self.get_moving_gpos() # create moving grid pos generator
|
|
495
|
+
movgspeed = self.trajectory.traj(0.0, delta_t=1 / self.source.sample_freq, der=1)
|
|
497
496
|
fill_buffer_generator = self._fill_buffer(num)
|
|
498
|
-
for
|
|
497
|
+
for _i in range(2):
|
|
499
498
|
next(fill_buffer_generator)
|
|
500
|
-
|
|
499
|
+
|
|
501
500
|
# preliminary implementation of different steering vectors
|
|
502
|
-
steer_func = {
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
501
|
+
steer_func = {
|
|
502
|
+
'classic': _steer_I,
|
|
503
|
+
'inverse': _steer_II,
|
|
504
|
+
'true level': _steer_III,
|
|
505
|
+
'true location': _steer_IV,
|
|
506
|
+
}[self.steer.steer_type]
|
|
507
507
|
|
|
508
508
|
# start processing
|
|
509
509
|
flag = True
|
|
510
|
-
dflag = True
|
|
510
|
+
dflag = True # data is available
|
|
511
511
|
while flag:
|
|
512
512
|
for i in range(num):
|
|
513
513
|
tpos = next(movgpos).astype(fdtype)
|
|
514
|
-
rm = self.steer.env._r(
|
|
515
|
-
blockr0[i
|
|
516
|
-
blockrm[i
|
|
514
|
+
rm = self.steer.env._r(tpos, mpos) # .astype(fdtype)
|
|
515
|
+
blockr0[i, :] = self.get_r0(tpos)
|
|
516
|
+
blockrm[i, :, :] = rm
|
|
517
517
|
if self.conv_amp:
|
|
518
518
|
ht = next(movgspeed)
|
|
519
|
-
blockrmconv[i
|
|
519
|
+
blockrmconv[i, :, :] = rm * (1 - self.get_macostheta(ht, tpos, rm)) ** 2
|
|
520
520
|
if self.conv_amp:
|
|
521
521
|
steer_func(blockrmconv, blockr0, amp)
|
|
522
522
|
else:
|
|
523
523
|
steer_func(blockrm, blockr0, amp)
|
|
524
524
|
_delays(blockrm, c, d_interp2, d_index)
|
|
525
|
-
#_modf(delays, d_interp2, d_index)
|
|
526
|
-
maxdelay = (d_index.max((1,2)) + arange(0,num)).max()+2
|
|
525
|
+
# _modf(delays, d_interp2, d_index)
|
|
526
|
+
maxdelay = (d_index.max((1, 2)) + arange(0, num)).max() + 2 # + because of interpolation
|
|
527
527
|
# increase buffer size because of greater delays
|
|
528
528
|
while maxdelay > self.buffer.shape[0] and dflag:
|
|
529
529
|
self.increase_buffer(num)
|
|
@@ -531,230 +531,233 @@ class BeamformerTimeTraj( BeamformerTime ):
|
|
|
531
531
|
next(fill_buffer_generator)
|
|
532
532
|
except:
|
|
533
533
|
dflag = False
|
|
534
|
-
samplesleft = self.buffer.shape[0]-self.bufferIndex
|
|
534
|
+
samplesleft = self.buffer.shape[0] - self.bufferIndex
|
|
535
535
|
# last block may be shorter
|
|
536
|
-
if samplesleft-maxdelay <= 0:
|
|
537
|
-
num = sum((d_index.max((1,2))+1+arange(0,num)) < samplesleft)
|
|
538
|
-
n_index = arange(num,dtype=idtype)[:,newaxis]
|
|
539
|
-
flag=False
|
|
536
|
+
if samplesleft - maxdelay <= 0:
|
|
537
|
+
num = sum((d_index.max((1, 2)) + 1 + arange(0, num)) < samplesleft)
|
|
538
|
+
n_index = arange(num, dtype=idtype)[:, newaxis]
|
|
539
|
+
flag = False
|
|
540
540
|
# init step
|
|
541
|
-
p_res = self.buffer[self.bufferIndex:self.bufferIndex+maxdelay
|
|
542
|
-
Phi, autopow = self.delay_and_sum(num,p_res,d_interp2,d_index,amp)
|
|
541
|
+
p_res = self.buffer[self.bufferIndex : self.bufferIndex + maxdelay, :].copy()
|
|
542
|
+
Phi, autopow = self.delay_and_sum(num, p_res, d_interp2, d_index, amp)
|
|
543
543
|
if 'Cleant' not in self.__class__.__name__:
|
|
544
544
|
if 'Sq' not in self.__class__.__name__:
|
|
545
545
|
yield Phi[:num]
|
|
546
|
-
elif self.r_diag:
|
|
547
|
-
yield (Phi[:num]**2 - autopow[:num]).clip(min=0)
|
|
546
|
+
elif self.r_diag:
|
|
547
|
+
yield (Phi[:num] ** 2 - autopow[:num]).clip(min=0)
|
|
548
548
|
else:
|
|
549
|
-
yield Phi[:num]**2
|
|
549
|
+
yield Phi[:num] ** 2
|
|
550
550
|
else:
|
|
551
551
|
# choose correct distance
|
|
552
|
-
if self.conv_amp
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
blockrm1 = blockrm
|
|
556
|
-
Gamma = zeros(Phi.shape,dtype=fdtype)
|
|
557
|
-
Gamma_autopow = zeros(Phi.shape,dtype=fdtype)
|
|
552
|
+
blockrm1 = blockrmconv if self.conv_amp else blockrm
|
|
553
|
+
Gamma = zeros(Phi.shape, dtype=fdtype)
|
|
554
|
+
Gamma_autopow = zeros(Phi.shape, dtype=fdtype)
|
|
558
555
|
J = 0
|
|
559
|
-
t_ind = arange(p_res.shape[0],dtype=idtype)
|
|
560
|
-
# deconvolution
|
|
561
|
-
while
|
|
556
|
+
t_ind = arange(p_res.shape[0], dtype=idtype)
|
|
557
|
+
# deconvolution
|
|
558
|
+
while self.n_iter > J:
|
|
562
559
|
# print(f"start clean iteration {J+1} of max {self.n_iter}")
|
|
563
560
|
if self.r_diag:
|
|
564
|
-
powPhi = (Phi[:num]*Phi[:num]-autopow).sum(0).clip(min=0)
|
|
561
|
+
powPhi = (Phi[:num] * Phi[:num] - autopow).sum(0).clip(min=0)
|
|
565
562
|
else:
|
|
566
|
-
powPhi = (Phi[:num]*Phi[:num]).sum(0)
|
|
563
|
+
powPhi = (Phi[:num] * Phi[:num]).sum(0)
|
|
567
564
|
# find index of max power focus point
|
|
568
565
|
imax = argmax(powPhi)
|
|
569
566
|
# find backward delays
|
|
570
|
-
t_float = (d_interp2[:num,imax,m_index] +
|
|
571
|
-
d_index[:num,imax,m_index] + \
|
|
572
|
-
n_index).astype(fdtype)
|
|
567
|
+
t_float = (d_interp2[:num, imax, m_index] + d_index[:num, imax, m_index] + n_index).astype(fdtype)
|
|
573
568
|
# determine max/min delays in sample units
|
|
574
569
|
# + 2 because we do not want to extrapolate behind the last sample
|
|
575
|
-
ind_max = t_float.max(0).astype(idtype)+2
|
|
570
|
+
ind_max = t_float.max(0).astype(idtype) + 2
|
|
576
571
|
ind_min = t_float.min(0).astype(idtype)
|
|
577
572
|
# store time history at max power focus point
|
|
578
|
-
h = Phi[:num,imax]*blockr0[:num,imax]
|
|
573
|
+
h = Phi[:num, imax] * blockr0[:num, imax]
|
|
579
574
|
for m in range(numMics):
|
|
580
575
|
# subtract interpolated time history from microphone signals
|
|
581
|
-
p_res[ind_min[m]:ind_max[m],m] -= self.damp*interp(
|
|
582
|
-
t_ind[ind_min[m]:ind_max[m]],
|
|
583
|
-
t_float[:num,m],
|
|
584
|
-
h/blockrm1[:num,imax,m],
|
|
585
|
-
|
|
586
|
-
nextPhi, nextAutopow = self.delay_and_sum(num,p_res,d_interp2,d_index,amp)
|
|
576
|
+
p_res[ind_min[m] : ind_max[m], m] -= self.damp * interp(
|
|
577
|
+
t_ind[ind_min[m] : ind_max[m]],
|
|
578
|
+
t_float[:num, m],
|
|
579
|
+
h / blockrm1[:num, imax, m],
|
|
580
|
+
)
|
|
581
|
+
nextPhi, nextAutopow = self.delay_and_sum(num, p_res, d_interp2, d_index, amp)
|
|
587
582
|
if self.r_diag:
|
|
588
|
-
pownextPhi = (nextPhi[:num]*nextPhi[:num]-nextAutopow).sum(0).clip(min=0)
|
|
583
|
+
pownextPhi = (nextPhi[:num] * nextPhi[:num] - nextAutopow).sum(0).clip(min=0)
|
|
589
584
|
else:
|
|
590
|
-
pownextPhi = (nextPhi[:num]*nextPhi[:num]).sum(0)
|
|
585
|
+
pownextPhi = (nextPhi[:num] * nextPhi[:num]).sum(0)
|
|
591
586
|
# print(f"total signal power: {powPhi.sum()}")
|
|
592
|
-
if pownextPhi.sum() < powPhi.sum():
|
|
593
|
-
Gamma[:num,imax] += self.damp*Phi[:num,imax]
|
|
594
|
-
Gamma_autopow[:num,imax] = autopow[:num,imax].copy()
|
|
595
|
-
Phi=nextPhi
|
|
596
|
-
autopow=nextAutopow
|
|
587
|
+
if pownextPhi.sum() < powPhi.sum(): # stopping criterion
|
|
588
|
+
Gamma[:num, imax] += self.damp * Phi[:num, imax]
|
|
589
|
+
Gamma_autopow[:num, imax] = autopow[:num, imax].copy()
|
|
590
|
+
Phi = nextPhi
|
|
591
|
+
autopow = nextAutopow
|
|
597
592
|
# print(f"clean max: {L_p((Gamma**2).sum(0)/num).max()} dB")
|
|
598
593
|
J += 1
|
|
599
594
|
else:
|
|
600
595
|
break
|
|
601
596
|
if 'Sq' not in self.__class__.__name__:
|
|
602
597
|
yield Gamma[:num]
|
|
603
|
-
elif self.r_diag:
|
|
604
|
-
yield Gamma[:num]**2 - (self.damp**2)*Gamma_autopow[:num]
|
|
598
|
+
elif self.r_diag:
|
|
599
|
+
yield Gamma[:num] ** 2 - (self.damp**2) * Gamma_autopow[:num]
|
|
605
600
|
else:
|
|
606
|
-
yield Gamma[:num]**2
|
|
601
|
+
yield Gamma[:num] ** 2
|
|
607
602
|
self.bufferIndex += num
|
|
608
603
|
try:
|
|
609
604
|
next(fill_buffer_generator)
|
|
610
|
-
except:
|
|
605
|
+
except:
|
|
611
606
|
dflag = False
|
|
612
|
-
pass
|
|
613
607
|
|
|
614
|
-
def delay_and_sum(self,num,p_res,d_interp2,d_index,amp):
|
|
615
|
-
|
|
616
|
-
if self.precision==64
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
fdtype = float32
|
|
620
|
-
result = empty((num, self.grid.size), dtype=fdtype) # output array
|
|
621
|
-
autopow = empty((num, self.grid.size), dtype=fdtype) # output array
|
|
608
|
+
def delay_and_sum(self, num, p_res, d_interp2, d_index, amp):
|
|
609
|
+
"""Standard delay-and-sum method."""
|
|
610
|
+
fdtype = float64 if self.precision == 64 else float32
|
|
611
|
+
result = empty((num, self.grid.size), dtype=fdtype) # output array
|
|
612
|
+
autopow = empty((num, self.grid.size), dtype=fdtype) # output array
|
|
622
613
|
_delayandsum5(p_res, d_index, d_interp2, amp, result, autopow)
|
|
623
|
-
return result, autopow
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
class BeamformerTimeSqTraj(
|
|
627
|
-
"""
|
|
628
|
-
Provides a time domain beamformer with time-dependent
|
|
614
|
+
return result, autopow
|
|
615
|
+
|
|
616
|
+
|
|
617
|
+
class BeamformerTimeSqTraj(BeamformerTimeSq, BeamformerTimeTraj):
|
|
618
|
+
"""Provides a time domain beamformer with time-dependent
|
|
629
619
|
power signal output and possible autopower removal
|
|
630
620
|
for a grid moving along a trajectory.
|
|
631
621
|
"""
|
|
632
|
-
|
|
622
|
+
|
|
633
623
|
# internal identifier
|
|
634
|
-
digest = Property(
|
|
635
|
-
depends_on
|
|
636
|
-
|
|
637
|
-
|
|
624
|
+
digest = Property(
|
|
625
|
+
depends_on=[
|
|
626
|
+
'_steer_obj.digest',
|
|
627
|
+
'source.digest',
|
|
628
|
+
'r_diag',
|
|
629
|
+
'weights',
|
|
630
|
+
'precision',
|
|
631
|
+
'rvec',
|
|
632
|
+
'conv_amp',
|
|
633
|
+
'trajectory.digest',
|
|
634
|
+
'__class__',
|
|
635
|
+
],
|
|
636
|
+
)
|
|
638
637
|
|
|
639
638
|
@cached_property
|
|
640
|
-
def _get_digest(
|
|
639
|
+
def _get_digest(self):
|
|
641
640
|
return digest(self)
|
|
642
|
-
|
|
643
641
|
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
CLEANT deconvolution method, see :ref:`Cousson et al., 2019<Cousson2019>`.
|
|
647
|
-
|
|
648
|
-
An implementation of the CLEAN method in time domain. This class can only
|
|
642
|
+
|
|
643
|
+
class BeamformerCleant(BeamformerTime):
|
|
644
|
+
"""CLEANT deconvolution method, see :ref:`Cousson et al., 2019<Cousson2019>`.
|
|
645
|
+
|
|
646
|
+
An implementation of the CLEAN method in time domain. This class can only
|
|
649
647
|
be used for static sources.
|
|
650
648
|
"""
|
|
651
649
|
|
|
652
650
|
#: Boolean flag, always False
|
|
653
|
-
r_diag = Enum(False,
|
|
654
|
-
desc="False, as we do not remove autopower in this beamformer")
|
|
651
|
+
r_diag = Enum(False, desc='False, as we do not remove autopower in this beamformer')
|
|
655
652
|
|
|
656
|
-
#: iteration damping factor also referred as loop gain in Cousson et al.
|
|
653
|
+
#: iteration damping factor also referred as loop gain in Cousson et al.
|
|
657
654
|
#: defaults to 0.6
|
|
658
|
-
damp = Range(0.01, 1.0, 0.6,
|
|
659
|
-
desc="damping factor (loop gain)")
|
|
655
|
+
damp = Range(0.01, 1.0, 0.6, desc='damping factor (loop gain)')
|
|
660
656
|
|
|
661
657
|
#: max number of iterations
|
|
662
|
-
n_iter = Int(100,
|
|
663
|
-
|
|
664
|
-
|
|
658
|
+
n_iter = Int(100, desc='maximum number of iterations')
|
|
659
|
+
|
|
665
660
|
# internal identifier
|
|
666
|
-
digest = Property(
|
|
667
|
-
depends_on
|
|
668
|
-
|
|
669
|
-
)
|
|
661
|
+
digest = Property(
|
|
662
|
+
depends_on=['_steer_obj.digest', 'source.digest', 'weights', '__class__', 'damp', 'n_iter'],
|
|
663
|
+
)
|
|
670
664
|
|
|
671
665
|
@cached_property
|
|
672
|
-
def _get_digest(
|
|
666
|
+
def _get_digest(self):
|
|
673
667
|
return digest(self)
|
|
674
668
|
|
|
675
669
|
|
|
676
|
-
class BeamformerCleantSq(
|
|
677
|
-
"""
|
|
678
|
-
CLEANT deconvolution method, see :ref:`Cousson et al., 2019<Cousson2019>`
|
|
670
|
+
class BeamformerCleantSq(BeamformerCleant):
|
|
671
|
+
"""CLEANT deconvolution method, see :ref:`Cousson et al., 2019<Cousson2019>`
|
|
679
672
|
with optional removal of autocorrelation.
|
|
680
|
-
|
|
681
|
-
An implementation of the CLEAN method in time domain. This class can only
|
|
673
|
+
|
|
674
|
+
An implementation of the CLEAN method in time domain. This class can only
|
|
682
675
|
be used for static sources.
|
|
683
676
|
"""
|
|
684
677
|
|
|
685
678
|
#: Boolean flag, if 'True' (default), the main diagonal is removed before beamforming.
|
|
686
|
-
r_diag = Bool(True,
|
|
687
|
-
desc="removal of diagonal")
|
|
679
|
+
r_diag = Bool(True, desc='removal of diagonal')
|
|
688
680
|
|
|
689
681
|
# internal identifier
|
|
690
|
-
digest = Property(
|
|
691
|
-
depends_on
|
|
692
|
-
|
|
693
|
-
)
|
|
682
|
+
digest = Property(
|
|
683
|
+
depends_on=['_steer_obj.digest', 'source.digest', 'weights', '__class__', 'damp', 'n_iter', 'r_diag'],
|
|
684
|
+
)
|
|
694
685
|
|
|
695
686
|
@cached_property
|
|
696
|
-
def _get_digest(
|
|
687
|
+
def _get_digest(self):
|
|
697
688
|
return digest(self)
|
|
698
|
-
|
|
699
|
-
|
|
700
689
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
CLEANT deconvolution method, see :ref:`Cousson et al., 2019<Cousson2019>`.
|
|
704
|
-
|
|
690
|
+
|
|
691
|
+
class BeamformerCleantTraj(BeamformerCleant, BeamformerTimeTraj):
|
|
692
|
+
"""CLEANT deconvolution method, see :ref:`Cousson et al., 2019<Cousson2019>`.
|
|
693
|
+
|
|
705
694
|
An implementation of the CLEAN method in time domain for moving sources
|
|
706
|
-
with known trajectory.
|
|
695
|
+
with known trajectory.
|
|
707
696
|
"""
|
|
697
|
+
|
|
708
698
|
#: Floating point and integer precision
|
|
709
|
-
precision = Trait(32, [32,64],
|
|
710
|
-
desc="numeric precision")
|
|
699
|
+
precision = Trait(32, [32, 64], desc='numeric precision')
|
|
711
700
|
|
|
712
701
|
# internal identifier
|
|
713
|
-
digest = Property(
|
|
714
|
-
depends_on
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
702
|
+
digest = Property(
|
|
703
|
+
depends_on=[
|
|
704
|
+
'_steer_obj.digest',
|
|
705
|
+
'source.digest',
|
|
706
|
+
'weights',
|
|
707
|
+
'precision',
|
|
708
|
+
'__class__',
|
|
709
|
+
'damp',
|
|
710
|
+
'n_iter',
|
|
711
|
+
'rvec',
|
|
712
|
+
'conv_amp',
|
|
713
|
+
'trajectory.digest',
|
|
714
|
+
],
|
|
715
|
+
)
|
|
718
716
|
|
|
719
717
|
@cached_property
|
|
720
|
-
def _get_digest(
|
|
718
|
+
def _get_digest(self):
|
|
721
719
|
return digest(self)
|
|
722
|
-
|
|
723
720
|
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
CLEANT deconvolution method, see :ref:`Cousson et al., 2019<Cousson2019>`
|
|
721
|
+
|
|
722
|
+
class BeamformerCleantSqTraj(BeamformerCleantTraj, BeamformerTimeSq):
|
|
723
|
+
"""CLEANT deconvolution method, see :ref:`Cousson et al., 2019<Cousson2019>`
|
|
727
724
|
with optional removal of autocorrelation.
|
|
728
|
-
|
|
725
|
+
|
|
729
726
|
An implementation of the CLEAN method in time domain for moving sources
|
|
730
|
-
with known trajectory.
|
|
727
|
+
with known trajectory.
|
|
731
728
|
"""
|
|
732
729
|
|
|
733
730
|
#: Boolean flag, if 'True' (default), the main diagonal is removed before beamforming.
|
|
734
|
-
r_diag = Bool(True,
|
|
735
|
-
desc="removal of diagonal")
|
|
731
|
+
r_diag = Bool(True, desc='removal of diagonal')
|
|
736
732
|
|
|
737
733
|
# internal identifier
|
|
738
|
-
digest = Property(
|
|
739
|
-
depends_on
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
734
|
+
digest = Property(
|
|
735
|
+
depends_on=[
|
|
736
|
+
'_steer_obj.digest',
|
|
737
|
+
'source.digest',
|
|
738
|
+
'weights',
|
|
739
|
+
'precision',
|
|
740
|
+
'__class__',
|
|
741
|
+
'damp',
|
|
742
|
+
'n_iter',
|
|
743
|
+
'rvec',
|
|
744
|
+
'conv_amp',
|
|
745
|
+
'trajectory.digest',
|
|
746
|
+
'r_diag',
|
|
747
|
+
],
|
|
748
|
+
)
|
|
743
749
|
|
|
744
750
|
@cached_property
|
|
745
|
-
def _get_digest(
|
|
751
|
+
def _get_digest(self):
|
|
746
752
|
return digest(self)
|
|
747
|
-
|
|
748
753
|
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
Provides an Integrator in the time domain.
|
|
752
|
-
"""
|
|
754
|
+
|
|
755
|
+
class IntegratorSectorTime(TimeInOut):
|
|
756
|
+
"""Provides an Integrator in the time domain."""
|
|
753
757
|
|
|
754
758
|
#: :class:`~acoular.grids.RectGrid` object that provides the grid locations.
|
|
755
|
-
grid = Trait(RectGrid,
|
|
756
|
-
|
|
757
|
-
|
|
759
|
+
grid = Trait(RectGrid, desc='beamforming grid')
|
|
760
|
+
|
|
758
761
|
#: List of sectors in grid
|
|
759
762
|
sectors = List()
|
|
760
763
|
|
|
@@ -762,51 +765,49 @@ class IntegratorSectorTime( TimeInOut ):
|
|
|
762
765
|
clip = Float(-350.0)
|
|
763
766
|
|
|
764
767
|
#: Number of channels in output (= number of sectors).
|
|
765
|
-
numchannels = Property(
|
|
768
|
+
numchannels = Property(depends_on=['sectors'])
|
|
766
769
|
|
|
767
770
|
# internal identifier
|
|
768
|
-
digest = Property(
|
|
769
|
-
depends_on
|
|
770
|
-
|
|
771
|
-
)
|
|
771
|
+
digest = Property(
|
|
772
|
+
depends_on=['sectors', 'clip', 'grid.digest', 'source.digest', '__class__'],
|
|
773
|
+
)
|
|
772
774
|
|
|
773
775
|
@cached_property
|
|
774
|
-
def _get_digest(
|
|
776
|
+
def _get_digest(self):
|
|
775
777
|
return digest(self)
|
|
776
|
-
|
|
778
|
+
|
|
777
779
|
@cached_property
|
|
778
|
-
def _get_numchannels
|
|
780
|
+
def _get_numchannels(self):
|
|
779
781
|
return len(self.sectors)
|
|
780
782
|
|
|
781
|
-
def result(
|
|
782
|
-
"""
|
|
783
|
-
Python generator that yields the source output integrated over the given
|
|
783
|
+
def result(self, num=1):
|
|
784
|
+
"""Python generator that yields the source output integrated over the given
|
|
784
785
|
sectors, block-wise.
|
|
785
|
-
|
|
786
|
+
|
|
786
787
|
Parameters
|
|
787
788
|
----------
|
|
788
789
|
num : integer, defaults to 1
|
|
789
790
|
This parameter defines the size of the blocks to be yielded
|
|
790
791
|
(i.e. the number of samples per block).
|
|
791
|
-
|
|
792
|
+
|
|
792
793
|
Returns
|
|
793
794
|
-------
|
|
794
|
-
Samples in blocks of shape (num, :attr:`numchannels`).
|
|
795
|
+
Samples in blocks of shape (num, :attr:`numchannels`).
|
|
795
796
|
:attr:`numchannels` is the number of sectors.
|
|
796
797
|
The last block may be shorter than num.
|
|
798
|
+
|
|
797
799
|
"""
|
|
798
800
|
inds = [self.grid.indices(*sector) for sector in self.sectors]
|
|
799
801
|
gshape = self.grid.shape
|
|
800
|
-
o = empty((num, self.numchannels), dtype=float)
|
|
802
|
+
o = empty((num, self.numchannels), dtype=float) # output array
|
|
801
803
|
for r in self.source.result(num):
|
|
802
804
|
ns = r.shape[0]
|
|
803
805
|
mapshape = (ns,) + gshape
|
|
804
806
|
rmax = r.max()
|
|
805
|
-
rmin = rmax * 10**(self.clip/10.0)
|
|
806
|
-
r = where(r>rmin, r, 0.0)
|
|
807
|
-
i
|
|
808
|
-
|
|
809
|
-
h = r[:].reshape(mapshape)[ (s_[:],) + ind ]
|
|
807
|
+
rmin = rmax * 10 ** (self.clip / 10.0)
|
|
808
|
+
r = where(r > rmin, r, 0.0)
|
|
809
|
+
for i, ind in enumerate(inds):
|
|
810
|
+
h = r[:].reshape(mapshape)[(s_[:],) + ind]
|
|
810
811
|
o[:ns, i] = h.reshape(h.shape[0], -1).sum(axis=1)
|
|
811
812
|
i += 1
|
|
812
|
-
yield o[:ns]
|
|
813
|
+
yield o[:ns]
|