Coverage for kwave/ktransducer.py: 21%
268 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-10-24 11:55 -0700
« prev ^ index » next coverage.py v6.5.0, created at 2022-10-24 11:55 -0700
1from kwave.kgrid import kWaveGrid
2from kwave.ksensor import kSensor
3from kwave.utils import *
6# force value to be a positive integer
7def make_pos_int(val):
8 return np.abs(val).astype(int)
11class kWaveTransducerSimple(object):
13 def __init__(
14 self,
15 kgrid: kWaveGrid,
16 number_elements=128,
17 element_width=1,
18 element_length=20,
19 element_spacing=0,
20 position=None,
21 radius=float('inf')
22 ):
23 """
25 Args:
26 kgrid: kWaveGrid object
27 number_elements: the total number of transducer elements
28 element_width: the width of each element in grid points
29 element_length: the length of each element in grid points
30 element_spacing: the spacing (kerf width) between the transducer elements in grid points
31 position: the position of the corner of the transducer in the grid
32 radius: the radius of curvature of the transducer [m]
33 """
34 # allocate the grid size and spacing
35 self.stored_grid_size = [kgrid.Nx, kgrid.Ny, kgrid.Nz] # size of the grid in which the transducer is defined
36 self.grid_spacing = [kgrid.dx, kgrid.dy, kgrid.dz] # corresponding grid spacing
38 self.number_elements = make_pos_int(number_elements)
39 self.element_width = make_pos_int(element_width)
40 self.element_length = make_pos_int(element_length)
41 self.element_spacing = make_pos_int(element_spacing)
43 if position is None:
44 position = [1, 1, 1]
45 self.position = make_pos_int(position)
47 assert np.isinf(radius), 'Only a value of transducer.radius = inf is currently supported'
48 self.radius = radius
50 # check the transducer fits into the grid
51 if np.sum(self.position == 0):
52 raise ValueError('The defined transducer must be positioned within the grid')
53 elif (self.position[1] + self.number_elements * self.element_width + (
54 self.number_elements - 1) * self.element_spacing) > self.stored_grid_size[1]:
55 raise ValueError('The defined transducer is too large or positioned outside the grid in the y-direction')
56 elif (self.position[2] + self.element_length) > self.stored_grid_size[2]:
57 print(self.position[2])
58 print(self.element_length)
59 print(self.stored_grid_size[2])
60 raise ValueError('The defined transducer is too large or positioned outside the grid in the z-direction')
61 elif self.position[0] > self.stored_grid_size[0]:
62 raise ValueError('The defined transducer is positioned outside the grid in the x-direction')
64 @property
65 def element_pitch(self):
66 return (self.element_spacing + self.element_width) * self.grid_spacing[1]
68 @property
69 def transducer_width(self):
70 """
71 total width of the transducer in grid points
72 Returns:
73 the overall length of the transducer
74 """
75 return self.number_elements * self.element_width + (self.number_elements - 1) * self.element_spacing
78class NotATransducer(kSensor):
80 def __init__(
81 self,
82 transducer: kWaveTransducerSimple,
83 kgrid: kWaveGrid,
84 active_elements=None,
85 focus_distance=float('inf'),
86 elevation_focus_distance=float('inf'),
87 receive_apodization='Rectangular',
88 transmit_apodization='Rectangular',
89 sound_speed=1540,
90 input_signal=None,
91 steering_angle_max=None,
92 steering_angle=None
93 ):
94 """
96 'time_reversal_boundary_data' and 'record' fields should not be defined
97 for the objects of this class
98 Args:
99 kgrid: kWaveGrid object
100 active_elements: the transducer elements that are currently active elements
101 elevation_focus_distance: the focus depth in the elevation direction [m]
102 receive_apodization: transmit apodization
103 transmit_apodization: receive apodization
104 sound_speed: sound speed used to calculate beamforming delays [m/s]
105 focus_distance: focus distance used to calculate beamforming delays [m]
106 input_signal:
107 steering_angle_max:
108 steering_angle:
109 """
110 super().__init__()
111 assert isinstance(transducer, kWaveTransducerSimple)
112 self.transducer = transducer
113 # time index to start recording if transducer is used as a sensor
114 self.record_start_index = 1
115 # stored value of appended_zeros (accessed using get and set methods).
116 # This is used to set the number of zeros that are appended and prepended to the input signal.
117 self.stored_appended_zeros = 'auto'
118 # stored value of the minimum beamforming delay.
119 # This is used to offset the delay mask so that all the delays are >= 0
120 self.stored_beamforming_delays_offset = 'auto'
121 # stored value of the steering_angle_max (accessed using get and set methods).
122 # This can be set by the user and is used to derive the two parameters above.
123 self.stored_steering_angle_max = 'auto'
124 # stored value of the steering_angle (accessed using get and set methods)
125 self.stored_steering_angle = 0
127 ####################################
128 # if the sensor is a transducer, check that the simulation is in 3D
129 assert kgrid.dim == 3, 'Transducer inputs are only compatible with 3D simulations.'
131 ####################################
133 # allocate the temporal spacing
134 if is_number(kgrid.dt):
135 self.dt = kgrid.dt
136 elif kgrid.t_array is not None:
137 self.dt = kgrid.t_array[1] - kgrid.t_array[0]
138 else:
139 raise ValueError('kgrid.dt or kgrid.t_array must be explicitly defined')
141 if active_elements is None:
142 active_elements = np.ones((transducer.number_elements, 1))
143 self.active_elements = active_elements
145 self.elevation_focus_distance = elevation_focus_distance
147 # check the length of the input
148 assert not is_number(receive_apodization) or len(receive_apodization) == self.number_active_elements, \
149 'The length of the receive apodization input must match the number of active elements'
150 self.receive_apodization = receive_apodization
152 # check the length of the input
153 assert not is_number(transmit_apodization) or len(transmit_apodization) == self.number_active_elements, \
154 'The length of the transmit apodization input must match the number of active elements'
155 self.transmit_apodization = transmit_apodization
157 # check to see the sound_speed is positive
158 assert sound_speed > 0, 'transducer.sound_speed must be greater than 0'
159 self.sound_speed = sound_speed
161 self.focus_distance = focus_distance
163 if input_signal is not None:
164 input_signal = np.squeeze(input_signal)
165 assert input_signal.ndim == 1, 'transducer.input_signal must be a one-dimensional array'
166 self.stored_input_signal = np.atleast_2d(input_signal).T # force the input signal to be a column vector
168 if steering_angle_max is not None:
169 # set the maximum steering angle using the set method (this avoids having to duplicate error checking)
170 self.steering_angle_max = steering_angle_max
172 if steering_angle is not None:
173 # set the maximum steering angle using the set method (this
174 # avoids having to duplicate error checking)
175 self.steering_angle = steering_angle
177 # assign the data type for the transducer matrix based on the
178 # number of different elements (uint8 supports 255 numbers so
179 # most of the time this data type will be used)
180 mask_type = get_smallest_possible_type(transducer.number_elements, 'uint')
182 # create an empty transducer mask (the grid points within
183 # element 'n' are all given the value 'n')
184 assert transducer.stored_grid_size is not None
185 self.indexed_mask = np.zeros(transducer.stored_grid_size, dtype=mask_type)
187 # create a second empty mask used for the elevation beamforming
188 # delays (the grid points across each element are numbered 1 to
189 # M, where M is the number of grid points in the elevation
190 # direction)
191 self.indexed_element_voxel_mask = np.zeros(transducer.stored_grid_size, dtype=mask_type)
193 # create the corresponding indexing variable 1:M
194 element_voxel_index = np.tile(np.arange(transducer.element_length) + 1, (transducer.element_width, 1))
196 # for each transducer element, calculate the grid point indices
197 for element_index in range(0, transducer.number_elements):
198 # assign the current transducer position
199 element_pos_x = transducer.position[0]
200 element_pos_y = transducer.position[1] + (transducer.element_width + transducer.element_spacing) * element_index
201 element_pos_z = transducer.position[2]
203 element_pos_x = element_pos_x - 1
204 element_pos_y = element_pos_y - 1
205 element_pos_z = element_pos_z - 1
207 # assign the grid points within the current element the
208 # index of the element
209 self.indexed_mask[element_pos_x, element_pos_y:element_pos_y + transducer.element_width,
210 element_pos_z:element_pos_z + transducer.element_length] = element_index + 1
212 # assign the individual grid points an index corresponding
213 # to their order across the element
214 self.indexed_element_voxel_mask[element_pos_x, element_pos_y:element_pos_y + transducer.element_width,
215 element_pos_z:element_pos_z + transducer.element_length] = element_voxel_index
217 # double check the transducer fits within the desired grid size
218 assert (np.array(self.indexed_mask.shape) == transducer.stored_grid_size).all(), \
219 'Desired transducer is larger than the input grid_size'
221 self.sxx = self.syy = self.szz = self.sxy = self.sxz = self.syz = None
222 self.u_mode = self.p_mode = None
223 self.ux = self.uy = self.uz = None
225 @staticmethod
226 def isfield(_):
227 # return field_name in dir(self)
228 # return eng.isfield(self.transducer, field_name)
229 return False # this method call was returning false always because Matlab 'isfield' calls are false for classes
231 def __contains__(self, item):
232 return self.isfield(item)
234 @property
235 def beamforming_delays(self):
236 """
237 calculate the beamforming delays based on the focus and steering settings
238 Returns:
240 """
241 # calculate the element pitch in [m]
242 element_pitch = self.transducer.element_pitch
244 # create indexing variable
245 element_index = np.arange(-(self.number_active_elements - 1) / 2, (self.number_active_elements + 1) / 2)
247 # check for focus depth
248 if np.isinf(self.focus_distance):
250 # calculate time delays for a steered beam
251 delay_times = element_pitch * element_index * \
252 np.sin(self.steering_angle * np.pi / 180) / self.sound_speed # [s]
254 else:
256 # calculate time delays for a steered and focussed beam
257 delay_times = self.focus_distance / self.sound_speed * (
258 1 - np.sqrt(1 + (element_index * element_pitch / self.focus_distance) ** 2
259 - 2 * (element_index * element_pitch / self.focus_distance) * np.sin(
260 self.steering_angle * np.pi / 180))) # [s]
262 # convert the delays to be in units of time points
263 delay_times = (delay_times / self.dt).round().astype(int)
264 return delay_times
266 @property
267 def beamforming_delays_offset(self):
268 """
269 offset used to make all the delays in the delay_mask positive (either set to 'auto' or based on the setting for steering_angle_max)
270 Returns:
271 the stored value of the offset used to force the values in delay_mask to be >= 0
272 """
273 return self.stored_beamforming_delays_offset
275 @property
276 def mask(self):
277 """
278 allow mask query to allow compatability with regular sensor structure - return the active sensor mask
279 Returns:
281 """
282 return self.active_elements_mask
284 @property
285 def indexed_active_elements_mask(self):
286 # copy the indexed elements mask
287 mask = self.indexed_mask
288 if mask is None:
289 return None
291 mask = np.copy(mask)
293 # remove inactive elements from the mask
294 for element_index in range(self.transducer.number_elements):
295 if not self.active_elements[element_index]:
296 mask[mask == (element_index + 1)] = 0 # +1 compatibility
298 # force the lowest element index to be 1
299 lowest_active_element_index = matlab_find(self.active_elements)[0][0]
300 mask[mask != 0] = mask[mask != 0] - lowest_active_element_index + 1
301 return mask
303 @property
304 def indexed_elements_mask(self): # nr
305 return self.indexed_mask
307 @property
308 def steering_angle(self): # nr
309 return self.stored_steering_angle
311 # set the stored value of the steering angle
312 @steering_angle.setter
313 def steering_angle(self, steering_angle):
314 # force to be scalar
315 steering_angle = float(steering_angle)
317 # check if the steering angle is between -90 and 90
318 assert -90 < steering_angle < 90, 'Input for steering_angle must be betweeb -90 and 90 degrees.'
320 # check if the steering angle is less than the maximum steering angle
321 if self.stored_steering_angle_max != 'auto' and (abs(steering_angle) > self.stored_steering_angle_max):
322 raise ValueError('Input for steering_angle cannot be greater than steering_angle_max.')
324 # update the stored value
325 self.stored_steering_angle = steering_angle
327 @property
328 def steering_angle_max(self):
329 return self.stored_steering_angle_max
331 @steering_angle_max.setter
332 def steering_angle_max(self, steering_angle_max):
333 # force input to be scalar and positive
334 steering_angle_max = float(steering_angle_max)
336 # check the steering angle is within range
337 assert -90 < steering_angle_max < 90, 'Input for steering_angle_max must be between -90 and 90.'
339 # check the maximum steering angle is greater than the current steering angle
340 assert self.stored_steering_angle_max == 'auto' or abs(self.stored_steering_angle) <= steering_angle_max, \
341 'Input for steering_angle_max cannot be less than the current steering_angle.'
343 # overwrite the stored value
344 self.stored_steering_angle_max = steering_angle_max
346 # store a copy of the current value for the steering angle
347 current_steering_angle = self.stored_steering_angle
349 # overwrite with the user defined maximum value
350 self.stored_steering_angle = steering_angle_max
352 # set the beamforming delay offset to zero (this means the delays will contain negative
353 # values which we can use to derive the new values for the offset)
354 self.stored_appended_zeros = 0
355 self.stored_beamforming_delays_offset = 0
357 # get the element beamforming delays and reverse
358 delay_times = -self.beamforming_delays
360 # get the maximum and minimum beamforming delays
361 min_delay, max_delay = delay_times.min(), delay_times.max()
363 # add the maximum and minimum elevation delay if the elevation focus is not set to infinite
364 if not np.isinf(self.elevation_focus_distance):
365 max_delay = max_delay + max(self.elevation_beamforming_delays)
366 min_delay = min_delay + min(self.elevation_beamforming_delays)
368 # set the beamforming offset to the difference between the
369 # maximum and minimum delay
370 self.stored_appended_zeros = max_delay - min_delay
372 # set the minimum beamforming delay (converting to a positive number)
373 self.stored_beamforming_delays_offset = - min_delay
375 # reset the previous value of the steering angle
376 self.stored_steering_angle = current_steering_angle
378 @property
379 def elevation_beamforming_mask(self): # nr
380 # get elevation beamforming mask
381 delay_mask = self.delay_mask(2)
383 # extract the active elements
384 delay_mask = delay_mask[self.active_elements_mask != 0]
386 # force delays to start from zero
387 delay_mask = delay_mask - delay_mask.min()
389 # create an empty output mask
390 mask = np.zeros((delay_mask.size, delay_mask.max() + 1))
392 # populate the mask by setting 1's at the index given by the delay time
393 for index in range(delay_mask.size):
394 mask[index, delay_mask[index]] = 1
396 # flip the mask so the shortest delays are at the right
397 return np.fliplr(mask)
399 @property
400 def input_signal(self):
401 signal = self.stored_input_signal
403 # check the signal is not empty
404 assert signal is not None, 'Transducer input signal is not defined'
406 # automatically prepend and append zeros if the beamforming
407 # delay offset is set
409 # check if the beamforming delay offset is set. If so, use this
410 # number to prepend and append this number of zeros to the
411 # input signal. Otherwise, calculate how many zeros are needed
412 # and prepend and append these.
413 stored_appended_zeros = self.stored_appended_zeros
414 if stored_appended_zeros != 'auto':
416 # use the current value of the beamforming offset to add
417 # zeros to the input signal
418 signal = np.vstack([np.zeros((stored_appended_zeros, 1)), signal, np.zeros((stored_appended_zeros, 1))])
420 else:
421 # get the current delay beam forming
422 delay_mask = self.delay_mask()
424 # find the maximum delay
425 delay_max = delay_mask.max()
427 # count the number of leading zeros in the input signal
428 leading_zeros = matlab_find(signal)[0, 0] - 1
430 # count the number of trailing zeros in the input signal
431 trailing_zeros = matlab_find(np.flipud(signal))[0, 0] - 1
433 # check the number of leading zeros is sufficient given the
434 # maximum delay
435 if leading_zeros < delay_max + 1:
437 print(f' prepending transducer.input_signal with {delay_max - leading_zeros + 1} leading zeros')
439 # prepend extra leading zeros
440 signal = np.vstack([np.zeros((delay_max - leading_zeros + 1, 1)), signal])
442 # check the number of leading zeros is sufficient given the
443 # maximum delay
444 if trailing_zeros < delay_max + 1:
446 print(f' appending transducer.input_signal with {delay_max - trailing_zeros + 1} trailing zeros')
448 # append extra trailing zeros
449 signal = np.vstack([signal, np.zeros((delay_max - trailing_zeros + 1, 1))])
451 return signal
453 @property
454 def number_active_elements(self):
455 return self.active_elements.sum()
457 @property
458 def appended_zeros(self):
459 """
460 number of zeros appended to input signal to allow a single time series to be used
461 within kspaceFirstOrder3D (either set to 'auto' or based on the setting for steering_angle_max)
462 Returns:
464 """
465 return self.stored_appended_zeros
467 @property
468 def grid_size(self):
469 """
470 return the size of the grid
471 Returns: grid size
473 """
474 return self.transducer.stored_grid_size
476 @property
477 def active_elements_mask(self):
478 """
479 return a binary mask showing the locations of the active elements
480 Returns:
482 """
483 indexed_mask = np.copy(self.indexed_mask)
484 active_elements = self.active_elements.squeeze()
485 number_elements = int(self.transducer.number_elements)
487 # copy the indexed elements mask
488 mask = indexed_mask
490 # remove inactive elements from the mask
491 for element_index in range(1, number_elements + 1):
492 mask[mask == element_index] = active_elements[element_index - 1]
494 # convert remaining mask to binary
495 mask[mask != 0] = 1
497 return mask
499 @property
500 def all_elements_mask(self):
501 """
502 binary mask of all the transducer elements (both active and inactive)
503 Returns:
504 a binary mask showing the locations of all the elements (both active and inactive)
505 """
506 mask = np.copy(self.indexed_mask)
507 mask[mask != 0] = 1
508 return mask
510 def expand_grid(self, expand_size):
511 self.indexed_mask = expand_matrix(self.indexed_mask, expand_size, 0)
513 def retract_grid(self, retract_size):
514 indexed_mask = self.indexed_mask
515 retract_size = np.array(retract_size[0]).astype(np.int)
517 self.indexed_mask = indexed_mask[retract_size[0]:-retract_size[0], retract_size[1]:-retract_size[1], retract_size[2]:-retract_size[2]]
519 @property
520 def transmit_apodization_mask(self):
521 """
522 % convert the transmit apodization into the form of a element mask,
523 % where the apodization values are placed at the grid points
524 % belonging to the active transducer elements. These values are
525 % then extracted in the correct order within
526 % kspaceFirstOrder_inputChecking using apodization =
527 % transmit_apodization_mask(active_elements_mask ~= 0)
528 Returns:
530 """
531 # get transmit apodization
532 apodization = self.get_transmit_apodization()
534 # create an empty mask;
535 mask = np.zeros(self.transducer.stored_grid_size)
537 # assign the apodization values to every grid point in the transducer
538 mask_index = self.indexed_active_elements_mask
539 mask_index = mask_index[mask_index != 0]
540 mask[self.active_elements_mask == 1] = apodization[mask_index - 1, 0] # -1 for conversion
541 return mask
543 def get_transmit_apodization(self):
544 """
545 return the transmit apodization, converting strings of window
546 type to actual numbers using getWin
547 Returns:
549 """
551 # check if a user defined apodization is given and whether this
552 # is still the correct size (in case the number of active
553 # elements has changed)
554 if is_number(self.transmit_apodization):
555 assert self.transmit_apodization.size == self.number_active_elements, \
556 'The length of the transmit apodization input must match the number of active elements'
558 # assign apodization
559 apodization = self.transmit_apodization
560 else:
562 # if the number of active elements is greater than 1,
563 # create apodization using getWin, otherwise, assign 1
564 if self.number_active_elements > 1:
565 apodization, _ = get_win(int(self.number_active_elements), type_=self.transmit_apodization)
566 else:
567 apodization = 1
568 apodization = np.array(apodization)
569 return apodization
571 def delay_mask(self, mode=None):
572 """
573 % mode == 1: both delays
574 % mode == 2: elevation only
575 % mode == 3: azimuth only
576 Returns:
578 """
579 # assign the delays to a new mask using the indexed_element_mask
580 indexed_active_elements_mask_copy = self.indexed_active_elements_mask
581 mask = np.zeros(self.transducer.stored_grid_size, dtype=np.float32)
583 if indexed_active_elements_mask_copy is None:
584 return mask
586 active_elements_index = matlab_find(indexed_active_elements_mask_copy)
588 # calculate azimuth focus delay times provided they are not all zero
589 if (not np.isinf(self.focus_distance) or (self.steering_angle != 0)) and (mode is None or mode != 2):
591 # get the element beamforming delays and reverse
592 delay_times = -self.beamforming_delays
594 # add delay times
595 # mask[active_elements_index] = delay_times[indexed_active_elements_mask_copy[active_elements_index]]
596 mask[unflatten_matlab_mask(mask, active_elements_index, diff=-1)] = matlab_mask(delay_times, matlab_mask(indexed_active_elements_mask_copy, active_elements_index, diff=-1), diff=-1).squeeze()
598 # calculate elevation focus time delays provided each element is longer than one grid point
599 if not np.isinf(self.elevation_focus_distance) and (self.transducer.element_length > 1) and (mode is None or mode != 3):
601 # get elevation beamforming delays
602 elevation_delay_times = self.elevation_beamforming_delays
604 # get current mask
605 element_voxel_mask = self.indexed_element_voxel_mask
607 # add delay times
608 mask[unflatten_matlab_mask(mask, active_elements_index - 1)] += matlab_mask(elevation_delay_times, matlab_mask(element_voxel_mask, active_elements_index - 1) - 1)[:, 0] # -1s compatibility
610 # shift delay times (these should all be >= 0, where a value of 0 means no delay)
611 if self.stored_appended_zeros == 'auto':
612 mask[unflatten_matlab_mask(mask, active_elements_index - 1)] -= mask[unflatten_matlab_mask(mask, active_elements_index - 1)].min() # -1s compatibility
613 else:
614 mask[unflatten_matlab_mask(mask, active_elements_index - 1)] += self.stored_beamforming_delays_offset # -1s compatibility
615 return mask.astype(np.uint8)
617 @property
618 def elevation_beamforming_delays(self):
619 """
620 calculate the elevation beamforming delays based on the focus setting
621 Returns:
622 """
623 if not np.isinf(self.elevation_focus_distance):
624 # create indexing variable
626 element_index = np.arange(-(self.transducer.element_length - 1) / 2, (self.transducer.element_length + 1) / 2)
628 # calculate time delays for a focussed beam
629 delay_times = (self.elevation_focus_distance - np.sqrt((element_index * self.transducer.grid_spacing[2]) ** 2 + self.elevation_focus_distance ** 2)) / self.sound_speed
631 # convert the delays to be in units of time points and then reverse
632 delay_times = -np.round(delay_times / self.dt).astype(np.int32)
634 else:
635 # create an empty array
636 delay_times = np.zeros((1, self.transducer.element_length))
637 return delay_times
639 # # function to create a scan line based on the input sensor data and the current apodization and beamforming setting
640 # def scan_line(obj, sensor_data):
641 #
642 # # get the current apodization setting
643 # apodization = obj.get_receive_apodization
644 #
645 # # get the current beamforming weights and reverse
646 # delays = -obj.beamforming_delays
647 #
648 # # offset the received sensor_data by the beamforming delays and apply receive apodization
649 # for element_index in np.arange(0, obj.number_active_elements):
650 # if delays(element_index) > 0:
651 # # shift element data forwards
652 # sensor_data[element_index, :] = apodization[element_index] *[sensor_data(element_index, 1 + delays(element_index):end), zeros(1, delays(element_index))];
653 #
654 # elif delays(element_index) < 0
655 # # shift element data backwards
656 # sensor_data[element_index, :] = apodization[element_index] *[zeros(1, -delays(element_index)), sensor_data(element_index, 1:end + delays(element_index))];
657 #
658 # # form the a-line summing across the elements
659 # line = sum(sensor_data)
660 # return lin
662 def combine_sensor_data(self, sensor_data):
663 # check the data is the correct size
664 if sensor_data.shape[0] != (self.number_active_elements * self.transducer.element_width * self.transducer.element_length):
665 raise ValueError('The number of time series in the input sensor_data must match the number of grid points in the active tranducer elements.')
667 # get index of which element each time series belongs to
668 # Tricky things going on here
669 ind = self.indexed_active_elements_mask[0].T[self.indexed_active_elements_mask[0].T > 0]
671 # create empty output
672 sensor_data_sum = np.zeros((int(self.number_active_elements), sensor_data.shape[1]))
674 # check if an elevation focus is set
675 if np.isinf(self.elevation_focus_distance):
677 raise NotImplementedError
679 # # loop over time series and sum
680 # for ii = 1:length(ind)
681 # sensor_data_sum(ind(ii), :) = sensor_data_sum(ind(ii), :) + sensor_data(ii, :);
682 # end
684 else:
686 # get the elevation delay for each grid point of each
687 # active transducer element (this is given in units of grid
688 # points)
689 dm = self.delay_mask(2)
690 # dm = dm[self.active_elements_mask != 0]
691 dm = dm[0].T[self.active_elements_mask[0].T != 0]
692 dm = dm.astype(np.int32)
694 # loop over time series, shift and sum
695 for ii in range(len(ind)):
696 # FARID: something nasty can be here
697 end = -dm[ii] if dm[ii] != 0 else sensor_data_sum.shape[-1]
698 sensor_data_sum[ind[ii]-1, 0:end] = sensor_data_sum[ind[ii]-1, 0:end] + sensor_data[ii, dm[ii]:]
700 # divide by number of time series in each element
701 sensor_data_sum = sensor_data_sum * (1 / (self.transducer.element_width * self.transducer.element_length))
702 return sensor_data_sum