feat(#123): GPS refresh — self-telemetry stock-fw fallback (firmware-aware poll)
parent
0b4796a707
commit
dc20cc17f1
@ -0,0 +1,79 @@
|
|||||||
|
// Self-telemetry GPS read (#110). The client refreshes the radio's own
|
||||||
|
// position via the self telemetry request (CMD_SEND_TELEMETRY_REQ, len==4)
|
||||||
|
// instead of APP_START (which reset the session), then extracts GPS from the
|
||||||
|
// CayenneLPP reply (firmware self-reply: [0x8B][reserved][self-pubkey 6][LPP]).
|
||||||
|
|
||||||
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:meshcore_open/connector/meshcore_protocol.dart';
|
||||||
|
import 'package:meshcore_open/helpers/cayenne_lpp.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('buildSendTelemetryReq(null) — the self request', () {
|
||||||
|
test(
|
||||||
|
'is CMD_SEND_TELEMETRY_REQ + 3 reserved = 4-byte frame (firmware len==4)',
|
||||||
|
() {
|
||||||
|
// Firmware MyMesh.cpp:2030 matches the self request on `len == 4` (total
|
||||||
|
// frame). 5 bytes ([39,0,0,0,0]) misses it and gets RESP_CODE_ERR (#110).
|
||||||
|
expect(buildSendTelemetryReq(null), [cmdSendTelemetryReq, 0, 0, 0]);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('CayenneLpp.extractGps', () {
|
||||||
|
test('returns lat/lon from a parsed GPS reading', () {
|
||||||
|
final parsed = <Map<String, dynamic>>[
|
||||||
|
<String, dynamic>{
|
||||||
|
'channel': 1,
|
||||||
|
'values': <String, dynamic>{
|
||||||
|
'voltage': 4.1,
|
||||||
|
'gps': <String, dynamic>{
|
||||||
|
'latitude': 12.34,
|
||||||
|
'longitude': 56.78,
|
||||||
|
'altitude': 100.0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
final gps = CayenneLpp.extractGps(parsed);
|
||||||
|
expect(gps, isNotNull);
|
||||||
|
expect(gps!.latitude, closeTo(12.34, 1e-9));
|
||||||
|
expect(gps.longitude, closeTo(56.78, 1e-9));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns null when no channel carries GPS', () {
|
||||||
|
final parsed = <Map<String, dynamic>>[
|
||||||
|
<String, dynamic>{
|
||||||
|
'channel': 1,
|
||||||
|
'values': <String, dynamic>{'voltage': 4.1},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
expect(CayenneLpp.extractGps(parsed), isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns null for empty telemetry', () {
|
||||||
|
expect(CayenneLpp.extractGps(<Map<String, dynamic>>[]), isNull);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('parseDeviceInfoStrings', () {
|
||||||
|
test('extracts build date, model, and firmware version', () {
|
||||||
|
final frame = Uint8List.fromList([
|
||||||
|
0x0d, 0x0e, 0xaf, 0x28, 0, 0, 0, 0, // header (code, ver, counts, rsvd)
|
||||||
|
...'24-Jun-2026'.codeUnits, 0,
|
||||||
|
...'RAK 3401'.codeUnits, 0,
|
||||||
|
...'1.1.0-1.1.0'.codeUnits, 0,
|
||||||
|
]);
|
||||||
|
expect(parseDeviceInfoStrings(frame), [
|
||||||
|
'24-Jun-2026',
|
||||||
|
'RAK 3401',
|
||||||
|
'1.1.0-1.1.0',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('returns empty for a short frame', () {
|
||||||
|
expect(parseDeviceInfoStrings(Uint8List.fromList([0x0d, 0x0e])), isEmpty);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
Loading…
Reference in new issue