This is the beginning of a parser for AIS messages, written in Rust.
API documentation is available at https://docs.rs/ais
If you just want to take a bunch of AIS NMEA sentences and see what they mean, you can try running this as a (very rough) command-line utility.
If your NMEA source is sending UDP packets to port 4722, for example:
nc -u -l 4722 | cargo run
You should start seeing messages stream in:
AidToNavigationReport(AidToNavigationReport { message_type: 21, repeat_indicator: 1, mmsi: 993692016, aid_type: Some(ReferencePoint), name: "6W", accuracy: Unaugmented, longitude: Some(-122.80445), latitude: Some(37.705833), dimension_to_bow: 0, dimension_to_stern: 0, dimension_to_port: 0, dimension_to_starboard: 0, epfd_type: Some(Surveyed), utc_second: 61, off_position: false, regional_reserved: 0, raim: false, virtual_aid: false, assigned_mode: false })
BaseStationReport(BaseStationReport { message_type: 4, repeat_indicator: 0, mmsi: 3669710, year: Some(2020), month: Some(4), day: Some(18), hour: Some(8), minute: Some(46), second: Some(40), fix_quality: DGPS, longitude: Some(-122.42347), latitude: Some(37.96206), epfd_type: None, raim: true, radio_status: Sotdma(SotdmaMessage { sync_state: UtcDirect, slot_timeout: 0, sub_message: SlotOffset(2250) }) })
PositionReport(PositionReport { message_type: 1, repeat_indicator: 0, mmsi: 367625810, navigation_status: Some(UnderWayUsingEngine), rate_of_turn: Some(RateOfTurn { raw: 0 }), speed_over_ground: Some(0.1), position_accuracy: DGPS, longitude: Some(-122.398), latitude: Some(37.80256), course_over_ground: Some(343.8), true_heading: Some(55), timestamp: 41, maneuver_indicator: None, raim: false, radio_status: Sotdma(SotdmaMessage { sync_state: UtcDirect, slot_timeout: 2, sub_message: SlotNumber(1524) }) })
BaseStationReport(BaseStationReport { message_type: 4, repeat_indicator: 0, mmsi: 3669145, year: Some(2020), month: Some(4), day: Some(18), hour: Some(8), minute: Some(46), second: Some(41), fix_quality: DGPS, longitude: Some(-122.46484), latitude: Some(37.794273), epfd_type: None, raim: true, radio_status: Sotdma(SotdmaMessage { sync_state: UtcDirect, slot_timeout: 3, sub_message: ReceivedStations(187) }) })
Here's an example that parses a single NMEA sentence. In this case, it contains an Aid to Navigation Report:
use ais::{AisFragments, AisParser};
use ais::messages::AisMessage;
// The line below is an NMEA sentence, much as you'd see coming out of an AIS decoder.
let line = b"!AIVDM,1,1,,B,E>kb9O9aS@7PUh10dh19@;0Tah2cWrfP:l?M`00003vP100,0*01";
let mut parser = AisParser::new();
if let AisFragments::Complete(sentence) = parser.parse(line, true)? {
// This sentence is complete, ie unfragmented
assert_eq!(sentence.num_fragments, 1);
// The data was transmitted on AIS channel B
assert_eq!(sentence.channel, Some('B'));
if let Some(message) = sentence.message {
match message {
AisMessage::AidToNavigationReport(report) => {
assert_eq!(report.mmsi, 993692028);
assert_eq!(report.name, "SF OAK BAY BR VAIS E");
// There are a ton more fields available here
},
_ => panic!("Unexpected message type"),
}
}
}
# Ok::<(), ais::errors::Error>(())
Right now, only a few common types are supported. They are:
- Position Report (types 1-3)
- Base Station Report (type 4)
- Static and Voyage Related Data (type 5)
- Binary Broadcast Message (type 8)
- UTC/Date Response (type 11)
- Interrogation (type 15)
- DGNSS Broadcast Binary Message (type 17)
- Static Class B Position Report (type 18)
- Extended Class B Position Report (type 19)
- Data Link Management Message (type 20)
- Aid to Navigation Report (type 21)
- Static Data Report (type 24)
Others to come soon, I hope!
This library has experimental support for no_std
; add the library like this to your Cargo.toml
:
ais = { version = "0.9", default-features = false }
Some AIS messages have variable length fields; this is accomplished using heapless
Vec
and String
types, allocating the maximum possible data size on the stack.
If you have access to an allocator, opting into the alloc
feature may be more space efficient, as observed messages sizes are often much smaller than the maximum possible.
The std
feature is the default, and it is more or less identical to the alloc
version; it's required to build the binary CLI tool.