gps: avoid pow() in GPS_HARDSLEEP threshold heuristic (#11179)

* gps: avoid pow() in GPS_HARDSLEEP threshold heuristic

GPS::down() used pow(seconds, 1.22) to pick between GPS_SOFTSLEEP and
GPS_HARDSLEEP - a curve fit the surrounding comment already describes
as "not particularly accurate". On flash-constrained builds where this
was the only pow() call site (e.g. wio-e5), it single-handedly pulled
in the full double-precision libm pow/rem_pio2 chain for a heuristic
threshold decision.

Replaces it with gpsHardsleepThresholdMs(), a piecewise-linear lookup
over the same curve, sampled at 16 points and verified to track the
original formula within ~0.5% for inputs >=10s and ~1.6% for 5-10s
(worse only in relative terms below 5s, where the absolute difference
is at most a couple of seconds - negligible against update intervals
measured in tens of seconds to hours).

Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>

* gps: trim comments to repo convention (1-2 lines)

Addresses a CodeRabbit nitpick: the explanatory comments in
GPSUpdateScheduling.cpp and test_gps_update_scheduling/test_main.cpp had
grown into multi-line blocks with provenance detail that belongs in the
commit message, not inline. Trims each to 1-2 lines, keeping only the
essential rationale/bounds.

Signed-off-by: Andrew Yong <me@ndoo.sg>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>

* Refactor main function to setup and loop for tests

Signed-off-by: Thomas Göttgens <tgoettgens@gmail.com>

* gps: extend the hardsleep threshold table below 5s and tighten its tests

The 0s-to-5s chord read 42% high at 1s, 22% at 2s and 12% at 3s, against the
~1.6% the comment claimed. Adding 1s, 2s and 3s sample points brings the worst
error below 10s to 1.60% at 7s. Above 10s it is 0.55% at 728s, unchanged.

Tests: sample off-breakpoint values only, including both worst-error inputs
(7s and 728s). Replace the 3000ms absolute floor, which made the 1s assertion
unfalsifiable given a true value of 2750ms, with 2% and 0.75% bounds. Add
breakpoint-exactness and clamp-boundary coverage.

---------

Signed-off-by: Andrew Yong <me@ndoo.sg>
Signed-off-by: Thomas Göttgens <tgoettgens@gmail.com>
Co-authored-by: Austin <vidplace7@gmail.com>
Co-authored-by: Thomas Göttgens <tgoettgens@gmail.com>
This commit is contained in:
Andrew Yong
2026-08-12 12:42:19 +00:00
committed by GitHub
co-authored by Austin Thomas Göttgens
parent 579d26e1b2
commit 54d6ce833e
4 changed files with 121 additions and 5 deletions
+25
View File
@@ -2,6 +2,31 @@
#include "Default.h"
// Sampled from the original `2750 * seconds^1.22` curve. Interpolation tracks it within 0.6% for
// inputs >=10s and 1.7% below that; the 1s/2s/3s points keep the convex first segment from
// overshooting (a 0s-to-5s chord reads 42% high at 1s).
static constexpr uint32_t kThresholdCurveSecs[] = {0, 1, 2, 3, 5, 10, 15, 20, 30, 45, 60, 90, 120, 180, 240, 300, 450, 600, 900};
static constexpr uint32_t kThresholdCurveMs[] = {0, 2750, 6406, 10506, 19592, 45639, 74845,
106314, 174350, 285925, 406141, 666053, 946093, 1551548,
2203893, 2893481, 4745172, 6740269, 11053722};
static constexpr size_t kThresholdCurvePoints = sizeof(kThresholdCurveSecs) / sizeof(kThresholdCurveSecs[0]);
// How long does gps_update_interval need to be, for GPS_HARDSLEEP to become more efficient than
// GPS_SOFTSLEEP? Avoids pow() so this heuristic doesn't pull double-precision libm into the image.
uint32_t gpsHardsleepThresholdMs(uint32_t predictedSearchSecs)
{
if (predictedSearchSecs >= kThresholdCurveSecs[kThresholdCurvePoints - 1])
return kThresholdCurveMs[kThresholdCurvePoints - 1];
size_t i = 1;
while (kThresholdCurveSecs[i] < predictedSearchSecs)
i++;
uint32_t x0 = kThresholdCurveSecs[i - 1], x1 = kThresholdCurveSecs[i];
uint32_t y0 = kThresholdCurveMs[i - 1], y1 = kThresholdCurveMs[i];
return y0 + (uint32_t)((uint64_t)(y1 - y0) * (predictedSearchSecs - x0) / (x1 - x0));
}
// Mark the time when searching for GPS position begins
void GPSUpdateScheduling::informSearching()
{