Fixposition SDK 0.0.0-heads/main-0-g6592994
Collection of c++ libraries and apps for use with Fixposition products on Linux
Loading...
Searching...
No Matches
parser.hpp
Go to the documentation of this file.
1/**
2 * \verbatim
3 * ___ ___
4 * \ \ / /
5 * \ \/ / Copyright (c) Fixposition AG (www.fixposition.com) and contributors
6 * / /\ \ License: see the LICENSE file
7 * /__/ \__\
8 *
9 * Based on work by flipflip (https://github.com/phkehl)
10 * \endverbatim
11 *
12 * @file
13 * @brief Fixposition SDK: Parser
14 *
15 * @page FPSDK_COMMON_PARSER Parser
16 *
17 * **API**: fpsdk_common/parser.hpp and fpsdk::common::parser
18 *
19 * @section FPSDK_COMMON_PARSER_INTRO Introduction
20 *
21 * This implements a message parser for various protocols (see #fpsdk::common::parser::Protocol). A
22 * fpsdk::common::parser::Parser object is fed with data and it emits one fpsdk::common::parser::ParserMsg after
23 * another. No data is discarded, any data input into the parser is also output as a message. Unknown parts of the input
24 * data (other protocols not recognised by this parser, incorrect or incomplete messages, spurious data, line noise,
25 * etc.) are output as one or multiple messages of type fpsdk::common::parser::Protocol::OTHER.
26 *
27 * Note that the Parser does not *decode* messages. It only *extracts* messages (frames) from a stream of bytes.
28 * The *decoding* of messages, i.e. decoding the data or fields from the message payload must be done by the
29 * application. However, a number of types and utility functions to facilitate this are included.
30 *
31 * The Parser code is organised into the following parts:
32 *
33 * - @subpage FPSDK_COMMON_PARSER_TYPES
34 * - @subpage FPSDK_COMMON_PARSER_CRC
35 * - @subpage FPSDK_COMMON_PARSER_FPA
36 * - @subpage FPSDK_COMMON_PARSER_FPB
37 * - @subpage FPSDK_COMMON_PARSER_NMEA
38 * - @subpage FPSDK_COMMON_PARSER_NOVB
39 * - @subpage FPSDK_COMMON_PARSER_QGC
40 * - @subpage FPSDK_COMMON_PARSER_RTCM3
41 * - @subpage FPSDK_COMMON_PARSER_SBF
42 * - @subpage FPSDK_COMMON_PARSER_SPARTN
43 * - @subpage FPSDK_COMMON_PARSER_UBX
44 * - @subpage FPSDK_COMMON_PARSER_UNIB
45 *
46 * @section FPSDK_COMMON_PARSER_USAGE Usage
47 *
48 * The usage of the parser is best explained in source code:
49 *
50 * @code{cpp}
51 * #include <cstdint>
52 * #include <ifstream>
53 *
54 * #include <fpsdk_common/logging.hpp>
55 * #include <fpsdk_common/parser.hpp>
56 *
57 * using namespace fpsdk::common::parser;
58 *
59 * std::ifstream input("some_logfile.bin", std::ios::binary);
60 * Parser parser;
61 * ParserMsg msg;
62 *
63 * while (input.good()) {
64 *
65 * // Read a chunk of data from the logfile
66 * uint8_t data[MAX_ADD_SIZE];
67 * const std::size_t size = input.readsome((char*)data, sizeof(data));
68 *
69 * // No more data from file
70 * if (size <= 0) {
71 * break;
72 * }
73 *
74 * // Add the chunk of data to the parser
75 * if (!parser.Add(data, size)) {
76 * // We added too much data! This only happens if we add more than MAX_ADD_SIZE at a time.
77 * WARNING(("Parser overflow!");
78 * parser.Reset();
79 * continue;
80 * }
81 *
82 * // Run parser and print message name and size to the screen
83 * while (parser.Process(msg)) {
84 * msg.MakeInfo(); // Try to populate msg.info_
85 * INFO("Message %s, size %d (%s)", msg.name_.c_str(), (int)msg.Size(), msg.info_.c_str());
86 * }
87 * }
88 *
89 * // There may be some remaining data in the parser.
90 * // If there is any data left, we should see only type OTHER "messages" here.
91 * while (parser.Flush(msg)) {
92 * msg.MakeInfo();
93 * INFO("Message %s, size %d", msg.name_.c_str(), (int)msg.Size(), msg.info_.c_str());
94 * }
95 * @endcode
96 *
97 * For a more sophisticated example app see the @ref FPSDK_APPS_PARSERTOOL (fpsdk_apps/parsertool/parsertool.cpp).
98 *
99 * @section FPSDK_COMMON_PARSER_NAMING Protocol and message naming
100 *
101 * The protocols names are defined in #fpsdk::common::parser::Protocol and can be stringified using
102 * fpsdk::common::parser::ProtocolStr(). The names must match `/^[A-Z][A-Z0-9_]{2,5}$/.
103 *
104 * The message naming scheme consists of words separated by dashes. The first word is always the protocol name.
105 * Depending on the protocol one or two more words are added. All words must match `/^[A-Z][A-Z0-9]{2,9}$/`.
106 *
107 * Examples:
108 *
109 * - **FP_A**-NAME, e.g. `FP_A-ODOMETRY`, `FP_A-RAWIMU`. See fpsdk::common::parser::nmea::NmeaGetMessageName() for
110 * details.
111 * - **FP_B**-NAME, e.g. `FP_B-SYSTEMSTATUS`, `FP_B-GNSSSTATUS`. See fpsdk::common::parser::fpb::FpbGetMessageName() for
112 * details.
113 * - **NMEA**-TALKER-FORMATTER, e.g. `NMEA-GN-GGA`, `NMEA-GN-RMC`. See fpsdk::common::parser::nmea::NmeaGetMessageName()
114 * for details.
115 * - **UBX**-CLASS-MESSAGE, e.g. `UBX-NAV-PVT`, `UBX-RXM-RAWX`. See fpsdk::common::parser::ubx::UbxGetMessageName() for
116 * details.
117 * - **RTCM3**-TYPENNNN, e.g. `RTCM3-TYPE1234`. See fpsdk::common::parser::rtcm3::Rtcm3GetMessageName() for details.
118 * - **UNI_B**-NAME, .e.g. `UNI_B-VERSION`, `UNI_B-BESTNAV`. See fpsdk::common::parser::unib::UnibGetMessageName() for
119 * details.
120 * - **NOV_B**-NAME, .e.g. `NOV_B-BESTGNSSPOS`, `NOV_B-RAWDMI`. See fpsdk::common::parser::novb::NovbGetMessageName()
121 * for details.
122 * - **SPARTN**-TYPE-SUBTYPE, e.g. `SPARTN-OCB-GPS`. See fpsdk::common::parser::spartn::SpartnGetMessageName() for
123 * details.
124 * - **SBF**-NAME, .e.g. `SBF-NAVCART`, `SBF-MEASEPOCH`. See fpsdk::common::parser::sbf::SbfGetMessageName()
125 * for details.
126 * - **QGC**-GROUP-MESSAGE, e.g. `QGC-RAW-HASE6`. See fpsdk::common::parser::qgc::QgcGetMessageName() for
127 * details.
128 * - **OTHER** (there are no individual messages in this "protocol")
129 *
130 * @section FPSDK_COMMON_PARSER_DESIGN Design
131 *
132 * @image html parser.drawio.svg ""
133 *
134 * The maximum message size for each protocol is limited. For example, while the FP_B frame meta data would allow for
135 * messages up to 65'536 bytes, the parser discards any message larger than #fpsdk::common::parser::MAX_FP_B_SIZE.
136 * Messages larger than this are not practical and they do not (should not) exist. For example, it would take 5.6
137 * seconds for such a message to transfer over a serial port at baudrate 115'200.
138 *
139 */
140#ifndef __FPSDK_COMMON_PARSER_HPP__
141#define __FPSDK_COMMON_PARSER_HPP__
142
143/* LIBC/STL */
144#include <cstdint>
145#include <vector>
146
147/* EXTERNAL */
148
149/* PACKAGE */
150#include "parser/types.hpp"
151
152namespace fpsdk {
153namespace common {
154/**
155 * @brief Parser
156 */
157namespace parser {
158/* ****************************************************************************************************************** */
159
160/**
161 * @brief Message parser class
162 */
164{
165 public:
166 //
167 // ----- Constructor -----------------------------------------------------------------------------------------------
168
169 /**
170 * @brief Constructor, initialises parser and makes it ready to accept data and emit messages
171 */
173
174 //
175 // ----- Input data ------------------------------------------------------------------------------------------------
176
177 /**
178 * @brief Add data to parser
179 *
180 * @param[in] data Pointer to data
181 * @param[in] size Size of data (should be <= #MAX_ADD_SIZE)
182 *
183 * @returns true if data was added to the parser (or data was empty, that is, data != NULL and size = 0),
184 * false if there was not enough space left
185 */
186 bool Add(const uint8_t* data, const std::size_t size);
187
188 /**
189 * @brief Add data to parser
190 *
191 * @param[in] data data, can be empty (should be <= #MAX_ADD_SIZE)
192 *
193 * @returns true if data was added to the parser (or data was empty), false if there was not enough space left
194 */
195 bool Add(const std::vector<uint8_t>& data);
196
197 /**
198 * @brief Reset parser
199 *
200 * Resets the parser state and discards all collected data.
201 */
202 void Reset();
203
204 //
205 // ----- Process data (run parser) ---------------------------------------------------------------------------------
206
207 /**
208 * @brief Process data in parser, return message
209 *
210 * @param[out] msg The detected message frame
211 *
212 * @returns true if a message was detected, false otherwise (meaning: not enough data in parser)
213 */
214 bool Process(ParserMsg& msg);
215
216 /**
217 * @brief Get remaining data from parser as OTHER message(s)
218 *
219 * @param[out] msg A chunk of the remaining data as a OTHER message
220 *
221 * @returns true if there was remaining data, false if parser buffer was empty
222 */
223 bool Flush(ParserMsg& msg);
224
225 //
226 // ----- Utilities -------------------------------------------------------------------------------------------------
227
228 /**
229 * @brief Get parser statistics
230 *
231 * @returns a copy of the parser statistics
232 */
234
235 //
236 // ----- Private ---------------------------------------------------------------------------------------------------
237 private:
238 // Parser state
239 // clang-format off
240 uint8_t buf_[MAX_ADD_SIZE + (2 * MAX_ANY_SIZE)]; //!< Parser buffer
241 std::size_t size_; //!< Buffer size (size of data that is available for processing)
242 std::size_t offs_; //!< Buffer offset (points to start of data to be processed)
243 ParserStats stats_; //!< Statistics
244 // clang-format on
245
246 void EmitMessage(ParserMsg&, const std::size_t, const Protocol); //!< Helper to emit a normal message
247 void EmitGarbage(ParserMsg&); //!< Helper to emit a OTHER message
248};
249
250/* ****************************************************************************************************************** */
251} // namespace parser
252} // namespace common
253} // namespace fpsdk
254#endif // __FPSDK_COMMON_PARSER_HPP__
bool Flush(ParserMsg &msg)
Get remaining data from parser as OTHER message(s)
void Reset()
Reset parser.
ParserStats GetStats() const
Get parser statistics.
bool Add(const std::vector< uint8_t > &data)
Add data to parser.
bool Process(ParserMsg &msg)
Process data in parser, return message.
bool Add(const uint8_t *data, const std::size_t size)
Add data to parser.
Parser()
Constructor, initialises parser and makes it ready to accept data and emit messages.
Protocol
Protocols (message types), see also Protocol and message naming.
Definition types.hpp:42
static constexpr std::size_t MAX_ADD_SIZE
Max size for Parser::Add() that is guaranteed to work.
Definition types.hpp:175
static constexpr std::size_t MAX_ANY_SIZE
The largest of the above.
Definition types.hpp:189
Fixposition SDK: Common library.
Definition doc.hpp:21
Fixposition SDK.
Fixposition SDK: Parser.
Message frame output by the Parser.
Definition types.hpp:100