Coverage for /opt/obspy/update-docs/src/obspy/obspy/signal/cross_correlation : 49%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
#!/usr/bin/env python #-------------------------------------------------------------------- # Filename: cross_correlation.py # Author: Moritz Beyreuther, Tobias Megies # Email: megies@geophysik.uni-muenchen.de # # Copyright (C) 2008-2012 Moritz Beyreuther, Tobias Megies #------------------------------------------------------------------- Signal processing routines based on cross correlation techniques.
:copyright: The ObsPy Development Team (devs@obspy.org) :license: GNU Lesser General Public License, Version 3 (http://www.gnu.org/copyleft/lesser.html) """
""" Cross correlation of tr1 and tr2 in the time domain using window_len.
::
Mid Sample | |AAAAAAAAAAAAAAA|AAAAAAAAAAAAAAA|AAAAAAAAAAAAAAA|AAAAAAAAAAAAAAA| |BBBBBBBBBBBBBBB|BBBBBBBBBBBBBBB|BBBBBBBBBBBBBBB|BBBBBBBBBBBBBBB| |<-shift_len/2->| <- region of support -> |<-shift_len/2->|
:type tr1: :class:`~numpy.ndarray`, :class:`~obspy.core.trace.Trace` :param tr1: Trace 1 :type tr2: :class:`~numpy.ndarray`, :class:`~obspy.core.trace.Trace` :param tr2: Trace 2 to correlate with trace 1 :type shift_len: int :param shift_len: Total length of samples to shift for cross correlation. :type full_xcorr: bool :param full_xcorr: If ``True``, the complete xcorr function will be returned as :class:`~numpy.ndarray` :return: **index, value[, fct]** - Index of maximum xcorr value and the value itself. The complete xcorr function is returned only if ``full_xcorr=True``.
.. note:: As shift_len gets higher the window supporting the cross correlation actually gets smaller. So with shift_len=0 you get the correlation coefficient of both traces as a whole without any shift applied. As the xcorr function works in time domain and does not zero pad at all, with higher shifts allowed the window of support gets smaller so that the moving windows shifted against each other do not run out of the timeseries bounds at high time shifts. Of course there are other possibilities to do cross correlations e.g. in frequency domain.
.. seealso:: `ObsPy-users mailing list <http://lists.obspy.org/pipermail/obspy-users/2011-March/000056.html>`_ and `ticket #249 <https://obspy.org/ticket/249>`_.
.. rubric:: Example
>>> tr1 = np.random.randn(10000).astype('float32') >>> tr2 = tr1.copy() >>> a, b = xcorr(tr1, tr2, 1000) >>> a 0 >>> round(b, 7) 1.0 """ # if we get Trace objects, use their data arrays tr = tr.data
# check if shift_len parameter is in an acceptable range. # if not the underlying c code tampers with shift_len and uses shift_len/2 # instead. we want to avoid this silent automagic and raise an error in the # python layer right here. # see ticket #249 and src/xcorr.c lines 43-57 msg = "shift_len too large. The underlying C code would silently " + \ "use shift_len/2 which we want to avoid." raise ValueError(msg) # be nice and adapt type if necessary
C.byref(shift), C.byref(coe_p))
else:
full_xcorr=False, abs_max=True): """ Calculates the cross correlation on each of the specified components separately, stacks them together and estimates the maximum and shift of maximum on the stack.
Basically the same as :func:`~obspy.signal.cross_correlation.xcorr` but for (normally) three components, please also take a look at the documentation of that function. Useful e.g. for estimation of waveform similarity on a three component seismogram.
:type st1: :class:`~obspy.core.stream.Stream` :param st1: Stream 1, containing one trace for Z, N, E component (other component_id codes are ignored) :type st2: :class:`~obspy.core.stream.Stream` :param st2: Stream 2, containing one trace for Z, N, E component (other component_id codes are ignored) :type shift_len: int :param shift_len: Total length of samples to shift for cross correlation. :type components: List of strings :param components: List of components to use in cross-correlation, defaults to ``['Z', 'N', 'E']``. :type full_xcorr: bool :param full_xcorr: If ``True``, the complete xcorr function will be returned as :class:`~numpy.ndarray`. :return: **index, value[, fct]** - index of maximum xcorr value and the value itself. The complete xcorr function is returned only if ``full_xcorr=True``. """ streams = [st1, st2] # check if we can actually use the provided streams safely for st in streams: if not isinstance(st, Stream): raise TypeError("Expected Stream object but got %s." % type(st)) for component in components: if not len(st.select(component=component)) == 1: msg = "Expected exactly one %s trace in stream" % component + \ " but got %s." % len(st.select(component=component)) raise ValueError(msg) ndat = len(streams[0].select(component=components[0])[0]) if False in [len(st.select(component=component)[0]) == ndat \ for st in streams for component in components]: raise ValueError("All traces have to be the same length.") # everything should be ok with the input data... corp = np.zeros(2 * shift_len + 1, dtype='float64', order='C')
for component in components: xx = xcorr(streams[0].select(component=component)[0], streams[1].select(component=component)[0], shift_len, full_xcorr=True) corp += xx[2]
corp /= len(components)
shift, value = xcorr_max(corp, abs_max=abs_max)
if full_xcorr: return shift, value, corp else: return shift, value
""" Return shift and value of maximum xcorr function
:type fct: :class:`~numpy.ndarray` :param fct: xcorr function e.g. returned by xcorr :type abs_max: bool :param abs_max: determines if the absolute maximum should be used. :return: **shift, value** - Shift and value of maximum xcorr.
.. rubric:: Example
>>> fct = np.zeros(101) >>> fct[50] = -1.0 >>> xcorr_max(fct) (0.0, -1.0) >>> fct[50], fct[60] = 0.0, 1.0 >>> xcorr_max(fct) (10.0, 1.0) >>> fct[60], fct[40] = 0.0, -1.0 >>> xcorr_max(fct) (-10.0, -1.0) >>> fct[60], fct[40] = 0.5, -1.0 >>> xcorr_max(fct, abs_max=True) (-10.0, -1.0) >>> xcorr_max(fct, abs_max=False) (10.0, 0.5) """
t_after, cc_maxlag, filter=None, filter_options={}, plot=False, filename=None): """ Calculate the correction for the differential pick time determined by cross correlation of the waveforms in narrow windows around the pick times. For details on the fitting procedure refer to [Deichmann1992]_.
The parameters depend on the epicentral distance and magnitude range. For small local earthquakes (Ml ~0-2, distance ~3-10 km) with consistent manual picks the following can be tried::
t_before=0.05, t_after=0.2, cc_maxlag=0.10, filter="bandpass", filter_options={'filter_low': 1, 'filter_high': 20}
The appropriate parameter sets can and should be determined/verified visually using the option `show=True` on a representative set of picks.
To get the corrected differential pick time calculate: ``((pick2 + pick2_corr) - pick1)``. To get a corrected differential travel time using origin times for both events calculate: ``((pick2 + pick2_corr - ot2) - (pick1 - ot1))``
:type pick1: :class:`~obspy.core.utcdatetime.UTCDateTime` :param pick1: Time of pick for `trace1`. :type trace1: :class:`~obspy.core.trace.Trace` :param trace1: Waveform data for `pick1`. Add some time at front/back. The appropriate part of the trace is used automatically. :type pick2: :class:`~obspy.core.utcdatetime.UTCDateTime` :param pick2: Time of pick for `trace2`. :type trace2: :class:`~obspy.core.trace.Trace` :param trace2: Waveform data for `pick2`. Add some time at front/back. The appropriate part of the trace is used automatically. :type t_before: float :param t_before: Time to start cross correlation window before pick times in seconds. :type t_after: float :param t_after: Time to end cross correlation window after pick times in seconds. :type cc_maxlag: float :param cc_maxlag: Maximum lag time tested during cross correlation in seconds. :type filter: string :param filter: None for no filtering or name of filter type as passed on to :meth:`~obspy.core.Trace.trace.filter` if filter should be used. To avoid artifacts in filtering provide sufficiently long time series for `trace1` and `trace2`. :type filter_options: dict :param filter_options: Filter options that get passed on to :meth:`~obspy.core.Trace.trace.filter` if filtering is used. :type plot: bool :param plot: Determines if pick is refined automatically (default, ""), if an informative matplotlib plot is shown ("plot"), or if an interactively changeable PyQt Window is opened ("interactive"). :type filename: string :param filename: If plot option is selected, specifying a filename here (e.g. 'myplot.pdf' or 'myplot.png') will output the plot to a file instead of opening a plot window. :rtype: (float, float) :returns: Correction time `pick2_corr` for `pick2` pick time as a float and corresponding correlation coefficient. """ # perform some checks on the traces msg = "Sampling rates do not match: %s != %s" % \ (trace1.stats.sampling_rate, trace2.stats.sampling_rate) raise Exception(msg) msg = "Trace ids do not match: %s != %s" % (trace1.id, trace2.id) warnings.warn(msg) # check data, apply filter and take correct slice of traces # check if necessary time spans are present in data msg = "Trace %s starts too late." % _i raise Exception(msg) msg = "Trace %s ends too early." % _i raise Exception(msg) msg = "Artifacts from signal processing possible. Trace " + \ "%s should have more additional data at the start." % _i warnings.warn(msg) msg = "Artifacts from signal processing possible. Trace " + \ "%s should have more additional data at the end." % _i warnings.warn(msg) # apply signal processing and take correct slice of data # cross correlate shift_len, full_xcorr=True) # check results of cross correlation msg = "Absolute maximum is negative: %.3f. " % cc_max + \ "Using positive maximum: %.3f" % max(cc) warnings.warn(msg) cc_max = max(cc) cc_shift = cc.argmax() - (len(cc) / 2) msg = "Maximum of cross correlation lower than 0.8: %s" % cc_max warnings.warn(msg) # make array with time shifts in seconds corresponding to cc function # take the subportion of the cross correlation around the maximum that is # convex and fit a parabola. # use vertex as subsample resolution best cc fit. # XXX this could be improved.. msg = "Fitting at maximum lag. Maximum lag time should be increased." warnings.warn(msg) # work on subarrays msg = "Less than 3 samples selected for fit to cross " + \ "correlation: %s" % num_samples raise Exception(msg) msg = "Less than 5 samples selected for fit to cross " + \ "correlation: %s" % num_samples warnings.warn(msg) # quadratic fit for small subwindow cc[first_sample:last_sample + 1], deg=2, full=True)[:2] # check results of fit msg = "Fitted parabola opens upwards!" warnings.warn(msg) msg = "Residual in quadratic fit to cross correlation maximum " + \ "larger than 0.1: %s" % residual warnings.warn(msg) # X coordinate of vertex of parabola gives time shift to correct # differential pick time. Y coordinate gives maximum correlation # coefficient. # this is the shift to apply on the time axis of `trace2` to align the # traces. Actually we do not want to shift the trace to align it but we # want to correct the time of `pick2` so that the traces align without # shifting. This is the negative of the cross correlation shift. # plot the results if selected import matplotlib if filename: matplotlib.use('agg') import matplotlib.pyplot as plt fig = plt.figure() ax1 = fig.add_subplot(211) tmp_t = np.linspace(0, len(slices[0]) / samp_rate, len(slices[0])) ax1.plot(tmp_t, slices[0].data / float(slices[0].data.max()), "k", label="Trace 1") ax1.plot(tmp_t, slices[1].data / float(slices[1].data.max()), "r", label="Trace 2") ax1.plot(tmp_t - dt, slices[1].data / float(slices[1].data.max()), "g", label="Trace 2 (shifted)") ax1.legend(loc="lower right", prop={'size': "small"}) ax1.set_title("%s" % slices[0].id) ax1.set_xlabel("time [s]") ax1.set_ylabel("norm. amplitude") ax2 = fig.add_subplot(212) ax2.plot(cc_t, cc_convex, ls="", marker=".", c="k", label="xcorr (convex)") ax2.plot(cc_t, cc_concave, ls="", marker=".", c="0.7", label="xcorr (concave)") ax2.plot(cc_t[first_sample:last_sample + 1], cc[first_sample:last_sample + 1], "b.", label="used for fitting") tmp_t = np.linspace(cc_t[first_sample], cc_t[last_sample], num_samples * 10) ax2.plot(tmp_t, scipy.polyval(coeffs, tmp_t), "b", label="fit") ax2.axvline(-dt, color="g", label="vertex") ax2.axhline(coeff, color="g") ax2.set_xlabel("%.2f at %.3f seconds correction" % (coeff, -dt)) ax2.set_ylabel("correlation coefficient") ax2.set_ylim(-1, 1) ax2.legend(loc="lower right", prop={'size': "x-small"}) #plt.legend(loc="lower left") if filename: fig.savefig(fname=filename) else: plt.show()
if __name__ == '__main__': import doctest doctest.testmod(exclude_empty=True) |