Source code for locate

r"""Functions to locate earthquakes detected by EQcorrscan.
Designed primarily locate stacks of detections to give family locations.
Extensions may later be written.

Copyright 2015 Calum Chamberlain

This file is part of EQcorrscan.

    EQcorrscan is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    EQcorrscan is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with EQcorrscan.  If not, see <http://www.gnu.org/licenses/>.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals


[docs]def synth_compare(stream, stream_list, cores=4, debug=0): r"""Compare a specific stream to a list of synthetic templates, or \ earthquakes of known source and find the best matching event. :type stream: :class:obspy.Stream :param stream: Stream to be compared to streams with known locations. :type stream_list: list :param stream_list: List of streams with known locations :type cores: int :param cores: Number of cores to parallel over :type debug: int :param debug: Debug level, high is more debug :returns: int, float: index of best match and cross-correlation sum """ from eqcorrscan.core.match_filter import _channel_loop import numpy as np import copy from obspy import Trace stream_copy = stream.copy() templates = copy.deepcopy(stream_list) # Need to fill the stream_list - template - channels template_stachan = [] for template in templates: for tr in template: template_stachan += [(tr.stats.station, tr.stats.channel)] template_stachan = list(set(template_stachan)) for stachan in template_stachan: if not stream_copy.select(station=stachan[0], channel=stachan[1]): # Remove template traces rather than adding NaN data for template in templates: if template.select(station=stachan[0], channel=stachan[1]): for tr in template.select(station=stachan[0], channel=stachan[1]): template.remove(tr) # Remove un-needed channels for tr in stream_copy: if not (tr.stats.station, tr.stats.channel) in template_stachan: stream_copy.remove(tr) # Also pad out templates to have all channels for template in templates: for stachan in template_stachan: if not template.select(station=stachan[0], channel=stachan[1]): nulltrace = Trace() nulltrace.stats.station = stachan[0] nulltrace.stats.channel = stachan[1] nulltrace.stats.sampling_rate = template[0].stats.sampling_rate nulltrace.stats.starttime = template[0].stats.starttime nulltrace.data = np.array([np.NaN] * len(template[0].data), dtype=np.float32) template += nulltrace # Hand off cross-correaltion to _channel_loop, which runs in parallel [cccsums, no_chans] = _channel_loop(templates, stream_copy, cores, debug) cccsums = [np.max(cccsum) for cccsum in cccsums] # Find the maximum cccsum and index thereof index = np.argmax(cccsums) cccsum = cccsums[index] return index, cccsum
[docs]def cross_net(stream, env=False, debug=0, master=False): r"""Function to generate picks for each channel based on optimal moveout \ defined by maximum cross-correaltion with master trace. Master trace \ will be the first trace in the stream. :type stream: :class: obspy.Stream :param stream: Stream to pick :type envelope: bool :param envelope: To compute cross-correlations on the envelope or not. :type debug: int :param debug: Debug level from 0-5 :type master: obspy.Trace :param master: Trace to use as master, if False, will use the first trace \ in stream. :returns: list of pick class """ from obspy.signal.cross_correlation import xcorr from obspy.signal.filter import envelope from eqcorrscan.utils.sfile_util import PICK import matplotlib.pyplot as plt import numpy as np picks = [] samp_rate = stream[0].stats.sampling_rate if not env: if debug > 2: print('Using the raw data') st = stream.copy() st.resample(samp_rate) else: st = stream.copy() if debug > 2: print('Computing envelope') for tr in st: tr.resample(samp_rate) tr.data = envelope(tr.data) if debug > 2: st.plot(equal_scale=False, size=(800, 600)) if not master: master = st[0] else: master = master master.data = np.nan_to_num(master.data) for tr in st: tr.data = np.nan_to_num(tr.data) if debug > 2: msg = ' '.join(['Comparing', tr.stats.station, tr.stats.channel, 'with the master']) print(msg) shift_len = int(0.3 * len(tr)) if debug > 2: print('Shift length is set to ' + str(shift_len) + ' samples') if debug > 3: index, cc, cc_vec = xcorr(master, tr, shift_len, full_xcorr=True) cc_vec = np.nan_to_num(cc_vec) if debug > 4: print(cc_vec) fig = plt.figure() ax1 = fig.add_subplot(211) x = np.linspace(0, len(master) / samp_rate, len(master)) ax1.plot(x, master.data / float(master.data.max()), 'k', label='Master') ax1.plot(x + (index / samp_rate), tr.data / float(tr.data.max()), 'r', label='Slave shifted') ax1.legend(loc="lower right", prop={'size': "small"}) ax1.set_xlabel("time [s]") ax1.set_ylabel("norm. amplitude") ax2 = fig.add_subplot(212) print(len(cc_vec)) x = np.linspace(0, len(cc_vec) / samp_rate, len(cc_vec)) ax2.plot(x, cc_vec, label='xcorr') # ax2.set_ylim(-1, 1) # ax2.set_xlim(0, len(master)) plt.show() index, cc = xcorr(master, tr, shift_len) pick = PICK(station=tr.stats.station, channel=tr.stats.channel, impulsivity='E', phase='S', weight='1', time=tr.stats.starttime + (index / tr.stats.sampling_rate)) if debug > 2: print(pick) picks.append(pick) del st return picks
[docs]def stalta_pick(stream, stalen, ltalen, trig_on, trig_off, freqmin=False, freqmax=False, debug=0, show=False): r"""Simple sta-lta (short-term average/long-term average) picker, using \ obspy's stalta routine to generate the characteristic function. Currently very basic quick wrapper, there are many other (better) options \ in obspy, found \ (here)[http://docs.obspy.org/packages/autogen/obspy.signal.trigger.html]. :type stream: obspy.Stream :param stream: The stream to pick on, can be any number of channels. :type stalen: float :param stalen: Length of the short-term average window in seconds. :type ltalen: float :param ltalen: Length of the long-term average window in seconds. :type trig_on: float :param trig_on: sta/lta ratio to trigger a detection/pick :type trig_off: float :param trig_off: sta/lta ratio to turn the trigger off - no further picks\ will be made between exceeding trig_on until trig_off is reached. :type freqmin: float :param freqmin: Low-cut frequency in Hz for bandpass filter :type freqmax: float :param freqmax: High-cut frequency in Hz for bandpass filter :type debug: int :param debug: Debug output level from 0-5. :type show: bool :param show: Show picks on waveform. :returns: list of pick class. """ from obspy.signal.trigger import classicSTALTA, triggerOnset, plotTrigger from sfile_util import PICK import EQcorrscan_plotting as plotting picks = [] for tr in stream: # We are going to assume, for now, that if the pick is made on the # horizontal channel then it is an S, otherwise we will assume it is # a P-phase: obviously a bad assumption... if tr.stats.channel[-1] == 'Z': phase = 'P' else: phase = 'S' if freqmin and freqmax: tr.detrend('simple') tr.filter('bandpass', freqmin=freqmin, freqmax=freqmax, corners=3, zerophase=True) df = tr.stats.sampling_rate cft = classicSTALTA(tr.data, int(stalen * df), int(ltalen * df)) if debug > 3: plotTrigger(tr, cft, trig_on, trig_off) triggers = triggerOnset(cft, trig_on, trig_off) for trigger in triggers: on = tr.stats.starttime + (trigger[0] / df) # off = tr.stats.starttime + (trigger[1] / df) pick = PICK(station=tr.stats.station, channel=tr.stats.channel, time=on, phase=phase) if debug > 2: print('Pick made:') print(pick) picks.append(pick) # QC picks del pick pick_stations = list(set([pick.station for pick in picks])) for pick_station in pick_stations: station_picks = [pick for pick in picks if pick.station == pick_station] # If P-pick is after S-picks, remove it. p_time = [pick.time for pick in station_picks if pick.phase == 'P'] s_time = [pick.time for pick in station_picks if pick.phase == 'S'] if p_time > s_time: p_pick = [pick for pick in station_picks if pick.phase == 'P'] for pick in p_pick: print('P pick after S pick, removing P pick') picks.remove(pick) if show: plotting.pretty_template_plot(stream, picks=picks, title='Autopicks', size=(8, 9)) return picks
if __name__ == "__main__": import doctest doctest.testmod()