Ejemplo n.º 1
0
 def test_noise_plot(self):
     signal = self.st.copy().trim(
         self.st[0].stats.starttime + 10,
         self.st[0].stats.starttime + 15)
     noise = self.st.copy().trim(self.st[0].stats.starttime + 20)
     fig = noise_plot(
         signal=signal, noise=noise, show=False, return_figure=True)
     return fig
Ejemplo n.º 2
0
def _template_gen(picks,
                  st,
                  length,
                  swin='all',
                  prepick=0.05,
                  all_horiz=False,
                  delayed=True,
                  plot=False,
                  min_snr=None,
                  plotdir=None):
    """
    Master function to generate a multiplexed template for a single event.

    Function to generate a cut template as :class:`obspy.core.stream.Stream`
    from a given set of picks and data.  Should be given pre-processed
    data (downsampled and filtered).

    :type picks: list
    :param picks: Picks to extract data around, where each pick in the \
        list is an obspy.core.event.origin.Pick object.
    :type st: obspy.core.stream.Stream
    :param st: Stream to extract templates from
    :type length: float
    :param length: Length of template in seconds
    :type swin: str
    :param swin:
        P, S, P_all, S_all or all, defaults to all: see note in
        :func:`eqcorrscan.core.template_gen.template_gen`
    :type prepick: float
    :param prepick:
        Length in seconds to extract before the pick time default is 0.05
        seconds.
    :type all_horiz: bool
    :param all_horiz:
        To use both horizontal channels even if there is only a pick on one
        of them.  Defaults to False.
    :type delayed: bool
    :param delayed:
        If True, each channel will begin relative to it's own pick-time, if
        set to False, each channel will begin at the same time.
    :type plot: bool
    :param plot:
        To plot the template or not, default is False. Plots are saved as
        `template-starttime_template.png` and `template-starttime_noise.png`,
        where `template-starttime` is the start-time of the template
    :type min_snr: float
    :param min_snr:
        Minimum signal-to-noise ratio for a channel to be included in the
        template, where signal-to-noise ratio is calculated as the ratio of
        the maximum amplitude in the template window to the rms amplitude in
        the whole window given.
    :type plotdir: str
	:param plotdir:
        The path to save plots to. If `plotdir=None` (default) then the figure
        will be shown on screen.

    :returns: Newly cut template.
    :rtype: :class:`obspy.core.stream.Stream`

    .. note::
        By convention templates are generated with P-phases on the
        vertical channel and S-phases on the horizontal channels, normal
        seismograph naming conventions are assumed, where Z denotes vertical
        and N, E, R, T, 1 and 2 denote horizontal channels, either oriented
        or not.  To this end we will **only** use Z channels if they have a
        P-pick, and will use one or other horizontal channels **only** if
        there is an S-pick on it.

    .. note::
        swin argument: Setting to `P` will return only data for channels
        with P picks, starting at the pick time (minus the prepick).
        Setting to `S` will return only data for channels with
        S picks, starting at the S-pick time (minus the prepick)
        (except if `all_horiz=True` when all horizontal channels will
        be returned if there is an S pick on one of them). Setting to `all`
        will return channels with either a P or S pick (including both
        horizontals if `all_horiz=True`) - with this option vertical channels
        will start at the P-pick (minus the prepick) and horizontal channels
        will start at the S-pick time (minus the prepick).
        `P_all` will return cut traces starting at the P-pick time for all
        channels. `S_all` will return cut traces starting at the S-pick
        time for all channels.

    .. warning::
        If there is no phase_hint included in picks, and swin=all, all
        channels with picks will be used.
    """
    from eqcorrscan.utils.plotting import pretty_template_plot as tplot
    from eqcorrscan.utils.plotting import noise_plot

    # the users picks intact.
    if not isinstance(swin, list):
        swin = [swin]
    for _swin in swin:
        assert _swin in ['P', 'all', 'S', 'P_all', 'S_all']
    picks_copy = []
    for pick in picks:
        if not pick.waveform_id:
            Logger.warning(
                "Pick not associated with waveform, will not use it: "
                "{0}".format(pick))
            continue
        if not pick.waveform_id.station_code or not \
                pick.waveform_id.channel_code:
            Logger.warning(
                "Pick not associated with a channel, will not use it:"
                " {0}".format(pick))
            continue
        picks_copy.append(pick)
    if len(picks_copy) == 0:
        return Stream()
    st_copy = Stream()
    for tr in st:
        # Check that the data can be represented by float16, and check they
        # are not all zeros
        if np.all(tr.data.astype(np.float16) == 0):
            Logger.error("Trace is all zeros at float16 level, either gain or "
                         "check. Not using in template: {0}".format(tr))
            continue
        st_copy += tr
    st = st_copy
    if len(st) == 0:
        return st
    # Get the earliest pick-time and use that if we are not using delayed.
    picks_copy.sort(key=lambda p: p.time)
    first_pick = picks_copy[0]
    if plot:
        stplot = st.slice(first_pick.time - 20,
                          first_pick.time + length + 90).copy()
        noise = stplot.copy()
    # Work out starttimes
    starttimes = []
    for _swin in swin:
        for tr in st:
            starttime = {
                'station': tr.stats.station,
                'channel': tr.stats.channel,
                'picks': []
            }
            station_picks = [
                pick for pick in picks_copy
                if pick.waveform_id.station_code == tr.stats.station
            ]
            if _swin == 'P_all':
                p_pick = [
                    pick for pick in station_picks
                    if pick.phase_hint.upper()[0] == 'P'
                ]
                if len(p_pick) == 0:
                    continue
                starttime.update({'picks': p_pick})
            elif _swin == 'S_all':
                s_pick = [
                    pick for pick in station_picks
                    if pick.phase_hint.upper()[0] == 'S'
                ]
                if len(s_pick) == 0:
                    continue
                starttime.update({'picks': s_pick})
            elif _swin == 'all':
                if all_horiz and tr.stats.channel[-1] in [
                        '1', '2', '3', 'N', 'E'
                ]:
                    # Get all picks on horizontal channels
                    channel_pick = [
                        pick for pick in station_picks
                        if pick.waveform_id.channel_code[-1] in
                        ['1', '2', '3', 'N', 'E']
                    ]
                else:
                    channel_pick = [
                        pick for pick in station_picks
                        if pick.waveform_id.channel_code == tr.stats.channel
                    ]
                if len(channel_pick) == 0:
                    continue
                starttime.update({'picks': channel_pick})
            elif _swin == 'P':
                p_pick = [
                    pick for pick in station_picks
                    if pick.phase_hint.upper()[0] == 'P'
                    and pick.waveform_id.channel_code == tr.stats.channel
                ]
                if len(p_pick) == 0:
                    continue
                starttime.update({'picks': p_pick})
            elif _swin == 'S':
                if tr.stats.channel[-1] in ['Z', 'U']:
                    continue
                s_pick = [
                    pick for pick in station_picks
                    if pick.phase_hint.upper()[0] == 'S'
                ]
                if not all_horiz:
                    s_pick = [
                        pick for pick in s_pick
                        if pick.waveform_id.channel_code == tr.stats.channel
                    ]
                starttime.update({'picks': s_pick})
                if len(starttime['picks']) == 0:
                    continue
            if not delayed:
                starttime.update({'picks': [first_pick]})
            starttimes.append(starttime)
    # Cut the data
    st1 = Stream()
    for _starttime in starttimes:
        Logger.info("Working on channel %s.%s" %
                    (_starttime['station'], _starttime['channel']))
        tr = st.select(station=_starttime['station'],
                       channel=_starttime['channel'])[0]
        Logger.info("Found Trace {0}".format(tr))
        used_tr = False
        for pick in _starttime['picks']:
            if not pick.phase_hint:
                Logger.warning(
                    "Pick for {0}.{1} has no phase hint given, you should not "
                    "use this template for cross-correlation"
                    " re-picking!".format(pick.waveform_id.station_code,
                                          pick.waveform_id.channel_code))
            starttime = pick.time - prepick
            Logger.debug("Cutting {0}".format(tr.id))
            noise_amp = _rms(
                tr.slice(starttime=starttime - 100, endtime=starttime).data)
            tr_cut = tr.slice(starttime=starttime,
                              endtime=starttime + length,
                              nearest_sample=False).copy()
            if plot:
                noise.select(station=_starttime['station'],
                             channel=_starttime['channel']).trim(
                                 noise[0].stats.starttime, starttime)
            if len(tr_cut.data) == 0:
                Logger.warning(
                    "No data provided for {0}.{1} starting at {2}".format(
                        tr.stats.station, tr.stats.channel, starttime))
                continue
            # Ensure that the template is the correct length
            if len(tr_cut.data) == (tr_cut.stats.sampling_rate * length) + 1:
                tr_cut.data = tr_cut.data[0:-1]
            Logger.debug(
                'Cut starttime = %s\nCut endtime %s' %
                (str(tr_cut.stats.starttime), str(tr_cut.stats.endtime)))
            if min_snr is not None and \
               max(tr_cut.data) / noise_amp < min_snr:
                Logger.warning(
                    "Signal-to-noise ratio {0} below threshold for {1}.{2}, "
                    "not using".format(
                        max(tr_cut.data) / noise_amp, tr_cut.stats.station,
                        tr_cut.stats.channel))
                continue
            st1 += tr_cut
            used_tr = True
        if not used_tr:
            Logger.warning('No pick for {0}'.format(tr.id))
    if plot and len(st1) > 0:
        plot_kwargs = dict(show=True)
        if plotdir is not None:
            if not os.path.isdir(plotdir):
                os.makedirs(plotdir)
            plot_kwargs.update(dict(show=False, save=True))
        tplot(st1,
              background=stplot,
              picks=picks_copy,
              title='Template for ' + str(st1[0].stats.starttime),
              savefile="{0}/{1}_template.png".format(plotdir,
                                                     st1[0].stats.starttime),
              **plot_kwargs)
        noise_plot(signal=st1,
                   noise=noise,
                   savefile="{0}/{1}_noise.png".format(plotdir,
                                                       st1[0].stats.starttime),
                   **plot_kwargs)
        del stplot
    return st1