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:
Andrew Yong
2026-07-13 20:04:24 -05:00
committed by GitHub
co-authored by Ben Meadors
parent 269b974e96
commit dd509a8ecf
3 changed files with 140 additions and 30 deletions
+38 -5
View File
@@ -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