sudspy is a lightweight Python package for reading and converting PC‑SUDS / SRC SUDS seismic data files into ObsPy objects (Streams, Picks, and Inventory metadata).
SUDS (Seismic Unified Data System) files, as produced by SRC instuments (e.g. EchoPro / Gecko / Waves), are self‑describing binary streams made up of a sequence of tagged blocks.
Each block has:
- a struct tag (type + sizes),
- a struct body (metadata),
- optionally a data payload (e.g. waveform samples).
Important properties:
- SUDS files are appendable → simple UNIX concatenation (
cat *.dmx > combined.sud) is valid. - There is no global header.
- Context is defined by ordering (e.g. FEATURE follows DESCRIPTRACE).
- There is no true location code concept in SUDS.
Because SUDS is a linear tagged format:
- Each block is self‑contained.
- Block sizes are explicit.
As a result, concatenating files works without rewriting metadata.
sudspy is intentionally layered:
- Read binary SUDS files.
- Yield
SudsBlockobjects with:struct_typestruct_body- raw
data - file offset
No interpretation beyond parsing.
- Decode specific SUDS structs (STATIONCOMP, DESCRIPTRACE, FEATURE, INSTRUMENT, COMMENT).
- Collect related metadata using ordering rules, not assumptions.
Examples:
collect_instruments()collect_comments()collect_stations()
Still no ObsPy objects here.
High‑level convenience functions that convert SUDS → ObsPy:
- Waveforms
read_suds_stream()→Stream
- Picks
read_suds_picks()→ list ofPick
- Metadata
read_suds_inv()→Inventory
This is the only layer that depends on ObsPy.
SUDS has no native location code.
sudspy handles this consistently:
- All internal processing uses
(NET, STA, CHAN)only. - Location codes are injected once, at the ObsPy boundary:
- either as
""(empty), - or via
default_location="00", - or via a user‑supplied mapping.
- either as
This avoids ambiguity and metadata drift.
import sudspy
st = sudspy.read_suds_stream(
"data/day01.sud",
default_location="00"
)
print(st)picks = sudspy.read_suds_picks(
"data/day01.sud",
location_code="00"
)
print(len(picks), "picks")inv = sudspy.read_suds_inv(
"data/day01.sud",
default_location="00"
)
print(inv)st.remove_response(
inventory=inv,
output="VEL",
zero_mean=True,
taper=True
)from sudspy import fast_merge
from obspy import Stream
merged = Stream()
for ch in sorted({tr.stats.channel for tr in st}):
traces = [tr for tr in st if tr.stats.channel == ch]
merged += fast_merge(traces, gap_fill="nan")