def Amp_pick_sfile(sfile, datapath, respdir, chans=['Z'], var_wintype=True, winlen=0.9, pre_pick=0.2, pre_filt=True, lowcut=1.0, highcut=20.0, corners=4): """ Function to read information from a SEISAN s-file, load the data and the \ picks, cut the data for the channels given around the S-window, simulate \ a Wood Anderson seismometer, then pick the maximum peak-to-trough \ amplitude. Output will be put into a mag_calc.out file which will be in full S-file \ format and can be copied to a REA database. :type sfile: string :type datapath: string :param datapath: Path to the waveform files - usually the path to the WAV \ directory :type respdir: string :param respdir: Path to the response information directory :type chans: List of strings :param chans: List of the channels to pick on, defaults to ['Z'] - should \ just be the orientations, e.g. Z,1,2,N,E :type var_wintype: bool :param var_wintype: If True, the winlen will be \ multiplied by the P-S time if both P and S picks are \ available, otherwise it will be multiplied by the \ hypocentral distance*0.34 - dervided using a p-s ratio of \ 1.68 and S-velocity of 1.5km/s to give a large window, \ defaults to True :type winlen: float :param winlen: Length of window, see above parameter, if var_wintype is \ False then this will be in seconds, otherwise it is the \ multiplier to the p-s time, defaults to 0.5. :type pre_pick: float :param pre_pick: Time before the s-pick to start the cut window, defaults \ to 0.2 :type pre_filt: bool :param pre_filt: To apply a pre-filter or not, defaults to True :type lowcut: float :param lowcut: Lowcut in Hz for the pre-filter, defaults to 1.0 :type highcut: float :param highcut: Highcut in Hz for the pre-filter, defaults to 20.0 :type corners: int :param corners: Number of corners to use in the pre-filter """ # Hardwire a p-s multiplier of hypocentral distance based on p-s ratio of # 1.68 and an S-velocity 0f 1.5km/s, deliberately chosen to be quite slow ps_multiplier = 0.34 from eqcorrscan.utils import Sfile_util from obspy import read from scipy.signal import iirfilter from obspy.signal.invsim import paz2AmpValueOfFreqResp import warnings # First we need to work out what stations have what picks event = Sfile_util.readpicks(sfile)[0] # Convert these picks into a lists stations = [] # List of stations channels = [] # List of channels picktimes = [] # List of pick times picktypes = [] # List of pick types distances = [] # List of hypocentral distances picks_out = [] for pick in event.picks: if pick.phase_hint in ['P', 'S']: picks_out.append(pick) # Need to be able to remove this if there # isn't data for a station! stations.append(pick.waveform_id.station_code) channels.append(pick.waveform_id.channel_code) picktimes.append(pick.time) picktypes.append(pick.phase_hint) arrival = [ arrival for arrival in event.origins[0].arrivals if arrival.pick_id == pick.resource_id ] distances.append(arrival.distance) # Read in waveforms stream = read(datapath + '/' + Sfile_util.readwavename(sfile)[0]) if len(Sfile_util.readwavename(sfile)) > 1: for wavfile in Sfile_util.readwavename(sfile): stream += read(datapath + '/' + wavfile) stream.merge() # merge the data, just in case! # For each station cut the window uniq_stas = list(set(stations)) del arrival for sta in uniq_stas: for chan in chans: print 'Working on ' + sta + ' ' + chan tr = stream.select(station=sta, channel='*' + chan) if not tr: # Remove picks from file # picks_out=[picks_out[i] for i in xrange(len(picks))\ # if picks_out[i].station+picks_out[i].channel != \ # sta+chan] warnings.warn('There is no station and channel match in the ' + 'wavefile!') break else: tr = tr[0] # Apply the pre-filter if pre_filt: try: tr.detrend('simple') except: dummy = tr.split() dummy.detrend('simple') tr = dummy.merge()[0] tr.filter('bandpass', freqmin=lowcut, freqmax=highcut, corners=corners) sta_picks = [ i for i in xrange(len(stations)) if stations[i] == sta ] pick_id = event.picks[sta_picks[0]].resource_id arrival = [ arrival for arrival in event.origins[0].arrivals if arrival.pick_id == pick_id ] hypo_dist = arrival.distance CAZ = arrival.azimuth if var_wintype: if 'S' in [picktypes[i] for i in sta_picks] and\ 'P' in [picktypes[i] for i in sta_picks]: # If there is an S-pick we can use this :D S_pick = [ picktimes[i] for i in sta_picks if picktypes[i] == 'S' ] S_pick = min(S_pick) P_pick = [ picktimes[i] for i in sta_picks if picktypes[i] == 'P' ] P_pick = min(P_pick) try: tr.trim(starttime=S_pick - pre_pick, endtime=S_pick + (S_pick - P_pick) * winlen) except: break elif 'S' in [picktypes[i] for i in sta_picks]: S_pick = [ picktimes[i] for i in sta_picks if picktypes[i] == 'S' ] S_pick = min(S_pick) P_modelled = S_pick - hypo_dist * ps_multiplier try: tr.trim(starttime=S_pick - pre_pick, endtime=S_pick + (S_pick - P_modelled) * winlen) except: break else: # In this case we only have a P pick P_pick = [ picktimes[i] for i in sta_picks if picktypes[i] == 'P' ] P_pick = min(P_pick) S_modelled = P_pick + hypo_dist * ps_multiplier try: tr.trim(starttime=S_modelled - pre_pick, endtime=S_modelled + (S_modelled - P_pick) * winlen) except: break # Work out the window length based on p-s time or distance elif 'S' in [picktypes[i] for i in sta_picks]: # If the window is fixed we still need to find the start time, # which can be based either on the S-pick (this elif), or # on the hypocentral distance and the P-pick # Take the minimum S-pick time if more than one S-pick is # available S_pick = [ picktimes[i] for i in sta_picks if picktypes[i] == 'S' ] S_pick = min(S_pick) try: tr.trim(starttime=S_pick - pre_pick, endtime=S_pick + winlen) except: break else: # In this case, there is no S-pick and the window length is # fixed we need to calculate an expected S_pick based on the # hypocentral distance, this will be quite hand-wavey as we # are not using any kind of velocity model. P_pick = [ picktimes[i] for i in sta_picks if picktypes[i] == 'P' ] P_pick = min(P_pick) hypo_dist = [ distances[i] for i in sta_picks if picktypes[i] == 'P' ][0] S_modelled = P_pick + hypo_dist * ps_multiplier try: tr.trim(starttime=S_modelled - pre_pick, endtime=S_modelled + winlen) except: break # Find the response information resp_info = _find_resp(tr.stats.station, tr.stats.channel, tr.stats.network, tr.stats.starttime, tr.stats.delta, respdir) PAZ = [] seedresp = [] if resp_info and 'gain' in resp_info: PAZ = resp_info elif resp_info: seedresp = resp_info # Simulate a Wood Anderson Seismograph if PAZ and len(tr.data) > 10: # Set ten data points to be the minimum to pass tr = _sim_WA(tr, PAZ, None, 10) elif seedresp and len(tr.data) > 10: tr = _sim_WA(tr, None, seedresp, 10) elif len(tr.data) > 10: warnings.warn('No PAZ for ' + tr.stats.station + ' ' + tr.stats.channel + ' at time: ' + str(tr.stats.starttime)) continue if len(tr.data) <= 10: # Should remove the P and S picks if len(tr.data)==0 warnings.warn('No data found for: ' + tr.stats.station) # print 'No data in miniseed file for '+tr.stats.station+\ # ' removing picks' # picks_out=[picks_out[i] for i in xrange(len(picks_out))\ # if i not in sta_picks] break # Get the amplitude amplitude, period, delay = _max_p2t(tr.data, tr.stats.delta) if amplitude == 0.0: break print 'Amplitude picked: ' + str(amplitude) # Note, amplitude should be in meters at the moment! # Remove the pre-filter response if pre_filt: # Generate poles and zeros for the filter we used earlier: this # is how the filter is designed in the convenience methods of # filtering in obspy. z, p, k = iirfilter(corners, [ lowcut / (0.5 * tr.stats.sampling_rate), highcut / (0.5 * tr.stats.sampling_rate) ], btype='band', ftype='butter', output='zpk') filt_paz = { 'poles': list(p), 'zeros': list(z), 'gain': k, 'sensitivity': 1.0 } amplitude /= (paz2AmpValueOfFreqResp(filt_paz, 1 / period) * filt_paz['sensitivity']) # Convert amplitude to mm if PAZ: # Divide by Gain to get to nm (returns pm? 10^-12) # amplitude *=PAZ['gain'] amplitude /= 1000 if seedresp: # Seedresp method returns mm amplitude *= 1000000 # Write out the half amplitude, approximately the peak amplitude as # used directly in magnitude calculations # Page 343 of Seisan manual: # Amplitude (Zero-Peak) in units of nm, nm/s, nm/s^2 or counts amplitude *= 0.5 # Generate a PICK type object for this pick picks_out.append( Sfile_util.PICK(station=tr.stats.station, channel=tr.stats.channel, impulsivity=' ', phase='IAML', weight='', polarity=' ', time=tr.stats.starttime + delay, coda=999, amplitude=amplitude, peri=period, azimuth=float('NaN'), velocity=float('NaN'), AIN=999, SNR='', azimuthres=999, timeres=float('NaN'), finalweight=999, distance=hypo_dist, CAZ=CAZ)) # Copy the header from the sfile to a new local S-file fin = open(sfile, 'r') fout = open('mag_calc.out', 'w') for line in fin: if not line[79] == '7': fout.write(line) else: fout.write(line) break fin.close() for pick in picks_out: fout.write(pick) # Note this uses the legacy pick class fout.close() # Write picks out to new s-file for pick in picks_out: print pick # Sfile_util.populateSfile('mag_calc.out', picks_out) return picks_out
def lag_calc(detections, detect_data, templates, shift_len=0.2, min_cc=0.4): """ Overseer function to take a list of detection objects, cut the data for them to lengths of the same length of the template + shift_len on either side. This will then write out SEISAN s-file for the detections with pick times based on the lag-times found at the maximum correlation, providing that correlation is above the min_cc. :type detections: List of DETECTION :param detections: List of DETECTION objects :type detect_data: obspy.Stream :param detect_data: All the data needed to cut from - can be a gappy Stream :type templates: List of tuple of String, obspy.Stream :param templates: List of the templates used as tuples of template name, template :type shift_len: float :param shift_len: Shift length allowed for the pick in seconds, will be plus/minus this amount - default=0.2 :type min_cc: float :param min_cc: Minimum cross-correlation value to be considered a pick, default=0.4 """ from eqcorrscan.utils import Sfile_util from obspy import Stream # First work out the delays for each template delays = [] # List of tuples for template in templates: temp_delays = [] for tr in tempate[1]: temp_delays.append((tr.stats.station, tr.stats.channel,\ tr.stats.starttime-template.sort['starttime'][0].stats.starttime)) delays.append((template[0], temp_delays)) detect_streams = [] for detection in detections: detect_stream = [] for tr in detect_data: tr_copy = tr.copy() template = [ t for t in templates if t[0] == detection.template_name ][0] template = template.select(station=tr.stats.station, channel=tr.stats.channel) if template: template_len = len(template[0]) else: continue # If there is no template-data match then skip the rest # of the trace loop. delay = [ delay for delay in delays if delay[0] == detection.template_name ][0] delay=[d for d in delay if d[0]==tr.stats.station and \ d[1]==tr.stats.channel][0] detect_stream.append(tr_copy.trim(starttime=detection.detect_time-\ shift_len+delay, endtime=detection.detect_time+delay+\ shift_len+template_len)) detect_streams.append((detection.template_name, Stream(detect_stream))) # Tuple of template name and data stream # Segregate detections by template lags = [] for template in templates: template_detections=[detect[1] for detect in detect_streams\ if detect[0]==template[0]] lags.append(day_loop(template_detections, template[1])) # Write out the lags! for event in lags: # I think I have an old version of Sfile_util here sfilename = Sfile_util.blanksfile(wavefile, 'L', 'PYTH', 'out', True) picks = [] for pick in event: picks.append(Sfile_util.PICK()) Sfile_util.populateSfile(sfilename, picks)