nmea_parser/ais/vdm_t22.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 22: Channel Management
22#[derive(Default, Clone, Debug, PartialEq)]
23pub struct ChannelManagement {
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 /// Channel A number (12 bits).
34 pub channel_a: u16,
35
36 /// Channel B number (12 bits).
37 pub channel_b: u16,
38
39 /// TxRx mode:
40 /// 0 = TxA/TxB, RxA/RxB (default)
41 /// 1 = TxA, RxA/RxB
42 /// 2 = TxB, RxA/RxB
43 /// 3 = Reserved for future use
44 pub txrx: u8,
45
46 /// Power level to be used:
47 /// 0 = low,
48 /// 1 = high
49 pub power: bool,
50
51 /// Northeast latitude to 0.1 minutes.
52 pub ne_lat: Option<f64>,
53
54 /// Northeast longitude to 0.1 minutes.
55 pub ne_lon: Option<f64>,
56
57 /// Southwest latitude to 0.1 minutes.
58 pub sw_lat: Option<f64>,
59
60 /// Southwest longitude to 0.1 minutes.
61 pub sw_lon: Option<f64>,
62
63 /// MMSI of destination 1 (30 bits).
64 pub dest1_mmsi: Option<u32>,
65
66 /// MMSI of destination 2 (30 bits).
67 pub dest2_mmsi: Option<u32>,
68
69 /// Addressed:
70 /// false = broadcast,
71 /// true = addressed
72 pub addressed: bool,
73
74 /// Channel A band:
75 /// false = default,
76 /// true = 12.5 kHz
77 pub channel_a_band: bool,
78
79 /// Channel B band:
80 /// false = default,
81 /// true = 12.5 kHz
82 pub channel_b_band: bool,
83
84 /// Size of transitional zone (3 bits).
85 pub zonesize: u8,
86}
87
88// -------------------------------------------------------------------------------------------------
89
90/// AIS VDM/VDO type 22: Channel Management
91pub(crate) fn handle(
92 bv: &BitVec,
93 station: Station,
94 own_vessel: bool,
95) -> Result<ParsedMessage, ParseError> {
96 let addressed = pick_u64(bv, 139, 1) != 0;
97 Ok(ParsedMessage::ChannelManagement(ChannelManagement {
98 own_vessel: { own_vessel },
99 station: { station },
100 mmsi: { pick_u64(bv, 8, 30) as u32 },
101 channel_a: { pick_u64(bv, 40, 12) as u16 },
102 channel_b: { pick_u64(bv, 52, 12) as u16 },
103 txrx: { pick_u64(bv, 64, 4) as u8 },
104 power: { pick_u64(bv, 68, 1) != 0 },
105 ne_lat: {
106 if !addressed {
107 Some(pick_i64(bv, 87, 17) as f64 / 600.0)
108 } else {
109 None
110 }
111 },
112 ne_lon: {
113 if !addressed {
114 Some(pick_i64(bv, 69, 18) as f64 / 600.0)
115 } else {
116 None
117 }
118 },
119 sw_lat: {
120 if !addressed {
121 Some(pick_i64(bv, 122, 17) as f64 / 600.0)
122 } else {
123 None
124 }
125 },
126 sw_lon: {
127 if !addressed {
128 Some(pick_i64(bv, 104, 18) as f64 / 600.0)
129 } else {
130 None
131 }
132 },
133 dest1_mmsi: {
134 if addressed {
135 Some(pick_u64(bv, 69, 30) as u32)
136 } else {
137 None
138 }
139 },
140 dest2_mmsi: {
141 if addressed {
142 Some(pick_u64(bv, 104, 30) as u32)
143 } else {
144 None
145 }
146 },
147 addressed: { pick_u64(bv, 139, 1) != 0 },
148 channel_a_band: { pick_u64(bv, 140, 1) != 0 },
149 channel_b_band: { pick_u64(bv, 141, 1) != 0 },
150 zonesize: { pick_u64(bv, 142, 3) as u8 },
151 }))
152}
153
154// -------------------------------------------------------------------------------------------------
155
156#[cfg(test)]
157mod test {
158 use super::*;
159
160 #[test]
161 fn test_parse_vdm_type22() {
162 let mut p = NmeaParser::new();
163 match p.parse_sentence("!AIVDM,1,1,,A,F030ot22N2P6aoQbhe4736L20000,0*1A") {
164 Ok(ps) => {
165 match ps {
166 // The expected result
167 ParsedMessage::ChannelManagement(cm) => {
168 assert_eq!(cm.mmsi, 3160048);
169 assert_eq!(cm.channel_a, 2087);
170 assert_eq!(cm.channel_b, 2088);
171 assert_eq!(cm.txrx, 0);
172 assert!(!cm.power);
173 assert::close(cm.ne_lat.unwrap_or(0.0), 45.55, 0.01);
174 assert::close(cm.ne_lon.unwrap_or(0.0), -73.50, 0.01);
175 assert::close(cm.sw_lat.unwrap_or(0.0), 42.33, 0.01);
176 assert::close(cm.sw_lon.unwrap_or(0.0), -80.17, 0.01);
177 assert!(!cm.addressed);
178 assert!(!cm.channel_a_band);
179 assert!(!cm.channel_b_band);
180 assert_eq!(cm.zonesize, 4);
181 }
182 ParsedMessage::Incomplete => {
183 assert!(false);
184 }
185 _ => {
186 assert!(false);
187 }
188 }
189 }
190 Err(e) => {
191 assert_eq!(e.to_string(), "OK");
192 }
193 }
194 }
195}