def correct_response(st, removeResp=False, removePAZ=False, simPAZ=False, pre_filt=None, cornFreq=0.0083): """ Correct the seismometer response. Seismometer response is given in either a dictionary ``removeResp'' or a dictionary ``removePAZ''. ``removeResp has precedence. The dictionaries have the following structure removeResp: dictionary with Response information to be removed has the following keys: respfile: (str) filename of evalresp response file. units: (str) Units to return response in. Can be either DIS, VEL or ACC start_stage: (int) integer stage numbers of start stage (<0 causes default evalresp bahaviour). stop_stage: (int) integer stage numbers of stop stage removePAZ: dictionary with poles and zeros to be removed has the following keys: poles: (list of complex numbers) location of poles zeros: (list of complex numbers) location of zeros gain: (float) gain sensitivity: (float) sensitivity It can easily be retrieved with obspy.arclink.client.Client.getPAZ if ``removeResp'' is given the response of each trace must be present in the respfile. If ``removePAZ'' is used the response is assumed to be the same for all traces in the stream. A filter specified in pre_filt can be applied in to avoid amplification of noise. The instrument to be simulated is either described in the dictionary simPAZ or if simPAZ is False by the corner frequency ``cornFreq''. Response correction is done in place and original data is overwritten. The input stream ``st'' should be demeaned and tapered. :type st: obspy.core.stream.Stream :param st: data stream to be corrected :type removeResp: dict :param removeResp: Response information to be removed :type removePAZ: dict :param removePAZ: Response information to be removed :type simPAZ: dict :param simPAZ: Response information to be simulated :type cornFreq: float :param cornFreq: corner frequency of instrument to be simulated :type pre_filt: list :param pre_filt: 4 corners of the filter """ for tr in st: starttime = tr.stats['starttime'] endtime = tr.stats['endtime'] network = tr.stats['network'] station = tr.stats['station'] channel = tr.stats['channel'] location = tr.stats['location'] length = tr.stats['npts'] sampling_rate = tr.stats['sampling_rate'] np2l = nextpow2(2.*length) if not simPAZ: simPAZ = cornFreq2Paz(cornFreq, damp=0.70716) simresp, freqs = np.conj(pazToFreqResp(simPAZ['poles'], simPAZ['zeros'], scale_fac=simPAZ['gain']*simPAZ['sensitivity'], t_samp=1./sampling_rate, nfft=np2l, freq=True)) #see Doc of pazToFreqResp for reason of conj() if removeResp: freqresp, freqs = evalresp(1./sampling_rate,np2l,removeResp['respfile'], starttime, network=network, station=station, channel=channel, locid=location, start_stage=removeResp['start_stage'], stop_stage=removeResp['stop_stage'], units=removeResp['units'], freq=True) else: freqresp, freqs = np.conj(pazToFreqResp(removePAZ['poles'], removePAZ['zeros'], scale_fac=removePAZ['gain']*removePAZ['sensitivity'], t_samp=1./sampling_rate, nfft=np2l, freq=True)) #see Doc of pazToFreqResp for reason of conj() ftr = np.fft.rfft(tr.data,n=np2l) ftr /= freqresp ftr[0] = 0.j # correct the NaN in the DC component ftr *= simresp if pre_filt: ftr *= c_sac_taper(freqs, flimit=pre_filt) tr.data = np.fft.irfft(ftr) tr.trim(starttime,endtime) return
def time_shift_apply(corr_data, shift): """ Apply time shift to traces. Apply time shifts to traces e.g. to align them to a common time base. Such shifts can occur in corrlation traces in case of a drifting clock. This function ``applies`` the shifts. To correct for shift estimated with :class:`~miic.core.stretch_mod.time_shift_estimate` you need to apply negative shifts. Shifting is done in frequency domain with 5% tapering. :type corr_data: :py:class:`~numpy.ndarray` :param corr_data: 2d ndarray containing the correlation functions that are to be shifted. One for each row. :type shift: :py:class:`~numpy.ndarray` :param shift: ndarray with shift.shape[0] = corr_data.shape[0] containing the shifts in units of the sampling interval by which the trace are to be shifted :rtype: :py:class:`~numpy.ndarray` :return: **shifted_mat**: shifted version of the input matrix """ mat = corr_data # check input # shift is just a 1d array if len(shift.shape) == 1: t_shift = np.zeros([shift.shape[0], 1]) t_shift[:, 0] = shift shift = t_shift # shift has the wrong length elif shift.shape[0] != mat.shape[0]: print 'InputError: shift.shape[0] must be equal corr_data.shape[0]' return 0 # shift has multiple columns (multiple measurements for the same time) if shift.shape[1] > 1: shift = np.delete(shift, np.arange(1, shift.shape[1]), axis=1) # taper the reference matrix to avoid interpolation taper = cosTaper(mat.shape[1], 0.05) mat *= np.tile(taper, [mat.shape[0], 1]) # find a suitable length for the FFT N = nextpow2(2 * mat.shape[1]) w = np.zeros([1, N / 2 + 1]) # original and shifted phase w[0, :] = np.linspace(0, np.pi, N / 2 + 1) pha = np.exp(-1j * (shift) * w) # Fourier Transform F = np.fft.rfft(mat, N, 1) # apply the phase shift sF = F * pha # transform to time domain smat = np.fft.irfft(sF) # cut to original size shifted_mat = smat[:, 0:mat.shape[1]] return shifted_mat
def correct_response(st, removeResp=False, removePAZ=False, simPAZ=False, pre_filt=None, cornFreq=0.0083): """ Correct the seismometer response. Seismometer response is given in either a dictionary ``removeResp'' or a dictionary ``removePAZ''. ``removeResp has precedence. The dictionaries have the following structure removeResp: dictionary with Response information to be removed has the following keys: respfile: (str) filename of evalresp response file. units: (str) Units to return response in. Can be either DIS, VEL or ACC start_stage: (int) integer stage numbers of start stage (<0 causes default evalresp bahaviour). stop_stage: (int) integer stage numbers of stop stage removePAZ: dictionary with poles and zeros to be removed has the following keys: poles: (list of complex numbers) location of poles zeros: (list of complex numbers) location of zeros gain: (float) gain sensitivity: (float) sensitivity It can easily be retrieved with obspy.arclink.client.Client.getPAZ if ``removeResp'' is given the response of each trace must be present in the respfile. If ``removePAZ'' is used the response is assumed to be the same for all traces in the stream. A filter specified in pre_filt can be applied in to avoid amplification of noise. The instrument to be simulated is either described in the dictionary simPAZ or if simPAZ is False by the corner frequency ``cornFreq''. Response correction is done in place and original data is overwritten. The input stream ``st'' should be demeaned and tapered. :type st: obspy.core.stream.Stream :param st: data stream to be corrected :type removeResp: dict :param removeResp: Response information to be removed :type removePAZ: dict :param removePAZ: Response information to be removed :type simPAZ: dict :param simPAZ: Response information to be simulated :type cornFreq: float :param cornFreq: corner frequency of instrument to be simulated :type pre_filt: list :param pre_filt: 4 corners of the filter """ for tr in st: starttime = tr.stats['starttime'] endtime = tr.stats['endtime'] network = tr.stats['network'] station = tr.stats['station'] channel = tr.stats['channel'] location = tr.stats['location'] length = tr.stats['npts'] sampling_rate = tr.stats['sampling_rate'] np2l = nextpow2(2. * length) if not simPAZ: simPAZ = cornFreq2Paz(cornFreq, damp=0.70716) simresp, freqs = np.conj( pazToFreqResp( simPAZ['poles'], simPAZ['zeros'], scale_fac=simPAZ['gain'] * simPAZ['sensitivity'], t_samp=1. / sampling_rate, nfft=np2l, freq=True)) #see Doc of pazToFreqResp for reason of conj() if removeResp: freqresp, freqs = evalresp(1. / sampling_rate, np2l, removeResp['respfile'], starttime, network=network, station=station, channel=channel, locid=location, start_stage=removeResp['start_stage'], stop_stage=removeResp['stop_stage'], units=removeResp['units'], freq=True) else: freqresp, freqs = np.conj( pazToFreqResp( removePAZ['poles'], removePAZ['zeros'], scale_fac=removePAZ['gain'] * removePAZ['sensitivity'], t_samp=1. / sampling_rate, nfft=np2l, freq=True)) #see Doc of pazToFreqResp for reason of conj() ftr = np.fft.rfft(tr.data, n=np2l) ftr /= freqresp ftr[0] = 0.j # correct the NaN in the DC component ftr *= simresp if pre_filt: ftr *= c_sac_taper(freqs, flimit=pre_filt) tr.data = np.fft.irfft(ftr) tr.trim(starttime, endtime) return
def time_shift_apply(corr_data, shift): """ Apply time shift to traces. Apply time shifts to traces e.g. to align them to a common time base. Such shifts can occur in corrlation traces in case of a drifting clock. This function ``applies`` the shifts. To correct for shift estimated with :class:`~miic.core.stretch_mod.time_shift_estimate` you need to apply negative shifts. Shifting is done in frequency domain with 5% tapering. :type corr_data: :py:class:`~numpy.ndarray` :param corr_data: 2d ndarray containing the correlation functions that are to be shifted. One for each row. :type shift: :py:class:`~numpy.ndarray` :param shift: ndarray with shift.shape[0] = corr_data.shape[0] containing the shifts in units of the sampling interval by which the trace are to be shifted :rtype: :py:class:`~numpy.ndarray` :return: **shifted_mat**: shifted version of the input matrix """ mat = corr_data # check input # shift is just a 1d array if len(shift.shape) == 1: t_shift = np.zeros([shift.shape[0], 1]) t_shift[:, 0] = shift shift = t_shift # shift has the wrong length elif shift.shape[0] != mat.shape[0]: print "InputError: shift.shape[0] must be equal corr_data.shape[0]" return 0 # shift has multiple columns (multiple measurements for the same time) if shift.shape[1] > 1: shift = np.delete(shift, np.arange(1, shift.shape[1]), axis=1) # taper the reference matrix to avoid interpolation taper = cosTaper(mat.shape[1], 0.05) mat *= np.tile(taper, [mat.shape[0], 1]) # find a suitable length for the FFT N = nextpow2(2 * mat.shape[1]) w = np.zeros([1, N / 2 + 1]) # original and shifted phase w[0, :] = np.linspace(0, np.pi, N / 2 + 1) pha = np.exp(-1j * (shift) * w) # Fourier Transform F = np.fft.rfft(mat, N, 1) # apply the phase shift sF = F * pha # transform to time domain smat = np.fft.irfft(sF) # cut to original size shifted_mat = smat[:, 0 : mat.shape[1]] return shifted_mat