4 releases
| new 0.1.3 | Jan 26, 2026 |
|---|---|
| 0.1.2 | Jan 20, 2026 |
| 0.1.1 | Nov 24, 2025 |
| 0.1.0 | Nov 24, 2025 |
#1303 in Parser implementations
Used in ship162
5MB
9K
SLoC
AIS Message Parser and Decoder
This library provides functionality to:
- demodulate AIS messages from I/Q samples
- parse NMEA AIVDM/AIVDO messages
and to convert them to binary u8 data, then decode them to structured AIS messages.
Feature Flags
rtlsdr: Enables theRtlSdrReceiversource for reading directly from USB RTL-SDR dongles.mqtt: Enables theMqttReceiversource for connecting to AIS data streams over MQTT (e.g. Digitraffic). Disabled by default.
ship162
ship162 is a complete maritime tracking application that includes the rs162 Rust library for decoding AIS (Automatic Identification System) messages from binary feeds and NMEA sentences using the deku library for clean, declarative binary data parsing.
The library takes its inspiration from the Python pyais library and leverages deku to provide efficient, type-safe AIS message decoding. The major specificity compared to other implementations is the deku-based decoder, which enables clean bit-level parsing with compile-time guarantees.
The directions ambitioned by ship162 include:
- providing high-performance AIS decoding in Rust;
- offering efficient multi-receiver AIS decoding;
- serving real-time enriched maritime data to external applications;
- end-to-end demodulation and decoding from SDR hardware to structured data.
The ultimate goal is to create a complete maritime tracking solution that can receive raw radio signals and output structured AIS data, similar to what dump1090 does for aviation ADS-B messages.
Features
- Complete AIS message support: Handles all standard AIS message types (1-27)
- NMEA sentence parsing: Full support for AIVDM/AIVDO messages with multi-fragment assembly
- Type-safe decoding: Leverages Rust's type system and deku for reliable parsing
- JSON serialization: Built-in serde support for easy data export
- Real-time processing: Efficient handling of live AIS data streams
- Multi-fragment messages: Automatic assembly of multi-sentence AIS messages
Optional Features
rtlsdr: Support for real-time demodulation from RTL-SDR USB dongles (enabled viadesperado).mqtt: Support for connecting to MQTT brokers (e.g., Finnish Digitraffic). Disabled by default.
Similar Projects
- AIS-catcher in C++
- pyais in Python
- ais or nmea-parser in Rust
The key differentiator of ship162 is its use of deku for declarative binary parsing, providing both performance and correctness guarantees that are difficult to achieve with manual bit manipulation.
Future Roadmap
The long-term vision for ship162 is to become a complete end-to-end demodulation and decoding application that can:
- Receive raw RF signals from SDR hardware
- Demodulate AIS signals (161.975 MHz and 162.025 MHz)
- Decode NMEA sentences in real-time
- Provide Python and WebAssembly bindings
- Offer a complete maritime tracking solution
This will make ship162 the maritime equivalent of rs1090 for aviation tracking.
Installation
Run the following Cargo command in your project directory:
# Standard installation
cargo add rs162
# With MQTT support
cargo add rs162 --features mqtt
# With RTL-SDR support
cargo add rs162 --features rtlsdr
Or add the following line to your Cargo.toml:
rs162 = "0.1.0" # check for the latest version
Configuration File
The ship162 application supports TOML configuration files for defining multiple AIS data sources. This makes it easy to configure and manage different input sources without using command-line arguments.
Configuration File Locations
The application searches for configuration files in the following locations (in priority order):
- Path specified by the
SHIP162_CONFIGenvironment variable $XDG_CONFIG_HOME/ship162/config.toml~/.config/ship162/config.toml
Configuration Format
# Example configuration with multiple sources
# RTL-SDR device by index
[[sources]]
rtlsdr = { device = 0 }
gain = 49.6 # Recommended max gain for 162 MHz
bias_tee = true # Enable bias-tee if using powered antenna
# RTL-SDR device by serial number
[[sources]]
rtlsdr = { serial = "00000001" }
gain = 49.6
# RTL-SDR device by manufacturer/product filter
[[sources]]
rtlsdr = { manufacturer = "Realtek", product = "RTL2838UHIDIR" }
gain = 49.6
# SoapySDR device (e.g., PlutoSDR, LimeSDR)
[[sources]]
soapy = "driver=rtlsdr"
gain = 49.6
gain_element = "TUNER" # Optional: specify gain element
bias_tee = false
# PlutoSDR by IP address
[[sources]]
pluto = "192.168.2.1"
gain = 73.0 # Maximum gain for PlutoSDR
# TCP source (e.g., Norwegian Coastal Administration)
[[sources]]
tcp = "153.44.253.27:5631"
# TCP source with SSH tunnel (requires --features ssh)
# [[sources]]
# tcp = { host = "remote-ais-server.example.com", port = 5631, jump = "jumphost" }
# MQTT source (requires --features mqtt)
[[sources]]
mqtt = "mqtt://mqtt.digitraffic.fi"
# I/Q sample file
[[sources]]
iqfile = "/path/to/samples.cf32"
Configuration Options
RTL-SDR Sources
RTL-SDR devices can be selected by:
- Device index:
rtlsdr = { device = 0 } - Serial number:
rtlsdr = { serial = "00000001" } - Manufacturer/Product:
rtlsdr = { manufacturer = "Realtek", product = "RTL2838UHIDIR" }
Common options:
gain: Gain in dB (recommended: 49.6 for max gain)bias_tee: Enable/disable bias-tee (default: false)
SoapySDR Sources
SoapySDR sources use a driver string:
soapy = "driver=rtlsdr"for RTL-SDR via SoapySDRsoapy = "driver=plutosdr"for PlutoSDR via SoapySDRsoapy = "driver=lime"for LimeSDR
Common options:
gain: Gain in dBgain_element: Gain element name (default: "TUNER")bias_tee: Enable/disable bias-tee
PlutoSDR Sources
PlutoSDR sources require an IP address or URI:
pluto = "192.168.2.1"orpluto = "ip:192.168.2.1"
Common options:
gain: Gain in dB (recommended: 73.0 for AIS)
TCP Sources
TCP sources connect to a remote AIS feed:
tcp = "host:port"- Simple TCP connectiontcp = { host = "hostname", port = 5631 }- Structured formattcp = { host = "hostname", port = 5631, jump = "jumphost" }- SSH tunneled connection (requires--features ssh)
SSH Tunneling
SSH tunneling allows secure connections to remote AIS sources through jump hosts. This feature requires building with the ssh feature:
cargo build --release --features rtlsdr,ssh
Configuration:
# TCP source with SSH tunnel
[[sources]]
tcp = { host = "remote-ais-server.example.com", port = 5631, jump = "jumphost.example.com" }
SSH Setup:
SSH tunneling uses standard SSH configuration:
-
~/.ssh/config - SSH client configuration:
Host jumphost.example.com User myuser IdentityFile ~/.ssh/id_ed25519 -
~/.ssh/known_hosts - Host verification keys (populated automatically on first connection)
-
~/.ssh/id_* - SSH keys for authentication (generate with
ssh-keygen)
The jump parameter refers to a hostname in your ~/.ssh/config or a direct hostname that can be resolved.
MQTT Sources
MQTT sources connect to an MQTT broker (requires --features mqtt):
mqtt = "mqtt://broker.example.com"
No additional configuration options.
I/Q File Sources
I/Q file sources read from a file containing raw I/Q samples:
iqfile = "/path/to/file.cf32"
No additional configuration options.
Using Configuration Files
Create a configuration file at ~/.config/ship162/config.toml and run:
cargo run --release --features rtlsdr,soapy
Or specify a custom configuration file location:
SHIP162_CONFIG=/path/to/config.toml cargo run --release
See config.toml.example in the repository for a complete example with documentation.
Usage
Basic AIS Message Decoding
use rs162::decode::ais::Message;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Single NMEA sentence
let nmea = "!AIVDM,1,1,,B,15M67FC000G?ufbE`FepT@3n00Sa,0*5C";
let message = Message::from_nmea(&[nmea])?;
// Convert to JSON
let json = serde_json::to_string(&message)?;
println!("{}", json);
Ok(())
}
Multi-fragment Messages
use rs162::decode::ais::Message;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Multi-sentence Type 5 static and voyage data
let sentences = [
"!AIVDM,2,1,1,A,55?MbV02;H;s<HtKR20EHE:0@T4@Dn2222222216L961O5Gf0NSQEp6ClRp8,0*1C",
"!AIVDM,2,2,1,A,88888888880,2*25",
];
let message = Message::from_nmea(&sentences)?;
let json = serde_json::to_string(&message)?;
println!("{}", json);
Ok(())
}
Processing NMEA Files
# See examples/nmea_file.rs for a complete file processor
cargo run --example nmea_file
Real-time TCP Stream Processing
# See examples/nmea_tcp.rs for live AIS data processing
# Connects to Norwegian Coastal Administration's free AIS feed
cargo run --release --example nmea_tcp -- 153.44.253.27:5631 | \
jq -c '.message + .mmsi_info + {timestamp: (.timestamp | strftime("%Y-%m-%dT%H:%M:%SZ"))}'
Demodulating from I/Q Samples
# See examples/iqfile.rs for processing I/Q sample files
cargo run --release --example iqfile | \
jq -c '.message + .mmsi_info + {timestamp: (.timestamp | strftime("%Y-%m-%dT%H:%M:%SZ"))}'
Demodulating from rtl_tcp
# See examples/rtltcp.rs
rtl_tcp -a 127.0.0.1 -p 1234 -f 162M -s 288k -g 49.6
cargo run --release --example rtltcp | \
jq -c '.message + .mmsi_info + {timestamp: (.timestamp | strftime("%Y-%m-%dT%H:%M:%SZ"))}'
Demodulating from RTL-SDR Devices
[!WARNING]
Please read the following important note for Linux users: https://github.com/ccostes/rtl-sdr-rs#uload-kernel-modules
# See examples/rtlsdr.rs
cargo run --release --example rtlsdr | \
jq -c '.message + .mmsi_info + {timestamp: (.timestamp | strftime("%Y-%m-%dT%H:%M:%SZ"))}'
# See examples/rtlsdr_async.rs
cargo run --release --example rtlsdr_async | \
jq -c '.message + .mmsi_info + {timestamp: (.timestamp | strftime("%Y-%m-%dT%H:%M:%SZ"))}'
Technical Standards
The library implements:
- IEC 61162-1: Maritime navigation digital interfaces (NMEA 0183)
- ITU-R M.1371: Technical characteristics for AIS
- IEC 62320-1: AIS transponder equipment standards
Data Sources
Norwegian Coastal Administration
Real-time AIS data is freely available from the Norwegian Coastal Administration:
- Host: 153.44.253.27
- Port: 5631
- Format: IEC 61162-1 (NMEA with timestamps)
- License: Norwegian license for public data
More information: https://www.kystverket.no/en/sea-transport-and-ports/ais/access-to-ais-data/
Finnish Digitraffic MQTT Feed
[!NOTE] You must compile the library or application with
--features mqttto access this source.
Real-time AIS data is available via MQTT from Finnish Digitraffic:
- Broker:
mqtt.digitraffic.fi - Topics:
vessels-v2/\<mmsi\>/metadatavessels-v2/\<mmsi\>/locationsvessels-v2/status
- Format: JSON messages containing AIS data
- License: "Kaikki Digitraffic -palvelun kautta jaettava tieto on koneluettavaa avointa dataa ja on käytettävissä Creative Commons 4.0 Nimeä -käyttöluvalla, mikä mahdollistaa uusien palveluiden ja ohjelmistojen kehittämisen." (machine translation: "All data distributed through the Digitraffic service is machine-readable open data and is available under the Creative Commons 4.0 Attribution license, which enables the development of new services and software.")
More information: https://www.digitraffic.fi/en/marine-traffic/
Agent-based coding
Most of the codebase has been generated with the assistance of Claude Sonnet 4, using specifications from:
- GPSD AIVDM documentation
- Test cases adapted from the pyais library
- ITU-R M.1371 specifications
Contributing
Contributions are welcome! The project particularly benefits from:
- Real-world test cases and data samples
- Performance optimizations
- Documentation improvements
License
This project is licensed under the MIT License. See the license file for details.
Dependencies
~16–28MB
~442K SLoC