nmea_parser/ais/
vdm_t25.rs

1/*
2Copyright 2020-2021 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 25: Single Slot Binary Message
22#[derive(Default, Clone, Debug, PartialEq)]
23pub struct SingleSlotBinaryMessage {
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    /// User ID (30 bits)
31    pub mmsi: u32,
32
33    /// When 'addressed' flag is on this field contains the parsed destination MMSI.
34    pub dest_mmsi: Option<u32>,
35
36    /// When 'addressed' flag is off and 'structured' flag on this field contains
37    /// application ID which consists of 10-bit DAC and 6-bit FID as in message types 6 and 8.
38    pub app_id: Option<u16>,
39
40    /// Data field of length 0-128 bits.
41    pub data: BitVec,
42}
43
44// -------------------------------------------------------------------------------------------------
45
46/// AIS VDM/VDO type 25: Single Slot Binary Message
47#[allow(clippy::collapsible_if)]
48pub(crate) fn handle(
49    bv: &BitVec,
50    station: Station,
51    own_vessel: bool,
52) -> Result<ParsedMessage, ParseError> {
53    let addressed = pick_u64(bv, 38, 1) != 0;
54    let structured = pick_u64(bv, 39, 1) != 0;
55
56    Ok(ParsedMessage::SingleSlotBinaryMessage(
57        SingleSlotBinaryMessage {
58            own_vessel: { own_vessel },
59            station: { station },
60            mmsi: { pick_u64(bv, 8, 30) as u32 },
61            dest_mmsi: {
62                if addressed {
63                    Some(pick_u64(bv, 40, 30) as u32)
64                } else {
65                    None
66                }
67            },
68            app_id: {
69                if addressed {
70                    None
71                } else if structured {
72                    Some(pick_u64(bv, 70, 16) as u16)
73                } else {
74                    None
75                }
76            },
77            data: {
78                if addressed {
79                    BitVec::from_bitslice(&bv[70..max(70, bv.len())])
80                } else if structured {
81                    BitVec::from_bitslice(&bv[86..max(86, bv.len())])
82                } else {
83                    BitVec::from_bitslice(&bv[40..max(40, bv.len())])
84                }
85            },
86        },
87    ))
88}
89
90// -------------------------------------------------------------------------------------------------
91
92#[cfg(test)]
93mod test {
94    use super::*;
95
96    #[test]
97    fn test_parse_vdm_type25() {
98        let mut p = NmeaParser::new();
99
100        // Test 1
101        match p.parse_sentence("!AIVDM,1,1,,A,I6SWo?8P00a3PKpEKEVj0?vNP<65,0*73") {
102            Ok(ps) => {
103                match ps {
104                    // The expected result
105                    ParsedMessage::SingleSlotBinaryMessage(ssbm) => {
106                        assert_eq!(ssbm.mmsi, 440006460);
107                        assert_eq!(ssbm.dest_mmsi, Some(134218384));
108                        assert_eq!(ssbm.app_id, None);
109                    }
110                    ParsedMessage::Incomplete => {
111                        assert!(false);
112                    }
113                    _ => {
114                        assert!(false);
115                    }
116                }
117            }
118            Err(e) => {
119                assert_eq!(e.to_string(), "OK");
120            }
121        }
122
123        // Test 2
124        match p.parse_sentence("!AIVDM,1,1,,A,I8IRGB40QPPa0:<HP::V=gwv0l48,0*0E") {
125            Ok(ps) => {
126                match ps {
127                    // The expected result
128                    ParsedMessage::SingleSlotBinaryMessage(ssbm) => {
129                        assert_eq!(ssbm.mmsi, 563648328);
130                        assert_eq!(ssbm.dest_mmsi, None);
131                        assert_eq!(ssbm.app_id, Some(16424));
132                    }
133                    ParsedMessage::Incomplete => {
134                        assert!(false);
135                    }
136                    _ => {
137                        assert!(false);
138                    }
139                }
140            }
141            Err(e) => {
142                assert_eq!(e.to_string(), "OK");
143            }
144        }
145
146        // Test 3
147        match p.parse_sentence("!AIVDM,1,1,,A,I6SWVNP001a3P8FEKNf=Qb0@00S8,0*6B") {
148            Ok(ps) => {
149                match ps {
150                    // The expected result
151                    ParsedMessage::SingleSlotBinaryMessage(ssbm) => {
152                        assert_eq!(ssbm.mmsi, 440002170);
153                        assert_eq!(ssbm.dest_mmsi, None);
154                        assert_eq!(ssbm.app_id, None);
155                    }
156                    ParsedMessage::Incomplete => {
157                        assert!(false);
158                    }
159                    _ => {
160                        assert!(false);
161                    }
162                }
163            }
164            Err(e) => {
165                assert_eq!(e.to_string(), "OK");
166            }
167        }
168    }
169}