1 #include <marnav/ais/ais.hpp>
2 #include <marnav/ais/message_01.hpp>
3 #include <marnav/ais/message_02.hpp>
4 #include <marnav/ais/message_03.hpp>
5 #include <marnav/ais/message_04.hpp>
6 #include <marnav/ais/message_05.hpp>
7 #include <marnav/ais/message_06.hpp>
8 #include <marnav/ais/message_07.hpp>
9 #include <marnav/ais/message_08.hpp>
10 #include <marnav/ais/message_09.hpp>
11 #include <marnav/ais/message_10.hpp>
12 #include <marnav/ais/message_11.hpp>
13 #include <marnav/ais/message_12.hpp>
14 #include <marnav/ais/message_13.hpp>
15 #include <marnav/ais/message_14.hpp>
16 #include <marnav/ais/message_17.hpp>
17 #include <marnav/ais/message_18.hpp>
18 #include <marnav/ais/message_19.hpp>
19 #include <marnav/ais/message_20.hpp>
20 #include <marnav/ais/message_21.hpp>
21 #include <marnav/ais/message_22.hpp>
22 #include <marnav/ais/message_23.hpp>
23 #include <marnav/ais/message_24.hpp>
28 /// @example parse_ais.cpp
29 /// This example shows how to parse AIS messages from NMEA sentences.
31 /// @example read_ais.cpp
32 /// This is an example on how to parse and handle AIS messages while
33 /// receiving NMEA sentences.
35 /// @example create_nmea_from_ais.cpp
36 /// Shows how to create a NMEA sentence or sentences from AIS data.
42 uint8_t decode_armoring(char c
)
50 char encode_armoring(uint8_t value
)
52 value
&= 0x3f; // ensure 6 bits
62 static raw
collect(const std::vector
<std::pair
<std::string
, uint32_t>> & v
)
65 result
.reserve(64); // 64 bytes (512) are enough for AIS messages
67 for (auto const & item
: v
) {
68 const std::string
& payload
= item
.first
;
69 const uint32_t pad
= item
.second
;
71 auto end
= payload
.cend();
73 for (auto i
= payload
.cbegin(); i
!= end
; ++i
) {
75 uint8_t value
= decode_armoring(*i
);
78 result
.append(value
>> pad
, 6 - pad
);
80 result
.append(value
, 6);
88 static std::function
<std::unique_ptr
<message
>(const raw
&)> instantiate_message(
89 message_id type
, size_t size
)
91 #define REGISTER_MESSAGE(m) \
93 m::ID, detail::factory::parse<m> \
98 const std::function
<std::unique_ptr
<message
>(const raw
&)> parse
;
101 static const std::vector
<entry
> known_messages
= {
102 REGISTER_MESSAGE(message_01
),
103 REGISTER_MESSAGE(message_02
),
104 REGISTER_MESSAGE(message_03
),
105 REGISTER_MESSAGE(message_04
),
106 REGISTER_MESSAGE(message_05
),
107 REGISTER_MESSAGE(message_06
),
108 REGISTER_MESSAGE(message_07
),
109 REGISTER_MESSAGE(message_08
),
110 REGISTER_MESSAGE(message_09
),
111 REGISTER_MESSAGE(message_10
),
112 REGISTER_MESSAGE(message_11
),
113 REGISTER_MESSAGE(message_12
),
114 REGISTER_MESSAGE(message_13
),
115 REGISTER_MESSAGE(message_14
),
116 REGISTER_MESSAGE(message_17
),
117 REGISTER_MESSAGE(message_18
),
118 REGISTER_MESSAGE(message_19
),
119 REGISTER_MESSAGE(message_20
),
120 REGISTER_MESSAGE(message_21
),
121 REGISTER_MESSAGE(message_22
),
122 REGISTER_MESSAGE(message_23
),
123 REGISTER_MESSAGE(message_24
),
126 #undef REGISTER_MESSAGE
129 auto const & i
= std::find_if(begin(known_messages
), end(known_messages
),
130 [type
](const entry
& e
) { return e
.id
== type
; });
132 if (i
== end(known_messages
))
133 throw unknown_message
{"unknown message in ais/instantiate_message: "
134 + std::to_string(static_cast<uint8_t>(type
)) + " (" + std::to_string(size
)
143 /// Parses the specified data and creates corresponding AIS messages.
145 /// @param[in] v All NMEA payloads, necessary to build the AIS message.
146 /// This may be obtained using nmea::collect_payload.
147 /// @return The constructed AIS message.
148 /// @exception unknown_message Will be thrown if the AIS message is not supported.
149 /// @exception std::invalid_argument Error has been occurred during parsing of
151 std::unique_ptr
<message
> make_message(const std::vector
<std::pair
<std::string
, uint32_t>> & v
)
153 auto bits
= collect(v
);
154 message_id type
= static_cast<message_id
>(bits
.get
<uint8_t>(0, 6));
155 return instantiate_message(type
, bits
.size())(bits
);
158 /// Encodes the specified message and returns a container with payload and padding
159 /// information. This payload container can be used directly with NMEA funcitons.
161 /// @param[in] msg The message to encode.
162 /// @return The container with payload/padding information
164 std::vector
<std::pair
<std::string
, uint32_t>> encode_message(const message
& msg
)
166 auto bits
= msg
.get_data();
167 if (bits
.size() == 0)
168 throw std::invalid_argument
{"message not able to encode"};
170 std::vector
<std::pair
<std::string
, uint32_t>> result
;
172 std::pair
<std::string
, uint32_t> current
{"", 0};
173 for (raw::size_type ofs
= 0; ofs
< bits
.size(); ofs
+= 6) {
174 if (ofs
+ 6 < bits
.size()) {
178 bits
.get(value
, ofs
, 6);
179 current
.first
+= encode_armoring(value
);
181 // append to string, only 51 characters per string (happens to be NMEA restriction)
182 if (current
.first
.size() == 56) {
183 result
.push_back(current
);
184 current
.first
.clear();
188 // last, append remainder padded to the string
190 auto remainder
= bits
.size() - ofs
;
191 current
.second
= 6 - remainder
;
193 bits
.get(value
, ofs
, remainder
);
194 value
<<= current
.second
;
195 current
.first
+= encode_armoring(value
);
196 result
.push_back(current
);