ObsPy Tutorial
Handling Station Metadata
</div> </div> </div> image: User:Abbaszade656 / Wikimedia Commons / CC-BY-SA-4.0

Workshop for the "Training in Network Management Systems and Analytical Tools for Seismic"

Baku, October 2018

Seismo-Live: http://seismo-live.org

Authors:

In [ ]:
%matplotlib inline
import matplotlib.pyplot as plt
plt.style.use('ggplot')
plt.rcParams['figure.figsize'] = 12, 8
  • for station metadata, the de-facto standard of the future (replacing SEED/RESP) is FDSN StationXML
  • FDSN StationXML files can be read using read_inventory()
In [ ]:
import obspy

inventory = obspy.read_inventory("./data/station_PFO.xml")
print(type(inventory))
  • the nested ObsPy Inventory class structure (Inventory/Station/Channel/Response/...) is closely modelled after FDSN StationXML
In [ ]:
!head data/station_BFO.xml
In [ ]:
print(inventory)
In [ ]:
network = inventory[0]
print(network)
In [ ]:
station = network[0]
print(station)
In [ ]:
channel = station[0]
print(channel)
In [ ]:
print(channel.response)
In [ ]:
st = obspy.read("./data/waveform_PFO.mseed")
print(st)
In [ ]:
inv = obspy.read_inventory("./data/station_PFO.xml", format="STATIONXML")
In [ ]:
print(st[0].stats)
  • the instrument response can be deconvolved from the waveform data using the convenience method Stream.remove_response()
  • evalresp is used internally to calculate the instrument response
In [ ]:
st.plot()
st.remove_response(inventory=inv)
st.plot()
  • several options can be used to specify details of the deconvolution (water level, frequency domain prefiltering), output units (velocity/displacement/acceleration), demeaning, tapering and to specify if any response stages should be omitted
In [ ]:
st = obspy.read("./data/waveform_PFO.mseed")
st.remove_response(inventory=inv, water_level=60, pre_filt=(0.01, 0.02, 8, 10), output="DISP")
st.plot()

Finally, if station metadata is not available from the operator or from the data center serving the data, or in case of temporary station deployments and field campaigns, full station metadata can be assembled using ObsPy, including response information gathered from the IRIS Nominal Response Library (NRL):

In [ ]:
import obspy
from obspy.core.inventory import Inventory, Network, Station, Channel, Site
from obspy.clients.nrl.client import NRL


# We'll first create all the various objects. These strongly follow the
# hierarchy of StationXML files.
inv = Inventory(
    # We'll add networks later.
    networks=[],
    # The source should be the id whoever create the file.
    source="ObsPy-Tutorial")

net = Network(
    # This is the network code according to the SEED standard.
    code="XX",
    # A list of stations. We'll add one later.
    stations=[],
    description="A test stations.",
    # Start-and end dates are optional.
    start_date=obspy.UTCDateTime(2016, 1, 2))

sta = Station(
    # This is the station code according to the SEED standard.
    code="ABC",
    latitude=1.0,
    longitude=2.0,
    elevation=345.0,
    creation_date=obspy.UTCDateTime(2016, 1, 2),
    site=Site(name="First station"))

cha = Channel(
    # This is the channel code according to the SEED standard.
    code="HHZ",
    # This is the location code according to the SEED standard.
    location_code="",
    # Note that these coordinates can differ from the station coordinates.
    latitude=1.0,
    longitude=2.0,
    elevation=345.0,
    depth=10.0,
    azimuth=0.0,
    dip=-90.0,
    sample_rate=200)

# By default this accesses the always up-to-date NRL online.
# Offline copies of the NRL can also be used instead.
nrl = NRL()
# The contents of the NRL can be explored interactively in a Python prompt,
# see API documentation of NRL submodule:
# http://docs.obspy.org/packages/obspy.clients.nrl.html
# Here we assume that the end point of data logger and sensor are already
# known:
response = nrl.get_response(
    sensor_keys=['Nanometrics', 'Trillium Compact 120 (Vault, Posthole, OBS)', '1500 V/m/s'],
    datalogger_keys=['REF TEK', 'RT 130 & 130-SMA', '1', '200'])


# Now tie it all together.
cha.response = response
sta.channels.append(cha)
net.stations.append(sta)
inv.networks.append(net)

# And finally write it to a StationXML file. We also force a validation against
# the StationXML schema to ensure it produces a valid StationXML file.
#
# Note that it is also possible to serialize to any of the other inventory
# output formats ObsPy supports.
print(inv)
print(inv[0][0][0])
inv.write("station.xml", format="stationxml", validate=True)
In [ ]:
cha = inv[0][0][0]

cha.plot(min_freq=0.001)
print(cha)

The contents of the NRL can be inspected in an interactive shell:

In [ ]:
nrl.sensors
In [ ]:
nrl.sensors['Streckeisen']
In [ ]:
nrl.dataloggers