|
|
|
@@ -144,84 +144,97 @@ class _DroneStatusMessagesPanelState extends ConsumerState<DroneStatusMessagesPa
|
|
|
|
|
// Wertkachel bieten wuerde.
|
|
|
|
|
_sensorStatusRow(frame),
|
|
|
|
|
const SizedBox(height: 8),
|
|
|
|
|
..._statusGrid([
|
|
|
|
|
// Arming + erste 3 Temperaturen als eigenes Zeilenpaar direkt
|
|
|
|
|
// unter der Sensor-Status-Zeile (Doku), noch vor den
|
|
|
|
|
// farbcodierten Feldern.
|
|
|
|
|
_statusRow(
|
|
|
|
|
'Arming',
|
|
|
|
|
frame == null ? '—' : (frame.armed ? 'Armed' : 'Disarmed'),
|
|
|
|
|
level: frame?.armed == true ? SignalLevel.green : null,
|
|
|
|
|
),
|
|
|
|
|
_statusRow('Temperatures', _formatTemperatures(frame)),
|
|
|
|
|
..._statusRows([
|
|
|
|
|
// Arming + erste 3 Temperaturen + Flugmodus als eigene Zeile
|
|
|
|
|
// direkt unter der Sensor-Status-Zeile (Doku), noch vor den
|
|
|
|
|
// farbcodierten Feldern. Flugmodus zeigt bei fehlender
|
|
|
|
|
// Nav-Uebersteuerung "Idle" (siehe MspNavMode._modeNames) - das
|
|
|
|
|
// heisst nur "kein GPS-Nav-Modus aktiv", nicht zwingend "steht am
|
|
|
|
|
// Boden" (reine Angle/Horizon/Acro-Fluglage ist ueber
|
|
|
|
|
// MSP_NAV_STATUS nicht sichtbar).
|
|
|
|
|
[
|
|
|
|
|
_statusRow(
|
|
|
|
|
'Arming',
|
|
|
|
|
frame == null ? '—' : (frame.armed ? 'Armed' : 'Disarmed'),
|
|
|
|
|
level: frame?.armed == true ? SignalLevel.green : null,
|
|
|
|
|
),
|
|
|
|
|
_statusRow('Temperatures', _formatTemperatures(frame)),
|
|
|
|
|
_statusRow('Flight mode', frame != null ? frame.flightMode : '—'),
|
|
|
|
|
],
|
|
|
|
|
// Farbcodierte Felder (Doku: "drone status felder im menue auch
|
|
|
|
|
// farblich kodieren", Schwellwerte je Drohnenprofil - siehe
|
|
|
|
|
// computeTelemetryFieldStatus), informelle Felder ohne Ampel-
|
|
|
|
|
// Bewertung (Koordinaten/Heading/Alt/Speed/Vertical speed)
|
|
|
|
|
// danach. Je 2 Felder pro Zeile (Doku: "platz effizienter
|
|
|
|
|
// nutzen").
|
|
|
|
|
_statusRow('GPS satellites', frame != null ? '${frame.numSat}' : '—', level: fieldStatus.numSat),
|
|
|
|
|
_statusRow(
|
|
|
|
|
'GPS precision (HDOP)',
|
|
|
|
|
frame != null && frame.hasFix ? frame.hdop.toStringAsFixed(2) : '—',
|
|
|
|
|
level: fieldStatus.hdop,
|
|
|
|
|
),
|
|
|
|
|
_statusRow(
|
|
|
|
|
'Current draw',
|
|
|
|
|
frame != null ? '${frame.currentA.toStringAsFixed(1)} A' : '—',
|
|
|
|
|
level: fieldStatus.currentA,
|
|
|
|
|
),
|
|
|
|
|
_statusRow(
|
|
|
|
|
'Battery voltage',
|
|
|
|
|
frame != null ? '${frame.batteryVoltage.toStringAsFixed(2)} V' : '—',
|
|
|
|
|
level: fieldStatus.batteryVoltage,
|
|
|
|
|
),
|
|
|
|
|
_statusRow('RX quality', frame != null ? '${frame.linkQuality}%' : '—', level: fieldStatus.rxQuality),
|
|
|
|
|
_statusRow('RX SNR', frame != null ? '${frame.snrDb} dB' : '—', level: fieldStatus.rxSnr),
|
|
|
|
|
_statusRow(
|
|
|
|
|
'GPS coordinates',
|
|
|
|
|
frame != null && frame.hasFix
|
|
|
|
|
? '${frame.lat.toStringAsFixed(6)}, ${frame.lon.toStringAsFixed(6)}'
|
|
|
|
|
: '—',
|
|
|
|
|
),
|
|
|
|
|
_statusRow('Heading', frame != null ? '${frame.headingDeg.round()}°' : '—'),
|
|
|
|
|
_statusRow('Altitude', _formatAltitude(frame)),
|
|
|
|
|
_statusRow('Speed', frame != null ? '${frame.speedMs.toStringAsFixed(1)} m/s' : '—'),
|
|
|
|
|
// Ans Ende der Liste (Doku: "ans ende der liste noch die
|
|
|
|
|
// steig/sinkrate hinzufuegen") - bildet mit "Speed" das letzte
|
|
|
|
|
// Zeilenpaar.
|
|
|
|
|
_statusRow('Vertical speed', _formatVerticalSpeed(frame)),
|
|
|
|
|
// danach.
|
|
|
|
|
[
|
|
|
|
|
_statusRow(
|
|
|
|
|
'GPS fix type',
|
|
|
|
|
_formatFixType(frame),
|
|
|
|
|
level: frame != null ? _fixTypeLevel(frame.fixType) : null,
|
|
|
|
|
),
|
|
|
|
|
_statusRow('GPS satellites', frame != null ? '${frame.numSat}' : '—', level: fieldStatus.numSat),
|
|
|
|
|
_statusRow(
|
|
|
|
|
'GPS precision (HDOP)',
|
|
|
|
|
frame != null && frame.hasFix ? frame.hdop.toStringAsFixed(2) : '—',
|
|
|
|
|
level: fieldStatus.hdop,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
_statusRow(
|
|
|
|
|
'Current draw',
|
|
|
|
|
frame != null ? '${frame.currentA.toStringAsFixed(1)} A' : '—',
|
|
|
|
|
level: fieldStatus.currentA,
|
|
|
|
|
),
|
|
|
|
|
_statusRow(
|
|
|
|
|
'Battery voltage',
|
|
|
|
|
frame != null ? '${frame.batteryVoltage.toStringAsFixed(2)} V' : '—',
|
|
|
|
|
level: fieldStatus.batteryVoltage,
|
|
|
|
|
),
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
_statusRow('RX quality', frame != null ? '${frame.linkQuality}%' : '—', level: fieldStatus.rxQuality),
|
|
|
|
|
_statusRow('RX SNR', frame != null ? '${frame.snrDb} dB' : '—', level: fieldStatus.rxSnr),
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
_statusRow(
|
|
|
|
|
'GPS coordinates',
|
|
|
|
|
frame != null && frame.hasFix
|
|
|
|
|
? '${frame.lat.toStringAsFixed(6)}, ${frame.lon.toStringAsFixed(6)}'
|
|
|
|
|
: '—',
|
|
|
|
|
),
|
|
|
|
|
_statusRow('Heading', frame != null ? '${frame.headingDeg.round()}°' : '—'),
|
|
|
|
|
],
|
|
|
|
|
[
|
|
|
|
|
_statusRow('Altitude', _formatAltitude(frame)),
|
|
|
|
|
_statusRow('Speed', frame != null ? '${frame.speedMs.toStringAsFixed(1)} m/s' : '—'),
|
|
|
|
|
],
|
|
|
|
|
// Ans Ende der Liste (Doku: "ans ende der liste noch die
|
|
|
|
|
// steig/sinkrate hinzufuegen") - eigene Zeile, da kein Partnerfeld
|
|
|
|
|
// mehr uebrig ist.
|
|
|
|
|
[_statusRow('Vertical speed', _formatVerticalSpeed(frame))],
|
|
|
|
|
]),
|
|
|
|
|
],
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Ordnet die Statuszeilen zu je 2 pro Zeile an (Doku: "2 felder pro zeile
|
|
|
|
|
/// im drone status menue erlauben um platz effizienter zu nutzen") - bei
|
|
|
|
|
/// ungerader Feldanzahl bleibt die letzte Zeile einspaltig statt sie
|
|
|
|
|
/// kuenstlich zu strecken.
|
|
|
|
|
List<Widget> _statusGrid(List<Widget> tiles) {
|
|
|
|
|
final rows = <Widget>[];
|
|
|
|
|
for (var i = 0; i < tiles.length; i += 2) {
|
|
|
|
|
final hasPair = i + 1 < tiles.length;
|
|
|
|
|
// Kein CrossAxisAlignment.stretch: diese Row sitzt direkt als
|
|
|
|
|
// ListView-Kind, das seinen Kindern unbegrenzte Hoehe gibt (0<=h<=
|
|
|
|
|
// Infinity) - stretch verlangt aber eine endliche Hoehe, um die Kacheln
|
|
|
|
|
// darauf zu strecken, und wirft sonst "BoxConstraints forces an
|
|
|
|
|
// infinite height" (fuehrte zum komplett schwarzen Status-Tab). Default
|
|
|
|
|
// (center) reicht, da beide Kacheln einer Zeile ohnehin fast gleich
|
|
|
|
|
// hohen Inhalt haben.
|
|
|
|
|
rows.add(Row(
|
|
|
|
|
children: [
|
|
|
|
|
Expanded(child: tiles[i]),
|
|
|
|
|
if (hasPair) ...[
|
|
|
|
|
const SizedBox(width: 8),
|
|
|
|
|
Expanded(child: tiles[i + 1]),
|
|
|
|
|
/// Ordnet die Statuszeilen in explizit vorgegebenen Gruppen an (Doku: "2
|
|
|
|
|
/// felder pro zeile im drone status menue erlauben um platz effizienter zu
|
|
|
|
|
/// nutzen", inzwischen mit einzelnen 3er-Zeilen wie Arming/Temperatures/
|
|
|
|
|
/// Flight mode) - anders als eine reine Zweier-Paarbildung braucht das
|
|
|
|
|
/// gemischte 2er/3er-Layout eine explizite Gruppierung pro Zeile statt
|
|
|
|
|
/// automatischem Chunking.
|
|
|
|
|
List<Widget> _statusRows(List<List<Widget>> rows) {
|
|
|
|
|
return [
|
|
|
|
|
for (final tiles in rows)
|
|
|
|
|
Row(
|
|
|
|
|
children: [
|
|
|
|
|
for (var i = 0; i < tiles.length; i++) ...[
|
|
|
|
|
if (i > 0) const SizedBox(width: 8),
|
|
|
|
|
Expanded(child: tiles[i]),
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
],
|
|
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
return rows;
|
|
|
|
|
),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static const _signalColors = {
|
|
|
|
@@ -359,6 +372,25 @@ class _DroneStatusMessagesPanelState extends ConsumerState<DroneStatusMessagesPa
|
|
|
|
|
return '$estimated (GPS ${frame.gpsAltitudeM.toStringAsFixed(0)} m)';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// gpsFixType_e (siehe TelemetryFrame.fixType-Doku): 0=kein Fix, 1=2D,
|
|
|
|
|
// 2=3D - alles darueber hinaus (bisher unbekannt bei iNAV 9.1.0) wird
|
|
|
|
|
// trotzdem angezeigt statt verschluckt.
|
|
|
|
|
String _formatFixType(TelemetryFrame? frame) {
|
|
|
|
|
if (frame == null) return '—';
|
|
|
|
|
return switch (frame.fixType) {
|
|
|
|
|
0 => 'No fix',
|
|
|
|
|
1 => '2D fix',
|
|
|
|
|
2 => '3D fix',
|
|
|
|
|
final other => 'Unknown ($other)',
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SignalLevel _fixTypeLevel(int fixType) {
|
|
|
|
|
if (fixType >= 2) return SignalLevel.green;
|
|
|
|
|
if (fixType == 1) return SignalLevel.yellow;
|
|
|
|
|
return SignalLevel.red;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String _formatTemperatures(TelemetryFrame? frame) {
|
|
|
|
|
if (frame == null) return '—';
|
|
|
|
|
return frame.temperaturesC.map((t) => t == null ? '—' : '${t.round()}°').join(' / ');
|
|
|
|
|