def geophone_response(resonance_frequency, gain, damping=0.707, output_resistance=np.inf, cable_length=np.inf, cable_capacitance=np.inf, sensitivity=1, stage_sequence_number=1): paz = corn_freq_2_paz(resonance_frequency, damp=damping) l = cable_length R = output_resistance C = cable_capacitance if ((R * l * C) != np.inf) and ((R * l * C) != 0): pole_cable = -1 / (R * l * C) paz['poles'].append(pole_cable) i_s = InstrumentSensitivity(sensitivity, resonance_frequency, input_units='M/S', output_units='M/S', input_units_description='velocity', output_units_description='velocity') pzr = PolesZerosResponseStage(stage_sequence_number, gain, resonance_frequency, 'M/S', 'M/S', 'LAPLACE (RADIANT/SECOND)', resonance_frequency, paz['zeros'], paz['poles']) return Response(instrument_sensitivity=i_s, response_stages=[pzr])
def accelerometer_response(resonance_frequency, gain, sensitivity=1, stage_sequence_number=1, damping=0.707): i_s = InstrumentSensitivity(sensitivity, resonance_frequency, input_units='M/S/S', output_units='M/S/S', input_units_description='acceleration', output_units_description='acceleration') paz = corn_freq_2_paz(resonance_frequency, damp=damping) paz['zeros'] = [] pzr = PolesZerosResponseStage(1, 1, 14, 'M/S/S', 'M/S', 'LAPLACE (RADIANT/SECOND)', 1, [], paz['poles']) return Response(instrument_sensitivity=i_s, response_stages=[pzr])
def getStation(stationBlock, units, transFuncs): ## Should probably do a check up here to see that the order given in block is consistent ## for entry in stationBlock: if entry.name == 'Station Identifier': # print 'NewStation!',entry.station_call_letters staDict = { 'code': entry.station_call_letters, 'latitude': entry.latitude, 'longitude': entry.longitude, 'elevation': entry.elevation, 'channels': [], 'site': Site(entry.site_name), 'creation_date': UTCDateTime(entry.start_effective_date), # Allows for save 'start_date': UTCDateTime(entry.start_effective_date), 'end_date': UTCDateTime(entry.end_effective_date) } staNetCode = entry.network_code # If found a new channel, reset the stages elif entry.name == 'Channel Identifier': # print 'NewChannel!',entry.channel_identifier stages = [] chaDict = { 'code': entry.channel_identifier, 'location_code': entry.location_identifier, 'latitude': entry.latitude, 'longitude': entry.longitude, 'elevation': entry.elevation, 'depth': entry.local_depth, 'sample_rate': entry.sample_rate, 'start_date': UTCDateTime(entry.start_date), 'end_date': UTCDateTime(entry.end_date), 'azimuth': entry.azimuth, 'dip': entry.dip } #code, location_code, latitude, longitude, elevation, depth # If on a new stage, set up the dictionary again # ...paz stage elif entry.name == 'Response Poles and Zeros': # Get units stageReqs = {} # print entry.name,entry.stage_sequence_number # print entry # quit() stageReqs['input_units'] = units[entry.stage_signal_input_units] stageReqs['output_units'] = units[entry.stage_signal_output_units] # Collect the poles and zeros lastType = 'paz' if entry.number_of_complex_zeros == 0: zeros = np.array([], dtype=float) else: zeros = np.array(entry.real_zero, dtype=float) + np.array( entry.imaginary_zero, dtype=float) * 1j if entry.number_of_complex_poles == 0: poles = np.array([], dtype=float) else: poles = np.array(entry.real_pole, dtype=float) + np.array( entry.imaginary_pole, dtype=float) * 1j # Form the paz response dictionary (also ensure arrays are 1D) pazDict = { 'pz_transfer_function_type': transFuncs[entry.transfer_function_types], 'normalization_factor': entry.A0_normalization_factor, 'normalization_frequency': entry.normalization_frequency, 'zeros': setArrDim(zeros), 'poles': setArrDim(poles) } # ...coeff stage elif entry.name == 'Response Coefficients': # Get units stageReqs = {} # print entry.name,entry.stage_sequence_number stageReqs['input_units'] = units[entry.signal_input_units] stageReqs['output_units'] = units[entry.signal_output_units] # Collect the coefficients lastType = 'coef' if entry.number_of_denominators == 0: denom = np.array([], dtype=float) denomErr = np.array([], dtype=float) else: denom = np.array(entry.denominator_coefficient, dtype=float) denomErr = np.array(entry.denominator_error, dtype=float) if entry.number_of_numerators == 0: numer = np.array([], dtype=float) numerErr = np.array([], dtype=float) else: numer = np.array(entry.numerator_coefficient, dtype=float) numerErr = np.array(entry.numerator_error, dtype=float) # Convert these arrays into lists of numbers which have uncertainty (also ensure arrays are 1D) denomArr = genArrWithUncertainty(setArrDim(denom), setArrDim(denomErr)) numerArr = genArrWithUncertainty(setArrDim(numer), setArrDim(numerErr)) # Form the coeefficient response dictionary coefDict = { 'cf_transfer_function_type': transFuncs[entry.response_type], 'numerator': numerArr, 'denominator': denomArr } # Get the decimation sampling info elif entry.name == 'Decimation': # print entry.name,entry.stage_sequence_number stageReqs['decimation_input_sample_rate'] = Frequency( entry.input_sample_rate) stageReqs['decimation_factor'] = entry.decimation_factor stageReqs['decimation_offset'] = entry.decimation_offset stageReqs['decimation_delay'] = FloatWithUncertaintiesAndUnit( entry.estimated_delay) stageReqs['decimation_correction'] = FloatWithUncertaintiesAndUnit( entry.correction_applied) # Get the stage sensitivity elif entry.name == 'Channel Sensitivity Gain': # print entry.name,entry.stage_sequence_number if entry.stage_sequence_number != 0: stageReqs[ 'stage_sequence_number'] = entry.stage_sequence_number stageReqs['stage_gain'] = entry.sensitivity_gain stageReqs['stage_gain_frequency'] = entry.frequency # See what type of stage this was if lastType == 'paz': pazDict.update(stageReqs) stages.append(PolesZerosResponseStage(**pazDict)) else: coefDict.update(stageReqs) stages.append(CoefficientsTypeResponseStage(**coefDict)) # If on the last stage, send off the collected stage info else: if len(stages) > 0: instrSens = InstrumentSensitivity(entry.sensitivity_gain, entry.frequency, stages[0].input_units, stages[-1].output_units) # Finalize the channel dictionary, and append this channel to the station dictionary chaResp = Response(response_stages=stages, instrument_sensitivity=instrSens) chaDict['response'] = chaResp staDict['channels'].append(Channel(**chaDict)) # Return the stations to the list of stations (also track the network code) return Station(**staDict), staNetCode
def _read_response_stage(stage, rate, stage_number, input_units, output_units): """ Private function to read a response stage :param stage: response stage element :param rate: stage sample rate :param stage_number: response stage number :param input_units: input units of stage :param output_units output units of stage """ elem_type = stage.tag.split("}")[1] stage_sequence_number = stage_number # Obtain the stage gain and frequency # Default to a gain of 0 and frequency of 0 if missing stage_gain = _attr2obj(stage, "gain", float) or 0 stage_gain_frequency = _attr2obj(stage, "gainFrequency", float) or 0.0 name = _attr2obj(stage, "name", str) resource_id = _attr2obj(stage, "publicID", str) # Determine the decimation parameters # This is dependent on the type of stage # Decimation delay/correction need to be normalized if elem_type == "responseFIR": decimation_factor = _attr2obj(stage, "decimationFactor", int) if rate != 0.0: temp = _attr2obj(stage, "delay", float) / rate decimation_delay = _read_float_var(temp, FloatWithUncertaintiesAndUnit, unit=True) temp = _attr2obj(stage, "correction", float) / rate decimation_corr = _read_float_var(temp, FloatWithUncertaintiesAndUnit, unit=True) else: decimation_delay = _read_float_var("inf", FloatWithUncertaintiesAndUnit, unit=True) decimation_corr = _read_float_var("inf", FloatWithUncertaintiesAndUnit, unit=True) decimation_input_sample_rate = \ _read_float_var(rate, Frequency) decimation_offset = int(0) elif elem_type == "datalogger": decimation_factor = int(1) decimation_delay = _read_float_var(0.00, FloatWithUncertaintiesAndUnit, unit=True) decimation_corr = _read_float_var(0.00, FloatWithUncertaintiesAndUnit, unit=True) decimation_input_sample_rate = \ _read_float_var(rate, Frequency) decimation_offset = int(0) elif elem_type == "responsePAZ" or elem_type == "responsePolynomial": decimation_factor = None decimation_delay = None decimation_corr = None decimation_input_sample_rate = None decimation_offset = None else: raise ValueError("Unknown type of response: " + str(elem_type)) # set up list of for this stage arguments kwargs = { "stage_sequence_number": stage_sequence_number, "input_units": str(input_units), "output_units": str(output_units), "input_units_description": None, "output_units_description": None, "resource_id": None, "resource_id2": resource_id, "stage_gain": stage_gain, "stage_gain_frequency": stage_gain_frequency, "name": name, "description": None, "decimation_input_sample_rate": decimation_input_sample_rate, "decimation_factor": decimation_factor, "decimation_offset": decimation_offset, "decimation_delay": decimation_delay, "decimation_correction": decimation_corr } # Different processing for different types of responses # currently supported: # PAZ, COEFF, FIR, Polynomial response is not supported; # could not find example if elem_type == 'responsePAZ': # read normalization params normalization_freq = _attr2obj(stage, "normalizationFrequency", Frequency) normalization_factor = _attr2obj(stage, "normalizationFactor", float) # Parse the type of the transfer function # A: Laplace (rad) # B: Laplace (Hz) # D: digital (z-transform) pz_transfer_function_type = _attr2obj(stage, "type", str) if pz_transfer_function_type == 'A': pz_transfer_function_type = 'LAPLACE (RADIANS/SECOND)' elif pz_transfer_function_type == 'B': pz_transfer_function_type = 'LAPLACE (HERTZ)' elif pz_transfer_function_type == 'D': pz_transfer_function_type = 'DIGITAL (Z-TRANSFORM)' else: msg = ("Unknown transfer function code %s. Defaulting to Laplace" "(rad)") % pz_transfer_function_type warnings.warn(msg) pz_transfer_function_type = 'LAPLACE (RADIANS/SECOND)' # Parse string of poles and zeros # paz are stored as a string in arclinkXML # e.g. (-0.01234,0.01234) (-0.01234,-0.01234) zeros_array = stage.find(_ns("zeros")).text poles_array = stage.find(_ns("poles")).text if zeros_array is not None: zeros_array = zeros_array.split(" ") else: zeros_array = [] if poles_array is not None: poles_array = poles_array.split(" ") else: poles_array = [] # Keep counter for pole/zero number cnt = 0 poles = [] zeros = [] for el in poles_array: poles.append(_tag2pole_or_zero(el, cnt)) cnt += 1 for el in zeros_array: zeros.append(_tag2pole_or_zero(el, cnt)) cnt += 1 # Return the paz response return PolesZerosResponseStage( pz_transfer_function_type=pz_transfer_function_type, normalization_frequency=normalization_freq, normalization_factor=normalization_factor, zeros=zeros, poles=poles, **kwargs) elif elem_type == 'datalogger': cf_transfer_function_type = "DIGITAL" numerator = [] denominator = [] return CoefficientsTypeResponseStage( cf_transfer_function_type=cf_transfer_function_type, numerator=numerator, denominator=denominator, **kwargs) elif elem_type == 'responsePolynomial': raise NotImplementedError("responsePolynomial not" "implemented. Contact the ObsPy developers") # Polynomial response (UNTESTED) # Currently not implemented in ObsPy (20-11-2015) f_low = None f_high = None max_err = None appr_type = _attr2obj(stage, "approximationType", str) appr_low = _attr2obj(stage, "approximationLowerBound", float) appr_high = _attr2obj(stage, "approximationUpperBound", float) coeffs_str = _tag2obj(stage, _ns("coefficients"), str) if coeffs_str is not None: coeffs = coeffs_str.split(" ") coeffs_float = [] i = 0 # pass additional mapping of coefficient counter # so that a proper stationXML can be formatted for c in coeffs: temp = _read_float_var(c, FilterCoefficient, additional_mapping={str("number"): i}) coeffs_float.append(temp) i += 1 return PolynomialResponseStage(approximation_type=appr_type, frequency_lower_bound=f_low, frequency_upper_bound=f_high, approximation_lower_bound=appr_low, approximation_upper_bound=appr_high, maximum_error=max_err, coefficients=coeffs, **kwargs) elif elem_type == 'responseFIR': # For the responseFIR obtain the symmetry and # list of coefficients coeffs_str = _tag2obj(stage, _ns("coefficients"), str) coeffs_float = [] if coeffs_str is not None and coeffs_str != 'None': coeffs = coeffs_str.split(" ") i = 0 # pass additional mapping of coefficient counter # so that a proper stationXML can be formatted for c in coeffs: temp = _read_float_var(c, FilterCoefficient, additional_mapping={str("number"): i}) coeffs_float.append(temp) i += 1 # Write the FIR symmetry to what ObsPy expects # A = NONE, B = ODD, C = EVEN symmetry = _attr2obj(stage, "symmetry", str) if symmetry == 'A': symmetry = 'NONE' elif symmetry == 'B': symmetry = 'ODD' elif symmetry == 'C': symmetry = 'EVEN' else: raise ValueError('Unknown symmetry metric; expected A, B, or C') return FIRResponseStage(coefficients=coeffs_float, symmetry=symmetry, **kwargs) else: raise NotImplementedError