mirror of
https://github.com/dz0ny/meshcore-sar.git
synced 2026-08-11 16:30:28 +00:00
fix: retain last valid gps data
ref:
This commit is contained in:
@@ -335,12 +335,9 @@ class ContactsProvider with ChangeNotifier {
|
|||||||
debugPrint(' ✅ Parsed new telemetry');
|
debugPrint(' ✅ Parsed new telemetry');
|
||||||
debugPrint(' New telemetry timestamp: ${telemetry.timestamp}');
|
debugPrint(' New telemetry timestamp: ${telemetry.timestamp}');
|
||||||
|
|
||||||
// Ignore placeholder GPS coordinates (0,0) so we don't overwrite a
|
final incomingGps = telemetry.gpsLocation;
|
||||||
// previously known/saved position with invalid telemetry data.
|
if (_isInvalidTelemetryGps(incomingGps)) {
|
||||||
if (_isInvalidTelemetryGps(telemetry.gpsLocation)) {
|
// Always sanitize invalid placeholder GPS to null first.
|
||||||
debugPrint(
|
|
||||||
' ⚠️ Ignoring invalid telemetry GPS coordinates: ${telemetry.gpsLocation}',
|
|
||||||
);
|
|
||||||
telemetry = ContactTelemetry(
|
telemetry = ContactTelemetry(
|
||||||
gpsLocation: null,
|
gpsLocation: null,
|
||||||
batteryPercentage: telemetry.batteryPercentage,
|
batteryPercentage: telemetry.batteryPercentage,
|
||||||
@@ -353,6 +350,25 @@ class ContactsProvider with ChangeNotifier {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep last valid GPS for router/chat/room contacts when current
|
||||||
|
// telemetry does not provide a valid GPS fix.
|
||||||
|
if (_shouldRetainLastValidGps(contact, telemetry.gpsLocation)) {
|
||||||
|
debugPrint(
|
||||||
|
' ⚠️ Retaining last valid GPS. Incoming telemetry GPS is invalid/missing: $incomingGps',
|
||||||
|
);
|
||||||
|
final previousGps = _getValidGpsOrNull(contact.telemetry?.gpsLocation);
|
||||||
|
telemetry = ContactTelemetry(
|
||||||
|
gpsLocation: previousGps,
|
||||||
|
batteryPercentage: telemetry.batteryPercentage,
|
||||||
|
batteryMilliVolts: telemetry.batteryMilliVolts,
|
||||||
|
temperature: telemetry.temperature,
|
||||||
|
timestamp: telemetry.timestamp,
|
||||||
|
humidity: telemetry.humidity,
|
||||||
|
pressure: telemetry.pressure,
|
||||||
|
extraSensorData: telemetry.extraSensorData,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Update contact with new telemetry AND last seen time
|
// Update contact with new telemetry AND last seen time
|
||||||
// lastAdvert is Unix timestamp in seconds
|
// lastAdvert is Unix timestamp in seconds
|
||||||
final currentTimestamp = (DateTime.now().millisecondsSinceEpoch / 1000)
|
final currentTimestamp = (DateTime.now().millisecondsSinceEpoch / 1000)
|
||||||
@@ -390,6 +406,29 @@ class ContactsProvider with ChangeNotifier {
|
|||||||
return lat.abs() < epsilon && lon.abs() < epsilon;
|
return lat.abs() < epsilon && lon.abs() < epsilon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LatLng? _getValidGpsOrNull(LatLng? location) {
|
||||||
|
if (location == null || _isInvalidTelemetryGps(location)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _shouldRetainLastValidGps(Contact contact, LatLng? incomingGps) {
|
||||||
|
final isSupportedType =
|
||||||
|
contact.isChat || contact.isRepeater || contact.isRoom;
|
||||||
|
if (!isSupportedType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final hasPreviousValidGps =
|
||||||
|
_getValidGpsOrNull(contact.telemetry?.gpsLocation) != null;
|
||||||
|
if (!hasPreviousValidGps) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return incomingGps == null;
|
||||||
|
}
|
||||||
|
|
||||||
/// Find contact by public key prefix (6 bytes)
|
/// Find contact by public key prefix (6 bytes)
|
||||||
Contact? _findContactByPrefix(Uint8List prefix) {
|
Contact? _findContactByPrefix(Uint8List prefix) {
|
||||||
if (prefix.length < 6) return null;
|
if (prefix.length < 6) return null;
|
||||||
|
|||||||
@@ -9,6 +9,29 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||||||
void main() {
|
void main() {
|
||||||
TestWidgetsFlutterBinding.ensureInitialized();
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
Uint8List createPublicKey(int seed) {
|
||||||
|
return Uint8List.fromList(List<int>.generate(32, (index) => seed + index));
|
||||||
|
}
|
||||||
|
|
||||||
|
Contact createContact({
|
||||||
|
required Uint8List key,
|
||||||
|
required ContactType type,
|
||||||
|
String? name,
|
||||||
|
}) {
|
||||||
|
return Contact(
|
||||||
|
publicKey: key,
|
||||||
|
type: type,
|
||||||
|
flags: 0,
|
||||||
|
outPathLen: 0,
|
||||||
|
outPath: Uint8List(64),
|
||||||
|
advName: name ?? 'Test Contact',
|
||||||
|
lastAdvert: DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
||||||
|
advLat: (46.0569 * 1e6).toInt(),
|
||||||
|
advLon: (14.5058 * 1e6).toInt(),
|
||||||
|
lastMod: DateTime.now().millisecondsSinceEpoch ~/ 1000,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
group('ContactsProvider.updateTelemetry', () {
|
group('ContactsProvider.updateTelemetry', () {
|
||||||
late ContactsProvider provider;
|
late ContactsProvider provider;
|
||||||
late Uint8List publicKey;
|
late Uint8List publicKey;
|
||||||
@@ -106,5 +129,70 @@ void main() {
|
|||||||
expect(updated.displayLocation!.latitude, closeTo(45.0001, 0.0001));
|
expect(updated.displayLocation!.latitude, closeTo(45.0001, 0.0001));
|
||||||
expect(updated.displayLocation!.longitude, closeTo(13.9999, 0.0001));
|
expect(updated.displayLocation!.longitude, closeTo(13.9999, 0.0001));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'retains last valid gps for chat/repeater/room when telemetry gps is invalid or missing',
|
||||||
|
() {
|
||||||
|
final contactTypes = <ContactType>[
|
||||||
|
ContactType.chat,
|
||||||
|
ContactType.repeater,
|
||||||
|
ContactType.room,
|
||||||
|
];
|
||||||
|
|
||||||
|
for (var i = 0; i < contactTypes.length; i++) {
|
||||||
|
final scopedProvider = ContactsProvider();
|
||||||
|
final scopedKey = createPublicKey(16 + i);
|
||||||
|
final contactType = contactTypes[i];
|
||||||
|
scopedProvider.addOrUpdateContact(
|
||||||
|
createContact(
|
||||||
|
key: scopedKey,
|
||||||
|
type: contactType,
|
||||||
|
name: 'Contact ${contactType.name}',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final validGps = CayenneLppParser.createGpsData(
|
||||||
|
latitude: 45.1234,
|
||||||
|
longitude: 13.8765,
|
||||||
|
);
|
||||||
|
scopedProvider.updateTelemetry(scopedKey.sublist(0, 6), validGps);
|
||||||
|
|
||||||
|
// No GPS frame should keep previous valid GPS.
|
||||||
|
final batteryOnly = CayenneLppParser.createBatteryData(3.8);
|
||||||
|
scopedProvider.updateTelemetry(scopedKey.sublist(0, 6), batteryOnly);
|
||||||
|
|
||||||
|
var updated = scopedProvider.findContactByKey(scopedKey)!;
|
||||||
|
expect(updated.telemetry, isNotNull);
|
||||||
|
expect(updated.telemetry!.gpsLocation, isNotNull);
|
||||||
|
expect(
|
||||||
|
updated.telemetry!.gpsLocation!.latitude,
|
||||||
|
closeTo(45.1234, 0.0001),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
updated.telemetry!.gpsLocation!.longitude,
|
||||||
|
closeTo(13.8765, 0.0001),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Invalid 0,0 GPS frame should also keep previous valid GPS.
|
||||||
|
final invalidGps = CayenneLppParser.createGpsData(
|
||||||
|
latitude: 0.0,
|
||||||
|
longitude: 0.0,
|
||||||
|
);
|
||||||
|
scopedProvider.updateTelemetry(scopedKey.sublist(0, 6), invalidGps);
|
||||||
|
|
||||||
|
updated = scopedProvider.findContactByKey(scopedKey)!;
|
||||||
|
expect(updated.telemetry, isNotNull);
|
||||||
|
expect(updated.telemetry!.gpsLocation, isNotNull);
|
||||||
|
expect(
|
||||||
|
updated.telemetry!.gpsLocation!.latitude,
|
||||||
|
closeTo(45.1234, 0.0001),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
updated.telemetry!.gpsLocation!.longitude,
|
||||||
|
closeTo(13.8765, 0.0001),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user