telemetry fix gate for telemetry stream removed
This commit is contained in:
@@ -17,10 +17,19 @@ class FcCapabilities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Normalisiertes Telemetrie-Frame, das an die UI weitergereicht wird (Doku 3.4).
|
/// Normalisiertes Telemetrie-Frame, das an die UI weitergereicht wird (Doku 3.4).
|
||||||
|
///
|
||||||
|
/// Der Strom wird bewusst NICHT auf einen gueltigen GPS-Fix gegated (frueher
|
||||||
|
/// wurde ohne Fix gar kein Frame gesendet) - jeder Konsument entscheidet
|
||||||
|
/// selbst anhand von [hasFix], ob/wie er [lat]/[lon] verwendet. Der
|
||||||
|
/// Kartenmarker (FlyScreen) faellt bei fehlendem Fix weiterhin auf den
|
||||||
|
/// ersten Wegpunkt zurueck, das Settings-Diagnosepanel zeigt Hoehe/
|
||||||
|
/// Geschwindigkeit/Armed-Status dagegen auch ohne Fix an.
|
||||||
class TelemetryFrame {
|
class TelemetryFrame {
|
||||||
const TelemetryFrame({
|
const TelemetryFrame({
|
||||||
required this.lat,
|
required this.lat,
|
||||||
required this.lon,
|
required this.lon,
|
||||||
|
required this.hasFix,
|
||||||
|
required this.numSat,
|
||||||
required this.altitudeM,
|
required this.altitudeM,
|
||||||
required this.speedMs,
|
required this.speedMs,
|
||||||
required this.headingDeg,
|
required this.headingDeg,
|
||||||
@@ -28,8 +37,12 @@ class TelemetryFrame {
|
|||||||
this.activeWaypointIndex,
|
this.activeWaypointIndex,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Nur aussagekraeftig, wenn [hasFix] - ohne Fix meldet der FC oft (0,0)
|
||||||
|
/// oder eine veraltete Position.
|
||||||
final double lat;
|
final double lat;
|
||||||
final double lon;
|
final double lon;
|
||||||
|
final bool hasFix;
|
||||||
|
final int numSat;
|
||||||
final double altitudeM;
|
final double altitudeM;
|
||||||
final double speedMs;
|
final double speedMs;
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ class MockFlightControllerLink implements FlightControllerLink {
|
|||||||
_telemetryController?.add(TelemetryFrame(
|
_telemetryController?.add(TelemetryFrame(
|
||||||
lat: 52.5,
|
lat: 52.5,
|
||||||
lon: 13.4,
|
lon: 13.4,
|
||||||
|
hasFix: true,
|
||||||
|
numSat: 12,
|
||||||
altitudeM: 120,
|
altitudeM: 120,
|
||||||
speedMs: 18,
|
speedMs: 18,
|
||||||
headingDeg: _heading,
|
headingDeg: _heading,
|
||||||
|
|||||||
@@ -21,6 +21,12 @@ import 'msp_telemetry_codec.dart';
|
|||||||
/// hat aktuell keine Felder dafuer; sobald die UI das braucht, ist ein
|
/// hat aktuell keine Felder dafuer; sobald die UI das braucht, ist ein
|
||||||
/// dritter, noch selteners abgefragter Zweig (`MSP2_INAV_ANALOG`) trivial
|
/// dritter, noch selteners abgefragter Zweig (`MSP2_INAV_ANALOG`) trivial
|
||||||
/// ergaenzt.
|
/// ergaenzt.
|
||||||
|
///
|
||||||
|
/// Der Strom wird NICHT auf einen gueltigen GPS-Fix gegated - jeder Zyklus
|
||||||
|
/// emittiert ein [TelemetryFrame], auch ohne Fix (dann mit `hasFix: false`
|
||||||
|
/// und der vom FC gemeldeten, ggf. bedeutungslosen Position). Konsumenten
|
||||||
|
/// wie FlyScreen entscheiden selbst anhand von [TelemetryFrame.hasFix], ob
|
||||||
|
/// sie die Position anzeigen.
|
||||||
class MspTelemetryPoller {
|
class MspTelemetryPoller {
|
||||||
MspTelemetryPoller(this._client);
|
MspTelemetryPoller(this._client);
|
||||||
|
|
||||||
@@ -77,17 +83,12 @@ class MspTelemetryPoller {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sicherheitsrelevante Anzeigen nie mit Platzhaltern fuellen (Doku 9):
|
|
||||||
// ohne GPS-Fix gibt es keine sinnvolle Position - dann lieber gar kein
|
|
||||||
// Telemetrie-Frame senden, statt (0,0) oder einen eingefrorenen alten
|
|
||||||
// Wert zu zeigen. Die bestehende Fly-Anzeige zeigt ohne Frame ohnehin
|
|
||||||
// keinen Drohnen-Marker (siehe telemetryProvider/FlyScreen).
|
|
||||||
if (!gps.hasFix) return;
|
|
||||||
|
|
||||||
if (!_framesController.isClosed) {
|
if (!_framesController.isClosed) {
|
||||||
_framesController.add(TelemetryFrame(
|
_framesController.add(TelemetryFrame(
|
||||||
lat: gps.lat,
|
lat: gps.lat,
|
||||||
lon: gps.lon,
|
lon: gps.lon,
|
||||||
|
hasFix: gps.hasFix,
|
||||||
|
numSat: gps.numSat,
|
||||||
altitudeM: altitudeM,
|
altitudeM: altitudeM,
|
||||||
speedMs: gps.speedMs,
|
speedMs: gps.speedMs,
|
||||||
headingDeg: gps.headingDeg,
|
headingDeg: gps.headingDeg,
|
||||||
|
|||||||
@@ -132,10 +132,11 @@ class FlyScreen extends ConsumerWidget {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Vor der ersten Telemetrie (Verbindungsaufbau laeuft noch, oder Mock
|
// Vor der ersten Telemetrie (Verbindungsaufbau laeuft noch, oder Mock
|
||||||
// hat noch nicht getickt) auf dem ersten Wegpunkt statt gar nicht
|
// hat noch nicht getickt) und ohne GPS-Fix (TelemetryFrame wird bewusst
|
||||||
// anzeigen - vermeidet einen leeren/verwirrenden Kartenausschnitt beim
|
// nicht auf einen Fix gegated, siehe dortige Doku - ohne Fix meldet der
|
||||||
// Eintritt in den Fly-Modus.
|
// FC aber oft (0,0) oder eine veraltete Position) auf dem ersten
|
||||||
final droneMarker = telemetry != null
|
// Wegpunkt statt an einer erfundenen Stelle anzeigen.
|
||||||
|
final droneMarker = telemetry != null && telemetry.hasFix
|
||||||
? Marker(
|
? Marker(
|
||||||
point: LatLng(telemetry.lat, telemetry.lon),
|
point: LatLng(telemetry.lat, telemetry.lon),
|
||||||
width: 28,
|
width: 28,
|
||||||
|
|||||||
@@ -141,7 +141,9 @@ class _WifiPanel extends ConsumerWidget {
|
|||||||
// auslösen. Ist bereits eine echte Verbindung aktiv (z.B. weil der
|
// auslösen. Ist bereits eine echte Verbindung aktiv (z.B. weil der
|
||||||
// Fly-Modus sie aufgebaut hat), liefert dies denselben Provider/dieselbe
|
// Fly-Modus sie aufgebaut hat), liefert dies denselben Provider/dieselbe
|
||||||
// MSP-Client-Instanz wie FlyScreen - keine zweite, konkurrierende
|
// MSP-Client-Instanz wie FlyScreen - keine zweite, konkurrierende
|
||||||
// Abfrage-Schleife.
|
// Abfrage-Schleife. Der Telemetrie-Strom selbst ist nicht auf einen
|
||||||
|
// GPS-Fix gegated (siehe TelemetryFrame-Doku) - die Felder erscheinen
|
||||||
|
// hier also auch ohne Fix, sobald ueberhaupt eine MSP-Antwort ankommt.
|
||||||
final telemetry = linkState == LinkState.connected
|
final telemetry = linkState == LinkState.connected
|
||||||
? ref.watch(telemetryProvider).value
|
? ref.watch(telemetryProvider).value
|
||||||
: null;
|
: null;
|
||||||
@@ -173,7 +175,7 @@ class _WifiPanel extends ConsumerWidget {
|
|||||||
const Spacer(),
|
const Spacer(),
|
||||||
_StreamActivityPill(isReceiving: isReceiving, packetCount: packetCount),
|
_StreamActivityPill(isReceiving: isReceiving, packetCount: packetCount),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
_StatePill(state: linkState, connectedLabel: connectedSsid),
|
_StatePill(state: linkState, connectedLabel: connectedSsid ?? 'unknown'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
if (errorReason != null) ...[
|
if (errorReason != null) ...[
|
||||||
@@ -264,6 +266,7 @@ class _TelemetryFields extends StatelessWidget {
|
|||||||
spacing: 16,
|
spacing: 16,
|
||||||
runSpacing: 4,
|
runSpacing: 4,
|
||||||
children: [
|
children: [
|
||||||
|
_field('GPS', t.hasFix ? 'Fix (${t.numSat} sats)' : 'No fix (${t.numSat} sats)'),
|
||||||
_field('Lat', t.lat.toStringAsFixed(6)),
|
_field('Lat', t.lat.toStringAsFixed(6)),
|
||||||
_field('Lon', t.lon.toStringAsFixed(6)),
|
_field('Lon', t.lon.toStringAsFixed(6)),
|
||||||
_field('Alt', '${t.altitudeM.toStringAsFixed(1)} m'),
|
_field('Alt', '${t.altitudeM.toStringAsFixed(1)} m'),
|
||||||
|
|||||||
@@ -12,7 +12,10 @@ import 'package:dmc_app/transport/msp/msp_frame_decoder.dart';
|
|||||||
/// Beantwortet jede ueber [transport] gesendete MSP-Anfrage sofort mit einer
|
/// Beantwortet jede ueber [transport] gesendete MSP-Anfrage sofort mit einer
|
||||||
/// synthetischen Antwort - simuliert den Flightcontroller, ohne echte
|
/// synthetischen Antwort - simuliert den Flightcontroller, ohne echte
|
||||||
/// Hardware (Doku Kommunikationsschicht Abschnitt 9).
|
/// Hardware (Doku Kommunikationsschicht Abschnitt 9).
|
||||||
Timer _startFakeFlightController(LoopbackTransport transport) {
|
Timer _startFakeFlightController(
|
||||||
|
LoopbackTransport transport, {
|
||||||
|
int fixType = 3,
|
||||||
|
}) {
|
||||||
var answered = 0;
|
var answered = 0;
|
||||||
return Timer.periodic(const Duration(milliseconds: 5), (_) {
|
return Timer.periodic(const Duration(milliseconds: 5), (_) {
|
||||||
while (answered < transport.sentData.length) {
|
while (answered < transport.sentData.length) {
|
||||||
@@ -24,7 +27,7 @@ Timer _startFakeFlightController(LoopbackTransport transport) {
|
|||||||
switch (request.function) {
|
switch (request.function) {
|
||||||
case MspCommands.rawGps:
|
case MspCommands.rawGps:
|
||||||
final d = ByteData(18);
|
final d = ByteData(18);
|
||||||
d.setUint8(0, 3); // fixType: 3D-Fix
|
d.setUint8(0, fixType); // fixType: 3 = 3D-Fix, 0 = kein Fix
|
||||||
d.setUint8(1, 11);
|
d.setUint8(1, 11);
|
||||||
d.setInt32(2, 525200000, Endian.little); // lat 52.52
|
d.setInt32(2, 525200000, Endian.little); // lat 52.52
|
||||||
d.setInt32(6, 134050000, Endian.little); // lon 13.405
|
d.setInt32(6, 134050000, Endian.little); // lon 13.405
|
||||||
@@ -66,6 +69,8 @@ void main() {
|
|||||||
|
|
||||||
expect(frame.lat, closeTo(52.52, 1e-6));
|
expect(frame.lat, closeTo(52.52, 1e-6));
|
||||||
expect(frame.lon, closeTo(13.405, 1e-6));
|
expect(frame.lon, closeTo(13.405, 1e-6));
|
||||||
|
expect(frame.hasFix, isTrue);
|
||||||
|
expect(frame.numSat, 11);
|
||||||
expect(frame.altitudeM, closeTo(50.0, 1e-6));
|
expect(frame.altitudeM, closeTo(50.0, 1e-6));
|
||||||
expect(frame.speedMs, closeTo(5.0, 1e-6));
|
expect(frame.speedMs, closeTo(5.0, 1e-6));
|
||||||
expect(frame.headingDeg, closeTo(270.0, 1e-6));
|
expect(frame.headingDeg, closeTo(270.0, 1e-6));
|
||||||
@@ -76,6 +81,25 @@ void main() {
|
|||||||
timeout: const Timeout(Duration(seconds: 5)),
|
timeout: const Timeout(Duration(seconds: 5)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
test(
|
||||||
|
'subscribeTelemetry() liefert auch ohne GPS-Fix ein Frame (Strom wird '
|
||||||
|
'nicht auf einen Fix gegated)',
|
||||||
|
() async {
|
||||||
|
final transport = LoopbackTransport();
|
||||||
|
final responder = _startFakeFlightController(transport, fixType: 0);
|
||||||
|
final link = MspFlightControllerLink(transport: transport);
|
||||||
|
|
||||||
|
await link.connect();
|
||||||
|
final frame = await link.subscribeTelemetry().first;
|
||||||
|
|
||||||
|
expect(frame.hasFix, isFalse);
|
||||||
|
|
||||||
|
await link.disconnect();
|
||||||
|
responder.cancel();
|
||||||
|
},
|
||||||
|
timeout: const Timeout(Duration(seconds: 5)),
|
||||||
|
);
|
||||||
|
|
||||||
test('readActiveWaypointIndex() fragt MSP_NAV_STATUS direkt ab', () async {
|
test('readActiveWaypointIndex() fragt MSP_NAV_STATUS direkt ab', () async {
|
||||||
final transport = LoopbackTransport();
|
final transport = LoopbackTransport();
|
||||||
final responder = _startFakeFlightController(transport);
|
final responder = _startFakeFlightController(transport);
|
||||||
|
|||||||
Reference in New Issue
Block a user