feat: align fast-GPS beacons with MeshUI firmware (16-byte format + cadence parity)

Switch the fast-GPS beacon to the firmware's canonical 16-byte wire format
(magic, key6, lat/lon microdegrees, speed km/h) — no on-wire timestamp; the
receiver stamps its own RX time. Drops compatibility with the older 19-byte app
format.

Mirror the firmware's adaptive send cadence in the app-fallback path
(LocationTrackingService): anchor + dwell motion hysteresis, EMA ground speed,
9-min stationary keepalive, fix-quality (accuracy) gate, and a post-RX hold-off
to avoid channel collisions. Parked nodes now beacon the stable anchor instead
of GPS wander.

Add the fast_gps_region config: region-scope picker in settings backed by the
firmware's fast_gps_region / fast_gps_regions custom vars.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Janez T
2026-06-24 13:17:02 +02:00
parent 3283d4ca20
commit 3c08e150ef
7 changed files with 490 additions and 113 deletions

View File

@@ -762,10 +762,11 @@ void main() {
senderKey6: '323334353637',
latitude: 44.123456,
longitude: 13.654321,
timestampSeconds: 1700001234,
speedKmh: 12,
),
);
final nowSeconds = DateTime.now().millisecondsSinceEpoch ~/ 1000;
final updated = provider.findContactByKey(publicKey)!;
expect(updated.telemetry, isNotNull);
expect(updated.telemetry!.gpsLocation, isNotNull);
@@ -780,8 +781,9 @@ void main() {
expect(updated.telemetry!.batteryMilliVolts, isNotNull);
expect(updated.advLat, equals((44.123456 * 1e6).round()));
expect(updated.advLon, equals((13.654321 * 1e6).round()));
expect(updated.lastAdvert, equals(1700001234));
expect(updated.lastMod, equals(1700001234));
// The beacon carries no timestamp; the receiver stamps its own RX time.
expect(updated.lastAdvert, closeTo(nowSeconds, 5));
expect(updated.lastMod, closeTo(nowSeconds, 5));
expect(
provider.estimatedLocationFor(updated.publicKeyHex),
isNotNull,
@@ -804,7 +806,7 @@ void main() {
senderKey6: '010203040506',
latitude: 10,
longitude: 20,
timestampSeconds: 99,
speedKmh: 0,
),
);
final after = provider.findContactByKey(publicKey)!;