rtcm_framer.cc
Go to the documentation of this file.
1 /**************************************************************************/ /**
2  * @brief RTCM 3 message framer.
3  * @file
4  ******************************************************************************/
5 
6 #define P1_VMODULE_NAME rtcm_framer
7 
8 #include "rtcm_framer.h"
9 
10 #include <cstring> // For memmove()
11 #if P1_HAVE_STD_OSTREAM
12 # include <iomanip>
13 # include <ostream>
14 # include <type_traits>
15 #endif
16 
18 
19 using namespace point_one::rtcm;
20 
21 static constexpr uint8_t RTCM3_PREAMBLE = 0xD3;
22 
23 /**
24  * Transport header:
25  * Preamble = 8 bits
26  * Reserved = 6 bits
27  * Message Length = 10 bits
28  * Variable Length Data Message (not counted)
29  */
30 static constexpr size_t RTCM_HEADER_BYTES = 3;
31 /** Qualcomm 24-bit CRC */
32 static constexpr size_t RTCM_CRC_BYTES = 3;
33 
34 static constexpr size_t RTCM_OVERHEAD_BYTES =
36 
37 static constexpr size_t RTCM_MAX_SIZE_BYTES =
39 
40 static constexpr uint32_t RTCM_CRC24Q[256] = {
41  0x000000, 0x864CFB, 0x8AD50D, 0x0C99F6, 0x93E6E1, 0x15AA1A, 0x1933EC,
42  0x9F7F17, 0xA18139, 0x27CDC2, 0x2B5434, 0xAD18CF, 0x3267D8, 0xB42B23,
43  0xB8B2D5, 0x3EFE2E, 0xC54E89, 0x430272, 0x4F9B84, 0xC9D77F, 0x56A868,
44  0xD0E493, 0xDC7D65, 0x5A319E, 0x64CFB0, 0xE2834B, 0xEE1ABD, 0x685646,
45  0xF72951, 0x7165AA, 0x7DFC5C, 0xFBB0A7, 0x0CD1E9, 0x8A9D12, 0x8604E4,
46  0x00481F, 0x9F3708, 0x197BF3, 0x15E205, 0x93AEFE, 0xAD50D0, 0x2B1C2B,
47  0x2785DD, 0xA1C926, 0x3EB631, 0xB8FACA, 0xB4633C, 0x322FC7, 0xC99F60,
48  0x4FD39B, 0x434A6D, 0xC50696, 0x5A7981, 0xDC357A, 0xD0AC8C, 0x56E077,
49  0x681E59, 0xEE52A2, 0xE2CB54, 0x6487AF, 0xFBF8B8, 0x7DB443, 0x712DB5,
50  0xF7614E, 0x19A3D2, 0x9FEF29, 0x9376DF, 0x153A24, 0x8A4533, 0x0C09C8,
51  0x00903E, 0x86DCC5, 0xB822EB, 0x3E6E10, 0x32F7E6, 0xB4BB1D, 0x2BC40A,
52  0xAD88F1, 0xA11107, 0x275DFC, 0xDCED5B, 0x5AA1A0, 0x563856, 0xD074AD,
53  0x4F0BBA, 0xC94741, 0xC5DEB7, 0x43924C, 0x7D6C62, 0xFB2099, 0xF7B96F,
54  0x71F594, 0xEE8A83, 0x68C678, 0x645F8E, 0xE21375, 0x15723B, 0x933EC0,
55  0x9FA736, 0x19EBCD, 0x8694DA, 0x00D821, 0x0C41D7, 0x8A0D2C, 0xB4F302,
56  0x32BFF9, 0x3E260F, 0xB86AF4, 0x2715E3, 0xA15918, 0xADC0EE, 0x2B8C15,
57  0xD03CB2, 0x567049, 0x5AE9BF, 0xDCA544, 0x43DA53, 0xC596A8, 0xC90F5E,
58  0x4F43A5, 0x71BD8B, 0xF7F170, 0xFB6886, 0x7D247D, 0xE25B6A, 0x641791,
59  0x688E67, 0xEEC29C, 0x3347A4, 0xB50B5F, 0xB992A9, 0x3FDE52, 0xA0A145,
60  0x26EDBE, 0x2A7448, 0xAC38B3, 0x92C69D, 0x148A66, 0x181390, 0x9E5F6B,
61  0x01207C, 0x876C87, 0x8BF571, 0x0DB98A, 0xF6092D, 0x7045D6, 0x7CDC20,
62  0xFA90DB, 0x65EFCC, 0xE3A337, 0xEF3AC1, 0x69763A, 0x578814, 0xD1C4EF,
63  0xDD5D19, 0x5B11E2, 0xC46EF5, 0x42220E, 0x4EBBF8, 0xC8F703, 0x3F964D,
64  0xB9DAB6, 0xB54340, 0x330FBB, 0xAC70AC, 0x2A3C57, 0x26A5A1, 0xA0E95A,
65  0x9E1774, 0x185B8F, 0x14C279, 0x928E82, 0x0DF195, 0x8BBD6E, 0x872498,
66  0x016863, 0xFAD8C4, 0x7C943F, 0x700DC9, 0xF64132, 0x693E25, 0xEF72DE,
67  0xE3EB28, 0x65A7D3, 0x5B59FD, 0xDD1506, 0xD18CF0, 0x57C00B, 0xC8BF1C,
68  0x4EF3E7, 0x426A11, 0xC426EA, 0x2AE476, 0xACA88D, 0xA0317B, 0x267D80,
69  0xB90297, 0x3F4E6C, 0x33D79A, 0xB59B61, 0x8B654F, 0x0D29B4, 0x01B042,
70  0x87FCB9, 0x1883AE, 0x9ECF55, 0x9256A3, 0x141A58, 0xEFAAFF, 0x69E604,
71  0x657FF2, 0xE33309, 0x7C4C1E, 0xFA00E5, 0xF69913, 0x70D5E8, 0x4E2BC6,
72  0xC8673D, 0xC4FECB, 0x42B230, 0xDDCD27, 0x5B81DC, 0x57182A, 0xD154D1,
73  0x26359F, 0xA07964, 0xACE092, 0x2AAC69, 0xB5D37E, 0x339F85, 0x3F0673,
74  0xB94A88, 0x87B4A6, 0x01F85D, 0x0D61AB, 0x8B2D50, 0x145247, 0x921EBC,
75  0x9E874A, 0x18CBB1, 0xE37B16, 0x6537ED, 0x69AE1B, 0xEFE2E0, 0x709DF7,
76  0xF6D10C, 0xFA48FA, 0x7C0401, 0x42FA2F, 0xC4B6D4, 0xC82F22, 0x4E63D9,
77  0xD11CCE, 0x575035, 0x5BC9C3, 0xDD8538};
78 
79 /******************************************************************************/
80 static uint32_t CRC24Hash(const uint8_t* data, size_t len) {
81  size_t i;
82  unsigned crc = 0;
83 
84  for (i = 0; i < len; i++) {
85  crc = (crc << 8) ^ RTCM_CRC24Q[data[i] ^ (unsigned char)(crc >> 16)];
86  }
87 
88  crc = (crc & 0x00ffffff);
89  return crc;
90 }
91 
92 // Note: Assuming we're on a little endian system.
93 
94 /******************************************************************************/
95 static constexpr uint16_t EndianSwap16(const uint8_t* num_ptr) {
96  return (num_ptr[0] << 8) | (num_ptr[1] << 0);
97 }
98 
99 /******************************************************************************/
100 static constexpr uint32_t EndianSwap24(const uint8_t* num_ptr) {
101  return (num_ptr[0] << 16) | (num_ptr[1] << 8) | (num_ptr[2] << 0);
102 }
103 
104 /******************************************************************************/
105 template <typename T>
107  public:
108  HexPrintableIntegerInst(T value) : value_(value) {}
109 
110  template <typename U> // all instantiations of this template are my friends
112 
113  private:
114  const T value_;
115 };
116 
117 /******************************************************************************/
118 template <typename T>
120  const HexPrintableIntegerInst<T>& obj) {
121 #if P1_HAVE_STD_OSTREAM
122  static_assert(std::is_integral<T>::value, "Integer required.");
123 
124  stream << "0x" << std::hex << std::setfill('0') << std::setw(sizeof(obj) * 2);
125 
126  if (sizeof(T) == 1) {
127  stream << (((unsigned)obj.value_) & 0xFF);
128  } else {
129  stream << obj.value_;
130  }
131 
132  stream << std::dec;
133 
134  if (sizeof(obj) == 1) {
135  if (obj.value_ >= 0x20 && obj.value_ <= 0x7E) {
136  stream << " ('" << (char)obj.value_ << "')";
137  } else {
138  stream << " (---)";
139  }
140  }
141 #endif
142  return stream;
143 }
144 
145 /**
146  * @brief Wrap an integer so it will be output to a stream as its hex
147  * representation.
148  *
149  * For example:
150  *
151  * ```cpp
152  * std::cout << HexPrintableValue((int16_t)-255) << std::endl;
153  * std::cout << HexPrintableValue((uint32_t)255) << std::endl;
154  * std::cout << HexPrintableValue((uint8_t)48) << std::endl;
155  * ```
156  *
157  * generates the following output:
158  *
159  * ```
160  * 0xff01
161  * 0x000000ff
162  * 0x30 ('0')
163  * ```
164  *
165  * @tparam T The type of the value parameter (inferred implicitly).
166  * @param value The integer value to wrap.
167  *
168  * @return The wrapped integer that can be used in an @ref p1_ostream.
169  */
170 template <typename T>
172  return HexPrintableIntegerInst<T>(value);
173 }
174 
175 /******************************************************************************/
176 RTCMFramer::RTCMFramer(void* buffer, size_t capacity_bytes) {
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 {
185  SetBuffer(buffer, capacity_bytes);
186  }
187 }
188 
189 /******************************************************************************/
191 
192 /******************************************************************************/
193 void RTCMFramer::SetBuffer(void* buffer, size_t capacity_bytes) {
194  if (capacity_bytes < RTCM_OVERHEAD_BYTES) {
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) {
212  buffer = new uint8_t[capacity_bytes];
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)));
222  static_cast<uint32_t>(capacity_bytes - (buffer_ - buffer_unaligned));
223 
224  Reset();
225 }
226 
227 /******************************************************************************/
230  next_byte_index_ = 0;
232  error_count_ = 0;
233  decoded_msg_count_ = 0;
234 }
235 
236 /******************************************************************************/
237 size_t RTCMFramer::OnData(const uint8_t* buffer, size_t length_bytes) {
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];
245  buffer_[next_byte_index_++] = byte;
246  int32_t dispatched_message_size = OnByte(false);
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.
251  next_byte_index_ = 0;
252  total_dispatched_bytes += (size_t)dispatched_message_size;
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.
264  total_dispatched_bytes += Resync();
265  } else {
266  // OnByte() caught an unrecoverable error and reset the buffer. Nothing
267  // to do.
268  }
269  }
270  return total_dispatched_bytes;
271  } else {
272  return 0;
273  }
274 }
275 
276 /******************************************************************************/
277 int32_t RTCMFramer::OnByte(bool quiet) {
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 
294  uint8_t byte = buffer_[next_byte_index_ - 1];
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  */
337  uint16_t header_byte_1_2_le = EndianSwap16(buffer_ + 1);
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 {
346  error_count_++;
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="
360  << capacity_bytes_ - RTCM_OVERHEAD_BYTES << " B)]";
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) {
390  size_t check_size = current_message_size_ - RTCM_CRC_BYTES;
391  uint16_t header_byte_3_4_le = EndianSwap16(buffer_ + RTCM_HEADER_BYTES);
392  uint16_t message_type = header_byte_3_4_le >> 4;
393  uint32_t calculated_crc = CRC24Hash(buffer_, check_size);
394  uint32_t crc_expected = EndianSwap24(buffer_ + check_size);
395  if (calculated_crc == crc_expected) {
397  VLOG(1) << "CRC passed. Dispatching message. [message=" << message_type
398  << ", size=" << current_message_size_
399  << " B, crc=" << HexPrintableInteger(crc_expected) << "]";
400  if (callback_) {
401  callback_(message_type, buffer_, current_message_size_);
402  }
404  return static_cast<int32_t>(current_message_size_);
405  } else {
406  error_count_++;
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 }
428 
429 /******************************************************************************/
430 uint32_t RTCMFramer::Resync() {
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.
454  uint32_t available_bytes = next_byte_index_;
455  VLOG(1) << "Attempting resynchronization. [" << available_bytes - 1
456  << " candidate bytes]";
457  uint32_t total_message_size = 0;
459  next_byte_index_ = 0;
460  for (uint32_t offset = 1; offset < available_bytes; ++offset) {
461  uint8_t current_byte = buffer_[offset];
462 
463  // Skip forward until we see a SYNC0.
464  if (state_ == State::SYNC) {
465  if (current_byte == RTCM3_PREAMBLE) {
466  VLOG(1) << "Candidate message start found @ offset " << offset << "/"
467  << available_bytes << ".";
468  // Shift all of the data left in the buffer.
469  available_bytes -= offset;
470  std::memmove(buffer_, buffer_ + offset, available_bytes);
471  offset = 0;
472  } else {
473  VLOG(4) << "Skipping non-sync byte 0 @ offset " << offset << "/"
474  << available_bytes
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.
492  next_byte_index_ = offset + 1;
493  int32_t message_size = OnByte(true);
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) {
499  total_message_size += message_size;
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 << ", "
505  << (available_bytes - message_size - 1)
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 
516  next_byte_index_ = 0;
517  }
518  }
519 
520  VLOG(1) << "Resynchronization finished. " << next_byte_index_
521  << " bytes remaining in buffer.";
522 
523  return total_message_size;
524 }
525 
526 /******************************************************************************/
528  if (is_buffer_managed_ && buffer_ != nullptr) {
529  delete[] buffer_;
530  is_buffer_managed_ = false;
531  buffer_ = nullptr;
532  }
533 }
void Reset()
Reset the framer and discard all pending data.
Definition: rtcm_framer.cc:228
static constexpr size_t RTCM_HEADER_BYTES
Transport header: Preamble = 8 bits Reserved = 6 bits Message Length = 10 bits Variable Length Data M...
Definition: rtcm_framer.cc:30
void ClearManagedBuffer()
Free the buffer_ if it's being managed internally.
Definition: rtcm_framer.cc:527
static constexpr size_t RTCM_CRC_BYTES
Qualcomm 24-bit CRC.
Definition: rtcm_framer.cc:32
State state_
Definition: rtcm_framer.h:160
~RTCMFramer()
Definition: rtcm_framer.cc:190
static constexpr uint32_t RTCM_CRC24Q[256]
Definition: rtcm_framer.cc:40
Definition: rtcm_framer.h:14
#define VLOG(verboselevel)
Definition: logging.h:88
static constexpr uint32_t EndianSwap24(const uint8_t *num_ptr)
Definition: rtcm_framer.cc:100
@ DATA
uint32_t Resync()
Perform a resynchronization operation starting at buffer_[1].
Definition: rtcm_framer.cc:430
uint8_t * buffer_
Definition: rtcm_framer.h:157
size_t OnData(const uint8_t *buffer, size_t length_bytes)
Process incoming data.
Definition: rtcm_framer.cc:237
uint32_t next_byte_index_
Definition: rtcm_framer.h:161
static constexpr size_t RTCM_OVERHEAD_BYTES
Definition: rtcm_framer.cc:34
p1_ostream & operator<<(p1_ostream &stream, const HexPrintableIntegerInst< T > &obj)
Definition: rtcm_framer.cc:119
MessageCallback callback_
Definition: rtcm_framer.h:153
uint32_t decoded_msg_count_
Definition: rtcm_framer.h:165
RTCM 3 message framer.
HexPrintableIntegerInst(T value)
Definition: rtcm_framer.cc:108
RTCMFramer()=default
Construct a framer instance with no buffer allocated.
size_t current_message_size_
Definition: rtcm_framer.h:162
static constexpr uint16_t EndianSwap16(const uint8_t *num_ptr)
Definition: rtcm_framer.cc:95
HexPrintableIntegerInst< T > HexPrintableInteger(T value)
Wrap an integer so it will be output to a stream as its hex representation.
Definition: rtcm_framer.cc:171
static constexpr size_t RTCM_MAX_SIZE_BYTES
Definition: rtcm_framer.cc:37
std::ostream p1_ostream
Definition: portability.h:75
friend p1_ostream & operator<<(p1_ostream &, const HexPrintableIntegerInst< U > &)
@ HEADER
const T value_
static uint32_t CRC24Hash(const uint8_t *data, size_t len)
Definition: rtcm_framer.cc:80
uint32_t error_count_
Definition: rtcm_framer.h:164
#define LOG(severity)
Definition: logging.h:77
API wrapper for optional compilation of logging support.
bool warn_on_error_
Definition: rtcm_framer.h:155
uint32_t capacity_bytes_
Definition: rtcm_framer.h:158
bool is_buffer_managed_
Definition: rtcm_framer.h:156
static constexpr uint8_t RTCM3_PREAMBLE
Definition: rtcm_framer.cc:21
int32_t OnByte(bool quiet)
Process a single byte.
Definition: rtcm_framer.cc:277
@ SYNC
void SetBuffer(void *buffer, size_t capacity_bytes)
Set the buffer to use for message framing.
Definition: rtcm_framer.cc:193