Skip to content

Commit 4fdce20

Browse files
committed
Improve the autopicker algorithm
1 parent b048afe commit 4fdce20

File tree

1 file changed

+36
-19
lines changed

1 file changed

+36
-19
lines changed

sourcespec/ssp_wave_picking.py

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,38 +18,54 @@
1818
def _refine_theo_pick_time(
1919
trace, phase, theo_pick_time, s_minus_p, freqmin, debug=False):
2020
"""
21-
Refine theoretical pick time through the analysis of the smoothed envelope
22-
of the trace.
21+
Refine theoretical pick time through the analysis of the smoothed
22+
and contrasted envelope of the trace.
2323
"""
2424
tr = trace.copy()
25-
# demean, highpass, normalize
25+
# Demean, highpass, normalize
2626
tr.detrend('demean')
2727
tr.filter('highpass', freq=freqmin, corners=2)
2828
tr.normalize()
29-
# smoothed envelope
30-
tr_smooth = tr.copy()
31-
tr_smooth.data = np.abs(tr_smooth.data)
29+
# Build the envelope, smooth it and increase the contrast
30+
tr_envelope = tr.copy()
31+
tr_envelope.data = np.abs(tr_envelope.data)
3232
if phase == 'P':
3333
# Less smoothing for P phases
3434
npts = int(0.02*s_minus_p/tr.stats.delta)
3535
else:
3636
# More smoothing for S phases
37-
npts = int(0.1*s_minus_p/tr.stats.delta)
37+
npts = int(0.05*s_minus_p/tr.stats.delta)
3838
for _ in range(10):
39-
tr_smooth.data = smooth(tr_smooth.data, npts)
40-
tr_smooth.normalize()
39+
tr_envelope.data = smooth(tr_envelope.data, npts)
40+
# Increase the contrast by rising to a certain power
41+
power = 3 if phase == 'P' else 6
42+
tr_envelope.data = tr_envelope.data**power
43+
tr_envelope.normalize()
4144
# Cut the trace around the theoretical pick time
42-
tr_cut = tr_smooth.copy()
45+
tr_cut = tr_envelope.copy()
4346
cut_t0 = theo_pick_time - 0.7*s_minus_p
4447
cut_t1 = theo_pick_time + 0.3*s_minus_p
4548
tr_cut.trim(cut_t0, cut_t1)
46-
# cut shift is used for debug plots
47-
cut_shift = tr_smooth.stats.starttime - tr_cut.stats.starttime
48-
# cut again up to the maximum amplitude
49-
max_time = tr_cut.stats.starttime + tr_cut.times()[np.argmax(tr_cut.data)]
50-
tr_cut.trim(
51-
starttime=tr_cut.stats.starttime,
52-
endtime=max_time)
49+
# Threshold the cut trace, then cut it again up to its maximum
50+
rms = np.sqrt(np.mean(tr_cut.data**2))
51+
threshold = 0.1 if phase == 'P' else 0.8
52+
threshold *= rms
53+
# Loop to try to find a threshold that gives a cut trace
54+
# with at least 5 samples
55+
for _ in range(10):
56+
_tmp_tr_cut = tr_cut.copy()
57+
_tmp_tr_cut.data[_tmp_tr_cut.data >= threshold] = threshold
58+
max_time =\
59+
_tmp_tr_cut.stats.starttime +\
60+
_tmp_tr_cut.times()[np.argmax(_tmp_tr_cut.data)]
61+
_tmp_tr_cut.trim(
62+
starttime=tr_cut.stats.starttime,
63+
endtime=max_time)
64+
if len(_tmp_tr_cut.data) < 5:
65+
threshold *= 2
66+
else:
67+
tr_cut = _tmp_tr_cut.copy()
68+
break
5369
# Remove a linear function defined by first/last sample of the trace
5470
tr_cut_detrend = tr_cut.copy()
5571
tr_cut_detrend.detrend('simple')
@@ -76,7 +92,8 @@ def _refine_theo_pick_time(
7692
_fig, ax = plt.subplots(figsize=(10, 5))
7793
ax.set_ylim(-1.1, 1.1)
7894
ax.plot(tr.times(), tr.data, 'k', lw=0.5)
79-
ax.plot(tr.times(), tr_smooth.data, 'r')
95+
ax.plot(tr.times(), tr_envelope.data, 'r')
96+
cut_shift = tr_envelope.stats.starttime - tr_cut.stats.starttime
8097
ax.plot(tr_cut.times()-cut_shift, tr_cut.data, 'b', lw=2)
8198
ax.plot(
8299
tr_cut_detrend.times()-cut_shift, tr_cut_detrend.data, 'g', lw=4)
@@ -105,7 +122,7 @@ def _refine_theo_pick_time(
105122
def refine_trace_picks(trace, freqmin, debug=False):
106123
"""
107124
Refine theoretical pick times through the analysis of the smoothed
108-
envelope of the trace.
125+
and contrasted envelope of the trace.
109126
"""
110127
p_arrival_phase = trace.stats.arrivals['P'][0]
111128
p_arrival_time = trace.stats.arrivals['P'][1]

0 commit comments

Comments
 (0)