novatel_message_extractor.cpp
Go to the documentation of this file.
1 // *****************************************************************************
2 //
3 // Copyright (c) 2017, Southwest Research Institute® (SwRI®)
4 // All rights reserved.
5 //
6 // Redistribution and use in source and binary forms, with or without
7 // modification, are permitted provided that the following conditions are met:
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of Southwest Research Institute® (SwRI®) nor the
14 // names of its contributors may be used to endorse or promote products
15 // derived from this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL SOUTHWEST RESEARCH INSTITUTE BE LIABLE FOR ANY
21 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 //
28 // *****************************************************************************
29 
31 
32 #include <limits>
33 #include <sstream>
34 
35 #include <boost/algorithm/string/split.hpp>
36 #include <boost/algorithm/string/classification.hpp>
37 
38 #include <ros/ros.h>
39 
44 
45 namespace novatel_gps_driver
46 {
47  const std::string NovatelMessageExtractor::CHECKSUM_FLAG = "*";
48  const std::string NovatelMessageExtractor::FIELD_SEPARATOR = ",";
49  const std::string NovatelMessageExtractor::HEADER_SEPARATOR = ";";
50  const std::string NovatelMessageExtractor::NMEA_SENTENCE_FLAG = "$";
51  const std::string NovatelMessageExtractor::NOVATEL_SENTENCE_FLAG = "#";
52  const std::string NovatelMessageExtractor::NOVATEL_ASCII_FLAGS = "$#";
53  const std::string NovatelMessageExtractor::NOVATEL_BINARY_SYNC_BYTES = "\xAA\x44\x12";
54  const std::string NovatelMessageExtractor::NOVATEL_ENDLINE = "\r\n";
55 
57  {
58  int32_t j;
59  uint32_t ulCRC;
60  ulCRC = static_cast<uint32_t>(i);
61  for ( j = 8 ; j > 0; j-- )
62  {
63  if ( ulCRC & 1 )
64  ulCRC = static_cast<uint32_t>(( ulCRC >> 1 ) ^ NOVATEL_CRC32_POLYNOMIAL);
65  else
66  ulCRC >>= 1;
67  }
68  return ulCRC;
69  }
70 
72  uint32_t ulCount, // Number of bytes in the data block
73  const uint8_t* ucBuffer ) // Data block
74  {
75  uint32_t ulTemp1;
76  uint32_t ulTemp2;
77  uint32_t ulCRC = 0;
78  while ( ulCount-- != 0 )
79  {
80  ulTemp1 = static_cast<uint32_t>(( ulCRC >> 8 ) & 0x00FFFFFFL);
81  ulTemp2 = CRC32Value( ((int32_t) ulCRC ^ *ucBuffer++ ) & 0xff );
82  ulCRC = ulTemp1 ^ ulTemp2;
83  }
84  return( ulCRC );
85  }
86 
87  uint8_t NovatelMessageExtractor::NmeaChecksum(const std::string& sentence)
88  {
89  uint8_t checksum = 0;
90  std::string::const_iterator it = sentence.begin();
91  for (; it != sentence.end(); ++it)
92  {
93  checksum ^= *it;
94  }
95  return checksum;
96  }
97 
98  size_t NovatelMessageExtractor::GetSentenceChecksumStart(const std::string& str, size_t start_idx)
99  {
100  return str.find(CHECKSUM_FLAG, start_idx);
101  }
102 
104  const std::string& str,
105  std::vector<std::string>& vectorized_message,
106  const std::string& delimiters)
107  {
108  boost::algorithm::split(vectorized_message, str, boost::algorithm::is_any_of(delimiters));
109  }
110 
112  const std::string& sentence,
113  std::string& message_id,
114  std::vector<std::string>& header,
115  std::vector<std::string>& body)
116  {
117  message_id.clear();
118  header.clear();
119  body.clear();
120 
121  std::vector<std::string> vectorized_message;
122  VectorizeString(sentence, vectorized_message, HEADER_SEPARATOR);
123 
124  if (vectorized_message.size() != 2)
125  {
126  return false;
127  }
128 
129  VectorizeString(vectorized_message[0], header, FIELD_SEPARATOR);
130 
131  VectorizeString(vectorized_message[1], body, FIELD_SEPARATOR);
132 
133  if (!header.empty())
134  {
135  message_id = header.front();
136  }
137  else
138  {
139  return false;
140  }
141  return true;
142  }
143 
144  int32_t NovatelMessageExtractor::GetBinaryMessage(const std::string& str,
145  size_t start_idx,
146  BinaryMessage& msg)
147  {
148  if (str.length() < HeaderParser::BINARY_HEADER_LENGTH + 4)
149  {
150  // The shortest a binary message can be (header + no data + CRC)
151  // is 32 bytes, so just return if we don't have at least that many.
152  ROS_DEBUG("Binary message was too short to parse.");
153  return -1;
154  }
155 
156  ROS_DEBUG("Reading binary header.");
157  msg.header_.ParseHeader(reinterpret_cast<const uint8_t*>(&str[start_idx]));
158  uint16_t data_start = static_cast<uint16_t>(msg.header_.header_length_ + start_idx);
159  uint16_t data_length = msg.header_.message_length_;
160 
161  if (msg.header_.sync0_ != static_cast<uint8_t>(NOVATEL_BINARY_SYNC_BYTES[0]) ||
162  msg.header_.sync1_ != static_cast<uint8_t>(NOVATEL_BINARY_SYNC_BYTES[1]) ||
163  msg.header_.sync2_ != static_cast<uint8_t>(NOVATEL_BINARY_SYNC_BYTES[2]))
164  {
165  ROS_ERROR("Sync bytes were incorrect; this should never happen and is definitely a bug: %x %x %x",
166  msg.header_.sync0_, msg.header_.sync1_, msg.header_.sync2_);
167  return -2;
168  }
169 
171  {
172  ROS_WARN("Binary header length was unexpected: %u (expected %u)",
174  }
175 
176  ROS_DEBUG("Msg ID: %u Data start / length: %u / %u",
177  msg.header_.message_id_, data_start, data_length);
178 
179  if (data_start + data_length + 4 > str.length())
180  {
181  ROS_DEBUG("Not enough data.");
182  return -1;
183  }
184 
185  ROS_DEBUG("Reading binary message data.");
186  msg.data_.resize(data_length);
187  std::copy(&str[data_start], &str[data_start+data_length], reinterpret_cast<char*>(&msg.data_[0]));
188 
189  ROS_DEBUG("Calculating CRC.");
190 
191  uint32_t crc = CalculateBlockCRC32(static_cast<uint32_t>(msg.header_.header_length_) + data_length,
192  reinterpret_cast<const uint8_t*>(&str[start_idx]));
193 
194  ROS_DEBUG("Reading CRC.");
195  msg.crc_ = ParseUInt32(reinterpret_cast<const uint8_t*>(&str[data_start+data_length]));
196 
197  if (crc != msg.crc_)
198  {
199  // Invalid CRC
200  ROS_DEBUG("Invalid CRC; Calc: %u In msg: %u", crc, msg.crc_);
201  return -2;
202  }
203 
204  // On success, return how many bytes we read
205 
206  ROS_DEBUG("Finishing reading binary message.");
207  return static_cast<int32_t>(msg.header_.header_length_ + data_length + 4);
208  }
209 
211  const std::string& str,
212  size_t start_idx,
213  std::string& sentence)
214  {
215  sentence.clear();
216 
217  size_t checksum_start = GetSentenceChecksumStart(str, start_idx);
218  if (checksum_start == std::string::npos)
219  {
220  // Sentence not complete. Just return.
221  return -1;
222  }
223  else if (checksum_start + 8 >= str.size())
224  {
225  // Sentence not complete. Just return.
226  return -1;
227  }
228  else
229  {
230  // Compare the checksums
231  sentence = str.substr(start_idx + 1, checksum_start - start_idx - 1);
232  std::string checksum_str = str.substr(checksum_start + 1, 8);
233  uint64_t checksum = std::strtoul(checksum_str.c_str(), 0, 16);
234  uint64_t calculated_checksum = CalculateBlockCRC32(
235  static_cast<uint32_t>(sentence.size()),
236  reinterpret_cast<const uint8_t*>(sentence.c_str()));
237 
238  if (checksum == ULONG_MAX)
239  {
240  // Invalid checksum -- strtoul failed
241  return 1;
242  }
243  else if(static_cast<uint32_t>(checksum) == calculated_checksum)
244  {
245  return 0;
246  }
247  else
248  {
249  ROS_WARN("Expected checksum: [%lx]", calculated_checksum);
250  // Invalid checksum
251  return 1;
252  }
253  }
254  }
255 
257  const std::string& str,
258  size_t start_idx,
259  std::string& sentence,
260  bool keep_container)
261  {
262  sentence.clear();
263 
264  size_t checksum_start = GetSentenceChecksumStart(str, start_idx);
265  if (checksum_start == std::string::npos)
266  {
267  // Sentence not complete. Just return.
268  return -1;
269  }
270  else if (checksum_start + 2 >= str.size())
271  {
272  // Sentence not complete. Just return.
273  return -1;
274  }
275  else
276  {
277  // Compare the checksums
278  sentence = str.substr(start_idx + 1, checksum_start - start_idx - 1);
279  std::string checksum_str = str.substr(checksum_start + 1, 2);
280  uint64_t checksum = std::strtoul(checksum_str.c_str(), 0, 16);
281  uint64_t calculated_checksum = NmeaChecksum(sentence);
282  if (checksum == ULONG_MAX)
283  {
284  // Invalid checksum
285  return 1;
286  }
287  else if(static_cast<uint32_t>(checksum) == calculated_checksum)
288  {
289  if (keep_container)
290  {
291  sentence.insert(0, "$");
292  std::string recreated_checksum_str("*");
293  recreated_checksum_str += checksum_str;
294  sentence.insert(sentence.end(),
295  recreated_checksum_str.begin(),
296  recreated_checksum_str.end());
297  }
298  return 0;
299  }
300  else
301  {
302  ROS_WARN("Expected: [%lx]", calculated_checksum);
303  // Invalid checksum
304  return 1;
305  }
306  }
307  }
308 
309  void NovatelMessageExtractor::FindAsciiSentence(const std::string& sentence,
310  size_t current_idx,
311  size_t& start_idx,
312  size_t& end_idx,
313  size_t& invalid_char_idx)
314  {
315  start_idx = sentence.find_first_of(NOVATEL_ASCII_FLAGS, current_idx);
316  end_idx = std::string::npos;
317  invalid_char_idx = std::string::npos;
318 
319  if (start_idx == std::string::npos)
320  {
321  return;
322  }
323 
324  end_idx = sentence.find(NOVATEL_ENDLINE, start_idx);
325 
326  size_t search_stop_idx = std::min(end_idx, sentence.length());
327  for (size_t i = start_idx; i < search_stop_idx; i++)
328  {
329  if (sentence[i] == 9 || sentence[i] == 10 || sentence[i] == 11 || sentence[i] == 13 ||
330  (sentence[i] >= 32 && sentence[i] <= 126))
331  {
332  continue;
333  }
334 
335  invalid_char_idx = i;
336  break;
337  }
338  }
339 
341  const std::string& data,
342  NovatelSentence& sentence)
343  {
344  return GetNovatelMessageParts(
345  data, sentence.id, sentence.header, sentence.body);
346  }
347 
349  const std::string& sentence,
350  NmeaSentence& vectorized_message)
351  {
352  VectorizeString(sentence, vectorized_message.body, FIELD_SEPARATOR);
353  if (!vectorized_message.body.empty())
354  {
355  vectorized_message.id = vectorized_message.body.front();
356  }
357  }
358 
360  const std::string input,
361  std::vector<NmeaSentence>& nmea_sentences,
362  std::vector<NovatelSentence>& novatel_sentences,
363  std::vector<BinaryMessage>& binary_messages,
364  std::string& remaining,
365  bool keep_nmea_container)
366  {
367  bool parse_error = false;
368 
369  size_t sentence_start = 0;
370  while(sentence_start != std::string::npos && sentence_start < input.size())
371  {
372  size_t ascii_start_idx;
373  size_t ascii_end_idx;
374  size_t invalid_ascii_idx;
375  size_t binary_start_idx = input.find(NOVATEL_BINARY_SYNC_BYTES, sentence_start);
376 
377  FindAsciiSentence(input, sentence_start, ascii_start_idx, ascii_end_idx, invalid_ascii_idx);
378 
379  ROS_DEBUG("Binary start: %lu ASCII start / end / invalid: %lu / %lu / %lu",
380  binary_start_idx, ascii_start_idx, ascii_end_idx, invalid_ascii_idx);
381 
382  if (binary_start_idx == std::string::npos && ascii_start_idx == std::string::npos)
383  {
384  // If we don't see either a binary or an ASCII message, just give up.
385  break;
386  }
387 
388  if (ascii_start_idx == std::string::npos ||
389  (binary_start_idx != std::string::npos && binary_start_idx < ascii_start_idx))
390  {
391  // If we see a binary header or if there's both a binary and ASCII header but
392  // the binary one comes first, try to parse it.
393  BinaryMessage cur_msg;
394  int32_t result = GetBinaryMessage(input, binary_start_idx, cur_msg);
395  if (result > 0)
396  {
397  binary_messages.push_back(cur_msg);
398  sentence_start += binary_start_idx + result;
399  ROS_DEBUG("Parsed a binary message with %u bytes.", result);
400  }
401  else if (result == -1)
402  {
403  // Sentence is not complete, add it to the remaining and break;
404  remaining = input.substr(binary_start_idx);
405  ROS_DEBUG("Binary message was incomplete, waiting for more.");
406  break;
407  }
408  else
409  {
410  // Sentence had an invalid checksum, just iterate to the next sentence
411  sentence_start += 1;
412  ROS_WARN("Invalid binary message checksum");
413  parse_error = true;
414  }
415  }
416  else
417  {
418  // If we saw the beginning of an ASCII message, try to parse it.
419  size_t ascii_len = ascii_end_idx - ascii_start_idx;
420  if (invalid_ascii_idx != std::string::npos)
421  {
422  // If we see an invalid character, don't even bother trying to parse
423  // the rest of the message. By this point we also know there's no
424  // binary header before this point, so just skip the data.
425  ROS_WARN("Invalid ASCII char: [%s]", input.substr(ascii_start_idx, ascii_len).c_str());
426  ROS_WARN(" %s^", std::string(invalid_ascii_idx-ascii_start_idx-1, ' ').c_str());
427  sentence_start += invalid_ascii_idx + 1;
428  }
429  else if (ascii_end_idx != std::string::npos)
430  {
431  // If we've got a start, an end, and no invalid characters, we've
432  // got a valid ASCII message.
433  ROS_DEBUG("ASCII sentence:\n[%s]", input.substr(ascii_start_idx, ascii_len).c_str());
434  if (input[ascii_start_idx] == NMEA_SENTENCE_FLAG[0])
435  {
436  std::string cur_sentence;
437  int32_t result = GetNmeaSentence(
438  input,
439  ascii_start_idx,
440  cur_sentence,
441  keep_nmea_container);
442  if (result == 0)
443  {
444  nmea_sentences.push_back(NmeaSentence());
445  VectorizeNmeaSentence(cur_sentence, nmea_sentences.back());
446  sentence_start = ascii_end_idx;
447  }
448  else if (result < 0)
449  {
450  // Sentence is not complete, add it to the remaining and break.
451  // This is legacy code from before FindAsciiSentence was implemented,
452  // and it will probably never happen, but it doesn't hurt anything to
453  // have it here.
454  remaining = input.substr(ascii_start_idx);
455  ROS_DEBUG("Waiting for more NMEA data.");
456  break;
457  }
458  else
459  {
460  ROS_WARN("Invalid NMEA checksum for: [%s]",
461  input.substr(ascii_start_idx, ascii_len).c_str());
462  // Sentence had an invalid checksum, just iterate to the next sentence
463  sentence_start += 1;
464  parse_error = true;
465  }
466  }
467  else if (input[ascii_start_idx] == NOVATEL_SENTENCE_FLAG[0])
468  {
469  std::string cur_sentence;
470  int32_t result = GetNovatelSentence(input, ascii_start_idx, cur_sentence);
471  if (result == 0)
472  {
473  // Send to parser for testing:
474  novatel_sentences.push_back(NovatelSentence());
475  if (!VectorizeNovatelSentence(cur_sentence, novatel_sentences.back()))
476  {
477  novatel_sentences.pop_back();
478  parse_error = true;
479  ROS_ERROR_THROTTLE(1.0, "Unable to vectorize novatel sentence");
480  }
481  sentence_start = ascii_end_idx;
482  }
483  else if (result < 0)
484  {
485 
486  // Sentence is not complete, add it to the remaining and break.
487  // This is legacy code from before FindAsciiSentence was implemented,
488  // and it will probably never happen, but it doesn't hurt anything to
489  // have it here.
490  remaining = input.substr(ascii_start_idx);
491  ROS_DEBUG("Waiting for more NovAtel data.");
492  break;
493  }
494  else
495  {
496  // Sentence had an invalid checksum, just iterate to the next sentence
497  sentence_start += 1;
498  ROS_WARN("Invalid NovAtel checksum for: [%s]",
499  input.substr(ascii_start_idx, ascii_len).c_str());
500  parse_error = true;
501  }
502  }
503  }
504  else
505  {
506  ROS_DEBUG("Incomplete ASCII sentence, waiting for more.");
507  remaining = input.substr(ascii_start_idx);
508  break;
509  }
510  }
511  }
512 
513  return !parse_error;
514  }
515 
516  double NovatelMessageExtractor::GetMostRecentUtcTime(const std::vector<NmeaSentence>& sentences)
517  {
518  std::vector<NmeaSentence>::const_reverse_iterator iter;
519  for (iter = sentences.rbegin(); iter != sentences.rend(); iter++)
520  {
521  if (iter->id == GpggaParser::MESSAGE_NAME || iter->id == GprmcParser::MESSAGE_NAME)
522  {
523  if (iter->body.size() > 1)
524  {
525  if (iter->body[1].empty() || iter->body[1] == "0")
526  {
527  return 0;
528  }
529  else
530  {
531  double utc_float;
532  if (swri_string_util::ToDouble(iter->body[1], utc_float))
533  {
534  return UtcFloatToSeconds(utc_float);
535  }
536  return 0;
537  }
538  }
539  }
540  }
541 
542  return 0;
543  }
544 
546  const novatel_gps_msgs::Gprmc& gprmc,
547  const novatel_gps_msgs::Gpgga& gpgga,
548  gps_common::GPSFixPtr gps_fix)
549  {
550  gps_fix->header.stamp = gpgga.header.stamp;
551  gps_fix->altitude = gpgga.alt;
552  // gps_fix.climb // We don't get this information from the receiver
553  // gps_fix.dip // We don't get this information from the receiver
554  // gps_fix.err // To be filled in later from BestPos messages
555  // gps_fix.err_climb // We don't get this information from the receiver
556  // gps_fix.err_dip // We don't get this information from the receiver
557  // gps_fix.err_horz // To be filled in later from BestPos messages
558  // gps_fix.err_pitch // We don't get this information from the receiver
559  // gps_fix.err_roll // We don't get this information from the receiver
560  // gps_fix.err_speed = ERR_INIT_HIGH;
561  // gps_fix.err_time = ERR_INIT_HIGH;
562  // gps_fix.err_track = ERR_INIT_HIGH;
563  // gps_fix.err_vert = ERR_INIT_HIGH;
564  // gps_fix.gdop = ERR_INIT_HIGH
565  gps_fix->hdop = gpgga.hdop;
566  gps_fix->latitude = gprmc.lat;
567  if (gpgga.lat_dir == "S")
568  {
569  gps_fix->latitude *= -1;
570  }
571 
572  gps_fix->longitude = gprmc.lon;
573  if (gpgga.lon_dir == "W")
574  {
575  gps_fix->longitude *= -1;
576  }
577  // gps_fix.pdop = ERR_INIT_HIGH;
578  // gps_fix.pitch = 0.0;
579  // gps_fix.roll = 0.0;
580  gps_fix->speed = gprmc.speed;
581  // gps_fix.tdop = ERR_INIT_HIGH;
582  gps_fix->time = gpgga.utc_seconds;
583  gps_fix->track = gprmc.track;
584  // gps_fix.vdop = ERR_INIT_HIGH;
585 
586  gps_fix->status.status = gps_common::GPSStatus::STATUS_FIX;
587  gps_fix->status.satellites_used = static_cast<uint16_t>(gpgga.num_sats);
588 
589  }
590 
591 }
uint8_t NmeaChecksum(const std::string &sentence)
Calculates the checksum of a NMEA sentence.
std::vector< std::string > header
void FindAsciiSentence(const std::string &sentence, size_t current_idx, size_t &start_idx, size_t &end_idx, size_t &invalid_char_idx)
Searches for a valid ASCII message within a string.
static const std::string MESSAGE_NAME
Definition: gpgga.h:52
void ParseHeader(const uint8_t *data)
Definition: binary_header.h:67
static const std::string CHECKSUM_FLAG
Precedes checkums in ASCII messages.
bool GetNovatelMessageParts(const std::string &sentence, std::string &message_id, std::vector< std::string > &header, std::vector< std::string > &body)
Splits an ASCII NovAtel message up into header and body parts.
static const std::string FIELD_SEPARATOR
Separates data fields in ASCII messages.
static const std::string HEADER_SEPARATOR
Separates header from body in ASCII NovAtel messages.
int32_t GetBinaryMessage(const std::string &str, size_t start_idx, BinaryMessage &msg)
Extracts a binary message from a data buffer.
double GetMostRecentUtcTime(const std::vector< NmeaSentence > &sentences)
Iterates through the provided messages to find the first GPGGA or GPRMC message with a valid UTC time...
#define ROS_WARN(...)
int32_t GetNovatelSentence(const std::string &str, size_t start_idx, std::string &sentence)
Extracts a NovAtel sentence from an input string.
uint32_t CRC32Value(int32_t i)
Calculates the CRC-32 of a single value.
uint32_t ParseUInt32(const uint8_t *buffer)
Converts a buffer containing 4 bytes into an unsigned 32-bit int.
#define ROS_ERROR_THROTTLE(period,...)
std::vector< std::string > body
std::vector< std::string > body
Definition: nmea_sentence.h:44
void GetGpsFixMessage(const novatel_gps_msgs::Gprmc &gprmc, const novatel_gps_msgs::Gpgga &gpgga, gps_common::GPSFixPtr gps_fix)
Combines data receives in GPRMC and GPGGA message to produce a gps_common/GPSFixPtr ROS message...
bool VectorizeNovatelSentence(const std::string &data, NovatelSentence &sentence)
Takes a sentence extracted by GetNovatelSentence and converts it into a data structure.
static const std::string MESSAGE_NAME
Definition: gprmc.h:53
static const std::string NOVATEL_ASCII_FLAGS
Used to search for both types of ASCII messages.
static constexpr uint32_t BINARY_HEADER_LENGTH
Definition: header.h:51
size_t GetSentenceChecksumStart(const std::string &str, size_t start_idx)
Finds the location of the next checksum in a valid ASCII sentence.
double UtcFloatToSeconds(double utc_float)
static const std::string NMEA_SENTENCE_FLAG
Indicates the beginning of a NMEA sentence.
void VectorizeString(const std::string &str, std::vector< std::string > &vectorized_message, const std::string &delimiters)
bool ExtractCompleteMessages(const std::string input, std::vector< NmeaSentence > &nmea_sentences, std::vector< NovatelSentence > &novatel_sentences, std::vector< BinaryMessage > &binary_messages, std::string &remaining, bool keep_nmea_container=false)
Extracts messages from a buffer of NovAtel data.
void VectorizeNmeaSentence(const std::string &sentence, NmeaSentence &vectorized_message)
Takes a sentence extracted by GetNmeaSentence and converts it into a data structure.
std::vector< uint8_t > data_
static const std::string NOVATEL_ENDLINE
Indicates the end of an ASCII message.
static const std::string NOVATEL_BINARY_SYNC_BYTES
Indicates the beginning of a binary NovAtel message.
#define ROS_ERROR(...)
static const std::string NOVATEL_SENTENCE_FLAG
Indicates the beginning of an ASCII NovAtel message.
int32_t GetNmeaSentence(const std::string &str, size_t start_idx, std::string &sentence, bool keep_container=false)
Extracts an NMEA sentence from an input string.
bool ToDouble(const std::string &string, double &value)
#define ROS_DEBUG(...)
uint32_t CalculateBlockCRC32(uint32_t ulCount, const uint8_t *ucBuffer)
Calculates the CRC-32 of a block of data all at once.


novatel_gps_driver
Author(s):
autogenerated on Wed Jul 3 2019 19:36:46