Skip to content

Commit

Permalink
Added support for message type 19
Browse files Browse the repository at this point in the history
  • Loading branch information
squidpickles committed Nov 14, 2021
1 parent cd0cd1c commit 0fa5300
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 19 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.8.0] - 2021-11-13
### Added
- Support for message type 19 (Extended Class B Position Report)
### Changed
- Edition 2021
- Updated Nom dependency to v7
- `AssignedMode` moved into `messages.types`

## [0.7.0] - 2021-03-11
### Added
- Support for message type 17 (DGNSS Broadcast Binary Message).
Expand Down
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "ais"
version = "0.7.0"
version = "0.8.0"
authors = ["Kevin Rauwolf <[email protected]>"]
description = "An AIS parser library"
homepage = "https://github.com/squidpickles/ais"
Expand All @@ -10,11 +10,11 @@ readme = "README.md"
keywords = ["ais", "nmea"]
categories = ["parser-implementations"]
license = "Apache-2.0"
edition = "2018"
edition = "2021"

[dependencies]
thiserror = "1"
nom = "6.1"
nom = "7"

[badges]
travis-ci = { repository = "squidpickles/ais", branch = "master" }
134 changes: 134 additions & 0 deletions src/messages/extended_class_b_position_report.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
//! ExtendedClass B Position Report (type 19)
use super::navigation::*;
use super::parsers::*;
use super::types::AssignedMode;
use super::types::Dte;
use super::types::EpfdType;
use super::AisMessageType;
use crate::errors::Result;
use crate::messages::types::ShipType;
use nom::bits::{bits, complete::take as take_bits};
use nom::combinator::map;
use nom::IResult;

#[derive(Debug, PartialEq)]
pub struct ExtendedClassBPositionReport {
pub message_type: u8,
pub repeat_indicator: u8,
pub mmsi: u32,
pub speed_over_ground: Option<f32>,
pub position_accuracy: Accuracy,
pub longitude: Option<f32>,
pub latitude: Option<f32>,
pub course_over_ground: Option<f32>,
pub true_heading: Option<u16>,
pub timestamp: u8,
pub name: String,
pub type_of_ship_and_cargo: Option<ShipType>,
pub dimension_to_bow: u16,
pub dimension_to_stern: u16,
pub dimension_to_port: u16,
pub dimension_to_starboard: u16,
pub epfd_type: Option<EpfdType>,
pub raim: bool,
pub dte: Dte,
pub assigned_mode: AssignedMode,
}

impl<'a> AisMessageType<'a> for ExtendedClassBPositionReport {
fn name(&self) -> &'static str {
"Extended Class B Position Report"
}

fn parse(data: &[u8]) -> Result<Self> {
let (_, report) = parse_base(data)?;
Ok(report)
}
}

fn parse_base(data: &[u8]) -> IResult<&[u8], ExtendedClassBPositionReport> {
bits(move |data| -> IResult<_, _> {
let (data, message_type) = take_bits(6u8)(data)?;
let (data, repeat_indicator) = take_bits(2u8)(data)?;
let (data, mmsi) = take_bits(30u32)(data)?;
let (data, _regional_reserved) = take_bits::<_, u8, _, _>(8u8)(data)?;
let (data, speed_over_ground) = map(take_bits(10u16), parse_speed_over_ground)(data)?;
let (data, position_accuracy) = map(take_bits(1u8), Accuracy::parse)(data)?;
let (data, longitude) = map(|data| signed_i32(data, 28), parse_longitude)(data)?;
let (data, latitude) = map(|data| signed_i32(data, 27), parse_latitude)(data)?;
let (data, course_over_ground) = map(take_bits(12u16), parse_cog)(data)?;
let (data, true_heading) = map(take_bits(9u16), parse_heading)(data)?;
let (data, timestamp) = take_bits(6u8)(data)?;
let (data, _regional_reserved) = take_bits::<_, u8, _, _>(4u8)(data)?;
let (data, name) = parse_6bit_ascii(data, 120)?;
let (data, type_of_ship_and_cargo) = map(take_bits(8u8), ShipType::parse)(data)?;
let (data, dimension_to_bow) = take_bits(9u16)(data)?;
let (data, dimension_to_stern) = take_bits(9u16)(data)?;
let (data, dimension_to_port) = take_bits(6u16)(data)?;
let (data, dimension_to_starboard) = take_bits(6u16)(data)?;
let (data, epfd_type) = map(take_bits(4u8), EpfdType::parse)(data)?;
let (data, raim) = map(take_bits(1u8), u8_to_bool)(data)?;
let (data, dte) = map(take_bits::<_, u8, _, _>(1u8), Into::into)(data)?;
let (data, assigned_mode) = map(take_bits(1u8), AssignedMode::parse)(data)?;
let (data, _spare) = take_bits::<_, u8, _, _>(4u8)(data)?;
Ok((
data,
ExtendedClassBPositionReport {
message_type,
repeat_indicator,
mmsi,
speed_over_ground,
position_accuracy,
longitude,
latitude,
course_over_ground,
true_heading,
timestamp,
name,
type_of_ship_and_cargo,
dimension_to_bow,
dimension_to_stern,
dimension_to_port,
dimension_to_starboard,
epfd_type,
raim,
dte,
assigned_mode,
},
))
})(data)
}

#[cfg(test)]
mod tests {
#![allow(clippy::unreadable_literal)]
use super::*;
use crate::test_helpers::*;

#[test]
fn test_position() {
let bytestream = b"C6:ijoP00:9NNF4TEspILDN0Vc0jNc1WWV0000000000S2<6R20P";
let bitstream = crate::messages::unarmor(bytestream, 0).unwrap();
let report = ExtendedClassBPositionReport::parse(&bitstream).unwrap();
assert_eq!(report.message_type, 19);
assert_eq!(report.repeat_indicator, 0);
assert_eq!(report.mmsi, 413954782);
assert_eq!(report.speed_over_ground, Some(0.0));
assert_eq!(report.position_accuracy, Accuracy::DGPS);
f32_equal_naive(report.longitude.unwrap(), 120.12851);
f32_equal_naive(report.latitude.unwrap(), 31.931623);
assert_eq!(report.course_over_ground, Some(40.7));
assert_eq!(report.true_heading, Some(40));
assert_eq!(report.timestamp, 60);
assert_eq!(report.name, "SU YOU 333");
assert_eq!(report.type_of_ship_and_cargo, Some(ShipType::Cargo));
assert_eq!(report.dimension_to_bow, 35);
assert_eq!(report.dimension_to_stern, 13);
assert_eq!(report.dimension_to_port, 4);
assert_eq!(report.dimension_to_starboard, 4);
assert_eq!(report.epfd_type, None);
assert_eq!(report.raim, false);
assert_eq!(report.dte, Dte::NotReady);
assert_eq!(report.assigned_mode, AssignedMode::Autonomous);
}
}
5 changes: 5 additions & 0 deletions src/messages/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod base_station_report;
pub mod binary_broadcast_message;
pub mod data_link_management_message;
pub mod dgnss_broadcast_binary_message;
pub mod extended_class_b_position_report;
pub mod interrogation;
mod navigation;
mod parsers;
Expand All @@ -28,6 +29,7 @@ pub enum AisMessage {
StaticAndVoyageRelatedData(static_and_voyage_related_data::StaticAndVoyageRelatedData),
DgnssBroadcastBinaryMessage(dgnss_broadcast_binary_message::DgnssBroadcastBinaryMessage),
StandardClassBPositionReport(standard_class_b_position_report::StandardClassBPositionReport),
ExtendedClassBPositionReport(extended_class_b_position_report::ExtendedClassBPositionReport),
DataLinkManagementMessage(data_link_management_message::DataLinkManagementMessage),
AidToNavigationReport(aid_to_navigation_report::AidToNavigationReport),
StaticDataReport(static_data_report::StaticDataReport),
Expand Down Expand Up @@ -69,6 +71,9 @@ pub fn parse(unarmored: &[u8]) -> Result<AisMessage> {
18 => Ok(AisMessage::StandardClassBPositionReport(
standard_class_b_position_report::StandardClassBPositionReport::parse(&unarmored)?,
)),
19 => Ok(AisMessage::ExtendedClassBPositionReport(
extended_class_b_position_report::ExtendedClassBPositionReport::parse(&unarmored)?,
)),
20 => Ok(AisMessage::DataLinkManagementMessage(
data_link_management_message::DataLinkManagementMessage::parse(&unarmored)?,
)),
Expand Down
17 changes: 1 addition & 16 deletions src/messages/standard_class_b_position_report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use super::navigation::*;
use super::parsers::*;
use super::radio_status::{ItdmaMessage, RadioStatus, SotdmaMessage};
use super::types::AssignedMode;
use super::AisMessageType;
use crate::errors::Result;
use nom::bits::{bits, complete::take as take_bits};
Expand Down Expand Up @@ -59,22 +60,6 @@ impl CarrierSense {
}
}

#[derive(Debug, PartialEq)]
pub enum AssignedMode {
Autonomous,
Assigned,
}

impl AssignedMode {
pub fn parse(val: u8) -> Self {
match val {
0 => Self::Autonomous,
1 => Self::Assigned,
_ => unreachable!(),
}
}
}

fn parse_base(data: &[u8]) -> IResult<&[u8], StandardClassBPositionReport> {
bits(move |data| -> IResult<_, _> {
let (data, message_type) = take_bits(6u8)(data)?;
Expand Down
16 changes: 16 additions & 0 deletions src/messages/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,3 +189,19 @@ impl From<u8> for Dte {
}
}
}

#[derive(Debug, PartialEq)]
pub enum AssignedMode {
Autonomous,
Assigned,
}

impl AssignedMode {
pub fn parse(val: u8) -> Self {
match val {
0 => Self::Autonomous,
1 => Self::Assigned,
_ => unreachable!(),
}
}
}

0 comments on commit 0fa5300

Please sign in to comment.