acoular 25.4__py3-none-any.whl → 25.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
acoular/tbeamform.py CHANGED
@@ -1,7 +1,14 @@
1
1
  # ------------------------------------------------------------------------------
2
2
  # Copyright (c) Acoular Development Team.
3
3
  # ------------------------------------------------------------------------------
4
- """Implements beamformers in the time domain.
4
+ """
5
+ Implements beamformers in the time domain.
6
+
7
+ .. inheritance-diagram::
8
+ acoular.tbeamform
9
+ :top-classes:
10
+ acoular.base.TimeOut
11
+ :parts: 1
5
12
 
6
13
  .. autosummary::
7
14
  :toctree: generated/
@@ -17,32 +24,8 @@
17
24
  IntegratorSectorTime
18
25
  """
19
26
 
20
- # imports from other packages
21
-
22
- from numpy import (
23
- arange,
24
- argmax,
25
- array,
26
- ceil,
27
- dot,
28
- empty,
29
- float32,
30
- float64,
31
- histogram,
32
- int32,
33
- int64,
34
- interp,
35
- isscalar,
36
- newaxis,
37
- r_,
38
- s_,
39
- sqrt,
40
- sum, # noqa: A004
41
- unique,
42
- where,
43
- zeros,
44
- )
45
- from scipy.linalg import norm
27
+ import numpy as np
28
+ import scipy.linalg as spla
46
29
  from traits.api import Bool, CArray, Enum, Float, Instance, Int, List, Map, Property, Range, cached_property
47
30
 
48
31
  from .base import SamplesGenerator, TimeOut
@@ -57,7 +40,8 @@ from .trajectory import Trajectory
57
40
 
58
41
 
59
42
  def const_power_weight(bf):
60
- """Internal helper function for :class:`BeamformerTime`.
43
+ """
44
+ Internal helper function for :class:`BeamformerTime`.
61
45
 
62
46
  Provides microphone weighting
63
47
  to make the power per unit area of the
@@ -73,14 +57,14 @@ def const_power_weight(bf):
73
57
  array of floats
74
58
  The weight factors.
75
59
  """
76
- r = bf.steer.env._r(zeros((3, 1)), bf.steer.mics.pos) # distances to center
60
+ r = bf.steer.env._r(np.zeros((3, 1)), bf.steer.mics.pos) # distances to center
77
61
  # round the relative distances to one decimal place
78
62
  r = (r / r.max()).round(decimals=1)
79
- ru, ind = unique(r, return_inverse=True)
63
+ ru, ind = np.unique(r, return_inverse=True)
80
64
  ru = (ru[1:] + ru[:-1]) / 2
81
- count, bins = histogram(r, r_[0, ru, 1.5 * r.max() - 0.5 * ru[-1]])
65
+ count, bins = np.histogram(r, np.r_[0, ru, 1.5 * r.max() - 0.5 * ru[-1]])
82
66
  bins *= bins
83
- weights = sqrt((bins[1:] - bins[:-1]) / count)
67
+ weights = np.sqrt((bins[1:] - bins[:-1]) / count)
84
68
  weights /= weights.mean()
85
69
  return weights[ind]
86
70
 
@@ -90,7 +74,9 @@ possible_weights = {'none': None, 'power': const_power_weight}
90
74
 
91
75
 
92
76
  class BeamformerTime(TimeOut):
93
- """Provides a basic time domain beamformer with time signal output
77
+ """
78
+ Provides a basic time domain beamformer with time signal output.
79
+
94
80
  for a spatially fixed grid.
95
81
  """
96
82
 
@@ -121,41 +107,42 @@ class BeamformerTime(TimeOut):
121
107
  return digest(self)
122
108
 
123
109
  def _get_weights(self):
124
- return self.weights_(self)[newaxis] if self.weights_ else 1.0
110
+ return self.weights_(self)[np.newaxis] if self.weights_ else 1.0
125
111
 
126
112
  def result(self, num=2048):
127
- """Python generator that yields the time-domain beamformer output.
113
+ """
114
+ Python generator that yields the time-domain beamformer output.
128
115
 
129
116
  The output time signal starts for source signals that were emitted from
130
117
  the :class:`~acoular.grids.Grid` at `t=0`.
131
118
 
132
119
  Parameters
133
120
  ----------
134
- num : int
121
+ num : :class:`int`
135
122
  This parameter defines the size of the blocks to be yielded
136
123
  (i.e. the number of samples per block). Defaults to 2048.
137
124
 
138
125
  Yields
139
126
  ------
140
- numpy.ndarray
141
- Samples in blocks of shape (num, :attr:`~BeamformerTime.num_channels`).
127
+ :class:`numpy.ndarray`
128
+ Samples in blocks of shape (``num``, :attr:`~BeamformerTime.num_channels`).
142
129
  :attr:`~BeamformerTime.num_channels` is usually very \
143
130
  large (number of grid points).
144
- The last block returned by the generator may be shorter than num.
131
+ The last block returned by the generator may be shorter than ``num``.
145
132
  """
146
133
  # initialize values
147
134
  steer_func = self.steer._steer_funcs_time[self.steer.steer_type]
148
- fdtype = float64
149
- idtype = int64
135
+ fdtype = np.float64
136
+ idtype = np.int64
150
137
  num_mics = self.steer.mics.num_mics
151
- n_index = arange(0, num + 1)[:, newaxis]
138
+ n_index = np.arange(0, num + 1)[:, np.newaxis]
152
139
  c = self.steer.env.c / self.source.sample_freq
153
- amp = empty((1, self.steer.grid.size, num_mics), dtype=fdtype)
154
- # delays = empty((1,self.steer.grid.size,num_mics),dtype=fdtype)
155
- d_index = empty((1, self.steer.grid.size, num_mics), dtype=idtype)
156
- d_interp2 = empty((1, self.steer.grid.size, num_mics), dtype=fdtype)
157
- steer_func(self.steer.rm[newaxis, :, :], self.steer.r0[newaxis, :], amp)
158
- _delays(self.steer.rm[newaxis, :, :], c, d_interp2, d_index)
140
+ amp = np.empty((1, self.steer.grid.size, num_mics), dtype=fdtype)
141
+ # delays = np.empty((1,self.steer.grid.size,num_mics),dtype=fdtype)
142
+ d_index = np.empty((1, self.steer.grid.size, num_mics), dtype=idtype)
143
+ d_interp2 = np.empty((1, self.steer.grid.size, num_mics), dtype=fdtype)
144
+ steer_func(self.steer.rm[np.newaxis, :, :], self.steer.r0[np.newaxis, :], amp)
145
+ _delays(self.steer.rm[np.newaxis, :, :], c, d_interp2, d_index)
159
146
  amp.shape = amp.shape[1:]
160
147
  # delays.shape = delays.shape[1:]
161
148
  d_index.shape = d_index.shape[1:]
@@ -165,7 +152,7 @@ class BeamformerTime(TimeOut):
165
152
 
166
153
  buffer = SamplesBuffer(
167
154
  source=self.source,
168
- length=int(ceil((num + max_sample_delay) / num)) * num,
155
+ length=int(np.ceil((num + max_sample_delay) / num)) * num,
169
156
  result_num=num + max_sample_delay,
170
157
  shift_index_by='num',
171
158
  dtype=fdtype,
@@ -174,68 +161,73 @@ class BeamformerTime(TimeOut):
174
161
  p_res *= weights
175
162
  if p_res.shape[0] < buffer.result_num: # last block shorter
176
163
  num = p_res.shape[0] - max_sample_delay
177
- n_index = arange(0, num + 1)[:, newaxis]
164
+ # exit loop if there is not enough data left to be processed
165
+ if num <= 0:
166
+ break
167
+ n_index = np.arange(0, num + 1)[:, np.newaxis]
178
168
  # init step
179
- Phi, autopow = self._delay_and_sum(num, p_res, d_interp2, d_index, amp)
169
+ phi, autopow = self._delay_and_sum(num, p_res, d_interp2, d_index, amp)
180
170
  if 'Cleant' not in self.__class__.__name__:
181
171
  if 'Sq' not in self.__class__.__name__:
182
- yield Phi[:num]
172
+ yield phi[:num]
183
173
  elif self.r_diag:
184
- yield (Phi[:num] ** 2 - autopow[:num]).clip(min=0)
174
+ yield (phi[:num] ** 2 - autopow[:num]).clip(min=0)
185
175
  else:
186
- yield Phi[:num] ** 2
176
+ yield phi[:num] ** 2
187
177
  else:
188
178
  p_res_copy = p_res.copy()
189
- Gamma = zeros(Phi.shape)
190
- Gamma_autopow = zeros(Phi.shape)
191
- J = 0
179
+ gamma = np.zeros(phi.shape)
180
+ gamma_autopow = np.zeros(phi.shape)
181
+ j = 0
192
182
  # deconvolution
193
- while self.n_iter > J:
194
- # print(f"start clean iteration {J+1} of max {self.n_iter}")
195
- powPhi = (Phi[:num] ** 2 - autopow).sum(0).clip(min=0) if self.r_diag else (Phi[:num] ** 2).sum(0)
196
- imax = argmax(powPhi)
183
+ while self.n_iter > j:
184
+ # print(f"start clean iteration {j+1} of max {self.n_iter}")
185
+ pow_phi = (phi[:num] ** 2 - autopow).sum(0).clip(min=0) if self.r_diag else (phi[:num] ** 2).sum(0)
186
+ imax = np.argmax(pow_phi)
197
187
  t_float = d_interp2[imax] + d_index[imax] + n_index
198
- t_ind = t_float.astype(int64)
188
+ t_ind = t_float.astype(np.int64)
199
189
  for m in range(num_mics):
200
- p_res_copy[t_ind[: num + 1, m], m] -= self.damp * interp(
190
+ p_res_copy[t_ind[: num + 1, m], m] -= self.damp * np.interp(
201
191
  t_ind[: num + 1, m],
202
192
  t_float[:num, m],
203
- Phi[:num, imax] * self.steer.r0[imax] / self.steer.rm[imax, m],
193
+ phi[:num, imax] * self.steer.r0[imax] / self.steer.rm[imax, m],
204
194
  )
205
- nextPhi, nextAutopow = self._delay_and_sum(num, p_res_copy, d_interp2, d_index, amp)
195
+ next_phi, next_autopow = self._delay_and_sum(num, p_res_copy, d_interp2, d_index, amp)
206
196
  if self.r_diag:
207
- pownextPhi = (nextPhi[:num] ** 2 - nextAutopow).sum(0).clip(min=0)
197
+ pow_next_phi = (next_phi[:num] ** 2 - next_autopow).sum(0).clip(min=0)
208
198
  else:
209
- pownextPhi = (nextPhi[:num] ** 2).sum(0)
210
- # print(f"total signal power: {powPhi.sum()}")
211
- if pownextPhi.sum() < powPhi.sum(): # stopping criterion
212
- Gamma[:num, imax] += self.damp * Phi[:num, imax]
213
- Gamma_autopow[:num, imax] = autopow[:num, imax].copy()
214
- Phi = nextPhi
215
- autopow = nextAutopow
216
- # print(f"clean max: {L_p((Gamma**2).sum(0)/num).max()} dB")
217
- J += 1
199
+ pow_next_phi = (next_phi[:num] ** 2).sum(0)
200
+ # print(f"total signal power: {pow_phi.sum()}")
201
+ if pow_next_phi.sum() < pow_phi.sum(): # stopping criterion
202
+ gamma[:num, imax] += self.damp * phi[:num, imax]
203
+ gamma_autopow[:num, imax] = autopow[:num, imax].copy()
204
+ phi = next_phi
205
+ autopow = next_autopow
206
+ # print(f"clean max: {L_p((gamma**2).sum(0)/num).max()} dB")
207
+ j += 1
218
208
  else:
219
209
  break
220
210
  if 'Sq' not in self.__class__.__name__:
221
- yield Gamma[:num]
211
+ yield gamma[:num]
222
212
  elif self.r_diag:
223
- yield Gamma[:num] ** 2 - (self.damp**2) * Gamma_autopow[:num]
213
+ yield gamma[:num] ** 2 - (self.damp**2) * gamma_autopow[:num]
224
214
  else:
225
- yield Gamma[:num] ** 2
215
+ yield gamma[:num] ** 2
226
216
 
227
217
  def _delay_and_sum(self, num, p_res, d_interp2, d_index, amp):
228
218
  """Standard delay-and-sum method."""
229
- result = empty((num, self.steer.grid.size), dtype=float) # output array
230
- autopow = empty((num, self.steer.grid.size), dtype=float) # output array
219
+ result = np.empty((num, self.steer.grid.size), dtype=float) # output array
220
+ autopow = np.empty((num, self.steer.grid.size), dtype=float) # output array
231
221
  _delayandsum4(p_res, d_index, d_interp2, amp, result, autopow)
232
222
  return result, autopow
233
223
 
234
224
 
235
225
  class BeamformerTimeSq(BeamformerTime):
236
- """Provides a time domain beamformer with time-dependend
237
- power signal output and possible autopower removal
238
- for a spatially fixed grid.
226
+ """
227
+ Time domain beamformer with squared output and optional autopower removal.
228
+
229
+ Provides a time domain beamformer with time-dependend power signal output and possible autopower
230
+ removal for a spatially fixed grid.
239
231
  """
240
232
 
241
233
  #: Boolean flag, if 'True' (default), the main diagonal is removed before beamforming.
@@ -251,30 +243,33 @@ class BeamformerTimeSq(BeamformerTime):
251
243
  return digest(self)
252
244
 
253
245
  def result(self, num=2048):
254
- """Python generator that yields the **squared** time-domain beamformer output.
246
+ """
247
+ Python generator that yields the **squared** time-domain beamformer output.
255
248
 
256
249
  The squared output time signal starts for source signals that were emitted from
257
250
  the :class:`~acoular.grids.Grid` at `t=0`.
258
251
 
259
252
  Parameters
260
253
  ----------
261
- num : int
254
+ num : :class:`int`
262
255
  This parameter defines the size of the blocks to be yielded
263
256
  (i.e. the number of samples per block). Defaults to 2048.
264
257
 
265
258
  Yields
266
259
  ------
267
- numpy.ndarray
268
- Samples in blocks of shape (num, :attr:`~BeamformerTime.num_channels`).
260
+ :class:`numpy.ndarray`
261
+ Samples in blocks of shape (``num``, :attr:`~BeamformerTime.num_channels`).
269
262
  :attr:`~BeamformerTime.num_channels` is usually very \
270
263
  large (number of grid points).
271
- The last block returned by the generator may be shorter than num.
264
+ The last block returned by the generator may be shorter than ``num``.
272
265
  """
273
266
  return super().result(num)
274
267
 
275
268
 
276
269
  class BeamformerTimeTraj(BeamformerTime):
277
- """Provides a basic time domain beamformer with time signal output
270
+ """
271
+ Provides a basic time domain beamformer with time signal output.
272
+
278
273
  for a grid moving along a trajectory.
279
274
  """
280
275
 
@@ -283,7 +278,7 @@ class BeamformerTimeTraj(BeamformerTime):
283
278
  trajectory = Instance(Trajectory, desc='trajectory of the grid center')
284
279
 
285
280
  #: Reference vector, perpendicular to the y-axis of moving grid.
286
- rvec = CArray(dtype=float, shape=(3,), value=array((0, 0, 0)), desc='reference vector')
281
+ rvec = CArray(dtype=float, shape=(3,), value=np.array((0, 0, 0)), desc='reference vector')
287
282
 
288
283
  #: Considering of convective amplification in beamforming formula.
289
284
  conv_amp = Bool(False, desc='determines if convective amplification of source is considered')
@@ -312,10 +307,12 @@ class BeamformerTimeTraj(BeamformerTime):
312
307
  """Python generator that yields the moving grid coordinates samplewise."""
313
308
 
314
309
  def cross(a, b):
315
- """Cross product for fast computation
310
+ """
311
+ Cross product for fast computation.
312
+
316
313
  because numpy.cross is ultra slow in this case.
317
314
  """
318
- 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]])
315
+ return np.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]])
319
316
 
320
317
  start_t = 0.0
321
318
  gpos = self.steer.grid.pos
@@ -325,74 +322,87 @@ class BeamformerTimeTraj(BeamformerTime):
325
322
  if rflag:
326
323
  for g in trajg:
327
324
  # grid is only translated, not rotated
328
- tpos = gpos + array(g)[:, newaxis]
325
+ tpos = gpos + np.array(g)[:, np.newaxis]
329
326
  yield tpos
330
327
  else:
331
328
  for g, g1 in zip(trajg, trajg1):
332
329
  # grid is both translated and rotated
333
- loc = array(g) # translation array([0., 0.4, 1.])
334
- dx = array(g1) # direction vector (new x-axis)
330
+ loc = np.array(g) # translation array([0., 0.4, 1.])
331
+ dx = np.array(g1) # direction vector (new x-axis)
335
332
  dy = cross(self.rvec, dx) # new y-axis
336
333
  dz = cross(dx, dy) # new z-axis
337
- RM = array((dx, dy, dz)).T # rotation matrix
338
- RM /= sqrt((RM * RM).sum(0)) # column normalized
339
- tpos = dot(RM, gpos) + loc[:, newaxis] # rotation+translation
334
+ rm = np.array((dx, dy, dz)).T # rotation matrix
335
+ rm /= np.sqrt((rm * rm).sum(0)) # column normalized
336
+ tpos = np.dot(rm, gpos) + loc[:, np.newaxis] # rotation+translation
340
337
  # print(loc[:])
341
338
  yield tpos
342
339
 
343
340
  def _get_macostheta(self, g1, tpos, rm):
344
- vvec = array(g1) # velocity vector
345
- ma = norm(vvec) / self.steer.env.c # machnumber
346
- fdv = (vvec / sqrt((vvec * vvec).sum()))[:, newaxis] # unit vecor velocity
347
- mpos = self.steer.mics.pos[:, newaxis, :]
348
- rmv = tpos[:, :, newaxis] - mpos
349
- return (ma * sum(rmv.reshape((3, -1)) * fdv, 0) / rm.reshape(-1)).reshape(rm.shape)
341
+ vvec = np.array(g1) # velocity vector
342
+ ma = spla.norm(vvec) / self.steer.env.c # machnumber
343
+ fdv = (vvec / np.sqrt((vvec * vvec).sum()))[:, np.newaxis] # unit vecor velocity
344
+ mpos = self.steer.mics.pos[:, np.newaxis, :]
345
+ rmv = tpos[:, :, np.newaxis] - mpos
346
+ return (ma * np.sum(rmv.reshape((3, -1)) * fdv, 0) / rm.reshape(-1)).reshape(rm.shape)
350
347
 
351
348
  def get_r0(self, tpos):
352
- if isscalar(self.steer.ref) and self.steer.ref > 0:
349
+ """
350
+ Get reference distance for grid positions.
351
+
352
+ Parameters
353
+ ----------
354
+ tpos : :class:`numpy.ndarray`
355
+ Grid positions.
356
+
357
+ Returns
358
+ -------
359
+ :class:`float` or :class:`numpy.ndarray`
360
+ Reference distance(s).
361
+ """
362
+ if np.isscalar(self.steer.ref) and self.steer.ref > 0:
353
363
  return self.steer.ref # full((self.steer.grid.size,), self.steer.ref)
354
364
  return self.steer.env._r(tpos)
355
365
 
356
366
  def result(self, num=2048):
357
- """Python generator that yields the time-domain beamformer output.
367
+ """
368
+ Python generator that yields the time-domain beamformer output.
358
369
 
359
370
  The output time signal starts for source signals that were emitted from
360
371
  the :class:`~acoular.grids.Grid` at `t=0`.
361
372
 
362
373
  Parameters
363
374
  ----------
364
- num : int
375
+ num : :class:`int`
365
376
  This parameter defines the size of the blocks to be yielded
366
377
  (i.e. the number of samples per block). Defaults to 2048.
367
378
 
368
379
  Yields
369
380
  ------
370
- numpy.ndarray
371
- Samples in blocks of shape (num, :attr:`~BeamformerTime.num_channels`).
381
+ :class:`numpy.ndarray`
382
+ Samples in blocks of shape (``num``, :attr:`~BeamformerTime.num_channels`).
372
383
  :attr:`~BeamformerTime.num_channels` is usually very \
373
384
  large (number of grid points).
374
- The last block returned by the generator may be shorter than num.
385
+ The last block returned by the generator may be shorter than ``num``.
375
386
  """
376
387
  # initialize values
377
388
  if self.precision == 64:
378
- fdtype = float64
379
- idtype = int64
389
+ fdtype = np.float64
390
+ idtype = np.int64
380
391
  else:
381
- fdtype = float32
382
- idtype = int32
383
- w = self._get_weights()
392
+ fdtype = np.float32
393
+ idtype = np.int32
384
394
  c = self.steer.env.c / self.source.sample_freq
385
395
  num_mics = self.steer.mics.num_mics
386
396
  mpos = self.steer.mics.pos.astype(fdtype)
387
- m_index = arange(num_mics, dtype=idtype)
388
- n_index = arange(num, dtype=idtype)[:, newaxis]
389
- blockrm = empty((num, self.steer.grid.size, num_mics), dtype=fdtype)
390
- blockrmconv = empty((num, self.steer.grid.size, num_mics), dtype=fdtype)
391
- amp = empty((num, self.steer.grid.size, num_mics), dtype=fdtype)
392
- # delays = empty((num,self.steer.grid.size,num_mics),dtype=fdtype)
393
- d_index = empty((num, self.steer.grid.size, num_mics), dtype=idtype)
394
- d_interp2 = empty((num, self.steer.grid.size, num_mics), dtype=fdtype)
395
- blockr0 = empty((num, self.steer.grid.size), dtype=fdtype)
397
+ m_index = np.arange(num_mics, dtype=idtype)
398
+ n_index = np.arange(num, dtype=idtype)[:, np.newaxis]
399
+ blockrm = np.empty((num, self.steer.grid.size, num_mics), dtype=fdtype)
400
+ blockrmconv = np.empty((num, self.steer.grid.size, num_mics), dtype=fdtype)
401
+ amp = np.empty((num, self.steer.grid.size, num_mics), dtype=fdtype)
402
+ # delays = np.empty((num,self.steer.grid.size,num_mics),dtype=fdtype)
403
+ d_index = np.empty((num, self.steer.grid.size, num_mics), dtype=idtype)
404
+ d_interp2 = np.empty((num, self.steer.grid.size, num_mics), dtype=fdtype)
405
+ blockr0 = np.empty((num, self.steer.grid.size), dtype=fdtype)
396
406
  movgpos = self._get_moving_gpos() # create moving grid pos generator
397
407
  movgspeed = self.trajectory.traj(0.0, delta_t=1 / self.source.sample_freq, der=1)
398
408
  weights = self._get_weights()
@@ -427,35 +437,35 @@ class BeamformerTimeTraj(BeamformerTime):
427
437
  except StopIteration:
428
438
  break
429
439
  if time_block.shape[0] < buffer.result_num: # last block shorter
430
- num = sum((d_index.max((1, 2)) + 1 + arange(0, num)) < time_block.shape[0])
431
- n_index = arange(num, dtype=idtype)[:, newaxis]
440
+ num = np.sum((d_index.max((1, 2)) + 1 + np.arange(0, num)) < time_block.shape[0])
441
+ n_index = np.arange(num, dtype=idtype)[:, np.newaxis]
432
442
  flag = False
433
443
  # init step
434
444
  p_res = time_block.copy()
435
- Phi, autopow = self._delay_and_sum(num, p_res, d_interp2, d_index, amp)
445
+ phi, autopow = self._delay_and_sum(num, p_res, d_interp2, d_index, amp)
436
446
  if 'Cleant' not in self.__class__.__name__:
437
447
  if 'Sq' not in self.__class__.__name__:
438
- yield Phi[:num]
448
+ yield phi[:num]
439
449
  elif self.r_diag:
440
- yield (Phi[:num] ** 2 - autopow[:num]).clip(min=0)
450
+ yield (phi[:num] ** 2 - autopow[:num]).clip(min=0)
441
451
  else:
442
- yield Phi[:num] ** 2
452
+ yield phi[:num] ** 2
443
453
  else:
444
454
  # choose correct distance
445
455
  blockrm1 = blockrmconv if self.conv_amp else blockrm
446
- Gamma = zeros(Phi.shape, dtype=fdtype)
447
- Gamma_autopow = zeros(Phi.shape, dtype=fdtype)
448
- J = 0
449
- t_ind = arange(p_res.shape[0], dtype=idtype)
456
+ gamma = np.zeros(phi.shape, dtype=fdtype)
457
+ gamma_autopow = np.zeros(phi.shape, dtype=fdtype)
458
+ j = 0
459
+ t_ind = np.arange(p_res.shape[0], dtype=idtype)
450
460
  # deconvolution
451
- while self.n_iter > J:
452
- # print(f"start clean iteration {J+1} of max {self.n_iter}")
461
+ while self.n_iter > j:
462
+ # print(f"start clean iteration {j+1} of max {self.n_iter}")
453
463
  if self.r_diag:
454
- powPhi = (Phi[:num] * Phi[:num] - autopow).sum(0).clip(min=0)
464
+ pow_phi = (phi[:num] * phi[:num] - autopow).sum(0).clip(min=0)
455
465
  else:
456
- powPhi = (Phi[:num] * Phi[:num]).sum(0)
466
+ pow_phi = (phi[:num] * phi[:num]).sum(0)
457
467
  # find index of max power focus point
458
- imax = argmax(powPhi)
468
+ imax = np.argmax(pow_phi)
459
469
  # find backward delays
460
470
  t_float = (d_interp2[:num, imax, m_index] + d_index[:num, imax, m_index] + n_index).astype(fdtype)
461
471
  # determine max/min delays in sample units
@@ -463,49 +473,51 @@ class BeamformerTimeTraj(BeamformerTime):
463
473
  ind_max = t_float.max(0).astype(idtype) + 2
464
474
  ind_min = t_float.min(0).astype(idtype)
465
475
  # store time history at max power focus point
466
- h = Phi[:num, imax] * blockr0[:num, imax]
476
+ h = phi[:num, imax] * blockr0[:num, imax]
467
477
  for m in range(num_mics):
468
478
  # subtract interpolated time history from microphone signals
469
- p_res[ind_min[m] : ind_max[m], m] -= self.damp * interp(
479
+ p_res[ind_min[m] : ind_max[m], m] -= self.damp * np.interp(
470
480
  t_ind[ind_min[m] : ind_max[m]],
471
481
  t_float[:num, m],
472
482
  h / blockrm1[:num, imax, m],
473
483
  )
474
- nextPhi, nextAutopow = self._delay_and_sum(num, p_res, d_interp2, d_index, amp)
484
+ next_phi, next_autopow = self._delay_and_sum(num, p_res, d_interp2, d_index, amp)
475
485
  if self.r_diag:
476
- pownextPhi = (nextPhi[:num] * nextPhi[:num] - nextAutopow).sum(0).clip(min=0)
486
+ pow_next_phi = (next_phi[:num] * next_phi[:num] - next_autopow).sum(0).clip(min=0)
477
487
  else:
478
- pownextPhi = (nextPhi[:num] * nextPhi[:num]).sum(0)
479
- # print(f"total signal power: {powPhi.sum()}")
480
- if pownextPhi.sum() < powPhi.sum(): # stopping criterion
481
- Gamma[:num, imax] += self.damp * Phi[:num, imax]
482
- Gamma_autopow[:num, imax] = autopow[:num, imax].copy()
483
- Phi = nextPhi
484
- autopow = nextAutopow
485
- # print(f"clean max: {L_p((Gamma**2).sum(0)/num).max()} dB")
486
- J += 1
488
+ pow_next_phi = (next_phi[:num] * next_phi[:num]).sum(0)
489
+ # print(f"total signal power: {pow_phi.sum()}")
490
+ if pow_next_phi.sum() < pow_phi.sum(): # stopping criterion
491
+ gamma[:num, imax] += self.damp * phi[:num, imax]
492
+ gamma_autopow[:num, imax] = autopow[:num, imax].copy()
493
+ phi = next_phi
494
+ autopow = next_autopow
495
+ # print(f"clean max: {L_p((gamma**2).sum(0)/num).max()} dB")
496
+ j += 1
487
497
  else:
488
498
  break
489
499
  if 'Sq' not in self.__class__.__name__:
490
- yield Gamma[:num]
500
+ yield gamma[:num]
491
501
  elif self.r_diag:
492
- yield Gamma[:num] ** 2 - (self.damp**2) * Gamma_autopow[:num]
502
+ yield gamma[:num] ** 2 - (self.damp**2) * gamma_autopow[:num]
493
503
  else:
494
- yield Gamma[:num] ** 2
504
+ yield gamma[:num] ** 2
495
505
 
496
506
  def _delay_and_sum(self, num, p_res, d_interp2, d_index, amp):
497
507
  """Standard delay-and-sum method."""
498
- fdtype = float64 if self.precision == 64 else float32
499
- result = empty((num, self.steer.grid.size), dtype=fdtype) # output array
500
- autopow = empty((num, self.steer.grid.size), dtype=fdtype) # output array
508
+ fdtype = np.float64 if self.precision == 64 else np.float32
509
+ result = np.empty((num, self.steer.grid.size), dtype=fdtype) # output array
510
+ autopow = np.empty((num, self.steer.grid.size), dtype=fdtype) # output array
501
511
  _delayandsum5(p_res, d_index, d_interp2, amp, result, autopow)
502
512
  return result, autopow
503
513
 
504
514
 
505
515
  class BeamformerTimeSqTraj(BeamformerTimeSq, BeamformerTimeTraj):
506
- """Provides a time domain beamformer with time-dependent
507
- power signal output and possible autopower removal
508
- for a grid moving along a trajectory.
516
+ """
517
+ Time domain beamformer with squared output for a grid moving along a trajectory.
518
+
519
+ Provides a time domain beamformer with time-dependent power signal output and possible autopower
520
+ removal for a grid moving along a trajectory.
509
521
  """
510
522
 
511
523
  # internal identifier
@@ -527,30 +539,32 @@ class BeamformerTimeSqTraj(BeamformerTimeSq, BeamformerTimeTraj):
527
539
  return digest(self)
528
540
 
529
541
  def result(self, num=2048):
530
- """Python generator that yields the **squared** time-domain beamformer output.
542
+ """
543
+ Python generator that yields the **squared** time-domain beamformer output.
531
544
 
532
545
  The squared output time signal starts for source signals that were emitted from
533
546
  the :class:`~acoular.grids.Grid` at `t=0`.
534
547
 
535
548
  Parameters
536
549
  ----------
537
- num : int
550
+ num : :class:`int`
538
551
  This parameter defines the size of the blocks to be yielded
539
552
  (i.e. the number of samples per block). Defaults to 2048.
540
553
 
541
554
  Yields
542
555
  ------
543
- numpy.ndarray
544
- Samples in blocks of shape (num, :attr:`~BeamformerTime.num_channels`).
556
+ :class:`numpy.ndarray`
557
+ Samples in blocks of shape (``num``, :attr:`~BeamformerTime.num_channels`).
545
558
  :attr:`~BeamformerTime.num_channels` is usually very \
546
559
  large (number of grid points).
547
- The last block returned by the generator may be shorter than num.
560
+ The last block returned by the generator may be shorter than ``num``.
548
561
  """
549
562
  return super().result(num)
550
563
 
551
564
 
552
565
  class BeamformerCleant(BeamformerTime):
553
- """CLEANT deconvolution method.
566
+ """
567
+ CLEANT deconvolution method.
554
568
 
555
569
  An implementation of the CLEAN method in time domain. This class can only
556
570
  be used for static sources. See :cite:`Cousson2019` for details.
@@ -576,30 +590,32 @@ class BeamformerCleant(BeamformerTime):
576
590
  return digest(self)
577
591
 
578
592
  def result(self, num=2048):
579
- """Python generator that yields the deconvolved time-domain beamformer output.
593
+ """
594
+ Python generator that yields the deconvolved time-domain beamformer output.
580
595
 
581
596
  The output starts for signals that were emitted from the :class:`~acoular.grids.Grid` at
582
597
  `t=0`.
583
598
 
584
599
  Parameters
585
600
  ----------
586
- num : int
601
+ num : :class:`int`
587
602
  This parameter defines the size of the blocks to be yielded
588
603
  (i.e. the number of samples per block). Defaults to 2048.
589
604
 
590
605
  Yields
591
606
  ------
592
- numpy.ndarray
593
- Samples in blocks of shape (num, :attr:`~BeamformerTime.num_channels`).
607
+ :class:`numpy.ndarray`
608
+ Samples in blocks of shape (``num``, :attr:`~BeamformerTime.num_channels`).
594
609
  :attr:`~BeamformerTime.num_channels` is usually very \
595
610
  large (number of grid points).
596
- The last block returned by the generator may be shorter than num.
611
+ The last block returned by the generator may be shorter than ``num``.
597
612
  """
598
613
  return super().result(num)
599
614
 
600
615
 
601
616
  class BeamformerCleantSq(BeamformerCleant):
602
- """CLEANT deconvolution method with optional removal of autocorrelation.
617
+ """
618
+ CLEANT deconvolution method with optional removal of autocorrelation.
603
619
 
604
620
  An implementation of the CLEAN method in time domain. This class can only
605
621
  be used for static sources. See :cite:`Cousson2019` for details on the method
@@ -619,7 +635,8 @@ class BeamformerCleantSq(BeamformerCleant):
619
635
  return digest(self)
620
636
 
621
637
  def result(self, num=2048):
622
- """Python generator that yields the *squared* deconvolved time-domain beamformer output.
638
+ """
639
+ Python generator that yields the *squared* deconvolved time-domain beamformer output.
623
640
 
624
641
  The output starts for signals that were emitted from the :class:`~acoular.grids.Grid` at
625
642
  `t=0`. Per default, block-wise removal of autocorrelation is performed, which can be turned
@@ -627,23 +644,24 @@ class BeamformerCleantSq(BeamformerCleant):
627
644
 
628
645
  Parameters
629
646
  ----------
630
- num : int
647
+ num : :class:`int`
631
648
  This parameter defines the size of the blocks to be yielded
632
649
  (i.e. the number of samples per block). Defaults to 2048.
633
650
 
634
651
  Yields
635
652
  ------
636
- numpy.ndarray
637
- Samples in blocks of shape (num, :attr:`~BeamformerTime.num_channels`).
653
+ :class:`numpy.ndarray`
654
+ Samples in blocks of shape (``num``, :attr:`~BeamformerTime.num_channels`).
638
655
  :attr:`~BeamformerTime.num_channels` is usually very \
639
656
  large (number of grid points).
640
- The last block returned by the generator may be shorter than num.
657
+ The last block returned by the generator may be shorter than ``num``.
641
658
  """
642
659
  return super().result(num)
643
660
 
644
661
 
645
662
  class BeamformerCleantTraj(BeamformerCleant, BeamformerTimeTraj):
646
- """CLEANT deconvolution method.
663
+ """
664
+ CLEANT deconvolution method.
647
665
 
648
666
  An implementation of the CLEAN method in time domain for moving sources
649
667
  with known trajectory. See :cite:`Cousson2019` for details.
@@ -672,30 +690,32 @@ class BeamformerCleantTraj(BeamformerCleant, BeamformerTimeTraj):
672
690
  return digest(self)
673
691
 
674
692
  def result(self, num=2048):
675
- """Python generator that yields the deconvolved time-domain beamformer output.
693
+ """
694
+ Python generator that yields the deconvolved time-domain beamformer output.
676
695
 
677
696
  The output starts for signals that were emitted from the :class:`~acoular.grids.Grid` at
678
697
  `t=0`.
679
698
 
680
699
  Parameters
681
700
  ----------
682
- num : int
701
+ num : :class:`int`
683
702
  This parameter defines the size of the blocks to be yielded
684
703
  (i.e. the number of samples per block). Defaults to 2048.
685
704
 
686
705
  Yields
687
706
  ------
688
- numpy.ndarray
689
- Samples in blocks of shape (num, :attr:`~BeamformerTime.num_channels`).
707
+ :class:`numpy.ndarray`
708
+ Samples in blocks of shape (``num``, :attr:`~BeamformerTime.num_channels`).
690
709
  :attr:`~BeamformerTime.num_channels` is usually very \
691
710
  large (number of grid points).
692
- The last block returned by the generator may be shorter than num.
711
+ The last block returned by the generator may be shorter than ``num``.
693
712
  """
694
713
  return super().result(num)
695
714
 
696
715
 
697
716
  class BeamformerCleantSqTraj(BeamformerCleantTraj, BeamformerTimeSq):
698
- """CLEANT deconvolution method with optional removal of autocorrelation.
717
+ """
718
+ CLEANT deconvolution method with optional removal of autocorrelation.
699
719
 
700
720
  An implementation of the CLEAN method in time domain for moving sources
701
721
  with known trajectory. See :cite:`Cousson2019` for details on the method and
@@ -726,7 +746,8 @@ class BeamformerCleantSqTraj(BeamformerCleantTraj, BeamformerTimeSq):
726
746
  return digest(self)
727
747
 
728
748
  def result(self, num=2048):
729
- """Python generator that yields the *squared* deconvolved time-domain beamformer output.
749
+ """
750
+ Python generator that yields the *squared* deconvolved time-domain beamformer output.
730
751
 
731
752
  The output starts for signals that were emitted from the :class:`~acoular.grids.Grid` at
732
753
  `t=0`. Per default, block-wise removal of autocorrelation is performed, which can be turned
@@ -734,17 +755,17 @@ class BeamformerCleantSqTraj(BeamformerCleantTraj, BeamformerTimeSq):
734
755
 
735
756
  Parameters
736
757
  ----------
737
- num : int
758
+ num : :class:`int`
738
759
  This parameter defines the size of the blocks to be yielded
739
760
  (i.e. the number of samples per block). Defaults to 2048.
740
761
 
741
762
  Yields
742
763
  ------
743
- numpy.ndarray
744
- Samples in blocks of shape (num, :attr:`~BeamformerTime.num_channels`).
764
+ :class:`numpy.ndarray`
765
+ Samples in blocks of shape (``num``, :attr:`~BeamformerTime.num_channels`).
745
766
  :attr:`~BeamformerTime.num_channels` is usually very \
746
767
  large (number of grid points).
747
- The last block returned by the generator may be shorter than num.
768
+ The last block returned by the generator may be shorter than ``num``.
748
769
  """
749
770
  return super().result(num)
750
771
 
@@ -781,32 +802,32 @@ class IntegratorSectorTime(TimeOut):
781
802
  return len(self.sectors)
782
803
 
783
804
  def result(self, num=1):
784
- """Python generator that yields the source output integrated over the given
785
- sectors, block-wise.
805
+ """
806
+ Python generator that yields the source output integrated over specified grid sectors.
786
807
 
787
808
  Parameters
788
809
  ----------
789
- num : integer, defaults to 1
790
- This parameter defines the size of the blocks to be yielded
791
- (i.e. the number of samples per block).
810
+ num : :class:`int`
811
+ Size of the blocks to be yielded (number of samples per block). Default is ``1``.
792
812
 
793
813
  Returns
794
814
  -------
795
- Samples in blocks of shape (num, :attr:`num_channels`).
815
+ :class:`numpy.ndarray`
816
+ Samples in blocks of shape (``num``, :attr:`num_channels`).
796
817
  :attr:`num_channels` is the number of sectors.
797
818
  The last block may be shorter than num.
798
819
  """
799
820
  inds = [self.grid.indices(*sector) for sector in self.sectors]
800
821
  gshape = self.grid.shape
801
- o = empty((num, self.num_channels), dtype=float) # output array
822
+ o = np.empty((num, self.num_channels), dtype=float) # output array
802
823
  for r in self.source.result(num):
803
824
  ns = r.shape[0]
804
825
  mapshape = (ns,) + gshape
805
826
  rmax = r.max()
806
827
  rmin = rmax * 10 ** (self.clip / 10.0)
807
- r = where(r > rmin, r, 0.0)
828
+ r = np.where(r > rmin, r, 0.0)
808
829
  for i, ind in enumerate(inds):
809
- h = r[:].reshape(mapshape)[(s_[:],) + ind]
830
+ h = r[:].reshape(mapshape)[(np.s_[:],) + ind]
810
831
  o[:ns, i] = h.reshape(h.shape[0], -1).sum(axis=1)
811
832
  i += 1
812
833
  yield o[:ns]