Пример #1
0
def test_bng_waveforms(ind=0):

    has_data, bng = test_add_data(ind)
    if not has_data:
        pass

    bng.calc(1., 15., [-2., 5.])
    stream = bng.data.copy()
    stream.filter('bandpass', freqmin=0.01, freqmax=0.04, zerophase=True)
    trN = stream.select(component='1')[0].copy()
    trE = stream.select(component='2')[0].copy()
    azim = bng.meta.phi

    N, E = rotate_rt_ne(trN.data, trE.data, azim)
    trN.data = -1. * N
    trE.data = -1. * E

    # Update stats of streams
    trN.stats.channel = trN.stats.channel[:-1] + 'N'
    trE.stats.channel = trE.stats.channel[:-1] + 'E'

    # Store corrected traces in new stream and rotate to
    # R, T using back-azimuth
    stcorr = Stream(traces=[trN, trE])
    stcorr.rotate('NE->RT', back_azimuth=bng.meta.baz)

    # Merge original and corrected streams
    st = stream + stcorr
    plot = plotting.plot_bng_waveforms(bng, st, 15., [-2., 5.])
Пример #2
0
 def test_rotate_ne_rt_ne(self):
     """
     Rotating there and back with the same back-azimuth should not change
     the data.
     """
     # load the data
     with gzip.open(os.path.join(self.path, 'rjob_20051006_n.gz')) as f:
         data_n = np.loadtxt(f)
     with gzip.open(os.path.join(self.path, 'rjob_20051006_e.gz')) as f:
         data_e = np.loadtxt(f)
     # Use double precision to get more accuracy for testing.
     data_n = np.require(data_n, np.float64)
     data_e = np.require(data_e, np.float64)
     ba = 33.3
     new_n, new_e = rotate_ne_rt(data_n, data_e, ba)
     new_n, new_e = rotate_rt_ne(new_n, new_e, ba)
     self.assertTrue(np.allclose(data_n, new_n, rtol=1E-7, atol=1E-12))
     self.assertTrue(np.allclose(data_e, new_e, rtol=1E-7, atol=1E-12))
Пример #3
0
    def calc(self, dphi, dts, tt, bp=None, showplot=False):
        """
        Method to estimate azimuth of component `?H1` (or `?HN`). This
        method minimizes the energy (RMS) of the transverse component of
        P-wave data within some bandwidth. 

        Parameters
        ----------
        dphi : float
            Azimuth increment for search (deg)
        dts : float
            Length of time window on either side of 
            predicted P-wave arrival time (sec)
        tt : list
            List of two floats containing the time picks relative
            to P-wave time, within which to perform the estimation of
            station orientation (sec)
        bp : list
            List of two floats containing the low- and high-frequency
            corners of a bandpass filter (Hz)
        showplot : bool
            Whether or not to plot waveforms.

        Attributes
        ----------
        meta.phi : float
            Azimuth of H1 (or HN) component (deg)
        meta.cc : float
            Cross-correlation coefficient between
            vertical and radial component
        meta.snr : float
            Signal-to-noise ratio of P-wave measured on the 
            vertical seismogram
        meta.TR : float
            Measure of the transverse to radial ratio. In reality
            this is 1 - T/R
        meta.RZ : float
            Measure of the radial to vertical ratio. In reality 
            this is 1 - R/Z

        """

        # Work on a copy of the waveform data
        stream = self.data.copy()

        # Filter if specified
        if bp:
            stream.filter('bandpass',
                          freqmin=bp[0],
                          freqmax=bp[1],
                          zerophase=True)

        # Get data and noise based on symmetric waveform wrt arrival
        start = stream[0].stats.starttime
        stnoise = stream.copy().trim(start, start + dts + tt[0])
        stdata = stream.copy().trim(start + dts + tt[0], start + dts + tt[1])

        # Define signal and noise
        tr1 = stdata.select(component='1')[0].copy()
        tr2 = stdata.select(component='2')[0].copy()
        trZ = stdata.select(component='Z')[0].copy()
        ntrZ = stnoise.select(component='Z')[0].copy()

        # Calculate and store SNR as attribute
        self.meta.snr = 10. * np.log10(
            utils.rms(trZ) * utils.rms(trZ) / utils.rms(ntrZ) /
            utils.rms(ntrZ))

        # Search through azimuths from 0 to 180 deg and find best-fit azimuth
        ang = np.arange(0., 180., dphi)
        cc1 = np.zeros(len(ang))
        cc2 = np.zeros(len(ang))
        cc3 = np.zeros(len(ang))
        cc4 = np.zeros(len(ang))
        for k, a in enumerate(ang):
            R, T = rotate_ne_rt(tr1.data, tr2.data, a)
            covmat = np.corrcoef(R, trZ.data)
            cc1[k] = covmat[0, 1]
            cc2[k] = 1. - utils.rms(T) / utils.rms(R)
            cc3[k] = utils.rms(T)
            cc4[k] = 1. - utils.rms(R) / utils.rms(trZ.data)

        # Get argument of minimum of cc3 and store useful measures
        ia = cc3.argmin()
        self.meta.cc = cc1[ia]
        self.meta.TR = cc2[ia]
        self.meta.RZ = cc4[ia]

        # correct for angles above 360
        phi = (self.meta.baz - float(ia) * dphi)

        # Use azimuth where CC is negative
        if self.meta.cc < 0.:
            phi += 180.

        if phi < 0.:
            phi += 360.
        if phi >= 360.:
            phi -= 360.

        # Store the best-fit azimuth
        self.meta.phi = phi

        # If a plot is requested, rotate Z12 to ZNE and then ZRT
        if showplot:

            # Now rotate components to proper N, E and then R, T
            sttmp = stream.copy()

            # Apply filter if defined previously
            if bp:
                sttmp.filter('bandpass',
                             freqmin=bp[0],
                             freqmax=bp[1],
                             zerophase=True)

            # Copy traces
            trN = sttmp.select(component='1')[0].copy()
            trE = sttmp.select(component='2')[0].copy()

            # Rotating from 1,2 to N,E is the negative of
            # rotation from RT to NE, with baz corresponding
            # to azim of component 1, or phi previously determined
            azim = self.meta.phi
            N, E = rotate_rt_ne(trN.data, trE.data, azim)
            trN.data = -1. * N
            trE.data = -1. * E

            # Update stats of streams
            trN.stats.channel = trN.stats.channel[:-1] + 'N'
            trE.stats.channel = trE.stats.channel[:-1] + 'E'

            # Store corrected traces in new stream and rotate to
            # R, T using back-azimuth
            stcorr = Stream(traces=[trN, trE])
            stcorr.rotate('NE->RT', back_azimuth=self.meta.baz)

            # Merge original and corrected streams
            st = stream + stcorr

            # Plot
            plot = plotting.plot_bng_waveforms(self, st, dts, tt)
            plot.show()

        return
Пример #4
0
    def rotate(self, align=None):
        """
        Rotates 3-component seismograms from vertical (Z),
        east (E) and north (N) to longitudinal (L), 
        radial (Q) and tangential (T) components of motion.
        Note that the method 'rotate' from ``obspy.core.stream.Stream``
        is used for the rotation ``'ZNE->ZRT'`` and ``'ZNE->LQT'``.
        Rotation ``'ZNE->PVH'`` is implemented separately here 
        due to different conventions.

        Parameters
        ----------
        align : str
            Alignment of coordinate system for rotation
            ('ZNE' or 'LQT')

        """

        if not self.meta.accept:
            return

        # Use default values from meta data if arguments are not specified
        if not align:
            align = self.meta.align

        if align == 'ZNE':
            # Rotating from 1,2 to N,E is the negative of
            # rotation from RT to NE, with
            # baz corresponding to azim of component 1
            from obspy.signal.rotate import rotate_rt_ne

            # Copy traces
            trZ = self.dataZ12.select(component='Z')[0].copy()
            trN = self.dataZ12.select(component='1')[0].copy()
            trE = self.dataZ12.select(component='2')[0].copy()

            azim = self.sta.azcorr
            N, E = rotate_rt_ne(trN.data, trE.data, azim)
            trN.data = -1.*N
            trE.data = -1.*E

            # Update stats of streams
            trN.stats.channel = trN.stats.channel[:-1] + 'N'
            trE.stats.channel = trE.stats.channel[:-1] + 'E'

            self.dataZNE = Stream(traces=[trZ, trN, trE])

        elif align == 'LQT':
            data = self.dataZNE.copy()
            data.rotate('ZNE->LQT',
                             back_azimuth=self.meta.baz,
                             inclination=self.meta.inc)
            # for tr in data:
            #     if tr.stats.channel.endswith('Q'):
            #         tr.data = -tr.data
            self.meta.align = align
            self.meta.rotated = True
            self.dataLQT = data.copy()

        else:
            raise(Exception("incorrect 'align' argument"))
Пример #5
0
    def rotate(self, vp=None, vs=None, align=None):
        """
        Rotates 3-component seismograms from vertical (Z),
        east (E) and north (N) to longitudinal (L), 
        radial (Q) and tangential (T) components of motion.
        Note that the method 'rotate' from ``obspy.core.stream.Stream``
        is used for the rotation ``'ZNE->ZRT'`` and ``'ZNE->LQT'``.
        Rotation ``'ZNE->PVH'`` is implemented separately here 
        due to different conventions.

        Parameters
        ----------
        vp : float
            P-wave velocity at surface (km/s)
        vs : float
            S-wave velocity at surface (km/s)
        align : str
            Alignment of coordinate system for rotation
            ('ZRT', 'LQT', or 'PVH')

        Returns
        -------
        rotated : bool
            Whether or not the object has been rotated

        """

        if not self.meta.accept:
            return

        if self.meta.rotated:
            print("Data have been rotated already - continuing")
            return

        # Use default values from meta data if arguments are not specified
        if not align:
            align = self.meta.align

        if align == 'ZNE':
            # Rotating from 1,2 to N,E is the negative of
            # rotation from RT to NE, with
            # baz corresponding to azim of component 1
            from obspy.signal.rotate import rotate_rt_ne

            # Copy traces
            trZ = self.data.select(component='Z')[0].copy()
            trN = self.data.select(component='1')[0].copy()
            trE = self.data.select(component='2')[0].copy()

            azim = self.sta.azcorr
            N, E = rotate_rt_ne(trN.data, trE.data, azim)
            trN.data = -1. * N
            trE.data = -1. * E

            # Update stats of streams
            trN.stats.channel = trN.stats.channel[:-1] + 'N'
            trE.stats.channel = trE.stats.channel[:-1] + 'E'

            self.data = Stream(traces=[trZ, trN, trE])

        elif align == 'ZRT':
            self.data.rotate('NE->RT', back_azimuth=self.meta.baz)
            self.meta.align = align
            self.meta.rotated = True

        elif align == 'LQT':
            self.data.rotate('ZNE->LQT',
                             back_azimuth=self.meta.baz,
                             inclination=self.meta.inc)
            for tr in self.data:
                if tr.stats.channel.endswith('Q'):
                    tr.data = -tr.data
            self.meta.align = align
            self.meta.rotated = True

        elif align == 'PVH':

            # First rotate to ZRT
            self.data.rotate('NE->RT', back_azimuth=self.meta.baz)

            # Copy traces
            trP = self.data.select(component='Z')[0].copy()
            trV = self.data.select(component='R')[0].copy()
            trH = self.data.select(component='T')[0].copy()

            slow = self.meta.slow
            if not vp:
                vp = self.meta.vp
            if not vs:
                vs = self.meta.vs

            # Vertical slownesses
            # P vertical slowness
            qp = np.sqrt(1. / vp / vp - slow * slow)
            # S vertical slowness
            qs = np.sqrt(1. / vs / vs - slow * slow)

            # Elements of rotation matrix
            m11 = slow * vs * vs / vp
            m12 = -(1. - 2. * vs * vs * slow * slow) / (2. * vp * qp)
            m21 = (1. - 2. * vs * vs * slow * slow) / (2. * vs * qs)
            m22 = slow * vs

            # Rotation matrix
            rot = np.array([[-m11, m12], [-m21, m22]])

            # Vector of Radial and Vertical
            r_z = np.array([trV.data, trP.data])

            # Rotation
            vec = np.dot(rot, r_z)

            # Extract P and SV, SH components
            trP.data = vec[0, :]
            trV.data = vec[1, :]
            trH.data = -trH.data / 2.

            # Update stats of streams
            trP.stats.channel = trP.stats.channel[:-1] + 'P'
            trV.stats.channel = trV.stats.channel[:-1] + 'V'
            trH.stats.channel = trH.stats.channel[:-1] + 'H'

            # Over-write data attribute
            self.data = Stream(traces=[trP, trV, trH])
            self.meta.align = align
            self.meta.rotated = True

        else:
            raise (Exception("incorrect 'align' argument"))