Fixposition SDK 0.0.0-heads/main-0-g90a51ff
Collection of c++ libraries and apps for use with Fixposition products
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_SPARTN
41 * - @subpage FPSDK_COMMON_PARSER_UBX
42 * - @subpage FPSDK_COMMON_PARSER_UNIB
43 *
44 * @section FPSDK_COMMON_PARSER_USAGE Usage
45 *
46 * The usage of the parser is best explained in source code:
47 *
48 * @code{cpp}
49 * #include <cstdint>
50 * #include <ifstream>
51 *
52 * #include <fpsdk_common/logging.hpp>
53 * #include <fpsdk_common/parser.hpp>
54 *
55 * using namespace fpsdk::common::parser;
56 *
57 * std::ifstream input("some_logfile.bin", std::ios::binary);
58 * Parser parser;
59 * ParserMsg msg;
60 *
61 * while (input.good()) {
62 *
63 * // Read a chunk of data from the logfile
64 * uint8_t data[MAX_ADD_SIZE];
65 * const std::size_t size = input.readsome((char*)data, sizeof(data));
66 *
67 * // No more data from file
68 * if (size <= 0) {
69 * break;
70 * }
71 *
72 * // Add the chunk of data to the parser
73 * if (!parser.Add(data, size)) {
74 * // We added too much data! This will not happen if we don't add more than MAX_ANY_SIZE at a time.
75 * WARNING(("Parser overflow!");
76 * parser.Reset();
77 * continue;
78 * }
79 *
80 * // Run parser and print message name and size to the screen
81 * while (parser.Process(msg)) {
82 * msg.MakeInfo(); // Try to populate msg.info_
83 * INFO("Message %s, size %d (%s)", msg.name_.c_str(), (int)msg.data_.size(), msg.info_.c_str());
84 * }
85 * }
86 *
87 * // There may be some remaining data in the parser.
88 * // If there is anytghing, we should see only type OTHER "messages" here.
89 * while (parser.Flush(msg)) {
90 * msg.MakeInfo();
91 * INFO("Message %s, size %d", msg.name_.c_str(), (int)msg.data_.size(), msg.info_.c_str());
92 * }
93 * @endcode
94 *
95 * For a more sophisticated example app see the @ref FPSDK_APPS_PARSERTOOL (fpsdk_apps/parsertool/parsertool.cpp).
96 *
97 * @section FPSDK_COMMON_PARSER_NAMING Protocol and message naming
98 *
99 * The protocols names are defined in #fpsdk::common::parser::Protocol and can be stringified using
100 * fpsdk::common::parser::ProtocolStr(). The names must match `/^[A-Z][A-Z0-9_]{2,5}$/.
101 *
102 * The message naming scheme consists of words separated by dashes. The first word is always the protocol name.
103 * Depending on the protocol one or two more words are added. All words must match `/^[A-Z][A-Z0-9]{2,9}$/`.
104 *
105 * Examples:
106 *
107 * - **FP_A**-NAME, e.g. `FP_A-ODOMETRY`, `FP_A-RAWIMU`. See fpsdk::common::parser::nmea::NmeaGetMessageName() for
108 * details.
109 * - **FP_B**-NAME, e.g. `FP_B-SYSTEMSTATUS`, `FP_B-GNSSSTATUS`. See fpsdk::common::parser::fpb::FpbGetMessageName() for
110 * details.
111 * - **NMEA**-TALKER-FORMATTER, e.g. `NMEA-GN-GGA`, `NMEA-GN-RMC`. See fpsdk::common::parser::nmea::NmeaGetMessageName()
112 * for details.
113 * - **UBX**-CLASS-MESSAGE, e.g. `UBX-NAV-PVT`, `UBX-RXM-RAWX`. See fpsdk::common::parser::ubx::UbxGetMessageName() for
114 * details.
115 * - **RTCM3**-TYPENNNN, e.g. `RTCM3-TYPE1234`. See fpsdk::common::parser::rtcm3::Rtcm3GetMessageName() for details.
116 * - **UNI_B**-NAME, .e.g. `UNI_B-VERSION`, `UNI_B-BESTNAV`. See fpsdk::common::parser::unib::UnibGetMessageName() for
117 * details.
118 * - **NOV_B**-NAME, .e.g. `NOV_B-BESTGNSSPOS`, `NOV_B-RAWDMI`. See fpsdk::common::parser::novb::NovbGetMessageName()
119 * for details.
120 * - **SPARTN**-TYPE-SUBTYPE, e.g. `SPARTN-OCB-GPS`. See fpsdk::common::parser::spartn::SpartnGetMessageName() for
121 * details.
122 * - **OTHER** (there are no individual messages in this "protocol")
123 *
124 * @section FPSDK_COMMON_PARSER_DESIGN Design
125 *
126 * @image html parser.drawio.svg ""
127 *
128 * The parser implementation does not use any STL containers, templates or other c++ conveniences. Instead, low-level
129 * c-like types are used, in order to prevent any expensive heap (re)allocation and too much copying of data. For
130 * example, the returned ParserMsg contains a pointer to message rather than a std::vector<uint8_t>, and a const
131 * char* message name instead of a std::string. Applications can convert (copy) these easily to std::vector resp.
132 * std::string for further processing. Some of the utilities, such as fpsdk::common::parser::ubx::UbxMakeMessage(), do
133 * use STL containers for convenience.
134 *
135 * The maximum message size for each protocol is limited. For example, while the FP_B frame meta data would allow for
136 * messages up to 65'536 bytes, the parser discards any message larger than #fpsdk::common::parser::MAX_FP_B_SIZE.
137 * Messages larger than this are not practical and they do not (should not) exist. For example, it would take 5.6
138 * seconds for such a message to transfer over a serial port at baudrate 115'200.
139 *
140 */
141#ifndef __FPSDK_COMMON_PARSER_HPP__
142#define __FPSDK_COMMON_PARSER_HPP__
143
144/* LIBC/STL */
145#include <cstdint>
146#include <vector>
147
148/* EXTERNAL */
149
150/* PACKAGE */
151#include "parser/types.hpp"
152
153namespace fpsdk {
154namespace common {
155/**
156 * @brief Parser
157 */
158namespace parser {
159/* ****************************************************************************************************************** */
160
161/**
162 * @brief Message parser class
163 */
165{
166 public:
167 //
168 // ----- Constructor -----------------------------------------------------------------------------------------------
169
170 /**
171 * @brief Constructor, initialises parser and makes it ready to accept data and emit messages
172 */
174
175 //
176 // ----- Input data ------------------------------------------------------------------------------------------------
177
178 /**
179 * @brief Add data to parser
180 *
181 * @param[in] data Pointer to data
182 * @param[in] size Size of data (should be <= #MAX_ADD_SIZE)
183 *
184 * @returns true if data was added to the parser, 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, 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__
Message parser class.
Definition parser.hpp:165
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:145
static constexpr std::size_t MAX_ANY_SIZE
The largest of the above.
Definition types.hpp:157
Fixposition SDK.
Fixposition SDK: Parser.
Message frame output by the Parser.
Definition types.hpp:96