def grab_data(scnl, T1, T2, hostname, port, fill_value=0): # scnl = list of station names (eg. ['PS4A.EHZ.AV.--','PVV.EHZ.AV.--','PS1A.EHZ.AV.--']) # T1 and T2 are start/end obspy UTCDateTimes # fill_value can be 0 (default), 'latest', or 'interpolate' # # returns stream of traces with gaps accounted for # # print('{} - {}'.format(T1.strftime('%Y.%m.%d %H:%M:%S'),T2.strftime('%Y.%m.%d %H:%M:%S'))) print('Grabbing data...') st=Stream() client = Client(hostname, int(port)) for sta in scnl: try: tr=client.get_waveforms(sta.split('.')[2], sta.split('.')[0],sta.split('.')[3],sta.split('.')[1], T1, T2, cleanup=True) if len(tr)>1: if fill_value==0 or fill_value==None: tr.detrend('demean') tr.taper(max_percentage=0.01) for sub_trace in tr: # deal with error when sub-traces have different dtypes if sub_trace.data.dtype.name != 'int32': sub_trace.data=sub_trace.data.astype('int32') if sub_trace.data.dtype!=np.dtype('int32'): sub_trace.data=sub_trace.data.astype('int32') # deal with rare error when sub-traces have different sample rates if sub_trace.stats.sampling_rate!=np.round(sub_trace.stats.sampling_rate): sub_trace.stats.sampling_rate=np.round(sub_trace.stats.sampling_rate) print('Merging gappy data...') tr.merge(fill_value=fill_value) # deal where trace length is smaller than expected window length if tr[0].stats.endtime - tr[0].stats.starttime < T2 - T1: tr.detrend('demean') tr.taper(max_percentage=0.01) except: tr=Stream() # if no data, create a blank trace for that channel if not tr: from obspy import Trace from numpy import zeros tr=Trace() tr.stats['station']=sta.split('.')[0] tr.stats['channel']=sta.split('.')[1] tr.stats['network']=sta.split('.')[2] tr.stats['location']=sta.split('.')[3] tr.stats['sampling_rate']=100 tr.stats['starttime']=T1 tr.data=zeros(int((T2-T1)*tr.stats['sampling_rate']),dtype='int32') st+=tr st.trim(T1,T2,pad=True, fill_value=0) print('Detrending data...') st.detrend('demean') return st
xlim((1 / 10., 150.)) savefig('NOISE' + str(st[0].stats.starttime.year) + str(st[0].stats.starttime.julday) + \ str(st[0].stats.starttime.hour) + str(st[0].stats.starttime.minute) + \ st[0].stats.station + st[0].stats.location + st[0].stats.channel + \ st[1].stats.station + st[1].stats.location + st[1].stats.channel + \ st[2].stats.station + st[2].stats.location + st[2].stats.channel + \ '.jpg', format = 'jpeg', dpi=400) savefig('NOISE' + str(st[0].stats.starttime.year) + str(st[0].stats.starttime.julday) + \ str(st[0].stats.starttime.hour) + str(st[0].stats.starttime.minute) + \ st[0].stats.station + st[0].stats.location + st[0].stats.channel + \ st[1].stats.station + st[1].stats.location + st[1].stats.channel + \ st[2].stats.station + st[2].stats.location + st[2].stats.channel + \ '.pdf', format = 'pdf', dpi=400) clf() st.detrend() titlelegend = 'Time Series: ' + str(st[0].stats.starttime.year) + ' ' + str(st[0].stats.starttime.julday) + ' ' + \ str(st[0].stats.starttime.hour) + ':' + str(st[0].stats.starttime.minute) + ':' + str(st[0].stats.starttime.second) + \ ' ' + str(st[0].stats.npts*delta) + ' seconds' tval = np.arange(0, st[0].stats.npts / st[0].stats.sampling_rate, st[0].stats.delta) tseriesplot = figure(2) title(titlelegend, fontsize=12) subplot(311) title(titlelegend, fontsize=12) plot(tval,st[0].data,'r',label='TSeries ' + st[0].stats.station + ' ' + \ st[0].stats.location + ' ' + st[0].stats.channel ) legend(prop={'size': 12}) xlim((0, np.amax(tval)))
summary = [] summary.append("#" * 79) summary.append("######## %s --- %s ########" % (T1, T2)) summary.append("#" * 79) summary.append(st.__str__(extended=True)) if exceptions: summary.append("#" * 33 + " Exceptions " + "#" * 33) summary += exceptions summary.append("#" * 79) trig = [] mutt = [] if st: # preprocessing, backup original data for plotting at end st.merge(0) st.detrend("linear") for tr in st: tr.data = tr.data * cosTaper(len(tr), 0.01) #st.simulate(paz_remove="self", paz_simulate=cornFreq2Paz(1.0), remove_sensitivity=False) st.sort() st.filter("bandpass", freqmin=PAR.LOW, freqmax=PAR.HIGH, corners=1, zerophase=True) st.trim(T1, T2) st_trigger = st.copy() st.normalize(global_max=False) # do the triggering trig = coincidenceTrigger("recstalta", PAR.ON, PAR.OFF, st_trigger, thr_coincidence_sum=PAR.MIN_STATIONS, max_trigger_length=PAR.MAXLEN, trigger_off_extension=PAR.ALLOWANCE, details=True, sta=PAR.STA, lta=PAR.LTA) for t in trig:
chan = 'HHZ' stime = UTCDateTime('2019-08-16 12:59:10') etime = stime + 120 client = Client() inv = client.get_stations(network=net, station=sta, starttime=stime, endtime=etime, channel=chan, level="response") st = Stream() st += client.get_waveforms(net, sta, loc, chan, stime, etime) st.detrend('constant') st.merge(fill_value=0) st.attach_response(inv) st.remove_response(output="DISP") #st.rotate(method="->ZNE",inventory=inv) st.filter("bandpass", freqmin=.5, freqmax=5) tr = st[0] t = np.linspace(0, (tr.stats.npts - 1) / tr.stats.sampling_rate, num=tr.stats.npts) fig = plt.figure(1, figsize=(12, 12)) plt.ylabel('Displacement (mm)', fontsize=14) plt.xlim([0, 120]) plt.ylim([-.02, .02]) plt.xlabel('seconds after origin', fontsize=14) plt.title('%s-%s-%s-%s, 2019-08-16, Hutchinson, KS Earthquake' %
def getChannelWaveformFiles (network, station, location, channel, starttime, endtime, removeTrend, performInstrumentCorrection,applyScale,deconFilter1, deconFilter2, deconFilter3, deconFilter4, waterLevel, unit, client, fileTag, respdir = None, inventory = None): debug = False # # stream holds the final stream # thisStartTime = UTCDateTime(starttime) thisEndTime = UTCDateTime(endtime) stream = Stream() streamIn = Stream() try: # # read in the files to a stream # print("[INFO] checking:",fileTag) if performInstrumentCorrection: streamIn = read(fileTag, starttime=thisStartTime, endtime=thisEndTime, nearest_sample=True, apply_calib=False ) else: print("[INFO] Apply scaling") streamIn = read(fileTag, starttime=thisStartTime, endtime=thisEndTime, nearest_sample=True, apply_calib=applyScale ) #print "STREAM IN",fileTag, starttime, endtime except Exception as e: print(str(e)) print("[ERRORnnelWaveformFile] ",network, station, location, channel, starttime, endtime) return(None) try: # # select the desire streams only # if location == "--": streamOut = streamIn.select(network=network, station=station, location="", channel=channel) else: streamOut = streamIn.select(network=network, station=station, location=location, channel=channel) for i in range(len(streamOut)): if performInstrumentCorrection: # # get the network, station, location and channel information # (thisNSLC,thisTime,junk) = str(streamOut[i]).split('|') (net,sta,loc,chan) = thisNSLC.strip().split('.') if len(loc) == 0: loc = "--" # # if respdir is defined, first look into user's respdir for stationXML files, if not found get it from FDSN # (start,end) = thisTime.split(' - ') inv = None if ( respdir is not None): print("[INFO] Getting response from", respdir) thisloc = loc if loc == '--': thisloc = '' inventory, inv = getResponseFromFile (inventory, respdir, net, sta, thisloc, chan, starttime, debug) if inv is not None: if debug: print("[INFO]: Attaching",inv) streamOut[i].attach_response(inv) stream += streamOut[i] else: thisStarttime = UTCDateTime(start.strip()) print("NO RESPONSE FILE:",net,sta,loc,chan,thisStarttime) if (inv is None and client is not None): # # The FDSN webservices return StationXML metadata. # print("[INFO] Getting response from IRIS") try: thisStarttime = UTCDateTime(start.strip()) thisEndtime = UTCDateTime(end.strip()) inv = client.get_stations(network=net,station=sta,location=loc,channel=chan,starttime=thisStarttime,endtime=thisEndtime,level="response") streamOut[i].attach_response(inv) stream += streamOut[i] if debug: print("[INFO] Response attached:",inv) except Exception as e: print(str(e)) thisStarttime = UTCDateTime(start.strip()) print("NO RESPONSE:",net,sta,loc,chan,thisStarttime,thisEndtime) continue else: print("[INFO] Response not removed") stream += streamOut[i] # # print stream Gap information # # print "\n\nSTREAM GAP INFORMATION:\n" #stream.printGaps() if(removeTrend > 0): stream.detrend("demean") # # remove the instrument response # if performInstrumentCorrection: print("[INFO] PERFORM INSTRUMENT CORRECTION",unit) if deconFilter1<=0 and deconFilter2<=0 and deconFilter3<=0 and deconFilter4<=0: print("[INFO] NO DECON FILTER APPLIED") stream.remove_response(output=unit,pre_filt=None, zero_mean=False, taper=False,water_level=waterLevel) else: stream.remove_response(output=unit,pre_filt=[deconFilter1,deconFilter2,deconFilter3,deconFilter4], zero_mean=False, taper=False,water_level=waterLevel) except Exception as e: print(str(e)) print("[ERROR] get_waveforms",network, station, location, channel, starttime, endtime) return(None,None) return(inventory,stream)
def section_plot(self, assoc_id, files, seconds_ahead=5, record_length=100, channel='Z', scale_factor=2, outfile=None): """ Plot a record section, with picks and waveforms ordered by distance :param assoc_id: ID number for the association to plot :param files: list of waveform files :param seconds_ahead: time before origin_time to start plot :param record_length: time after origin_time to end plot :param channel: channel to plot (default='Z') :param scale_factor: factor to scale wiggles by :param outfile: save plot to this filename """ assert files, 'empty wavefile list' station = self.assoc_db.query(Candidate.sta).\ filter(Candidate.assoc_id == assoc_id).all() sta_list = [] for sta, in station: sta_list.append(str(sta)) station_single = self.assoc_db.query(Pick.sta).\ filter(Pick.assoc_id == assoc_id).\ filter(Pick.locate_flag == None).all() for sta, in station_single: sta_list.append(str(sta)) # print sta_list eve = self.assoc_db.query(Associated).\ filter(Associated.id == assoc_id).first() # Earthquakes' epicenter eq_lat = eve.latitude eq_lon = eve.longitude # Read the waveforms ST = Stream() for file in files: st = read(file) ST += st # in case of some seismometer use channel code like BH1, BH2 # or BH3, resign the channel code as: if channel == 'E' or channel == 'e': Chan = 'E1' elif channel == 'N' or channel == 'n': Chan = 'N2' elif channel == 'Z' or channel == 'z': Chan = 'Z3' else: print('Please input component E, e, N, n, Z, or z,' ' the default is Z') # Calculate distance from headers lat/lon ST_new = Stream() for tr in ST: if tr.stats.channel[2] in Chan and tr.stats.station in sta_list: if ((tr.stats.starttime.datetime < eve.ot) and (tr.stats.endtime.datetime > eve.ot)): tr.trim( UTCDateTime(eve.ot - timedelta(seconds=seconds_ahead)), UTCDateTime(eve.ot + timedelta(seconds=record_length))) ST_new += tr # print ST_new.__str__(extended=True) # remove traces from same station, but different # samples # X31A..B Z | 2011-01-18T01:40:30.325Z - 18T01:41:44.975Z | 2987 samps # WMOK..B Z | 2011-01-18T01:40:30.325Z - 18T01:41:44.975Z | 2987 samps # X31A..B Z | 2011-01-18T01:40:30.325Z - 18T01:42:10.325Z | 4001 samps # WMOK..B Z | 2011-01-18T01:40:30.325Z - 18T01:42:10.325Z | 4001 samps # Remove traces from same station with different # samples while True: ST_new_sta = [] for tr in ST_new: ST_new_sta.append(tr.stats.station) duplicate = list( set([tr for tr in ST_new_sta if ST_new_sta.count(tr) > 1])) if not duplicate: break index = [ i for (i, j) in enumerate(ST_new_sta) if j == duplicate[-1] ] i = 0 while True: if (ST_new[index[i]].stats.npts < ST_new[index[i + 1]].stats.npts): del ST_new[index[i]] break elif (ST_new[index[i]].stats.npts >= ST_new[index[i + 1]].stats.npts): del ST_new[index[i + 1]] break # print ST_new.__str__(extended=True) ST_new.detrend('demean') # ST_new.filter('bandpass', freqmin=0.1, freqmax=100) segs, ticklocs, sta, circle_x, circle_y, segs_picks, ticklocs_picks, \ data_picks = self._make_section_arrays(ST_new, scale_factor, channel, seconds_ahead, record_length, assoc_id, eve) tick_min, tick_max = min(ticklocs), max(ticklocs) offsets = np.zeros((len(ST_new), 2), dtype=float) offsets[:, 0] = ticklocs offsets_picks = np.zeros((len(segs_picks), 2), dtype=float) offsets_picks[:, 0] = ticklocs_picks lines = LineCollection(segs, offsets=offsets, transOffset=None, linewidths=.25, color='gray') lines_picks = LineCollection(segs_picks, offsets=offsets_picks, transOffset=None, linewidths=1, color='k') # Set up axis fig = plt.figure(figsize=(15, 8)) ax1 = fig.add_subplot(111) x1 = tick_max + (tick_max - tick_min) * 0.1 plt.ylim(0, record_length) plt.xlim(0, x1) # Plot data and picks ax1.plot(circle_x, circle_y, 'o', c='gray') # dots where picks cross the waveform ax1.add_collection(lines) # Plot the data ax1.add_collection(lines_picks) # Plot the picks # Set axis labels and ticks ax1.set_xticks(ticklocs) ax1.set_xticklabels(sta) ax1.invert_yaxis() ax1.xaxis.tick_top() plt.setp(plt.xticks()[1], rotation=45) ax2 = ax1.secondary_xaxis('bottom') ax2.set_xlabel('Offset(km)') plt.ylabel('Record Length (s)', fontsize=18) plt.title( f'{channel}-channel Section Plot of Event at {tr.stats.starttime}') # plt.tight_layout() # Plot travel-time curves ttable = self.tt_stations_db_1D.query(TTtable1D).\ filter(TTtable1D.d_km <= x1).all() D = [x.d_km for x in ttable] P = [x.p_tt + seconds_ahead for x in ttable] S = [x.s_tt + seconds_ahead for x in ttable] ax1.plot(D, P, 'b--', D, S, 'r--', linewidth=2) if outfile: plt.savefig(outfile) else: plt.show()
days = int(float(hours) / 24.) + 1 if days < 1: days = 1 for day in range(days): ctime = stime2 + 24. * 60. * 60. * day string = '/msd/' + net + '_' + sta + '/' + str(ctime.year) + '/' + str( ctime.julday).zfill(3) + '/' #string = '/tr1/telemetry_days/' + net + '_' + sta + '/' + str(ctime.year) + '/*' + str(ctime.julday).zfill(3) + '/' st += read(string + loc + '_LH*.seed') if pcorr: st += read(string + '30_LDO*.seed') st.trim(starttime=stime2, endtime=stime2 + hours * 60. * 60.) st.merge() st.detrend('linear') st.detrend('constant') print(st) st.merge() print(st) #st.decimate(10) #st.decimate(10) st.rotate(method="->ZNE", inventory=inventory) for tr in st: if tr.stats.channel == 'LHN': tr.stats.channel = "LH1" if tr.stats.channel == "LHE": tr.stats.channel = "LH2"
def section_plot(self, assoc_id, files, seconds_ahead=5, record_length=100, channel='Z'): station = self.assoc_db.query( Candidate.sta).filter(Candidate.assoc_id == assoc_id).all() sta_list = [] for sta, in station: sta_list.append(str(sta)) station_single = self.assoc_db.query(Pick.sta).filter( Pick.assoc_id == assoc_id).filter(Pick.locate_flag == None).all() for sta, in station_single: sta_list.append(str(sta)) # print sta_list eve = self.assoc_db.query(Associated).filter( Associated.id == assoc_id).first() # Earthquakes' epicenter eq_lat = eve.latitude eq_lon = eve.longitude # Reading the waveforms ST = Stream() for file in files: st = read(file) ST += st # in case of some seismometer use channel code like BH1, BH2 or BH3, resign the channel code as: if channel == 'E' or channel == 'e': Chan = 'E1' elif channel == 'N' or channel == 'n': Chan = 'N2' elif channel == 'Z' or channel == 'z': Chan = 'Z3' else: print( 'Please input component E, e, N, n, Z, or z, the default is Z') # Calculating distance from headers lat/lon ST_new = Stream() # ;print ST for tr in ST: if tr.stats.channel[2] in Chan and tr.stats.station in sta_list: if tr.stats.starttime.datetime < eve.ot and tr.stats.endtime.datetime > eve.ot: tr.trim( UTCDateTime(eve.ot - timedelta(seconds=seconds_ahead)), UTCDateTime(eve.ot + timedelta(seconds=record_length))) ST_new += tr # print ST_new.__str__(extended=True) # remove the traces from same station, but different samples number (trace length) # .X31A..B Z | 2011-01-18T01:40:30.325000Z - 2011-01-18T01:41:44.975000Z | 40.0 Hz, 2987 samples # .WMOK..B Z | 2011-01-18T01:40:30.325000Z - 2011-01-18T01:41:44.975000Z | 40.0 Hz, 2987 samples # .X31A..B Z | 2011-01-18T01:40:30.325000Z - 2011-01-18T01:42:10.325000Z | 40.0 Hz, 4001 samples # .WMOK..B Z | 2011-01-18T01:40:30.325000Z - 2011-01-18T01:42:10.325000Z | 40.0 Hz, 4001 samples while True: ST_new_sta = [] for tr in ST_new: ST_new_sta.append(tr.stats.station) duplicate = list( set([tr for tr in ST_new_sta if ST_new_sta.count(tr) > 1])) if not duplicate: break index = [ i for (i, j) in enumerate(ST_new_sta) if j == duplicate[-1] ] i = 0 while True: if ST_new[index[i]].stats.npts < ST_new[index[i + 1]].stats.npts: del ST_new[index[i]] break elif ST_new[index[i]].stats.npts >= ST_new[index[ i + 1]].stats.npts: del ST_new[index[i + 1]] break # print ST_new.__str__(extended=True) ST_new.detrend('demean') # ST_new.filter('bandpass', freqmin=0.1, freqmax=100) factor = 10 numRows = len(ST_new) segs = [] ticklocs = [] sta = [] circle_x = [] circle_y = [] segs_picks = [] ticklocs_picks = [] for tr in ST_new: dmax = tr.data.max() dmin = tr.data.min() data = tr.data / (dmax - dmin) * factor t = np.arange( 0, round(tr.stats.npts / tr.stats.sampling_rate / tr.stats.delta) ) * tr.stats.delta # due to the float point arithmetic issue, can not use "t=np.arange(0,tr.stats.npts/tr.stats.sampling_rate,tr.stats.delta)" segs.append(np.hstack((data[:, np.newaxis], t[:, np.newaxis]))) lon, lat = self.tt_stations_db_1D.query( Station1D.longitude, Station1D.latitude).filter( Station1D.sta == tr.stats.station).first() distance = int( gps2dist_azimuth(lat, lon, eq_lat, eq_lon)[0] / 1000. ) # gps2DistAzimuth return in meters, convert to km by /1000 # distance=self.assoc_db.query(Candidate.d_km).filter(Candidate.assoc_id==assoc_id).filter(Candidate.sta==tr.stats.station).first()[0]#;print distance,tr.stats.station ticklocs.append(distance) sta.append(tr.stats.station) # DOT plot where picks are picked, notice that for vertical trace plot p is queried from Pick table, s from PickModified table # horizontal trace plot p and s queried from PickModified table if channel == 'Z3': picks_p = self.assoc_db.query( Pick.time).filter(Pick.assoc_id == assoc_id).filter( Pick.sta == tr.stats.station).filter( Pick.chan == tr.stats.channel).filter( Pick.phase == 'P').all() if not picks_p: picks_p = self.assoc_db.query(PickModified.time).filter( PickModified.assoc_id == assoc_id).filter( PickModified.sta == tr.stats.station).filter( PickModified.phase == 'P').all() picks_s = self.assoc_db.query(PickModified.time).filter( PickModified.assoc_id == assoc_id).filter( PickModified.sta == tr.stats.station).filter( PickModified.phase == 'S').all() # print picks_p,picks_s else: picks_p = self.assoc_db.query(PickModified.time).filter( PickModified.assoc_id == assoc_id).filter( PickModified.sta == tr.stats.station).filter( PickModified.phase == 'P').all() picks_s = self.assoc_db.query(PickModified.time).filter( PickModified.assoc_id == assoc_id).filter( PickModified.sta == tr.stats.station).filter( PickModified.phase == 'S').all() # print picks_p,picks_s picks = picks_p + picks_s # picks=self.assoc_db.query(PickModified.time).filter(PickModified.assoc_id==assoc_id).filter(PickModified.sta==tr.stats.station).all() for pick, in picks: index = int( (pick - eve.ot + timedelta(seconds=seconds_ahead)).total_seconds() / tr.stats.delta) # ;print pick,eve.ot,index,len(data) circle_x.append(distance + data[index]) circle_y.append(t[index]) # BAR plot where picks are picked t_picks = np.array([t[index], t[index]]) data_picks = np.array([data.min(), data.max()]) segs_picks.append( np.hstack( (data_picks[:, np.newaxis], t_picks[:, np.newaxis]))) ticklocs_picks.append(distance) tick_max = max(ticklocs) tick_min = min(ticklocs) offsets = np.zeros((numRows, 2), dtype=float) offsets[:, 0] = ticklocs offsets_picks = np.zeros((len(segs_picks), 2), dtype=float) offsets_picks[:, 0] = ticklocs_picks # lines=LineCollection(segs,offsets=offsets,transOffset=None,linewidths=.25,colors=[colorConverter.to_rgba(i) for i in ('b','g','r','c','m','y','k')]) #color='gray' lines = LineCollection(segs, offsets=offsets, transOffset=None, linewidths=.25, color='gray') # lines_picks=LineCollection(segs_picks,offsets=offsets_picks,transOffset=None,linewidths=1,color='r') lines_picks = LineCollection(segs_picks, offsets=offsets_picks, transOffset=None, linewidths=1, color='k') # print sta,ticklocs fig = plt.figure(figsize=(15, 8)) ax1 = fig.add_subplot(111) # ax1.plot(circle_x,circle_y,'o') # blue dots indicating where to cross the waveforms ax1.plot(circle_x, circle_y, 'o', c='gray') x0 = tick_min - (tick_max - tick_min) * 0.1 x1 = tick_max + (tick_max - tick_min) * 0.1 plt.ylim(0, record_length) plt.xlim(0, x1) ax1.add_collection(lines) ax1.add_collection(lines_picks) ax1.set_xticks(ticklocs) ax1.set_xticklabels(sta) ax1.invert_yaxis() ax1.xaxis.tick_top() # ax2 = ax1.twiny() # ax2.xaxis.tick_bottom() plt.setp(plt.xticks()[1], rotation=45) # xlabel('Station (km)') plt.xlabel('channel: ' + channel, fontsize=18) plt.ylabel('Record Length (s)', fontsize=18) # plt.title('Section Plot of Event at %s'%(tr.stats.starttime)) # plt.tight_layout() Dist = self.tt_stations_db_1D.query(TTtable1D.d_km).all() Ptime = self.tt_stations_db_1D.query(TTtable1D.p_tt).all() Stime = self.tt_stations_db_1D.query(TTtable1D.s_tt).all() D = [] P = [] S = [] for dist, in Dist: D.append(dist) for p, in Ptime: P.append(p + seconds_ahead) for s, in Stime: S.append(s + seconds_ahead) ax1.plot(D, P, 'k--', D, S, 'k--', linewidth=2) plt.show()
for day in range(239, 340): try: stP += read("/msd/" + netp + "_" + stap + "/" + str(sday.year) + "/" + str(day).zfill(3) + "/" + loc3 + "_" + chan3 + "*.seed") except: print('Day', day, 'has no data for test sensor') print(Daynum) # Rename data for rotations and organize in reverse for tr in st: if tr.stats.channel == 'LH1': tr.stats.channel = 'LHN' if tr.stats.channel == 'LH2': tr.stats.channel = 'LHE' st.detrend() st.merge() st.sort(reverse=True) for tr in st2: if tr.stats.channel == 'LH1': tr.stats.channel = 'LHN' if tr.stats.channel == 'LH2': tr.stats.channel = 'LHE' st2.detrend() st2.merge(fill_value=0.) st2.sort(reverse=True) # Data is in ZNE format if debug: