def dataless_resp(self): """ Function used in order to extract both instrument response and instrument sensor make and model information from channel information. """ sp = self.import_dataless() # get station information min_freq = 1e-4 inventory = sp.getInventory() channels = inventory['channels'] for channel in channels: try: code = channel["channel_id"] net, stat, loc, chan = code.split('.') # check for and resolve channel naming problems from IRIS if chan == 'MHE': chan = 'LHE' if chan == 'MHZ': chan = 'LHZ' if chan == 'MHN': chan = 'LHN' sample_rate = channel['sampling_rate'] data = sp.getPAZ(code) poles = data['poles'] zeros = data['zeros'] t_samp = 1.0 / sample_rate #nyquist = sampling_rate / 2.0 nfft = sample_rate / min_freq cpx_resp, freqs = pazToFreqResp(poles, zeros, 1, t_samp, nfft, freq=True) #print "cpx_resp before: ", cpx_resp #print "freqs before: ", freqs # reduce the number of values saved to database; factor of 100 cpx_resp, freqs = (np.real(cpx_resp[::100]), np.real(freqs[::100])) cpx_resp, freqs = (np.abs(cpx_resp), np.abs(freqs)) #print "cpx_resp after: ", cpx_resp #print "freqs after: ", freqs out_code = '{}_{}_{}'.format(net, stat, chan) #print out_code, self.output_SQL(out_code, cpx_resp, freqs) self.output_resp(out_code, cpx_resp, freqs) except: a = 5
def dataless_resp(self): """ Function used in order to extract both instrument response and instrument sensor make and model information from channel information. """ sp = self.import_dataless() # get station information min_freq = 1e-4 inventory = sp.getInventory() channels = inventory['channels'] for channel in channels: try: code = channel["channel_id"] net, stat, loc, chan = code.split('.') # check for and resolve channel naming problems from IRIS if chan == 'MHE': chan = 'LHE' if chan == 'MHZ': chan = 'LHZ' if chan == 'MHN': chan = 'LHN' sample_rate = channel['sampling_rate'] data = sp.getPAZ(code) poles = data['poles'] zeros = data['zeros'] t_samp = 1.0 / sample_rate #nyquist = sampling_rate / 2.0 nfft = sample_rate / min_freq cpx_resp, freqs = pazToFreqResp(poles, zeros, 1, t_samp, nfft, freq=True) #print "cpx_resp before: ", cpx_resp #print "freqs before: ", freqs # reduce the number of values saved to database; factor of 100 cpx_resp, freqs =(np.real(cpx_resp[::100]), np.real(freqs[::100])) cpx_resp, freqs =(np.abs(cpx_resp), np.abs(freqs)) #print "cpx_resp after: ", cpx_resp #print "freqs after: ", freqs out_code = '{}_{}_{}'.format(net,stat,chan) #print out_code, self.output_SQL(out_code, cpx_resp, freqs) self.output_resp(out_code, cpx_resp, freqs) except: a = 5
def dataless_resp(self): """ Function used in order to extract both instrument response and instrument sensor make and model information from channel information. """ sp = self.import_dataless() # get station information min_freq = 1e-4 inventory = sp.getInventory() channels = inventory['channels'] for channel in channels: try: code = channel["channel_id"] net, stat, loc, chan = code.split('.') sample_rate = channel['sampling_rate'] data = sp.getPAZ(code) poles = data['poles'] zeros = data['zeros'] t_samp = 1.0 / sample_rate #nyquist = sampling_rate / 2.0 nfft = sample_rate / min_freq cpx_resp, freqs = pazToFreqResp(poles, zeros, 1, t_samp, nfft, freq=True) cpx_resp, freqs = cpx_resp[::100], freqs[::100] out_code = '{}_{}_{}'.format(net, stat, chan) print out_code, self.output_SQL(out_code, cpx_resp, freqs) except: continue
def dataless_resp(self): """ Function used in order to extract both instrument response and instrument sensor make and model information from channel information. """ sp = self.import_dataless() # get station information min_freq = 1e-4 inventory = sp.getInventory() channels = inventory['channels'] for channel in channels: try: code = channel["channel_id"] net, stat, loc, chan = code.split('.') sample_rate = channel['sampling_rate'] data = sp.getPAZ(code) poles = data['poles'] zeros = data['zeros'] t_samp = 1.0 / sample_rate #nyquist = sampling_rate / 2.0 nfft = sample_rate / min_freq cpx_resp, freqs = pazToFreqResp(poles, zeros, 1, t_samp, nfft, freq=True) cpx_resp, freqs = cpx_resp[::100], freqs[::100] out_code = '{}_{}_{}'.format(net,stat,chan) print out_code, self.output_SQL(out_code, cpx_resp, freqs) except: continue
def plot_response_curves(resp, freq_msu, amp_msu, best_freep, best_damp, best_scale, msu_freep,\ msu_damp, amp_average, amp_label, seismometer, sac_pz_file): # build an array of zeros with same length as freq_msu amp_predicted = np.zeros_like(freq_msu) # loop over the frequencies present in the msu data file one at a time # to find the amplitudes predicted for a given frequency # based on the best resp file for i, freq in enumerate(freq_msu): amp_predicted[i] = sim.paz2AmpValueOfFreqResp(resp, freq) # this code taken straight from the Obspy webpage examples # numbers for obspy to create a resp curve, based on an fft of a time series # with sample rate of 0.01 samp_rate = 0.01 npts = 16384 # obtain "continuous" amp and freq values from obsby function to display continuous response curve poles = resp['poles'] zeros = resp['zeros'] h, f = sim.pazToFreqResp(poles, zeros, best_scale, samp_rate, npts, freq=True) # plotting amp vs freq plt.figure() #plt.subplot(121) # plot the continuous response curve, and the msu data, plt.loglog(f, abs(h), freq_msu, amp_msu, 'go', markersize=6 ) # plot the predicted amplitudes at the MSU frequencies plt.loglog(freq_msu, amp_predicted, 'ro', markersize=4 ) # labels plt.xlabel('Frequency [Hz]') # this str function is part of the standard Python, no need to "import" a special "package" plt.ylabel( str(amp_label) ) plt.suptitle('Frequency vs Amplitude: Channel ' + str(seismometer) ) # plot over range from 2/3 * minimum frequency to 2.0 * maximum frequency # and over range from 2/3 * minimum amplitude to 2.0 * maximum amplitude plx_min = 0.05 # freq_msu[0] * 0.66 plx_max = 40.0 # freq_msu[len(freq_msu) - 1] * 2.00 ply_min = 0.10 # amp_msu[0] * 0.66 ply_max = 1000.0 # amp_msu[len(freq_msu) - 1] * 2.00 plt.axis([plx_min, plx_max, ply_min, ply_max]) freep_per = 100. * ( abs ( best_freep - msu_freep ) / msu_freep ) damp_per = 100. * ( abs ( best_damp - msu_damp ) / msu_damp ) scale_per = 100. * ( abs ( best_scale - amp_average ) / amp_average ) rsp = "" cdt = "Calibration date = "+ (time.strftime("%d/%m/%Y %H:%M:%S")) tfp = "free period = %.3f Hz (%.2f%% MSU: %.3f)" % ( 1./best_freep, freep_per, 1./msu_freep ) print ( "\n" ) print tfp tdr = "damping = %.3f (%.2f%% MSU: %.3f)" % ( best_damp, damp_per, msu_damp ) print tdr tsf = "scale = %.2f V.m/sec( Avg. amp: %.2f)" % ( best_scale, amp_average ) print tsf spz = "File: %s" % ( sac_pz_file ) #f.write("ZEROS {}\n".format(len(resp['zeros']) + 1 )) zzz = "ZEROS: {}".format(len(resp['zeros']) + 1 ) # f.write("POLES {}\n".format(len(resp['poles']))) ppp = "POLES {}\n".format(len(resp['poles'])) for pole in resp['poles']: # f.write("{:e} {:e}\n".format(pole.real, pole.imag)) rsp = rsp+"real: {:e} Imaginary: {:e}\n".format(pole.real, pole.imag) # f.write("CONSTANT {:e}".format(resp['gain'])) print "\nsensor gain constant {:e} V.m/sec".format(resp['gain']) # post results as text lines on the plot xtext = plx_min * 7. ytext = ply_min * 60 plt.text( xtext, ytext, cdt ) ytext = ply_min * 40 plt.text( xtext, ytext, tfp ) ytext = ply_min * 30 plt.text( xtext, ytext, tdr ) ytext = ply_min * 20 plt.text( xtext, ytext, tsf ) ytext = ply_min * 10 plt.text( xtext, ytext, zzz ) ytext = ply_min * 5 plt.text( xtext, ytext, ppp ) ytext = ply_min * 2 plt.text( xtext, ytext, rsp ) # post some symbols and text for a legend amp_symbol = np.zeros(1) amp_symbol[0] = best_scale * 1.0 freq_symbol = np.zeros(1) freq_symbol[0] = freq_msu[0] plt.loglog(freq_symbol, amp_symbol, 'go', markersize=6 ) plt.text( freq_symbol[0] * 1.1, amp_symbol[0], 'Measurement', va='center' ) amp_symbol[0] = best_scale * 0.70 freq_symbol[0] = freq_msu[0] plt.loglog(freq_symbol, amp_symbol, 'ro', markersize=4 ) plt.text( freq_symbol[0] * 1.1, amp_symbol[0], 'Model Best Fit', va='center' ) plt.grid(True, which='major') plt.grid(True, which='minor') fileopts = getoptions() # Use the getoptions def to parse the command line options. wdir = fileopts[0] # working directory fig = wdir+"\\"+seismometer + '_freq_v_amp' + '.png' # Place it in current working directory - drb txt = "best-fit freq. vs ampl. plot: %s" % ( fig ) print "\n" print txt plt.savefig( fig ) plt.show() # plt.close() # plotting phase vs freq, not sure how much this can be trusted #plt.subplot(122) plt.figure() #take negative of imaginary part phase = np.unwrap(np.arctan2(-h.imag, h.real)) plt.semilogx(f, phase) plt.xlabel('Frequency [Hz]') plt.ylabel('Phase [radians]') # title, centered above both subplots plt.suptitle('Frequency vs Phase: Seismometer ' + str(seismometer) ) plt.axis([0.004, 100, -3.5, 0.5]) # make more room in between subplots for the ylabel of right plot #plt.subplots_adjust(wspace=0.3) plt.grid(True, which='major') plt.grid(True, which='minor') fig = wdir+"\\"+ seismometer + '_freq_v_phase' + '.png' # save in data directory txt = "plotted best-fit frequency vs phase results - saved in file: %s" % ( fig ) print "\n" print txt plt.savefig( fig )
def correct_response(st, removeResp=False, removePAZ=False, simPAZ=False, pre_filt=None, cornFreq=0.0083): """ Correct the seismometer response. Seismometer response is given in either a dictionary ``removeResp'' or a dictionary ``removePAZ''. ``removeResp has precedence. The dictionaries have the following structure removeResp: dictionary with Response information to be removed has the following keys: respfile: (str) filename of evalresp response file. units: (str) Units to return response in. Can be either DIS, VEL or ACC start_stage: (int) integer stage numbers of start stage (<0 causes default evalresp bahaviour). stop_stage: (int) integer stage numbers of stop stage removePAZ: dictionary with poles and zeros to be removed has the following keys: poles: (list of complex numbers) location of poles zeros: (list of complex numbers) location of zeros gain: (float) gain sensitivity: (float) sensitivity It can easily be retrieved with obspy.arclink.client.Client.getPAZ if ``removeResp'' is given the response of each trace must be present in the respfile. If ``removePAZ'' is used the response is assumed to be the same for all traces in the stream. A filter specified in pre_filt can be applied in to avoid amplification of noise. The instrument to be simulated is either described in the dictionary simPAZ or if simPAZ is False by the corner frequency ``cornFreq''. Response correction is done in place and original data is overwritten. The input stream ``st'' should be demeaned and tapered. :type st: obspy.core.stream.Stream :param st: data stream to be corrected :type removeResp: dict :param removeResp: Response information to be removed :type removePAZ: dict :param removePAZ: Response information to be removed :type simPAZ: dict :param simPAZ: Response information to be simulated :type cornFreq: float :param cornFreq: corner frequency of instrument to be simulated :type pre_filt: list :param pre_filt: 4 corners of the filter """ for tr in st: starttime = tr.stats['starttime'] endtime = tr.stats['endtime'] network = tr.stats['network'] station = tr.stats['station'] channel = tr.stats['channel'] location = tr.stats['location'] length = tr.stats['npts'] sampling_rate = tr.stats['sampling_rate'] np2l = nextpow2(2.*length) if not simPAZ: simPAZ = cornFreq2Paz(cornFreq, damp=0.70716) simresp, freqs = np.conj(pazToFreqResp(simPAZ['poles'], simPAZ['zeros'], scale_fac=simPAZ['gain']*simPAZ['sensitivity'], t_samp=1./sampling_rate, nfft=np2l, freq=True)) #see Doc of pazToFreqResp for reason of conj() if removeResp: freqresp, freqs = evalresp(1./sampling_rate,np2l,removeResp['respfile'], starttime, network=network, station=station, channel=channel, locid=location, start_stage=removeResp['start_stage'], stop_stage=removeResp['stop_stage'], units=removeResp['units'], freq=True) else: freqresp, freqs = np.conj(pazToFreqResp(removePAZ['poles'], removePAZ['zeros'], scale_fac=removePAZ['gain']*removePAZ['sensitivity'], t_samp=1./sampling_rate, nfft=np2l, freq=True)) #see Doc of pazToFreqResp for reason of conj() ftr = np.fft.rfft(tr.data,n=np2l) ftr /= freqresp ftr[0] = 0.j # correct the NaN in the DC component ftr *= simresp if pre_filt: ftr *= c_sac_taper(freqs, flimit=pre_filt) tr.data = np.fft.irfft(ftr) tr.trim(starttime,endtime) return
def correct_response(st, removeResp=False, removePAZ=False, simPAZ=False, pre_filt=None, cornFreq=0.0083): """ Correct the seismometer response. Seismometer response is given in either a dictionary ``removeResp'' or a dictionary ``removePAZ''. ``removeResp has precedence. The dictionaries have the following structure removeResp: dictionary with Response information to be removed has the following keys: respfile: (str) filename of evalresp response file. units: (str) Units to return response in. Can be either DIS, VEL or ACC start_stage: (int) integer stage numbers of start stage (<0 causes default evalresp bahaviour). stop_stage: (int) integer stage numbers of stop stage removePAZ: dictionary with poles and zeros to be removed has the following keys: poles: (list of complex numbers) location of poles zeros: (list of complex numbers) location of zeros gain: (float) gain sensitivity: (float) sensitivity It can easily be retrieved with obspy.arclink.client.Client.getPAZ if ``removeResp'' is given the response of each trace must be present in the respfile. If ``removePAZ'' is used the response is assumed to be the same for all traces in the stream. A filter specified in pre_filt can be applied in to avoid amplification of noise. The instrument to be simulated is either described in the dictionary simPAZ or if simPAZ is False by the corner frequency ``cornFreq''. Response correction is done in place and original data is overwritten. The input stream ``st'' should be demeaned and tapered. :type st: obspy.core.stream.Stream :param st: data stream to be corrected :type removeResp: dict :param removeResp: Response information to be removed :type removePAZ: dict :param removePAZ: Response information to be removed :type simPAZ: dict :param simPAZ: Response information to be simulated :type cornFreq: float :param cornFreq: corner frequency of instrument to be simulated :type pre_filt: list :param pre_filt: 4 corners of the filter """ for tr in st: starttime = tr.stats['starttime'] endtime = tr.stats['endtime'] network = tr.stats['network'] station = tr.stats['station'] channel = tr.stats['channel'] location = tr.stats['location'] length = tr.stats['npts'] sampling_rate = tr.stats['sampling_rate'] np2l = nextpow2(2. * length) if not simPAZ: simPAZ = cornFreq2Paz(cornFreq, damp=0.70716) simresp, freqs = np.conj( pazToFreqResp( simPAZ['poles'], simPAZ['zeros'], scale_fac=simPAZ['gain'] * simPAZ['sensitivity'], t_samp=1. / sampling_rate, nfft=np2l, freq=True)) #see Doc of pazToFreqResp for reason of conj() if removeResp: freqresp, freqs = evalresp(1. / sampling_rate, np2l, removeResp['respfile'], starttime, network=network, station=station, channel=channel, locid=location, start_stage=removeResp['start_stage'], stop_stage=removeResp['stop_stage'], units=removeResp['units'], freq=True) else: freqresp, freqs = np.conj( pazToFreqResp( removePAZ['poles'], removePAZ['zeros'], scale_fac=removePAZ['gain'] * removePAZ['sensitivity'], t_samp=1. / sampling_rate, nfft=np2l, freq=True)) #see Doc of pazToFreqResp for reason of conj() ftr = np.fft.rfft(tr.data, n=np2l) ftr /= freqresp ftr[0] = 0.j # correct the NaN in the DC component ftr *= simresp if pre_filt: ftr *= c_sac_taper(freqs, flimit=pre_filt) tr.data = np.fft.irfft(ftr) tr.trim(starttime, endtime) return