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 _channel_from_stats(stats): if stats.standard.units in UNITS: units = UNITS[stats.standard.units] else: units = '' instrument = stats.standard.instrument serialnum = stats.standard.sensor_serial_number if len(instrument) or len(serialnum): equipment = Equipment(type=instrument, serial_number=serialnum) else: equipment = None depth = 0.0 azimuth = None c1 = 'horizontal_orientation' in stats.standard c2 = c1 and not np.isnan(stats.standard.horizontal_orientation) if c2: azimuth = stats.standard.horizontal_orientation else: azimuth = 0 if not (azimuth >= 0 and azimuth <= 360): azimuth = 0 response = None if 'response' in stats: response = stats['response'] else: # we may have instrument sensitivity... frequency = 1 / stats['standard']['instrument_period'] units = stats.standard.units if not np.isnan(stats['standard']['instrument_sensitivity']): sens = stats['standard']['instrument_sensitivity'] else: sens = 1.0 sensitivity = InstrumentSensitivity(sens, frequency=frequency, input_units=units, output_units='COUNTS') response = Response(instrument_sensitivity=sensitivity) comments = Comment(stats.standard.comments) logging.debug('channel: %s' % stats.channel) channel = Channel(stats.channel, stats.location, stats.coordinates['latitude'], stats.coordinates['longitude'], stats.coordinates['elevation'], depth, azimuth=azimuth, sample_rate=stats.sampling_rate, storage_format=stats.standard.source_format, calibration_units=units, comments=[comments], response=response, sensor=equipment) return channel
def _channel_from_stats(stats): if stats.standard.units in UNITS: units = UNITS[stats.standard.units] else: units = "" instrument = stats.standard.instrument serialnum = stats.standard.sensor_serial_number if len(instrument) or len(serialnum): equipment = Equipment(type=instrument, serial_number=serialnum) else: equipment = None depth = 0.0 azimuth = None c1 = "horizontal_orientation" in stats.standard c2 = c1 and not np.isnan(stats.standard.horizontal_orientation) if c2: azimuth = stats.standard.horizontal_orientation else: azimuth = 0 if not (azimuth >= 0 and azimuth <= 360): azimuth = 0 response = None if "response" in stats: response = stats["response"] else: # we may have instrument sensitivity... frequency = 1 / stats["standard"]["instrument_period"] units = stats.standard.units if not np.isnan(stats["standard"]["instrument_sensitivity"]): sens = stats["standard"]["instrument_sensitivity"] else: sens = 1.0 sensitivity = InstrumentSensitivity(sens, frequency=frequency, input_units=units, output_units="COUNTS") response = Response(instrument_sensitivity=sensitivity) comments = Comment(stats.standard.comments) logging.debug(f"channel: {stats.channel}") channel = Channel( stats.channel, stats.location, stats.coordinates["latitude"], stats.coordinates["longitude"], stats.coordinates["elevation"], depth, azimuth=azimuth, sample_rate=stats.sampling_rate, calibration_units=units, comments=[comments], response=response, sensor=equipment, ) return channel
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 read_fdsn_station_text_file(path_or_file_object): """ Function reading a FDSN station text file to an inventory object. :param path_or_file_object: File name or file like object. """ def _read(obj): r = unicode_csv_reader(obj, delimiter=native_str("|")) header = next(r) header[0] = header[0].lstrip("#") header = [_i.strip().lower() for _i in header] # IRIS currently has a wrong header name. Just map it. header = [ _i.replace("instrument", "sensordescription") for _i in header ] all_lines = [] for line in r: # Skip comment lines. if line[0].startswith("#"): continue all_lines.append([_i.strip() for _i in line]) return {"header": tuple(header), "content": all_lines} # Enable reading from files and buffers opened in binary mode. if (hasattr(path_or_file_object, "mode") and "b" in path_or_file_object.mode) or \ isinstance(path_or_file_object, io.BytesIO): buf = io.StringIO(path_or_file_object.read().decode("utf-8")) buf.seek(0, 0) path_or_file_object = buf if hasattr(path_or_file_object, "read"): content = _read(path_or_file_object) else: with open(path_or_file_object, "rt", newline="", encoding="utf8") as fh: content = _read(fh) # Figure out the type. if content["header"] == network_components: level = "network" filetypes = network_types elif content["header"] == station_components: level = "station" filetypes = station_types elif content["header"] == channel_components: level = "channel" filetypes = channel_types else: raise ValueError("Unknown type of header.") content = content["content"] converted_content = [] # Convert all types. for line in content: converted_content.append( [v_type(value) for value, v_type in zip(line, filetypes)]) # Now convert to an inventory object. inv = Inventory(networks=[], source=None) if level == "network": for net in converted_content: network = Network(code=net[0], description=net[1], start_date=net[2], end_date=net[3], total_number_of_stations=net[4]) inv.networks.append(network) elif level == "station": networks = collections.OrderedDict() for sta in converted_content: site = Site(name=sta[5]) station = Station(code=sta[1], latitude=sta[2], longitude=sta[3], elevation=sta[4], site=site, start_date=sta[6], end_date=sta[7]) if sta[0] not in networks: networks[sta[0]] = [] networks[sta[0]].append(station) for network_code, stations in networks.items(): net = Network(code=network_code, stations=stations) inv.networks.append(net) elif level == "channel": networks = collections.OrderedDict() stations = collections.OrderedDict() for channel in converted_content: net, sta, loc, chan, lat, lng, ele, dep, azi, dip, inst, scale, \ scale_freq, scale_units, s_r, st, et = channel if net not in networks: networks[net] = Network(code=net) if (net, sta) not in stations: station = Station(code=sta, latitude=lat, longitude=lng, elevation=ele) networks[net].stations.append(station) stations[(net, sta)] = station sensor = Equipment(type=inst) if scale is not None and scale_freq is not None: resp = Response(instrument_sensitivity=InstrumentSensitivity( value=scale, frequency=scale_freq, input_units=scale_units, output_units=None)) else: resp = None try: channel = Channel(code=chan, location_code=loc, latitude=lat, longitude=lng, elevation=ele, depth=dep, azimuth=azi, dip=dip, sensor=sensor, sample_rate=s_r, start_date=st, end_date=et, response=resp) except Exception as e: warnings.warn( "Failed to parse channel %s.%s.%s.%s due to: %s" % (net, sta, loc, chan, str(e)), UserWarning) continue stations[(net, sta)].channels.append(channel) inv.networks.extend(list(networks.values())) else: # Cannot really happen - just a safety measure. raise NotImplementedError("Unknown level: %s" % str(level)) return inv
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 test_write_stationtxt(self): """ Test writing stationtxt at channel level """ # Manually create a test Inventory object. resp_1 = Response( instrument_sensitivity=InstrumentSensitivity(frequency=0.02, input_units="M/S", output_units=None, value=8.48507E8)) resp_2 = Response( instrument_sensitivity=InstrumentSensitivity(frequency=1.0, input_units="M/S**2", output_units=None, value=53435.4)) resp_3 = Response( instrument_sensitivity=InstrumentSensitivity(frequency=0.03, input_units="M/S", output_units=None, value=6.27252E8)) test_inv = Inventory( source=None, networks=[ Network( code="IU", start_date=obspy.UTCDateTime("1988-01-01T00:00:00"), end_date=obspy.UTCDateTime("2500-12-31T23:59:59"), total_number_of_stations=1, description="Global Seismograph Network (GSN - IRIS/USGS)", stations=[ Station(code="ANMO", latitude=34.9459, longitude=-106.4572, elevation=1850.0, channels=[ Channel(code="BCI", location_code="", latitude=34.9459, longitude=-106.4572, elevation=1850.0, depth=100.0, azimuth=0.0, dip=0.0, sample_rate=0.0, sensor=Equipment( description= "Geotech KS-36000-I Borehole " "Seismometer"), start_date=obspy.UTCDateTime( "1989-08-29T00:00:00"), end_date=obspy.UTCDateTime( "1995-02-01T00:00:00"), response=resp_1), Channel( code="LNZ", location_code="20", latitude=34.9459, longitude=-106.4572, elevation=1820.7, depth=0.0, azimuth=0.0, dip=-90.0, sample_rate=0.0, sensor=Equipment( description="Titan Accelerometer"), start_date=obspy.UTCDateTime( "2013-06-20T16:30:00"), response=resp_2), ]), ]), Network( code="6E", start_date=obspy.UTCDateTime("2013-01-01T00:00:00"), end_date=obspy.UTCDateTime("2016-12-31T23:59:59"), total_number_of_stations=1, description="Wabash Valley Seismic Zone", stations=[ Station( code="SH01", latitude=37.7457, longitude=-88.1368, elevation=126.0, channels=[ Channel( code="LOG", location_code="", latitude=37.7457, longitude=-88.1368, elevation=126.0, depth=0.0, azimuth=0.0, dip=0.0, sample_rate=0.0, sensor=Equipment( description="Reftek 130 Datalogger"), start_date=obspy.UTCDateTime( "2013-11-23T00:00:00"), end_date=obspy.UTCDateTime( "2016-12-31T23:59:59"), response=resp_3) ]), ]) ]) # CHANNEL level test stio = io.StringIO() test_inv.write(stio, format="STATIONTXT", level="CHANNEL") # check contents content = stio.getvalue() expected = [ ("Network|Station|Location|Channel|Latitude|Longitude|" "Elevation|Depth|Azimuth|Dip|SensorDescription|Scale|" "ScaleFreq|ScaleUnits|SampleRate|StartTime|EndTime"), ("IU|ANMO||BCI|34.9459|-106.4572|1850.0|100.0|0.0|" "0.0|Geotech KS-36000-I Borehole Seismometer|" "848507000.0|0.02|M/S|0.0|1989-08-29T00:00:00|" "1995-02-01T00:00:00"), ("IU|ANMO|20|LNZ|34.9459|-106.4572|1820.7|0.0|0.0|" "-90.0|Titan Accelerometer|53435.4|1.0|M/S**2|0.0|" "2013-06-20T16:30:00|"), ("6E|SH01||LOG|37.7457|-88.1368|126.0|0.0|0.0|0.0|" "Reftek 130 Datalogger|627252000.0|0.03|M/S|0.0|" "2013-11-23T00:00:00|2016-12-31T23:59:59"), ] num_lines_written = 0 for line in expected: self.assertIn(line, content) num_lines_written = num_lines_written + 1 # assert that the number of lines written equals # the number of lines expected self.assertEqual(num_lines_written, len(expected)) # STATION level test stio = io.StringIO() test_inv.write(stio, format="STATIONTXT", level="STATION") # check contents content = stio.getvalue() expected = [ ("Network|Station|Latitude|Longitude|" "Elevation|SiteName|StartTime|EndTime"), ("IU|ANMO|34.9459|-106.4572|1850.0||"), ("6E|SH01|37.7457|-88.1368|126.0||"), ] num_lines_written = 0 for line in expected: self.assertIn(line, content) num_lines_written = num_lines_written + 1 # assert that the number of lines written equals # the number of lines expected self.assertEqual(num_lines_written, len(expected)) # NETWORK level test stio = io.StringIO() test_inv.write(stio, format="STATIONTXT", level="NETWORK") # check contents content = stio.getvalue() expected = [ ("Network|Description|StartTime|EndTime|TotalStations"), ("IU|Global Seismograph Network (GSN - IRIS/USGS)|" "1988-01-01T00:00:00|2500-12-31T23:59:59|1"), ("6E|Wabash Valley Seismic Zone|" "2013-01-01T00:00:00|2016-12-31T23:59:59|1"), ] num_lines_written = 0 for line in expected: self.assertIn(line, content) num_lines_written = num_lines_written + 1 # assert that the number of lines written equals # the number of lines expected self.assertEqual(num_lines_written, len(expected))
def test_reading_channel_file(self): """ Test reading a file at the channel level. """ resp_1 = Response( instrument_sensitivity=InstrumentSensitivity(frequency=0.02, input_units="M/S", output_units=None, value=4.88233E8)) resp_2 = Response( instrument_sensitivity=InstrumentSensitivity(frequency=0.03, input_units="M/S", output_units=None, value=4.98112E8)) resp_3 = Response( instrument_sensitivity=InstrumentSensitivity(frequency=0.03, input_units="M/S", output_units=None, value=6.27252E8)) # Manually create an expected Inventory object. expected_inv = Inventory( source=None, networks=[ Network( code="AK", stations=[ Station( code="BAGL", latitude=60.4896, longitude=-142.0915, elevation=1470, channels=[ Channel( code="LHZ", location_code="", latitude=60.4896, longitude=-142.0915, elevation=1470, depth=0.0, azimuth=0.0, dip=-90.0, sample_rate=1.0, sensor=Equipment( type="Nanometrics Trillium 240 Sec " "Response sn 400 and a"), start_date=obspy.UTCDateTime( "2013-01-01T00:00:00"), end_date=obspy.UTCDateTime( "2599-12-31T23:59:59"), response=resp_1) ]), Station( code="BWN", latitude=64.1732, longitude=-149.2991, elevation=356.0, channels=[ Channel( code="LHZ", location_code="", latitude=64.1732, longitude=-149.2991, elevation=356.0, depth=0.0, azimuth=0.0, dip=-90.0, sample_rate=1.0, sensor=Equipment( type="Nanometrics Trillium 240 Sec " "Response sn 400 and a"), start_date=obspy.UTCDateTime( "2010-07-23T00:00:00"), end_date=obspy.UTCDateTime( "2014-05-28T23:59:59"), response=resp_1), Channel( code="LHZ", location_code="", latitude=64.1732, longitude=-149.2991, elevation=356.0, depth=1.5, azimuth=0.0, dip=-90.0, sample_rate=1.0, sensor=Equipment( type="Nanometrics Trillium 120 Sec " "Response/Quanterra 33"), start_date=obspy.UTCDateTime( "2014-08-01T00:00:00"), end_date=obspy.UTCDateTime( "2599-12-31T23:59:59"), response=resp_2) ]) ]), Network( code="AZ", stations=[ Station( code="BZN", latitude=33.4915, longitude=-116.667, elevation=1301.0, channels=[ Channel( code="LHZ", location_code="", latitude=33.4915, longitude=-116.667, elevation=1301.0, depth=0.0, azimuth=0.0, dip=-90.0, sample_rate=1.0, sensor=Equipment( type= "Streckeisen STS-2 G1/Quanterra 330 " "Linear Phase Be"), start_date=obspy.UTCDateTime( "2010-07-26T17:22:00"), end_date=obspy.UTCDateTime( "2013-07-15T21:22:23"), response=resp_3), Channel( code="LHZ", location_code="", latitude=33.4915, longitude=-116.667, elevation=1301.0, depth=0.0, azimuth=0.0, dip=-90.0, sample_rate=1.0, sensor=Equipment( type= "Streckeisen STS-2 G1/Quanterra 330 " "Linear Phase Be"), start_date=obspy.UTCDateTime( "2013-07-15T21:22:23"), end_date=obspy.UTCDateTime( "2013-10-22T19:30:00"), response=resp_3), Channel( code="LHZ", location_code="", latitude=33.4915, longitude=-116.667, elevation=1301.0, depth=0.0, azimuth=0.0, dip=-90.0, sample_rate=1.0, sensor=Equipment( type= "Streckeisen STS-2 G1/Quanterra 330 " "Linear Phase Be"), start_date=obspy.UTCDateTime( "2013-10-22T19:30:00"), end_date=obspy.UTCDateTime( "2599-12-31T23:59:59"), response=resp_3) ]) ]) ]) # Read from a filename. filename = os.path.join(self.data_dir, "channel_level_fdsn.txt") inv = read_fdsn_station_text_file(filename) inv_obs = obspy.read_inventory(filename) # Copy creation date as it will be slightly different otherwise. inv.created = expected_inv.created inv_obs.created = expected_inv.created self.assertEqual(inv, expected_inv) self.assertEqual(inv_obs, expected_inv) # Read from open file in text mode. with open(filename, "rt", encoding="utf8") as fh: inv = read_fdsn_station_text_file(fh) fh.seek(0, 0) inv_obs = obspy.read_inventory(fh) inv.created = expected_inv.created inv_obs.created = expected_inv.created self.assertEqual(inv, expected_inv) self.assertEqual(inv_obs, expected_inv) # Read from open file in binary mode. with open(filename, "rb") as fh: inv = read_fdsn_station_text_file(fh) fh.seek(0, 0) inv_obs = obspy.read_inventory(fh) inv.created = expected_inv.created inv_obs.created = expected_inv.created self.assertEqual(inv, expected_inv) self.assertEqual(inv_obs, expected_inv) # Read from StringIO. with open(filename, "rt", encoding="utf8") as fh: with io.StringIO(fh.read()) as buf: buf.seek(0, 0) inv = read_fdsn_station_text_file(buf) buf.seek(0, 0) inv_obs = obspy.read_inventory(buf) inv.created = expected_inv.created inv_obs.created = expected_inv.created self.assertEqual(inv, expected_inv) self.assertEqual(inv_obs, expected_inv) # Read from BytesIO. with open(filename, "rb") as fh: with io.BytesIO(fh.read()) as buf: buf.seek(0, 0) inv = read_fdsn_station_text_file(buf) buf.seek(0, 0) inv_obs = obspy.read_inventory(buf) inv.created = expected_inv.created inv_obs.created = expected_inv.created self.assertEqual(inv, expected_inv) self.assertEqual(inv_obs, expected_inv)