Skip to main content

RTCMFramer Class

Frame and validate incoming RTCM 3 messages. More...

Declaration

class point_one::rtcm::RTCMFramer { ... }

Included Headers

Public Member Typedefs Index

usingMessageCallback = void(*)(uint16_t, const void *, size_t)

Enumerations Index

enum classState { ... }

Public Constructors Index

RTCMFramer ()=default

Construct a framer instance with no buffer allocated. More...

RTCMFramer (const RTCMFramer &)=delete
RTCMFramer (RTCMFramer &&)=delete
RTCMFramer (size_t capacity_bytes)

Construct a framer instance with an internally allocated buffer. More...

RTCMFramer (void *buffer, size_t capacity_bytes)

Construct a framer instance with a user-specified buffer. More...

Public Destructor Index

~RTCMFramer ()

Public Operators Index

RTCMFramer &operator= (const RTCMFramer &)=delete
RTCMFramer &operator= (RTCMFramer &&)=delete

Public Member Functions Index

uint32_tGetNumDecodedMessages () const

Get the number of decoded messages. More...

uint32_tGetNumErrors () const

Get the number of preamble synchronizations that resulted in errors. More...

size_tOnData (const uint8_t *buffer, size_t length_bytes)

Process incoming data. More...

voidReset ()

Reset the framer and discard all pending data. More...

voidSetBuffer (void *buffer, size_t capacity_bytes)

Set the buffer to use for message framing. More...

voidSetMessageCallback (MessageCallback callback)

Specify a function to be called when a message is framed. More...

voidWarnOnError (bool enabled)

Enable/disable warnings for CRC and "message too large" failures. More...

Private Member Functions Index

voidClearManagedBuffer ()

Free the buffer_ if it's being managed internally. More...

int32_tOnByte (bool quiet)

Process a single byte. More...

uint32_tResync ()

Perform a resynchronization operation starting at buffer_[1]. More...

Private Member Attributes Index

uint8_t *buffer_ {nullptr}
MessageCallbackcallback_ = nullptr
uint32_tcapacity_bytes_ {0}
size_tcurrent_message_size_ {0}
uint32_tdecoded_msg_count_ {0}
uint32_terror_count_ {0}
boolis_buffer_managed_ = false
uint32_tnext_byte_index_ {0}
Statestate_ {State::SYNC}
boolwarn_on_error_ = true

Description

Frame and validate incoming RTCM 3 messages.

This class locates and validates RTCM 3 messages within a stream of binary data. Data may be stored in an internally allocated buffer, or in an external buffer supplied by the user.

The callback function provided to SetMessageCallback() will be called each time a complete message is received. Any messages that do not pass the CRC check, or that are too big to be stored in the data buffer, will be discarded.

Example usage:

 void MessageReceived(uint16_t message_type, const void* data, size_t data_len) {
  ...
 }
 
 framer.SetMessageCallback(MessageReceived);

Definition at line 39 of file rtcm_framer.h.

Public Member Typedefs

MessageCallback

using point_one::rtcm::RTCMFramer::MessageCallback = void (*)(uint16_t, const void*, size_t)

Definition at line 41 of file rtcm_framer.h.

41 using MessageCallback = void (*)(uint16_t, const void*, size_t);

Enumerations

State

enum class point_one::rtcm::RTCMFramer::State
strong
Enumeration values
SYNC (= 0)
HEADER (= 1)
DATA (= 2)

Definition at line 147 of file rtcm_framer.h.

147 enum class State {
148 SYNC = 0,
149 HEADER = 1,
150 DATA = 2,
151 };

Public Constructors

RTCMFramer()

point_one::rtcm::RTCMFramer::RTCMFramer ()
default

Construct a framer instance with no buffer allocated.

info

You must call SetBuffer() to assign a buffer, otherwise all incoming data will be discarded.

Definition at line 50 of file rtcm_framer.h.

RTCMFramer()

point_one::rtcm::RTCMFramer::RTCMFramer (const RTCMFramer &)
delete

Definition at line 75 of file rtcm_framer.h.

RTCMFramer()

point_one::rtcm::RTCMFramer::RTCMFramer (RTCMFramer &&)
delete

Definition at line 76 of file rtcm_framer.h.

RTCMFramer()

point_one::rtcm::RTCMFramer::RTCMFramer (size_t capacity_bytes)
inline explicit

Construct a framer instance with an internally allocated buffer.

Parameters
capacity_bytes

The maximum framing buffer capacity (in bytes).

Definition at line 57 of file rtcm_framer.h.

RTCMFramer()

RTCMFramer::RTCMFramer (void * buffer, size_t capacity_bytes)

Construct a framer instance with a user-specified buffer.

Postcondition

buffer must exist for the lifetime of this instance.

Parameters
buffer

The framing buffer to use. Set to nullptr to allocate a buffer internally.

capacity_bytes

The maximum framing buffer capacity (in bytes).

Declaration at line 70 of file rtcm_framer.h, definition at line 176 of file rtcm_framer.cc.

177 // Allocate a buffer internally.
178 if (buffer == nullptr) {
179 // We enforce 4B alignment, so we need to allocate 3 extra bytes to
180 // guarantee that the buffer has at least the requested capacity.
181 SetBuffer(nullptr, capacity_bytes + 3);
182 }
183 // Use user-provided storage.
184 else {
186 }
187}

Public Destructor

~RTCMFramer()

RTCMFramer::~RTCMFramer ()

Declaration at line 72 of file rtcm_framer.h, definition at line 190 of file rtcm_framer.cc.

Public Operators

operator=()

RTCMFramer & point_one::rtcm::RTCMFramer::operator= (const RTCMFramer &)
delete

Definition at line 77 of file rtcm_framer.h.

operator=()

RTCMFramer & point_one::rtcm::RTCMFramer::operator= (RTCMFramer &&)
delete

Definition at line 78 of file rtcm_framer.h.

Public Member Functions

GetNumDecodedMessages()

uint32_t point_one::rtcm::RTCMFramer::GetNumDecodedMessages ()
inline

Get the number of decoded messages.

Returns

The number of RTCM messages successfully decoded.

Definition at line 133 of file rtcm_framer.h.

133 uint32_t GetNumDecodedMessages() const { return decoded_msg_count_; }

GetNumErrors()

uint32_t point_one::rtcm::RTCMFramer::GetNumErrors ()
inline

Get the number of preamble synchronizations that resulted in errors.

This is not an accurate count of failed messages since the RTCM preamble is not unique and may appear anywhere in the data stream, but gives an approximate count.

Returns

The number of length or CRC failures found in decoding so far.

Definition at line 144 of file rtcm_framer.h.

144 uint32_t GetNumErrors() const { return error_count_; }

OnData()

size_t RTCMFramer::OnData (const uint8_t * buffer, size_t length_bytes)

Process incoming data.

Parameters
buffer

A buffer containing data to be framed.

length_bytes

The number of bytes to be framed.

Returns

The total size of all valid, complete messages, or 0 if no messages were completed.

Declaration at line 126 of file rtcm_framer.h, definition at line 237 of file rtcm_framer.cc.

238 // Process each byte. If the user-supplied buffer was too small, we can't
239 // parse messages.
240 if (buffer_ != nullptr) {
241 VLOG(2) << "Received " << length_bytes << " bytes.";
242 size_t total_dispatched_bytes = 0;
243 for (size_t idx = 0; idx < length_bytes; ++idx) {
244 uint8_t byte = buffer[idx];
247 if (dispatched_message_size == 0) {
248 // Waiting for more data. Nothing to do.
249 } else if (dispatched_message_size > 0) {
250 // Message framed successfully. Reset for the next one.
253 } else if (next_byte_index_ > 0) {
254 // If OnByte() indicated an error (size < 0) and there is still data in
255 // the buffer, either the CRC failed or the payload was too big to fit
256 // in the buffer.
257 //
258 // In either case, it is possible we found what looked like the preamble
259 // somewhere within the data stream but it wasn't actually a valid
260 // message. In that case, the data we processed may contain the start of
261 // a valid message, or even one or more complete messages, starting
262 // somewhere after byte 0 Perform a resync operation to find valid
263 // messages.
265 } else {
266 // OnByte() caught an unrecoverable error and reset the buffer. Nothing
267 // to do.
268 }
269 }
271 } else {
272 return 0;
273 }
274}

Reset()

void RTCMFramer::Reset ()

Reset the framer and discard all pending data.

Declaration at line 115 of file rtcm_framer.h, definition at line 228 of file rtcm_framer.cc.

SetBuffer()

void RTCMFramer::SetBuffer (void * buffer, size_t capacity_bytes)

Set the buffer to use for message framing.

Postcondition

buffer must exist for the lifetime of this instance.

Parameters
buffer

The framing buffer to use. Set to nullptr to allocate a buffer internally.

capacity_bytes

The maximum framing buffer capacity (in bytes).

Declaration at line 90 of file rtcm_framer.h, definition at line 193 of file rtcm_framer.cc.

195 LOG(ERROR) << "RTCM framing buffer too small. [capacity=" << capacity_bytes
196 << " B, min=" << RTCM_OVERHEAD_BYTES << " B]";
197 return;
198 }
199 // Restrict the buffer capacity to 2^31 bytes. We don't expect to ever have a
200 // single message anywhere near that large, and don't expect users to ever
201 // pass in a buffer that size. Using uint32_t instead of size_t internally
202 // makes it easier to guarantee behavior between 64b and 32b architectures. We
203 // do 2^31, not 2^32, so we can use int32_t for return values internally.
204 else if (capacity_bytes > 0x7FFFFFFF) {
205 LOG(WARNING) << "Limiting buffer capacity to 2^31 B. [original_capacity="
206 << capacity_bytes << " B]";
207 capacity_bytes = 0x7FFFFFFF;
208 }
209
211 if (buffer == nullptr) {
213 is_buffer_managed_ = true;
214 }
215
216 // Enforce 4B alignment at the beginning of the buffer.
217 uint8_t* buffer_unaligned = static_cast<uint8_t*>(buffer);
218 buffer_ = reinterpret_cast<uint8_t*>(
219 (reinterpret_cast<size_t>(buffer_unaligned) + 3) &
220 ~(static_cast<size_t>(3)));
223
224 Reset();
225}

SetMessageCallback()

void point_one::rtcm::RTCMFramer::SetMessageCallback (MessageCallback callback)
inline

Specify a function to be called when a message is framed.

Parameters
callback

The function to be called with the message header and a pointer to the message payload.

Definition at line 110 of file rtcm_framer.h.

WarnOnError()

void point_one::rtcm::RTCMFramer::WarnOnError (bool enabled)
inline

Enable/disable warnings for CRC and "message too large" failures.

This is typically used when the incoming stream has multiple types of binary content (e.g., interleaved FusionEngine and RTCM messages), and the RTCM message preamble is expected to appear in the non-RTCM content occasionally.

Parameters
enabled

If true, issue warnings on errors.

Definition at line 102 of file rtcm_framer.h.

102 void WarnOnError(bool enabled) { warn_on_error_ = enabled; }

Private Member Functions

ClearManagedBuffer()

void RTCMFramer::ClearManagedBuffer ()

Free the buffer_ if it's being managed internally.

Declaration at line 191 of file rtcm_framer.h, definition at line 527 of file rtcm_framer.cc.

528 if (is_buffer_managed_ && buffer_ != nullptr) {
529 delete[] buffer_;
530 is_buffer_managed_ = false;
531 buffer_ = nullptr;
532 }
533}

OnByte()

int32_t RTCMFramer::OnByte (bool quiet)

Process a single byte.

Precondition

The byte must be located at buffer_[next_byte_index_ - 1].

Parameters
quiet

If true, suppress failure warning messages.

Returns

The total size of all valid, complete messages, 0 if no messages were completed, or <0 CRC or "message too large" error.

Declaration at line 178 of file rtcm_framer.h, definition at line 277 of file rtcm_framer.cc.

278 // User-supplied buffer was too small. Can't parse messages.
279 if (buffer_ == nullptr) {
280 return 0;
281 }
282
283 // If warnings are disabled, run in quiet mode.
284 if (!warn_on_error_) {
285 quiet = true;
286 }
287
288 // Pull out the byte being processed.
289 if (next_byte_index_ == 0) {
290 LOG(ERROR) << "Byte not found in buffer.";
291 return 0;
292 }
293
295
296 // Look for the first sync byte.
297 //
298 // Note that we always put the first byte at offset 0 in the buffer, and the
299 // buffer is guaranteed to be 4B-aligned by the constructor, so the framed
300 // message will always be 4B aligned.
301 bool crc_check_needed = false;
302 if (state_ == State::SYNC) {
303 VLOG(4) << "Searching for sync byte. [byte=" << HexPrintableInteger(byte)
304 << "]";
305 if (byte == RTCM3_PREAMBLE) {
306 VLOG(4) << "Found sync byte 0.";
308 } else {
310 }
311 }
312 // Search for a message header.
313 else if (state_ == State::HEADER) {
314 VLOG(4) << "Received " << next_byte_index_ << "/" << RTCM_HEADER_BYTES
315 << " header bytes. [byte=" << HexPrintableInteger(byte) << "]";
316
317 // Check if the header is complete.
319 // Compute the full message size. If the message is too large to fit in
320 // the buffer, we cannot parse it. Otherwise, start collecting the
321 // message payload.
322 //
323 // Note that while we compute the current_message_size_ here, we
324 // intentionally do the "too big" check below with the payload size. That
325 // way we implicitly handle cases where the payload is large enough to
326 // cause current_message_size_ to overflow. Normally, this won't happen
327 // for legit packets that are just too big for the user's buffer, but it
328 // could happen on a bogus header if we find the preamble randomly in an
329 // incoming byte stream. The buffer capacity is always
330 // >=RTCM_OVERHEAD_BYTES, so the subtraction will never be negative.
331 /**
332 * Transport header (big endian):
333 * Preamble = 8 bits
334 * Reserved = 6 bits
335 * Message Length = 10 bits
336 */
338 uint16_t payload_size_bytes = header_byte_1_2_le & 0x3FF;
339 current_message_size_ = payload_size_bytes + RTCM_OVERHEAD_BYTES;
340 VLOG(3) << "Header complete. Waiting for payload. [payload_size="
341 << payload_size_bytes << " B]";
345 } else {
347 if (quiet) {
348 VLOG(2) << "Message too large for buffer. [size="
350 << " B (payload=" << payload_size_bytes
351 << " B), buffer_capacity=" << capacity_bytes_
352 << " B (max_payload=" << capacity_bytes_ - RTCM_OVERHEAD_BYTES
353 << " B)]";
354 } else {
355 LOG(WARNING) << "Message too large for buffer. [size="
357 << " B (payload=" << payload_size_bytes
358 << " B), buffer_capacity=" << capacity_bytes_
359 << " B (max_payload="
361 }
362
364 return -1;
365 }
366 }
367 }
368 // Collect the message payload and CRC.
369 else if (state_ == State::DATA) {
370 VLOG(4) << "Received " << next_byte_index_ << "/" << current_message_size_
371 << " message bytes (" << next_byte_index_ << "/"
373 << " payload bytes). [byte=" << HexPrintableInteger(byte) << "]";
374
375 // If we received the full payload, check the CRC and dispatch it.
377 VLOG(3) << "Payload complete. Checking CRC.";
378 crc_check_needed = true;
379 }
380 }
381 // Illegal state.
382 else {
383 LOG(ERROR) << "Impossible parsing state.";
384 Reset();
385 return -1;
386 }
387
388 // Payload complete (or message has no payload). Check the CRC.
389 if (crc_check_needed) {
392 uint16_t message_type = header_byte_3_4_le >> 4;
397 VLOG(1) << "CRC passed. Dispatching message. [message=" << message_type
398 << ", size=" << current_message_size_
399 << " B, crc=" << HexPrintableInteger(crc_expected) << "]";
400 if (callback_) {
402 }
404 return static_cast<int32_t>(current_message_size_);
405 } else {
407 if (quiet) {
408 VLOG(2) << "CRC check failed. [message=" << message_type
409 << ", size=" << current_message_size_
410 << " B, crc=" << HexPrintableInteger(calculated_crc)
411 << ", expected_crc=" << HexPrintableInteger(crc_expected)
412 << "]";
413 } else {
414 LOG(WARNING) << "CRC check failed. [message=" << message_type
415 << ", size=" << current_message_size_
416 << " B, crc=" << HexPrintableInteger(calculated_crc)
417 << ", expected_crc=" << HexPrintableInteger(crc_expected)
418 << "]";
419 }
421 return -1;
422 }
423 }
424
425 // No messages completed.
426 return 0;
427}

Resync()

uint32_t RTCMFramer::Resync ()

Perform a resynchronization operation starting at buffer_[1].

Returns

The total size of all valid, complete messages, or 0 if no messages were completed.

Declaration at line 186 of file rtcm_framer.h, definition at line 430 of file rtcm_framer.cc.

431 // If the message preamble shows up randomly somewhere in the data stream, we
432 // may sync to it and try to parse a message starting at that arbitrary
433 // location. We will eventually detect the bad sync either by CRC failure or
434 // because the payload size we extract from the bogus message header is too
435 // large.
436 //
437 // In either case, the bytes we collected - both the bogus header and the
438 // payload bytes based on the size in the header - may contain the start of a
439 // valid message, or even one or more complete messages. We need to resync and
440 // try to find those messages. Any of the following scenarios is possible:
441 // ...validvalidval <-- Contiguous valid messages
442 // ...valid...valid...val <-- Multiple valid messages, separated by invalid
443 // ...valid...valid... <-- Similar, but ending with invalid
444 // ...val <-- Start of a valid message, no complete messages
445 // ... <-- No valid content
446 //
447 // Ideally, we would search for these messages in-place and avoid shifting the
448 // data around in the buffer. However, since we need the messages to be
449 // 4B-aligned and there's only a 1-in-4 chance of that happening naturally,
450 // we'd have to shift the data 75% of the time regardless.
451 //
452 // Given that, we simply shift all data left in the buffer and process one
453 // message at a time. This is not as efficient, but it's the simplest option.
455 VLOG(1) << "Attempting resynchronization. [" << available_bytes - 1
456 << " candidate bytes]";
462
463 // Skip forward until we see a SYNC0.
464 if (state_ == State::SYNC) {
466 VLOG(1) << "Candidate message start found @ offset " << offset << "/"
467 << available_bytes << ".";
468 // Shift all of the data left in the buffer.
470 std::memmove(buffer_, buffer_ + offset, available_bytes);
471 offset = 0;
472 } else {
473 VLOG(4) << "Skipping non-sync byte 0 @ offset " << offset << "/"
475 << ". [byte=" << HexPrintableInteger(current_byte) << "]";
476 continue;
477 }
478 }
479
480 // Process this byte. If we end up back in the SYNC0 state, either A) the
481 // SYNC0 we found was a valid message and got dispatched, or B) was not the
482 // start of a valid message.
483 //
484 // In (A), message_size > 0 indicating there was a valid message, so we know
485 // we can just keep going with the rest of the data in the buffer.
486 //
487 // In (B), message_size == 0 In that case we'll rewind back to the byte
488 // just after we located the SYNC0 and see if there's another one.
489 //
490 // Note that next_byte_index_ always points to the next open slot, i.e.,
491 // one byte _after_ the current byte.
494
495 if (state_ == State::SYNC) {
496 // Note that offset will be incremented when we loop around, so we set it
497 // to N-1, where N is wherever we want to start searching next.
498 if (message_size > 0) {
500 offset = message_size - 1;
501 VLOG(1)
502 << "Resync found a complete message. Continuing search @ offset "
503 << offset + 1 << "/" << available_bytes
504 << ". [message_size=" << message_size << ", "
506 << " candidate bytes remaining]";
507 } else {
508 size_t prev_offset = offset;
509 offset = 0;
510 VLOG(1) << "Candidate message rejected after " << prev_offset
511 << " bytes. Restarting search @ offset " << offset + 1 << "/"
512 << available_bytes << ". [" << available_bytes - 1
513 << " candidate bytes remaining]";
514 }
515
517 }
518 }
519
520 VLOG(1) << "Resynchronization finished. " << next_byte_index_
521 << " bytes remaining in buffer.";
522
523 return total_message_size;
524}

Private Member Attributes

buffer_

uint8_t* point_one::rtcm::RTCMFramer::buffer_ {nullptr}

Definition at line 157 of file rtcm_framer.h.

157 uint8_t* buffer_{nullptr};

callback_

MessageCallback point_one::rtcm::RTCMFramer::callback_ = nullptr

Definition at line 153 of file rtcm_framer.h.

153 MessageCallback callback_ = nullptr;

capacity_bytes_

uint32_t point_one::rtcm::RTCMFramer::capacity_bytes_ {0}

Definition at line 158 of file rtcm_framer.h.

158 uint32_t capacity_bytes_{0};

current_message_size_

size_t point_one::rtcm::RTCMFramer::current_message_size_ {0}

Definition at line 162 of file rtcm_framer.h.

162 size_t current_message_size_{0};

decoded_msg_count_

uint32_t point_one::rtcm::RTCMFramer::decoded_msg_count_ {0}

Definition at line 165 of file rtcm_framer.h.

165 uint32_t decoded_msg_count_{0};

error_count_

uint32_t point_one::rtcm::RTCMFramer::error_count_ {0}

Definition at line 164 of file rtcm_framer.h.

164 uint32_t error_count_{0};

is_buffer_managed_

bool point_one::rtcm::RTCMFramer::is_buffer_managed_ = false

Definition at line 156 of file rtcm_framer.h.

156 bool is_buffer_managed_ = false;

next_byte_index_

uint32_t point_one::rtcm::RTCMFramer::next_byte_index_ {0}

Definition at line 161 of file rtcm_framer.h.

161 uint32_t next_byte_index_{0};

state_

State point_one::rtcm::RTCMFramer::state_ {State::SYNC}

Definition at line 160 of file rtcm_framer.h.

160 State state_{State::SYNC};

warn_on_error_

bool point_one::rtcm::RTCMFramer::warn_on_error_ = true

Definition at line 155 of file rtcm_framer.h.

155 bool warn_on_error_ = true;

The documentation for this class was generated from the following files:


Generated via doxygen2docusaurus 2.0.0 by Doxygen 1.9.8.