Skip to main content

Sans-I/O NMEA 0183 + AIS (AIVDM/AIVDO) parser, Rust-backed

Project description

marlin-py

Python bindings for the Marlin Rust suite — NMEA 0183 envelope parsing, typed sentence decoding, and AIS (AIVDM/AIVDO) message decoding with multi-sentence reassembly.

Status

Wraps three Rust crates: marlin-nmea-envelope (framing + checksum), marlin-nmea-0183 (GGA, GLL, HDT, RMC, VTG, PSXN, PRDID typed decoders), and marlin-ais (Types 1/2/3/5/18/19/24A/24B + reassembly). py.typed marker and .pyi stubs ship with the package; mypy --strict is clean across the entire python/marlin/, tests/, and examples/ tree.

Install

The PyPI distribution name is marlin-py; the Python import name stays plain marlin.

# From PyPI — wheels for Linux x86_64 / aarch64, macOS universal2,
# Windows x86_64. abi3 wheel covers Python 3.9 through 3.13+.
pip install marlin-py

# Local development — build the extension in-place
cd bindings/python
maturin develop

Quickstart: envelope

from marlin.envelope import StreamingParser

parser = StreamingParser()
parser.feed(b"$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47\r\n")
for sentence in parser:
    talker = sentence.talker.decode() if sentence.talker else ""
    print(talker, sentence.sentence_type, sentence.checksum_ok)
    # GP GGA True

OneShotParser handles UDP datagrams (no \r\n required). parse() is a one-call convenience for a single complete sentence.

Quickstart: NMEA typed decode

from marlin.envelope import parse
from marlin.nmea import Nmea0183Parser, decode_gga, DecodeOptions, PrdidDialect

# Single-sentence convenience.
raw = parse(b"$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47")  # raises EnvelopeError on framing/checksum failure
gga = decode_gga(raw)                # raises DecodeError if not GGA or fields are malformed
print(gga.latitude_deg, gga.longitude_deg, gga.fix_quality)

# Streaming parser — same feed/iterate API as the envelope layer.
parser = Nmea0183Parser.streaming()
parser.feed(b"$GPGGA,123519,4807.038,N,01131.000,E,1,08,0.9,545.4,M,46.9,M,,*47\r\n")
for msg in parser:
    print(type(msg).__name__, msg)

DecodeOptions configures dialect-sensitive sentences:

opts = DecodeOptions().with_prdid_dialect(PrdidDialect.PITCH_ROLL_HEADING)
parser = Nmea0183Parser.streaming(options=opts)

Per-sentence extension functions (decode_gga, decode_vtg, decode_hdt, decode_psxn, decode_prdid) are public so downstream code can build its own message enum and delegate to Marlin for the standard types.

Quickstart: AIS

from marlin.ais import AisParser, PositionReportA

parser = AisParser.streaming()
parser.feed(b"!AIVDM,1,1,,A,13aGmP0P00PD;88MD5MTDww@2<0L,0*23\r\n")
for msg in parser:
    if isinstance(msg.body, PositionReportA):
        print(msg.body.mmsi, msg.body.latitude_deg, msg.body.longitude_deg)

Multi-sentence reassembly is automatic — feed fragments in order and the parser yields a complete AisMessage only when the last fragment arrives.

AIS clock modes

Fragment reassembly has three timeout behaviours, selected at construction.

No timeout — fragments are buffered until the 16-slot eviction cap is reached. The oldest incomplete group is discarded when a 17th arrives. No clock reads occur:

parser = AisParser.streaming()          # timeout_ms omitted → no timeout

Auto clock — the binding calls time.monotonic_ns() internally to drive the timeout. Use this when system time is reliable and you do not need deterministic replay:

parser = AisParser.streaming(timeout_ms=60_000)          # clock="auto" implied
# or explicitly:
parser = AisParser.streaming(timeout_ms=60_000, clock="auto")

Manual clock — the caller drives the clock via tick(now_ms=...). No clock reads occur inside the binding. This is the correct mode for unit tests, simulators, and any environment where time is patched:

parser = AisParser.streaming(timeout_ms=60_000, clock="manual")

parser.feed(fragment_bytes)
parser.tick(now_ms=current_time_ms)     # expire stale groups at this instant
for msg in parser:
    ...

Calling tick() on a parser that wasn't built with clock="manual" raises ValueError — including parsers built with no timeout_ms (which default to clock="auto").

Errors

Every exception is a MarlinError subclass (importable from marlin):

  • EnvelopeError — framing or checksum failure
  • DecodeError — field-level decode failure in a typed NMEA sentence
  • AisError — AIS armor or bit-level decode failure
  • ReassemblyError — fragment reassembly violation (out-of-order, channel mismatch, or timeout eviction)

Property tests (via Hypothesis) verify panic-freedom on arbitrary byte inputs — no input can cause the binding to crash the interpreter.

Examples

Six runnable scripts live in bindings/python/examples/:

Script What it shows
one_shot_no_crlf.py Single-datagram framing without \r\n (OneShotParser)
parse_log_file.py Disk-backed .nmea log replay (StreamingParser)
streaming_tcp_style.py TCP-style chunked feed; sentences straddle feed() boundaries
decode_aivdm_log.py Multi-fragment AIS reassembly: Type 1 + Type 5
parse_stdin.py Stdin pipe reader for live NMEA/AIS capture
live_ais_dashboard.py Live ship tracker: envelope + AIS in one script, periodic refresh

Type stubs

The py.typed marker is included in the package. Every public class, function, and constant has a .pyi stub. Downstream projects that run mypy --strict get full type-check coverage without extra configuration.

Rust crates

The Python bindings wrap three Rust crates: marlin-nmea-envelope, marlin-nmea-0183, marlin-ais.

MSRV / Python version

Rust 1.82. Python 3.9+.

License

Dual-licensed under MIT OR Apache-2.0.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

marlin_py-0.1.1.tar.gz (171.5 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

marlin_py-0.1.1-cp39-abi3-win_amd64.whl (351.6 kB view details)

Uploaded CPython 3.9+Windows x86-64

marlin_py-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (430.1 kB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ x86-64

marlin_py-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (419.1 kB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ ARM64

marlin_py-0.1.1-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl (791.0 kB view details)

Uploaded CPython 3.9+macOS 10.12+ universal2 (ARM64, x86-64)macOS 10.12+ x86-64macOS 11.0+ ARM64

File details

Details for the file marlin_py-0.1.1.tar.gz.

File metadata

  • Download URL: marlin_py-0.1.1.tar.gz
  • Upload date:
  • Size: 171.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for marlin_py-0.1.1.tar.gz
Algorithm Hash digest
SHA256 7945c512cad5209f354a6f5c7ca2c483aef49e48213b47a11d87aeca4a4fba72
MD5 732d9890bb17d103be312971895dc4c9
BLAKE2b-256 b2a4abaa500f35e9f4489e1e37b794339bdf0336e60aa8942b8e52479d326834

See more details on using hashes here.

Provenance

The following attestation bundles were made for marlin_py-0.1.1.tar.gz:

Publisher: python-bindings.yml on jkr78/marlin

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file marlin_py-0.1.1-cp39-abi3-win_amd64.whl.

File metadata

  • Download URL: marlin_py-0.1.1-cp39-abi3-win_amd64.whl
  • Upload date:
  • Size: 351.6 kB
  • Tags: CPython 3.9+, Windows x86-64
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for marlin_py-0.1.1-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 1c3c157d9ef841c3ecd088445a2d74aa81eeae520a5fe523b0dfc9a11c7e6084
MD5 066b13899eaf88203b83a394750b85f0
BLAKE2b-256 064252b94f42b8b05d8e71395898de5f2100e7367d000f80111f7f26a2ec9215

See more details on using hashes here.

Provenance

The following attestation bundles were made for marlin_py-0.1.1-cp39-abi3-win_amd64.whl:

Publisher: python-bindings.yml on jkr78/marlin

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file marlin_py-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for marlin_py-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 0a7d686edf8749f09c20e5d6db8dc6c6c2a38b29162363a455610a3578669d4a
MD5 79a778eb0213b01ffba3dd48267ac077
BLAKE2b-256 7ca495c93de89efd3756ace1ca5dc4105fd240f99b4bb9bd059a92c228174477

See more details on using hashes here.

Provenance

The following attestation bundles were made for marlin_py-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:

Publisher: python-bindings.yml on jkr78/marlin

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file marlin_py-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for marlin_py-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 a8af68120e8008cdb2f27328da06b288fd8b657a9a8a01853aac04442faaf4d5
MD5 cca398de1bc948cf4a96861084636065
BLAKE2b-256 b845bec92c86c192a52a1e6bb753e90eafc46cfdf7f33c22b92354737b8f8d57

See more details on using hashes here.

Provenance

The following attestation bundles were made for marlin_py-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:

Publisher: python-bindings.yml on jkr78/marlin

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file marlin_py-0.1.1-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl.

File metadata

File hashes

Hashes for marlin_py-0.1.1-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Algorithm Hash digest
SHA256 aa9772d27538ae7f4ea5dd14289e394c9031c1e0feb83ad7a4ac291e08e296ab
MD5 d423520d1094a2806483aadbbd664199
BLAKE2b-256 22f06568b4987607a28d11f532c5d1310eb5f42f848640e93e2554f5f6127b98

See more details on using hashes here.

Provenance

The following attestation bundles were made for marlin_py-0.1.1-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl:

Publisher: python-bindings.yml on jkr78/marlin

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page