Compute GeoCoord's UTM/MGRS/OSGR/OLC lazily instead of eagerly (#10996)
GeoCoord's constructor unconditionally computed all five coordinate representations (DMS, UTM, MGRS, OSGR, OLC) via setCoords(), even though most callers only ever read one. The UTM conversion alone pulls in a chain of libm trig functions (atan, __kernel_tan, __ieee754_acos, __ieee754_pow, __kernel_rem_pio2/__ieee754_rem_pio2) that then have to be linked in regardless of whether anything is ever displayed. The only screen consumer (UIRenderer.cpp's GPS coordinate display) already dispatches on a single configured format and reads exactly one representation per call - never more than one. Compute DMS eagerly (cheap, most commonly needed) and defer UTM/MGRS/OSGR/OLC to first access via their own getters, tracked with per-representation mutable dirty flags, so a caller that never touches a given representation never pulls in its conversion code or the trig functions it needs. On stm32wl, GeoCoord's heavy constructor is reached only through NMEAWPL.cpp's NMEA/CalTopo serial export (GPS-gated, so wio-e5 only), which only ever reads the DMS getters - so this recovers 8,288 bytes flash there (of the 10,172-byte ceiling if the whole feature were cut) with the feature fully intact and zero behavior change on any platform. Updated test_geocoord_extreme_coords_no_oob (the existing regression test for a historical out-of-bounds crash in the UTM/MGRS conversion on extreme lat/lon) to explicitly call each representation's getter, since it previously relied on the constructor eagerly triggering all four conversions - which this change intentionally defers. Verified: full native test suite passes (586/586 test cases, including this one under ASan), and rak4631 (nRF52, screen-equipped, where the UTM/MGRS/OSGR/OLC getters are actually reachable) builds successfully. Assisted-by: Claude Sonnet 5 <noreply@anthropic.com> Signed-off-by: Andrew Yong <me@ndoo.sg> Co-authored-by: Ben Meadors <benmmeadors@gmail.com>
This commit is contained in:
co-authored by
GitHub
Ben Meadors
parent
269b974e96
commit
dd509a8ecf
+38
-5
@@ -36,19 +36,52 @@ GeoCoord::GeoCoord(double lat, double lon, int32_t alt) : _altitude(alt)
|
||||
GeoCoord::setCoords();
|
||||
}
|
||||
|
||||
// Initialize all the coordinate systems
|
||||
// Initialize DMS eagerly (cheap, most commonly used); UTM/MGRS/OSGR/OLC are computed lazily on
|
||||
// first access via their getters (see ensure*() below), since most callers never touch them.
|
||||
void GeoCoord::setCoords()
|
||||
{
|
||||
double lat = _latitude * 1e-7;
|
||||
double lon = _longitude * 1e-7;
|
||||
GeoCoord::latLongToDMS(lat, lon, _dms);
|
||||
GeoCoord::latLongToUTM(lat, lon, _utm);
|
||||
GeoCoord::latLongToMGRS(lat, lon, _mgrs);
|
||||
GeoCoord::latLongToOSGR(lat, lon, _osgr);
|
||||
GeoCoord::latLongToOLC(lat, lon, _olc);
|
||||
_utmValid = false;
|
||||
_mgrsValid = false;
|
||||
_osgrValid = false;
|
||||
_olcValid = false;
|
||||
_dirty = false;
|
||||
}
|
||||
|
||||
void GeoCoord::ensureUTM() const
|
||||
{
|
||||
if (!_utmValid) {
|
||||
GeoCoord::latLongToUTM(_latitude * 1e-7, _longitude * 1e-7, _utm);
|
||||
_utmValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
void GeoCoord::ensureMGRS() const
|
||||
{
|
||||
if (!_mgrsValid) {
|
||||
GeoCoord::latLongToMGRS(_latitude * 1e-7, _longitude * 1e-7, _mgrs);
|
||||
_mgrsValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
void GeoCoord::ensureOSGR() const
|
||||
{
|
||||
if (!_osgrValid) {
|
||||
GeoCoord::latLongToOSGR(_latitude * 1e-7, _longitude * 1e-7, _osgr);
|
||||
_osgrValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
void GeoCoord::ensureOLC() const
|
||||
{
|
||||
if (!_olcValid) {
|
||||
GeoCoord::latLongToOLC(_latitude * 1e-7, _longitude * 1e-7, _olc);
|
||||
_olcValid = true;
|
||||
}
|
||||
}
|
||||
|
||||
void GeoCoord::updateCoords(int32_t lat, int32_t lon, int32_t alt)
|
||||
{
|
||||
// If marked dirty or new coordinates
|
||||
|
||||
+93
-23
@@ -65,14 +65,24 @@ class GeoCoord
|
||||
int32_t _altitude = 0;
|
||||
|
||||
DMS _dms = {};
|
||||
UTM _utm = {};
|
||||
MGRS _mgrs = {};
|
||||
OSGR _osgr = {};
|
||||
OLC _olc = {};
|
||||
// Computed lazily on first access via ensure*() below; mutable so const getters can populate
|
||||
// them on demand.
|
||||
mutable UTM _utm = {};
|
||||
mutable MGRS _mgrs = {};
|
||||
mutable OSGR _osgr = {};
|
||||
mutable OLC _olc = {};
|
||||
mutable bool _utmValid = false;
|
||||
mutable bool _mgrsValid = false;
|
||||
mutable bool _osgrValid = false;
|
||||
mutable bool _olcValid = false;
|
||||
|
||||
bool _dirty = true;
|
||||
|
||||
void setCoords();
|
||||
void ensureUTM() const;
|
||||
void ensureMGRS() const;
|
||||
void ensureOSGR() const;
|
||||
void ensureOLC() const;
|
||||
|
||||
public:
|
||||
GeoCoord();
|
||||
@@ -123,26 +133,86 @@ class GeoCoord
|
||||
uint32_t getDMSLonSec() const { return _dms.lonSec; }
|
||||
char getDMSLonCP() const { return _dms.lonCP; }
|
||||
|
||||
// UTM getters
|
||||
uint8_t getUTMZone() const { return _utm.zone; }
|
||||
char getUTMBand() const { return _utm.band; }
|
||||
uint32_t getUTMEasting() const { return _utm.easting; }
|
||||
uint32_t getUTMNorthing() const { return _utm.northing; }
|
||||
// UTM getters (computed on first access - see ensureUTM())
|
||||
uint8_t getUTMZone() const
|
||||
{
|
||||
ensureUTM();
|
||||
return _utm.zone;
|
||||
}
|
||||
char getUTMBand() const
|
||||
{
|
||||
ensureUTM();
|
||||
return _utm.band;
|
||||
}
|
||||
uint32_t getUTMEasting() const
|
||||
{
|
||||
ensureUTM();
|
||||
return _utm.easting;
|
||||
}
|
||||
uint32_t getUTMNorthing() const
|
||||
{
|
||||
ensureUTM();
|
||||
return _utm.northing;
|
||||
}
|
||||
|
||||
// MGRS getters
|
||||
uint8_t getMGRSZone() const { return _mgrs.zone; }
|
||||
char getMGRSBand() const { return _mgrs.band; }
|
||||
char getMGRSEast100k() const { return _mgrs.east100k; }
|
||||
char getMGRSNorth100k() const { return _mgrs.north100k; }
|
||||
uint32_t getMGRSEasting() const { return _mgrs.easting; }
|
||||
uint32_t getMGRSNorthing() const { return _mgrs.northing; }
|
||||
// MGRS getters (computed on first access - see ensureMGRS())
|
||||
uint8_t getMGRSZone() const
|
||||
{
|
||||
ensureMGRS();
|
||||
return _mgrs.zone;
|
||||
}
|
||||
char getMGRSBand() const
|
||||
{
|
||||
ensureMGRS();
|
||||
return _mgrs.band;
|
||||
}
|
||||
char getMGRSEast100k() const
|
||||
{
|
||||
ensureMGRS();
|
||||
return _mgrs.east100k;
|
||||
}
|
||||
char getMGRSNorth100k() const
|
||||
{
|
||||
ensureMGRS();
|
||||
return _mgrs.north100k;
|
||||
}
|
||||
uint32_t getMGRSEasting() const
|
||||
{
|
||||
ensureMGRS();
|
||||
return _mgrs.easting;
|
||||
}
|
||||
uint32_t getMGRSNorthing() const
|
||||
{
|
||||
ensureMGRS();
|
||||
return _mgrs.northing;
|
||||
}
|
||||
|
||||
// OSGR getters
|
||||
char getOSGRE100k() const { return _osgr.e100k; }
|
||||
char getOSGRN100k() const { return _osgr.n100k; }
|
||||
uint32_t getOSGREasting() const { return _osgr.easting; }
|
||||
uint32_t getOSGRNorthing() const { return _osgr.northing; }
|
||||
// OSGR getters (computed on first access - see ensureOSGR())
|
||||
char getOSGRE100k() const
|
||||
{
|
||||
ensureOSGR();
|
||||
return _osgr.e100k;
|
||||
}
|
||||
char getOSGRN100k() const
|
||||
{
|
||||
ensureOSGR();
|
||||
return _osgr.n100k;
|
||||
}
|
||||
uint32_t getOSGREasting() const
|
||||
{
|
||||
ensureOSGR();
|
||||
return _osgr.easting;
|
||||
}
|
||||
uint32_t getOSGRNorthing() const
|
||||
{
|
||||
ensureOSGR();
|
||||
return _osgr.northing;
|
||||
}
|
||||
|
||||
// OLC getter
|
||||
void getOLCCode(char *code) { strncpy(code, _olc.code, OLC_CODE_LEN + 1); } // +1 for null termination
|
||||
// OLC getter (computed on first access - see ensureOLC())
|
||||
void getOLCCode(char *code)
|
||||
{
|
||||
ensureOLC();
|
||||
strncpy(code, _olc.code, OLC_CODE_LEN + 1); // +1 for null termination
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user