stationxml-rs 0.2.1

Pure Rust FDSN StationXML and SeisComP SC3ML reader/writer with format-agnostic inventory model
Documentation

stationxml-rs

Pure Rust FDSN StationXML and SeisComP SC3ML reader/writer. Zero unsafe, zero C dependencies.

Crates.io docs.rs CI License Rust

What is StationXML?

FDSN StationXML is the standard XML format for seismic station metadata, used by earthquake monitoring networks worldwide (IRIS, FDSN, BMKG, etc.). It describes station locations, instrument responses, poles & zeros, and data logger configurations.

SeisComP SC3ML is an alternative XML format used by the SeisComP earthquake monitoring system. It stores the same information in a flat, reference-based structure rather than FDSN's nested hierarchy.

This crate provides a format-agnostic inventory model with pluggable backends for both formats, enabling read, write, and cross-format conversion.

Features

  • FDSN StationXML 1.2 read and write
  • SeisComP SC3ML 0.6--0.13 read and write
  • Cross-format conversion: FDSN <-> SC3ML via shared inventory model
  • Auto-detect format from root XML element
  • Full instrument response: poles & zeros, FIR coefficients, stage gains, sensitivity
  • Builder pattern for constructing inventories programmatically
  • Sensor library: embedded database of common seismometers (GS-11D, Trillium, etc.)
  • ADC conversion helpers: counts <-> voltage <-> physical units
  • Zero unsafe -- no FFI, no transmute, no raw pointers
  • Zero C dependencies -- pure Rust, compiles anywhere rustc runs

Quick Start

Add to your Cargo.toml:

[dependencies]
stationxml-rs = "0.2"

Read with auto-detection

use stationxml_rs::read_from_file;

// Auto-detects FDSN or SC3ML from the XML content
let inv = read_from_file("station.xml").unwrap();

for net in &inv.networks {
    for sta in &net.stations {
        println!("{}.{} ({}, {})", net.code, sta.code, sta.latitude, sta.longitude);
        for ch in &sta.channels {
            println!("  {}.{} {} Hz", ch.code, ch.location_code, ch.sample_rate);
        }
    }
}

Read and write with explicit format

use stationxml_rs::{Fdsn, Sc3ml, StationXmlFormat};

// Read FDSN StationXML
let inv = Fdsn::read_from_str("<FDSNStationXML ...>...</FDSNStationXML>").unwrap();

// Write as SC3ML (cross-format conversion)
let sc3ml_xml = Sc3ml::write_to_string(&inv).unwrap();

Build an inventory from scratch

use stationxml_rs::{InventoryBuilder, write_to_string, Fdsn};

let inv = InventoryBuilder::new("MyOrg")
    .network("IU", |net| {
        net.description("Global Seismograph Network")
           .station("ANMO", -34.9462, 138.5866, 48.0, |sta| {
               sta.description("Albuquerque, New Mexico")
                  .channel("BHZ", "00", |ch| {
                      ch.sample_rate(20.0)
                        .azimuth(0.0)
                        .dip(-90.0)
                  })
           })
    })
    .build();

let xml = write_to_string::<Fdsn>(&inv).unwrap();

API Overview

use stationxml_rs::{
    // Top-level functions
    read_from_file, read_from_str,      // auto-detect format
    read_from_file_as, write_to_file,   // explicit format
    write_to_string, detect_format,     // utility

    // Format backends
    Fdsn, Sc3ml, StationXmlFormat,      // format markers + trait
    Format,                             // enum: Fdsn | Sc3ml

    // Core inventory types
    Inventory, Network, Station, Channel,
    Response, ResponseStage,
    PolesZeros, Coefficients, FIR,
    Equipment, Site,

    // Builder
    InventoryBuilder,

    // Helpers
    AdcConversion,                      // counts <-> physical units
    SensorEntry, find_sensor,           // sensor database
    load_sensor_library,

    // Error handling
    StationXmlError, Result,
};
Type Description
Inventory Top-level container for all station metadata
Network Seismic network (e.g. "IU", "GE") with stations
Station Station with location, site info, and channels
Channel Channel with orientation, sample rate, and response
Response Instrument response with sensitivity and stage chain
ResponseStage Single response stage (poles/zeros, coefficients, or FIR)
PolesZeros Analog/digital transfer function (Laplace or Z-transform)
FIR Finite Impulse Response filter coefficients
Coefficients Gain-only stage (e.g. ADC stage)
Equipment Sensor or data logger description
Fdsn FDSN StationXML 1.2 format backend
Sc3ml SeisComP SC3ML 0.6--0.13 format backend
InventoryBuilder Fluent builder for constructing inventories
AdcConversion ADC conversion helpers (counts, voltage, physical)

Supported Formats

Format Namespace Read Write
FDSN StationXML 1.2 http://www.fdsn.org/xml/station/1 Yes Yes
SeisComP SC3ML 0.6--0.13 http://geofon.gfz-potsdam.de/ns/seiscomp3-schema/* Yes Yes

SC3ML Reference Resolution

SC3ML uses a flat structure with publicID references. The reader automatically resolves:

<Inventory>
  <sensor publicID="Sensor#GS11D" response="ResponsePAZ#GS11D">  -->  Channel.sensor
  <datalogger publicID="Datalogger#PB24">                        -->  Channel.data_logger
  <responsePAZ publicID="ResponsePAZ#GS11D">                     -->  Response stage 1
  <responseFIR publicID="ResponseFIR#DECIM2">                     -->  Response FIR stage
  <network>
    <station>
      <sensorLocation>
        <stream sensor="Sensor#GS11D" datalogger="Datalogger#PB24">  (references above)

The writer reverses this: deduplicating shared sensors/responses into top-level definitions.

Architecture

src/
  lib.rs           -- crate root, re-exports, top-level read/write functions
  inventory.rs     -- core types: Inventory, Network, Station, Channel, Response
  format.rs        -- StationXmlFormat trait, Format enum, detect_format()
  error.rs         -- StationXmlError enum (thiserror)
  datetime.rs      -- shared ISO 8601 datetime parsing/formatting
  builder.rs       -- InventoryBuilder fluent API
  sensor.rs        -- embedded sensor database (GS-11D, Trillium, etc.)
  conversion.rs    -- ADC conversion helpers
  fdsn/
    mod.rs         -- pub struct Fdsn; impl StationXmlFormat
    types.rs       -- FDSN-specific XML serde structs
    reader.rs      -- FDSN XML -> Inventory
    writer.rs      -- Inventory -> FDSN XML
  sc3ml/
    mod.rs         -- pub struct Sc3ml; impl StationXmlFormat
    types.rs       -- SC3ML-specific XML serde structs
    reader.rs      -- SC3ML XML -> Inventory (reference resolution)
    writer.rs      -- Inventory -> SC3ML XML (deduplication)

Design Decisions

  • Format-agnostic core: Inventory types don't depend on any XML format
  • Trait-based backends: StationXmlFormat trait for each format (Fdsn, Sc3ml)
  • Auto-detect: detect_format() inspects root XML element name
  • Roundtrip-safe: read -> write -> read produces identical Inventory
  • ObsPy-compatible: output XML is parseable by obspy.read_inventory()
  • Embedded sensor DB: data/sensors.json compiled into binary via include_str!

TDD with ObsPy

Test vectors are generated by Python/ObsPy scripts, ensuring compatibility with the de facto reference implementation:

cd pyscripts && uv run python -m pyscripts.generate_vectors
cargo test

Development

cargo build                     # build
cargo test                      # all tests (109 tests)
cargo clippy -- -D warnings     # lint (strict)
cargo fmt -- --check            # format check
cargo doc --no-deps --open      # browse docs locally

References

Sister Projects

License

Apache-2.0