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 failureDecodeError— field-level decode failure in a typed NMEA sentenceAisError— AIS armor or bit-level decode failureReassemblyError— 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
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c85178574e83f604f775a811a533a3985488f4986b738903cd0970c91c2841c8
|
|
| MD5 |
dcd94dec4c01670ea70284faad33059f
|
|
| BLAKE2b-256 |
d331c022d36130428ce9bc34d03c8294bfec60ff91314f07defc535cc3a18fa2
|
Provenance
The following attestation bundles were made for marlin_py-0.1.0.tar.gz:
Publisher:
python-bindings.yml on jkr78/marlin
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
marlin_py-0.1.0.tar.gz -
Subject digest:
c85178574e83f604f775a811a533a3985488f4986b738903cd0970c91c2841c8 - Sigstore transparency entry: 1397530208
- Sigstore integration time:
-
Permalink:
jkr78/marlin@df5cb56f11cc465d04fc312fcf989c1d9009b377 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/jkr78
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-bindings.yml@df5cb56f11cc465d04fc312fcf989c1d9009b377 -
Trigger Event:
workflow_dispatch
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fa6296b6f928b6e061743132faf3f2e74671e9020b6bc9a6f133240520a338d6
|
|
| MD5 |
0ca64627fdf644f5cd4d1d64fcb094d2
|
|
| BLAKE2b-256 |
5fd448927f2782087afe5ada25ef93b98b9d2f80c4297c350b4f2f305a8a6794
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
marlin_py-0.1.0-cp39-abi3-win_amd64.whl -
Subject digest:
fa6296b6f928b6e061743132faf3f2e74671e9020b6bc9a6f133240520a338d6 - Sigstore transparency entry: 1397530264
- Sigstore integration time:
-
Permalink:
jkr78/marlin@df5cb56f11cc465d04fc312fcf989c1d9009b377 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/jkr78
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-bindings.yml@df5cb56f11cc465d04fc312fcf989c1d9009b377 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file marlin_py-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: marlin_py-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 416.4 kB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cc0176d3c44bebd2ec090a2f121bf094304bd35169f2d6c35967de34e277b3fb
|
|
| MD5 |
f76887eb0380a7dc415afe6877947c5d
|
|
| BLAKE2b-256 |
9ebaa7664fe7b4cf2d91f4e1b837d1f23ebe36e659223307c6559781b51ddc85
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
marlin_py-0.1.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
cc0176d3c44bebd2ec090a2f121bf094304bd35169f2d6c35967de34e277b3fb - Sigstore transparency entry: 1397530284
- Sigstore integration time:
-
Permalink:
jkr78/marlin@df5cb56f11cc465d04fc312fcf989c1d9009b377 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/jkr78
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-bindings.yml@df5cb56f11cc465d04fc312fcf989c1d9009b377 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file marlin_py-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: marlin_py-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 404.5 kB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
03ef5aa8566fb76e22bbaf0e587cdd887d141d046f8b7226c2833d11296f3771
|
|
| MD5 |
796085e7d8327c25197bee4db55c5496
|
|
| BLAKE2b-256 |
569935829c9443300d16d735986ee1aabdd87a6b429550b0cd08b9f85bf95d0e
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
marlin_py-0.1.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
03ef5aa8566fb76e22bbaf0e587cdd887d141d046f8b7226c2833d11296f3771 - Sigstore transparency entry: 1397530248
- Sigstore integration time:
-
Permalink:
jkr78/marlin@df5cb56f11cc465d04fc312fcf989c1d9009b377 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/jkr78
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-bindings.yml@df5cb56f11cc465d04fc312fcf989c1d9009b377 -
Trigger Event:
workflow_dispatch
-
Statement type:
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
- Download URL: marlin_py-0.1.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl
- Upload date:
- Size: 763.0 kB
- Tags: CPython 3.9+, macOS 10.12+ universal2 (ARM64, x86-64), macOS 10.12+ x86-64, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
22fc98f9a098d5f5d377534cf96f5116c092e1aa0659a48f40142f928a42bbe9
|
|
| MD5 |
d8aba98532d9aaff117bc7bc7685348f
|
|
| BLAKE2b-256 |
aff6f9d12318f482c3bda6a353f91edcbe3e99fc20449646702a67304c6724d9
|
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
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
marlin_py-0.1.0-cp39-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl -
Subject digest:
22fc98f9a098d5f5d377534cf96f5116c092e1aa0659a48f40142f928a42bbe9 - Sigstore transparency entry: 1397530221
- Sigstore integration time:
-
Permalink:
jkr78/marlin@df5cb56f11cc465d04fc312fcf989c1d9009b377 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/jkr78
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-bindings.yml@df5cb56f11cc465d04fc312fcf989c1d9009b377 -
Trigger Event:
workflow_dispatch
-
Statement type: