def cfrequency(data, fs, smoothie, fk): nfft = nextpow2(data.shape[0]) freq = np.arange(0, float(fs) - 1. / (nextpow2(data.shape[0]) / float(fs)), 1. / (nextpow2(data.shape[0]) / float(fs))) freqaxis = freq[0:len(freq) / 2 + 1] cfreq = np.zeros(data.shape[0]) if (np.size(data.shape) > 1): i = 0 for row in data: Px_wm = welch(row, hamming(len(row)), nextpow2(len(row))) Px = Px_wm[0:len(Px_wm) / 2] cfreq[i] = np.sqrt(sum(pow(freqaxis, 2) * Px) / (sum(Px))) i = i + 1 cfreq = smooth(cfreq, smoothie) cfreq_add = append(append([cfreq[0]] * (size(fk) // 2), cfreq), [cfreq[size(cfreq) - 1]] * (size(fk) // 2)) dcfreq = signal.lfilter(fk, 1, cfreq_add) dcfreq = dcfreq[size(fk) // 2:(size(dcfreq) - size(fk) // 2)] return cfreq, dcfreq else: Px_wm = welch(data, np.hamming(len(data)), nextpow2(len(data))) Px = Px_wm[0:len(Px_wm) / 2] cfreq = np.sqrt(np.sum(pow(freqaxis, 2) * Px) / (sum(Px))) return cfreq
def decovit(UIN,WIN,DT,NT,TSHIFT,F0,ITMAX,MINDERR): print('Iterative Decon (Ligorria & Ammon):\n') RMS = np.zeros([ITMAX,1]) nfft = nextpow2(NT) P0 = np.zeros(nfft) U0 = np.zeros(nfft) W0 = np.zeros(nfft) U0[0:NT]=UIN W0[0:NT]=WIN gaussF = gaussFilter( DT, nfft, F0 ) U = gfilter( U0, nfft, gaussF , DT) W = gfilter( W0, nfft, gaussF , DT) Wf = np.fft.fft( W0, nfft ) R = U powerU = np.sum(U**2) it = 0 sumsq_i = 1 d_error = 100*powerU + MINDERR maxlag = 0.5*nfft print('\tMax Spike Display is '+str((maxlag)*DT)) while np.abs(d_error) > MINDERR and it < ITMAX: RW= correl(R, W, nfft) RW = RW/np.sum(W**2) i1=np.argmax( np.abs( RW[0:int(maxlag)-1] ) ) amp = RW[i1]/DT P0[i1] = P0[i1] + amp P = gfilter(P0, nfft, gaussF, DT) P = gfilter(P, nfft, Wf, DT) R = U - P sumsq = np.sum( R**2 )/powerU RMS[it] = sumsq d_error = 100*(sumsq_i - sumsq) sumsq_i = sumsq it = it+1 P = gfilter( P0, nfft, gaussF, DT ) P = phaseshift(P,nfft,DT,TSHIFT) RFI=P[0:NT] RMS = RMS[0:it-1] print('\t# iterations: ',it,'\n') print('\tFinal RMS: ',float(RMS[it-2]),'\n') return RFI, RMS, it
def xcorrf(trace1, trace2, shift=None): """ Cross-correlation of numpy arrays data1 and data2 in frequency domain. """ data1 = trace1.data data2 = trace2.data complex_result = data1.dtype == complex or data2.dtype == complex N1 = len(data1) N2 = len(data2) data1 = data1.astype("float64") data2 = data2.astype("float64") # Always use 2**n-sized FFT, perform xcorr size = max(2 * shift + 1, (N1 + N2) // 2 + shift) nfft = nextpow2(size) IN1 = fft(data1, nfft) IN1 *= conjugate(fft(data2, nfft)) ret = ifft(IN1) del IN1 if not complex_result: ret = ret.real # shift data for time lag 0 to index 'shift' ret = roll(ret, -(N1 - N2) // 2 + shift)[: 2 * shift + 1] return copy(ret)
def _find_and_open_files(self): """ Helper function walking the file tree below self.db_path and attempts to find the correct netCDF files. """ found_files = [] for root, dirs, filenames in os.walk(self.db_path, followlinks=True): # Limit depth of filetree traversal nested_levels = os.path.relpath(root, self.db_path).split( os.path.sep) if len(nested_levels) >= 4: del dirs[:] if "ordered_output.nc4" not in filenames: continue found_files.append(os.path.join(root, "ordered_output.nc4")) if len(found_files) == 0: raise InstaseisNotFoundError( "No suitable netCDF files found under '%s'" % self.db_path) elif len(found_files) not in [1, 2, 4]: raise InstaseisError( "1, 2 or 4 netCDF must be present in the folder structure. " "Found %i: \t%s" % (len(found_files), "\n\t".join(found_files))) # Parse to find the correct components. netcdf_files = collections.defaultdict(list) patterns = ["PX", "PZ", "MZZ", "MXX_P_MYY", "MXZ_MYZ", "MXY_MXX_M_MYY"] for filename in found_files: s = os.path.relpath(filename, self.db_path).split(os.path.sep) for p in patterns: if p in s: netcdf_files[p].append(filename) # Assert at most one file per type. for key, files in netcdf_files.items(): if len(files) != 1: raise InstaseisError( "Found %i files for component %s:\n\t%s" % ( len(files), key, "\n\t".join(files))) netcdf_files[key] = files[0] # Two valid cases. if "PX" in netcdf_files or "PZ" in netcdf_files: self._parse_fs_meshes(netcdf_files) elif "MZZ" in netcdf_files or "MXX_P_MYY" in netcdf_files or \ "MXZ_MYZ" in netcdf_files or "MXY_MXX_M_MYY" or netcdf_files: if sorted(netcdf_files.keys()) != sorted([ "MZZ", "MXX_P_MYY", "MXZ_MYZ", "MXY_MXX_M_MYY"]): raise InstaseisError( "Expecting all four elemental moment tensor subfolders " "to be present.") self._parse_mt_meshes(netcdf_files) else: raise InstaseisError("Could not find any suitable netCDF files.") # Set some common variables. self.nfft = nextpow2(self.ndumps) * 2 self.planet_radius = self.parsed_mesh.planet_radius self.dump_type = self.parsed_mesh.dump_type
def spectralWhitening(data, sr=None, smoothi=None, freq_domain=False, apply_filter=None): """ Apply spectral whitening to data. sr: sampling rate (only needed for smoothing) smoothi: None or int Data is divided by its smoothed (Default: None) amplitude spectrum. """ if freq_domain: mask = False spec = data else: mask = np.ma.getmask(data) N = len(data) nfft = nextpow2(N) spec = fft(data, nfft) #df = sr/N spec_ampl = np.sqrt(np.abs(np.multiply(spec, np.conjugate(spec)))) if isinstance(smoothi, basestring) and isnumber(smoothi) and smoothi > 0: smoothi = int(smoothi * N / sr) spec /= ifftshift(smooth(fftshift(spec_ampl), smoothi)) else: spec /= spec_ampl if apply_filter is not None: spec *= filterResp(*apply_filter, sr=sr, N=len(spec), whole=True)[1] if freq_domain: return spec else: ret = np.real(ifft(spec, nfft)[:N]) if USE_FFTW3: ret = ret.copy() return fillArray(ret, mask=mask, fill_value=0.)
def test(): from sito import read from obspy.signal.freqattributes import mper # , welch # from mtspec import mtspec ms = read("/home/richter/Data/Parkfield/raw/PKD_1996_296.mseed") # ms.plotTrace() print ms[0].stats # -*- snip -*- data = ms[0].data data = data - np.mean(data) # data -= np.linspace(0,1,len(data))*(data[-1]-data[0])+data[0] N = len(data) df = 1.0 / (ms[0].stats.endtime - ms[0].stats.starttime) print N // 2 * df spec1 = mper(data, cosTaper(N, 0.05), nextpow2(N))[: N // 2] # spec2 = welch(data, cosTaper(N, 0.05), nextpow2(N), len(data)/10, 0.2)[:N//2] spec1_d = oct_downsample(spec1, df, fac=1.3) freq1 = get_octfreqs(len(spec1_d), df, fac=1.3) ax = plot_psd(spec1, log=True) ax = plot_psd(spec1_d, freq1, log=True, ax=ax) plt.show()
def whiten(self, smooth=None, apply_filter=None): """ Apply spectral whitening to data. """ logging.info("Whitening {:} traces...", len(self.traces)) for tr in self.traces: mask = np.ma.getmask(tr.data) N = len(tr.data) nfft = nextpow2(N) spec = fft(tr.data, nfft) df = tr.stats['sampling_rate'] spec_ampl = np.sqrt(np.abs(np.multiply(spec, np.conjugate(spec)))) if isinstance(smooth, basestring) and isnumber(smooth) and\ (smooth > 0): smooth = int(smooth * N / df) spec /= ifftshift(smooth_func(fftshift(spec_ampl), smooth)) else: spec /= spec_ampl if apply_filter is not None: spec *= bandpass_response(*apply_filter, sr=df, N=len(spec), whole=True)[1] ret = np.real(ifft(spec, nfft)[:N]) tr.data = fill_array(ret, mask=mask, fill_value=0.)
def _get_info(self): """ Returns a dictionary with information about the currently loaded database. """ # Get the size of all netCDF files. filesize = 0 for m in self.meshes: if m: filesize += os.path.getsize(m.filename) if self._is_reciprocal: if self.meshes.pz is not None and self.meshes.px is not None: components = 'vertical and horizontal' elif self.meshes.pz is None and self.meshes.px is not None: components = 'horizontal only' elif self.meshes.pz is not None and self.meshes.px is None: components = 'vertical only' else: components = '4 elemental moment tensors' return dict( is_reciprocal=self._is_reciprocal, components=components, source_depth=float(self.parsed_mesh.source_depth) if self._is_reciprocal is False else None, velocity_model=self.parsed_mesh.background_model, external_model_name=self.parsed_mesh.external_model_name, attenuation=self.parsed_mesh.attenuation, period=float(self.parsed_mesh.dominant_period), dump_type=self.parsed_mesh.dump_type, excitation_type=self.parsed_mesh.excitation_type, dt=float(self.parsed_mesh.dt), sampling_rate=float(1.0 / self.parsed_mesh.dt), npts=int(self.parsed_mesh.ndumps), nfft=int(nextpow2(self.parsed_mesh.ndumps) * 2), length=float(self.parsed_mesh.dt * (self.parsed_mesh.ndumps - 1)), stf=self.parsed_mesh.stf_kind, src_shift=float(self.parsed_mesh.source_shift), src_shift_samples=int(self.parsed_mesh.source_shift_samp), slip=self.parsed_mesh.stf_norm, sliprate=self.parsed_mesh.stf_d_norm, spatial_order=int(self.parsed_mesh.npol), min_radius=float(self.parsed_mesh.kwf_rmin) * 1e3, max_radius=float(self.parsed_mesh.kwf_rmax) * 1e3, planet_radius=float(self.parsed_mesh.planet_radius), min_d=float(self.parsed_mesh.kwf_colatmin), max_d=float(self.parsed_mesh.kwf_colatmax), time_scheme=self.parsed_mesh.time_scheme, directory=os.path.relpath(self.db_path), filesize=filesize, compiler=self.parsed_mesh.axisem_compiler, user=self.parsed_mesh.axisem_user, format_version=int(self.parsed_mesh.file_version), axisem_version=self.parsed_mesh.axisem_version, datetime=self.parsed_mesh.creation_time )
def deconvf(rsp_list, src, sampling_rate, water=0.05, gauss=2., tshift=10., pad=0, length=None, normalize=True, normalize_to_src=False, returnall=False): """ Frequency-domain deconvolution using waterlevel method. rsp, src data containing the response and source functions, respectively water waterlevel to stabilize the deconvolution gauss Gauss parameter of Low-pass filter tshift shift the resulting function by that amount pad multiply number of samples used for fft by 2**pad """ if length == None: length = len(src) N = length nfft = nextpow2(N) * 2 ** pad freq = np.fft.fftfreq(nfft, d=1. / sampling_rate) gauss = np.exp(np.maximum(-(0.5 * 2 * pi * freq / gauss) ** 2, -700.) - 1j * tshift * 2 * pi * freq) spec_src = fft(src, nfft) spec_src_conj = np.conjugate(spec_src) spec_src_water = np.abs(spec_src * spec_src_conj) spec_src_water = np.maximum(spec_src_water, max(spec_src_water) * water) if normalize_to_src: spec_src = gauss * spec_src * spec_src_conj / spec_src_water rf_src = ifft(spec_src, nfft)[:N] #i1 = int((tshift-1)*sampling_rate) #i2 = int((tshift+1)*sampling_rate) norm = 1 / max(rf_src) rf_src = norm * rf_src flag = False if not isinstance(rsp_list, (list, tuple)): flag = True rsp_list = [rsp_list] rf_list = [ifft(gauss * fft(rsp, nfft) * spec_src_conj / spec_src_water, nfft)[:N] for rsp in rsp_list] if normalize: if not normalize_to_src: norm = 1. / max(rf_list[0]) for rf in rf_list: rf *= norm if returnall: if not normalize_to_src: spec_src = gauss * spec_src * spec_src_conj / spec_src_water rf_src = ifft(spec_src, nfft)[:N] norm = 1 / max(rf_src) rf_src = norm * rf_src return rf_list, rf_src, spec_src_conj, spec_src_water, freq, gauss, norm, N, nfft elif flag: return rf else: return rf_list
def bwith(data, fs, smoothie, fk): """ Bandwidth of a signal. Computes the bandwidth of the given data which can be windowed or not. The bandwidth corresponds to the level where the power of the spectrum is half its maximum value. It is determined as the level of 1/sqrt(2) times the maximum Fourier amplitude. If data are windowed the bandwidth of each window is returned. :type data: :class:`~numpy.ndarray` :param data: Data to make envelope of. :param fs: Sampling frequency in Hz. :param smoothie: Factor for smoothing the result. :param fk: Coefficients for calculating time derivatives (calculated via central difference). :return: **bwith[, dbwithd]** - Bandwidth, Time derivative of predominant period (windowed only). """ new_dtype = np.float32 if data.dtype.itemsize == 4 else np.float64 data = np.require(data, dtype=new_dtype) nfft = util.nextpow2(data.shape[1]) freqaxis = np.linspace(0, fs, nfft + 1) bwith = np.zeros(data.shape[0]) f = fftpack.fft(data, nfft) f_sm = util.smooth(abs(f[:, 0:nfft // 2]), 10) if np.size(data.shape) > 1: i = 0 for row in f_sm: minfc = abs(row - max(abs(row * (1 / np.sqrt(2))))) [mdist_ind, _mindist] = min(enumerate(minfc), key=itemgetter(1)) bwith[i] = freqaxis[mdist_ind] i = i + 1 # bwith_add = \ # np.append(np.append([bwith[0]] * (np.size(fk) // 2), bwith), # [bwith[np.size(bwith) - 1]] * (np.size(fk) // 2)) # faster alternative bwith_add = np.hstack( ([bwith[0]] * (np.size(fk) // 2), bwith, [bwith[np.size(bwith) - 1]] * (np.size(fk) // 2))) dbwith = signal.lfilter(fk, 1, bwith_add) # dbwith = dbwith[np.size(fk) // 2:(np.size(dbwith) - # np.size(fk) // 2)] # correct start and end values of time derivative dbwith = dbwith[np.size(fk) - 1:] bwith = util.smooth(bwith, smoothie) dbwith = util.smooth(dbwith, smoothie) return bwith, dbwith else: minfc = abs(data - max(abs(data * (1 / np.sqrt(2))))) [mdist_ind, _mindist] = min(enumerate(minfc), key=itemgetter(1)) bwith = freqaxis[mdist_ind] return bwith
def fft(self, nfft=None): if self.stats.is_fft: raise ValueError('Trace already ffted.') self.stats.npts_data = self.stats.npts if nfft is None: nfft = nextpow2(self.stats.npts_data) self.stats.nfft = nfft self.stats.stdev = (np.sum(self.data ** 2)) ** 0.5 self.data = fft(self.data, nfft, overwrite_x=False) self.stats.is_fft = True self.stats.filter += 'FFT'
def test_util_filterResponse(self): sr = self.stream[0].stats.sampling_rate # test filterResponse vs filter2 st = self.stream.copy() data = st[0].data.copy() N = len(data) nfft = nextpow2(len(data)) values = util.main.filterResp(1, 5, corners=2, sr=20, N=nfft, whole=True)[1] st.filter2(1, 5, corners=2) data2 = ifft(fft(data, nfft) * values, nfft) np.testing.assert_array_almost_equal(st[0].data, data2[:N]) # test stream interface st = self.stream.copy() st.fft() st.filter2(1, 5, corners=2) st.ifft() np.testing.assert_array_almost_equal(st[0].data, data2[:N]) # filtering with filterResponse and zerophase=Trueproduces a peak at # the end of data. With nfft=N there is no peak anymore, but still the values are not the same st = self.stream.copy() freqs, values = util.main.filterResp(1, 5, sr=20, N=nfft, corners=2, whole=True, zerophase=True) st.filter2(1, 5, corners=2, zerophase=True) data2 = ifft(fft(data, nfft) * values, nfft) np.testing.assert_array_almost_equal(st[0].data[:-10 * sr], data2[:N - 10 * sr]) return from numpy.fft.helper import ifftshift, fftfreq import matplotlib.pyplot as plt real_freqs = (ifftshift(freqs) - np.pi) * 0.5 * sr / np.pi freqs2 = fftfreq(nfft, 1 / 20.) print real_freqs print freqs2 plt.subplot(411) plt.plot(real_freqs, np.abs(values)) ax = plt.subplot(412) plt.plot(data, label='data') plt.legend() plt.subplot(413, sharex=ax) plt.plot(st[0].data, alpha=0.5, label='stream.filter2') plt.plot(data2[:N], alpha=0.5, label='filterResponse') plt.legend() plt.show()
def shifttrace_freq(stream, t_shift): if isinstance(stream, Stream): for i, tr in enumerate(stream): ndat = tr.stats.npts samp = tr.stats.sampling_rate nfft = nextpow2(ndat) nfft *= 2 tr1 = np.fft.rfft(tr.data, nfft) for k in xrange(0, nfft / 2): tr1[k] *= np.complex( np.cos((t_shift[i] * samp) * (k / float(nfft)) * 2.0 * np.pi), -np.sin((t_shift[i] * samp) * (k / float(nfft)) * 2.0 * np.pi), ) tr1 = np.fft.irfft(tr1, nfft) tr.data = tr1[0:ndat]
def convSTF(st, sigma=30.): gauss = lambda (t, s): 1. / (2. * np.pi * s**2.)**.5 \ * np.exp(-1*(t**2)/(2*(s**2))) df = st[0].stats.sampling_rate dt = 1./df t = np.linspace(0., sigma * 20., sigma * 20 * df + 1) stf = gauss((t-sigma*10, sigma)) nstf = len(stf) for tr in st: tr.data *= cosTaper(len(tr.data), p=0.05) nfft = util.nextpow2(max(nstf, tr.stats.npts)) * 2 stff = np.fft.rfft(stf, n=nfft) * dt trf = np.fft.rfft(tr, n=nfft) * dt tr.data = np.fft.irfft(stff * trf)[sigma*10*df:sigma*10*df+len(tr.data)] * df return 1
def spectrum_calc(tr): """ Simple code to calculate the spectrum of a trace :param tr: obspy Trace :return: freqs, spec_tr which are frequencies and spectrum of the trace To plot the spectrum: import matplotlib.pyplot as plt plt.loglog(freqs, spec_tr) """ nfft = nextpow2(tr.stats.npts) freqs = np.fft.rfftfreq(nfft) / tr.stats.delta spec_tr = np.abs(np.fft.rfft(tr.data, n=nfft)) return freqs, spec_tr
def logcep(data, fs, nc, p, n, w): # @UnusedVariable: n is never used!!! """ Cepstrum of a signal. Computes the cepstral coefficient on a logarithmic scale of the given data which can be windowed or not. If data are windowed the analytic signal and the envelope of each window is returned. :type data: :class:`~numpy.ndarray` :param data: Data to make envelope of. :param fs: Sampling frequency in Hz. :param nc: number of cepstral coefficients. :param p: Number of filters in filterbank. :param n: Number of data windows. :return: Cepstral coefficients. """ new_dtype = np.float32 if data.dtype.itemsize == 4 else np.float64 data = np.require(data, dtype=new_dtype) dataT = np.transpose(data) nfft = util.nextpow2(dataT.shape[0]) fc = fftpack.fft(dataT, nfft, 0) f = fc[1:len(fc) // 2 + 1, :] m, a, b = logbankm(p, nfft, fs, w) pw = np.real(np.multiply(f[a:b, :], np.conj(f[a:b, :]))) pth = np.max(pw) * 1E-20 ath = np.sqrt(pth) # h1 = np.transpose(np.array([[ath] * int(b + 1 - a)])) # h2 = m * abs(f[a - 1:b, :]) y = np.log(np.maximum(m * abs(f[a - 1:b, :]), ath)) z = util.rdct(y) z = z[1:, :] # nc = nc + 1 nf = np.size(z, 1) if (p > nc): z = z[:nc, :] elif (p < nc): z = np.vstack([z, np.zeros(nf, nc - p)]) return z
def spectralwhitening(st): """ Apply spectral whitening to data. Data is divided by its smoothed (Default: None) amplitude spectrum. """ for trace in arange(len(st)): data = st[trace].data n = len(data) nfft = nextpow2(n) spec = fft(data, nfft) spec_ampl = sqrt(abs(multiply(spec, conjugate(spec)))) spec /= spec_ampl #Do we need to do some smoothing here? ret = real(ifft(spec, nfft)[:n]) st[trace].data = ret return st
def gauss(data, sampling_rate, gauss, pad=0, hp=None, return_gauss=False): """ Gauss filter gauss Gauss parameter of Low-pass filter pad multiply number of samples used for fft by 2**pad """ N = len(data) nfft = nextpow2(N) * 2 ** pad freq = np.fft.fftfreq(nfft, d=1. / sampling_rate) gauss = np.exp(maximum(-(0.5 * 2 * pi * freq / gauss) ** 2, -700.)) if hp: hp = filterResp(hp[0], hp[1], corners=hp[2], zerophase=True, sr=sampling_rate, N=nfft, whole=True)[1] gauss = hp * gauss if return_gauss: return gauss # import pylab # pylab.plot(freq, gauss) # pylab.show() resp = ifft(gauss * fft(data, nfft), nfft)[:N] return resp
def cfrequency_unwindowed(data, fs): """ Central frequency of a signal. Computes the central frequency of the given data (a single waveform). The central frequency is a measure of the frequency where the power is concentrated. It corresponds to the second moment of the power spectral density function. The central frequency is returned in Hz. :type data: :class:`~numpy.ndarray` :param data: Data to estimate central frequency from. :param fs: Sampling frequency in Hz. :return: **cfreq** - Central frequency in Hz """ nfft = util.nextpow2(len(data)) freq = np.linspace(0, fs, nfft + 1) freqaxis = freq[0 : nfft // 2] Px_wm = welch(data, np.hamming(len(data)), nfft) Px = Px_wm[0 : len(Px_wm) // 2] cfreq = np.sqrt(np.sum(freqaxis ** 2 * Px) / (sum(Px))) return cfreq
def spectralwhitening_smooth(stream, N): """ Apply spectral whitening to data. Data is divided by its smoothed (Default: None) amplitude spectrum. """ stream2 = copy.deepcopy(stream) for trace in arange(len(stream2)): data = stream2[trace].data n = len(data) nfft = nextpow2(n) spec = fft(data, nfft) spec_ampl = sqrt(abs(multiply(spec, conjugate(spec)))) spec_ampl = smooth(spec_ampl, N) spec /= spec_ampl #Do we need to do some smoothing here? ret = real(ifft(spec, nfft)[:n]) stream2[trace].data = ret return stream2
def acorr(self, shift=None, normalize=True, oneside=False): if shift is None: shift = len(self.data) else: shift = int(shift * self.stats.sampling_rate) size = max(2 * shift + 1, len(self.data) + shift) nfft = nextpow2(size) IN1 = fft(self.data, nfft) IN1 *= np.conjugate(IN1) ret = ifft(IN1).real # shift data for time lag 0 to index 'shift' ret = np.roll(ret, shift)[:2 * shift + 1] # normalize xcorr if normalize: stdev = (np.sum(self.data ** 2)) ** 0.5 if stdev == 0: log.warning('Data is zero!!') ret[:] = 0. else: ret /= stdev ** 2 if oneside: ret = ret[shift:] self.data = ret self.stats.npts = len(ret)
def acorr(self, shift=None, normalize=True, oneside=False): if shift is None: shift = len(self.data) else: shift = int(shift * self.stats.sampling_rate) size = max(2 * shift + 1, len(self.data) + shift) nfft = nextpow2(size) IN1 = fft(self.data, nfft) IN1 *= np.conjugate(IN1) ret = ifft(IN1).real # shift data for time lag 0 to index 'shift' ret = np.roll(ret, shift)[:2 * shift + 1] # normalize xcorr if normalize: stdev = (np.sum(self.data**2))**0.5 if stdev == 0: log.warning('Data is zero!!') ret[:] = 0. else: ret /= stdev**2 if oneside: ret = ret[shift:] self.data = ret self.stats.npts = len(ret)
def xcorrf(trace1, trace2, shift=None): """ Cross-correlation of numpy arrays data1 and data2 in frequency domain. """ data1 = trace1.data data2 = trace2.data complex_result = (data1.dtype == complex or data2.dtype == complex) N1 = len(data1) N2 = len(data2) data1 = data1.astype('float64') data2 = data2.astype('float64') # Always use 2**n-sized FFT, perform xcorr size = max(2 * shift + 1, (N1 + N2) // 2 + shift) nfft = nextpow2(size) print size #Calculate fft of data1 and data2 IN1 = fft(data1, nfft) IN2 = fft(data2, nfft) IN1 *= conjugate(IN2) ret = ifft(IN1) del IN1, IN2 if not complex_result: ret = ret.real # shift data for time lag 0 to index 'shift' ret = roll(ret, -(N1 - N2) // 2 + shift)[:2 * shift + 1] return copy(ret)
def seisSim(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, **_kwargs): """ Simulate/Correct seismometer. :type data: 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: Dictionary, 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: Dictionary, 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: Boolean :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: Boolean :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: Boolean :param zero_mean: If true the mean of the data is subtracted :type taper: Boolean :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: Dictionary, 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 `~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: Boolean :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 zeropadded 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: Boolean :param pitsasim: Choose parameters to match instrument correction as done by PITSA. :type sacsim: Boolean :param sacsim: Choose parameters to match instrument correction as done by SAC. :type shsim: Boolean :param shsim: Choose parameters to match instrument correction as done by Seismic Handler. :return: The corrected data are returned as 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 artefacts 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("float64") if zero_mean: data -= data.mean() if taper: if sacsim: data *= cosTaper(ndat, taper_fraction, sactaper=sacsim, halfcosine=False) else: data *= cosTaper(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.nextpow2(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 = pazToFreqResp(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 = c_sac_taper(freqs, flimit=(fl1, fl2, fl3, fl4)) else: cos_win = cosTaper(freqs.size, freqs=freqs, flimit=(fl1, fl2, fl3, fl4)) data *= cos_win specInv(freq_response, water_level) data *= freq_response del freq_response # Forward filtering = Instrument simulation if paz_simulate: data *= pazToFreqResp(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 = simpleDetrend(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 mtinv_gs(st_tr, gl, fmin, fmax, fmax_hardcut_factor=4, S0=None, nsv=1, single_force=False, stat_subset=[], weighting_type=2, weights=[], cache_path='', force_recalc=False, cache=False, w_level=50, method='full_pca', constrained_sources=None): ''' Frequency domain moment tensor inversion. Features: - grid search for best source location - direct inversion for different error measures (see weighting type) - constrain to limited number of source mechanisms/time dependencies using principal component analysis - deconvolution of source time function used for Green's function simulations - caching the Green's matrix in Frequency space for speed up of repeated evaluation Theory see Diplomathesis 'The Effect of Tilt on Moment Tensor Inversion in the Nearfield of Active Volcanoes' section 2.2 :type st_tr: :class:`~obspy.core.stream.Stream` :param st_tr: Stream containing the seismograms, station names are numbers starting with 0001, channels are ['u', 'v', 'w'] :type gl: list of :class:`~obspy.core.stream.Stream` objects or list :param gl: list of Streams containing the green's functions, station names are numbers starting with 0001, channels are of format '%02d%1d' where the first number is the component of the receiver ([0,1,2]) and the second the source ([0,1,...,5] or [0,1,...,8] including single forces). Should have the same sample rate as st_tr. If no grid search is needed, just put a single stream in the list. In case of a high number of stations (>250), gl can be a list of strings containing the path to and names of the greens functions files :type fmin: float :param fmin: high pass frequency :type fmax: float :param fmax: low pass frequency :type fmax_hardcut_factor: int :param fmax_hardcut_factor: multiple of fmax where the inversion is stopped :type S0: :class:`numpy.ndarray` :param S0: source time function used for computation of the Green's function to deconvolve. Is assumed to have same sample rate as st_tr. If S0 is None, no deconvolution is performed. :type nsv: int :param nsv: number of mechanisms corresponding to the largest singular values in the constrained inversion :type single_force: Bool :param single_force: include single force in the inversion (then green's functions for single forces are needed in channels ([6,7,8])) :type stat_subset: List :param stat_subset: List of stations to use in the inversion :type weighting: int :param weighting_type: should be one of [0,1,2], 0: no weighting, 1: use l2 norm weighting for each trace, 2: use l2 norm weighting for each station (weight is sum of the weights of the traces) :type weights: listtype :param weights: a priori relative weighting of stations, should have length nstat or same length as stat_subset :type cache_path: string :param cache_path: path to a folder to cache Green's matrix :type force_recalc: Bool :param force_recalc: force reevalution of fourier transform of Green's functions (if cached in 'cache_path' to speed up inversion). :type cache: Bool :param cache: cache in a file or not :type w_level: int :param w_level: value of waterlevel in dB under max amplitude in spectrum of deconvolved Green's function returns a tuple containing: M_t - time dependent Momenttensor solution (if nsv > 1 than summed up mechanisms) m - time independent Momenttensor (nsv arrays) x - time dependence of principal component solutions in m s - singular values of principal components st_syn - synthetic seismograms generated with the inverted source st_tr - input seismograms filtered the same way as the synthetics (for comparison to synthetic seismograms) misfit - misfit of synthetics, definition depends on weighting (is only mathematically strict minimized for nsv=6 (9 in case of single force), otherwise approximately) argmin - index of Green's function in list that minimizes the misfit ''' st_tr = st_tr.copy() st_tr.filter('lowpass', freq=fmax, corners=4) st_tr.filter('highpass', freq=fmin, corners=4) # test if gl is a list or a stream if type(gl[0]) == str: st_g = read('%s'%(gl[0])) else: st_g = gl[0] ng = st_g[0].data.size df = st_tr[0].stats.sampling_rate dt = 1./df nstat = st_tr.select(channel='u').count() ndat = st_tr[0].data.size nfft = util.nextpow2(max(ndat, ng) * 2) # use freuquncies below corner frequency * fmax_hardcut_factor (tremendous # speed up in inversion) nfinv = int(fmax_hardcut_factor * fmax / (df / 2.) * nfft) nfinv = min(nfft/2+1, nfinv) # going to frequency domain # correction for stf used in green's forward simulation if S0 is None: S0w = 1. else: S0w = np.fft.rfft(S0, n=nfft)[:nfinv] * dt # introduce waterlevel to prevent instabilities in deconvolution of # greens functions, see obspy.signal.invsim specInv # Calculated waterlevel in the scale of spec swamp = waterlevel(S0w, w_level) # Find length in real fft frequency domain, spec is complex sqrt_len = np.abs(S0w) # Set/scale length to swamp, but leave phase untouched # 0 sqrt_len will transform in np.nans when dividing by it idx = np.where((sqrt_len < swamp) & (sqrt_len > 0.0)) S0w[idx] *= swamp / sqrt_len[idx] # setup seismogram matrix in fourier space utr = np.zeros((nstat * 3, ndat)) utrw = np.zeros((nstat * 3, nfft/2+1)) * 0j weights_l2 = np.zeros(nstat * 3) for k in np.arange(nstat): for i, chan in enumerate(['u', 'v', 'w']): utr[k*3 + i,:] = st_tr.select(station='%04d' % (k+1), channel=chan)[0].data utrw[k*3 + i,:] = np.fft.rfft(utr[k*3 + i,:], n=nfft) * dt # calculate l2 norm for weighting weights_l2[k*3 + i] = cumtrapz(utr[k*3 + i,:]**2, dx=dt)[-1] M_tl = [] ml = [] xl = [] sl = [] st_synl = [] misfitl = [] st_tr.filter('lowpass', freq=fmax, corners=4) if method == 'full_pca': for i, stg in enumerate(gl): if type(stg) == str: st_g = read('%s'%(stg)) else: st_g = stg print i if round(st_tr[0].stats.sampling_rate, 5) != round(st_g[0].stats.sampling_rate, 5): msg = 'sampling rates of Seismograms and Green\'s function are not the' msg = msg + ' same: %f != %f' % (st_tr[0].stats.sampling_rate, st_g[0].stats.sampling_rate) raise ValueError(msg) M_t, m, x, s, st_syn, misfit = mtinv((utrw, weights_l2.copy(), S0w, df, dt, nstat, ndat, ng, nfft, nfinv), st_tr, st_g, fmin, fmax, nsv=nsv, single_force=single_force, stat_subset=stat_subset, force_recalc=force_recalc, weighting_type=weighting_type, weights=weights, cache_path=cache_path + ('%06d_' % i), cache=cache) M_tl.append(M_t) ml.append(m) xl.append(x) sl.append(s) st_synl.append(st_syn) misfitl.append(misfit) misfit = np.array(misfitl) argmin = misfit.argmin() return M_tl[argmin], ml[argmin], xl[argmin], sl[argmin], st_synl[argmin], st_tr, misfit, argmin elif method == 'constrained': for i, stg in enumerate(gl): if type(stg) == str: st_g = read('%s'%(stg)) else: st_g = stg print i if round(st_tr[0].stats.sampling_rate, 5) != round(st_g[0].stats.sampling_rate, 5): msg = 'sampling rates of Seismograms and Green\'s function are not the' msg = msg + ' same: %f != %f' % (st_tr[0].stats.sampling_rate, st_g[0].stats.sampling_rate) raise ValueError(msg) M, stf, misfit, st_syn = mtinv_constrained((utrw, weights_l2.copy(), S0w, df, dt, nstat, ndat, ng, nfft, nfinv), st_tr, st_g, fmin, fmax, nsv=nsv, single_force=single_force, stat_subset=stat_subset, force_recalc=force_recalc, weighting_type=weighting_type, weights=weights, cache_path=cache_path + ('%06d_' % i), cache=cache, constrained_sources=constrained_sources) M_tl.append(M) sl.append(stf) st_synl.append(st_syn) misfitl.append(misfit) misfit = np.array(misfitl) argmin = misfit.argmin() return M_tl[argmin], sl[argmin], st_synl[argmin], st_tr, misfit, argmin
def relcalstack(st1, st2, calib_file, window_len, overlap_frac=0.5, smooth=0, save_data=True): """ Method for relative calibration of sensors using a sensor with known transfer function :param st1: Stream or Trace object, (known) :param st2: Stream or Trace object, (unknown) :type calib_file: str :param calib_file: file name of calibration file containing the PAZ of the known instrument in GSE2 standard. :type window_len: float :param window_len: length of sliding window in seconds :type overlap_frac: float :param overlap_frac: fraction of overlap, defaults to fifty percent (0.5) :type smooth: float :param smooth: variable that defines if the Konno-Ohmachi taper is used or not. default = 0 -> no taper generally used in geopsy: smooth = 40 :type save_data: bool :param save_data: Whether or not to save the result to a file. If True, two output files will be created: * The new response in station_name.window_length.resp * The ref response in station_name.refResp Defaults to True :returns: frequency, amplitude and phase spectrum implemented after relcalstack.c by M.Ohrnberger and J.Wassermann. """ # transform given trace objects to streams if isinstance(st1, Trace): st1 = Stream([st1]) if isinstance(st2, Trace): st2 = Stream([st2]) # check if sampling rate and trace length is the same if st1[0].stats.npts != st2[0].stats.npts: msg = "Traces don't have the same length!" raise ValueError(msg) elif st1[0].stats.sampling_rate != st2[0].stats.sampling_rate: msg = "Traces don't have the same sampling rate!" raise ValueError(msg) else: ndat1 = st1[0].stats.npts sampfreq = st1[0].stats.sampling_rate # read waveforms tr1 = st1[0].data.astype(np.float64) tr2 = st2[0].data.astype(np.float64) # get window length, nfft and frequency step ndat = int(window_len * sampfreq) nfft = nextpow2(ndat) # read calib file and calculate response function gg, _freq = _calcresp(calib_file, nfft, sampfreq) # calculate number of windows and overlap nwin = int(np.floor((ndat1 - nfft) / (nfft / 2)) + 1) noverlap = nfft * overlap_frac auto, _freq, _t = \ spectral_helper(tr1, tr1, NFFT=nfft, Fs=sampfreq, noverlap=noverlap) cross, freq, _t = \ spectral_helper(tr2, tr1, NFFT=nfft, Fs=sampfreq, noverlap=noverlap) res = (cross / auto).sum(axis=1) * gg # The first item might be zero. Problems with phase calculations. res = res[1:] freq = freq[1:] gg = gg[1:] res /= nwin # apply Konno-Ohmachi smoothing taper if chosen if smooth > 0: # Write in one matrix for performance reasons. spectra = np.empty((2, len(res.real))) spectra[0] = res.real spectra[1] = res.imag new_spectra = \ konnoOhmachiSmoothing(spectra, freq, bandwidth=smooth, count=1, max_memory_usage=1024, normalize=True) res.real = new_spectra[0] res.imag = new_spectra[1] amp = np.abs(res) # include phase unwrapping phase = np.unwrap(np.angle(res)) # + 2.0 * np.pi ra = np.abs(gg) rpha = np.unwrap(np.angle(gg)) if save_data: trans_new = (st2[0].stats.station + "." + st2[0].stats.channel + "." + str(window_len) + ".resp") trans_ref = st1[0].stats.station + ".refResp" # Create empty array for easy saving temp = np.empty((len(freq), 3)) temp[:, 0] = freq temp[:, 1] = amp temp[:, 2] = phase np.savetxt(trans_new, temp, fmt="%.10f") temp[:, 1] = ra temp[:, 2] = rpha np.savetxt(trans_ref, temp, fmt="%.10f") return freq, amp, phase
def deconvf(rsp_list, src, sampling_rate, water=0.05, gauss=2., tshift=10., pad=0, length=None, normalize=True, normalize_to_src=False, returnall=False): """ Frequency-domain deconvolution using waterlevel method. rsp, src data containing the response and source functions, respectively water waterlevel to stabilize the deconvolution gauss Gauss parameter of Low-pass filter tshift shift the resulting function by that amount pad multiply number of samples used for fft by 2**pad """ if length == None: length = len(src) N = length nfft = nextpow2(N) * 2**pad freq = np.fft.fftfreq(nfft, d=1. / sampling_rate) gauss = np.exp( maximum(-(0.5 * 2 * pi * freq / gauss)**2, -700.) - 1j * tshift * 2 * pi * freq) spec_src = fft(src, nfft) spec_src_conj = np.conjugate(spec_src) spec_src_water = np.abs(spec_src * spec_src_conj) spec_src_water = np.maximum(spec_src_water, max(spec_src_water) * water) if normalize_to_src: spec_src = gauss * spec_src * spec_src_conj / spec_src_water rf_src = ifft(spec_src, nfft)[:N] #i1 = int((tshift-1)*sampling_rate) #i2 = int((tshift+1)*sampling_rate) norm = 1 / max(rf_src) rf_src = norm * rf_src flag = False if not isinstance(rsp_list, (list, tuple)): flag = True rsp_list = [rsp_list] rf_list = [ ifft(gauss * fft(rsp, nfft) * spec_src_conj / spec_src_water, nfft)[:N] for rsp in rsp_list ] if normalize: if not normalize_to_src: norm = 1. / max(rf_list[0]) for rf in rf_list: rf *= norm if returnall: if not normalize_to_src: spec_src = gauss * spec_src * spec_src_conj / spec_src_water rf_src = ifft(spec_src, nfft)[:N] norm = 1 / max(rf_src) rf_src = norm * rf_src return rf_list, rf_src, spec_src_conj, spec_src_water, freq, gauss, norm, N, nfft elif flag: return rf else: return rf_list
def xcorrf(data1, data2, shift=None, shift_zero=0, oneside=False, demean=True, window=0, ndat1d=0, ndat2d=0, N1=None, N2=None, normalize=True, freq_domain=False, transform_back=True, stdev1=None, stdev2=None): """ Cross-correlation of numpy arrays data1 and data2 in frequency domain. We define cross-corelation as: xcorr[i] = sum_j (tr1[i+j-shift_zero] * tr2[j]) The data is demeaned before cross-correlation and the result normalized after cross-correlation. data1, data2: data shift: maximum samples to shift (window for i in the above formula) shift_zero: shift tr1 before cross-correlation by this amount of samples to the right (this means correlation function is shifted to the right or better: the window of what you get of the function is shifted to the left) oneside: if True only the right/positive side of the correlation function is returned. Overrides parameter shift_zero. demean: if True demean data beforehand normalize: if True normalize correlation function (1 means perfect correlation) window: Use only data in this window for demeaning and normalizing 0: window = min(ndat1, ndat2) >0: window = this parameter ndat1d, ndat2d: If >0 use different values for the length of the arrays when calculating the mean (defaults to window parameter) return: numpy array with correlation function of length 2*shift+1 for oneside=False and of length shift+1 for oneside=True """ if freq_domain and not transform_back: return data1 * np.conjugate(data2) elif freq_domain: min_size = max(2 * shift + 1 + abs(shift_zero), (N1 + N2) // 2 + shift + abs(shift_zero)) if len(data1) < min_size: raise ValueError('NFFT was not large enough to cover the desired ' 'xcorr!\nnfft: %d, required minimum: %d' % (len(data1), min_size)) ret = (ifft(data1 * np.conjugate(data2))).real else: complex_result = (data1.dtype == np.complex or data2.dtype == np.complex) N1 = len(data1) N2 = len(data2) #if isinstance(data1[0], np.integer) or isinstance(data2[0], np.integer): data1 = data1.astype('float64') data2 = data2.astype('float64') #if (N1-N2)%2==1: # raise ValueError('(N1-N2)%2 has to be 0') if window == 0: window = min(N1, N2) if ndat1d == 0: ndat1d = window if ndat2d == 0: ndat2d = window # determine indices for demeaning and normalization ind1 = max(0, (N1 - window) // 2) ind2 = min(N1, (N1 + window) // 2) ind3 = max(0, (N2 - window) // 2) ind4 = min(N2, (N2 + window) // 2) # demean and normalize data if demean: data1 -= np.sum(data1[ind1:ind2]) / ndat1d data2 -= np.sum(data2[ind3:ind4]) / ndat2d if normalize: data1 /= np.max(data1[ind1:ind2]) data2 /= np.max(data2[ind3:ind4]) # Always use 2**n-sized FFT, perform xcorr size = max(2 * shift + 1 + abs(shift_zero), (N1 + N2) // 2 + shift + abs(shift_zero)) nfft = nextpow2(size) IN1 = fft(data1, nfft) if USE_FFTW3: IN1 = IN1.copy() IN1 *= np.conjugate(fft(data2, nfft)) ret = ifft(IN1) if not USE_FFTW3: del IN1 if not complex_result: ret = ret.real # shift data for time lag 0 to index 'shift' ret = np.roll(ret, -(N1 - N2) // 2 + shift + shift_zero)[:2 * shift + 1] # normalize xcorr if normalize: if not freq_domain: stdev1 = (np.sum(data1[ind1:ind2] ** 2)) ** 0.5 stdev2 = (np.sum(data2[ind3:ind4] ** 2)) ** 0.5 # stdev1 = (np.sum(data1 ** 2)) ** 0.5 # stdev2 = (np.sum(data2 ** 2)) ** 0.5 if stdev1 == 0 or stdev2 == 0: log.warning('Data is zero!!') ret[:] = 0. else: ret /= stdev1 * stdev2 if oneside: ret = ret[shift:] return np.copy(ret)
def CalcResp(poles, zeros, scaleFac, MaxPer, sampfreq): ndat = MaxPer * sampfreq nfft = nextpow2(ndat) buffer = np.empty(nfft/2+1, dtype='complex128') # poles = [] # zeros = [] # file = open(str(pazFile), 'r') # # # read file until calibration section is found # text = ' ' # while text != 'CAL1': # textln = file.readline() # text = textln.split(' ')[0] # if not text == 'CAL1': # msg = 'could not find calibration section!' # raise NameError(msg) # else: # cal = textln[31:34] # if cal == 'PAZ': # # read poles # npoles = int(file.readline()) # for i in xrange(npoles): # pole = file.readline() # pole_r = float(pole.split(" ")[0]) # pole_i = float(pole.split(" ")[1]) # pole_c = pole_r + pole_i * 1.j # poles.append(pole_c) # # read zeros # nzeros = int(file.readline()) # for i in xrange(nzeros): # zero = file.readline() # zero_r = float(zero.split(" ")[0]) # zero_i = float(zero.split(" ")[1]) # zero_c = zero_r + zero_i * 1.j # zeros.append(zero_c) # # read scale factor # scale_fac = float(file.readline()) # file.close scale_fac = scaleFac # calculate transfer function delta_f = sampfreq / nfft F = np.empty(nfft/2+1) for i in xrange(nfft/2 + 1): fr = i * delta_f F[i] = fr om = 2 * np.pi * fr num = 1. + 0.j for ii in xrange(len(zeros)): s = 0. + om * 1.j dif = s - zeros[ii] num = dif * num denom = 1. + 0.j for ii in xrange(len(poles)): s = 0. + om * 1.j dif = s - poles[ii] denom = dif * denom t_om = 1. + 0.j if denom.real != 0. or denom.imag != 0.: t_om = num / denom t_om *= scale_fac if i < nfft/2 and i > 0: buffer[i] = t_om if i == 0: buffer[i] = t_om + 0.j if i == nfft/2: buffer[i] = t_om + 0.j # plot amp = abs(buffer) phase = np.arctan(buffer.imag / buffer.real) / np.pi * 180 # Plot fig = plt.figure() ax1 = plt.subplot(211) ax1.loglog(F, amp) ax1.set_ylabel('Amplitude') ax2 = plt.subplot(212) ax2.semilogx(F, phase) ax2.set_xlabel('Frequenzy [Hz]') ax2.set_ylabel('Phase') plt.show()
def relcalstack(st1, st2, calib_file, window_len, OverlapFrac=0.5, smooth=0): """ Method for relative calibration of sensors using a sensor with known transfer function :param st1/st2: Stream object, (known/unknown) the trace.stats dict like class must contain \ the parameters "sampling_rate", "npts" and "station" :type calib_file: String :param calib_file: name of calib file containing the known PAZ of known instrument in GSE2 standard. :type window_len: Float :param window_len: length of sliding window in seconds :type OverlapFrac: float :param OverlapFrac: fraction of overlap, defaults to fifty percent (0.5) :type smooth: Float :param smooth: variable that defines if the Konno-Ohmachi taper is used or not. default = 0 -> no taper \ generally used in geopsy: smooth = 40 """ # check Konno-Ohmachi if smooth < 0: smooth = 0 # check if sampling rate and trace length is the same if st1[0].stats.npts != st2[0].stats.npts: msg = 'Traces dont have the same length!' raise ValueError(msg) elif st1[0].stats.sampling_rate != st2[0].stats.sampling_rate: msg = 'Traces dont have the same sampling rate!' raise ValueError(msg) else: ndat1 = st1[0].stats.npts ndat2 = st2[0].stats.npts sampfreq = st1[0].stats.sampling_rate # read waveforms tr1 = st1[0].data.astype(np.float64) tr2 = st2[0].data.astype(np.float64) # get window length, nfft and frequency step ndat = int(window_len*sampfreq) nfft = nextpow2(ndat) df = sampfreq / nfft # initialize array for response function res = np.zeros(nfft/2+1, dtype='complex128') # read calib file and calculate response function gg, freq = calcresp(calib_file, nfft, sampfreq) # calculate number of windows and overlap nwin = int(np.floor((ndat1 - nfft)/(nfft/2)) + 1) noverlap = nfft * OverlapFrac auto, freq, t = spectral_helper(tr1, tr1, NFFT=nfft, Fs=sampfreq, noverlap = noverlap) cross, freq, t = spectral_helper(tr1, tr2, NFFT=nfft, Fs=sampfreq, noverlap = noverlap) # 180 Grad Phasenverschiebung cross.imag = -cross.imag for i in range(nwin): res += (cross[:,i] / auto[:,i]) * gg # apply Konno-Ohmachi smoothing taper if choosen if smooth > 0: res /= nwin res.real = smooth_spectra(res.real, freq, smooth, count=1, max_memory_in_mb=1024) res.imag = smooth_spectra(res.imag, freq, smooth, count=1, max_memory_in_mb=1024) else: res /= nwin #print 'Writing output....' f = open(st2[0].stats.station+"."+str(window_len)+".resp", "w") g = open(st1[0].stats.station+".refResp", "w") amp = [] phase = [] for i in range(nfft/2+1): a = np.sqrt(res.real[i]*res.real[i]+res.imag[i]*res.imag[i]) pha = np.arctan2(res.imag[i],res.real[i]) ra = np.sqrt(gg.real[i]*gg.real[i]+gg.imag[i]*gg.imag[i]) rpha = np.arctan2(gg.imag[i],gg.real[i]) amp.append(a) phase.append(pha) f.write("%f %f %f\n" %(freq[i], a, pha)) g.write("%f %f %f\n" %(freq[i], ra, rpha)) f.close() g.close() return freq, amp, phase
def deconvf(rsp_list, src, sampling_rate, water=0.05, gauss=2., tshift=10., pad=0, length=None, normalize=True, normalize_to_src=False, return_dict=False): """ Frequency-domain deconvolution using waterlevel method. Deconvolve src from arrays in rsp_list. :param rsp_list: either a list of arrays containing the response functions or a single array :param src: array of source function :param sampling_rate: sampling rate of the data :param water: waterlevel to stabilize the deconvolution :param gauss: Gauss parameter of Low-pass filter :param tshift: delay time 0s will be at time tshift afterwards :param pad: multiply number of samples used for fft by 2**pad :param length: number of data points in results, optional :param normalize: if results are normalized :param normalize_to_src: True -> normalized so that the maximum of a deconvolution of the source with itself is 1\n False -> normalized so that the maximum of the deconvolution of the first response array in rsp_list is 1 :param return_dict: return additionally a lot of different parameters in a dict for debugging purposes :return: (list of) array(s) with deconvolution(s) """ if length is None: length = len(src) N = length nfft = nextpow2(N) * 2 ** pad freq = np.fft.fftfreq(nfft, d=1. / sampling_rate) gauss = np.exp(np.maximum(-(0.5 * 2 * pi * freq / gauss) ** 2, -700.) - 1j * tshift * 2 * pi * freq) spec_src = fft(src, nfft) spec_src_conj = np.conjugate(spec_src) spec_src_water = np.abs(spec_src * spec_src_conj) spec_src_water = np.maximum(spec_src_water, max(spec_src_water) * water) if normalize_to_src: spec_src = gauss * spec_src * spec_src_conj / spec_src_water rf_src = ifft(spec_src, nfft)[:N] norm = 1 / max(rf_src) rf_src = norm * rf_src flag = False if not isinstance(rsp_list, (list, tuple)): flag = True rsp_list = [rsp_list] rf_list = [ifft(gauss * fft(rsp, nfft) * spec_src_conj / spec_src_water, nfft)[:N] for rsp in rsp_list] if normalize: if not normalize_to_src: norm = 1. / max(rf_list[0]) for rf in rf_list: rf *= norm if return_dict: if not normalize_to_src: spec_src = gauss * spec_src * spec_src_conj / spec_src_water rf_src = ifft(spec_src, nfft)[:N] norm = 1 / max(rf_src) rf_src = norm * rf_src ret_dict = {'rf_src': rf_src, 'rf_src_conj': spec_src_conj, 'spec_src_water': spec_src_water, 'freq': freq, 'gauss': gauss, 'norm': norm, 'N': N, 'nfft': nfft} return rf_list, ret_dict elif flag: return rf else: return rf_list
def xcorrf(data1, data2, shift=None, shift_zero=0, oneside=False, demean=True, window=0, ndat1d=0, ndat2d=0, N1=None, N2=None, normalize=True, freq_domain=False, transform_back=True, stdev1=None, stdev2=None): """ Cross-correlation of numpy arrays data1 and data2 in frequency domain. We define cross-corelation as: xcorr[i] = sum_j (tr1[i+j-shift_zero] * tr2[j]) The data is demeaned before cross-correlation and the result normalized after cross-correlation. data1, data2: data shift: maximum samples to shift (window for i in the above formula) shift_zero: shift tr1 before cross-correlation by this amount of samples to the right (this means correlation function is shifted to the right or better: the window of what you get of the function is shifted to the left) oneside: if True only the right/positive side of the correlation function is returned. Overrides parameter shift_zero. demean: if True demean data beforehand normalize: if True normalize correlation function (1 means perfect correlation) window: Use only data in this window for demeaning and normalizing 0: window = min(ndat1, ndat2) >0: window = this parameter ndat1d, ndat2d: If >0 use different values for the length of the arrays when calculating the mean (defaults to window parameter) return: numpy array with correlation function of length 2*shift+1 for oneside=False and of length shift+1 for oneside=True """ if freq_domain and not transform_back: return data1 * np.conjugate(data2) elif freq_domain: min_size = max(2 * shift + 1 + abs(shift_zero), (N1 + N2) // 2 + shift + abs(shift_zero)) if len(data1) < min_size: raise ValueError('NFFT was not large enough to cover the desired ' 'xcorr!\nnfft: %d, required minimum: %d' % (len(data1), min_size)) ret = (ifft(data1 * np.conjugate(data2))).real else: complex_result = (data1.dtype == np.complex or data2.dtype == np.complex) N1 = len(data1) N2 = len(data2) #if isinstance(data1[0], np.integer) or isinstance(data2[0], np.integer): data1 = data1.astype('float64') data2 = data2.astype('float64') #if (N1-N2)%2==1: # raise ValueError('(N1-N2)%2 has to be 0') if window == 0: window = min(N1, N2) if ndat1d == 0: ndat1d = window if ndat2d == 0: ndat2d = window # determine indices for demeaning and normalization ind1 = max(0, (N1 - window) // 2) ind2 = min(N1, (N1 + window) // 2) ind3 = max(0, (N2 - window) // 2) ind4 = min(N2, (N2 + window) // 2) # demean and normalize data if demean: data1 -= np.sum(data1[ind1:ind2]) / ndat1d data2 -= np.sum(data2[ind3:ind4]) / ndat2d if normalize: data1 /= np.max(data1[ind1:ind2]) data2 /= np.max(data2[ind3:ind4]) # Always use 2**n-sized FFT, perform xcorr size = max(2 * shift + 1 + abs(shift_zero), (N1 + N2) // 2 + shift + abs(shift_zero)) nfft = nextpow2(size) IN1 = fft(data1, nfft) if USE_FFTW3: IN1 = IN1.copy() IN1 *= np.conjugate(fft(data2, nfft)) ret = ifft(IN1) if not USE_FFTW3: del IN1 if not complex_result: ret = ret.real # shift data for time lag 0 to index 'shift' ret = np.roll(ret, -(N1 - N2) // 2 + shift + shift_zero)[:2 * shift + 1] # normalize xcorr if normalize: if not freq_domain: stdev1 = (np.sum(data1[ind1:ind2]**2))**0.5 stdev2 = (np.sum(data2[ind3:ind4]**2))**0.5 # stdev1 = (np.sum(data1 ** 2)) ** 0.5 # stdev2 = (np.sum(data2 ** 2)) ** 0.5 if stdev1 == 0 or stdev2 == 0: log.warning('Data is zero!!') ret[:] = 0. else: ret /= stdev1 * stdev2 if oneside: ret = ret[shift:] return np.copy(ret)
def array_processing(stream, win_len, win_frac, sll_x, slm_x, sll_y, slm_y, sl_s, semb_thres, vel_thres, frqlow, frqhigh, stime, etime, prewhiten, verbose=False, coordsys='lonlat', timestamp='mlabday', method=0, store=None): """ Method for Seismic-Array-Beamforming/FK-Analysis/Capon :param stream: Stream object, the trace.stats dict like class must contain an :class:`~obspy.core.util.attribdict.AttribDict` with 'latitude', 'longitude' (in degrees) and 'elevation' (in km), or 'x', 'y', 'elevation' (in km) items/attributes. See param ``coordsys``. :type win_len: float :param win_len: Sliding window length in seconds :type win_frac: float :param win_frac: Fraction of sliding window to use for step :type sll_x: float :param sll_x: slowness x min (lower) :type slm_x: float :param slm_x: slowness x max :type sll_y: float :param sll_y: slowness y min (lower) :type slm_y: float :param slm_y: slowness y max :type sl_s: float :param sl_s: slowness step :type semb_thres: float :param semb_thres: Threshold for semblance :type vel_thres: float :param vel_thres: Threshold for velocity :type frqlow: float :param frqlow: lower frequency for fk/capon :type frqhigh: float :param frqhigh: higher frequency for fk/capon :type stime: :class:`~obspy.core.utcdatetime.UTCDateTime` :param stime: Start time of interest :type etime: :class:`~obspy.core.utcdatetime.UTCDateTime` :param etime: End time of interest :type prewhiten: int :param prewhiten: Do prewhitening, values: 1 or 0 :param coordsys: valid values: 'lonlat' and 'xy', choose which stream attributes to use for coordinates :type timestamp: str :param timestamp: valid values: 'julsec' and 'mlabday'; 'julsec' returns the timestamp in seconds since 1970-01-01T00:00:00, 'mlabday' returns the timestamp in days (decimals represent hours, minutes and seconds) since '0001-01-01T00:00:00' as needed for matplotlib date plotting (see e.g. matplotlib's num2date) :type method: int :param method: the method to use 0 == bf, 1 == capon :type store: function :param store: A custom function which gets called on each iteration. It is called with the relative power map and the time offset as first and second arguments and the iteration number as third argument. Useful for storing or plotting the map for each iteration. For this purpose the dump function of this module can be used. :return: :class:`numpy.ndarray` of timestamp, relative relpow, absolute relpow, backazimuth, slowness """ res = [] eotr = True # check that sampling rates do not vary fs = stream[0].stats.sampling_rate if len(stream) != len(stream.select(sampling_rate=fs)): msg = 'in sonic sampling rates of traces in stream are not equal' raise ValueError(msg) grdpts_x = int(((slm_x - sll_x) / sl_s + 0.5) + 1) grdpts_y = int(((slm_y - sll_y) / sl_s + 0.5) + 1) geometry = get_geometry(stream, coordsys=coordsys, verbose=verbose) if verbose: print("geometry:") print(geometry) print("stream contains following traces:") print(stream) print(("stime = " + str(stime) + ", etime = " + str(etime))) time_shift_table = get_timeshift(geometry, sll_x, sll_y, sl_s, grdpts_x, grdpts_y) # offset of arrays spoint, _epoint = get_spoint(stream, stime, etime) # # loop with a sliding window over the dat trace array and apply bbfk # nstat = len(stream) fs = stream[0].stats.sampling_rate nsamp = int(win_len * fs) nstep = int(nsamp * win_frac) # generate plan for rfftr nfft = nextpow2(nsamp) deltaf = fs / float(nfft) nlow = int(frqlow / float(deltaf) + 0.5) nhigh = int(frqhigh / float(deltaf) + 0.5) nlow = max(1, nlow) # avoid using the offset nhigh = min(nfft // 2 - 1, nhigh) # avoid using nyquist nf = nhigh - nlow + 1 # include upper and lower frequency # to spead up the routine a bit we estimate all steering vectors in advance steer = np.empty((nf, grdpts_x, grdpts_y, nstat), dtype='c16') clibsignal.calcSteer(nstat, grdpts_x, grdpts_y, nf, nlow, deltaf, time_shift_table, steer) R = np.empty((nf, nstat, nstat), dtype='c16') ft = np.empty((nstat, nf), dtype='c16') newstart = stime tap = cosTaper(nsamp, p=0.22) # 0.22 matches 0.2 of historical C bbfk.c offset = 0 relpow_map = np.empty((grdpts_x, grdpts_y), dtype='f8') abspow_map = np.empty((grdpts_x, grdpts_y), dtype='f8') while eotr: try: for i, tr in enumerate(stream): dat = tr.data[spoint[i] + offset: spoint[i] + offset + nsamp] dat = (dat - dat.mean()) * tap ft[i, :] = np.fft.rfft(dat, nfft)[nlow:nlow + nf] except IndexError: break ft = np.require(ft, 'c16', ['C_CONTIGUOUS']) relpow_map.fill(0.) abspow_map.fill(0.) # computing the covariances of the signal at different receivers dpow = 0. for i in range(nstat): for j in range(i, nstat): R[:, i, j] = ft[i, :] * ft[j, :].conj() if method == 1: R[:, i, j] /= np.abs(R[:, i, j].sum()) if i != j: R[:, j, i] = R[:, i, j].conjugate() else: dpow += np.abs(R[:, i, j].sum()) dpow *= nstat if method == 1: # P(f) = 1/(e.H R(f)^-1 e) for n in range(nf): R[n, :, :] = np.linalg.pinv(R[n, :, :], rcond=1e-6) errcode = clibsignal.generalizedBeamformer( relpow_map, abspow_map, steer, R, nstat, prewhiten, grdpts_x, grdpts_y, nf, dpow, method) if errcode != 0: msg = 'generalizedBeamforming exited with error %d' raise Exception(msg % errcode) ix, iy = np.unravel_index(relpow_map.argmax(), relpow_map.shape) relpow, abspow = relpow_map[ix, iy], abspow_map[ix, iy] if store is not None: store(relpow_map, abspow_map, offset) # here we compute baz, slow slow_x = sll_x + ix * sl_s slow_y = sll_y + iy * sl_s slow = np.sqrt(slow_x ** 2 + slow_y ** 2) if slow < 1e-8: slow = 1e-8 azimut = 180 * math.atan2(slow_x, slow_y) / math.pi baz = azimut % -360 + 180 if relpow > semb_thres and 1. / slow > vel_thres: res.append(np.array([newstart.timestamp, relpow, abspow, baz, slow])) if verbose: print((newstart, (newstart + (nsamp / fs)), res[-1][1:])) if (newstart + (nsamp + nstep) / fs) > etime: eotr = False offset += nstep newstart += nstep / fs res = np.array(res) if timestamp == 'julsec': pass elif timestamp == 'mlabday': # 719162 == hours between 1970 and 0001 res[:, 0] = res[:, 0] / (24. * 3600) + 719162 else: msg = "Option timestamp must be one of 'julsec', or 'mlabday'" raise ValueError(msg) return np.array(res)
def mtinv_gs(st_tr, gl, fmin, fmax, fmax_hardcut_factor=4, S0=None, nsv=1, single_force=False, stat_subset=[], weighting_type=2, weights=[], cache_path='', force_recalc=False, cache=False, w_level=50): ''' Frequency domain moment tensor inversion. Features: - grid search for best source location - direct inversion for different error measures (see weighting type) - constrain to limited number of source mechanisms/time dependencies using principal component analysis - deconvolution of source time function used for Green's function simulations - caching the Green's matrix in Frequency space for speed up of repeated evaluation Theory see Diplomathesis 'The Effect of Tilt on Moment Tensor Inversion in the Nearfield of Active Volcanoes' section 2.2 :type st_tr: :class:`~obspy.core.stream.Stream` :param st_tr: Stream containing the seismograms, station names are numbers starting with 0001, channels are ['u', 'v', 'w'] :type gl: list of :class:`~obspy.core.stream.Stream` objects or list :param gl: list of Streams containing the green's functions, station names are numbers starting with 0001, channels are of format '%02d%1d' where the first number is the component of the receiver ([0,1,2]) and the second the source ([0,1,...,5] or [0,1,...,8] including single forces). Should have the same sample rate as st_tr. If no grid search is needed, just put a single stream in the list. In case of a high number of stations (>250), gl can be a list of strings containing the path to and names of the greens functions files :type fmin: float :param fmin: high pass frequency :type fmax: float :param fmax: low pass frequency :type fmax_hardcut_factor: int :param fmax_hardcut_factor: multiple of fmax where the inversion is stopped :type S0: :class:`numpy.ndarray` :param S0: source time function used for computation of the Green's function to deconvolve. Is assumed to have same sample rate as st_tr. If S0 is None, no deconvolution is performed. :type nsv: int :param nsv: number of mechanisms corresponding to the largest singular values in the constrained inversion :type single_force: Bool :param single_force: include single force in the inversion (then green's functions for single forces are needed in channels ([6,7,8])) :type stat_subset: List :param stat_subset: List of stations to use in the inversion :type weighting: int :param weighting_type: should be one of [0,1,2], 0: no weighting, 1: use l2 norm weighting for each trace, 2: use l2 norm weighting for each station (weight is sum of the weights of the traces) :type weights: listtype :param weights: a priori relative weighting of stations, should have length nstat or same length as stat_subset :type cache_path: string :param cache_path: path to a folder to cache Green's matrix :type force_recalc: Bool :param force_recalc: force reevalution of fourier transform of Green's functions (if cached in 'cache_path' to speed up inversion). :type cache: Bool :param cache: cache in a file or not :type w_level: int :param w_level: value of waterlevel in dB under max amplitude in spectrum of deconvolved Green's function returns a tuple containing: M_t - time dependent Momenttensor solution (if nsv > 1 than summed up mechanisms) m - time independent Momenttensor (nsv arrays) x - time dependence of principal component solutions in m s - singular values of principal components st_syn - synthetic seismograms generated with the inverted source st_tr - input seismograms filtered the same way as the synthetics (for comparison to synthetic seismograms) misfit - misfit of synthetics, definition depends on weighting (is only mathematically strict minimized for nsv=6 (9 in case of single force), otherwise approximately) argmin - index of Green's function in list that minimizes the misfit ''' st_tr = st_tr.copy() st_tr.filter('lowpass', freq=fmax, corners=4) st_tr.filter('highpass', freq=fmin, corners=4) # test if gl is a list or a stream if type(gl[0]) == str: st_g = read('%s' % (gl[0])) else: st_g = gl[0] ng = st_g[0].data.size df = st_tr[0].stats.sampling_rate dt = 1. / df nstat = st_tr.select(channel='u').count() ndat = st_tr[0].data.size nfft = nextpow2(max(ndat, ng) * 2) # use freuquncies below corner frequency * fmax_hardcut_factor (tremendous # speed up in inversion) nfinv = int(fmax_hardcut_factor * fmax / (df / 2.) * nfft) nfinv = min(nfft / 2 + 1, nfinv) # going to frequency domain # correction for stf used in green's forward simulation if S0 is None: S0w = 1. else: S0w = np.fft.rfft(S0, n=nfft)[:nfinv] * dt # introduce waterlevel to prevent instabilities in deconvolution of # greens functions, see obspy.signal.invsim specInv # Calculated waterlevel in the scale of spec swamp = waterlevel(S0w, w_level) # Find length in real fft frequency domain, spec is complex sqrt_len = np.abs(S0w) # Set/scale length to swamp, but leave phase untouched # 0 sqrt_len will transform in np.nans when dividing by it idx = np.where((sqrt_len < swamp) & (sqrt_len > 0.0)) S0w[idx] *= swamp / sqrt_len[idx] # setup seismogram matrix in fourier space utr = np.zeros((nstat * 3, ndat)) utrw = np.zeros((nstat * 3, nfft / 2 + 1)) * 0j weights_l2 = np.zeros(nstat * 3) for k in np.arange(nstat): for i, chan in enumerate(['u', 'v', 'w']): utr[k * 3 + i, :] = st_tr.select(station='%04d' % (k + 1), channel=chan)[0].data utrw[k * 3 + i, :] = np.fft.rfft(utr[k * 3 + i, :], n=nfft) * dt # calculate l2 norm for weighting weights_l2[k * 3 + i] = cumtrapz(utr[k * 3 + i, :]**2, dx=dt)[-1] M_tl = [] ml = [] xl = [] sl = [] st_synl = [] #misfitl = [] misfitarg = [] min_misfit = 1e6 st_tr.filter('lowpass', freq=fmax, corners=4) for i, stg in enumerate(gl): if type(stg) == str: st_g = read('%s' % (stg)) print 'Working on %s ' % stg else: st_g = stg if round(st_tr[0].stats.sampling_rate, 5) != round( st_g[0].stats.sampling_rate, 5): msg = 'sampling rates of Seismograms and Green\'s function are not the' msg = msg + ' same: %f != %f' % (st_tr[0].stats.sampling_rate, st_g[0].stats.sampling_rate) raise ValueError(msg) M_t, m, x, s, st_syn, misfit = mtinv( (utrw, weights_l2.copy(), S0w, df, dt, nstat, ndat, ng, nfft, nfinv), st_tr, st_g, fmin, fmax, nsv=nsv, single_force=single_force, stat_subset=stat_subset, force_recalc=force_recalc, weighting_type=weighting_type, weights=weights, cache_path=cache_path + ('%06d_' % i), cache=cache) if misfit < min_misfit: min_misfit = misfit M_tl = M_t ml = m xl = x sl = s st_synl = st_syn misfitl = misfit argmin = i else: pass #M_tl.append(M_t) #ml.append(m) #xl.append(x) #sl.append(s) #st_synl.append(st_syn) #misfitl.append(misfit) #del st_g, st_syn, M_t, m, x, s, misfit #misfit = np.array(misfitl) #argmin = misfit.argmin() return M_tl, ml, xl, sl, st_synl, st_tr, misfitl, argmin
def deconvf(rsp_list, src, sampling_rate, water=0.05, gauss=2., tshift=10., pad=0, length=None, normalize=True, normalize_to_src=False, return_dict=False): """ Frequency-domain deconvolution using waterlevel method. Deconvolve src from arrays in rsp_list. :param rsp_list: either a list of arrays containing the response functions or a single array :param src: array of source function :param sampling_rate: sampling rate of the data :param water: waterlevel to stabilize the deconvolution :param gauss: Gauss parameter of Low-pass filter :param tshift: delay time 0s will be at time tshift afterwards :param pad: multiply number of samples used for fft by 2**pad :param length: number of data points in results, optional :param normalize: if results are normalized :param normalize_to_src: True -> normalized so that the maximum of a deconvolution of the source with itself is 1\n False -> normalized so that the maximum of the deconvolution of the first response array in rsp_list is 1 :param return_dict: return additionally a lot of different parameters in a dict for debugging purposes :return: (list of) array(s) with deconvolution(s) """ if length is None: length = len(src) N = length nfft = nextpow2(N) * 2**pad freq = np.fft.fftfreq(nfft, d=1. / sampling_rate) gauss = np.exp( np.maximum(-(0.5 * 2 * pi * freq / gauss)**2, -700.) - 1j * tshift * 2 * pi * freq) spec_src = fft(src, nfft) spec_src_conj = np.conjugate(spec_src) spec_src_water = np.abs(spec_src * spec_src_conj) spec_src_water = np.maximum(spec_src_water, max(spec_src_water) * water) if normalize_to_src: spec_src = gauss * spec_src * spec_src_conj / spec_src_water rf_src = ifft(spec_src, nfft)[:N] norm = 1 / max(rf_src) rf_src = norm * rf_src flag = False if not isinstance(rsp_list, (list, tuple)): flag = True rsp_list = [rsp_list] rf_list = [ ifft(gauss * fft(rsp, nfft) * spec_src_conj / spec_src_water, nfft)[:N] for rsp in rsp_list ] if normalize: if not normalize_to_src: norm = 1. / max(rf_list[0]) for rf in rf_list: rf *= norm if return_dict: if not normalize_to_src: spec_src = gauss * spec_src * spec_src_conj / spec_src_water rf_src = ifft(spec_src, nfft)[:N] norm = 1 / max(rf_src) rf_src = norm * rf_src ret_dict = { 'rf_src': rf_src, 'rf_src_conj': spec_src_conj, 'spec_src_water': spec_src_water, 'freq': freq, 'gauss': gauss, 'norm': norm, 'N': N, 'nfft': nfft } return rf_list, ret_dict elif flag: return rf else: return rf_list
def relcalstack(st1, st2, calib_file, window_len, overlap_frac=0.5, smooth=0, save_data=True): """ Method for relative calibration of sensors using a sensor with known transfer function :param st1: Stream or Trace object, (known) :param st2: Stream or Trace object, (unknown) :type calib_file: String :param calib_file: file name of calibration file containing the PAZ of the known instrument in GSE2 standard. :type window_len: Float :param window_len: length of sliding window in seconds :type overlap_frac: float :param overlap_frac: fraction of overlap, defaults to fifty percent (0.5) :type smooth: Float :param smooth: variable that defines if the Konno-Ohmachi taper is used or not. default = 0 -> no taper generally used in geopsy: smooth = 40 :type save_data: Boolean :param save_data: Whether or not to save the result to a file. If True, two output files will be created: * The new response in station_name.window_length.resp * The ref response in station_name.refResp Defaults to True :returns: frequency, amplitude and phase spectrum implemented after relcalstack.c by M.Ohrnberger and J.Wassermann. """ # transform given trace objects to streams if isinstance(st1, Trace): st1 = Stream([st1]) if isinstance(st2, Trace): st2 = Stream([st2]) # check if sampling rate and trace length is the same if st1[0].stats.npts != st2[0].stats.npts: msg = "Traces don't have the same length!" raise ValueError(msg) elif st1[0].stats.sampling_rate != st2[0].stats.sampling_rate: msg = "Traces don't have the same sampling rate!" raise ValueError(msg) else: ndat1 = st1[0].stats.npts sampfreq = st1[0].stats.sampling_rate # read waveforms tr1 = st1[0].data.astype(np.float64) tr2 = st2[0].data.astype(np.float64) # get window length, nfft and frequency step ndat = int(window_len * sampfreq) nfft = nextpow2(ndat) # read calib file and calculate response function gg, _freq = _calcresp(calib_file, nfft, sampfreq) # calculate number of windows and overlap nwin = int(np.floor((ndat1 - nfft) / (nfft / 2)) + 1) noverlap = nfft * overlap_frac auto, _freq, _t = \ spectral_helper(tr1, tr1, NFFT=nfft, Fs=sampfreq, noverlap=noverlap) cross, freq, _t = \ spectral_helper(tr2, tr1, NFFT=nfft, Fs=sampfreq, noverlap=noverlap) res = (cross / auto).sum(axis=1) * gg # The first item might be zero. Problems with phase calculations. res = res[1:] freq = freq[1:] gg = gg[1:] res /= nwin # apply Konno-Ohmachi smoothing taper if chosen if smooth > 0: # Write in one matrix for performance reasons. spectra = np.empty((2, len(res.real))) spectra[0] = res.real spectra[1] = res.imag new_spectra = \ konnoOhmachiSmoothing(spectra, freq, bandwidth=smooth, count=1, max_memory_usage=1024, normalize=True) res.real = new_spectra[0] res.imag = new_spectra[1] amp = np.abs(res) # include phase unwrapping phase = np.unwrap(np.angle(res)) # + 2.0 * np.pi ra = np.abs(gg) rpha = np.unwrap(np.angle(gg)) if save_data: trans_new = (st2[0].stats.station + "." + st2[0].stats.channel + "." + str(window_len) + ".resp") trans_ref = st1[0].stats.station + ".refResp" # Create empty array for easy saving temp = np.empty((len(freq), 3)) temp[:, 0] = freq temp[:, 1] = amp temp[:, 2] = phase np.savetxt(trans_new, temp, fmt="%.10f") temp[:, 1] = ra temp[:, 2] = rpha np.savetxt(trans_ref, temp, fmt="%.10f") return freq, amp, phase
def corr_pairs(str1,str2,corrname,geoinf): """ Step through the traces in the relevant streams and correlate whatever overlaps enough. input: str1,str2, obspy stream objects: Streams holding traces for station 1 and station 2 winlen, int: window length in seconds overlap, int: overlap in seconds maxlag, int: maximum lag for correlation in seconds nu, int: pcc nu, exponent for phase cross correlation startday, UTCDateTime object: Time where stack should start (if data available) endday, UTCDateTime object: Maximum time until where stacking should be carried out Fs, float: Sampling rate of data in Hz fmin: Minimum frequency the signal was filtered to (or 0.001 Hz) fmax: Maximum frequency the signal was filtered to (or 0.5*sampling frequency) onebit: Boolean, do one-bitting or not verbose, boolean: loud or quiet output: cccstack, numpy array: classical cross correlation in time domain pccstack, numpy array: phase cross correlation ccccnt, int: Number of windows stacked for cccstack pcccnt, int: Number of windows stacked for pccstack """ print('Computing correlation stack for:',file=None) print('-------------',file=None) print(str1[0].id) print(str2[0].id) print('-------------',file=None) startday=UTCDateTime(inp.startdate) endday=UTCDateTime(inp.enddate) t1=startday Fs_new=inp.Fs tlen=int(inp.max_lag*Fs_new[-1])*2+1 # Initialize arrays and variables pcccnt=0 ccccnt=0 n1=0 n2=0 cccstack=np.zeros(tlen) pccstack=np.zeros(tlen,dtype=np.float64) cstack_ccc=np.zeros(tlen,dtype=np.complex128) cstack_pcc=np.zeros(tlen,dtype=np.complex128) # Collect intermediate traces in a binary file. if inp.write_all: if inp.get_pws: msg = 'Saving intermediate windows of phase weighted stack\ is not implemented yet. Intermediate windows will be saved as linear stack.' # set the size of float # set size of character array for processing string # format of file: # header consisting of float, float, float, string of 256, string of 256 # traces, traces, traces... # header values: Sampling rate Fs, number of samples in each trace, Nr. of windows in intermediate stacks, Endianness, preprocessing string interm_fs = Fs_new[-1] interm_nsam = tlen interm_nwin = inp.interm_nstack if cccstack.dtype.byteorder == '=': interm_endian = sys.byteorder elif cccstack.dtype.byteorder == '<': interm_endian = 'little' elif cccstack.dtype.byteorder == '>': interm_endian = 'big' interm_preproc = get_prepstring() # open the file(s) if inp.corrtype in ['both','pcc','ccc']: outdir = os.path.join(cfg.datadir,'correlations',inp.corrname) interm_file=os.path.join(outdir,str1[0].id+'.'+str2[0].id+'.'+inp.corrtype+'.'+\ inp.corrname+'.windows.bin') interm_file = open(interm_file,'wb') header_1 = np.array([interm_fs,interm_nsam,interm_nwin],dtype='f4') header_2 = np.array([interm_endian,interm_preproc],dtype='S256') header_1.tofile(interm_file) header_2.tofile(interm_file) else: print('Correlation type not recognized. Correlation types are:\ ccc, pcc or both.') MPI.COMM_WORLD.Abort(1) while n1<len(str1) and n2<len(str2): # Check if the end of one of the traces has been reached if str1[n1].stats.endtime-t1<inp.winlen-1: n1+=1 print('No more windows in trace 1..',file=None) continue elif str2[n2].stats.endtime-t1<inp.winlen-1: n2+=1 print('No more windows in trace 2..',file=None) continue # Check the starttime of the potentially new trace t1=max(t1,str1[n1].stats.starttime,str2[n2].stats.starttime) #print(t1,file=None) t2=t1+inp.winlen print(t1) # Check if the end of the desired stacking window is reached if t2>endday: #print('At end of correlation time',file=None) break tr1=str1[n1].slice(starttime=t1,endtime=t2-1/Fs_new[-1]) tr2=str2[n2].slice(starttime=t1,endtime=t2-1/Fs_new[-1]) if tr1.stats.npts != tr2.stats.npts: t1 = t2 - inp.olap continue # tr1.plot() #tr2.plot() #- Downsampling =============================================================== if len(tr1.data)>40 and len(tr2.data)>40: k=0 while k<len(Fs_new): if Fs_new[k]<tr1.stats.sampling_rate: tr1=proc.trim_next_sec(tr1,False,None) tr1=proc.downsample(tr1,Fs_new[k],False,None) if Fs_new[k]<tr2.stats.sampling_rate: tr2=proc.trim_next_sec(tr2,False,None) tr2=proc.downsample(tr2,Fs_new[k],False,None) k+=1 else: t1 = t2 - inp.olap continue #============================================================================== #- Checks #============================================================================== if tr1.data.any()==np.nan or tr2.data.any()==np.nan: t1 = t2 - inp.olap print('Encountered nan, skipping this trace pair...',file=None) continue if tr1.data.any()==np.inf or tr2.data.any()==np.inf: t1 = t2 - inp.olap print('Encountered inf, skipping this trace pair...',file=None) continue if len(tr1.data) == len(tr2.data): mlag = inp.max_lag / tr1.stats.delta mlag=int(mlag) # Check if the traces are both long enough if len(tr1.data)<=2*mlag or len(tr2.data)<=2*mlag: t1 = t2 - inp.olap print('One or both traces too short',file=None) continue # Check if too many zeros # I use epsilon for this check. That is convenient but not strictly right. It seems to do the job though. min doesn't work. if np.sum(np.abs(tr1.data)<sys.float_info.epsilon) > 0.1*tr1.stats.npts or \ np.sum(np.abs(tr2.data)<sys.float_info.epsilon) > 0.1*tr2.stats.npts: t1 = t2 - inp.olap if inp.verbose: print('More than 10% of trace equals 0, skipping.',file=None) continue #============================================================================== #- Data treatment #============================================================================== #- Glitch correction ========================================================== if inp.cap_glitches: std1 = np.std(tr1.data*1.e6) gllow = inp.glitch_thresh * -std1 glupp = inp.glitch_thresh * std1 tr1.data = np.clip(tr1.data*1.e6,gllow,glupp)/1.e6 std2 = np.std(tr2.data*1.e6) gllow = inp.glitch_thresh * -std2 glupp = inp.glitch_thresh * std2 tr2.data = np.clip(tr2.data*1.e6,gllow,glupp)/1.e6 # #- Whitening ================================================================== if inp.apply_white: tr1 = whiten(tr1) tr2 = whiten(tr2) #- One-bitting ================================================================ if inp.apply_onebit: tr1.data = np.sign(tr1.data) tr2.data = np.sign(tr2.data) #- RAM normalization...who wants to do all this stuff!! ================================================================ if inp.apply_ram: tr1 = ram_norm(tr1,inp.ram_window,prefilt=inp.ram_filter) tr2 = ram_norm(tr2,inp.ram_window,prefilt=inp.ram_filter) #============================================================================== #- Correlations proper #============================================================================== #- Taper if inp.taper_traces == True: tr1.taper(type='cosine',max_percentage=inp.perc_taper) tr2.taper(type='cosine',max_percentage=inp.perc_taper) #- Classical correlation part ===================================== if inp.corrtype == 'ccc' or inp.corrtype == 'both': #ccc=classic_xcorr(tr1, tr2, mlag) (ccc, params) = cross_covar(tr1.data, \ tr2.data, mlag,inp.normalize_correlation) if ccc.any() == np.nan: msg='NaN encountered, omitting correlation from stack.' warn(msg) print(tr1) print(tr2) t1 = t2 - inp.olap continue # normalization by trace energy en1 = params[2] en2 = params[3] if inp.normalize_correlation: ccc/=(sqrt(en1)*sqrt(en2)) cccstack+=ccc ccccnt+=1 print('Finished a correlation window',file=None) # Make this faster by zero padding if inp.get_pws == True: coh_ccc = np.zeros(nextpow2(len(ccc))) startindex = int(0.5*(len(coh_ccc) - len(ccc))) coh_ccc[startindex:startindex+len(ccc)] += ccc*np.hanning(len(ccc)) coh_ccc = hilbert(coh_ccc) tol = np.max(coh_ccc)/10000. #if tol < 1e-9: # tol = 1e-9 coh_ccc = coh_ccc/(np.absolute(coh_ccc)+tol) coh_ccc = coh_ccc[startindex:startindex+len(ccc)] cstack_ccc+=coh_ccc else: coh_ccc = None cstack_ccc = None if inp.write_all==True and ccccnt % inp.interm_nstack == 0: trcname = t2.strftime("end%Y.%j.%H.%M.%S") trcname = np.array([trcname],dtype='S24') trcname.tofile(interm_file) ccc = np.array(ccc,dtype='f4') ccc.tofile(interm_file) #id1=str1[n1].id.split('.')[0]+'.'+str1[n1].id.split('.')[1] #id2=str2[n2].id.split('.')[0]+'.'+str2[n2].id.split('.')[1] #win_dir = cfg.datadir+'/correlations/interm/'+id1+\ # '_'+id2+'/' #if os.path.exists(win_dir)==False: # os.mkdir(win_dir) #timestring = tr1.stats.starttime.strftime('.%Y.%j.%H.%M.%S') #savecorrs(ccc,coh_ccc,1,tr1.id,tr2.id,geoinf,\ #corrname,'ccc',win_dir,params,timestring,startday=t1,endday=t2) #- Phase correlation part ========================================= # To be implemented: Getting trace energy elif inp.corrtype == 'pcc' or inp.corrtype == 'both': pcc=phase_xcorr(tr1.data, tr2.data, mlag, inp.pcc_nu) pccstack+=pcc pcccnt+=1 if inp.get_pws == True: coh_pcc = np.zeros(nextpow2(len(pcc))) startindex = int(0.5*(len(coh_pcc) - len(pcc))) coh_pcc[startindex:startindex+len(pcc)] += pcc*np.hanning(len(pcc)) # Tapering and zero padding to make hilbert trafo faster coh_pcc = hilbert(coh_pcc) tol = np.max(coh_pcc)/10000. #if tol < 1e-9: # tol = 1e-9 coh_pcc = coh_pcc/(np.absolute(coh_pcc)+tol) coh_pcc = coh_pcc[startindex:startindex+len(pcc)] cstack_pcc+=coh_pcc else: coh_pcc = None cstack_pcc = None if inp.write_all==True: trcname = t2.strftime("end%Y.%j.%H.%M.%S") trcname = np.array([trcname],dtype='S24') trcname.tofile(interm_file) pcc = np.array(pcc,dtype='f4') pcc.tofile(interm_file) #id1=str1[n1].id.split('.')[0]+'.'+str1[n1].id.split('.')[1] #id2=str2[n2].id.split('.')[0]+'.'+str2[n2].id.split('.')[1] #win_dir = cfg.datadir+'/correlations/interm/'+id1+\ # '_'+id2+'/' # #if os.path.exists(win_dir)==False: # os.mkdir(win_dir) #timestring = tr1.stats.starttime.strftime('.%Y.%j.%H.%M.%S') #savecorrs(pcc,coh_pcc,1,tr1.id,tr2.id,geoinf,\ #corrname,'pcc',win_dir,None,timestring,startday=t1,endday=t2) #Update starttime t1 = t2 - inp.olap if 'interm_file' in locals(): interm_file.close() return(cccstack,pccstack,cstack_ccc,cstack_pcc,ccccnt,pcccnt)
def relcalstack(st1, st2, calib_file, window_len, OverlapFrac=0.5, smooth=0): """ Method for relative calibration of sensors using a sensor with known transfer function :param st1/st2: Stream object, (known/unknown) the trace.stats dict like class must contain \ the parameters "sampling_rate", "npts" and "station" :type calib_file: String :param calib_file: name of calib file containing the known PAZ of known instrument in GSE2 standard. :type window_len: Float :param window_len: length of sliding window in seconds :type OverlapFrac: float :param OverlapFrac: fraction of overlap, defaults to fifty percent (0.5) :type smooth: Float :param smooth: variable that defines if the Konno-Ohmachi taper is used or not. default = 0 -> no taper \ generally used in geopsy: smooth = 40 """ # check Konno-Ohmachi if smooth < 0: smooth = 0 # check if sampling rate and trace length is the same if st1[0].stats.npts != st2[0].stats.npts: msg = "Traces dont have the same length!" raise ValueError(msg) elif st1[0].stats.sampling_rate != st2[0].stats.sampling_rate: msg = "Traces dont have the same sampling rate!" raise ValueError(msg) else: ndat1 = st1[0].stats.npts ndat2 = st2[0].stats.npts sampfreq = st1[0].stats.sampling_rate # read waveforms tr1 = st1[0].data.astype(np.float64) tr2 = st2[0].data.astype(np.float64) # get window length, nfft and frequency step ndat = int(window_len * sampfreq) nfft = nextpow2(ndat) df = sampfreq / nfft # initialize array for response function res = np.zeros(nfft / 2 + 1, dtype="complex128") # read calib file and calculate response function gg, freq = calcresp(calib_file, nfft, sampfreq) # calculate number of windows and overlap nwin = int(np.floor((ndat1 - nfft) / (nfft / 2)) + 1) noverlap = nfft * OverlapFrac auto, freq, t = spectral_helper(tr1, tr1, NFFT=nfft, Fs=sampfreq, noverlap=noverlap) cross, freq, t = spectral_helper(tr1, tr2, NFFT=nfft, Fs=sampfreq, noverlap=noverlap) # 180 Grad Phasenverschiebung cross.imag = -cross.imag for i in range(nwin): res += (cross[:, i] / auto[:, i]) * gg # apply Konno-Ohmachi smoothing taper if choosen if smooth > 0: res /= nwin res.real = smooth_spectra(res.real, freq, smooth, count=1, max_memory_in_mb=1024) res.imag = smooth_spectra(res.imag, freq, smooth, count=1, max_memory_in_mb=1024) else: res /= nwin # print 'Writing output....' f = open(st2[0].stats.station + "." + str(window_len) + ".resp", "w") g = open(st1[0].stats.station + ".refResp", "w") amp = [] phase = [] for i in range(nfft / 2 + 1): a = np.sqrt(res.real[i] * res.real[i] + res.imag[i] * res.imag[i]) pha = np.arctan2(res.imag[i], res.real[i]) ra = np.sqrt(gg.real[i] * gg.real[i] + gg.imag[i] * gg.imag[i]) rpha = np.arctan2(gg.imag[i], gg.real[i]) amp.append(a) phase.append(pha) f.write("%f %f %f\n" % (freq[i], a, pha)) g.write("%f %f %f\n" % (freq[i], ra, rpha)) f.close() g.close() return freq, amp, phase
def array_processing(stream, win_len, win_frac, sll_x, slm_x, sll_y, slm_y, sl_s, semb_thres, vel_thres, frqlow, frqhigh, stime, etime, prewhiten, verbose=False, coordsys='lonlat', timestamp='mlabday', method=0, store=None): """ Method for Seismic-Array-Beamforming/FK-Analysis/Capon :param stream: Stream object, the trace.stats dict like class must contain an :class:`~obspy.core.util.attribdict.AttribDict` with 'latitude', 'longitude' (in degrees) and 'elevation' (in km), or 'x', 'y', 'elevation' (in km) items/attributes. See param ``coordsys``. :type win_len: float :param win_len: Sliding window length in seconds :type win_frac: float :param win_frac: Fraction of sliding window to use for step :type sll_x: float :param sll_x: slowness x min (lower) :type slm_x: float :param slm_x: slowness x max :type sll_y: float :param sll_y: slowness y min (lower) :type slm_y: float :param slm_y: slowness y max :type sl_s: float :param sl_s: slowness step :type semb_thres: float :param semb_thres: Threshold for semblance :type vel_thres: float :param vel_thres: Threshold for velocity :type frqlow: float :param frqlow: lower frequency for fk/capon :type frqhigh: float :param frqhigh: higher frequency for fk/capon :type stime: :class:`~obspy.core.utcdatetime.UTCDateTime` :param stime: Start time of interest :type etime: :class:`~obspy.core.utcdatetime.UTCDateTime` :param etime: End time of interest :type prewhiten: int :param prewhiten: Do prewhitening, values: 1 or 0 :param coordsys: valid values: 'lonlat' and 'xy', choose which stream attributes to use for coordinates :type timestamp: str :param timestamp: valid values: 'julsec' and 'mlabday'; 'julsec' returns the timestamp in seconds since 1970-01-01T00:00:00, 'mlabday' returns the timestamp in days (decimals represent hours, minutes and seconds) since '0001-01-01T00:00:00' as needed for matplotlib date plotting (see e.g. matplotlib's num2date) :type method: int :param method: the method to use 0 == bf, 1 == capon :type store: function :param store: A custom function which gets called on each iteration. It is called with the relative power map and the time offset as first and second arguments and the iteration number as third argument. Useful for storing or plotting the map for each iteration. For this purpose the dump function of this module can be used. :return: :class:`numpy.ndarray` of timestamp, relative relpow, absolute relpow, backazimuth, slowness """ res = [] eotr = True # check that sampling rates do not vary fs = stream[0].stats.sampling_rate if len(stream) != len(stream.select(sampling_rate=fs)): msg = 'in sonic sampling rates of traces in stream are not equal' raise ValueError(msg) grdpts_x = int(((slm_x - sll_x) / sl_s + 0.5) + 1) grdpts_y = int(((slm_y - sll_y) / sl_s + 0.5) + 1) geometry = get_geometry(stream, coordsys=coordsys, verbose=verbose) if verbose: print("geometry:") print(geometry) print("stream contains following traces:") print(stream) print("stime = " + str(stime) + ", etime = " + str(etime)) time_shift_table = get_timeshift(geometry, sll_x, sll_y, sl_s, grdpts_x, grdpts_y) # offset of arrays spoint, _epoint = get_spoint(stream, stime, etime) # # loop with a sliding window over the dat trace array and apply bbfk # nstat = len(stream) fs = stream[0].stats.sampling_rate nsamp = int(win_len * fs) nstep = int(nsamp * win_frac) # generate plan for rfftr nfft = nextpow2(nsamp) deltaf = fs / float(nfft) nlow = int(frqlow / float(deltaf) + 0.5) nhigh = int(frqhigh / float(deltaf) + 0.5) nlow = max(1, nlow) # avoid using the offset nhigh = min(nfft // 2 - 1, nhigh) # avoid using nyquist nf = nhigh - nlow + 1 # include upper and lower frequency # to speed up the routine a bit we estimate all steering vectors in advance steer = np.empty((nf, grdpts_x, grdpts_y, nstat), dtype=np.complex128) clibsignal.calcSteer(nstat, grdpts_x, grdpts_y, nf, nlow, deltaf, time_shift_table, steer) R = np.empty((nf, nstat, nstat), dtype=np.complex128) ft = np.empty((nstat, nf), dtype=np.complex128) newstart = stime tap = cosTaper(nsamp, p=0.22) # 0.22 matches 0.2 of historical C bbfk.c offset = 0 relpow_map = np.empty((grdpts_x, grdpts_y), dtype=np.float64) abspow_map = np.empty((grdpts_x, grdpts_y), dtype=np.float64) while eotr: try: for i, tr in enumerate(stream): dat = tr.data[spoint[i] + offset: spoint[i] + offset + nsamp] dat = (dat - dat.mean()) * tap ft[i, :] = np.fft.rfft(dat, nfft)[nlow:nlow + nf] except IndexError: break ft = np.ascontiguousarray(ft, np.complex128) relpow_map.fill(0.) abspow_map.fill(0.) # computing the covariances of the signal at different receivers dpow = 0. for i in range(nstat): for j in range(i, nstat): R[:, i, j] = ft[i, :] * ft[j, :].conj() if method == 1: R[:, i, j] /= np.abs(R[:, i, j].sum()) if i != j: R[:, j, i] = R[:, i, j].conjugate() else: dpow += np.abs(R[:, i, j].sum()) dpow *= nstat if method == 1: # P(f) = 1/(e.H R(f)^-1 e) for n in range(nf): R[n, :, :] = np.linalg.pinv(R[n, :, :], rcond=1e-6) errcode = clibsignal.generalizedBeamformer( relpow_map, abspow_map, steer, R, nstat, prewhiten, grdpts_x, grdpts_y, nf, dpow, method) if errcode != 0: msg = 'generalizedBeamforming exited with error %d' raise Exception(msg % errcode) ix, iy = np.unravel_index(relpow_map.argmax(), relpow_map.shape) relpow, abspow = relpow_map[ix, iy], abspow_map[ix, iy] if store is not None: store(relpow_map, abspow_map, offset) # here we compute baz, slow slow_x = sll_x + ix * sl_s slow_y = sll_y + iy * sl_s slow = np.sqrt(slow_x ** 2 + slow_y ** 2) if slow < 1e-8: slow = 1e-8 azimut = 180 * math.atan2(slow_x, slow_y) / math.pi baz = azimut % -360 + 180 if relpow > semb_thres and 1. / slow > vel_thres: res.append(np.array([newstart.timestamp, relpow, abspow, baz, slow])) if verbose: print(newstart, (newstart + (nsamp / fs)), res[-1][1:]) if (newstart + (nsamp + nstep) / fs) > etime: eotr = False offset += nstep newstart += nstep / fs res = np.array(res) if timestamp == 'julsec': pass elif timestamp == 'mlabday': # 719162 == hours between 1970 and 0001 res[:, 0] = res[:, 0] / (24. * 3600) + 719162 else: msg = "Option timestamp must be one of 'julsec', or 'mlabday'" raise ValueError(msg) return np.array(res)
def sonic(stream, win_len, win_frac, sll_x, slm_x, sll_y, slm_y, sl_s, semb_thres, vel_thres, frqlow, frqhigh, stime, etime, prewhiten, verbose=False, coordsys='lonlat', timestamp='mlabday'): """ Method for Seismic-Array-Beamforming/FK-Analysis :param stream: Stream object, the trace.stats dict like class must contain a obspy.core.util.AttribDict with 'latitude', 'longitude' (in degrees) and 'elevation' (in km), or 'x', 'y', 'elevation' (in km) items/attributes. See param coordsys :type win_len: Float :param win_len: Sliding window length in seconds :type win_frac: Float :param win_frac: Fraction of sliding window to use for step :type sll_x: Float :param sll_x: slowness x min (lower) :type slm_x: Float :param slm_x: slowness x max :type sll_y: Float :param sll_y: slowness y min (lower) :type slm_y: Float :param slm_y: slowness y max :type sl_s: Float :param sl_s: slowness step :type semb_thres: Float :param semb_thres: Threshold for semblance :type vel_thres: Float :param vel_thres: Threshold for velocity :type frqlow: Float :param frqlow: lower frequency for fk :type frqhigh: Float :param frqhigh: higher frequency for fk :type stime: UTCDateTime :param stime: Starttime of interest :type etime: UTCDateTime :param etime: Endtime of interest :type prewhiten: int :param prewhiten: Do prewhitening, values: 1 or 0 :param coordsys: valid values: 'lonlat' and 'xy', choose which stream attributes to use for coordinates :type timestamp: string :param timestamp: valid values: 'julsec' and 'mlabday'; 'julsec' returns the timestamp in secons since 1970-01-01T00:00:00, 'mlabday' returns the timestamp in days (decimals represent hours, minutes and seconds) since '0001-01-01T00:00:00' as needed for matplotlib date plotting (see e.g. matplotlibs num2date) :return: numpy.ndarray of timestamp, relative power, absolute power, backazimut, slowness """ res = [] eotr = True #XXX move all the the ctypes related stuff to bbfk (Moritz's job) # check that sampling rates do not vary df = stream[0].stats.sampling_rate if len(stream) != len(stream.select(sampling_rate=df)): msg = 'in sonic sampling rates of traces in stream are not equal' raise ValueError(msg) grdpts_x = int(((slm_x - sll_x) / sl_s + 0.5) + 1) grdpts_y = int(((slm_y - sll_y) / sl_s + 0.5) + 1) geometry = get_geometry(stream, coordsys=coordsys, verbose=verbose) if verbose: print("geometry:") print(geometry) print("stream contains following traces:") print(stream) print("stime = " + str(stime) + ", etime = " + str(etime)) time_shift_table_numpy = get_timeshift(geometry, sll_x, sll_y, sl_s, grdpts_x, grdpts_y) time_shift_table = ndarray2ptr3D(time_shift_table_numpy) # fillup the double trace pointer nstat = len(stream) trace = (C.c_void_p * nstat)() ntrace = np.empty(nstat, dtype="int32", order="C") for i, tr in enumerate(stream): # assure data are of correct type tr.data = np.require(tr.data, 'float64', ['C_CONTIGUOUS']) trace[i] = tr.data.ctypes.data_as(C.c_void_p) ntrace[i] = len(tr.data) # offset of arrays spoint, _epoint = get_spoint(stream, stime, etime) # # loop with a sliding window over the data trace array and apply bbfk # df = stream[0].stats.sampling_rate nsamp = int(win_len * df) nstep = int(nsamp * win_frac) # generate plan for rfftr nfft = nextpow2(nsamp) newstart = stime offset = 0 while eotr: try: buf = bbfk(spoint, offset, trace, ntrace, time_shift_table, frqlow, frqhigh, df, nsamp, nstat, prewhiten, grdpts_x, grdpts_y, nfft) abspow, power, ix, iy = buf except IndexError: break # here we compute baz, slow slow_x = sll_x + ix * sl_s slow_y = sll_y + iy * sl_s slow = np.sqrt(slow_x**2 + slow_y**2) if slow < 1e-8: slow = 1e-8 azimut = 180 * math.atan2(slow_x, slow_y) / math.pi baz = azimut - np.sign(azimut) * 180 if power > semb_thres and 1. / slow > vel_thres: res.append(np.array([newstart.timestamp, power, abspow, baz, slow])) if verbose: print(newstart, (newstart + (nsamp / df)), res[-1][1:]) if (newstart + (nsamp + nstep) / df) > etime: eotr = False offset += nstep newstart += nstep / df res = np.array(res) if timestamp == 'julsec': pass elif timestamp == 'mlabday': # 719162 == hours between 1970 and 0001 res[:, 0] = res[:, 0] / (24. * 3600) + 719162 else: msg = "Option timestamp must be one of 'julsec', or 'mlabday'" raise ValueError(msg) return np.array(res)
def sonic(stream, win_len, win_frac, sll_x, slm_x, sll_y, slm_y, sl_s, semb_thres, vel_thres, frqlow, frqhigh, stime, etime, prewhiten, verbose=False, coordsys='lonlat', timestamp='mlabday'): """ Method for Seismic-Array-Beamforming/FK-Analysis :param stream: Stream object, the trace.stats dict like class must contain a obspy.core.util.AttribDict with 'latitude', 'longitude' (in degrees) and 'elevation' (in km), or 'x', 'y', 'elevation' (in km) items/attributes. See param coordsys :type win_len: Float :param win_len: Sliding window length in seconds :type win_frac: Float :param win_frac: Fraction of sliding window to use for step :type sll_x: Float :param sll_x: slowness x min (lower) :type slm_x: Float :param slm_x: slowness x max :type sll_y: Float :param sll_y: slowness y min (lower) :type slm_y: Float :param slm_y: slowness y max :type sl_s: Float :param sl_s: slowness step :type semb_thres: Float :param semb_thres: Threshold for semblance :type vel_thres: Float :param vel_thres: Threshold for velocity :type frqlow: Float :param frqlow: lower frequency for fk :type frqhigh: Float :param frqhigh: higher frequency for fk :type stime: UTCDateTime :param stime: Starttime of interest :type etime: UTCDateTime :param etime: Endtime of interest :type prewhiten: int :param prewhiten: Do prewhitening, values: 1 or 0 :param coordsys: valid values: 'lonlat' and 'xy', choose which stream attributes to use for coordinates :type timestamp: string :param timestamp: valid values: 'julsec' and 'mlabday'; 'julsec' returns the timestamp in secons since 1970-01-01T00:00:00, 'mlabday' returns the timestamp in days (decimals represent hours, minutes and seconds) since '0001-01-01T00:00:00' as needed for matplotlib date plotting (see e.g. matplotlibs num2date) :return: numpy.ndarray of timestamp, relative power, absolute power, backazimut, slowness """ res = [] eotr = True #XXX move all the the ctypes related stuff to bbfk (Moritz's job) # check that sampling rates do not vary df = stream[0].stats.sampling_rate if len(stream) != len(stream.select(sampling_rate=df)): msg = 'in sonic sampling rates of traces in stream are not equal' raise ValueError(msg) grdpts_x = int(((slm_x - sll_x) / sl_s + 0.5) + 1) grdpts_y = int(((slm_y - sll_y) / sl_s + 0.5) + 1) geometry = get_geometry(stream, coordsys=coordsys, verbose=verbose) if verbose: print("geometry:") print(geometry) print("stream contains following traces:") print(stream) print("stime = " + str(stime) + ", etime = " + str(etime)) time_shift_table_numpy = get_timeshift(geometry, sll_x, sll_y, sl_s, grdpts_x, grdpts_y) time_shift_table = ndarray2ptr3D(time_shift_table_numpy) # fillup the double trace pointer nstat = len(stream) trace = (C.c_void_p * nstat)() ntrace = np.empty(nstat, dtype="int32", order="C") for i, tr in enumerate(stream): # assure data are of correct type tr.data = np.require(tr.data, 'float64', ['C_CONTIGUOUS']) trace[i] = tr.data.ctypes.data_as(C.c_void_p) ntrace[i] = len(tr.data) # offset of arrays spoint, _epoint = get_spoint(stream, stime, etime) # # loop with a sliding window over the data trace array and apply bbfk # df = stream[0].stats.sampling_rate nsamp = int(win_len * df) nstep = int(nsamp * win_frac) # generate plan for rfftr nfft = nextpow2(nsamp) newstart = stime offset = 0 while eotr: try: buf = bbfk(spoint, offset, trace, ntrace, time_shift_table, frqlow, frqhigh, df, nsamp, nstat, prewhiten, grdpts_x, grdpts_y, nfft) abspow, power, ix, iy = buf except IndexError: break # here we compute baz, slow slow_x = sll_x + ix * sl_s slow_y = sll_y + iy * sl_s slow = np.sqrt(slow_x ** 2 + slow_y ** 2) if slow < 1e-8: slow = 1e-8 azimut = 180 * math.atan2(slow_x, slow_y) / math.pi baz = azimut - np.sign(azimut) * 180 if power > semb_thres and 1. / slow > vel_thres: res.append(np.array([newstart.timestamp, power, abspow, baz, slow])) if verbose: print(newstart, (newstart + (nsamp / df)), res[-1][1:]) if (newstart + (nsamp + nstep) / df) > etime: eotr = False offset += nstep newstart += nstep / df res = np.array(res) if timestamp == 'julsec': pass elif timestamp == 'mlabday': # 719162 == hours between 1970 and 0001 res[:, 0] = res[:, 0] / (24. * 3600) + 719162 else: msg = "Option timestamp must be one of 'julsec', or 'mlabday'" raise ValueError(msg) return np.array(res)
def CalcResp(poles, zeros, scaleFac, MaxPer, sampfreq): ndat = MaxPer * sampfreq nfft = nextpow2(ndat) buffer = np.empty(nfft / 2 + 1, dtype='complex128') # poles = [] # zeros = [] # file = open(str(pazFile), 'r') # # # read file until calibration section is found # text = ' ' # while text != 'CAL1': # textln = file.readline() # text = textln.split(' ')[0] # if not text == 'CAL1': # msg = 'could not find calibration section!' # raise NameError(msg) # else: # cal = textln[31:34] # if cal == 'PAZ': # # read poles # npoles = int(file.readline()) # for i in xrange(npoles): # pole = file.readline() # pole_r = float(pole.split(" ")[0]) # pole_i = float(pole.split(" ")[1]) # pole_c = pole_r + pole_i * 1.j # poles.append(pole_c) # # read zeros # nzeros = int(file.readline()) # for i in xrange(nzeros): # zero = file.readline() # zero_r = float(zero.split(" ")[0]) # zero_i = float(zero.split(" ")[1]) # zero_c = zero_r + zero_i * 1.j # zeros.append(zero_c) # # read scale factor # scale_fac = float(file.readline()) # file.close scale_fac = scaleFac # calculate transfer function delta_f = sampfreq / nfft F = np.empty(nfft / 2 + 1) for i in xrange(nfft / 2 + 1): fr = i * delta_f F[i] = fr om = 2 * np.pi * fr num = 1. + 0.j for ii in xrange(len(zeros)): s = 0. + om * 1.j dif = s - zeros[ii] num = dif * num denom = 1. + 0.j for ii in xrange(len(poles)): s = 0. + om * 1.j dif = s - poles[ii] denom = dif * denom t_om = 1. + 0.j if denom.real != 0. or denom.imag != 0.: t_om = num / denom t_om *= scale_fac if i < nfft / 2 and i > 0: buffer[i] = t_om if i == 0: buffer[i] = t_om + 0.j if i == nfft / 2: buffer[i] = t_om + 0.j # plot amp = abs(buffer) phase = np.arctan(buffer.imag / buffer.real) / np.pi * 180 # Plot fig = plt.figure() ax1 = plt.subplot(211) ax1.loglog(F, amp) ax1.set_ylabel('Amplitude') ax2 = plt.subplot(212) ax2.semilogx(F, phase) ax2.set_xlabel('Frequenzy [Hz]') ax2.set_ylabel('Phase') plt.show()