11from __future__ import annotations
2+ from collections import deque
23
34import logging
45from copy import deepcopy
@@ -330,6 +331,8 @@ def follow_phase(
330331 window_size : int | tuple [int , int ] = 50 ,
331332 threshold : float = 5e-1 ,
332333 max_shift : int = 20 ,
334+ stack : bool = False ,
335+ no_of_stacks : int = 5 ,
333336 ) -> tuple [np .ndarray , list [datetime ], np .ndarray ]:
334337 """Follow a phase pick through a Blast.
335338
@@ -340,7 +343,7 @@ def follow_phase(
340343 2. Calculate normalized cross correlate with downwards neighbor.
341344 3. Evaluate maximum x-correlation in allowed window (max_shift).
342345 4. Update template trace and go to 2.
343-
346+ 4a. if stack=True: stack templates for correlation to stabilize
344347 5. Repeat for upward neighbors.
345348
346349 Args:
@@ -353,6 +356,12 @@ def follow_phase(
353356 Defaults to 5e-1.
354357 max_shift (int, optional): Maximum allowed shift in samples for
355358 neighboring picks. Defaults to 20.
359+ stack (bool): If True - (a default number of 5) templates will be stacked and used
360+ as correlation template. Stacking close to the initial template is limited to
361+ the distance to the initial tamplate. I.e. the correlation of a trace 3 traces
362+ next to the initial template will only use a stacked template of 3 traces
363+ (initial trace an trace 1 and 2 next to it), altough the no_of_stacks is set higher.
364+ no_of_stacks (int): Numbers of traces to stack to define the template.
356365
357366 Returns:
358367 tuple[np.ndarray, list[datetime], np.ndarray]: Tuple of channel number,
@@ -375,12 +384,21 @@ def follow_phase(
375384 def prepare_template (data : np .ndarray ) -> np .ndarray :
376385 return data * template_taper
377386
387+ # def stack_n_straces(data: np.ndarray,stack_traces) -> np.ndarray:
388+
389+ # return stacked_data
390+
378391 def correlate (data : np .ndarray , direction : Literal [1 , - 1 ] = 1 ) -> None :
379392 template = root_template .copy ()
380- index = root_idx
393+ template_deque = deque ([ np . array ( template )])
381394
395+ index = root_idx
382396 for ichannel , trace in enumerate (data ):
383397 template = prepare_template (template )
398+ # check if stacking is activated
399+ if stack and len (template_deque ) > 2 :
400+ template = prepare_template (template_stack )
401+
384402 norm = np .sqrt (np .sum (template ** 2 )) * np .sqrt (np .sum (trace ** 2 ))
385403 correlation = np .correlate (trace , template , mode = "same" )
386404 correlation = np .abs (correlation / norm )
@@ -395,7 +413,7 @@ def correlate(data: np.ndarray, direction: Literal[1, -1] = 1) -> None:
395413 phase_correlation = correlation [phase_idx ]
396414 phase_time = self ._sample_to_time (int (phase_idx ))
397415
398- if phase_correlation < threshold :
416+ if phase_correlation > threshold :
399417 continue
400418
401419 # Avoid the edges
@@ -409,14 +427,21 @@ def correlate(data: np.ndarray, direction: Literal[1, -1] = 1) -> None:
409427 template = trace [
410428 phase_idx - window_size [0 ] : phase_idx + window_size [1 ] + 1
411429 ].copy ()
430+
431+ # stacking
432+ if len (template_deque ) <= no_of_stacks :
433+ template_deque .append (template )
434+ if len (template_deque ) == no_of_stacks + 1 :
435+ template_deque .popleft ()
436+ template_stack = np .sum (template_deque , axis = 0 ) / len (template_deque )
437+
412438 index = phase_idx
413439
414440 correlate (self .data [pick_channel :])
415441 correlate (self .data [: pick_channel - 1 ][::- 1 ], direction = - 1 )
416442
417443 pick_channels = np .array (pick_channels ) + self .start_channel
418444 pick_correlations = np .array (pick_correlations )
419-
420445 return pick_channels , pick_times , pick_correlations
421446
422447 def taper (self , alpha : float = 0.05 ) -> None :
0 commit comments