mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
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:
@@ -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)!;
|
||||
|
||||
@@ -6,22 +6,38 @@ import 'package:meshcore_sar_app/utils/fast_gps_packet.dart';
|
||||
|
||||
void main() {
|
||||
group('FastGpsPacket', () {
|
||||
test('encodes and parses a valid packet', () {
|
||||
test('encodes and parses a valid 16-byte packet', () {
|
||||
final packet = FastGpsPacket(
|
||||
senderKey6: 'aabbccddeeff',
|
||||
latitude: 46.0569,
|
||||
longitude: 14.5058,
|
||||
timestampSeconds: 1700000000,
|
||||
speedKmh: 37,
|
||||
);
|
||||
|
||||
final encoded = packet.encodeBinary();
|
||||
expect(encoded.length, equals(16));
|
||||
expect(encoded[0], equals(FastGpsPacket.magic));
|
||||
|
||||
final parsed = FastGpsPacket.tryParseBinary(encoded);
|
||||
|
||||
expect(parsed, isNotNull);
|
||||
expect(parsed!.senderKey6, equals('aabbccddeeff'));
|
||||
expect(parsed.latitude, closeTo(46.0569, 0.000001));
|
||||
expect(parsed.longitude, closeTo(14.5058, 0.000001));
|
||||
expect(parsed.timestampSeconds, equals(1700000000));
|
||||
expect(parsed.speedKmh, equals(37));
|
||||
});
|
||||
|
||||
test('clamps speed to a single unsigned byte', () {
|
||||
final packet = FastGpsPacket(
|
||||
senderKey6: '001122334455',
|
||||
latitude: 1.0,
|
||||
longitude: 2.0,
|
||||
speedKmh: 999,
|
||||
);
|
||||
|
||||
final parsed = FastGpsPacket.tryParseBinary(packet.encodeBinary());
|
||||
expect(parsed, isNotNull);
|
||||
expect(parsed!.speedKmh, equals(255));
|
||||
});
|
||||
|
||||
test('supports negative coordinates', () {
|
||||
@@ -29,7 +45,7 @@ void main() {
|
||||
senderKey6: '001122334455',
|
||||
latitude: -33.8688,
|
||||
longitude: -151.2093,
|
||||
timestampSeconds: 42,
|
||||
speedKmh: 0,
|
||||
);
|
||||
|
||||
final parsed = FastGpsPacket.tryParseBinary(packet.encodeBinary());
|
||||
@@ -39,20 +55,29 @@ void main() {
|
||||
});
|
||||
|
||||
test('rejects malformed payloads', () {
|
||||
// Too short.
|
||||
expect(
|
||||
FastGpsPacket.tryParseBinary(Uint8List.fromList([0x47, 0x01])),
|
||||
isNull,
|
||||
);
|
||||
// Wrong magic at the correct length.
|
||||
expect(
|
||||
FastGpsPacket.tryParseBinary(
|
||||
Uint8List.fromList(List<int>.filled(19, 0)..[0] = 0x48),
|
||||
Uint8List.fromList(List<int>.filled(16, 0)..[0] = 0x48),
|
||||
),
|
||||
isNull,
|
||||
);
|
||||
// Legacy 19-byte format is no longer accepted.
|
||||
expect(
|
||||
FastGpsPacket.tryParseBinary(
|
||||
Uint8List.fromList(List<int>.filled(19, 0)..[0] = 0x47),
|
||||
),
|
||||
isNull,
|
||||
);
|
||||
});
|
||||
|
||||
test('rejects invalid coordinate ranges', () {
|
||||
final payload = Uint8List(19);
|
||||
final payload = Uint8List(16);
|
||||
payload[0] = FastGpsPacket.magic;
|
||||
payload.setRange(1, 7, [0, 1, 2, 3, 4, 5]);
|
||||
final data = ByteData.sublistView(payload);
|
||||
@@ -66,7 +91,7 @@ void main() {
|
||||
(14.5 * FastGpsPacket.coordinateScale).round(),
|
||||
Endian.little,
|
||||
);
|
||||
data.setUint32(15, 1, Endian.little);
|
||||
data.setUint8(15, 1);
|
||||
|
||||
expect(FastGpsPacket.tryParseBinary(payload), isNull);
|
||||
});
|
||||
@@ -78,7 +103,7 @@ void main() {
|
||||
senderKey6: 'aabbccddeeff',
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
timestampSeconds: 1700000000,
|
||||
speedKmh: 5,
|
||||
);
|
||||
|
||||
final parsed = FastGpsPacket.tryParseBinary(packet.encodeBinary());
|
||||
|
||||
Reference in New Issue
Block a user