Example #1
0
def powspecSN(st, stnoise, SNrat=1.5, win=None):
    """
    Return masked arrays of power spectrum, masking where SNratio is greater than SNrat
    USAGE
    freqs, amps, ampmask = powspecSN(st, stnoise, SNrat=1.5, freqlim=(0, 25), win=None)
    INPUTS
    st = obspy stream object, or trace object
    stnoise = obspy stream object from time period just before st (or whatever noise window desired, but needs to have same sample rate)
    win = tuple of time window in seconds (e.g. win=(3., 20.)) over which to compute mean squared frequency, None computes for entire time window in each trace of st
    OUTPUTS
    freqs = list of np.arrays of frequency vectors (Hz)
    amps = list of np.arrays of amplitudes
    ampmaks = list of np.arrays of masked amplitudes
    """
    import obspy.signal.spectral_estimation as spec

    st = Stream(st)  # turn into a stream object in case st is a trace
    stnoise = Stream(stnoise)
    freqs = []  # preallocate
    amps = []
    ampmask = []
    for i, trace in enumerate(st):
        if trace.stats.sampling_rate != stnoise[i].stats.sampling_rate:
            print 'Signal and noise sample rates are different. Abort!'
            return
        Fs = trace.stats.sampling_rate  # Time vector
        dat = trace.data
        pFs = stnoise[i].stats.sampling_rate
        pdat = stnoise[i].data
        tvec = maketvec(trace)  # Time vector

        if win is not None:
            if win[1] > tvec.max() or win[0] < tvec.min():
                print 'Time window specified not compatible with length of time series'
                return
            dat = dat[(tvec >= win[0]) & (tvec <= win[1])]
            trace = trace.trim(trace.stats.starttime + win[0],
                               trace.stats.starttime + win[1])
        # Find max nfft of the two and use that for both so they line up
        maxnfft = np.max((nextpow2(len(dat)), nextpow2(len(pdat))))
        Pxx, f = spec.psd(dat, NFFT=maxnfft, Fs=Fs)
        pPxx, pf = spec.psd(pdat, NFFT=maxnfft, Fs=pFs)
        idx = (Pxx / pPxx < SNrat)  # good values
        amps.append(Pxx)
        freqs.append(f)
        ampmask.append(ma.array(Pxx, mask=idx))
    return freqs, amps, ampmask
Example #2
0
def powspecSN(st, stnoise, SNrat=1.5, win=None):
    """
    Return masked arrays of power spectrum, masking where SNratio is greater than SNrat
    USAGE
    freqs, amps, ampmask = powspecSN(st, stnoise, SNrat=1.5, freqlim=(0, 25), win=None)
    INPUTS
    st = obspy stream object, or trace object
    stnoise = obspy stream object from time period just before st (or whatever noise window desired, but needs to have same sample rate)
    win = tuple of time window in seconds (e.g. win=(3., 20.)) over which to compute mean squared frequency, None computes for entire time window in each trace of st
    OUTPUTS
    freqs = list of np.arrays of frequency vectors (Hz)
    amps = list of np.arrays of amplitudes
    ampmaks = list of np.arrays of masked amplitudes
    """
    import obspy.signal.spectral_estimation as spec

    st = Stream(st)  # turn into a stream object in case st is a trace
    stnoise = Stream(stnoise)
    freqs = []  # preallocate
    amps = []
    ampmask = []
    for i, trace in enumerate(st):
        if trace.stats.sampling_rate != stnoise[i].stats.sampling_rate:
            print 'Signal and noise sample rates are different. Abort!'
            return
        Fs = trace.stats.sampling_rate  # Time vector
        dat = trace.data
        pFs = stnoise[i].stats.sampling_rate
        pdat = stnoise[i].data
        tvec = maketvec(trace)  # Time vector

        if win is not None:
            if win[1] > tvec.max() or win[0] < tvec.min():
                print 'Time window specified not compatible with length of time series'
                return
            dat = dat[(tvec >= win[0]) & (tvec <= win[1])]
            trace = trace.trim(trace.stats.starttime+win[0], trace.stats.starttime+win[1])
        # Find max nfft of the two and use that for both so they line up
        maxnfft = np.max((nextpow2(len(dat)), nextpow2(len(pdat))))
        Pxx, f = spec.psd(dat, NFFT=maxnfft, Fs=Fs)
        pPxx, pf = spec.psd(pdat, NFFT=maxnfft, Fs=pFs)
        idx = (Pxx/pPxx < SNrat)  # good values
        amps.append(Pxx)
        freqs.append(f)
        ampmask.append(ma.array(Pxx, mask=idx))
    return freqs, amps, ampmask
    def test_obspy_psd_vs_pitsa(self):
        """
        Test to compare results of PITSA's psd routine to the
        :func:`matplotlib.mlab.psd` routine wrapped in
        :func:`obspy.signal.spectral_estimation.psd`.
        The test works on 8192 samples long Gaussian noise with a standard
        deviation of 0.1 generated with PITSA, sampling rate for processing in
        PITSA was 100.0 Hz, length of nfft 512 samples. The overlap in PITSA
        cannot be controlled directly, instead only the number of overlapping
        segments can be specified.  Therefore the test works with zero overlap
        to have full control over the data segments used in the psd.
        It seems that PITSA has one frequency entry more, i.e. the psd is one
        point longer. I dont know were this can come from, for now this last
        sample in the psd is ignored.
        """
        SAMPLING_RATE = 100.0
        NFFT = 512
        NOVERLAP = 0
        file_noise = os.path.join(self.path, "pitsa_noise.npy")
        fn_psd_pitsa = "pitsa_noise_psd_samprate_100_nfft_512_noverlap_0.npy"
        file_psd_pitsa = os.path.join(self.path, fn_psd_pitsa)

        noise = np.load(file_noise)
        # in principle to mimic PITSA's results detrend should be specified as
        # some linear detrending (e.g. from matplotlib.mlab.detrend_linear)
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter('always')
            psd_obspy, _ = psd(noise,
                               NFFT=NFFT,
                               Fs=SAMPLING_RATE,
                               window=welch_taper,
                               noverlap=NOVERLAP)
            self.assertEqual(len(w), 1)
            self.assertTrue(
                'This wrapper is no longer necessary.' in str(w[0].message))

        psd_pitsa = np.load(file_psd_pitsa)

        # mlab's psd routine returns Nyquist frequency as last entry, PITSA
        # seems to omit it and returns a psd one frequency sample shorter.
        psd_obspy = psd_obspy[:-1]

        # test results. first couple of frequencies match not as exactly as all
        # the rest, test them separately with a little more allowance..
        np.testing.assert_array_almost_equal(psd_obspy[:3],
                                             psd_pitsa[:3],
                                             decimal=4)
        np.testing.assert_array_almost_equal(psd_obspy[1:5],
                                             psd_pitsa[1:5],
                                             decimal=5)
        np.testing.assert_array_almost_equal(psd_obspy[5:],
                                             psd_pitsa[5:],
                                             decimal=6)
    def test_obspy_psd_vs_pitsa(self):
        """
        Test to compare results of PITSA's psd routine to the
        :func:`matplotlib.mlab.psd` routine wrapped in
        :func:`obspy.signal.spectral_estimation.psd`.
        The test works on 8192 samples long Gaussian noise with a standard
        deviation of 0.1 generated with PITSA, sampling rate for processing in
        PITSA was 100.0 Hz, length of nfft 512 samples. The overlap in PITSA
        cannot be controlled directly, instead only the number of overlapping
        segments can be specified.  Therefore the test works with zero overlap
        to have full control over the data segments used in the psd.
        It seems that PITSA has one frequency entry more, i.e. the psd is one
        point longer. I dont know were this can come from, for now this last
        sample in the psd is ignored.
        """
        SAMPLING_RATE = 100.0
        NFFT = 512
        NOVERLAP = 0
        file_noise = os.path.join(self.path, "pitsa_noise.npy")
        fn_psd_pitsa = "pitsa_noise_psd_samprate_100_nfft_512_noverlap_0.npy"
        file_psd_pitsa = os.path.join(self.path, fn_psd_pitsa)

        noise = np.load(file_noise)
        # in principle to mimic PITSA's results detrend should be specified as
        # some linear detrending (e.g. from matplotlib.mlab.detrend_linear)
        with warnings.catch_warnings(record=True) as w:
            warnings.simplefilter('always')
            psd_obspy, _ = psd(noise, NFFT=NFFT, Fs=SAMPLING_RATE,
                               window=welch_taper, noverlap=NOVERLAP)
            self.assertEqual(len(w), 1)
            self.assertTrue('This wrapper is no longer necessary.' in
                            str(w[0].message))

        psd_pitsa = np.load(file_psd_pitsa)

        # mlab's psd routine returns Nyquist frequency as last entry, PITSA
        # seems to omit it and returns a psd one frequency sample shorter.
        psd_obspy = psd_obspy[:-1]

        # test results. first couple of frequencies match not as exactly as all
        # the rest, test them separately with a little more allowance..
        np.testing.assert_array_almost_equal(psd_obspy[:3], psd_pitsa[:3],
                                             decimal=4)
        np.testing.assert_array_almost_equal(psd_obspy[1:5], psd_pitsa[1:5],
                                             decimal=5)
        np.testing.assert_array_almost_equal(psd_obspy[5:], psd_pitsa[5:],
                                             decimal=6)