Main MRPT website > C++ reference for MRPT 1.9.9
CGPSInterface_parser_NMEA.cpp
Go to the documentation of this file.
1 /* +------------------------------------------------------------------------+
2  | Mobile Robot Programming Toolkit (MRPT) |
3  | http://www.mrpt.org/ |
4  | |
5  | Copyright (c) 2005-2017, Individual contributors, see AUTHORS file |
6  | See: http://www.mrpt.org/Authors - All rights reserved. |
7  | Released under BSD License. See details in http://www.mrpt.org/License |
8  +------------------------------------------------------------------------+ */
9 
10 #include "hwdrivers-precomp.h" // Precompiled headers
11 
12 #include <mrpt/system/os.h>
13 #include <mrpt/system/filesystem.h>
15 
16 using namespace mrpt::hwdrivers;
17 using namespace mrpt::obs;
18 using namespace mrpt::system;
19 using namespace std;
20 
21 const size_t MAX_NMEA_LINE_LENGTH = 1024;
22 
23 bool CGPSInterface::implement_parser_NMEA(size_t& out_minimum_rx_buf_to_decide)
24 {
25  out_minimum_rx_buf_to_decide = 3;
26 
27  if (m_rx_buffer.size() < 3) return true; // no need to skip a byte
28 
29  const size_t nBytesAval = m_rx_buffer.size(); // Available for read
30 
31  // If the string does not start with "$GP" it is not valid:
32  uint8_t peek_buffer[3];
33  m_rx_buffer.peek_many(&peek_buffer[0], 3);
34  if (peek_buffer[0] != '$' || peek_buffer[1] != 'G' || peek_buffer[2] != 'P')
35  {
36  // Not the start of a NMEA string, skip 1 char:
37  return false;
38  }
39  else
40  {
41  // It starts OK: try to find the end of the line
42  std::string line;
43  bool line_is_ended = false;
44  for (size_t i = 0; i < nBytesAval && i < MAX_NMEA_LINE_LENGTH; i++)
45  {
46  const char val = static_cast<char>(m_rx_buffer.peek(i));
47  if (val == '\r' || val == '\n')
48  {
49  line_is_ended = true;
50  break;
51  }
52  line.push_back(val);
53  }
54  if (line_is_ended)
55  {
56  // Pop from buffer:
57  for (size_t i = 0; i < line.size(); i++) m_rx_buffer.pop();
58 
59  // Parse:
60  const bool did_have_gga = m_just_parsed_messages.has_GGA_datum;
62  line, m_just_parsed_messages, false /*verbose*/))
63  {
64  // Parsers must set only the part of the msg type:
65  m_just_parsed_messages.sensorLabel = "NMEA";
66 
67  // Save GGA cache (useful for NTRIP,...)
68  const bool now_has_gga = m_just_parsed_messages.has_GGA_datum;
69  if (now_has_gga && !did_have_gga)
70  {
71  m_last_GGA = line;
72  }
73  }
74  else
75  {
76  if (m_verbose)
77  std::cerr << "[CGPSInterface::implement_parser_NMEA] Line "
78  "of unknown format ignored: `"
79  << line << "`\n";
80  }
81  return true;
82  }
83  else
84  {
85  // We still need to wait for more data to be read:
86  out_minimum_rx_buf_to_decide = nBytesAval + 1;
87  return true;
88  }
89  }
90 }
91 
92 /* -----------------------------------------------------
93  parse_NMEA
94 ----------------------------------------------------- */
96  const std::string& s, mrpt::obs::CObservationGPS& out_obs,
97  const bool verbose)
98 {
99  static mrpt::system::TTimeStamp last_known_date =
100  mrpt::system::now(); // For building complete date+time in msgs without
101  // a date.
102  static mrpt::system::TTimeStamp last_known_time = mrpt::system::now();
103 
104  if (verbose) cout << "[CGPSInterface] GPS raw string: " << s << endl;
105 
106  // Firstly! If the string does not start with "$GP" it is not valid:
107  if (s.size() < 7) return false;
108  if (s[0] != '$' || s[1] != 'G') return false;
109 
110  std::vector<std::string> lstTokens;
112  s, "*,\t\r\n", lstTokens, false /* do not skip blank tokens */);
113  if (lstTokens.size() < 3) return false;
114 
115  for (size_t i = 0; i < lstTokens.size(); i++)
116  lstTokens[i] = mrpt::system::trim(lstTokens[i]); // Trim whitespaces
117 
118  bool parsed_ok = false;
119  // Try to determine the kind of command:
120  if (lstTokens[0] == "$GPGGA" && lstTokens.size() >= 13)
121  {
122  // ---------------------------------------------
123  // GGA
124  // ---------------------------------------------
125  bool all_fields_ok = true;
126  std::string token;
127 
128  // Fill out the output structure:
130 
131  // Time:
132  token = lstTokens[1];
133  if (token.size() >= 6)
134  {
135  gga.fields.UTCTime.hour = 10 * (token[0] - '0') + token[1] - '0';
136  gga.fields.UTCTime.minute = 10 * (token[2] - '0') + token[3] - '0';
137  gga.fields.UTCTime.sec = atof(&(token.c_str()[4]));
138  }
139  else
140  all_fields_ok = false;
141 
142  // Latitude:
143  token = lstTokens[2];
144  if (token.size() >= 4)
145  {
146  double lat = 10 * (token[0] - '0') + token[1] - '0';
147  lat += atof(&(token.c_str()[2])) / 60.0;
149  }
150  else
151  all_fields_ok = false;
152 
153  // N/S:
154  token = lstTokens[3];
155  if (token.empty())
156  all_fields_ok = false;
157  else if (token[0] == 'S')
159 
160  // Longitude:
161  token = lstTokens[4];
162  if (token.size() >= 5)
163  {
164  double lat =
165  100 * (token[0] - '0') + 10 * (token[1] - '0') + token[2] - '0';
166  lat += atof(&(token.c_str()[3])) / 60.0;
168  }
169  else
170  all_fields_ok = false;
171 
172  // E_W:
173  token = lstTokens[5];
174  if (token.empty())
175  all_fields_ok = false;
176  else if (token[0] == 'W')
178 
179  // fix quality:
180  token = lstTokens[6];
181  if (!token.empty())
182  gga.fields.fix_quality = (unsigned char)atoi(token.c_str());
183 
184  // sats:
185  token = lstTokens[7];
186  if (!token.empty())
187  gga.fields.satellitesUsed = (unsigned char)atoi(token.c_str());
188 
189  // HDOP:
190  token = lstTokens[8];
191  if (!token.empty())
192  {
193  gga.fields.HDOP = (float)atof(token.c_str());
194  gga.fields.thereis_HDOP = true;
195  }
196 
197  // Altitude:
198  token = lstTokens[9];
199  if (token.empty())
200  all_fields_ok = false;
201  else
202  gga.fields.altitude_meters = atof(token.c_str());
203 
204  // Units of the altitude:
205  // token = lstTokens[10];
206  // ASSERT_(token == "M");
207 
208  // Geoidal separation [B] (undulation)
209  token = lstTokens[11];
210  if (!token.empty()) gga.fields.geoidal_distance = atof(token.c_str());
211 
212  // Units of the geoidal separation:
213  // token = lstTokens[12];
214  // ASSERT_(token == "M");
215 
216  // Total altitude [A]+[B] and mmGPS Corrected total altitude
217  // Corr([A]+[B]):
221 
222  if (all_fields_ok)
223  {
224  out_obs.setMsg(gga);
226  out_obs.timestamp =
227  gga.fields.UTCTime.getAsTimestamp(last_known_date);
228  out_obs.has_satellite_timestamp = true;
229  }
230  parsed_ok = all_fields_ok;
231  }
232  else if (lstTokens[0] == "$GPRMC" && lstTokens.size() >= 13)
233  {
234  // ---------------------------------------------
235  // GPRMC
236  // ---------------------------------------------
237  bool all_fields_ok = true;
238  std::string token;
239 
240  // Fill out the output structure:
242 
243  // Time:
244  token = lstTokens[1];
245  if (token.size() >= 6)
246  {
247  rmc.fields.UTCTime.hour = 10 * (token[0] - '0') + token[1] - '0';
248  rmc.fields.UTCTime.minute = 10 * (token[2] - '0') + token[3] - '0';
249  rmc.fields.UTCTime.sec = atof(&(token.c_str()[4]));
250  }
251  else
252  all_fields_ok = false;
253 
254  // Valid?
255  token = lstTokens[2];
256  if (token.empty())
257  all_fields_ok = false;
258  else
259  rmc.fields.validity_char = token.c_str()[0];
260 
261  // Latitude:
262  token = lstTokens[3];
263  if (token.size() >= 4)
264  {
265  double lat = 10 * (token[0] - '0') + token[1] - '0';
266  lat += atof(&(token.c_str()[2])) / 60.0;
268  }
269  else
270  all_fields_ok = false;
271 
272  // N/S:
273  token = lstTokens[4];
274  if (token.empty())
275  all_fields_ok = false;
276  else if (token[0] == 'S')
278 
279  // Longitude:
280  token = lstTokens[5];
281  if (token.size() >= 5)
282  {
283  double lat =
284  100 * (token[0] - '0') + 10 * (token[1] - '0') + token[2] - '0';
285  lat += atof(&(token.c_str()[3])) / 60.0;
287  }
288  else
289  all_fields_ok = false;
290 
291  // E/W:
292  token = lstTokens[6];
293  if (token.empty())
294  all_fields_ok = false;
295  else if (token[0] == 'W')
297 
298  // Speed:
299  token = lstTokens[7];
300  if (!token.empty()) rmc.fields.speed_knots = atof(token.c_str());
301 
302  // Direction:
303  token = lstTokens[8];
304  if (!token.empty()) rmc.fields.direction_degrees = atof(token.c_str());
305 
306  // Date:
307  token = lstTokens[9];
308  if (token.size() >= 6)
309  {
310  rmc.fields.date_day = 10 * (token[0] - '0') + token[1] - '0';
311  rmc.fields.date_month = 10 * (token[2] - '0') + token[3] - '0';
312  rmc.fields.date_year = atoi(&(token.c_str()[4]));
313  }
314  else
315  all_fields_ok = false;
316 
317  // Magnetic var
318  token = lstTokens[10];
319  if (token.size() >= 2)
320  {
321  rmc.fields.magnetic_dir = atof(token.c_str());
322  // E/W:
323  token = lstTokens[11];
324  if (token.empty())
325  all_fields_ok = false;
326  else if (token[0] == 'W')
328  }
329 
330  // Mode ind.
331  if (lstTokens.size() >= 14)
332  {
333  // Only for NMEA 2.3
334  token = lstTokens[12];
335  if (token.empty())
336  all_fields_ok = false;
337  else
338  rmc.fields.positioning_mode = token.c_str()[0];
339  }
340  else
341  rmc.fields.positioning_mode = 'A'; // Default for older receiver
342 
343  if (all_fields_ok)
344  {
345  out_obs.setMsg(rmc);
347  out_obs.timestamp =
349  last_known_date = rmc.getDateAsTimestamp();
350  last_known_time = out_obs.timestamp;
351  out_obs.has_satellite_timestamp = true;
352  }
353  parsed_ok = all_fields_ok;
354  }
355  else if (lstTokens[0] == "$GPGLL" && lstTokens.size() >= 5)
356  {
357  // ---------------------------------------------
358  // GPGLL
359  // ---------------------------------------------
360  bool all_fields_ok = true;
361  std::string token;
362 
363  // Fill out the output structure:
365  // Latitude:
366  token = lstTokens[1];
367  if (token.size() >= 4)
368  {
369  double lat = 10 * (token[0] - '0') + token[1] - '0';
370  lat += atof(&(token.c_str()[2])) / 60.0;
372  }
373  else
374  all_fields_ok = false;
375 
376  // N/S:
377  token = lstTokens[2];
378  if (token.empty())
379  all_fields_ok = false;
380  else if (token[0] == 'S')
382 
383  // Longitude:
384  token = lstTokens[3];
385  if (token.size() >= 5)
386  {
387  double lat =
388  100 * (token[0] - '0') + 10 * (token[1] - '0') + token[2] - '0';
389  lat += atof(&(token.c_str()[3])) / 60.0;
391  }
392  else
393  all_fields_ok = false;
394 
395  // E/W:
396  token = lstTokens[4];
397  if (token.empty())
398  all_fields_ok = false;
399  else if (token[0] == 'W')
401 
402  if (lstTokens.size() >= 7)
403  {
404  // Time:
405  token = lstTokens[5];
406  if (token.size() >= 6)
407  {
408  gll.fields.UTCTime.hour =
409  10 * (token[0] - '0') + token[1] - '0';
410  gll.fields.UTCTime.minute =
411  10 * (token[2] - '0') + token[3] - '0';
412  gll.fields.UTCTime.sec = atof(&(token.c_str()[4]));
413  }
414  else
415  all_fields_ok = false;
416 
417  // Valid?
418  token = lstTokens[6];
419  if (token.empty())
420  all_fields_ok = false;
421  else
422  gll.fields.validity_char = token.c_str()[0];
423  }
424 
425  if (all_fields_ok)
426  {
427  out_obs.setMsg(gll);
429  out_obs.timestamp =
430  gll.fields.UTCTime.getAsTimestamp(last_known_date);
431  last_known_time = out_obs.timestamp;
432  out_obs.has_satellite_timestamp = true;
433  }
434  parsed_ok = all_fields_ok;
435  }
436  else if (lstTokens[0] == "$GPVTG" && lstTokens.size() >= 9)
437  {
438  // ---------------------------------------------
439  // GPVTG
440  // ---------------------------------------------
441  bool all_fields_ok = true;
442  std::string token;
443 
444  // Fill out the output structure:
446 
447  vtg.fields.true_track = atof(lstTokens[1].c_str());
448  vtg.fields.magnetic_track = atof(lstTokens[3].c_str());
449  vtg.fields.ground_speed_knots = atof(lstTokens[5].c_str());
450  vtg.fields.ground_speed_kmh = atof(lstTokens[7].c_str());
451 
452  if (lstTokens[2] != "T" || lstTokens[4] != "M" || lstTokens[6] != "N" ||
453  lstTokens[8] != "K")
454  all_fields_ok = false;
455 
456  if (all_fields_ok)
457  {
458  out_obs.setMsg(vtg);
460  out_obs.timestamp = last_known_time;
461  out_obs.has_satellite_timestamp = false;
462  }
463  parsed_ok = all_fields_ok;
464  }
465  else if (lstTokens[0] == "$GPZDA" && lstTokens.size() >= 5)
466  {
467  // ---------------------------------------------
468  // GPZDA
469  // ---------------------------------------------
470  bool all_fields_ok = true;
471  std::string token;
472 
473  // Fill out the output structure:
475  //$--ZDA,hhmmss.ss,xx,xx,xxxx,xx,xx
476  // hhmmss.ss = UTC
477  // xx = Day, 01 to 31
478  // xx = Month, 01 to 12
479  // xxxx = Year
480  // xx = Local zone description, 00 to +/- 13 hours
481  // xx = Local zone minutes description (same sign as hours)
482 
483  // Time:
484  token = lstTokens[1];
485  if (token.size() >= 6)
486  {
487  zda.fields.UTCTime.hour = 10 * (token[0] - '0') + token[1] - '0';
488  zda.fields.UTCTime.minute = 10 * (token[2] - '0') + token[3] - '0';
489  zda.fields.UTCTime.sec = atof(&(token.c_str()[4]));
490  }
491  else
492  all_fields_ok = false;
493 
494  // Day:
495  token = lstTokens[2];
496  if (!token.empty()) zda.fields.date_day = atoi(token.c_str());
497  // Month:
498  token = lstTokens[3];
499  if (!token.empty()) zda.fields.date_month = atoi(token.c_str());
500  // Year:
501  token = lstTokens[4];
502  if (!token.empty()) zda.fields.date_year = atoi(token.c_str());
503 
504  if (all_fields_ok)
505  {
506  out_obs.setMsg(zda);
508  try
509  {
510  out_obs.timestamp = zda.getDateTimeAsTimestamp();
511  last_known_date = zda.getDateAsTimestamp();
512  out_obs.has_satellite_timestamp = true;
513  last_known_time = out_obs.timestamp;
514  }
515  catch (...)
516  {
517  // Invalid date:
518  out_obs.timestamp = out_obs.originalReceivedTimestamp;
519  }
520  }
521  parsed_ok = all_fields_ok;
522  }
523  else
524  {
525  // other commands?
526  }
527 
528  return parsed_ok;
529 }
uint64_t TTimeStamp
A system independent time type, it holds the the number of 100-nanosecond intervals since January 1...
Definition: datetime.h:32
double longitude_degrees
The measured longitude, in degrees (East:+ , West:-)
uint8_t fix_quality
NMEA standard values: 0 = invalid, 1 = GPS fix (SPS), 2 = DGPS fix, 3 = PPS fix, 4 = Real Time Kinema...
content_t fields
Message content, accesible by individual fields.
double latitude_degrees
The measured latitude, in degrees (North:+ , South:-)
double longitude_degrees
The measured longitude, in degrees (East:+ , West:-)
mrpt::system::TTimeStamp getAsTimestamp(const mrpt::system::TTimeStamp &date) const
Build an MRPT timestamp with the hour/minute/sec of this structure and the date from the given timest...
double latitude_degrees
The measured latitude, in degrees (North:+ , South:-)
This namespace provides a OS-independent interface to many useful functions: filenames manipulation...
Definition: math_frwds.h:30
mrpt::system::TTimeStamp originalReceivedTimestamp
The local computer-based timestamp based on the reception of the message in the computer.
content_t fields
Message content, accesible by individual fields.
uint32_t satellitesUsed
The number of satelites used to compute this estimation.
const size_t MAX_NMEA_LINE_LENGTH
mrpt::system::TTimeStamp now()
A shortcut for system::getCurrentTime.
Definition: datetime.h:76
Contains classes for various device interfaces.
content_t fields
Message content, accesible by individual fields.
UTC_time UTCTime
The GPS sensor measured timestamp (in UTC time)
STL namespace.
GLdouble s
Definition: glext.h:3676
int8_t validity_char
This will be: &#39;A&#39;=OK or &#39;V&#39;=void.
UTC_time UTCTime
The GPS sensor measured timestamp (in UTC time)
double orthometric_altitude
The measured orthometric altitude, in meters (A)+(B).
unsigned char uint8_t
Definition: rptypes.h:41
double altitude_meters
The measured altitude, in meters (A).
bool thereis_HDOP
This states whether to take into account the value in the HDOP field.
UTC_time UTCTime
The GPS sensor measured timestamp (in UTC time)
double corrected_orthometric_altitude
The corrected (only for TopCon mmGPS) orthometric altitude, in meters mmGPS(A+B). ...
mrpt::system::TTimeStamp getDateAsTimestamp() const
Build an MRPT timestamp with the year/month/day of this observation.
bool implement_parser_NMEA(size_t &out_minimum_rx_buf_to_decide)
uint8_t date_day
Date: day (1-31), month (1-12), two-digits year (00-99)
content_t fields
Message content, accesible by individual fields.
float HDOP
The HDOP (Horizontal Dilution of Precision) as returned by the sensor.
double longitude_degrees
The measured longitude, in degrees (East:+ , West:-)
This namespace contains representation of robot actions and observations.
int val
Definition: mrpt_jpeglib.h:955
double lat
[deg], [deg], hgt over sea level[m]
bool has_satellite_timestamp
If true, CObservation::timestamp has been generated from accurate satellite clock.
GLsizei const GLchar ** string
Definition: glext.h:4101
static bool parse_NMEA(const std::string &cmd_line, mrpt::obs::CObservationGPS &out_obs, const bool verbose=false)
Parses one line of NMEA data from a GPS receiver, and writes the recognized fields (if any) into an o...
void tokenize(const std::string &inString, const std::string &inDelimiters, std::deque< std::string > &outTokens, bool skipBlankTokens=true) noexcept
Tokenizes a string according to a set of delimiting characters.
double geoidal_distance
Undulation: Difference between the measured altitude and the geoid, in meters (B).
double magnetic_dir
Magnetic variation direction (East:+, West:-)
mrpt::system::TTimeStamp getDateAsTimestamp() const
Build an MRPT timestamp with the year/month/day of this observation.
mrpt::system::TTimeStamp getDateTimeAsTimestamp() const
Build an MRPT UTC timestamp with the year/month/day + hour/minute/sec of this observation.
void setMsg(const MSG_CLASS &msg)
Stores a message in the list messages, making a copy of the passed object.
mrpt::system::TTimeStamp timestamp
The associated UTC time-stamp.
Definition: CObservation.h:58
double direction_degrees
Measured speed direction (in degrees)
char positioning_mode
&#39;A&#39;: Autonomous, &#39;D&#39;: Differential, &#39;N&#39;: Not valid, &#39;E&#39;: Estimated, &#39;M&#39;: Manual
content_t fields
Message content, accesible by individual fields.
std::string trim(const std::string &str)
Removes leading and trailing spaces.
double latitude_degrees
The measured latitude, in degrees (North:+ , South:-)
int8_t validity_char
This will be: &#39;A&#39;=OK or &#39;V&#39;=void.
This class stores messages from GNSS or GNSS+IMU devices, from consumer-grade inexpensive GPS receive...
UTC_time UTCTime
The GPS sensor measured timestamp (in UTC time)



Page generated by Doxygen 1.8.14 for MRPT 1.9.9 Git: ae4571287 Thu Nov 23 00:06:53 2017 +0100 at dom oct 27 23:51:55 CET 2019