Build: add test presets
[marnav.git] / src / marnav / ais / ais.cpp
blob3735a52c54604d61894bfdb25e7d836ea0377d91
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>
25 #include <algorithm>
26 #include <functional>
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.
38 namespace marnav
40 namespace ais
42 uint8_t decode_armoring(char c)
44 auto value = c - '0';
45 if (value > 40)
46 value -= 8;
47 return value & 0x3f;
50 char encode_armoring(uint8_t value)
52 value &= 0x3f; // ensure 6 bits
53 if (value > 39)
54 value += 8;
55 return value + '0';
58 /// @cond DEV
60 namespace
62 static raw collect(const std::vector<std::pair<std::string, uint32_t>> & v)
64 raw result;
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();
72 auto last = end - 1;
73 for (auto i = payload.cbegin(); i != end; ++i) {
75 uint8_t value = decode_armoring(*i);
77 if (i == last) {
78 result.append(value >> pad, 6 - pad);
79 } else {
80 result.append(value, 6);
85 return result;
88 static std::function<std::unique_ptr<message>(const raw &)> instantiate_message(
89 message_id type, size_t size)
91 #define REGISTER_MESSAGE(m) \
92 { \
93 m::ID, detail::factory::parse<m> \
96 struct entry {
97 const message_id id;
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
128 using namespace std;
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)
135 + " bits)"};
137 return i->parse;
141 /// @endcond
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
150 /// the message.
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()) {
175 // normal case
177 uint8_t value = 0;
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();
185 current.second = 0;
187 } else {
188 // last, append remainder padded to the string
190 auto remainder = bits.size() - ofs;
191 current.second = 6 - remainder;
192 uint8_t value = 0;
193 bits.get(value, ofs, remainder);
194 value <<= current.second;
195 current.first += encode_armoring(value);
196 result.push_back(current);
200 return result;