I am reading NMEA-0183 message with C++ 11.
After a code revamp, I see some kind of buffer smashing and I cannot see why. Help is welcome.
Here are the logs I get:
GPS: probably data
GPS: $GPGLL,4851.87109,N,00226.20521,E,092741.00,A,A*6D
GPS: match is: 1 | 5 matches
GPS: match #0: 17a30a0 | buffer: 5f452ee0
GPS: match #0: B2z
GPS: match #1: 5f452ed0 | buffer: 5f452ee0
GPS: match #1:
GPS: match #2: 5f452ed0 | buffer: 5f452ee0
GPS: match #2: N
GPS: match #3: 5f452ed0 | buffer: 5f452ee0
GPS: match #3: 00226.20521
GPS: match #4: 5f452ed0 | buffer: 5f452ee0
GPS: match #4: E
GPS: match is: latitude:0.000000 | longitude: 2.436754
GPS: end of processing
Where I expect something like this (note this is the GPGLL message which works the same way as the one up above):
GPS: probably data
GPS: $GPRMC,164548.00,A,4851.85459,N,00226.22224,E,0.721,,060923,,,A*7F
GPS: match is: 1 | 5 matches
GPS: match #0: GPRMC,164548.00,A,4851.85459,N,00226.22224,E
GPS: match #1: 4851.85459
GPS: match #2: N
GPS: match #3: 00226.22224
GPS: match #4: E
GPS: match is: latitude:48.864243 | longitude: 2.437037
GPS: end of processing
And the code:
#include <iostream>
#include <regex>
/// @brief Returns the 01131.324 (Longitude 11 deg 31.324') coordinate as a
/// float
/// @param sReading The string of the coordinate
/// @return the float version
float latitudeDeegresToFloat(char const *sReading) {
char sDegrees[2 + 1] = {sReading[0], sReading[1], 0};
float degrees = (float)strtod(sDegrees, NULL);
float minutes = (float)strtod(sReading + 2, NULL);
const float latitude = degrees + minutes / 60.0;
return latitude;
}
/// @brief Returns the 11131.324 longitude (111 deg 31.324') coordinate as a
/// float
/// @param sReading The string of the coordinate
/// @return The float version
float longitudeDeegresToFloat(char const *sReading) {
char sDegrees[3 + 1] = {sReading[0], sReading[1], sReading[2], 0};
float degrees = (float)strtod(sDegrees, NULL);
float minutes = (float)strtod(sReading + 3, NULL);
const float longitude = degrees + minutes / 60.0;
return longitude;
}
int main() {
float latitude, longitude;
printf("GPS: probably data\n");
/// Regex string match
std::smatch match;
char buffer[256 * 4] = "$GPGLL,4851.87109,N,00226.20521,E,092741.00,A,A*6D";
printf("GPS: %s\n", buffer);
bool found = false;
// == Is is a GPGLL message Geographic Acquisition
#define COMMAND "$GPGGA"
if (strncmp(buffer, COMMAND, sizeof COMMAND - 1) == 0) {
//$GPGGA,123519,4807.038,N,01131.324,E,
std::string str(buffer + 1); // Skip leading $
std::regex rgx("GPGGA,[^,]*,([0-9\\.+-]+),(N|S),([0-9\\.+-]*),(E|W)\\.?");
found = std::regex_search(str, match, rgx);
}
// == Is is a GPGLL message Geographic Latitude Longitude
#define COMMAND "$GPGLL"
else if (strncmp(buffer, COMMAND, sizeof COMMAND - 1) == 0) {
std::string str(buffer + 1); // Skip leading $
std::regex rgx("GPGLL,([0-9\\.+-]+),(N|S),([0-9\\.+-]*),(E|W)\\.?");
found = std::regex_search(str, match, rgx);
}
// == Is is a GPRMC message (lat, long)
#define GPRMC "$GPRMC"
else if (strncmp(buffer, GPRMC, sizeof GPRMC - 1) == 0) {
std::string str(buffer + 1); // Skip leading $
std::regex rgx("GPRMC,[^,]*,A,([0-9\\.+-]+),(N|S),([0-9\\.+-]*),(W|E)\\.?");
found = std::regex_search(str, match, rgx);
} else {
printf("GPS: not a message to process but '%s'\n", buffer);
return 3;
}
printf("GPS: match is: %d | %d matches\n", found, match.size());
if (!found)
return 1;
int i = 0;
for (auto m : match) {
printf("GPS: match #%d: %x | buffer: %x\n", i, m.str().c_str(), buffer);
printf("GPS: match #%d: %s\n", i, m.str().c_str());
switch (i) {
// Latitude
case 1:
latitude = latitudeDeegresToFloat(m.str().c_str());
break;
// Latitude direction
case 2:
if (m.str().compare("S") == 0)
latitude = -latitude;
break;
// Longitude
case 3:
longitude = longitudeDeegresToFloat(m.str().c_str());
break;
case 4:
if (m.str().compare("W") == 0)
longitude = -longitude;
break;
}
i++;
}
printf("GPS: match is: latitude:%f | longitude: %f\n", latitude, longitude);
// == End of GPS processing
EndOfGpsProcessing:
printf("GPS: end of processing\n");
if (latitude == 0 && longitude == 0) {
printf("GPS: won't publish Null Island\n");
return 2;
}
return 0;
}
Here is a replit to work with: https://replit.com/@Stephanede/GPS-regex
The documentation of std::match_results
(which is what your std::smatch
actually is) explains what is going on:
Because std::match_results holds std::sub_matches, each of which is a pair of iterators into the original character sequence that was matched, it's undefined behavior to examine std::match_results if the original character sequence was destroyed or iterators to it were invalidated for other reasons.
The str
you pass into std::regex_search
must outlive match
, which is not the case.
You can actually do away with str
entirely and just call regex_search(buffer, match, rx)
.
Alternatively, create a std::string_view
that wraps buffer
at the top level or promote str
to the top level of your function.