nmea_parser/ais/vdm_t15.rs
1/*
2Copyright 2020 Timo Saarinen
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17use super::*;
18
19// -------------------------------------------------------------------------------------------------
20
21/// Type 15: Interrogation
22#[derive(Clone, Debug, PartialEq)]
23pub struct Interrogation {
24 /// True if the data is about own vessel, false if about other.
25 pub own_vessel: bool,
26
27 /// AIS station type.
28 pub station: Station,
29
30 /// Interrogation case based on data length
31 pub case: InterrogationCase,
32
33 /// Source MMSI (30 bits)
34 pub mmsi: u32,
35
36 /// Interrogated MMSI (30 bits)
37 pub mmsi1: u32,
38
39 /// First message type (6 bits)
40 pub type1_1: u8,
41
42 /// First slot offset (12 bits)
43 pub offset1_1: u16,
44
45 /// Second message type (6 bits)
46 pub type1_2: Option<u8>,
47
48 /// Second slot offset (12 bits)
49 pub offset1_2: Option<u16>,
50
51 /// Interrogated MMSI (30 bits)
52 pub mmsi2: Option<u32>,
53
54 /// First message type (6 bits)
55 pub type2_1: Option<u8>,
56
57 /// First slot offset (12 bits)
58 pub offset2_1: Option<u16>,
59}
60
61/// The four cases of interrogation, depending on data length mostly.
62#[derive(Clone, Copy, Debug, PartialEq)]
63pub enum InterrogationCase {
64 /// One station is interrogated for one message type.
65 Case1,
66
67 /// One station is interrogated for two message types.
68 Case2,
69
70 /// Two stations are interrogated for one message type each.
71 Case3,
72
73 /// One station is interrogated for two message types, and a second for one message type.
74 Case4,
75}
76
77impl InterrogationCase {
78 pub fn new(bv: &BitVec) -> InterrogationCase {
79 let len = bv.len();
80 if len >= 160 {
81 if pick_u64(bv, 90, 18) == 0 {
82 // Case 3 (160 bits but without second type and second slot)
83 InterrogationCase::Case3
84 } else {
85 // Case 4 (160 bits)
86 InterrogationCase::Case4
87 }
88 } else if len >= 110 {
89 // Case 2 (110 bits)
90 InterrogationCase::Case2
91 } else {
92 // Case 1 (88 bits)
93 InterrogationCase::Case1
94 }
95 }
96}
97
98// -------------------------------------------------------------------------------------------------
99
100/// AIS VDM/VDO type 15: Interrogation
101pub(crate) fn handle(
102 bv: &BitVec,
103 station: Station,
104 own_vessel: bool,
105) -> Result<ParsedMessage, ParseError> {
106 let case = InterrogationCase::new(bv);
107 Ok(ParsedMessage::Interrogation(Interrogation {
108 own_vessel,
109 station,
110 case,
111 mmsi: { pick_u64(bv, 8, 30) as u32 },
112 mmsi1: { pick_u64(bv, 40, 30) as u32 },
113 type1_1: { pick_u64(bv, 70, 6) as u8 },
114 offset1_1: { pick_u64(bv, 76, 12) as u16 },
115 type1_2: match case {
116 InterrogationCase::Case2 | InterrogationCase::Case4 => Some(pick_u64(bv, 90, 6) as u8),
117 _ => None,
118 },
119 offset1_2: match case {
120 InterrogationCase::Case2 | InterrogationCase::Case4 => {
121 Some(pick_u64(bv, 96, 12) as u16)
122 }
123 _ => None,
124 },
125 mmsi2: match case {
126 InterrogationCase::Case3 | InterrogationCase::Case4 => {
127 Some(pick_u64(bv, 110, 30) as u32)
128 }
129 _ => None,
130 },
131 type2_1: match case {
132 InterrogationCase::Case4 => Some(pick_u64(bv, 140, 6) as u8),
133 _ => None,
134 },
135 offset2_1: match case {
136 InterrogationCase::Case4 => Some(pick_u64(bv, 146, 12) as u16),
137 _ => None,
138 },
139 }))
140}
141
142// -------------------------------------------------------------------------------------------------
143
144#[cfg(test)]
145mod test {
146 use super::*;
147
148 #[test]
149 fn test_parse_vdm_type15() {
150 let mut p = NmeaParser::new();
151 match p.parse_sentence("!AIVDM,1,1,,B,?h3Ovn1GP<K0<P@59a0,2*04") {
152 Ok(ps) => {
153 match ps {
154 // The expected result
155 ParsedMessage::Interrogation(i) => {
156 assert_eq!(i.mmsi, 3669720);
157 assert_eq!(i.mmsi1, 367014320);
158 assert_eq!(i.type1_1, 3);
159 assert_eq!(i.offset1_1, 516);
160 assert_eq!(i.type1_2, Some(5));
161 assert_eq!(i.offset1_2, Some(617));
162 assert_eq!(i.mmsi2, None);
163 assert_eq!(i.type2_1, None);
164 assert_eq!(i.offset2_1, None);
165 }
166 ParsedMessage::Incomplete => {
167 assert!(false);
168 }
169 _ => {
170 assert!(false);
171 }
172 }
173 }
174 Err(e) => {
175 assert_eq!(e.to_string(), "OK");
176 }
177 }
178 }
179}