def remove_clipped(stream, max_count=2000000): """ Identify clipped waveforms having a count greater than the specified max count. This will remove any clipped waveforms from the stream. This is step 2 as found in the Rennolet et. al paper (https://doi.org/10.1193/101916EQS175DP) Args: stream (obspy.core.stream.Stream): Stream of raw data in counts. max_count (int): Maximum count for clipping. Default is 2 million. Returns: tuple of streams (obspy.core.stream.Stream): The first stream in the tuple has the clipped waveforms removed, while the second tuple in in the stream contains only the clipped waveforms. """ clipped_st = Stream() for tr in stream: if abs(tr.max()) >= max_count: stream.traces.remove(tr) clipped_st.append(tr) warnings.warn('Clipped trace was removed from the stream') warnings.warn(tr.get_id()) return stream, clipped_st
def calculate_fas(stream, imcs, periods, smoothing, bandwidth): """ Calculate the fourier amplitude spectra. This process requires getting the fourier amplitude spectra, applying the geometric mean to the horizontal traces, smoothing and returning the smoothed value for each requested period. Args: stream (obspy.core.stream.Stream): streams of strong ground motion. Traces in stream must be in units of g. imcs (list): list of imcs. (Currently only the geometric mean is supported and this is ignored. In the future it will account for channels and rotated channels.) Returns: dictionary: Dictionary of fas for the geometric mean. Units are in cm/s. The dictionary is structured as: fas_dict = { <period> : <value,... } """ fas_dict = {} sampling_rate = None # check units and add channel pga for trace in stream: if trace.stats['units'] != 'g' and trace.stats['units'] != 'cm/s/s': raise PGMException('Invalid units for sa: %r. ' 'Units must be g (cm/s/s)' % trace.stats['units']) if 'Z' not in trace.stats['channel'].upper(): sampling_rate = trace.stats.sampling_rate if sampling_rate is None: raise PGMException('No horizontal channels') spec_stream = Stream() for trace in stream: nfft = len(trace.data) # the fft scales so the factor of 1/nfft is applied spectra = abs(np.fft.rfft(trace.data, n=nfft)) / nfft spec_trace = Trace(data=spectra, header=trace.stats) spec_stream.append(spec_trace) ## The imc is always geometric mean. However, the combined stream is ## required rather than the single maximum value gm_trace = calculate_geometric_mean(spec_stream, return_combined=True) freqs = np.fft.rfftfreq(nfft, 1 / trace.stats.sampling_rate) fas_frequencies = 1 / np.asarray(list(periods)) smoothed_values = np.empty_like(fas_frequencies) if smoothing.lower() == 'konno_ohmachi': konno_ohmachi_smooth(gm_trace.astype(np.double), freqs, fas_frequencies, smoothed_values, bandwidth) else: raise PGMException('Not a valid smoothing option: %r' % smoothing) for idx, freq in enumerate(fas_frequencies): fas_dict[1/freq] = smoothed_values[idx] return fas_dict
def read_volume_one(filename, location=''): """Read channel data from USC volume 1 text file. Args: filename (str): Input DMG V1 filename. Returns: tuple: (list of obspy Trace, int line offset) """ volume = VOLUMES['V1'] # count the number of lines in the file with open(filename) as f: line_count = sum(1 for _ in f) # read as many channels as are present in the file line_offset = 0 stream = Stream([]) while line_offset < line_count: trace, line_offset = _read_channel(filename, line_offset, volume, location=location) # store the trace if the station type is in the valid_station_types list # or store the trace if there is no valid_station_types list if trace is not None: stream.append(trace) return stream
def filterpyrockoWaveform(self, Waveform): obspy_compat.plant() Logfile.red('Filter Waveform: ') cfg = FilterCfg(self.Config) new_frequence = (cfg.newFrequency()) st = Stream() for i in Waveform: tr = obspy_compat.to_pyrocko_trace(i) tr.downsample_to(new_frequence) Logfile.red('Downsampling to %d: from %d' % (new_frequence, i.stats.sampling_rate)) if switch == 0: Logfile.add('bandpass filtered stream for station %s ' % (i)) tr.bandpass(4, cfg.flo, cfg.fhi) elif switch == 1: Logfile.add('bandpass filtered stream for station %s ' % (i)) j = obspy_compat.to_obspy_trace(tr) st.append(j) return st
def filterWaveform(self, Waveform, cfg): Logfile.red('Filter Waveform: ') new_frequence = (cfg.config_filter.newFrequency) st = Stream() for i in Waveform: Logfile.red('Downsampling to %s: from %d' % (new_frequence, i.stats.sampling_rate)) j = i.resample(new_frequence) switch = cfg.config_filter.filterswitch Logfile.add('bandpass filtered \ stream for station %s ' % (i)) j.filter('bandpass', freqmin=cfg.config_filter.flo[switch-1], freqmax=cfg.config_filter.fhi[switch-1], corners=cfg.config_filter.ns[switch-1], zerophase=False) st.append(j) return st
def read_data(filename, **kwargs): """Read unknown csmip/dmg-like strong motion file. Args: filename (str): Path to data file. kwargs (ref): units (str): String determining which timeseries is return. Valid options include 'acc', 'vel', 'disp'. Default is 'acc'. Other arguments will be ignored. Returns: Stream: Obspy Stream containing three channels of acceleration data (cm/s**2). """ # Check for type units = kwargs.get('units', 'acc') if units not in UNITS: raise Exception('Not a valid choice of units.') # Count the number of lines in the file with open(filename) as f: line_count = sum(1 for _ in f) # Read as many channels as are present in the file line_offset = 0 trace_list = [] while line_offset < line_count: traces, line_offset = _read_volume(filename, line_offset) trace_list += traces stream = Stream([]) for trace in trace_list: if trace.stats['units'] == units: stream.append(trace) return stream
def read_dmg(filename, **kwargs): """Read DMG strong motion file. Notes: CSMIP is synonymous to as DMG in this reader. Args: filename (str): Path to possible DMG data file. kwargs (ref): units (str): String determining which timeseries is return. Valid options include 'acc', 'vel', 'disp'. Default is 'acc'. Other arguments will be ignored. Returns: Stream: Obspy Stream containing three channels of acceleration data (cm/s**2). """ if not is_dmg(filename): raise Exception('Not a DMG file format.') # Check for units and location units = kwargs.get('units', 'acc') location = kwargs.get('location', '') if units not in UNITS: raise Exception('Not a valid choice of units.') # Check for DMG format and determine volume type line = open(filename, 'rt').readline() if is_dmg(filename): if line.lower().find('uncorrected') >= 0: reader = 'V1' elif line.lower().find('corrected') >= 0: reader = 'V2' elif line.lower().find('response') >= 0: reader = 'V3' # Count the number of lines in the file with open(filename) as f: line_count = sum(1 for _ in f) # Read as many channels as are present in the file line_offset = 0 trace_list = [] while line_offset < line_count: if reader == 'V2': traces, line_offset = _read_volume_two(filename, line_offset, location=location) trace_list += traces else: raise AmptoolsException('Not a supported volume.') stream = Stream([]) for trace in trace_list: if trace.stats['standard']['units'] == units: stream.append(trace) return stream
def join_NERT(a,b): new=Stream() for i in range(len(a)): if a[i].stats['station'] == b[i].stats['station'] and \ a[i].stats['channel'] == b[i].stats['channel']: new.append(a[i]) else: new.append(a[i]) new.append(b[i]) return new
def purgeListStation(st,args,ty): new=Stream() ra=args.radius.split() lii = [] # select for distances if(ty=='d'): for i in range(len(st)): if(st[i].stats.gcarc >= eval(ra[0]) and st[i].stats.gcarc <= eval(ra[1])): new.append(st[i]) return new
def filter(self, stream, time_at_rec, la_s, lo_s, depth, Rayleigh=True): env_stream = Stream() dist, az, baz = gps2dist_azimuth(lat1=la_s, lon1=lo_s, lat2=self.prior['la_r'], lon2=self.prior['lo_r'], a=self.prior['radius'], f=0) if Rayleigh == True: phases = self.get_R_phases(time_at_rec) else: phases = self.get_L_phases(time_at_rec) for i, v in enumerate(stream.traces): npts = len(v.data) trace = stream.traces[i].copy() trace.detrend(type="demean") trace.interpolate( sampling_rate=10. / phases[i]['dt'] ) # No method specified, so : 'weighted_average_slopes' is used trace.filter('highpass', freq=phases[i]['fmin'], zerophase=True) trace.filter('lowpass', freq=phases[i]['fmax'], zerophase=True) trace.detrend() trace.detrend(type="demean") env = envelope(trace.data) zero_trace = Trace(np.zeros(npts), header={ "starttime": phases[i]['starttime'](dist, depth), 'delta': trace.meta.delta, "station": trace.meta.station, "network": trace.meta.network, "location": trace.meta.location, "channel": trace.meta.channel, "instaseis": trace.meta.instaseis }) env_trace = Trace(env, header={ "starttime": phases[i]['starttime'](dist, depth), 'delta': trace.meta.delta, "station": trace.meta.station, "network": trace.meta.network, "location": trace.meta.location, "channel": trace.meta.channel, "instaseis": trace.meta.instaseis }) env_stream.append(env_trace) return env_stream
def saveData(self, filename = None): stream = Stream() for i in range(0,self.ntraces): curr_trace = self.data[:,i] curr_trace = np.require(curr_trace, dtype='float32') temp = Trace(data=curr_trace) # Attributes in trace.stats will overwrite everything in # trace.stats.segy.trace_header temp.stats.delta = 0.01 # Add trace to stream stream.append(temp) print "Stream object before writing..." print stream stream.write(filename, format="SEGY", data_encoding=1, byteorder=sys.byteorder)
def split_traces(self, d_syn, traces_obs, time_at_receiver): Stream_split = Stream() for i, trace in enumerate(traces_obs.traces): new_trace = Trace(d_syn[i * len(trace):i * len(trace) + len(trace)], header={ "starttime": time_at_receiver, 'delta': trace.meta.delta, "station": trace.meta.station, "network": trace.meta.network, "location": trace.meta.location, "channel": trace.meta.channel, "instaseis": trace.meta.instaseis }) Stream_split.append(new_trace) return Stream_split
def saveData(self, filename=None): stream = Stream() for i in range(0, self.ntraces): curr_trace = self.data[:, i] curr_trace = np.require(curr_trace, dtype='float32') temp = Trace(data=curr_trace) # Attributes in trace.stats will overwrite everything in # trace.stats.segy.trace_header temp.stats.delta = 0.01 # Add trace to stream stream.append(temp) print "Stream object before writing..." print stream stream.write(filename, format="SEGY", data_encoding=1, byteorder=sys.byteorder)
def poisson_segmenting(poisson_times, noise_trace, samp_rate, delta, st_event_2): """ Sets out the events at the poisson times (from poisson_interevent), and adds the noise onto the event at that point. """ st_events_poisson = Stream() for i in range(0, len(poisson_times)): testnoise = np.array(noise_trace) noise_portion = testnoise[(poisson_times[i]*int(samp_rate)): (poisson_times[i]*int(samp_rate)) + len(st_event_2)] noise_plus_event_arr = noise_portion + np.array(st_event_2) noise_plus_event = Trace(noise_plus_event_arr) noise_plus_event.stats.sampling_rate = samp_rate noise_plus_event.stats.delta = delta noise_plus_event.stats.starttime = noise_trace.stats.starttime + poisson_times[i] #plt.plot(noise_plus_event) st_events_poisson.append(noise_plus_event) st_events_poisson[-1].stats.endtime+100 return st_events_poisson
def main(): # Read acceleration data. If you want velocity units='vel', # Cdisplacement units='disp' data_dir = '/Users/tnye/PROJECTS/Duration/data/ci3144585/ground_motion' files = [ '1994.c.017m30su.n0a', '1994.c.017m30su.n0b', '1994.c.017m30su.n0c' ] files = [os.path.join(data_dir, f) for f in files] stream = Stream() # Data for the trace we are working with # There are 3 traces (0, 1, 2), and i denotes trace we are working with for i in range(len(files)): cmp = processing.read_data(files[i], units='acc')[0] stream.append(cmp) durations = [(0.05, 0.75), (0.2, 0.8), (0.05, .95)] # Plots each boundary on the Norm Arias Intensity graph f, axes = plt.subplots(len(durations), 1, sharex=True, figsize=(6.5, 7.5)) for i, trace in enumerate(stream): # Convert acceleration to m/s/s acc = np.multiply(0.01, trace.data) channel = trace.stats['channel'] dt = trace.stats.delta Ia, NIa = arias_intensity.get_arias_intensity(acc, dt, 0) print("Arias Intensity (%s): %f" % (channel, np.amax(Ia))) if i == 2: xlab = True else: xlab = False arias_intensity.plot_durations(NIa, dt, durations, axes[i], xlab) axes[i].set_title(channel) plt.savefig('/Users/tnye/PROJECTS/Duration/figures/Afshari_Fig_1_tnye.png', dpi=300)
def copy(orig): """ True-copy a stream by creating a new stream and copying old attributes to it. This is necessary because the old stream accumulates *something* that causes CPU usage to increase over time as more data is added. This is a bug in obspy that I intend to find--or at the very least report--but until then this hack works fine and is plenty fast enough. In this example, we make a stream object with some RS 1Dv7 data and then copy it to a new stream: .. code-block:: python >>> import rsudp.raspberryshake as rs >>> from obspy.core.stream import Stream >>> rs.initRSlib(dport=8888, rsstn='R3BCF') >>> s = Stream() >>> d = rs.getDATA() >>> t = rs.make_trace(d) >>> s = rs.update_stream(s, d) >>> s 1 Trace(s) in Stream: AM.R3BCF.00.EHZ | 2020-02-21T19:58:50.292000Z - 2020-02-21T19:58:50.532000Z | 100.0 Hz, 25 samples >>> s = rs.copy(s) >>> s 1 Trace(s) in Stream: AM.R3BCF.00.EHZ | 2020-02-21T19:58:50.292000Z - 2020-02-21T19:58:50.532000Z | 100.0 Hz, 25 samples :param obspy.core.stream.Stream orig: The data stream to copy information from :rtype: obspy.core.stream.Stream :return: A low-memory copy of the passed data stream """ stream = Stream() for t in range(len(orig)): trace = Trace(data=orig[t].data) trace.stats.network = orig[t].stats.network trace.stats.location = orig[t].stats.location trace.stats.station = orig[t].stats.station trace.stats.channel = orig[t].stats.channel trace.stats.sampling_rate = orig[t].stats.sampling_rate trace.stats.starttime = orig[t].stats.starttime stream.append(trace).merge(fill_value=None) return stream.copy()
def stream_stack_distance_intervals(st, interval): """ Stack average traces in a stream if their distance difference is smaller than interval. The stream containing a number of traces with given distance (e.g. from source) is used to create a number of equally spaced traces by averaging traces that fall into the same distance bin. If interval is a scalar the bins are equally spaced with a width of interval. If interval is a sequence its elements define the lower distance limit of the bins. :type st: :class:`~obspy.core.stream.Stream` :param st: Stream fo be used for stacking. :type interval: scalar os array like :param interval: width of bins in case of scalar or smaller edge of bins if interval is a sequence. :rtype sst: :class:`~obspy.core.stream.Stream` :return: **sst**: stacked stream """ dist = [] npts = [] for tr in st: dist.append(tr.stats.sac['dist']) npts.append(tr.stats['npts']) if not hasattr(interval, "__len__"): bins = np.arange(min(dist), max(dist),interval) else: bins = np.array(interval) sst = Stream() for ii in bins: sst.append(Trace(data=np.zeros(max(npts),dtype=np.float64),header={ 'network':'stack','station':str(ii),'location':'', 'channel':st[0].stats['channel'],'starttime':st[0].stats['starttime'],'sampling_rate':st[0].stats['sampling_rate'], 'sac':{'dist':ii,'az':0,'evla':0.,'evlo':0.,'stla':ii/(np.pi*6371000)*180.,'stlo':0.}})) count = np.zeros_like(bins) for tr in st: ind = sum((tr.stats.sac['dist'] - bins)>=0)-1 sst[ind].data[0:tr.stats['npts']] += tr.data count[ind] += 1 for ind, tr in enumerate(sst): tr.data /= count[ind] return sst
def copy(orig): """ True copy a stream by creating a new stream and copying old attributes to it. This is necessary because the old stream accumulates *something* that causes CPU usage to increase over time as more data is added. This is a bug in obspy that I intend to find--or at the very least report--but until then this hack works fine. """ stream = Stream() for t in range(len(orig)): trace = Trace(data=orig[t].data) trace.stats.network = orig[t].stats.network trace.stats.location = orig[t].stats.location trace.stats.station = orig[t].stats.station trace.stats.channel = orig[t].stats.channel trace.stats.sampling_rate = orig[t].stats.sampling_rate trace.stats.starttime = orig[t].stats.starttime stream.append(trace).merge(fill_value=None) return stream.copy()
def read_cosmos(filename, **kwargs): """Read COSMOS V1/V2 strong motion file. There is one extra key in the Stats object for each Trace - "process_level". This will be set to either "V1" or "V2". Args: filename (str): Path to possible COSMOS V1/V2 data file. kwargs (ref): valid_station_types (list): List of valid station types. See table 6 in the COSMOS strong motion data format documentation for station type codes. Other arguments will be ignored. Returns: Stream: Obspy Stream containing three channels of acceleration data (cm/s**2). """ # get list of valid stations valid_station_types = kwargs.get('valid_station_types', None) # get list of valid stations location = kwargs.get('location', '') # count the number of lines in the file with open(filename) as f: line_count = sum(1 for _ in f) # read as many channels as are present in the file line_offset = 0 stream = Stream([]) while line_offset < line_count: trace, line_offset = _read_channel(filename, line_offset, location=location) # store the trace if the station type is in the valid_station_types list # or store the trace if there is no valid_station_types list if valid_station_types is not None: if trace.stats['format_specific'][ 'station_code'] in valid_station_types: stream.append(trace) else: stream.append(trace) return stream
def dataclean(alltrigs, opt, flag=1): """ Examine triggers and weed out spikes and calibration pulses using kurtosis and outlier ratios alltrigs: triggers output from triggering opt: opt from config flag: 1 if defining window to check, 0 if want to check whole waveform for spikes (note that different threshold values should be used for different window lengths) Returns good trigs (trigs) and junk (junk) """ trigs=Stream() junk=Stream() for i in range(len(alltrigs)): #define data dat=alltrigs[i].data if flag==0: datcut=dat else: datcut=alltrigs[i].data[range(int((opt.ptrig-opt.kurtwin/2)*opt.samprate), int((opt.ptrig+opt.kurtwin/2)*opt.samprate))] #calculate kurtosis in window k = stats.kurtosis(datcut) #compute kurtosis of frequency amplitude spectrum next datf = np.absolute(fft(dat)) kf = stats.kurtosis(datf) #calculate outlier ratio using z ((data-median)/mad), outliers have z>4.45 mad = np.median(np.absolute(dat - np.median(dat))) z=(dat-np.median(dat))/mad orm = len(z[z>4.45])/len(z) if k<opt.kurtmax and orm<opt.oratiomax and kf<opt.kurtfmax: trigs.append(alltrigs[i]) else: junk.append(alltrigs[i]) return trigs, junk
def corr_trace_to_obspy(corr_trace): """ Convert a correlation trace dictionary to an obspy trace. Convert a single correlation trace (or a list of) in an :class:`~obspy.core.trace.Trace` (or :class:`~obspy.core.stream.Stream`) object. :type corr_trace: dictionary of type correlation trace or list of these :param corr_trace: input date to be converted :rtype: :class:`~obspy.core.trace.Trace` if ``corr_trace`` is a dict and :class:`~obspy.core.stream.Stream` if ``corr_trace`` is a list of dicts :return: **st**: the obspy object containing the data """ if isinstance(corr_trace, list): st = Stream() for tr in corr_trace: st.append(_single_corr_trace_to_obspy_trace(tr)) else: st = _single_corr_trace_to_obspy_trace(corr_trace) return st
def rotate_stream(D, orientation, reference): """ """ channels = [] for trace in D: channels.append(trace.stats.channel) Drot = Stream() for (angle, trace) in zip(orientation, D): channel = trace.stats.channel trace_rot = trace.copy() if channel[2] in ['E', 'N', '1', '2']: az = angle['azimuth'] az0 = reference[channel]['azimuth'] if (az != az0): normal_dict = {'E': 'N', 'N': 'E', '1': '2', '2': '1'} normal = channel[0:2] + normal_dict[channel[2]] index = channels.index(normal) if channel[2] in ['E', '1']: dE = angle['azimuth'] * pi / 180.0 dN = orientation[index]['azimuth'] * pi / 180.0 tE = reference[channel]['azimuth'] * pi / 180.0 tN = reference[normal]['azimuth'] * pi / 180.0 dataE = trace.data dataN = D[index].data trace_rot.data = cos(dE - tE) * dataE + cos(dN - tE) * dataN else: dE = orientation[index]['azimuth'] * pi / 180.0 dN = angle['azimuth'] * pi / 180.0 tE = reference[normal]['azimuth'] * pi / 180.0 tN = reference[channel]['azimuth'] * pi / 180.0 dataE = D[index].data dataN = trace.data trace_rot.data = cos(dE - tN) * dataE + cos(dN - tN) * dataN Drot.append(trace_rot) return Drot
def _calculate_channel_arias(stream): """Calculates Arias Intensity. Args: stream (obspy.core.stream.Stream): Stream of acceleration values in m/s/s. Returns: Ia (obspy.core.stream.Stream): Stream of Arias intensity values in m/s with respect to time. NIa (obspy.core.stream.Stream): Stream of normalized Arias intensity values with respect to time. Raises: PGMException: If the units are not valid. Units must be m/s/s. """ Ia = Stream() NIa = Stream() for trace in stream: if trace.stats['units'] != 'm/s/s': raise PGMException('Invalid units for ARIAS: %r. ' 'Units must be m/s/s' % trace.stats['units']) dt = trace.stats['delta'] g = sp.g acc2 = trace.data # Calculate Arias intensity integration = integrate.cumtrapz(acc2 * acc2, dx=dt) arias_intensity = integration * np.pi / (2 * g) # Calculate normalized Arias intensity # divide arias intensity by its max value norm_arias_intensity = arias_intensity / np.amax(arias_intensity) stats_out = trace.stats.copy() stats_out['units'] = 'm/s' trace_ia = Trace(data=arias_intensity, header=stats_out) trace_nia = Trace(data=norm_arias_intensity, header=stats_out) Ia.append(trace_ia) NIa.append(trace_nia) return (Ia, NIa)
def filterWaveform(self, Waveform): Logfile.red('Filter Waveform: ') cfg = FilterCfg(self.Config) new_frequence = (cfg.newFrequency()) st = Stream() for i in Waveform: Logfile.red('Downsampling to %s: from %d' % (new_frequence, i.stats.sampling_rate)) j = i.resample(new_frequence) switch = cfg.filterswitch() if switch == 1: Logfile.add('bandpass filtered \ stream for station %s ' % (i)) j.filter('bandpass', freqmin=cfg.flo(), freqmax=cfg.fhi(), corners=cfg.ns(), zerophase=bool(self.Config['zph'])) elif switch == 2: Logfile.add('bandpass filtered \ stream for station %s ' % (i)) j.filter('bandpass', freqmin=cfg.flo2(), freqmax=cfg.fhi2(), corners=cfg.ns2(), zerophase=bool(self.Config['zph'])) st.append(j) return st
def main(): # Read acceleration data. If you want velocity units='vel', # Cdisplacement units='disp' data_dir = '/Users/tnye/PROJECTS/Duration/data/ci3144585/ground_motion' files = [ '1994.c.017m30su.n0a', '1994.c.017m30su.n0b', '1994.c.017m30su.n0c' ] files = [os.path.join(data_dir, f) for f in files] stream = Stream() # Data for the trace we are working with # There are 3 traces (0, 1, 2), and i denotes trace we are working with for i in range(len(files)): cmp = processing.read_data(files[i], units='acc')[0] stream.append(cmp) for i, trace in enumerate(stream): trace_stats = trace.stats channel = trace.stats['channel'] print(channel) dt = trace_stats.delta cav = CAV.get_CAV(trace.data, dt, 0)
def split_BW_SW(self, BW_SW_stream, epi, depth, time_at_receiver, npts): BW_stream = Stream() R_stream = Stream() L_stream = Stream() for i in BW_SW_stream: if 'X' in i.id: BW_stream.append(i) elif 'R1' in i.id: R_stream.append(i) elif 'G1' in i.id: L_stream.append(i) P_S_syn, P_syn, S_syn = self.get_window_split_syn( BW_stream, epi, depth, time_at_receiver, npts) return P_S_syn, P_syn, S_syn, R_stream, L_stream
def mergePreviews(stream): """ Merges all preview traces in one Stream object. Does not change the original stream because the data needs to be copied anyway. :type stream: :class:`~obspy.core.stream.Stream` :param stream: Stream object to be merged :rtype: :class:`~obspy.core.stream.Stream` :return: Merged Stream object. """ copied_traces = copy(stream.traces) stream.sort() # Group traces by id. traces = {} dtypes = [] for trace in stream: # Throw away empty traces. if trace.stats.npts == 0: continue if not hasattr(trace.stats, 'preview') or not trace.stats.preview: msg = 'Trace\n%s\n is no preview file.' % str(trace) raise Exception(msg) traces.setdefault(trace.id, []) traces[trace.id].append(trace) dtypes.append(trace.data.dtype) if len(traces) == 0: return Stream() # Initialize new Stream object. new_stream = Stream() for value in traces.values(): if len(value) == 1: new_stream.append(value[0]) continue # All traces need to have the same delta value and also be on the same # grid spacing. It is enough to only check the sampling rate because # the algorithm that creates the preview assures that the grid spacing # is correct. sampling_rates = set([tr.stats.sampling_rate for tr in value]) if len(sampling_rates) != 1: msg = 'More than one sampling rate for traces with id %s.' % \ value[0].id raise Exception(msg) delta = value[0].stats.delta # Check dtype. dtypes = set([native_str(tr.data.dtype) for tr in value]) if len(dtypes) > 1: msg = 'Different dtypes for traces with id %s' % value[0].id raise Exception(msg) dtype = dtypes.pop() # Get the minimum start and maximum endtime for all traces. min_starttime = min([tr.stats.starttime for tr in value]) max_endtime = max([tr.stats.endtime for tr in value]) samples = int(round((max_endtime - min_starttime) / delta)) + 1 data = np.empty(samples, dtype=dtype) # Fill with negative one values which corresponds to a gap. data[:] = -1 # Create trace and give starttime. new_trace = Trace(data=data, header=value[0].stats) # Loop over all traces in value and add to data. for trace in value: start_index = int((trace.stats.starttime - min_starttime) / delta) end_index = start_index + len(trace.data) # Element-by-element comparison. data[start_index:end_index] = \ np.maximum(data[start_index:end_index], trace.data) # set npts again, because data is changed in place new_trace.stats.npts = len(data) new_stream.append(new_trace) stream.traces = copied_traces return new_stream
def dataClean(alltrigs, opt, flag=1): """ Examine triggers and weed out spikes and calibration pulses using kurtosis and outlier ratios alltrigs: triggers output from triggering opt: opt from config flag: 1 if defining window to check, 0 if want to check whole waveform for spikes (note that different threshold values should be used for different window lengths) Returns good trigs (trigs) and several junk types (junk, junkFI, junkKurt) """ trigs=Stream() junkFI=Stream() junkKurt=Stream() junk=Stream() for i in range(len(alltrigs)): njunk = 0 ntele = 0 for n in range(opt.nsta): dat = alltrigs[i].data[n*opt.wshape:(n+1)*opt.wshape] if flag == 1: datcut=dat[range(int((opt.ptrig-opt.kurtwin/2)*opt.samprate), int((opt.ptrig+opt.kurtwin/2)*opt.samprate))] else: datcut=dat if np.sum(np.abs(dat))!=0.0: # Calculate kurtosis in window k = stats.kurtosis(datcut) # Compute kurtosis of frequency amplitude spectrum next datf = np.absolute(fft(dat)) kf = stats.kurtosis(datf) # Calculate outlier ratio using z ((data-median)/mad) mad = np.nanmedian(np.absolute(dat - np.nanmedian(dat))) z = (dat-np.median(dat))/mad # Outliers have z > 4.45 orm = len(z[z>4.45])/np.array(len(z)).astype(float) if k >= opt.kurtmax or orm >= opt.oratiomax or kf >= opt.kurtfmax: njunk+=1 winstart = int(opt.ptrig*opt.samprate - opt.winlen/10) winend = int(opt.ptrig*opt.samprate - opt.winlen/10 + opt.winlen) fftwin = np.reshape(fft(dat[winstart:winend]),(opt.winlen,)) if np.median(np.abs(dat[winstart:winend]))!=0: fi = np.log10(np.mean(np.abs(np.real( fftwin[int(opt.fiupmin*opt.winlen/opt.samprate):int( opt.fiupmax*opt.winlen/opt.samprate)])))/np.mean(np.abs(np.real( fftwin[int(opt.filomin*opt.winlen/opt.samprate):int( opt.filomax*opt.winlen/opt.samprate)])))) if fi<opt.telefi: ntele+=1 # Allow if there are enough good stations to correlate if njunk <= (opt.nsta-opt.ncor) and ntele <= opt.teleok: trigs.append(alltrigs[i]) else: if njunk > 0: if ntele > 0: junk.append(alltrigs[i]) else: junkKurt.append(alltrigs[i]) else: junkFI.append(alltrigs[i]) return trigs, junk, junkFI, junkKurt
def getData(tstart, tend, opt): """ Download data from files in a folder, from IRIS, or a Earthworm waveserver A note on SAC/miniSEED files: as this makes no assumptions about the naming scheme of your data files, please ensure that your headers contain the correct SCNL information! tstart: UTCDateTime of beginning of period of interest tend: UTCDateTime of end of period of interest opt: Options object describing station/run parameters Returns ObsPy stream objects, one for cutting and the other for triggering """ nets = opt.network.split(',') stas = opt.station.split(',') locs = opt.location.split(',') chas = opt.channel.split(',') st = Stream() if opt.server == 'file': # Generate list of files if opt.server == 'file': flist = list(itertools.chain.from_iterable(glob.iglob(os.path.join( root,opt.filepattern)) for root, dirs, files in os.walk(opt.searchdir))) # Determine which subset of files to load based on start and end times and # station name; we'll fully deal with stations below flist_sub = [] for f in flist: # Load header only stmp = obspy.read(f, headonly=True) # Check if station is contained in the stas list if stmp[0].stats.station in stas: # Check if contains either start or end time ststart = stmp[0].stats.starttime stend = stmp[-1].stats.endtime if (ststart<=tstart and tstart<=stend) or (ststart<=tend and tend<=stend) or (tstart<=stend and ststart<=tend): flist_sub.append(f) # Fully load data from file stmp = Stream() for f in flist_sub: tmp = obspy.read(f, starttime=tstart, endtime=tend+opt.maxdt) if len(tmp) > 0: stmp = stmp.extend(tmp) # Filter and merge stmp = stmp.filter('bandpass', freqmin=opt.fmin, freqmax=opt.fmax, corners=2, zerophase=True) stmp = stmp.taper(0.05,type='hann',max_length=opt.mintrig) for m in range(len(stmp)): if stmp[m].stats.sampling_rate != opt.samprate: stmp[m] = stmp[m].resample(opt.samprate) stmp = stmp.merge(method=1, fill_value=0) # Only grab stations/channels that we want and in order netlist = [] stalist = [] chalist = [] loclist = [] for s in stmp: stalist.append(s.stats.station) chalist.append(s.stats.channel) netlist.append(s.stats.network) loclist.append(s.stats.location) # Find match of SCNL in header or fill empty for n in range(len(stas)): for m in range(len(stalist)): if (stas[n] in stalist[m] and chas[n] in chalist[m] and nets[n] in netlist[m] and locs[n] in loclist[m]): st = st.append(stmp[m]) if len(st) == n: print("Couldn't find "+stas[n]+'.'+chas[n]+'.'+nets[n]+'.'+locs[n]) trtmp = Trace() trtmp.stats.sampling_rate = opt.samprate trtmp.stats.station = stas[n] st = st.append(trtmp.copy()) else: if '.' not in opt.server: client = Client(opt.server) else: client = EWClient(opt.server, opt.port) for n in range(len(stas)): try: stmp = client.get_waveforms(nets[n], stas[n], locs[n], chas[n], tstart, tend+opt.maxdt) for m in range(len(stmp)): stmp[m].data = np.where(stmp[m].data == -2**31, 0, stmp[m].data) # replace -2**31 (Winston NaN token) w 0 stmp = stmp.filter('bandpass', freqmin=opt.fmin, freqmax=opt.fmax, corners=2, zerophase=True) stmp = stmp.taper(0.05,type='hann',max_length=opt.mintrig) for m in range(len(stmp)): if stmp[m].stats.sampling_rate != opt.samprate: stmp[m] = stmp[m].resample(opt.samprate) stmp = stmp.merge(method=1, fill_value=0) except (obspy.clients.fdsn.header.FDSNException): try: # try again stmp = client.get_waveforms(nets[n], stas[n], locs[n], chas[n], tstart, tend+opt.maxdt) for m in range(len(stmp)): stmp[m].data = np.where(stmp[m].data == -2**31, 0, stmp[m].data) # replace -2**31 (Winston NaN token) w 0 stmp = stmp.filter('bandpass', freqmin=opt.fmin, freqmax=opt.fmax, corners=2, zerophase=True) stmp = stmp.taper(0.05,type='hann',max_length=opt.mintrig) for m in range(len(stmp)): if stmp[m].stats.sampling_rate != opt.samprate: stmp[m] = stmp[m].resample(opt.samprate) stmp = stmp.merge(method=1, fill_value=0) except (obspy.clients.fdsn.header.FDSNException): print('No data found for {0}.{1}'.format(stas[n],nets[n])) trtmp = Trace() trtmp.stats.sampling_rate = opt.samprate trtmp.stats.station = stas[n] stmp = Stream().extend([trtmp.copy()]) # Last check for length; catches problem with empty waveserver if len(stmp) != 1: print('No data found for {0}.{1}'.format(stas[n],nets[n])) trtmp = Trace() trtmp.stats.sampling_rate = opt.samprate trtmp.stats.station = stas[n] stmp = Stream().extend([trtmp.copy()]) st.extend(stmp.copy()) # Edit 'start' time if using offset option if opt.maxdt: dts = np.fromstring(opt.offset, sep=',') for n, tr in enumerate(st): tr.stats.starttime = tr.stats.starttime-dts[n] st = st.trim(starttime=tstart, endtime=tend, pad=True, fill_value=0) stC = st.copy() return st, stC
def trigger(st, stC, rtable, opt): """ Run triggering algorithm on a stream of data. st: OBSPy stream of data rtable: Repeater table contains reference time of previous trigger in samples opt: Options object describing station/run parameters Returns triggered traces as OBSPy trace object updates ptime for next run """ tr = st[0] t = tr.stats.starttime cft = coincidence_trigger(opt.trigalg, opt.trigon, opt.trigoff, stC, opt.nstaC, sta=opt.swin, lta=opt.lwin, details=True) if len(cft) > 0: ind = 0 # Slice out the data from st and save the maximum STA/LTA ratio value for # use in orphan expiration # Convert ptime from time of last trigger to seconds before start time if rtable.attrs.ptime: ptime = (UTCDateTime(rtable.attrs.ptime) - t) else: ptime = -opt.mintrig for n in range(len(cft)): ttime = cft[n]['time'] # This is a UTCDateTime, not samples if (ttime >= t + opt.atrig) and (ttime >= t + ptime + opt.mintrig) and (ttime < t + len(tr.data)/opt.samprate - 2*opt.atrig): ptime = ttime - t # Cut out and append all data to first trace tmp = st.slice(ttime - opt.ptrig, ttime + opt.atrig) ttmp = tmp.copy() ttmp = ttmp.trim(ttime - opt.ptrig, ttime + opt.atrig + 0.05, pad=True, fill_value=0) ttmp[0].data = ttmp[0].data[0:opt.wshape] - np.mean( ttmp[0].data[0:opt.wshape]) for s in range(1,len(ttmp)): ttmp[0].data = np.append(ttmp[0].data, ttmp[s].data[ 0:opt.wshape] - np.mean(ttmp[s].data[0:opt.wshape])) ttmp[0].stats.maxratio = np.max(cft[n]['cft_peaks']) if ind is 0: trigs = Stream(ttmp[0]) ind = ind+1 else: trigs = trigs.append(ttmp[0]) if ind is 0: return [] else: rtable.attrs.ptime = (t + ptime).isoformat() return trigs else: return []
def getData(tstart, tend, opt): """ Download data from files in a folder, from IRIS, or a Earthworm waveserver A note on SAC/miniSEED files: as this makes no assumptions about the naming scheme of your data files, please ensure that your headers contain the correct SCNL information! tstart: UTCDateTime of beginning of period of interest tend: UTCDateTime of end of period of interest opt: Options object describing station/run parameters Returns ObsPy stream objects, one for cutting and the other for triggering """ nets = opt.network.split(',') stas = opt.station.split(',') locs = opt.location.split(',') chas = opt.channel.split(',') st = Stream() if opt.server == 'SAC' or opt.server == 'miniSEED': # Generate list of files if opt.server == 'SAC': flist = list(itertools.chain.from_iterable(glob.iglob(os.path.join( root,'*.sac')) for root, dirs, files in os.walk(opt.sacdir)))+list( itertools.chain.from_iterable(glob.iglob(os.path.join( root,'*.SAC')) for root, dirs, files in os.walk(opt.sacdir))) elif opt.server == 'miniSEED': flist = list(itertools.chain.from_iterable(glob.iglob(os.path.join( root,'*.mseed')) for root, dirs, files in os.walk(opt.mseeddir)))+list( itertools.chain.from_iterable(glob.iglob(os.path.join( root,'*.MSEED')) for root, dirs, files in os.walk(opt.mseeddir))) # Determine which subset of files to load based on start and end times and # station name; we'll fully deal with stations below flist_sub = [] for f in flist: # Load header only stmp = obspy.read(f, headonly=True) # Check if station is contained in the stas list if stmp[0].stats.station in stas: # Check if contains either start or end time ststart = stmp[0].stats.starttime stend = stmp[0].stats.endtime if (ststart<=tstart and tstart<=stend) or (ststart<=tend and tend<=stend) or (tstart<=stend and ststart<=tend): flist_sub.append(f) # Fully load data from file stmp = Stream() for f in flist_sub: tmp = obspy.read(f, starttime=tstart, endtime=tend+opt.maxdt) if len(tmp) > 0: stmp = stmp.extend(tmp) # Filter and merge stmp = stmp.filter('bandpass', freqmin=opt.fmin, freqmax=opt.fmax, corners=2, zerophase=True) stmp = stmp.taper(0.05,type='hann',max_length=opt.mintrig) for m in range(len(stmp)): if stmp[m].stats.sampling_rate != opt.samprate: stmp[m] = stmp[m].resample(opt.samprate) stmp = stmp.merge(method=1, fill_value=0) # Only grab stations/channels that we want and in order netlist = [] stalist = [] chalist = [] loclist = [] for s in stmp: stalist.append(s.stats.station) chalist.append(s.stats.channel) netlist.append(s.stats.network) loclist.append(s.stats.location) # Find match of SCNL in header or fill empty for n in range(len(stas)): for m in range(len(stalist)): if (stas[n] in stalist[m] and chas[n] in chalist[m] and nets[n] in netlist[m] and locs[n] in loclist[m]): st = st.append(stmp[m]) if len(st) == n: print("Couldn't find "+stas[n]+'.'+chas[n]+'.'+nets[n]+'.'+locs[n]) trtmp = Trace() trtmp.stats.sampling_rate = opt.samprate trtmp.stats.station = stas[n] st = st.append(trtmp.copy()) else: if '.' not in opt.server: client = Client(opt.server) else: client = EWClient(opt.server, opt.port) for n in range(len(stas)): try: stmp = client.get_waveforms(nets[n], stas[n], locs[n], chas[n], tstart, tend+opt.maxdt) stmp = stmp.filter('bandpass', freqmin=opt.fmin, freqmax=opt.fmax, corners=2, zerophase=True) stmp = stmp.taper(0.05,type='hann',max_length=opt.mintrig) for m in range(len(stmp)): if stmp[m].stats.sampling_rate != opt.samprate: stmp[m] = stmp[m].resample(opt.samprate) stmp = stmp.merge(method=1, fill_value=0) except (obspy.clients.fdsn.header.FDSNException): try: # try again stmp = client.get_waveforms(nets[n], stas[n], locs[n], chas[n], tstart, tend+opt.maxdt) stmp = stmp.filter('bandpass', freqmin=opt.fmin, freqmax=opt.fmax, corners=2, zerophase=True) stmp = stmp.taper(0.05,type='hann',max_length=opt.mintrig) for m in range(len(stmp)): if stmp[m].stats.sampling_rate != opt.samprate: stmp[m] = stmp[m].resample(opt.samprate) stmp = stmp.merge(method=1, fill_value=0) except (obspy.clients.fdsn.header.FDSNException): print('No data found for {0}.{1}'.format(stas[n],nets[n])) trtmp = Trace() trtmp.stats.sampling_rate = opt.samprate trtmp.stats.station = stas[n] stmp = Stream().extend([trtmp.copy()]) # Last check for length; catches problem with empty waveserver if len(stmp) != 1: print('No data found for {0}.{1}'.format(stas[n],nets[n])) trtmp = Trace() trtmp.stats.sampling_rate = opt.samprate trtmp.stats.station = stas[n] stmp = Stream().extend([trtmp.copy()]) st.extend(stmp.copy()) # Edit 'start' time if using offset option if opt.maxdt: dts = np.fromstring(opt.offset, sep=',') for n, tr in enumerate(st): tr.stats.starttime = tr.stats.starttime-dts[n] st = st.trim(starttime=tstart, endtime=tend, pad=True, fill_value=0) stC = st.copy() return st, stC
def get_preview(self, trace_ids=[], starttime=None, endtime=None, network=None, station=None, location=None, channel=None, pad=False): """ Returns the preview trace. """ # build up query session = self.session() query = session.query(WaveformChannel) # start and end time try: starttime = UTCDateTime(starttime) except Exception: starttime = UTCDateTime() - 60 * 20 finally: query = query.filter(WaveformChannel.endtime > starttime.datetime) try: endtime = UTCDateTime(endtime) except Exception: # 10 minutes endtime = UTCDateTime() finally: query = query.filter(WaveformChannel.starttime < endtime.datetime) # process arguments if trace_ids: # filter over trace id list trace_filter = or_() for trace_id in trace_ids: temp = trace_id.split('.') if len(temp) != 4: continue trace_filter.append( and_(WaveformChannel.network == temp[0], WaveformChannel.station == temp[1], WaveformChannel.location == temp[2], WaveformChannel.channel == temp[3])) if trace_filter.clauses: query = query.filter(trace_filter) else: # filter over network/station/location/channel id kwargs = { 'network': network, 'station': station, 'location': location, 'channel': channel } for key, value in kwargs.items(): if value is None: continue col = getattr(WaveformChannel, key) if '*' in value or '?' in value: value = value.replace('?', '_') value = value.replace('*', '%') query = query.filter(col.like(value)) else: query = query.filter(col == value) # execute query results = query.all() session.close() # create Stream st = Stream() for result in results: preview = result.get_preview() st.append(preview) # merge and trim st = merge_previews(st) st.trim(starttime, endtime, pad=pad) return st
def get_preview(self, trace_ids=[], starttime=None, endtime=None, network=None, station=None, location=None, channel=None, pad=False): """ Returns the preview trace. """ # build up query session = self.session() query = session.query(WaveformChannel) # start and end time try: starttime = UTCDateTime(starttime) except: starttime = UTCDateTime() - 60 * 20 finally: query = query.filter(WaveformChannel.endtime > starttime.datetime) try: endtime = UTCDateTime(endtime) except: # 10 minutes endtime = UTCDateTime() finally: query = query.filter(WaveformChannel.starttime < endtime.datetime) # process arguments if trace_ids: # filter over trace id list trace_filter = or_() for trace_id in trace_ids: temp = trace_id.split('.') if len(temp) != 4: continue trace_filter.append(and_( WaveformChannel.network == temp[0], WaveformChannel.station == temp[1], WaveformChannel.location == temp[2], WaveformChannel.channel == temp[3])) if trace_filter.clauses: query = query.filter(trace_filter) else: # filter over network/station/location/channel id kwargs = {'network': network, 'station': station, 'location': location, 'channel': channel} for key, value in kwargs.items(): if value is None: continue col = getattr(WaveformChannel, key) if '*' in value or '?' in value: value = value.replace('?', '_') value = value.replace('*', '%') query = query.filter(col.like(value)) else: query = query.filter(col == value) # execute query results = query.all() session.close() # create Stream st = Stream() for result in results: preview = result.get_preview() st.append(preview) # merge and trim st = merge_previews(st) st.trim(starttime, endtime, pad=pad) return st
def mergePreviews(stream): """ Merges all preview traces in one Stream object. Does not change the original stream because the data needs to be copied anyway. :type stream: :class:`~obspy.core.Stream` :param stream: Stream object to be merged :rtype: :class:`~obspy.core.Stream` :return: Merged Stream object. """ copied_traces = copy(stream.traces) stream.sort() # Group traces by id. traces = {} dtypes = [] for trace in stream: # Throw away empty traces. if trace.stats.npts == 0: continue if not hasattr(trace.stats, 'preview') or not trace.stats.preview: msg = 'Trace\n%s\n is no preview file.' % str(trace) raise Exception(msg) traces.setdefault(trace.id, []) traces[trace.id].append(trace) dtypes.append(trace.data.dtype) if len(traces) == 0: return Stream() # Initialize new Stream object. new_stream = Stream() for value in traces.values(): if len(value) == 1: new_stream.append(value[0]) continue # All traces need to have the same delta value and also be on the same # grid spacing. It is enough to only check the sampling rate because # the algorithm that creates the preview assures that the grid spacing # is correct. sampling_rates = set([tr.stats.sampling_rate for tr in value]) if len(sampling_rates) != 1: msg = 'More than one sampling rate for traces with id %s.' % \ value[0].id raise Exception(msg) delta = value[0].stats.delta # Check dtype. dtypes = set([str(tr.data.dtype) for tr in value]) if len(dtypes) > 1: msg = 'Different dtypes for traces with id %s' % value[0].id raise Exception(msg) dtype = dtypes.pop() # Get the minimum start and maximum endtime for all traces. min_starttime = min([tr.stats.starttime for tr in value]) max_endtime = max([tr.stats.endtime for tr in value]) samples = (max_endtime - min_starttime) / delta + 1 data = np.empty(samples, dtype=dtype) # Fill with negative one values which corresponds to a gap. data[:] = -1 # Create trace and give starttime. new_trace = Trace(data=data, header=value[0].stats) # Loop over all traces in value and add to data. for trace in value: start_index = int((trace.stats.starttime - min_starttime) / delta) end_index = start_index + len(trace.data) # Element-by-element comparison. data[start_index:end_index] = \ np.maximum(data[start_index:end_index], trace.data) # set npts again, because data is changed in place new_trace.stats.npts = len(data) new_stream.append(new_trace) stream.traces = copied_traces return new_stream
def dataclean(alltrigs, opt, flag=1): """ Examine triggers and weed out spikes and calibration pulses using kurtosis and outlier ratios alltrigs: triggers output from triggering opt: opt from config flag: 1 if defining window to check, 0 if want to check whole waveform for spikes (note that different threshold values should be used for different window lengths) Returns good trigs (trigs) and junk (junk) """ trigs=Stream() junk=Stream() for i in range(len(alltrigs)): njunk = 0 ntele = 0 for n in range(opt.nsta): dat = alltrigs[i].data[n*opt.wshape:(n+1)*opt.wshape] if flag == 1: datcut=dat[range(int((opt.ptrig-opt.kurtwin/2)*opt.samprate), int((opt.ptrig+opt.kurtwin/2)*opt.samprate))] else: datcut=dat # Calculate kurtosis in window k = stats.kurtosis(datcut) # Compute kurtosis of frequency amplitude spectrum next datf = np.absolute(fft(dat)) kf = stats.kurtosis(datf) # Calculate outlier ratio using z ((data-median)/mad); outliers have z > 4.45 mad = np.median(np.absolute(dat - np.median(dat))) z = (dat-np.median(dat))/mad orm = len(z[z>4.45])/np.array(len(z)).astype(float) if k >= opt.kurtmax or orm >= opt.oratiomax or kf >= opt.kurtfmax: njunk+=1 winstart = opt.ptrig*opt.samprate - opt.winlen/10 winend = opt.ptrig*opt.samprate - opt.winlen/10 + opt.winlen fftwin = np.reshape(fft(dat[winstart:winend]),(opt.winlen,)) if np.median(np.abs(dat[winstart:winend]))!=0: fi = np.log10(np.mean(np.abs(np.real( fftwin[int(opt.fiupmin*opt.winlen/opt.samprate):int( opt.fiupmax*opt.winlen/opt.samprate)])))/np.mean(np.abs(np.real( fftwin[int(opt.filomin*opt.winlen/opt.samprate):int( opt.filomax*opt.winlen/opt.samprate)])))) if fi<opt.telefi: ntele+=1 # Allow if there are enough good stations to correlate if njunk <= (opt.nsta-opt.ncor) and ntele <= opt.teleok: trigs.append(alltrigs[i]) else: junk.append(alltrigs[i]) return trigs, junk
def trigger(st, stC, rtable, opt): """ Run triggering algorithm on a stream of data. st: OBSPy stream of data rtable: Repeater table contains reference time of previous trigger in samples opt: Options object describing station/run parameters Returns triggered traces as OBSPy trace object updates ptime for next run """ tr = st[0] t = tr.stats.starttime cft = coincidence_trigger("classicstalta", opt.trigon, opt.trigoff, stC, opt.nstaC, sta=opt.swin, lta=opt.lwin, details=True) if len(cft) > 0: ind = 0 # Slice out the data from st and save the maximum STA/LTA ratio value for # use in orphan expiration # Convert ptime from time of last trigger to seconds before start time if rtable.attrs.ptime: ptime = (UTCDateTime(rtable.attrs.ptime) - t) else: ptime = -opt.mintrig for n in range(len(cft)): ttime = cft[n]['time'] # This is a UTCDateTime, not samples if (ttime >= t + opt.atrig) and (ttime >= t + ptime + opt.mintrig) and (ttime < t + len(tr.data)/opt.samprate - 2*opt.atrig): ptime = ttime - t # Slice and save as first trace ttmp = st.slice(ttime - opt.ptrig, ttime + opt.atrig) ttmp[0].data = ttmp[0].data[0:opt.wshape] - np.mean( ttmp[0].data[0:opt.wshape]) for s in range(1,len(ttmp)): ttmp[0].data = np.append(ttmp[0].data, ttmp[s].data[ 0:opt.wshape] - np.mean(ttmp[s].data[0:opt.wshape])) ttmp[0].stats.maxratio = np.max(cft[n]['cft_peaks']) if ind is 0: trigs = Stream(ttmp[0]) ind = ind+1 else: trigs = trigs.append(ttmp[0]) if ind is 0: return [] else: rtable.attrs.ptime = (t + ptime).isoformat() return trigs else: return []
def stream_stack_distance_intervals(st, interval, norm_type='no'): """ Stack average traces in a stream if their distance difference is smaller than interval. The stream containing a number of traces with given distance (e.g. from source) is used to create a number of equally spaced traces by averaging traces that fall into the same distance bin. If interval is a scalar the bins are equally spaced with a width of interval. If interval is a sequence its elements define the lower distance limit of the bins. :type st: :class:`~obspy.core.stream.Stream` :param st: Stream fo be used for stacking. :type interval: scalar os array like :param interval: width of bins in case of scalar or smaller edge of bins if interval is a sequence. :type norm_type: str :param norm_type: normalization to be applied within bins before stacking Possibilities are `no` for no normalization, `max` for normalization to maximum, `rms` for normaliation to root mean square or `abs_mean` for normalization to the mean of the absolute value. :rtype sst: :class:`~obspy.core.stream.Stream` :return: **sst**: stacked stream """ dist = [] npts = [] for tr in st: dist.append(tr.stats.sac['dist']) npts.append(tr.stats['npts']) if not hasattr(interval, "__len__"): bins = np.arange(min(dist), max(dist),interval) else: bins = np.array(interval) sst = Stream() for ii in bins: sst.append(Trace(data=np.zeros(max(npts),dtype=np.float64),header={ 'network':'stack','station':str(ii),'location':'', 'channel':st[0].stats['channel'],'starttime':st[0].stats['starttime'],'sampling_rate':st[0].stats['sampling_rate'], 'sac':{'dist':ii,'az':0,'evla':0.,'evlo':0.,'stla':ii/(np.pi*6371000)*180.,'stlo':0.}})) count = np.zeros_like(bins) weight = np.zeros_like(bins) for tr in st: ind = sum((tr.stats.sac['dist'] - bins)>=0)-1 if norm_type == 'no': norm = 1. elif norm_type == 'max': norm = np.max(np.abs(tr.data)) elif norm_type == 'rms': norm = np.sqrt(np.mean(tr.data**2)) elif norm_type == 'abs_mean': norm = np.mean(np.abs(tr.data)) else: raise ValueError('norm_type %s not implemented' % norm_type) sst[ind].data[0:tr.stats['npts']] += (tr.data/norm) weight[ind] += norm count[ind] += 1 for ind, tr in enumerate(sst): tr.data *= weight[ind]/(count[ind]**2) return sst
def removeGaps(self, min_gap, max_gap, verbose="False"): """ Returns the Stream object without trace gaps/overlaps. :param min_gap: All gaps smaller than this value will be omitted. The value is assumed to be in seconds. Defaults to None. :param max_gap: All gaps larger than this value will be omitted. The value is assumed to be in seconds. Defaults to None. :param verbose: stdout traces removed. Default verbose=False """ new=Stream() self.sort() gap_list = [] # since one would be left if(len(self) != 0): self.append(self[0]) for _i in xrange(1,len(self.traces) - 0): # skip traces with different network, station, location or channel if self.traces[_i - 1].id != self.traces[_i + 0].id: new.append(self.traces[_i]) continue # different sampling rates should always result in a gap or overlap if self.traces[_i - 1].stats.delta == self.traces[_i + 0].stats.delta: flag = True else: flag = False stats = self.traces[_i - 1].stats stime = stats['endtime'] etime = self.traces[_i + 0].stats['starttime'] delta = etime.timestamp - stime.timestamp # Check that any overlap is not larger than the trace coverage if delta < 0: temp = self.traces[_i + 0].stats['endtime'].timestamp - \ etime.timestamp if (delta * -1) > temp: delta = -1 * temp # Check gap/overlap criteria if min_gap and delta < min_gap: new.append(self.traces[_i - 1]) continue if max_gap and delta > max_gap: new.append(self.traces[_i - 1]) continue # Number of missing samples nsamples = int(round(fabs(delta) * stats['sampling_rate'])) # skip if is equal to delta (1 / sampling rate) if flag and nsamples == 1: new.append(self.traces[_i - 1]) continue elif delta > 0: nsamples -= 1 else: nsamples += 1 gap_list.append([_i,stats['network'], stats['station'], stats['location'], stats['channel'], stime, etime, delta, nsamples]) if verbose == "True" or verbose == "TRUE" or verbose == "true": print "Removed because of gap: ",stats['network'],stats['station'],stats['channel'],stime,etime,delta, nsamples return new
def group_channels(streams): """Consolidate streams for the same event. Checks to see if there are channels for one event in different streams, and groups them into one stream. Then streams are checked for duplicate channels (traces). Args: streams (list): List of Stream objects. Returns: list: List of Stream objects. """ # Return the original stream if there is only one if len(streams) <= 1: return streams # Get the all traces trace_list = [] for stream in streams: for trace in stream: trace_list += [trace] # Create a list of duplicate traces and event matches duplicate_list = [] match_list = [] for idx1, trace1 in enumerate(trace_list): matches = [] network = trace1.stats['network'] station = trace1.stats['station'] starttime = trace1.stats['starttime'] endtime = trace1.stats['endtime'] channel = trace1.stats['channel'] location = trace1.stats['location'] if 'units' in trace1.stats.standard: units = trace1.stats.standard['units'] else: units = '' if 'process_level' in trace1.stats.standard: process_level = trace1.stats.standard['process_level'] else: process_level = '' data = np.asarray(trace1.data) for idx2, trace2 in enumerate(trace_list): if idx1 != idx2 and idx1 not in duplicate_list: event_match = False duplicate = False try: same_data = ((data == np.asarray(trace2.data)).all()) except AttributeError: same_data = (data == np.asarray(trace2.data)) if 'units' in trace2.stats.standard: units2 = trace2.stats.standard['units'] else: units2 = '' if 'process_level' in trace2.stats.standard: process_level2 = trace2.stats.standard['process_level'] else: process_level2 = '' if (network == trace2.stats['network'] and station == trace2.stats['station'] and starttime == trace2.stats['starttime'] and endtime == trace2.stats['endtime'] and channel == trace2.stats['channel'] and location == trace2.stats['location'] and units == units2 and process_level == process_level2 and same_data): duplicate = True elif (network == trace2.stats['network'] and station == trace2.stats['station'] and starttime == trace2.stats['starttime'] and location == trace2.stats['location'] and units == units2 and process_level == process_level2): event_match = True if duplicate: duplicate_list += [idx2] if event_match: matches += [idx2] match_list += [matches] # Create an updated list of streams streams = [] for idx, matches in enumerate(match_list): stream = Stream() grouped = False for match_idx in matches: if match_idx not in duplicate_list: if idx not in duplicate_list: stream.append(trace_list[match_idx]) duplicate_list += [match_idx] grouped = True if grouped: stream.append(trace_list[idx]) duplicate_list += [idx] streams += [stream] # Check for ungrouped traces for idx, trace in enumerate(trace_list): if idx not in duplicate_list: stream = Stream() streams += [stream.append(trace)] warnings.warn('One channel stream:\n%s' % (stream), Warning) return streams
return None, None # Create the Stream obj and populate it st = Stream() for (_, rec) in df.iterrows(): fname = os.path.join(rec['path'], rec['file']) for tr in _read(fname, fformat, headonly=False, **kwargs).traces: if (networks_ok) and (tr.stats.network not in networks): continue if (stations_ok) and (tr.stats.station not in stations): continue if (locations_ok) and (tr.stats.location not in locations): continue if (channels_ok) and (tr.stats.channel not in channels): continue st.append(tr) if st.count() > 0: # If the starttime is given then it trims the resulting traces if starttime: st.trim(starttime=starttime, endtime=endtime, nearest_sample=nearest_sample) st.merge(method=1, fill_value=0, interpolation_samples=1) else: print "Empty stream" n_trace = st.count() return st, n_trace