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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
acoular/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,
@@ -177,68 +164,70 @@ class BeamformerTime(TimeOut):
177
164
  # exit loop if there is not enough data left to be processed
178
165
  if num <= 0:
179
166
  break
180
- n_index = arange(0, num + 1)[:, newaxis]
167
+ n_index = np.arange(0, num + 1)[:, np.newaxis]
181
168
  # init step
182
- 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)
183
170
  if 'Cleant' not in self.__class__.__name__:
184
171
  if 'Sq' not in self.__class__.__name__:
185
- yield Phi[:num]
172
+ yield phi[:num]
186
173
  elif self.r_diag:
187
- yield (Phi[:num] ** 2 - autopow[:num]).clip(min=0)
174
+ yield (phi[:num] ** 2 - autopow[:num]).clip(min=0)
188
175
  else:
189
- yield Phi[:num] ** 2
176
+ yield phi[:num] ** 2
190
177
  else:
191
178
  p_res_copy = p_res.copy()
192
- Gamma = zeros(Phi.shape)
193
- Gamma_autopow = zeros(Phi.shape)
194
- J = 0
179
+ gamma = np.zeros(phi.shape)
180
+ gamma_autopow = np.zeros(phi.shape)
181
+ j = 0
195
182
  # deconvolution
196
- while self.n_iter > J:
197
- # print(f"start clean iteration {J+1} of max {self.n_iter}")
198
- powPhi = (Phi[:num] ** 2 - autopow).sum(0).clip(min=0) if self.r_diag else (Phi[:num] ** 2).sum(0)
199
- 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)
200
187
  t_float = d_interp2[imax] + d_index[imax] + n_index
201
- t_ind = t_float.astype(int64)
188
+ t_ind = t_float.astype(np.int64)
202
189
  for m in range(num_mics):
203
- 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(
204
191
  t_ind[: num + 1, m],
205
192
  t_float[:num, m],
206
- 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],
207
194
  )
208
- 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)
209
196
  if self.r_diag:
210
- 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)
211
198
  else:
212
- pownextPhi = (nextPhi[:num] ** 2).sum(0)
213
- # print(f"total signal power: {powPhi.sum()}")
214
- if pownextPhi.sum() < powPhi.sum(): # stopping criterion
215
- Gamma[:num, imax] += self.damp * Phi[:num, imax]
216
- Gamma_autopow[:num, imax] = autopow[:num, imax].copy()
217
- Phi = nextPhi
218
- autopow = nextAutopow
219
- # print(f"clean max: {L_p((Gamma**2).sum(0)/num).max()} dB")
220
- 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
221
208
  else:
222
209
  break
223
210
  if 'Sq' not in self.__class__.__name__:
224
- yield Gamma[:num]
211
+ yield gamma[:num]
225
212
  elif self.r_diag:
226
- yield Gamma[:num] ** 2 - (self.damp**2) * Gamma_autopow[:num]
213
+ yield gamma[:num] ** 2 - (self.damp**2) * gamma_autopow[:num]
227
214
  else:
228
- yield Gamma[:num] ** 2
215
+ yield gamma[:num] ** 2
229
216
 
230
217
  def _delay_and_sum(self, num, p_res, d_interp2, d_index, amp):
231
218
  """Standard delay-and-sum method."""
232
- result = empty((num, self.steer.grid.size), dtype=float) # output array
233
- 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
234
221
  _delayandsum4(p_res, d_index, d_interp2, amp, result, autopow)
235
222
  return result, autopow
236
223
 
237
224
 
238
225
  class BeamformerTimeSq(BeamformerTime):
239
- """Provides a time domain beamformer with time-dependend
240
- power signal output and possible autopower removal
241
- 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.
242
231
  """
243
232
 
244
233
  #: Boolean flag, if 'True' (default), the main diagonal is removed before beamforming.
@@ -254,30 +243,33 @@ class BeamformerTimeSq(BeamformerTime):
254
243
  return digest(self)
255
244
 
256
245
  def result(self, num=2048):
257
- """Python generator that yields the **squared** time-domain beamformer output.
246
+ """
247
+ Python generator that yields the **squared** time-domain beamformer output.
258
248
 
259
249
  The squared output time signal starts for source signals that were emitted from
260
250
  the :class:`~acoular.grids.Grid` at `t=0`.
261
251
 
262
252
  Parameters
263
253
  ----------
264
- num : int
254
+ num : :class:`int`
265
255
  This parameter defines the size of the blocks to be yielded
266
256
  (i.e. the number of samples per block). Defaults to 2048.
267
257
 
268
258
  Yields
269
259
  ------
270
- numpy.ndarray
271
- 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`).
272
262
  :attr:`~BeamformerTime.num_channels` is usually very \
273
263
  large (number of grid points).
274
- 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``.
275
265
  """
276
266
  return super().result(num)
277
267
 
278
268
 
279
269
  class BeamformerTimeTraj(BeamformerTime):
280
- """Provides a basic time domain beamformer with time signal output
270
+ """
271
+ Provides a basic time domain beamformer with time signal output.
272
+
281
273
  for a grid moving along a trajectory.
282
274
  """
283
275
 
@@ -286,7 +278,7 @@ class BeamformerTimeTraj(BeamformerTime):
286
278
  trajectory = Instance(Trajectory, desc='trajectory of the grid center')
287
279
 
288
280
  #: Reference vector, perpendicular to the y-axis of moving grid.
289
- 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')
290
282
 
291
283
  #: Considering of convective amplification in beamforming formula.
292
284
  conv_amp = Bool(False, desc='determines if convective amplification of source is considered')
@@ -315,10 +307,12 @@ class BeamformerTimeTraj(BeamformerTime):
315
307
  """Python generator that yields the moving grid coordinates samplewise."""
316
308
 
317
309
  def cross(a, b):
318
- """Cross product for fast computation
310
+ """
311
+ Cross product for fast computation.
312
+
319
313
  because numpy.cross is ultra slow in this case.
320
314
  """
321
- 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]])
322
316
 
323
317
  start_t = 0.0
324
318
  gpos = self.steer.grid.pos
@@ -328,74 +322,87 @@ class BeamformerTimeTraj(BeamformerTime):
328
322
  if rflag:
329
323
  for g in trajg:
330
324
  # grid is only translated, not rotated
331
- tpos = gpos + array(g)[:, newaxis]
325
+ tpos = gpos + np.array(g)[:, np.newaxis]
332
326
  yield tpos
333
327
  else:
334
328
  for g, g1 in zip(trajg, trajg1):
335
329
  # grid is both translated and rotated
336
- loc = array(g) # translation array([0., 0.4, 1.])
337
- 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)
338
332
  dy = cross(self.rvec, dx) # new y-axis
339
333
  dz = cross(dx, dy) # new z-axis
340
- RM = array((dx, dy, dz)).T # rotation matrix
341
- RM /= sqrt((RM * RM).sum(0)) # column normalized
342
- 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
343
337
  # print(loc[:])
344
338
  yield tpos
345
339
 
346
340
  def _get_macostheta(self, g1, tpos, rm):
347
- vvec = array(g1) # velocity vector
348
- ma = norm(vvec) / self.steer.env.c # machnumber
349
- fdv = (vvec / sqrt((vvec * vvec).sum()))[:, newaxis] # unit vecor velocity
350
- mpos = self.steer.mics.pos[:, newaxis, :]
351
- rmv = tpos[:, :, newaxis] - mpos
352
- 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)
353
347
 
354
348
  def get_r0(self, tpos):
355
- 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:
356
363
  return self.steer.ref # full((self.steer.grid.size,), self.steer.ref)
357
364
  return self.steer.env._r(tpos)
358
365
 
359
366
  def result(self, num=2048):
360
- """Python generator that yields the time-domain beamformer output.
367
+ """
368
+ Python generator that yields the time-domain beamformer output.
361
369
 
362
370
  The output time signal starts for source signals that were emitted from
363
371
  the :class:`~acoular.grids.Grid` at `t=0`.
364
372
 
365
373
  Parameters
366
374
  ----------
367
- num : int
375
+ num : :class:`int`
368
376
  This parameter defines the size of the blocks to be yielded
369
377
  (i.e. the number of samples per block). Defaults to 2048.
370
378
 
371
379
  Yields
372
380
  ------
373
- numpy.ndarray
374
- 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`).
375
383
  :attr:`~BeamformerTime.num_channels` is usually very \
376
384
  large (number of grid points).
377
- 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``.
378
386
  """
379
387
  # initialize values
380
388
  if self.precision == 64:
381
- fdtype = float64
382
- idtype = int64
389
+ fdtype = np.float64
390
+ idtype = np.int64
383
391
  else:
384
- fdtype = float32
385
- idtype = int32
386
- w = self._get_weights()
392
+ fdtype = np.float32
393
+ idtype = np.int32
387
394
  c = self.steer.env.c / self.source.sample_freq
388
395
  num_mics = self.steer.mics.num_mics
389
396
  mpos = self.steer.mics.pos.astype(fdtype)
390
- m_index = arange(num_mics, dtype=idtype)
391
- n_index = arange(num, dtype=idtype)[:, newaxis]
392
- blockrm = empty((num, self.steer.grid.size, num_mics), dtype=fdtype)
393
- blockrmconv = empty((num, self.steer.grid.size, num_mics), dtype=fdtype)
394
- amp = empty((num, self.steer.grid.size, num_mics), dtype=fdtype)
395
- # delays = empty((num,self.steer.grid.size,num_mics),dtype=fdtype)
396
- d_index = empty((num, self.steer.grid.size, num_mics), dtype=idtype)
397
- d_interp2 = empty((num, self.steer.grid.size, num_mics), dtype=fdtype)
398
- 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)
399
406
  movgpos = self._get_moving_gpos() # create moving grid pos generator
400
407
  movgspeed = self.trajectory.traj(0.0, delta_t=1 / self.source.sample_freq, der=1)
401
408
  weights = self._get_weights()
@@ -430,35 +437,35 @@ class BeamformerTimeTraj(BeamformerTime):
430
437
  except StopIteration:
431
438
  break
432
439
  if time_block.shape[0] < buffer.result_num: # last block shorter
433
- num = sum((d_index.max((1, 2)) + 1 + arange(0, num)) < time_block.shape[0])
434
- 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]
435
442
  flag = False
436
443
  # init step
437
444
  p_res = time_block.copy()
438
- 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)
439
446
  if 'Cleant' not in self.__class__.__name__:
440
447
  if 'Sq' not in self.__class__.__name__:
441
- yield Phi[:num]
448
+ yield phi[:num]
442
449
  elif self.r_diag:
443
- yield (Phi[:num] ** 2 - autopow[:num]).clip(min=0)
450
+ yield (phi[:num] ** 2 - autopow[:num]).clip(min=0)
444
451
  else:
445
- yield Phi[:num] ** 2
452
+ yield phi[:num] ** 2
446
453
  else:
447
454
  # choose correct distance
448
455
  blockrm1 = blockrmconv if self.conv_amp else blockrm
449
- Gamma = zeros(Phi.shape, dtype=fdtype)
450
- Gamma_autopow = zeros(Phi.shape, dtype=fdtype)
451
- J = 0
452
- 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)
453
460
  # deconvolution
454
- while self.n_iter > J:
455
- # 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}")
456
463
  if self.r_diag:
457
- powPhi = (Phi[:num] * Phi[:num] - autopow).sum(0).clip(min=0)
464
+ pow_phi = (phi[:num] * phi[:num] - autopow).sum(0).clip(min=0)
458
465
  else:
459
- powPhi = (Phi[:num] * Phi[:num]).sum(0)
466
+ pow_phi = (phi[:num] * phi[:num]).sum(0)
460
467
  # find index of max power focus point
461
- imax = argmax(powPhi)
468
+ imax = np.argmax(pow_phi)
462
469
  # find backward delays
463
470
  t_float = (d_interp2[:num, imax, m_index] + d_index[:num, imax, m_index] + n_index).astype(fdtype)
464
471
  # determine max/min delays in sample units
@@ -466,49 +473,51 @@ class BeamformerTimeTraj(BeamformerTime):
466
473
  ind_max = t_float.max(0).astype(idtype) + 2
467
474
  ind_min = t_float.min(0).astype(idtype)
468
475
  # store time history at max power focus point
469
- h = Phi[:num, imax] * blockr0[:num, imax]
476
+ h = phi[:num, imax] * blockr0[:num, imax]
470
477
  for m in range(num_mics):
471
478
  # subtract interpolated time history from microphone signals
472
- 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(
473
480
  t_ind[ind_min[m] : ind_max[m]],
474
481
  t_float[:num, m],
475
482
  h / blockrm1[:num, imax, m],
476
483
  )
477
- 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)
478
485
  if self.r_diag:
479
- 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)
480
487
  else:
481
- pownextPhi = (nextPhi[:num] * nextPhi[:num]).sum(0)
482
- # print(f"total signal power: {powPhi.sum()}")
483
- if pownextPhi.sum() < powPhi.sum(): # stopping criterion
484
- Gamma[:num, imax] += self.damp * Phi[:num, imax]
485
- Gamma_autopow[:num, imax] = autopow[:num, imax].copy()
486
- Phi = nextPhi
487
- autopow = nextAutopow
488
- # print(f"clean max: {L_p((Gamma**2).sum(0)/num).max()} dB")
489
- 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
490
497
  else:
491
498
  break
492
499
  if 'Sq' not in self.__class__.__name__:
493
- yield Gamma[:num]
500
+ yield gamma[:num]
494
501
  elif self.r_diag:
495
- yield Gamma[:num] ** 2 - (self.damp**2) * Gamma_autopow[:num]
502
+ yield gamma[:num] ** 2 - (self.damp**2) * gamma_autopow[:num]
496
503
  else:
497
- yield Gamma[:num] ** 2
504
+ yield gamma[:num] ** 2
498
505
 
499
506
  def _delay_and_sum(self, num, p_res, d_interp2, d_index, amp):
500
507
  """Standard delay-and-sum method."""
501
- fdtype = float64 if self.precision == 64 else float32
502
- result = empty((num, self.steer.grid.size), dtype=fdtype) # output array
503
- 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
504
511
  _delayandsum5(p_res, d_index, d_interp2, amp, result, autopow)
505
512
  return result, autopow
506
513
 
507
514
 
508
515
  class BeamformerTimeSqTraj(BeamformerTimeSq, BeamformerTimeTraj):
509
- """Provides a time domain beamformer with time-dependent
510
- power signal output and possible autopower removal
511
- 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.
512
521
  """
513
522
 
514
523
  # internal identifier
@@ -530,30 +539,32 @@ class BeamformerTimeSqTraj(BeamformerTimeSq, BeamformerTimeTraj):
530
539
  return digest(self)
531
540
 
532
541
  def result(self, num=2048):
533
- """Python generator that yields the **squared** time-domain beamformer output.
542
+ """
543
+ Python generator that yields the **squared** time-domain beamformer output.
534
544
 
535
545
  The squared output time signal starts for source signals that were emitted from
536
546
  the :class:`~acoular.grids.Grid` at `t=0`.
537
547
 
538
548
  Parameters
539
549
  ----------
540
- num : int
550
+ num : :class:`int`
541
551
  This parameter defines the size of the blocks to be yielded
542
552
  (i.e. the number of samples per block). Defaults to 2048.
543
553
 
544
554
  Yields
545
555
  ------
546
- numpy.ndarray
547
- 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`).
548
558
  :attr:`~BeamformerTime.num_channels` is usually very \
549
559
  large (number of grid points).
550
- 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``.
551
561
  """
552
562
  return super().result(num)
553
563
 
554
564
 
555
565
  class BeamformerCleant(BeamformerTime):
556
- """CLEANT deconvolution method.
566
+ """
567
+ CLEANT deconvolution method.
557
568
 
558
569
  An implementation of the CLEAN method in time domain. This class can only
559
570
  be used for static sources. See :cite:`Cousson2019` for details.
@@ -579,30 +590,32 @@ class BeamformerCleant(BeamformerTime):
579
590
  return digest(self)
580
591
 
581
592
  def result(self, num=2048):
582
- """Python generator that yields the deconvolved time-domain beamformer output.
593
+ """
594
+ Python generator that yields the deconvolved time-domain beamformer output.
583
595
 
584
596
  The output starts for signals that were emitted from the :class:`~acoular.grids.Grid` at
585
597
  `t=0`.
586
598
 
587
599
  Parameters
588
600
  ----------
589
- num : int
601
+ num : :class:`int`
590
602
  This parameter defines the size of the blocks to be yielded
591
603
  (i.e. the number of samples per block). Defaults to 2048.
592
604
 
593
605
  Yields
594
606
  ------
595
- numpy.ndarray
596
- 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`).
597
609
  :attr:`~BeamformerTime.num_channels` is usually very \
598
610
  large (number of grid points).
599
- 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``.
600
612
  """
601
613
  return super().result(num)
602
614
 
603
615
 
604
616
  class BeamformerCleantSq(BeamformerCleant):
605
- """CLEANT deconvolution method with optional removal of autocorrelation.
617
+ """
618
+ CLEANT deconvolution method with optional removal of autocorrelation.
606
619
 
607
620
  An implementation of the CLEAN method in time domain. This class can only
608
621
  be used for static sources. See :cite:`Cousson2019` for details on the method
@@ -622,7 +635,8 @@ class BeamformerCleantSq(BeamformerCleant):
622
635
  return digest(self)
623
636
 
624
637
  def result(self, num=2048):
625
- """Python generator that yields the *squared* deconvolved time-domain beamformer output.
638
+ """
639
+ Python generator that yields the *squared* deconvolved time-domain beamformer output.
626
640
 
627
641
  The output starts for signals that were emitted from the :class:`~acoular.grids.Grid` at
628
642
  `t=0`. Per default, block-wise removal of autocorrelation is performed, which can be turned
@@ -630,23 +644,24 @@ class BeamformerCleantSq(BeamformerCleant):
630
644
 
631
645
  Parameters
632
646
  ----------
633
- num : int
647
+ num : :class:`int`
634
648
  This parameter defines the size of the blocks to be yielded
635
649
  (i.e. the number of samples per block). Defaults to 2048.
636
650
 
637
651
  Yields
638
652
  ------
639
- numpy.ndarray
640
- 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`).
641
655
  :attr:`~BeamformerTime.num_channels` is usually very \
642
656
  large (number of grid points).
643
- 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``.
644
658
  """
645
659
  return super().result(num)
646
660
 
647
661
 
648
662
  class BeamformerCleantTraj(BeamformerCleant, BeamformerTimeTraj):
649
- """CLEANT deconvolution method.
663
+ """
664
+ CLEANT deconvolution method.
650
665
 
651
666
  An implementation of the CLEAN method in time domain for moving sources
652
667
  with known trajectory. See :cite:`Cousson2019` for details.
@@ -675,30 +690,32 @@ class BeamformerCleantTraj(BeamformerCleant, BeamformerTimeTraj):
675
690
  return digest(self)
676
691
 
677
692
  def result(self, num=2048):
678
- """Python generator that yields the deconvolved time-domain beamformer output.
693
+ """
694
+ Python generator that yields the deconvolved time-domain beamformer output.
679
695
 
680
696
  The output starts for signals that were emitted from the :class:`~acoular.grids.Grid` at
681
697
  `t=0`.
682
698
 
683
699
  Parameters
684
700
  ----------
685
- num : int
701
+ num : :class:`int`
686
702
  This parameter defines the size of the blocks to be yielded
687
703
  (i.e. the number of samples per block). Defaults to 2048.
688
704
 
689
705
  Yields
690
706
  ------
691
- numpy.ndarray
692
- 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`).
693
709
  :attr:`~BeamformerTime.num_channels` is usually very \
694
710
  large (number of grid points).
695
- 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``.
696
712
  """
697
713
  return super().result(num)
698
714
 
699
715
 
700
716
  class BeamformerCleantSqTraj(BeamformerCleantTraj, BeamformerTimeSq):
701
- """CLEANT deconvolution method with optional removal of autocorrelation.
717
+ """
718
+ CLEANT deconvolution method with optional removal of autocorrelation.
702
719
 
703
720
  An implementation of the CLEAN method in time domain for moving sources
704
721
  with known trajectory. See :cite:`Cousson2019` for details on the method and
@@ -729,7 +746,8 @@ class BeamformerCleantSqTraj(BeamformerCleantTraj, BeamformerTimeSq):
729
746
  return digest(self)
730
747
 
731
748
  def result(self, num=2048):
732
- """Python generator that yields the *squared* deconvolved time-domain beamformer output.
749
+ """
750
+ Python generator that yields the *squared* deconvolved time-domain beamformer output.
733
751
 
734
752
  The output starts for signals that were emitted from the :class:`~acoular.grids.Grid` at
735
753
  `t=0`. Per default, block-wise removal of autocorrelation is performed, which can be turned
@@ -737,17 +755,17 @@ class BeamformerCleantSqTraj(BeamformerCleantTraj, BeamformerTimeSq):
737
755
 
738
756
  Parameters
739
757
  ----------
740
- num : int
758
+ num : :class:`int`
741
759
  This parameter defines the size of the blocks to be yielded
742
760
  (i.e. the number of samples per block). Defaults to 2048.
743
761
 
744
762
  Yields
745
763
  ------
746
- numpy.ndarray
747
- 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`).
748
766
  :attr:`~BeamformerTime.num_channels` is usually very \
749
767
  large (number of grid points).
750
- 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``.
751
769
  """
752
770
  return super().result(num)
753
771
 
@@ -784,32 +802,32 @@ class IntegratorSectorTime(TimeOut):
784
802
  return len(self.sectors)
785
803
 
786
804
  def result(self, num=1):
787
- """Python generator that yields the source output integrated over the given
788
- sectors, block-wise.
805
+ """
806
+ Python generator that yields the source output integrated over specified grid sectors.
789
807
 
790
808
  Parameters
791
809
  ----------
792
- num : integer, defaults to 1
793
- This parameter defines the size of the blocks to be yielded
794
- (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``.
795
812
 
796
813
  Returns
797
814
  -------
798
- Samples in blocks of shape (num, :attr:`num_channels`).
815
+ :class:`numpy.ndarray`
816
+ Samples in blocks of shape (``num``, :attr:`num_channels`).
799
817
  :attr:`num_channels` is the number of sectors.
800
818
  The last block may be shorter than num.
801
819
  """
802
820
  inds = [self.grid.indices(*sector) for sector in self.sectors]
803
821
  gshape = self.grid.shape
804
- o = empty((num, self.num_channels), dtype=float) # output array
822
+ o = np.empty((num, self.num_channels), dtype=float) # output array
805
823
  for r in self.source.result(num):
806
824
  ns = r.shape[0]
807
825
  mapshape = (ns,) + gshape
808
826
  rmax = r.max()
809
827
  rmin = rmax * 10 ** (self.clip / 10.0)
810
- r = where(r > rmin, r, 0.0)
828
+ r = np.where(r > rmin, r, 0.0)
811
829
  for i, ind in enumerate(inds):
812
- h = r[:].reshape(mapshape)[(s_[:],) + ind]
830
+ h = r[:].reshape(mapshape)[(np.s_[:],) + ind]
813
831
  o[:ns, i] = h.reshape(h.shape[0], -1).sum(axis=1)
814
832
  i += 1
815
833
  yield o[:ns]