Metadata-Version: 2.4
Name: pymseed
Version: 1.0.0
Summary: A Python package for reading and writing miniSEED formatted data
Author-email: EarthScope Data Services <software@earthscope.org>
License-Expression: Apache-2.0
Project-URL: Homepage, https://earthscope.github.io/pymseed
Project-URL: Repository, https://github.com/EarthScope/pymseed
Project-URL: Issues, https://github.com/EarthScope/pymseed/issues
Project-URL: Documentation, https://earthscope.github.io/pymseed
Keywords: seismology,miniseed,mseed,data,waveform,seismic
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Science/Research
Classifier: Topic :: Scientific/Engineering
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: POSIX :: Linux
Classifier: Operating System :: POSIX :: Other
Classifier: Operating System :: Microsoft :: Windows
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cffi>=2.0.0
Requires-Dist: orjson>=3.11.7; implementation_name != "pypy"
Provides-Extra: numpy
Requires-Dist: numpy>=1.24.0; extra == "numpy"
Provides-Extra: jsonschema
Requires-Dist: jsonschema-rs>=0.35.0; extra == "jsonschema"
Provides-Extra: test
Requires-Dist: pytest>=7.0.0; extra == "test"
Requires-Dist: pytest-cov>=4.0.0; extra == "test"
Requires-Dist: pytest-markdown-docs>=0.9.0; extra == "test"
Requires-Dist: pymseed[numpy]; extra == "test"
Requires-Dist: pymseed[jsonschema]; extra == "test"
Provides-Extra: dev
Requires-Dist: pymseed[test]; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Requires-Dist: mypy>=1.0.0; extra == "dev"
Requires-Dist: pre-commit>=3.0.0; extra == "dev"
Provides-Extra: docs
Requires-Dist: sphinx>=9.0.0; extra == "docs"
Requires-Dist: myst-parser>=5.0.0; extra == "docs"
Requires-Dist: furo; extra == "docs"
Requires-Dist: sphinx-copybutton; extra == "docs"
Requires-Dist: pymseed[numpy]; extra == "docs"
Requires-Dist: pymseed[jsonschema]; extra == "docs"
Provides-Extra: release
Requires-Dist: build>=0.10.0; extra == "release"
Requires-Dist: twine>=4.0.0; extra == "release"
Provides-Extra: all
Requires-Dist: pymseed[dev,docs]; extra == "all"
Dynamic: license-file

# pymseed - miniSEED for Python

[![Python versions](https://img.shields.io/pypi/pyversions/pymseed)](https://pypi.org/project/pymseed/)
[![PyPI](https://img.shields.io/pypi/v/pymseed)](https://pypi.org/project/pymseed/)
[![conda-forge](https://img.shields.io/conda/vn/conda-forge/pymseed)](https://anaconda.org/conda-forge/pymseed)
[![Documentation](https://img.shields.io/badge/docs-earthscope.github.io/pymseed-blue)](https://earthscope.github.io/pymseed/)
[![Tests](https://github.com/EarthScope/pymseed/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/EarthScope/pymseed/actions/workflows/test.yml)

The pymseed package supports reading and writing of miniSEED formatted data.
Both [miniSEED version 2](https://fdsn.org/pdf/SEEDManual_V2.4.pdf)
(defined in the SEED standard) and [miniSEED version 3](https://docs.fdsn.org/projects/miniseed3)
are supported.

The package uses the C-language [libmseed](https://earthscope.github.io/libmseed)
for most of the data format and manipulation work.

## Installation

Install from [PyPI](https://pypi.org/project/pymseed/):

```bash
pip install pymseed
```

Or from [conda-forge](https://anaconda.org/conda-forge/pymseed):

```bash
conda install -c conda-forge pymseed
```

## Example usage

See the [full documentation](https://earthscope.github.io/pymseed/) for a
tutorial and complete API reference. Working programs for a variety of use
cases can be found in the
[examples](https://github.com/EarthScope/pymseed/tree/main/examples) directory of the repository.

Read a file and print details from each record. Each `msr` is only valid
during its own iteration, since the reader reuses it for the next record;
using it afterwards raises `ValueError`:
```python
from pymseed import MS3Record, TimeFormat

input_file = "examples/example_data.mseed"

for msr in MS3Record.from_file(input_file):
    # Print values directly
    print(f'   SourceID: {msr.sourceid}, record length {msr.reclen}')
    print(f' Start Time: {msr.starttime_str(timeformat=TimeFormat.ISOMONTHDAY_SPACE_Z)}')
    print(f'    Samples: {msr.samplecnt}')

    # Alternatively, use the library print function (print to console)
    msr.print()
```

Read a file into a trace list and print the list:
```python
from pymseed import MS3TraceList

traces = MS3TraceList.from_file("examples/example_data.mseed")

# Print the trace list using the library print function
traces.print(details=1, gaps=True)

# Alternatively, traverse the data structures and print each trace ID and segment
for traceid in traces:
    print(traceid)

    for segment in traceid:
        print('  ', segment)
```

Simple example of writing multiple channels of data:
```python
import math
from pymseed import MS3TraceList, timestr2nstime

# Generate sinusoid data, starting at 0, 45, and 90 degrees
data0 = list(map(lambda x: int(math.sin(math.radians(x)) * 500), range(0, 500)))
data1 = list(map(lambda x: int(math.sin(math.radians(x)) * 500), range(45, 500 + 45)))
data2 = list(map(lambda x: int(math.sin(math.radians(x)) * 500), range(90, 500 + 90)))

traces = MS3TraceList()

output_file = "output.mseed"
sample_rate = 40.0
starttime = timestr2nstime("2024-01-01T15:13:55.123456789Z")
format_version = 2
max_record_length = 512

# Add generated data to the trace list
traces.add_data(sourceid="FDSN:XX_TEST__B_S_1",
                data_samples=data0, sample_type='i',
                sample_rate=sample_rate, starttime=starttime)

traces.add_data(sourceid="FDSN:XX_TEST__B_S_2",
                data_samples=data1, sample_type='i',
                sample_rate=sample_rate, starttime=starttime)

traces.add_data(sourceid="FDSN:XX_TEST__B_S_3",
                data_samples=data2, sample_type='i',
                sample_rate=sample_rate, starttime=starttime)

traces.to_file(output_file,
               format_version=format_version,
               max_record_length=max_record_length)
```

### Converting between Source IDs and NSLC codes

miniSEED 3 and FDSN [Source Identifiers](https://docs.fdsn.org/projects/source-identifiers)
use a single string (for example `FDSN:IU_COLA_00_B_H_Z`) to identify a unique
time series channel. Classic SEED-style identifiers split the same information
into network, station, location, and channel (NSLC) codes.  Source IDs are a
superset of SEED v2 codes: all SEED codes can be represented as Source IDs,
but not all Source IDs will fit into SEED codes.

The utility methods `nslc2sourceid()` and `sourceid2nslc()` support mapping
between these identifier systems:

```python
from pymseed import nslc2sourceid, sourceid2nslc

# NSLC (four strings) → FDSN source ID
sid = nslc2sourceid("IU", "COLA", "00", "BHZ")
# 'FDSN:IU_COLA_00_B_H_Z'

# Blank location codes are represented as an empty string
sid2 = nslc2sourceid("XX", "TEST", "", "BHZ")
# 'FDSN:XX_TEST__B_H_Z'

# Source ID → (network, station, location, channel)
net, sta, loc, chan = sourceid2nslc("FDSN:IU_COLA_00_B_H_Z")
assert (net, sta, loc, chan) == ("IU", "COLA", "00", "BHZ")

# To accommodate practical identifier conversion `sourceid2nslc()` does not
# strictly require field lengths for SEED v2 conformance, instead converting
# fields to their most SEED-like form.  For example single character
# Source ID fields of band, source and subsource are collapsed to a SEED
# channel (B_H_Z -> BHZ); but if the codes cannot form a SEED channel
# they are left in the "extended channel" form of Source IDs.  Furthermore,
# larger-than-SEED network, station, and location codes are not truncated
# to fit SEED v2 fields.  For example:

nslc = sourceid2nslc("FDSN:NETWORK_STATION_LOCATION_G_SR_1")
assert nslc == ('NETWORK', 'STATION', 'LOCATION', 'G_SR_1')
```

Invalid source IDs raise `ValueError` from `sourceid2nslc()`; invalid NSLC codes
raise `ValueError` from `nslc2sourceid()`.

## Threaded usage

The pymseed package is safe to use with threads as long as the threads
are not sharing data structures, e.g. a `MS3TraceList`.

The underlying libmseed library uses thread-local storage for logging, so
each thread has its own message registry. This is set up automatically the
first time a thread makes a pymseed call, so no per-thread initialization
is required:

```python
from concurrent.futures import ThreadPoolExecutor
from pymseed import MS3TraceList

def process_file(filename):
    traces = MS3TraceList.from_file(filename)
    # ... process traces ...
    return len(traces)

# A list of files to process (example)
file_list = ["examples/example_data.mseed",
             "examples/example_data.mseed"]

with ThreadPoolExecutor(max_workers=4) as executor:
    results = executor.map(process_file, file_list)
```

Call `configure_logging()` explicitly only to change the log/error prefixes
or the maximum stored message count for the calling thread. A thread that
never calls it uses the settings from the most recent `configure_logging()`
call made anywhere in the process.

## Contributing

Contributions are welcome. Please submit pull requests against the `develop`
branch. See [development.md](development.md) for details on setting up a
development environment and running tests.

## Package design rationale

The package functionality and exposed API are designed to support the most
common use cases of reading and writing miniSEED data using `libmseed`.
The naming of functions, classes, arguments, etc. often follows the naming
used in the library in order to reference their fundamentals at the C level
if needed; even though this leaves some names distinctly non-Pythonic.

## License

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

[http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0)

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Copyright (C) 2026 EarthScope Data Services
