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}