Fixposition SDK 0.0.0-heads/main-0-gc9c007d
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_RTCM3
40 * - @subpage FPSDK_COMMON_PARSER_SBF
41 * - @subpage FPSDK_COMMON_PARSER_SPARTN
42 * - @subpage FPSDK_COMMON_PARSER_UBX
43 * - @subpage FPSDK_COMMON_PARSER_UNIB
44 *
45 * @section FPSDK_COMMON_PARSER_USAGE Usage
46 *
47 * The usage of the parser is best explained in source code:
48 *
49 * @code{cpp}
50 * #include <cstdint>
51 * #include <ifstream>
52 *
53 * #include <fpsdk_common/logging.hpp>
54 * #include <fpsdk_common/parser.hpp>
55 *
56 * using namespace fpsdk::common::parser;
57 *
58 * std::ifstream input("some_logfile.bin", std::ios::binary);
59 * Parser parser;
60 * ParserMsg msg;
61 *
62 * while (input.good()) {
63 *
64 * // Read a chunk of data from the logfile
65 * uint8_t data[MAX_ADD_SIZE];
66 * const std::size_t size = input.readsome((char*)data, sizeof(data));
67 *
68 * // No more data from file
69 * if (size <= 0) {
70 * break;
71 * }
72 *
73 * // Add the chunk of data to the parser
74 * if (!parser.Add(data, size)) {
75 * // We added too much data! This will not happen if we don't add more than MAX_ANY_SIZE at a time.
76 * WARNING(("Parser overflow!");
77 * parser.Reset();
78 * continue;
79 * }
80 *
81 * // Run parser and print message name and size to the screen
82 * while (parser.Process(msg)) {
83 * msg.MakeInfo(); // Try to populate msg.info_
84 * INFO("Message %s, size %d (%s)", msg.name_.c_str(), (int)msg.Size(), msg.info_.c_str());
85 * }
86 * }
87 *
88 * // There may be some remaining data in the parser.
89 * // If there is anytghing, we should see only type OTHER "messages" here.
90 * while (parser.Flush(msg)) {
91 * msg.MakeInfo();
92 * INFO("Message %s, size %d", msg.name_.c_str(), (int)msg.Size(), msg.info_.c_str());
93 * }
94 * @endcode
95 *
96 * For a more sophisticated example app see the @ref FPSDK_APPS_PARSERTOOL (fpsdk_apps/parsertool/parsertool.cpp).
97 *
98 * @section FPSDK_COMMON_PARSER_NAMING Protocol and message naming
99 *
100 * The protocols names are defined in #fpsdk::common::parser::Protocol and can be stringified using
101 * fpsdk::common::parser::ProtocolStr(). The names must match `/^[A-Z][A-Z0-9_]{2,5}$/.
102 *
103 * The message naming scheme consists of words separated by dashes. The first word is always the protocol name.
104 * Depending on the protocol one or two more words are added. All words must match `/^[A-Z][A-Z0-9]{2,9}$/`.
105 *
106 * Examples:
107 *
108 * - **FP_A**-NAME, e.g. `FP_A-ODOMETRY`, `FP_A-RAWIMU`. See fpsdk::common::parser::nmea::NmeaGetMessageName() for
109 * details.
110 * - **FP_B**-NAME, e.g. `FP_B-SYSTEMSTATUS`, `FP_B-GNSSSTATUS`. See fpsdk::common::parser::fpb::FpbGetMessageName() for
111 * details.
112 * - **NMEA**-TALKER-FORMATTER, e.g. `NMEA-GN-GGA`, `NMEA-GN-RMC`. See fpsdk::common::parser::nmea::NmeaGetMessageName()
113 * for details.
114 * - **UBX**-CLASS-MESSAGE, e.g. `UBX-NAV-PVT`, `UBX-RXM-RAWX`. See fpsdk::common::parser::ubx::UbxGetMessageName() for
115 * details.
116 * - **RTCM3**-TYPENNNN, e.g. `RTCM3-TYPE1234`. See fpsdk::common::parser::rtcm3::Rtcm3GetMessageName() for details.
117 * - **UNI_B**-NAME, .e.g. `UNI_B-VERSION`, `UNI_B-BESTNAV`. See fpsdk::common::parser::unib::UnibGetMessageName() for
118 * details.
119 * - **NOV_B**-NAME, .e.g. `NOV_B-BESTGNSSPOS`, `NOV_B-RAWDMI`. See fpsdk::common::parser::novb::NovbGetMessageName()
120 * for details.
121 * - **SPARTN**-TYPE-SUBTYPE, e.g. `SPARTN-OCB-GPS`. See fpsdk::common::parser::spartn::SpartnGetMessageName() for
122 * details.
123 * - **OTHER** (there are no individual messages in this "protocol")
124 *
125 * @section FPSDK_COMMON_PARSER_DESIGN Design
126 *
127 * @image html parser.drawio.svg ""
128 *
129 * The parser implementation does not use any STL containers, templates or other c++ conveniences. Instead, low-level
130 * c-like types are used, in order to prevent any expensive heap (re)allocation and too much copying of data. For
131 * example, the returned ParserMsg contains a pointer to message rather than a std::vector<uint8_t>, and a const
132 * char* message name instead of a std::string. Applications can convert (copy) these easily to std::vector resp.
133 * std::string for further processing. Some of the utilities, such as fpsdk::common::parser::ubx::UbxMakeMessage(), do
134 * use STL containers for convenience.
135 *
136 * The maximum message size for each protocol is limited. For example, while the FP_B frame meta data would allow for
137 * messages up to 65'536 bytes, the parser discards any message larger than #fpsdk::common::parser::MAX_FP_B_SIZE.
138 * Messages larger than this are not practical and they do not (should not) exist. For example, it would take 5.6
139 * seconds for such a message to transfer over a serial port at baudrate 115'200.
140 *
141 */
142#ifndef __FPSDK_COMMON_PARSER_HPP__
143#define __FPSDK_COMMON_PARSER_HPP__
144
145/* LIBC/STL */
146#include <cstdint>
147#include <vector>
148
149/* EXTERNAL */
150
151/* PACKAGE */
152#include "parser/types.hpp"
153
154namespace fpsdk {
155namespace common {
156/**
157 * @brief Parser
158 */
159namespace parser {
160/* ****************************************************************************************************************** */
161
162/**
163 * @brief Message parser class
164 */
166{
167 public:
168 //
169 // ----- Constructor -----------------------------------------------------------------------------------------------
170
171 /**
172 * @brief Constructor, initialises parser and makes it ready to accept data and emit messages
173 */
175
176 //
177 // ----- Input data ------------------------------------------------------------------------------------------------
178
179 /**
180 * @brief Add data to parser
181 *
182 * @param[in] data Pointer to data
183 * @param[in] size Size of data (should be <= #MAX_ADD_SIZE)
184 *
185 * @returns true if data was added to the parser (or data was empty, that is, data != NULL and size = 0),
186 * false if there was not enough space left
187 */
188 bool Add(const uint8_t* data, const std::size_t size);
189
190 /**
191 * @brief Add data to parser
192 *
193 * @param[in] data data, can be empty (should be <= #MAX_ADD_SIZE)
194 *
195 * @returns true if data was added to the parser (or data was empty), false if there was not enough space left
196 */
197 bool Add(const std::vector<uint8_t>& data);
198
199 /**
200 * @brief Reset parser
201 *
202 * Resets the parser state and discards all collected data.
203 */
204 void Reset();
205
206 //
207 // ----- Process data (run parser) ---------------------------------------------------------------------------------
208
209 /**
210 * @brief Process data in parser, return message
211 *
212 * @param[out] msg The detected message frame
213 *
214 * @returns true if a message was detected, false otherwise (meaning: not enough data in parser)
215 */
216 bool Process(ParserMsg& msg);
217
218 /**
219 * @brief Get remaining data from parser as OTHER message(s)
220 *
221 * @param[out] msg A chunk of the remaining data as a OTHER message
222 *
223 * @returns true if there was remaining data, false if parser buffer was empty
224 */
225 bool Flush(ParserMsg& msg);
226
227 //
228 // ----- Utilities -------------------------------------------------------------------------------------------------
229
230 /**
231 * @brief Get parser statistics
232 *
233 * @returns a copy of the parser statistics
234 */
236
237 //
238 // ----- Private ---------------------------------------------------------------------------------------------------
239 private:
240 // Parser state
241 // clang-format off
242 uint8_t buf_[MAX_ADD_SIZE + (2 * MAX_ANY_SIZE)]; //!< Parser buffer
243 std::size_t size_; //!< Buffer size (size of data that is available for processing)
244 std::size_t offs_; //!< Buffer offset (points to start of data to be processed)
245 ParserStats stats_; //!< Statistics
246 // clang-format on
247
248 void EmitMessage(ParserMsg&, const std::size_t, const Protocol); //!< Helper to emit a normal message
249 void EmitGarbage(ParserMsg&); //!< Helper to emit a OTHER message
250};
251
252/* ****************************************************************************************************************** */
253} // namespace parser
254} // namespace common
255} // namespace fpsdk
256#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:171
static constexpr std::size_t MAX_ANY_SIZE
The largest of the above.
Definition types.hpp:184
Fixposition SDK: Common library.
Definition doc.hpp:21
Fixposition SDK.
Fixposition SDK: Parser.
Message frame output by the Parser.
Definition types.hpp:98