#!/usr/bin/python
"""
Utility module of the EQcorrscan package to allow for different methods of
stacking of seismic signal in one place.
In alpha stages and only with linear stacking implimented thusfar
Calum Chamberlain 24/06/2015
Copyright 2015 Calum Chamberlain
This file is part of EQcorrscan.
EQcorrscan is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
EQcorrscan is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with EQcorrscan. If not, see <http://www.gnu.org/licenses/>.
"""
import numpy as np
[docs]def linstack(streams):
"""
Function to compute the linear stack of a series of seismic streams of
multiplexed data
:type streams: List of Streams
:param stream: List of streams to stack
:returns: stack - Stream
"""
# import matplotlib.pyplot as plt
stack=streams[np.argmax([len(stream) for stream in streams])].copy()
for tr in stack:
tr.data=tr.data/np.sqrt(np.mean(np.square(tr.data)))
tr.data=np.nan_to_num(tr.data)
for i in xrange(1,len(streams)):
# print "Stacking stream "+str(i)
for tr in stack:
# print tr.stats.station+'.'+tr.stats.channel
matchtr=streams[i].select(station=tr.stats.station,\
channel=tr.stats.channel)
if matchtr:
norm=matchtr[0].data/np.sqrt(np.mean(np.square(matchtr[0].data)))
norm=np.nan_to_num(norm)
tr.data=np.sum((norm,\
tr.data), axis=0)
return stack
[docs]def PWS_stack(streams, weight=2):
"""
Function to compute the phase weighted stack of a series of streams.
Recommend aligning the traces before stacking.
:type streams: list of obspy.Stream
:param streams: List of Stream to stack
:type weight: float
:param weight: Exponent to the phase stack used for weighting.
:return: obspy.Stream
"""
from scipy.signal import hilbert
# First get the linear stack which we will weight by the phase stack
Linstack=linstack(streams)
# Compute the instantaneous phase
instaphases=[]
print "Computing instantaneous phase"
for stream in streams:
instaphase=stream.copy()
for tr in instaphase:
analytic=hilbert(tr.data)
envelope=np.sqrt(np.sum((np.square(analytic),\
np.square(tr.data)), axis=0))
tr.data=analytic/envelope
instaphases.append(instaphase)
# Compute the phase stack
print "Computing the phase stack"
Phasestack=linstack(instaphases)
# print type(Phasestack)
# Compute the phase-weighted stack
for tr in Phasestack:
tr.data=Linstack.select(station=tr.stats.station)[0].data*\
np.abs(tr.data**weight)
return Phasestack
[docs]def align_traces(trace_list, shift_len):
"""
Function to allign traces relative to each other based on their
cross-correlation value
:type trace_list: List of Traces
:param trace_list: List of traces to allign
:type shift_len: int
:param shift_len: Length to allow shifting within in samples
:returns: list of shifts for best allignment in seconds
"""
from obspy.signal.cross_correlation import xcorr
from copy import deepcopy
traces=deepcopy(trace_list)
# Use trace with largest MAD amplitude as master
master=traces[0]
MAD_master=np.median(np.abs(master.data))
master_no=0
for i in xrange(1,len(traces)):
if np.median(np.abs(traces[i])) > MAD_master:
master=traces[i]
MAD_master=np.median(np.abs(master.data))
master_no=i
shifts=[]
for i in xrange(len(traces)):
if not master.stats.sampling_rate == traces[i].stats.sampling_rate:
raise ValueError('Sampling rates not the same')
shift, cc=xcorr(master, traces[i], shift_len)
shifts.append(shift/master.stats.sampling_rate)
return shifts