Beispiel #1
0
def resp_spectrum(source, resp_file, downsamp_freq):
    '''
    remove the instrument response with response spectrum from evalresp.
    the response spectrum is evaluated based on RESP/PZ files and then 
    inverted using obspy function of invert_spectrum. 
    '''
    #--------resp_file is the inverted spectrum response---------
    respz = np.load(resp_file)
    nrespz = respz[1][:]
    spec_freq = max(respz[0])

    #-------on current trace----------
    nfft = _npts2nfft(source.stats.npts)
    sps = source.stats.sample_rate

    #---------do the interpolation if needed--------
    if spec_freq < 0.5 * sps:
        raise ValueError(
            'spectrum file has peak freq smaller than the data, abort!')
    else:
        indx = np.where(respz[0] <= 0.5 * sps)
        nfreq = np.linspace(0, 0.5 * sps, nfft)
        nrespz = np.interp(nfreq, respz[0][indx], respz[1][indx])

    #----do interpolation if necessary-----
    source_spect = np.fft.rfft(source.data, n=nfft)

    #-----nrespz is inversed (water-leveled) spectrum-----
    source_spect *= nrespz
    source.data = np.fft.irfft(source_spect)[0:source.stats.npts]

    return source
Beispiel #2
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
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 *= c_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
Beispiel #4
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 type(tr) != obspy.Trace:
        raise ValueError("First Argument should be trace: %s" % type(tr))

    if not check_array_order(pre_filt):
        raise ValueError("Frequency band should be in ascending order: %s"
                         % pre_filt)

    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 *= c_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
Beispiel #5
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 not check_array_order(pre_filt):
        raise ValueError("Frequency band should be in ascending order: %s" %
                         pre_filt)

    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 *= c_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
Beispiel #6
0
    def process_function(st, inv):
        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...
        for tr in st:
            data = tr.data.astype(np.float64)

            # smart calculation of nfft dodging large primes
            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)
            data *= c_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

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

        st.interpolate(sampling_rate=sampling_rate,
                       starttime=starttime,
                       npts=npts)

        components = [tr.stats.channel[-1] for tr in st]
        if "N" in components and "E" in components:
            station_latitude = inv[0][0].latitude
            station_longitude = inv[0][0].longitude
            _, baz, _ = gps2DistAzimuth(station_latitude, station_longitude,
                                        event_latitude, event_longitude)

            st.rotate(method="NE->RT", back_azimuth=baz)

        # Convert to single precision to save space.
        for tr in st:
            tr.data = np.require(tr.data, dtype="float32")

        return st
    def process_function(st, inv):
        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...
        for tr in st:
            data = tr.data.astype(np.float64)

            # smart calculation of nfft dodging large primes
            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)
            data *= c_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

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

        st.interpolate(sampling_rate=sampling_rate, starttime=starttime,
                       npts=npts)

        components = [tr.stats.channel[-1] for tr in st]
        if "N" in components and "E" in components:
            station_latitude = inv[0][0].latitude
            station_longitude = inv[0][0].longitude
            _, baz, _ = gps2DistAzimuth(station_latitude, station_longitude,
                                        event_latitude, event_longitude)

            st.rotate(method="NE->RT", back_azimuth=baz)

        # Convert to single precision to save space.
        for tr in st:
            tr.data = np.require(tr.data, dtype="float32")

        return st
    def process_synthetics(self):

        lowpass_freq = 1 / 60.
        highpass_freq = 1 / 120.

        freqmin = highpass_freq
        freqmax = lowpass_freq

        f2 = highpass_freq
        f3 = lowpass_freq
        f1 = 0.8 * f2
        f4 = 1.2 * f3
        pre_filt = (f1, f2, f3, f4)

        self.tr.differentiate()

        # self.tr.data = convolve_stf(self.tr.data)

        self.tr.detrend("linear")
        self.tr.detrend("demean")
        self.tr.taper(max_percentage=0.05, type="hann")

        # Perform a frequency domain taper like during the response removal
        # just without an actual response...

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

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

        fy = 1.0 / (self.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 *= c_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
        self.tr.data = data
Beispiel #9
0
def sync_remove_response(pre_filt, st):
    """
    mimic obspy.remove_response, but only do the frequency taper
    """
    for trace in st:
        data = trace.data.astype(np.float64)
        npts = len(data)
        nfft = _npts2nfft(npts)
        data = np.fft.rfft(data, n=nfft)
        t_samp = trace.stats.delta
        fy = 1 / (t_samp * 2.0)
        freqs = np.linspace(0, fy, nfft // 2 + 1).astype(np.float64)
        freq_domain_taper = obspy.signal.invsim.cosine_sac_taper(
            freqs, flimit=pre_filt)
        data *= freq_domain_taper
        data = np.fft.irfft(data)[0:npts]
        trace.data = data

    return st
Beispiel #10
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 *= c_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:tr.stats.npts]
        # assign processed data and store processing information
        tr.data = data
    return stream
Beispiel #11
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
Beispiel #12
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
Beispiel #14
0
def convolve_stf(stf: np.array, data: np.array, nfft=None):
    # conv = np.convolve(stf, data, mode="same")
    from scipy import signal

    # recovered, remainder = signal.deconvolve(data, stf)

    from obspy.signal.util import _npts2nfft
    import math

    if nfft is None:
        nfft = _npts2nfft(npts=len(data))
    stf_conv_f = np.fft.rfft(stf, n=nfft)

    # Apply a 5 percent, at least 5 samples taper at the end.
    # The first sample is guaranteed to be zero in any case.
    tlen = max(int(math.ceil(0.05 * len(data))), 5)
    taper = np.ones_like(data)
    taper[-tlen:] = signal.hann(tlen * 2)[tlen:]
    dataf = np.fft.rfft(taper * data, n=nfft)

    f = stf_conv_f
    recovered = np.fft.irfft(dataf * f)[:nfft]
    return recovered
Beispiel #15
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)
Beispiel #16
0
def process_synt(datadir, station, event = None, stationxml_dir = None, period_band = None, npts=None,
                sampling_rate=None, output_dir=None, suffix=None):

    # ensemble the stream
    #station_list = generate_station_list(datadir)
    #for station in station_list:
    zdatafile = station[0] + "." + station[1] + ".MXZ.sem.sac"
    ndatafile = station[0] + "." + station[1] + ".MXN.sem.sac"
    edatafile = station[0] + "." + station[1] + ".MXE.sem.sac"
    zpath = os.path.join(datadir, zdatafile)
    npath = os.path.join(datadir, ndatafile)
    epath = os.path.join(datadir, edatafile)
    st = read(zpath)
    st2 = read(npath)
    st3 = read(epath)
    #print st
    #print st2
    st.append(st2[0])
    st.append(st3[0])

    #print "\nProcessing file: %s" %filename
    # interpolation value
    #npts = 3630
    #sampling_rate = 1.0  # Hz
    min_period = period_band[0]
    max_period = period_band[1]
    f2 = 1.0 / max_period
    f3 = 1.0 / min_period
    f1 = 0.8 * f2
    f4 = 1.2 * f3
    pre_filt = (f1, f2, f3, f4)

    # fetch event information
    event_name = get_event_name(event)
    short_event_name = adjust_event_name(event_name)

    origin = event.preferred_origin() or event.origins[0]
    event_latitude = origin.latitude
    event_longitude = origin.longitude
    event_time = origin.time
    event_depth = origin.depth
    starttime = event_time
    print "event_time:",event_time

    #output_path = os.path.join(output_dir, short_event_name)
    if not os.path.exists(output_dir):
        os.makedirs(output_dir)

    nrecords = len(st)
    # processing
    for i, tr in enumerate(st):
        #get component name
        cmpname = tr.stats.channel
        #print tr.stats
        #tr.interpolate(sampling_rate=sampling_rate, starttime=starttime,
        #                npts=npts)
        #tr.decimate(4, strict_length=False,no_filter=True)
        print "Processing %d of total %d" %(i+1, nrecords)
        print "Detrend, Demean and Taper..."
        tr.detrend("linear")
        tr.detrend("demean")
        tr.taper(max_percentage=0.05, type="hann")
        #print "response show:"
        #print tr.stats.response


        # Perform a frequency domain taper like during the response removal
        # just without an actual response...
        #for tr in st:
        print "Filtering..."
        data = tr.data.astype(np.float64)

        # smart calculation of nfft dodging large primes
        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)
        data *= c_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

        print "Detrend, Demean and Taper again..."
        tr.detrend("linear")
        tr.detrend("demean")
        tr.taper(max_percentage=0.05, type="hann")
        print "Interpolate..."
        try:
            tr.interpolate(sampling_rate=sampling_rate, starttime=starttime,
                           npts=npts)
        except:
            print "Error"
            return

    # rotate
    station_latitude = st[0].stats.sac['stla']
    station_longitude = st[0].stats.sac['stlo']
    event_latitude = st[0].stats.sac['evla']
    event_longitude = st[0].stats.sac['evlo']
    #print station_latitude, station_longitude
    #print station_latitude, station_longitude
    _, baz, _ = gps2DistAzimuth(station_latitude, station_longitude,
                                    event_latitude, event_longitude)
    print st
    st.rotate(method="NE->RT", back_azimuth=baz)
    # write out
    print st
    for tr in st:
        comp = tr.stats.channel
        outfn = station[0] + "." + station[1] + "." + comp + ".sac"
        if suffix is not None and suffix != "":
            outfn = outfn + "." + suffix
        outpath = os.path.join(output_dir, outfn)
        print "file saved:", outpath
        tr.stats.sac['b'] = 0
        tr.stats.sac['iztype'] = 9
        tr.write(outpath, format="SAC")
Beispiel #17
0
#-----directory to station list and response files--------
#resp_dir = '/Users/chengxin/Documents/Harvard/Kanto_basin/instrument/resp_all'
#locations = '/Users/chengxin/Documents/Harvard/Kanto_basin/code/KANTO/locations.txt'
resp_dir = '/Users/chengxin/Documents/Harvard/Kanto_basin/code/KANTO/instrument/resp_4types'
locations = '/Users/chengxin/Documents/Harvard/Kanto_basin/code/KANTO/instrument/resp_4types/station.lst'

#-----common variables for extracting resp using evalresp function------
water_level = 60
prefilt = [0.04, 0.05, 2, 3]
downsamp_freq = 20
dt = 1 / downsamp_freq
cc_len = 3600
step = 1800
npts = cc_len * downsamp_freq * 24
Nfft = _npts2nfft(npts)
tdate = obspy.UTCDateTime("2011-11-3T16:30:00.000")

#-----read the station list------
locs = pd.read_csv(locations)
nsta = len(locs)

for ii in range(nsta):
    station = locs.iloc[ii]['station']
    network = locs.iloc[ii]['network']
    print('work on station ' + station)

    tfiles = glob.glob(os.path.join(resp_dir, 'RESP.' + station + '*'))
    if len(tfiles) == 0:
        print('cannot find resp files for station ' + station)
Beispiel #18
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
Beispiel #19
0
                trE.data=simulate_seismometer(trE.data, 100, paz_remove=None, paz_simulate=None, remove_sensitivity=True, simulate_sensitivity=True, water_level=wt, taper=True, taper_fraction=tpf, pre_filt=pre_filt, seedresp=seedrespN, nfft_pow2=False)
                #trE.simulate(paz_remove=None, pre_filt=pre_filt, seedresp=seedrespE)
            
    #          trN.plot(outfile='espectros/Swave_%s_%s.png' % (units,trN.stats.station))
                #trN.plot()
#==================================================================================================================            
#=========    #Calculando la transformada de fourier   ==============================================================
#==================================================================================================================            
                #spectrum=mper(trN.data,200,np.size(trN.data))
                n=np.size(trN.data)
                #dt=0.01
                t=np.linspace(0,n-1,n)*dt
                df=1/(n*dt)
                freq=np.linspace(0,(n+1)/2,(n+1)/2)*df
                
                nfft = _npts2nfft(n)
                specN=np.fft.rfft(trN.data,n=nfft)
                specE=np.fft.rfft(trE.data,n=nfft)
            
                Hspec=np.sqrt((specN**2+specE**2)/2)
            
                smooth_Hspec=smooth(Hspec,smth)
                
                #Graficando log-espectro
              #  plt.plot(np.log10(freq),np.log10(abs(smooth_Hspec)),'k')
              #  plt.title('Espectro de desplazamiento onda S '+trN.stats.station)
               # plt.xlim(0,2)
               # plt.ylabel('log(A)')
               # plt.xlabel('log(frecuencia [Hz])')
               # plt.savefig("espectros/espectroH_SMOOTH10_%s.png" % (trN.stats.station))
                #plt.show()            
Beispiel #20
0
'''

hfile = '/Users/chengxin/Documents/Harvard/Kanto_basin/code/KANTO/CCF_C3/2010_01_11.h5'
with pyasdf.ASDFDataSet(hfile, mode='r') as ds:
    data_types = ds.auxiliary_data.list()

    #-----take the first data_type-----
    data_type = data_types[2]
    paths = ds.auxiliary_data[data_type].list()
    path = paths[2]

    data = ds.auxiliary_data[data_type][path].data[:]
    npts = len(data)

    #----do fft----
    nfft2 = _npts2nfft(npts)
    nfft1 = int(next_fast_len(npts))
    nfft3 = int(next_fast_len(npts * 2 + 1))
    print('nfft1 and nfft2 %d %d' % (nfft1, nfft2))

    t0 = time.time()
    spec1 = scipy.fftpack.fft(data, nfft1)
    wave1 = scipy.fftpack.ifft(spec1, nfft1)
    t1 = time.time()
    spec2 = np.fft.rfft(data, nfft2)
    wave2 = np.fft.irfft(spec2, nfft2)
    t2 = time.time()
    spec3 = scipy.fftpack.fft(data, nfft3)
    wave3 = scipy.fftpack.ifft(spec3, nfft3)
    t3 = time.time()
    print('fft and rfft takes %f s, %f s and %f s' %
Beispiel #21
0
def simulate_seismometer(
        data, samp_rate, paz_remove=None, paz_simulate=None,
        remove_sensitivity=True, simulate_sensitivity=True, water_level=600.0,
        zero_mean=True, taper=True, taper_fraction=0.05, pre_filt=None,
        seedresp=None, nfft_pow2=False, pitsasim=True, sacsim=False,
        shsim=False):
    """
    Simulate/Correct seismometer.

    :type data: NumPy :class:`~numpy.ndarray`
    :param data: Seismogram, detrend before hand (e.g. zero mean)
    :type samp_rate: float
    :param samp_rate: Sample Rate of Seismogram
    :type paz_remove: dict, None
    :param paz_remove: Dictionary containing keys 'poles', 'zeros', 'gain'
        (A0 normalization factor). poles and zeros must be a list of complex
        floating point numbers, gain must be of type float. Poles and Zeros are
        assumed to correct to m/s, SEED convention. Use None for no inverse
        filtering.
    :type paz_simulate: dict, None
    :param paz_simulate: Dictionary containing keys 'poles', 'zeros', 'gain'.
        Poles and zeros must be a list of complex floating point numbers, gain
        must be of type float. Or None for no simulation.
    :type remove_sensitivity: bool
    :param remove_sensitivity: Determines if data is divided by
        `paz_remove['sensitivity']` to correct for overall sensitivity of
        recording instrument (seismometer/digitizer) during instrument
        correction.
    :type simulate_sensitivity: bool
    :param simulate_sensitivity: Determines if data is multiplied with
        `paz_simulate['sensitivity']` to simulate overall sensitivity of
        new instrument (seismometer/digitizer) during instrument simulation.
    :type water_level: float
    :param water_level: Water_Level for spectrum to simulate
    :type zero_mean: bool
    :param zero_mean: If true the mean of the data is subtracted
    :type taper: bool
    :param taper: If true a cosine taper is applied.
    :type taper_fraction: float
    :param taper_fraction: Taper fraction of cosine taper to use
    :type pre_filt: list or tuple of floats
    :param pre_filt: Apply a bandpass filter to the data trace 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 seedresp: dict, None
    :param seedresp: Dictionary contains keys 'filename', 'date', 'units'.
        'filename' is the path to a RESP-file generated from a dataless SEED
        volume (or a file like object with RESP information);
        'date' is a :class:`~obspy.core.utcdatetime.UTCDateTime` object for the
        date that the response function should be extracted for (can be omitted
        when calling simulate() on Trace/Stream. the Trace's starttime will
        then be used);
        'units' defines the units of the response function.
        Can be either 'DIS', 'VEL' or 'ACC'.
    :type nfft_pow2: bool
    :param nfft_pow2: Number of frequency points to use for FFT. If True,
        the exact power of two is taken (default in PITSA). If False the
        data are not zero-padded to the next power of two which makes a
        slower FFT but is then much faster for e.g. evalresp which scales
        with the FFT points.
    :type pitsasim: bool
    :param pitsasim: Choose parameters to match
        instrument correction as done by PITSA.
    :type sacsim: bool
    :param sacsim: Choose parameters to match
        instrument correction as done by SAC.
    :type shsim: bool
    :param shsim: Choose parameters to match
        instrument correction as done by Seismic Handler.
    :return: The corrected data are returned as :class:`numpy.ndarray` float64
        array. float64 is chosen to avoid numerical instabilities.

    This function works in the frequency domain, where nfft is the next power
    of len(data) to avoid wrap around effects during convolution. The inverse
    of the frequency response of the seismometer (``paz_remove``) is
    convolved with the spectrum of the data and with the frequency response
    of the seismometer to simulate (``paz_simulate``). A 5% cosine taper is
    taken before simulation. The data must be detrended (e.g.) zero mean
    beforehand. If paz_simulate=None only the instrument correction is done.
    In the latter case, a broadband filter can be applied to the data trace
    using pre_filt. This restricts the signal to the valid frequency band and
    thereby avoids artifacts due to amplification of frequencies outside of the
    instrument's passband (for a detailed discussion see
    *Of Poles and Zeros*, F. Scherbaum, Kluwer Academic Publishers).

    .. versionchanged:: 0.5.1
        The default for `remove_sensitivity` and `simulate_sensitivity` has
        been changed to ``True``. Old deprecated keyword arguments `paz`,
        `inst_sim`, `no_inverse_filtering` have been removed.
    """
    # Checking the types
    if not paz_remove and not paz_simulate and not seedresp:
        msg = "Neither inverse nor forward instrument simulation specified."
        raise TypeError(msg)

    for d in [paz_remove, paz_simulate]:
        if d is None:
            continue
        for key in ['poles', 'zeros', 'gain']:
            if key not in d:
                raise KeyError("Missing key: %s" % key)
    # Translated from PITSA: spr_resg.c
    delta = 1.0 / samp_rate
    #
    ndat = len(data)
    data = data.astype(np.float64)
    if zero_mean:
        data -= data.mean()
    if taper:
        if sacsim:
            data *= cosine_taper(ndat, taper_fraction,
                                 sactaper=sacsim, halfcosine=False)
        else:
            data *= cosine_taper(ndat, taper_fraction)
    # The number of points for the FFT has to be at least 2 * ndat (in
    # order to prohibit wrap around effects during convolution) cf.
    # Numerical Recipes p. 429 calculate next power of 2.
    if nfft_pow2:
        nfft = util.next_pow_2(2 * ndat)
    # evalresp scales directly with nfft, therefore taking the next power of
    # two has a greater negative performance impact than the slow down of a
    # not power of two in the FFT
    else:
        nfft = _npts2nfft(ndat)
    # Transform data in Fourier domain
    data = np.fft.rfft(data, n=nfft)
    # Inverse filtering = Instrument correction
    if paz_remove:
        freq_response, freqs = paz_to_freq_resp(
            paz_remove['poles'], paz_remove['zeros'], paz_remove['gain'],
            delta, nfft, freq=True)
    if seedresp:
        freq_response, freqs = evalresp(delta, nfft, seedresp['filename'],
                                        seedresp['date'],
                                        units=seedresp['units'], freq=True,
                                        network=seedresp['network'],
                                        station=seedresp['station'],
                                        locid=seedresp['location'],
                                        channel=seedresp['channel'])
        if not remove_sensitivity:
            msg = "remove_sensitivity is set to False, but since seedresp " + \
                  "is selected the overall sensitivity will be corrected " + \
                  " for anyway!"
            warnings.warn(msg)
    if paz_remove or seedresp:
        if pre_filt:
            # make cosine taper
            fl1, fl2, fl3, fl4 = pre_filt
            if sacsim:
                cos_win = cosine_sac_taper(freqs, flimit=(fl1, fl2, fl3, fl4))
            else:
                cos_win = cosine_taper(freqs.size, freqs=freqs,
                                       flimit=(fl1, fl2, fl3, fl4))
            data *= cos_win
        invert_spectrum(freq_response, water_level)
        data *= freq_response
        del freq_response
    # Forward filtering = Instrument simulation
    if paz_simulate:
        data *= paz_to_freq_resp(paz_simulate['poles'], paz_simulate['zeros'],
                                 paz_simulate['gain'], delta, nfft)

    data[-1] = abs(data[-1]) + 0.0j
    # transform data back into the time domain
    data = np.fft.irfft(data)[0:ndat]
    if pitsasim:
        # linear detrend
        data = simple_detrend(data)
    if shsim:
        # detrend using least squares
        data = scipy.signal.detrend(data, type="linear")
    # correct for involved overall sensitivities
    if paz_remove and remove_sensitivity and not seedresp:
        data /= paz_remove['sensitivity']
    if paz_simulate and simulate_sensitivity:
        data *= paz_simulate['sensitivity']
    return data
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
Beispiel #23
0
                    if source.stats.npts != downsamp_freq * 24 * cc_len:
                        print(
                            'Moving to next: extraced response file does not match the sac length'
                        )
                        continue

                    #-----load the instrument response nyc file-----
                    resp_file = os.path.join(resp_dir,
                                             'resp.' + station + '.npy')
                    respz = np.load(resp_file)
                    if not os.path.isfile(resp_file):
                        print("no instrument response for " + station)
                        continue

                    #----------do fft now----------
                    nfft = _npts2nfft(source.stats.npts)
                    source_spect = np.fft.rfft(source.data, n=nfft)

                    fy = 1 / (dt * 2.0)
                    freq = np.linspace(0, fy, nfft // 2 + 1)

                    #-----apply a cosine taper to target freq-----
                    #cos_win = cosine_sac_taper(freq, flimit=pre_filt)
                    #source_spect *=cos_win
                    source_spect *= respz
                    source.data = np.fft.irfft(
                        source_spect)[0:source.stats.npts]
                    source.data = bandpass(source.data,
                                           freqmin,
                                           freqmax,
                                           downsamp_freq,
Beispiel #24
0
def simulate_seismometer(data,
                         samp_rate,
                         paz_remove=None,
                         paz_simulate=None,
                         remove_sensitivity=True,
                         simulate_sensitivity=True,
                         water_level=600.0,
                         zero_mean=True,
                         taper=True,
                         taper_fraction=0.05,
                         pre_filt=None,
                         seedresp=None,
                         nfft_pow2=False,
                         pitsasim=True,
                         sacsim=False,
                         shsim=False):
    """
    Simulate/Correct seismometer.

    :type data: NumPy :class:`~numpy.ndarray`
    :param data: Seismogram, detrend before hand (e.g. zero mean)
    :type samp_rate: float
    :param samp_rate: Sample Rate of Seismogram
    :type paz_remove: dict, None
    :param paz_remove: Dictionary containing keys 'poles', 'zeros', 'gain'
        (A0 normalization factor). poles and zeros must be a list of complex
        floating point numbers, gain must be of type float. Poles and Zeros are
        assumed to correct to m/s, SEED convention. Use None for no inverse
        filtering.
    :type paz_simulate: dict, None
    :param paz_simulate: Dictionary containing keys 'poles', 'zeros', 'gain'.
        Poles and zeros must be a list of complex floating point numbers, gain
        must be of type float. Or None for no simulation.
    :type remove_sensitivity: bool
    :param remove_sensitivity: Determines if data is divided by
        `paz_remove['sensitivity']` to correct for overall sensitivity of
        recording instrument (seismometer/digitizer) during instrument
        correction.
    :type simulate_sensitivity: bool
    :param simulate_sensitivity: Determines if data is multiplied with
        `paz_simulate['sensitivity']` to simulate overall sensitivity of
        new instrument (seismometer/digitizer) during instrument simulation.
    :type water_level: float
    :param water_level: Water_Level for spectrum to simulate
    :type zero_mean: bool
    :param zero_mean: If true the mean of the data is subtracted
    :type taper: bool
    :param taper: If true a cosine taper is applied.
    :type taper_fraction: float
    :param taper_fraction: Taper fraction of cosine taper to use
    :type pre_filt: list or tuple of floats
    :param pre_filt: Apply a bandpass filter to the data trace 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 seedresp: dict, None
    :param seedresp: Dictionary contains keys 'filename', 'date', 'units'.
        'filename' is the path to a RESP-file generated from a dataless SEED
        volume (or a file like object with RESP information);
        'date' is a :class:`~obspy.core.utcdatetime.UTCDateTime` object for the
        date that the response function should be extracted for (can be omitted
        when calling simulate() on Trace/Stream. the Trace's starttime will
        then be used);
        'units' defines the units of the response function.
        Can be either 'DIS', 'VEL' or 'ACC'.
    :type nfft_pow2: bool
    :param nfft_pow2: Number of frequency points to use for FFT. If True,
        the exact power of two is taken (default in PITSA). If False the
        data are not zero-padded to the next power of two which makes a
        slower FFT but is then much faster for e.g. evalresp which scales
        with the FFT points.
    :type pitsasim: bool
    :param pitsasim: Choose parameters to match
        instrument correction as done by PITSA.
    :type sacsim: bool
    :param sacsim: Choose parameters to match
        instrument correction as done by SAC.
    :type shsim: bool
    :param shsim: Choose parameters to match
        instrument correction as done by Seismic Handler.
    :return: The corrected data are returned as :class:`numpy.ndarray` float64
        array. float64 is chosen to avoid numerical instabilities.

    This function works in the frequency domain, where nfft is the next power
    of len(data) to avoid wrap around effects during convolution. The inverse
    of the frequency response of the seismometer (``paz_remove``) is
    convolved with the spectrum of the data and with the frequency response
    of the seismometer to simulate (``paz_simulate``). A 5% cosine taper is
    taken before simulation. The data must be detrended (e.g.) zero mean
    beforehand. If paz_simulate=None only the instrument correction is done.
    In the latter case, a broadband filter can be applied to the data trace
    using pre_filt. This restricts the signal to the valid frequency band and
    thereby avoids artifacts due to amplification of frequencies outside of the
    instrument's passband (for a detailed discussion see
    *Of Poles and Zeros*, F. Scherbaum, Kluwer Academic Publishers).

    .. versionchanged:: 0.5.1
        The default for `remove_sensitivity` and `simulate_sensitivity` has
        been changed to ``True``. Old deprecated keyword arguments `paz`,
        `inst_sim`, `no_inverse_filtering` have been removed.
    """
    # Checking the types
    if not paz_remove and not paz_simulate and not seedresp:
        msg = "Neither inverse nor forward instrument simulation specified."
        raise TypeError(msg)

    for d in [paz_remove, paz_simulate]:
        if d is None:
            continue
        for key in ['poles', 'zeros', 'gain']:
            if key not in d:
                raise KeyError("Missing key: %s" % key)
    # Translated from PITSA: spr_resg.c
    delta = 1.0 / samp_rate
    #
    ndat = len(data)
    data = data.astype(np.float64)
    if zero_mean:
        data -= data.mean()
    if taper:
        if sacsim:
            data *= cosine_taper(ndat,
                                 taper_fraction,
                                 sactaper=sacsim,
                                 halfcosine=False)
        else:
            data *= cosine_taper(ndat, taper_fraction)
    # The number of points for the FFT has to be at least 2 * ndat (in
    # order to prohibit wrap around effects during convolution) cf.
    # Numerical Recipes p. 429 calculate next power of 2.
    if nfft_pow2:
        nfft = util.next_pow_2(2 * ndat)
    # evalresp scales directly with nfft, therefore taking the next power of
    # two has a greater negative performance impact than the slow down of a
    # not power of two in the FFT
    else:
        nfft = _npts2nfft(ndat)
    # Transform data in Fourier domain
    data = np.fft.rfft(data, n=nfft)
    # Inverse filtering = Instrument correction
    if paz_remove:
        freq_response, freqs = paz_to_freq_resp(paz_remove['poles'],
                                                paz_remove['zeros'],
                                                paz_remove['gain'],
                                                delta,
                                                nfft,
                                                freq=True)
    if seedresp:
        freq_response, freqs = evalresp(delta,
                                        nfft,
                                        seedresp['filename'],
                                        seedresp['date'],
                                        units=seedresp['units'],
                                        freq=True,
                                        network=seedresp['network'],
                                        station=seedresp['station'],
                                        locid=seedresp['location'],
                                        channel=seedresp['channel'])
        if not remove_sensitivity:
            msg = "remove_sensitivity is set to False, but since seedresp " + \
                  "is selected the overall sensitivity will be corrected " + \
                  " for anyway!"
            warnings.warn(msg)
    if paz_remove or seedresp:
        if pre_filt:
            # make cosine taper
            fl1, fl2, fl3, fl4 = pre_filt
            if sacsim:
                cos_win = cosine_sac_taper(freqs, flimit=(fl1, fl2, fl3, fl4))
            else:
                cos_win = cosine_taper(freqs.size,
                                       freqs=freqs,
                                       flimit=(fl1, fl2, fl3, fl4))
            data *= cos_win
        invert_spectrum(freq_response, water_level)
        data *= freq_response
        del freq_response
    # Forward filtering = Instrument simulation
    if paz_simulate:
        data *= paz_to_freq_resp(paz_simulate['poles'], paz_simulate['zeros'],
                                 paz_simulate['gain'], delta, nfft)

    data[-1] = abs(data[-1]) + 0.0j
    # transform data back into the time domain
    data = np.fft.irfft(data)[0:ndat]
    if pitsasim:
        # linear detrend
        data = simple_detrend(data)
    if shsim:
        # detrend using least squares
        data = scipy.signal.detrend(data, type="linear")
    # correct for involved overall sensitivities
    if paz_remove and remove_sensitivity and not seedresp:
        data /= paz_remove['sensitivity']
    if paz_simulate and simulate_sensitivity:
        data *= paz_simulate['sensitivity']
    return data
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