示例#1
0
def convolve(trace, transfer_function, pre_filt=False):
    # write data to a numpy array
    data = trace.data
    npts = len(data)
    nfft = _npts2nfft(npts)
    
    # Transform data to frequency domain
    data = np.fft.rfft(data, n=nfft)

    # find the frequencies corresponding to each element of the fft
    delta = trace.stats.delta
    fy =  1. / (delta * 2.0)
    # start at zero to get zero for offset/ DC of fft
    frequencies = np.linspace(0, fy, nfft // 2 + 1)

    # pre-filter the data
    if pre_filt:
        freq_domain_taper = cosine_sac_taper(frequencies, flimit=pre_filt)
        data *= freq_domain_taper

    # perform the convolution
    data *= transfer_function(frequencies)
    data[-1] = abs(data[-1]) + 0.0j

    # transform data back into the time domain
    data = np.fft.irfft(data)[0:npts]

    # write data back into trace
    trace.data = data
示例#2
0
def pre_filter(stream, pre_filt=(0.02, 0.05, 8.0, 10.0)):
    """
    Applies the same filter as remove_response without actually removing the
    response.
    """
    for tr in stream:
        data = tr.data.astype(np.float64)
        nfft = _npts2nfft(len(data))
        fy = 1.0 / (tr.stats.delta * 2.0)
        freqs = np.linspace(0, fy, nfft // 2 + 1)

        # Transform data to Frequency domain
        data = np.fft.rfft(data, n=nfft)
        data *= cosine_sac_taper(freqs, flimit=pre_filt)
        data[-1] = abs(data[-1]) + 0.0j
        # transform data back into the time domain
        data = np.fft.irfft(data)[0:len(data)]
        # assign processed data and store processing information
        tr.data = data
    return stream
示例#3
0
def filter_synt(tr, pre_filt):
    """Perform a frequency domain taper like during the response removal just without an actual response..."""

    data = tr.data.astype(numpy.float64)
    # Smart calculation of nfft dodging large primes
    #	nfft = _npts2nfft(len(data))
    nfft = int(0.5 * (data.size - 1) + 1)
    fy = 1.0 / (tr.stats.delta * 2.0)
    #	freqs = numpy.linspace(0, fy, nfft // 2 + 1)
    freqs = numpy.linspace(0, fy, nfft)
    # Transform data to frequency domain
    #	data = numpy.fft.rfft(data, n=nfft)
    data = numpy.fft.rfft(data)
    data *= cosine_sac_taper(freqs, flimit=pre_filt)
    data[-1] = abs(data[-1]) + 0.0j
    # Transform data back into the time domain
    #	data = numpy.fft.irfft(data)[0:len(data)]
    data = numpy.fft.irfft(data, int((nfft - 1) * 2.0 + 1))
    # Assign processed data and store processing information
    tr.data = data
示例#4
0
def filter_synt(tr, pre_filt):
	"""Perform a frequency domain taper like during the response removal just without an actual response..."""

	data = tr.data.astype(numpy.float64)
	# Smart calculation of nfft dodging large primes
#	nfft = _npts2nfft(len(data))
	nfft = int(0.5 * (data.size - 1) + 1)
	fy = 1.0 / (tr.stats.delta * 2.0)
#	freqs = numpy.linspace(0, fy, nfft // 2 + 1)
	freqs = numpy.linspace(0, fy, nfft) 
	# Transform data to frequency domain
#	data = numpy.fft.rfft(data, n=nfft)
	data = numpy.fft.rfft(data)
	data *= cosine_sac_taper(freqs, flimit=pre_filt)
	data[-1] = abs(data[-1]) + 0.0j
	# Transform data back into the time domain
#	data = numpy.fft.irfft(data)[0:len(data)]
	data = numpy.fft.irfft(data, int((nfft - 1) * 2.0 + 1))
	# Assign processed data and store processing information
	tr.data = data
示例#5
0
def convolve(trace, func):
    
    from obspy.signal.util import _npts2nfft
    from obspy.signal.invsim import cosine_sac_taper
    
    data = trace.data
    delta = trace.stats.delta
    
    npts = len(data)
    nfft = _npts2nfft(npts)
    
    # Transform data to Frequency domain
    data = np.fft.rfft(data, n=nfft)

    fy =  1. / (delta * 2.0)
    # start at zero to get zero for offset/ DC of fft
    freqs = np.linspace(0, fy, nfft // 2 + 1)
    freq_response = func(freqs)
    
    if pre_filt:
        freq_domain_taper = cosine_sac_taper(freqs, flimit=pre_filt)
        data *= freq_domain_taper
        
    '''
    if water_level is None:
        # No water level used, so just directly invert the response.
        # First entry is at zero frequency and value is zero, too.
        # Just do not invert the first value (and set to 0 to make sure).
        freq_response[0] = 0.0
        freq_response[1:] = 1.0 / freq_response[1:]
    else:
        # Invert spectrum with specified water level.
        invert_spectrum(freq_response, water_level)
    '''

    
    data *= freq_response
    data[-1] = abs(data[-1]) + 0.0j

    # transform data back into the time domain
    data = np.fft.irfft(data)[0:npts]
def filter_synt(tr, pre_filt):
    """
    Perform a frequency domain taper like during the response removal
    just without an actual response...
    :param tr:
    :param pre_filt:
    :return:
    """

    data = tr.data.astype(np.float64)
    # smart calculation of nfft dodging large primes
    nfft = _npts2nfft(len(data))
    fy = 1.0 / (tr.stats.delta * 2.0)
    freqs = np.linspace(0, fy, nfft // 2 + 1)
    # Transform data to Frequency domain
    data = np.fft.rfft(data, n=nfft)
    data *= cosine_sac_taper(freqs, flimit=pre_filt)
    data[-1] = abs(data[-1]) + 0.0j
    # transform data back into the time domain
    data = np.fft.irfft(data)[0:len(data)]
    # assign processed data and store processing information
    tr.data = data
def filter_synt(tr, pre_filt):
    """
    Perform a frequency domain taper like during the response removal
    just without an actual response...
    :param tr:
    :param pre_filt:
    :return:
    """

    data = tr.data.astype(np.float64)
    # smart calculation of nfft dodging large primes
    nfft = _npts2nfft(len(data))
    fy = 1.0 / (tr.stats.delta * 2.0)
    freqs = np.linspace(0, fy, nfft // 2 + 1)
    # Transform data to Frequency domain
    data = np.fft.rfft(data, n=nfft)
    data *= cosine_sac_taper(freqs, flimit=pre_filt)
    data[-1] = abs(data[-1]) + 0.0j
    # transform data back into the time domain
    data = np.fft.irfft(data)[0:len(data)]
    # assign processed data and store processing information
    tr.data = data
示例#8
0
def filter_trace(tr, pre_filt):
    """
    Perform a frequency domain taper mimicing the behavior during the
    response removal, without a actual response removal.

    :param tr: input trace
    :param pre_filt: frequency array(Hz) in ascending order, to define
        the four corners of filter, for example, [0.01, 0.1, 0.2, 0.5].
    :type pre_filt: Numpy.array or list
    :return: filtered trace
    """
    if not isinstance(tr, Trace):
        raise TypeError("First Argument should be trace: %s" % type(tr))
    if len(pre_filt) != 4:
        raise ValueError("Length of filter must be 4(corner frequencies)")
    if not check_array_order(pre_filt, order="ascending"):
        raise ValueError("Frequency band should be in ascending order: %s"
                         % pre_filt)

    data = tr.data.astype(np.float64)
    origin_len = len(data)
    if origin_len == 0:
        return

    # smart calculation of nfft dodging large primes
    nfft = _npts2nfft(len(data))

    fy = 1.0 / (tr.stats.delta * 2.0)
    freqs = np.linspace(0, fy, nfft // 2 + 1)

    # Transform data to Frequency domain
    data = np.fft.rfft(data, n=nfft)
    data *= cosine_sac_taper(freqs, flimit=pre_filt)
    data[-1] = abs(data[-1]) + 0.0j
    # transform data back into the time domain
    data = np.fft.irfft(data)[0:origin_len]
    # assign processed data and store processing information
    tr.data = data
示例#9
0
def remove_response2(self, inventory=None, output="VEL", water_level=60,
                    pre_filt=None, zero_mean=True, taper=True,
                    taper_fraction=0.05, plot=False, fig=None, **kwargs):
    """
    EDIT OF OBSPY'S REMOVE_RESPONSE FUNCTION
    Including titles etc.
    
    Deconvolve instrument response.

    Uses the adequate :class:`obspy.core.inventory.response.Response`
    from the provided
    :class:`obspy.core.inventory.inventory.Inventory` data. Raises an
    exception if the response is not present.

    Note that there are two ways to prevent overamplification
    while convolving the inverted instrument spectrum: One possibility is
    to specify a water level which represents a clipping of the inverse
    spectrum and limits amplification to a certain maximum cut-off value
    (`water_level` in dB). The other possibility is to taper the waveform
    data in the frequency domain prior to multiplying with the inverse
    spectrum, i.e. perform a pre-filtering in the frequency domain
    (specifying the four corner frequencies of the frequency taper as a
    tuple in `pre_filt`).

    .. note::

        Any additional kwargs will be passed on to
        :meth:`obspy.core.inventory.response.Response.get_evalresp_response`,
        see documentation of that method for further customization (e.g.
        start/stop stage).

    .. note::

        Using :meth:`~Trace.remove_response` is equivalent to using
        :meth:`~Trace.simulate` with the identical response provided as
        a (dataless) SEED or RESP file and when using the same
        `water_level` and `pre_filt` (and options `sacsim=True` and
        `pitsasim=False` which influence very minor details in detrending
        and tapering).

    .. rubric:: Example

    >>> from obspy import read, read_inventory
    >>> st = read()
    >>> tr = st[0].copy()
    >>> inv = read_inventory()
    >>> tr.remove_response(inventory=inv)  # doctest: +ELLIPSIS
    <...Trace object at 0x...>
    >>> tr.plot()  # doctest: +SKIP

    .. plot::

        from obspy import read, read_inventory
        st = read()
        tr = st[0]
        inv = read_inventory()
        tr.remove_response(inventory=inv)
        tr.plot()

    Using the `plot` option it is possible to visualize the individual
    steps during response removal in the frequency domain to check the
    chosen `pre_filt` and `water_level` options to stabilize the
    deconvolution of the inverted instrument response spectrum:

    >>> from obspy import read, read_inventory
    >>> st = read("/path/to/IU_ULN_00_LH1_2015-07-18T02.mseed")
    >>> tr = st[0]
    >>> inv = read_inventory("/path/to/IU_ULN_00_LH1.xml")
    >>> pre_filt = [0.001, 0.005, 45, 50]
    >>> tr.remove_response(inventory=inv, pre_filt=pre_filt, output="DISP",
    ...                    water_level=60, plot=True)  # doctest: +SKIP
    <...Trace object at 0x...>

    .. plot::

        from obspy import read, read_inventory
        st = read("/path/to/IU_ULN_00_LH1_2015-07-18T02.mseed", "MSEED")
        tr = st[0]
        inv = read_inventory("/path/to/IU_ULN_00_LH1.xml", "STATIONXML")
        pre_filt = [0.001, 0.005, 45, 50]
        output = "DISP"
        tr.remove_response(inventory=inv, pre_filt=pre_filt, output=output,
                           water_level=60, plot=True)

    :type inventory: :class:`~obspy.core.inventory.inventory.Inventory`
        or None.
    :param inventory: Station metadata to use in search for adequate
        response. If inventory parameter is not supplied, the response
        has to be attached to the trace with :meth:`Trace.attach_response`
        beforehand.
    :type output: str
    :param output: Output units. One of:

        ``"DISP"``
            displacement, output unit is meters
        ``"VEL"``
            velocity, output unit is meters/second
        ``"ACC"``
            acceleration, output unit is meters/second**2

    :type water_level: float
    :param water_level: Water level for deconvolution.
    :type pre_filt: list or tuple of four float
    :param pre_filt: Apply a bandpass filter in frequency domain to the
        data before deconvolution. The list or tuple defines
        the four corner frequencies `(f1, f2, f3, f4)` of a cosine taper
        which is one between `f2` and `f3` and tapers to zero for
        `f1 < f < f2` and `f3 < f < f4`.
    :type zero_mean: bool
    :param zero_mean: If `True`, the mean of the waveform data is
        subtracted in time domain prior to deconvolution.
    :type taper: bool
    :param taper: If `True`, a cosine taper is applied to the waveform data
        in time domain prior to deconvolution.
    :type taper_fraction: float
    :param taper_fraction: Taper fraction of cosine taper to use.
    :type plot: bool or str
    :param plot: If `True`, brings up a plot that illustrates how the
        data are processed in the frequency domain in three steps. First by
        `pre_filt` frequency domain tapering, then by inverting the
        instrument response spectrum with or without `water_level` and
        finally showing data with inverted instrument response multiplied
        on it in frequency domain. It also shows the comparison of
        raw/corrected data in time domain. If a `str` is provided then the
        plot is saved to file (filename must have a valid image suffix
        recognizable by matplotlib e.g. '.png').
    """
   # limit_numpy_fft_cache()

    from obspy.core.inventory import PolynomialResponseStage
    from obspy.signal.invsim import (cosine_taper, cosine_sac_taper,
                                     invert_spectrum)
    if plot:
        import matplotlib.pyplot as plt

    if (isinstance(inventory, (str, native_str)) and
            inventory.upper() in ("DISP", "VEL", "ACC")):
        from obspy.core.util.deprecation_helpers import             ObsPyDeprecationWarning
        output = inventory
        inventory = None
        msg = ("The order of optional parameters in method "
               "remove_response has changed. 'output' is not accepted "
               "as first positional argument in the next release.")
        warnings.warn(msg, category=ObsPyDeprecationWarning, stacklevel=3)
    response = self._get_response(inventory)
    # polynomial response using blockette 62 stage 0
    if not response.response_stages and response.instrument_polynomial:
        coefficients = response.instrument_polynomial.coefficients
        self.data = np.poly1d(coefficients[::-1])(self.data)
        return self

    # polynomial response using blockette 62 stage 1 and no other stages
    if len(response.response_stages) == 1 and        isinstance(response.response_stages[0], PolynomialResponseStage):
        # check for gain
        if response.response_stages[0].stage_gain is None:
            msg = 'Stage gain not defined for %s - setting it to 1.0'
            warnings.warn(msg % self.id)
            gain = 1
        else:
            gain = response.response_stages[0].stage_gain
        coefficients = response.response_stages[0].coefficients[:]
        for i in range(len(coefficients)):
            coefficients[i] /= math.pow(gain, i)
        self.data = np.poly1d(coefficients[::-1])(self.data)
        return self

    # use evalresp
    data = self.data.astype(np.float64)
    npts = len(data)
    # time domain pre-processing
    if zero_mean:
        data -= data.mean()
    if taper:
        data *= cosine_taper(npts, taper_fraction,
                             sactaper=True, halfcosine=False)

    if plot:
        fontsz = 12
        color1 = "blue"
        color2 = "red"
        bbox = dict(boxstyle="round", fc="w", alpha=1, ec="w")
        bbox1 = dict(boxstyle="round", fc="blue", alpha=0.15)
        bbox2 = dict(boxstyle="round", fc="red", alpha=0.15)
        if fig is None:
            fig = plt.figure(figsize=(14, 10))
        fig.suptitle(str(self), fontsize=fontsz)
        ax1 = fig.add_subplot(321)
        ax1b = ax1.twinx()
        ax2 = fig.add_subplot(323, sharex=ax1)
        ax2b = ax2.twinx()
        ax3 = fig.add_subplot(325, sharex=ax1)
        ax3b = ax3.twinx()
        ax4 = fig.add_subplot(322)
        ax5 = fig.add_subplot(324, sharex=ax4)
        ax6 = fig.add_subplot(326, sharex=ax4)
        for ax_ in (ax1, ax2, ax3, ax4, ax5, ax6):
            ax_.grid(zorder=-10)
        if pre_filt is None:
            text = 'pre_filt: None'
        else:
            text = 'pre_filt: [{:.3g}, {:.3g}, {:.3g}, {:.3g}]'.format(
                *pre_filt)
        ax1.text(0.05, 0.1, text, ha="left", va="bottom",
                 transform=ax1.transAxes, fontsize=fontsz, bbox=bbox,
                 zorder=5)
        ax1.set_ylabel("Data spectrum, raw", bbox=bbox1, fontsize=fontsz)
        ax1b.set_ylabel("'pre_filt' taper fraction", bbox=bbox2, fontsize=fontsz)
        evalresp_info = "\n".join(
            ['output: %s' % output] +
            ['%s: %s' % (key, value) for key, value in kwargs.items()])
        ax2.text(0.05, 0.1, evalresp_info, ha="left",
                 va="bottom", transform=ax2.transAxes, zorder=5, bbox=bbox, fontsize=fontsz)
        ax2.set_ylabel("Data spectrum,\n"
                       "'pre_filt' applied", bbox=bbox1, fontsize=fontsz)
        ax2b.set_ylabel("Instrument response", bbox=bbox2, fontsize=fontsz)
        ax3.text(0.05, 0.1, 'water_level: %s' % water_level,
                 ha="left", va="bottom", transform=ax3.transAxes,
                 fontsize=fontsz, zorder=5, bbox=bbox)
        ax3.set_ylabel("Data spectrum,\nmultiplied with inverted\n"
                       "instrument response", bbox=bbox1, fontsize=fontsz)
        ax3b.set_ylabel("Inverted instrument response,\n"
                        "water level applied", bbox=bbox2, fontsize=fontsz)
        ax3.set_xlabel("Frequency [Hz]", fontsize=fontsz)
        
        times = self.times()
        ax4.plot(times, self.data, color="k")
        ax4.set_ylabel("Raw", fontsize=fontsz)
        ax4.yaxis.set_ticks_position("right")
        ax4.yaxis.set_label_position("right")
        ax5.plot(times, data, color="k")
        ax5.set_ylabel("Raw, after time\ndomain pre-processing", fontsize=fontsz)
        ax5.yaxis.set_ticks_position("right")
        ax5.yaxis.set_label_position("right")
        ax6.set_ylabel("Response removed", fontsize=fontsz)
        ax6.set_xlabel("Time [s]", fontsize=fontsz)
        ax6.yaxis.set_ticks_position("right")
        ax6.yaxis.set_label_position("right")
        ax1.tick_params(labelsize=fontsz)
        ax1b.tick_params(labelsize=fontsz)
        ax2.tick_params(labelsize=fontsz)
        ax2b.tick_params(labelsize=fontsz)
        ax3.tick_params(labelsize=fontsz)
        ax3b.tick_params(labelsize=fontsz)
        ax4.tick_params(labelsize=fontsz)
        ax5.tick_params(labelsize=fontsz)
        ax6.tick_params(labelsize=fontsz)

    # smart calculation of nfft dodging large primes
    from obspy.signal.util import _npts2nfft
    nfft = _npts2nfft(npts)
    # Transform data to Frequency domain
    data = np.fft.rfft(data, n=nfft)
    # calculate and apply frequency response,
    # optionally prefilter in frequency domain and/or apply water level
    freq_response, freqs =         response.get_evalresp_response(self.stats.delta, nfft,
                                       output=output, **kwargs)

    if plot:
        ax1.loglog(freqs, np.abs(data), color=color1, zorder=9, label= "Data")
        ax1.legend(loc = "upper left")

    # frequency domain pre-filtering of data spectrum
    # (apply cosine taper in frequency domain)
    if pre_filt:
        freq_domain_taper = cosine_sac_taper(freqs, flimit=pre_filt)
        data *= freq_domain_taper

    if plot:
        try:
            freq_domain_taper
        except NameError:
            freq_domain_taper = np.ones(len(freqs))
        ax1b.semilogx(freqs, freq_domain_taper, color=color2, zorder=10)
        ax1b.set_ylim(-0.05, 1.05)
        ax2.loglog(freqs, np.abs(data), color=color1, zorder=9, label = "Data")
        ax2b.loglog(freqs, np.abs(freq_response), color=color2, zorder=10, label = "Filter")
        ax2b.legend(loc = "upper left")

    if water_level is None:
        # No water level used, so just directly invert the response.
        # First entry is at zero frequency and value is zero, too.
        # Just do not invert the first value (and set to 0 to make sure).
        freq_response[0] = 0.0
        freq_response[1:] = 1.0 / freq_response[1:]
    else:
        # Invert spectrum with specified water level.
        invert_spectrum(freq_response, water_level)

    data *= freq_response
    data[-1] = abs(data[-1]) + 0.0j

    if plot:
        ax3.loglog(freqs, np.abs(data), color=color1, zorder=9, label = "Data")
        ax3b.loglog(freqs, np.abs(freq_response), color=color2, zorder=10, label = "Filter")

    # transform data back into the time domain
    data = np.fft.irfft(data)[0:npts]

    if plot:
        # Oftentimes raises NumPy warnings which we don't want to see.
        with np.errstate(all="ignore"):
            ax6.plot(times, data, color="k")
            plt.subplots_adjust(wspace=0.4)
            
            if plot is True and fig is None:
                plt.show()
            elif plot is True and fig is not None:
                pass
            else:
                plt.savefig(plot)
                plt.close(fig)

    # assign processed data and store processing information
    self.data = data
    return self, np.abs(freq_response)
def remove_response(trace,
                    inventory=None,
                    output="VEL",
                    water_level=60,
                    pre_filt=None,
                    zero_mean=True,
                    taper=True,
                    taper_fraction=0.05,
                    plot=False,
                    fig=None,
                    return_spectra=False,
                    **kwargs):
    """
    Deconvolve instrument response. Based on a method on trace.
    It adds units, which are obviously dependent on the problem.
    axis ax5b is modified to show the pre-filtered trace instead of the
    time domain pre filter.

    Uses the adequate :class:`obspy.core.inventory.response.Response`
    from the provided
    :class:`obspy.core.inventory.inventory.Inventory` data. Raises an
    exception if the response is not present.

    Note that there are two ways to prevent overamplification
    while convolving the inverted instrument spectrum: One possibility is
    to specify a water level which represents a clipping of the inverse
    spectrum and limits amplification to a certain maximum cut-off value
    (`water_level` in dB). The other possibility is to taper the waveform
    data in the frequency domain prior to multiplying with the inverse
    spectrum, i.e. perform a pre-filtering in the frequency domain
    (specifying the four corner frequencies of the frequency taper as a
    tuple in `pre_filt`).

    .. note::

        Any additional kwargs will be passed on to
        :meth:`obspy.core.inventory.response.Response.get_evalresp_response`,
        see documentation of that method for further customization (e.g.
        start/stop stage).

    .. note::

        Using :meth:`~Trace.remove_response` is equivalent to using
        :meth:`~Trace.simulate` with the identical response provided as
        a (dataless) SEED or RESP file and when using the same
        `water_level` and `pre_filt` (and options `sacsim=True` and
        `pitsasim=False` which influence very minor details in detrending
        and tapering).

    .. rubric:: Example

    >>> from obspy import read, read_inventory
    >>> st = read()
    >>> tr = st[0].copy()
    >>> inv = read_inventory()
    >>> tr.remove_response(inventory=inv)  # doctest: +ELLIPSIS
    <...Trace object at 0x...>
    >>> tr.plot()  # doctest: +SKIP

    .. plot::

        from obspy import read, read_inventory
        st = read()
        tr = st[0]
        inv = read_inventory()
        tr.remove_response(inventory=inv)
        tr.plot()

    Using the `plot` option it is possible to visualize the individual
    steps during response removal in the frequency domain to check the
    chosen `pre_filt` and `water_level` options to stabilize the
    deconvolution of the inverted instrument response spectrum:

    >>> from obspy import read, read_inventory
    >>> st = read("/path/to/IU_ULN_00_LH1_2015-07-18T02.mseed")
    >>> tr = st[0]
    >>> inv = read_inventory("/path/to/IU_ULN_00_LH1.xml")
    >>> pre_filt = [0.001, 0.005, 45, 50]
    >>> tr.remove_response(inventory=inv, pre_filt=pre_filt, output="DISP",
    ...                    water_level=60, plot=True)  # doctest: +SKIP
    <...Trace object at 0x...>

    .. plot::

        from obspy import read, read_inventory
        st = read("/path/to/IU_ULN_00_LH1_2015-07-18T02.mseed", "MSEED")
        tr = st[0]
        inv = read_inventory("/path/to/IU_ULN_00_LH1.xml", "STATIONXML")
        pre_filt = [0.001, 0.005, 45, 50]
        output = "DISP"
        tr.remove_response(inventory=inv, pre_filt=pre_filt, output=output,
                           water_level=60, plot=True)

    :type inventory: :class:`~obspy.core.inventory.inventory.Inventory`
        or None.
    :param inventory: Station metadata to use in search for adequate
        response. If inventory parameter is not supplied, the response
        has to be attached to the trace with :meth:`Trace.attach_response`
        beforehand.
    :type output: str
    :param output: Output units. One of:

        ``"DISP"``
            displacement, output unit is meters
        ``"VEL"``
            velocity, output unit is meters/second
        ``"ACC"``
            acceleration, output unit is meters/second**2

    :type water_level: float
    :param water_level: Water level for deconvolution.
    :type pre_filt: list or tuple of four float
    :param pre_filt: Apply a bandpass filter in frequency domain to the
        data before deconvolution. The list or tuple defines
        the four corner frequencies `(f1, f2, f3, f4)` of a cosine taper
        which is one between `f2` and `f3` and tapers to zero for
        `f1 < f < f2` and `f3 < f < f4`.
    :type zero_mean: bool
    :param zero_mean: If `True`, the mean of the waveform data is
        subtracted in time domain prior to deconvolution.
    :type taper: bool
    :param taper: If `True`, a cosine taper is applied to the waveform data
        in time domain prior to deconvolution.
    :type taper_fraction: float
    :param taper_fraction: Taper fraction of cosine taper to use.
    :type plot: bool or str
    :param plot: If `True`, brings up a plot that illustrates how the
        data are processed in the frequency domain in three steps. First by
        `pre_filt` frequency domain tapering, then by inverting the
        instrument response spectrum with or without `water_level` and
        finally showing data with inverted instrument response multiplied
        on it in frequency domain. It also shows the comparison of
        raw/corrected data in time domain. If a `str` is provided then the
        plot is saved to file (filename must have a valid image suffix
        recognizable by matplotlib e.g. '.png').
    """
    limit_numpy_fft_cache()

    from obspy.core.inventory import PolynomialResponseStage
    from obspy.signal.invsim import (cosine_taper, cosine_sac_taper,
                                     invert_spectrum)
    if plot:
        import matplotlib.pyplot as plt

    if (isinstance(inventory, (str, native_str))
            and inventory.upper() in ("DISP", "VEL", "ACC")):
        from obspy.core.util.deprecation_helpers import \
            ObsPyDeprecationWarning
        output = inventory
        inventory = None
        msg = ("The order of optional parameters in method "
               "remove_response has changed. 'output' is not accepted "
               "as first positional argument in the next release.")
        warnings.warn(msg, category=ObsPyDeprecationWarning, stacklevel=3)
    response = trace._get_response(inventory)
    # polynomial response using blockette 62 stage 0
    if not response.response_stages and response.instrument_polynomial:
        coefficients = response.instrument_polynomial.coefficients
        trace.data = np.poly1d(coefficients[::-1])(trace.data)
        return trace

    # polynomial response using blockette 62 stage 1 and no other stages
    if len(response.response_stages) == 1 and \
       isinstance(response.response_stages[0], PolynomialResponseStage):
        # check for gain
        if response.response_stages[0].stage_gain is None:
            msg = 'Stage gain not defined for %s - setting it to 1.0'
            warnings.warn(msg % trace.id)
            gain = 1
        else:
            gain = response.response_stages[0].stage_gain
        coefficients = response.response_stages[0].coefficients[:]
        for i in range(len(coefficients)):
            coefficients[i] /= math.pow(gain, i)
        trace.data = np.poly1d(coefficients[::-1])(trace.data)
        return trace

    # use evalresp
    data = trace.data.astype(np.float64)
    npts = len(data)
    # time domain pre-processing
    if zero_mean:
        data -= data.mean()
    if taper:
        data *= cosine_taper(npts,
                             taper_fraction,
                             sactaper=True,
                             halfcosine=False)

    if plot:
        color1 = "blue"
        color2 = "red"
        bbox = dict(boxstyle="round", fc="w", alpha=1, ec="w")
        bbox1 = dict(boxstyle="round", fc="blue", alpha=0.15)
        bbox2 = dict(boxstyle="round", fc="red", alpha=0.15)
        if fig is None:
            fig = plt.figure(figsize=(14, 10))
        fig.suptitle("{}:\nRemoving the Instrument Response".format(
            str(trace)),
                     fontsize=16)
        ax1 = fig.add_subplot(321)
        ax1b = ax1.twinx()
        ax2 = fig.add_subplot(323, sharex=ax1)
        ax2b = ax2.twinx()
        ax3 = fig.add_subplot(325, sharex=ax1)
        ax3b = ax3.twinx()
        ax4 = fig.add_subplot(322)
        ax5 = fig.add_subplot(324, sharex=ax4)
        ax6 = fig.add_subplot(326, sharex=ax4)
        for ax_ in (ax1, ax2, ax3, ax4, ax5, ax6):
            ax_.grid(zorder=-10)
        ax4.set_title("Waveform")
        # if pre_filt is not None:
        # #     text = 'pre_filt: None'
        # # else:

        output_dict = {
            'DISP': 'Displacement',
            'VEL': 'Velocity',
            'ACC': 'Acceleration'
        }

        # labels - specific to Apollo data
        unit_dict = {
            'DISP': 'DU/m',
            'VEL': 'DU/(m/s)',
            'ACC': r'DU/(m/s$\mathrm{^2}$)'
        }
        unit_inverse_dict = {
            'DISP': 'm/DU',
            'VEL': '(m/s)/DU',
            'ACC': r'(m/s$\mathrm{^2}$)/DU'
        }
        unit_final_dict = {
            'DISP': 'm',
            'VEL': 'm/s',
            'ACC': r'm/s$\mathrm{^2}$'
        }
        raw_unit = 'DU'

        ax1.set_ylabel("Raw [{}]".format(raw_unit), bbox=bbox1, fontsize=16)
        ax1.yaxis.set_label_coords(-0.13, 0.5)
        if pre_filt is not None:
            text = 'Pre-filter parameters:\n [{:.3g}, {:.3g}, {:.3g}, {:.3g}]'.format(
                *pre_filt)
            ax1.text(0.05,
                     0.1,
                     text,
                     ha="left",
                     va="bottom",
                     transform=ax1.transAxes,
                     fontsize="large",
                     bbox=bbox,
                     zorder=5)
            ax1b.set_ylabel("Pre-filter", bbox=bbox2, fontsize=16)
            ax1b.yaxis.set_label_coords(1.14, 0.5)
        ax1.set_title("Spectrum", color='b')
        output_dict = {
            'DISP': 'Displacement',
            'VEL': 'Velocity',
            'ACC': 'Acceleration'
        }
        evalresp_info = "\n".join(
            ['Output: %s' % output_dict[output]] +
            ['%s: %s' % (key, value) for key, value in kwargs.items()])
        ax2.text(0.05,
                 0.1,
                 evalresp_info,
                 ha="left",
                 va="bottom",
                 transform=ax2.transAxes,
                 fontsize="large",
                 zorder=5,
                 bbox=bbox)
        ax2.set_ylabel("Pre-processed [{}]".format(raw_unit),
                       bbox=bbox1,
                       fontsize=16)
        ax2.yaxis.set_label_coords(-0.13, 0.5)
        ax2b.set_ylabel("Instrument\nresponse [{}]".format(unit_dict[output]),
                        bbox=bbox2,
                        fontsize=16)

        ax2b.yaxis.set_label_coords(1.14, 0.5)
        if water_level is not None:
            ax3.text(0.05,
                     0.1,
                     "Water Level: %s" % water_level,
                     ha="left",
                     va="bottom",
                     transform=ax3.transAxes,
                     fontsize="large",
                     zorder=5,
                     bbox=bbox)
        ax3.set_ylabel("Multiplied with inverted\n"
                       "instrument response [{}]".format(
                           unit_final_dict[output]),
                       bbox=bbox1,
                       fontsize=16)
        ax3.yaxis.set_label_coords(-0.13, 0.5)
        if water_level is not None:
            ax3b.set_ylabel("Inverted instrument response,\n"
                            "water level applied [{}]".format(
                                unit_inverse_dict[output]),
                            bbox=bbox2,
                            fontsize=16)
        else:
            ax3b.set_ylabel("Inverted instrument\nresponse [{}]".format(
                unit_inverse_dict[output]),
                            bbox=bbox2,
                            fontsize=16)
        ax3b.yaxis.set_label_coords(1.14, 0.5)

        # xlbl = ax3b.yaxis.get_label()
        # print(xlbl.get_position())
        #
        #

        # xlbl = ax3b.yaxis.get_label()
        # print(xlbl.get_position())
        ax3.set_xlabel("Frequency [Hz]")
        times = trace.times()
        ax4.plot(times, trace.data, color="k")
        ax4.set_ylabel("Raw [{}]".format(raw_unit), fontsize=16)
        ax4.yaxis.set_ticks_position("right")
        ax4.yaxis.set_label_position("right")
        ax4.yaxis.set_label_coords(1.14, 0.5)
        # Ceri
        # ax5.plot(times, data, color="k")

        ax5.set_ylabel("Pre-processed [{}]".format(raw_unit), fontsize=16)
        ax5.yaxis.set_ticks_position("right")
        ax5.yaxis.set_label_position("right")
        ax5.yaxis.set_label_coords(1.14, 0.5)
        ax6.set_ylabel("Response removed [{}]".format(unit_final_dict[output]),
                       fontsize=16)
        ax6.set_xlabel("Time [s]")
        ax6.yaxis.set_ticks_position("right")
        ax6.yaxis.set_label_position("right")
        ax6.yaxis.set_label_coords(1.14, 0.5)

    # smart calculation of nfft dodging large primes
    from obspy.signal.util import _npts2nfft
    nfft = _npts2nfft(npts)
    # Transform data to Frequency domain
    data = np.fft.rfft(data, n=nfft)
    # calculate and apply frequency response,
    # optionally prefilter in frequency domain and/or apply water level
    freq_response, freqs = \
        response.get_evalresp_response(trace.stats.delta, nfft,
                                       output=output, **kwargs)

    if plot:
        ax1.loglog(freqs, np.abs(data), color=color1, zorder=9)

    # frequency domain pre-filtering of data spectrum
    # (apply cosine taper in frequency domain)
    if pre_filt:
        freq_domain_taper = cosine_sac_taper(freqs, flimit=pre_filt)
        data *= freq_domain_taper

    if plot:
        try:
            freq_domain_taper
        except NameError:
            freq_domain_taper = np.ones(len(freqs))
        if pre_filt is not None:
            ax1b.semilogx(freqs, freq_domain_taper, color=color2, zorder=10)
            ax1b.set_ylim(-0.05, 1.05)
        ax2.loglog(freqs, np.abs(data), color=color1, zorder=9)
        ax2b.loglog(freqs, np.abs(freq_response), color=color2, zorder=10)
        # Ceri - transform data back into the time domain - just to view it
        filt_data = np.fft.irfft(data)[0:npts]
        ax5.plot(times, filt_data, color="k")

    if water_level is None:
        # No water level used, so just directly invert the response.
        # First entry is at zero frequency and value is zero, too.
        # Just do not invert the first value (and set to 0 to make sure).
        freq_response[0] = 0.0
        freq_response[1:] = 1.0 / freq_response[1:]
    else:
        # Invert spectrum with specified water level.
        invert_spectrum(freq_response, water_level)

    data *= freq_response
    data[-1] = abs(data[-1]) + 0.0j

    spectra = np.abs(data)
    if plot:
        ax3.loglog(freqs, np.abs(data), color=color1, zorder=9)
        ax3b.loglog(freqs, np.abs(freq_response), color=color2, zorder=10)

    # transform data back into the time domain
    data = np.fft.irfft(data)[0:npts]

    if plot:
        # Oftentimes raises NumPy warnings which we don't want to see.
        with np.errstate(all="ignore"):
            ax6.plot(times, data, color="k")
            plt.subplots_adjust(wspace=0.4)
            if plot is True and fig is None:
                plt.show()
            elif plot is True and fig is not None:
                # Ceri - changed this so that the graph does actually show
                plt.show()
            else:
                plt.savefig(plot)
                plt.close(fig)

    # assign processed data and store processing information
    trace.data = data
    if return_spectra:
        return trace, freqs, spectra
    else:
        return trace
示例#11
0
def _remove_response_trace(trace,
                           inventory=None,
                           output="VEL",
                           pre_filt=None,
                           zero_mean=True,
                           taper=True,
                           taper_fraction=0.05,
                           **kwargs):
    response = trace._get_response(inventory)
    # polynomial response using blockette 62 stage 0
    if not response.response_stages and response.instrument_polynomial:
        coefficients = response.instrument_polynomial.coefficients
        trace.data = np.poly1d(coefficients[::-1])(trace.data)
        return trace

        # polynomial response using blockette 62 stage 1 and no other stages
    if len(response.response_stages) == 1 and isinstance(
            response.response_stages[0], PolynomialResponseStage):
        # check for gain
        if response.response_stages[0].stage_gain is None:
            msg = 'Stage gain not defined for %s - setting it to 1.0'
            warnings.warn(msg % trace.id)
            gain = 1
        else:
            gain = response.response_stages[0].stage_gain
        coefficients = response.response_stages[0].coefficients[:]
        for i in range(len(coefficients)):
            coefficients[i] /= np.pow(gain, i)
        trace.data = np.poly1d(coefficients[::-1])(trace.data)
        return trace

        # use evalresp
    data = trace.data.astype(np.float64)
    npts = len(data)
    # time domain pre-processing
    if zero_mean:
        data -= data.mean()
    if taper:
        data *= cosine_taper(npts,
                             taper_fraction,
                             sactaper=True,
                             halfcosine=False)

    nfft = _npts2nfft(npts)
    # Transform data to Frequency domain
    data = np.fft.rfft(data, n=nfft)
    # calculate and apply frequency response,
    # optionally prefilter in frequency domain and/or apply water level
    freq_response, freqs = response.get_evalresp_response(trace.stats.delta,
                                                          nfft,
                                                          output=output,
                                                          **kwargs)

    if pre_filt:
        freq_domain_taper = cosine_sac_taper(freqs, flimit=pre_filt)
        data *= freq_domain_taper

    freq_response[0] = 0.0
    freq_response[1:] = 1.0 / freq_response[1:]

    data *= freq_response
    data[-1] = abs(data[-1]) + 0.0j

    # transform data back into the time domain
    data = np.fft.irfft(data)[0:npts]

    # assign processed data and store processing information
    trace.data = data
    return trace
Original plot from obspy website.

Modified by Lucas Sawade, June 2019

"""

import matplotlib.pylab as plt
import numpy as np
from obspy.signal.invsim import cosine_sac_taper

plt.figure(figsize=(10, 3))

freqs = np.logspace(-2.01, 0, 2000)

plt.vlines([0.015, 0.03, 0.2, 0.4], -0.1, 1.3, color="#89160F")
plt.semilogx(freqs, cosine_sac_taper(freqs, (0.015, 0.03, 0.2, 0.4)),
             lw=2, color="#4C72B0")

props = {
    "bbox": dict(facecolor='white', edgecolor="0.5",
         boxstyle="square,pad=0.2"),
    "va": "top", "ha": "center", "color": "#89160F",
    "size": "large"}

plt.text(0.015, 1.25, "f1", **props)
plt.text(0.03, 1.25, "f2", **props)
plt.text(0.2, 1.25, "f3", **props)
plt.text(0.4, 1.25, "f4", **props)

plt.xlim(freqs[0], freqs[-1])
plt.ylim(-0.1, 1.3)
示例#13
0
def process_synthetics(st, freqmax, freqmin):
    f2 = 0.9 * freqmin
    f3 = 1.1 * freqmax
    f1 = 0.5 * f2
    f4 = 2.0 * f3
    pre_filt = (f1, f2, f3, f4)

    # Detrend and taper.
    st.detrend("linear")
    st.detrend("demean")
    st.taper(max_percentage=0.05, type="hann")

    # Perform a frequency domain taper like during the response removal
    # just without an actual response...
    # See function remove_response in trace.py from obspy.
    for tr in st:

        # Assuming displacement seismograms
        tr.differentiate()

        data = tr.data.astype(np.float64)
        orig_len = len(data)

        # smart calculation of nfft dodging large primes
        # noinspection PyProtectedMember
        from obspy.signal.util import _npts2nfft
        nfft = _npts2nfft(len(data))

        fy = 1.0 / (tr.stats.delta * 2.0)
        freqs = np.linspace(0, fy, nfft // 2 + 1)

        # Transform data to Frequency domain
        data = np.fft.rfft(data, n=nfft)
        # noinspection PyTypeChecker
        data *= cosine_sac_taper(freqs, flimit=pre_filt)
        data[-1] = abs(data[-1]) + 0.0j

        # transform data back into the time domain
        data = np.fft.irfft(data)[0:orig_len]

        # assign processed data and store processing information
        tr.data = data

        tr.detrend("linear")
        tr.detrend("demean")
        tr.taper(0.05, type="cosine")
        tr.filter("bandpass",
                  freqmin=freqmin,
                  freqmax=freqmax,
                  corners=3,
                  zerophase=True)
        tr.detrend("linear")
        tr.detrend("demean")
        tr.taper(0.05, type="cosine")
        tr.filter("bandpass",
                  freqmin=freqmin,
                  freqmax=freqmax,
                  corners=3,
                  zerophase=True)

    return st