mirror of
https://github.com/meshtastic/firmware.git
synced 2026-11-07 23:04:49 -05:00
* Checksum NMEA sentences from the $ delimiter The PositionLite printWPL() format begins with a CRLF, so the fixed start offset of 1 folded the newline and the $ into the checksum and every sentence went out with a wrong value. Locate the $ instead and stop at the terminator or a \*. * Clamp truncated writes and harden the remaining fixed buffers snprintf returns the length it would have written, so a truncated NMEA sentence made buf + len point past the buffer and bufsz - len underflow into a huge size for the checksum append. Clamp after each write. Also pulls in the rest of #11236: the two remaining Dropzone sprintf calls, the dead strcpy in mt_sprintf that wrote one byte past a zero-size allocation for an empty format, and the 10-byte errcode buffer that INT32_MIN overflows. Co-Authored-By: Andrew Yong <me@ndoo.sg> * Bail out on a zero-sized buffer and cast err for %ld snprintf writes nothing at all when bufsz is 0, not even a terminator, so the checksum helper would run strchr over whatever the buffer already held. Return before touching it. int32_t is not long on every target, so cast before formatting with %ld. Co-Authored-By: Andrew Yong <me@ndoo.sg> * Add NMEA sentence regression tests Covers checksum computation from the $ delimiter for both printWPL overloads and printGGA, zero-sized buffers, and truncated buffers down to one byte. Co-Authored-By: Andrew Yong <me@ndoo.sg> * Tighten checksum parsing and pin the WPL fixture checksum Require exactly two hex digits followed by the sentence terminator, and assert both WPL overloads against a known checksum instead of comparing them to each other. * Bump native suite count to 43 --------- Co-authored-by: Andrew Yong <me@ndoo.sg>
124 lines
4.9 KiB
C++
124 lines
4.9 KiB
C++
#if !MESHTASTIC_EXCLUDE_GPS
|
|
#include "NMEAWPL.h"
|
|
#include "GeoCoord.h"
|
|
#include "gps/RTC.h"
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
static uint32_t nmeaClamp(uint32_t len, size_t bufsz)
|
|
{
|
|
if (len >= bufsz)
|
|
return bufsz > 0 ? (uint32_t)(bufsz - 1) : 0;
|
|
return len;
|
|
}
|
|
|
|
static uint32_t nmeaChecksum(const char *buf)
|
|
{
|
|
uint32_t chk = 0;
|
|
const char *c = strchr(buf, '$');
|
|
if (c) {
|
|
for (c++; *c && *c != '*'; c++)
|
|
chk ^= (uint8_t)*c;
|
|
}
|
|
return chk;
|
|
}
|
|
|
|
/* -------------------------------------------
|
|
* 1 2 3 4 5 6
|
|
* | | | | | |
|
|
* $--WPL,llll.ll,a,yyyyy.yy,a,c--c*hh<CR><LF>
|
|
*
|
|
* Field Number:
|
|
* 1 Latitude
|
|
* 2 N or S (North or South)
|
|
* 3 Longitude
|
|
* 4 E or W (East or West)
|
|
* 5 Waypoint name
|
|
* 6 Checksum
|
|
* -------------------------------------------
|
|
*/
|
|
|
|
uint32_t printWPL(char *buf, size_t bufsz, const meshtastic_PositionLite &pos, const char *name, bool isCaltopoMode)
|
|
{
|
|
if (bufsz == 0)
|
|
return 0;
|
|
|
|
GeoCoord geoCoord(pos.latitude_i, pos.longitude_i, pos.altitude);
|
|
char type = isCaltopoMode ? 'P' : 'N';
|
|
uint32_t len = snprintf(buf, bufsz, "\r\n$G%cWPL,%02d%07.4f,%c,%03d%07.4f,%c,%s", type, geoCoord.getDMSLatDeg(),
|
|
(abs(geoCoord.getLatitude()) - geoCoord.getDMSLatDeg() * 1e+7) * 6e-6, geoCoord.getDMSLatCP(),
|
|
geoCoord.getDMSLonDeg(), (abs(geoCoord.getLongitude()) - geoCoord.getDMSLonDeg() * 1e+7) * 6e-6,
|
|
geoCoord.getDMSLonCP(), name);
|
|
len = nmeaClamp(len, bufsz);
|
|
uint32_t chk = nmeaChecksum(buf);
|
|
len = nmeaClamp(len + snprintf(buf + len, bufsz - len, "*%02X\r\n", chk), bufsz);
|
|
return len;
|
|
}
|
|
|
|
uint32_t printWPL(char *buf, size_t bufsz, const meshtastic_Position &pos, const char *name, bool isCaltopoMode)
|
|
{
|
|
if (bufsz == 0)
|
|
return 0;
|
|
|
|
GeoCoord geoCoord(pos.latitude_i, pos.longitude_i, pos.altitude);
|
|
char type = isCaltopoMode ? 'P' : 'N';
|
|
uint32_t len = snprintf(buf, bufsz, "$G%cWPL,%02d%07.4f,%c,%03d%07.4f,%c,%s", type, geoCoord.getDMSLatDeg(),
|
|
(abs(geoCoord.getLatitude()) - geoCoord.getDMSLatDeg() * 1e+7) * 6e-6, geoCoord.getDMSLatCP(),
|
|
geoCoord.getDMSLonDeg(), (abs(geoCoord.getLongitude()) - geoCoord.getDMSLonDeg() * 1e+7) * 6e-6,
|
|
geoCoord.getDMSLonCP(), name);
|
|
len = nmeaClamp(len, bufsz);
|
|
uint32_t chk = nmeaChecksum(buf);
|
|
len = nmeaClamp(len + snprintf(buf + len, bufsz - len, "*%02X\r\n", chk), bufsz);
|
|
return len;
|
|
}
|
|
/* -------------------------------------------
|
|
* 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
|
|
* | | | | | | | | | | | | | | |
|
|
* $--GGA,hhmmss.ss,ddmm.mm,a,ddmm.mm,a,x,xx,x.x,x.x,M,x.x,M,x.x,xxxx*hh<CR><LF>
|
|
*
|
|
* Field Number:
|
|
* 1 UTC of this position report, hh is hours, mm is minutes, ss.ss is seconds.
|
|
* 2 Latitude
|
|
* 3 N or S (North or South)
|
|
* 4 Longitude
|
|
* 5 E or W (East or West)
|
|
* 6 GPS Quality Indicator (non null)
|
|
* 7 Number of satellites in use, 00 - 12
|
|
* 8 Horizontal Dilution of precision (meters)
|
|
* 9 Antenna Altitude above/below mean-sea-level (geoid) (in meters)
|
|
* 10 Units of antenna altitude, meters
|
|
* 11 Geoidal separation, the difference between the WGS-84 earth ellipsoid and mean-sea-level (geoid), "-" means mean-sea-level
|
|
* below ellipsoid 12 Units of geoidal separation, meters 13 Age of differential GPS data, time in seconds since last SC104 type 1
|
|
* or 9 update, null field when DGPS is not used 14 Differential reference station ID, 0000-1023 15 Checksum
|
|
* -------------------------------------------
|
|
*/
|
|
|
|
uint32_t printGGA(char *buf, size_t bufsz, const meshtastic_Position &pos)
|
|
{
|
|
if (bufsz == 0)
|
|
return 0;
|
|
|
|
GeoCoord geoCoord(pos.latitude_i, pos.longitude_i, pos.altitude);
|
|
time_t timestamp = pos.timestamp;
|
|
|
|
tm *t = gmtime(×tamp);
|
|
if (getRTCQuality() > 0) { // use the device clock if we got time from somewhere. If not, use the GPS timestamp.
|
|
uint32_t rtc_sec = getValidTime(RTCQuality::RTCQualityDevice);
|
|
timestamp = rtc_sec;
|
|
t = gmtime(×tamp);
|
|
}
|
|
|
|
uint32_t len = snprintf(
|
|
buf, bufsz, "$GNGGA,%02d%02d%02d.%02d,%02d%07.4f,%c,%03d%07.4f,%c,%u,%02u,%04u,%04d,%c,%04d,%c,%d,%04d", t->tm_hour,
|
|
t->tm_min, t->tm_sec, pos.timestamp_millis_adjust, geoCoord.getDMSLatDeg(),
|
|
(abs(geoCoord.getLatitude()) - geoCoord.getDMSLatDeg() * 1e+7) * 6e-6, geoCoord.getDMSLatCP(), geoCoord.getDMSLonDeg(),
|
|
(abs(geoCoord.getLongitude()) - geoCoord.getDMSLonDeg() * 1e+7) * 6e-6, geoCoord.getDMSLonCP(), pos.fix_quality,
|
|
pos.sats_in_view, pos.HDOP, geoCoord.getAltitude(), 'M', pos.altitude_geoidal_separation, 'M', 0, 0);
|
|
|
|
len = nmeaClamp(len, bufsz);
|
|
uint32_t chk = nmeaChecksum(buf);
|
|
len = nmeaClamp(len + snprintf(buf + len, bufsz - len, "*%02X\r\n", chk), bufsz);
|
|
return len;
|
|
}
|
|
|
|
#endif |