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, VTG, HDT, PSXN, PRDID typed decoders), and marlin-ais (Types 1/2/3/5/18/19/24A/24B + reassembly). API surface is feature-complete for v0.1. 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 (once the v0.1.0 wheels publish)
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.0.tar.gz (160.2 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.0-cp39-abi3-win_amd64.whl (337.4 kB view details)

Uploaded CPython 3.9+Windows x86-64

marlin_py-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (416.4 kB view details)

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

marlin_py-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (404.5 kB view details)

Uploaded CPython 3.9+manylinux: glibc 2.17+ ARM64

marlin_py-0.1.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl (763.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.0.tar.gz.

File metadata

  • Download URL: marlin_py-0.1.0.tar.gz
  • Upload date:
  • Size: 160.2 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.0.tar.gz
Algorithm Hash digest
SHA256 c85178574e83f604f775a811a533a3985488f4986b738903cd0970c91c2841c8
MD5 dcd94dec4c01670ea70284faad33059f
BLAKE2b-256 d331c022d36130428ce9bc34d03c8294bfec60ff91314f07defc535cc3a18fa2

See more details on using hashes here.

Provenance

The following attestation bundles were made for marlin_py-0.1.0.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.0-cp39-abi3-win_amd64.whl.

File metadata

  • Download URL: marlin_py-0.1.0-cp39-abi3-win_amd64.whl
  • Upload date:
  • Size: 337.4 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.0-cp39-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 fa6296b6f928b6e061743132faf3f2e74671e9020b6bc9a6f133240520a338d6
MD5 0ca64627fdf644f5cd4d1d64fcb094d2
BLAKE2b-256 5fd448927f2782087afe5ada25ef93b98b9d2f80c4297c350b4f2f305a8a6794

See more details on using hashes here.

Provenance

The following attestation bundles were made for marlin_py-0.1.0-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.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for marlin_py-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 cc0176d3c44bebd2ec090a2f121bf094304bd35169f2d6c35967de34e277b3fb
MD5 f76887eb0380a7dc415afe6877947c5d
BLAKE2b-256 9ebaa7664fe7b4cf2d91f4e1b837d1f23ebe36e659223307c6559781b51ddc85

See more details on using hashes here.

Provenance

The following attestation bundles were made for marlin_py-0.1.0-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.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for marlin_py-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 03ef5aa8566fb76e22bbaf0e587cdd887d141d046f8b7226c2833d11296f3771
MD5 796085e7d8327c25197bee4db55c5496
BLAKE2b-256 569935829c9443300d16d735986ee1aabdd87a6b429550b0cd08b9f85bf95d0e

See more details on using hashes here.

Provenance

The following attestation bundles were made for marlin_py-0.1.0-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.0-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.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
Algorithm Hash digest
SHA256 22fc98f9a098d5f5d377534cf96f5116c092e1aa0659a48f40142f928a42bbe9
MD5 d8aba98532d9aaff117bc7bc7685348f
BLAKE2b-256 aff6f9d12318f482c3bda6a353f91edcbe3e99fc20449646702a67304c6724d9

See more details on using hashes here.

Provenance

The following attestation bundles were made for marlin_py-0.1.0-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