MSP_RAW_GPS liefert bereits eine eigene Hoehe (gpsSol.llh.alt, Offset 10,
u16 Meter) - bislang ungenutzt/uebersprungen. Jetzt als eigenes Feld
(MspGpsReading.altitudeM -> TelemetryFrame.gpsAltitudeM) geparst und im
selben "Altitude"-Feld wie die bisherige barometrisch/GPS-fusionierte
Schaetzung angezeigt ("120 m (GPS 119 m)"), statt einer eigenen Kachel -
nur bei vorhandenem Fix angehaengt, analog zur bestehenden hasFix-Handhabung
bei GPS coordinates/HDOP.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Neue Zeile ganz oben mit farblich kodierten Sensor-Badges (ACC/BARO/MAG/
GPS/RNG/OF/PITOT/TEMP, aus dem sensorStatus-Bitfeld von MSP2_INAV_STATUS,
zuvor schon dokumentiert aber ungenutzt), darunter ein Zeilenpaar fuer
Arming-Status und die ersten 3 Temperatursensoren (neu: MSP2_INAV_
TEMPERATURES, 0x201E). Ans Ende der Liste die Steig-/Sinkrate (vario aus
MSP_ALTITUDE, bislang nur die Hoehe selbst wurde daraus gelesen).
- msp_commands.dart: inavTemperatures-Konstante ergaenzt; die Sensor-
Status-Bits (bisher nur als Doc-Kommentar bei MSP2_INAV_STATUS notiert)
zu echten Konstanten (MspSensorStatusBits) promoviert, da jetzt
tatsaechlich gebraucht.
- msp_telemetry_codec.dart: parseMspAltitudeVerticalSpeedMs,
parseMspInavStatusSensorStatus, parseMspInavTemperaturesC ergaenzt +
Unit-Tests.
- TelemetryFrame: sensorStatusBits (Bitmaske, protokollneutral
durchgereicht wie navMode), temperaturesC (erste 3 Sensoren, null je
nicht konfiguriertem Slot), verticalSpeedMs.
- msp_telemetry_poller.dart: vario kommt aus derselben MSP_ALTITUDE-
Antwort wie die Hoehe (keine zusaetzliche Anfrage), sensorStatus aus
derselben MSP2_INAV_STATUS-Antwort wie ARMED; MSP2_INAV_TEMPERATURES neu
im 2-Hz-Statuszyklus abgefragt.
- MockFlightControllerLink: synthetische Sensor-/Temperatur-/Vario-Werte
(kein Pitot/Rangefinder/Opflow am T1 Ranger vorgesehen), leicht
schwankend, damit die neuen Felder auch ohne Hardware sichtbar auf
Werteaenderungen reagieren.
- drone_status_messages_panel.dart: eigene Sensor-Status-Zeile (lokale
Bit-Konstanten statt MSP-Import, analog zum bestehenden navMode-Muster
in domain/telemetry/drone_status.dart, damit die UI protokollneutral
bleibt), Arming+Temperaturen-Zeilenpaar, Vertical-speed-Zeile am Ende.
Auf dem Pixel_10a-Emulator verifiziert: Sensor-Badges gruen/grau je nach
Bitmaske, Arming/Temperaturen-Paar, Vertical speed am Listenende.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- detectSystemMessages() erkennt jetzt auch den Uebergang zu einem GPS-Fix
(flankengetriggert wie Battery/Failsafe), Meldung "GPS fix acquired (N
satellites)".
- Die automatisch erkannten Verbindungsmeldungen heissen jetzt "Drone
connected"/"Drone disconnected" statt "Connected"/"Connection lost" -
praeziser an die tatsaechliche Bedeutung angelehnt (Eintreffen/Ausbleiben
echter Telemetrie-Frames, nicht nur des rohen Socket-Zustands).
- DroneProfile: feste Pack-Alarmspannung (batteryVoltageGreenMinV/RedMinV)
ersetzt durch batteryCellCount + Pro-Zelle-Schwellwerte
(batteryVoltageGreenMinPerCellV/RedMinPerCellV, LiPo-Standardwerte 3.4/3.2
V als Default). Die alten Feldnamen bleiben als berechnete Getter
(Zellenzahl * Pro-Zelle-Wert) erhalten, damit
telemetry_field_status.dart unveraendert bleibt. T1-Ranger-Standardprofil
auf 4S gesetzt.
- DB-Schema v8 -> v9 (additiv, alte Pack-Spannungs-Spalten bleiben als tote
Spalten stehen), Repository und Share-Codec-Im-/Export entsprechend
angepasst; alte Exportdateien ohne die neuen Felder fallen auf die
DroneProfile-Defaults zurueck statt eine unbekannte Zellenzahl zu raten.
- DroneProfileEditor: "Battery voltage alarm"-Gruppe um ein Zellenzahl-Feld
erweitert und auf V/Zelle umbenannt.
- Tests ergaenzt/angepasst: GPS-Fix-Erkennung (Unit + Provider-Integration),
neuer v8->v9-Migrationstest analog zum bestehenden v7->v8-Test,
Connected/Disconnected-Umbenennung in Provider- und Widget-Tests,
Cell-Count-Roundtrip in Repository- und Share-Codec-Tests.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Renames DroneEvent/DroneEventSeverity/droneEventLogProvider/
DroneStatusWarningsPanel to SystemMessage/SystemMessageSeverity/
systemMessageLogProvider/DroneStatusMessagesPanel throughout, matching
what the tab now actually shows - general system messages, not just
drone-health warnings.
Adds a new SystemMessageSeverity.info level (blue) for messages that
aren't a warning/error, and three new message sources on top of the
existing battery/failsafe detection:
- "Connected", logged the first time telemetryProvider produces a frame
after having none - the mirror image of the existing "Connection lost"
detection (which already fires on the first stream error after having
had data), so no new transport-specific dependency was needed.
- "Connection type changed to X", from watching connectionSettingsProvider
(skips the initial load so it doesn't fire on every app start).
- "Mission sent (N waypoints)" / "Mission upload failed: ...", logged
from FlyScreen's send handler via a new public log() method on the
notifier, alongside the existing snackbar.
Verified on the Pixel_10a emulator: entering Fly mode logs "Connected"
once the mock telemetry starts, and tapping the send button logs
"Mission sent (3 waypoints)" right after the snackbar.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fly mode previously showed just a black placeholder. It now renders the
same MissionMap (route + waypoint markers) and footer (warnings banner,
altitude profile, BottomStatsBar) as the Plan screen, minus the
reticle/wheels since flying observes the route rather than replanning it.
Waypoints can still be edited via the details list for spontaneous
in-flight changes.
Live drone position comes from a new telemetryProvider/
flightControllerLinkProvider pair (autoDispose), the first place
FlightControllerLink/TelemetryFrame get wired into the UI - currently
backed by MockFlightControllerLink until a real MSP transport exists
(Doku 4.19). A drone marker renders on the map once a telemetry frame
arrives.
The Fly-mode header keeps the fit-to-area button but replaces "zoom to
home" with "zoom to drone" (FlyMapControls) - the first waypoint isn't a
meaningful reference point anymore once airborne.
Extracted computeMissionStats and MissionFooterBar out of PlanScreen so
Plan/Fly share the exact same stats/footer logic instead of duplicating
it, and extracted NavIconButton out of MapSearchControls so both Plan's
and Fly's map controls use the same button widget.
Widget tests that now mount FlyScreen switch from the ProviderScope-based
_wrap() helper to a manual ProviderContainer with an explicit
FlightControllerLink.disconnect() call before test end - the mock's
Timer.periodic doesn't get cancelled by container disposal alone, and
flutter_test's pending-timer check runs before addTearDown callbacks.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Kartendrehung (Doku 4.6): flutter_map's Zwei-Finger-Twist-Geste bewusst
deaktiviert (InteractionOptions mit InteractiveFlag.all & ~rotate in
mission_map.dart) - die Drohne fliegt nordorientiert, eine gedrehte Karte
wuerde die Peilung von Wegpunkten/Reticle nur verwirren, zumal der HTML-
Demonstrator gar keine Drehung kennt.
Warnungsliste (Doku 3.10), analog dem #warningsBtn/#warningsPanel-Flow im
HTML-Demonstrator:
- domain/mission/mission_warnings.dart: computeMissionWarnings() sammelt
alle aktuell zutreffenden Probleme (Route-Geometrie ausserhalb der
Drohnenfaehigkeiten, zu starker Wind, zu geringer Bodenabstand,
Reichweite/Ausdauer ueberschritten) zu einer Liste aus {text, action}-
Eintraegen - reine Funktion aus bereits vorhandenen Zwischenergebnissen,
unabhaengig von Riverpod/Widgets testbar.
- domain/wind/wind_math.dart: computeLegWindBad() ergaenzt (Bodenge-
schwindigkeit <= 1 m/s oder Windgeschwindigkeit > Drohnen-Maximum) -
bislang gab es nur die Windkomponente/Bodengeschwindigkeit selbst, aber
keine "zu stark"-Markierung.
- ui/widgets/warnings_panel.dart: Vollbild-Liste, jede Zeile verweist per
Aktionslabel auf den Loesungs-Screen ("Open altitude view"/"Open speed
view"/"Show on map").
- ui/widgets/bottom_stats_bar.dart: roter Warnungs-Button neben den
Mission-/Drohnen-Chips, nur sichtbar solange Warnungen aktiv sind.
- ui/widgets/waypoint_list_panel.dart: `_PanelTab` zu `PanelTab` public
gemacht plus neuer `initialTab`-Parameter, damit das Panel direkt auf
dem Altitude- oder Speed-Tab geoeffnet werden kann statt immer auf der
Liste zu starten.
- ui/providers/map_controller_provider.dart: fitMapToWaypoints()-Hilfs-
funktion extrahiert und in MapSearchControls' Fit-Button (vorher eigene
Kopie der Logik) sowie fuer die "Show on map"-Warnungsaktion
wiederverwendet.
- ui/screens/plan/plan_screen.dart: berechnet die Warnungsliste, ersetzt
den bisherigen Einzel-Banner (nur "Route exceeds...") durch alle
aktuellen Warnungstexte (durch " · " getrennt, wie im HTML-Demonstrator)
und verdrahtet Warnungs-Button/-Liste mit der passenden Navigation.
Getestet: 4 neue Unit-Tests fuer computeLegWindBad, 7 fuer
computeMissionWarnings (inkl. Grenzfaelle: veraltetes Gelaendeprofil,
kein Limit bei maxRangeM/maxEnduranceMin <= 0), 2 neue Widget-Tests
(Warnungs-Button erscheint bei Reichweiten-Ueberschreitung und oeffnet
die Liste; Antippen einer Wind-Warnung oeffnet den Speed-Tab direkt).
Alle 110 Tests sowie flutter analyze bestehen. Manuell auf dem
Pixel_10a-Emulator verifiziert: Steigraten-Ueberschreitung (Altitude auf
220m bei kurzer Distanz) macht Route/Banner/Warnungs-Button rot,
Warnungsliste zeigt den Eintrag mit "Show on map", Antippen schliesst
die Liste und zoomt die Karte korrekt auf die Bounding-Box beider
Wegpunkte.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Rundet das Altitude-Tab um ein reales Gelaendeprofil ab, damit sichtbar
wird, ob die geplante Flughoehe ausreichend Bodenabstand haelt:
- domain/mission/terrain_math.dart: reine Sampling-/Geometrie-Funktionen
(routeKeyForTerrain fuer Caching, distanzbasiertes Sampling alle 30m,
lineare Sollhoehen-Interpolation, terrainDangerRanges fuer Abschnitte
mit < 10m Bodenabstand) - unabhaengig testbar ohne Netzwerk/UI.
- services/terrain/terrain_service.dart: laedt AWS-Terrarium-PNG-Kacheln
(elevation-tiles-prod, zoom 13) und dekodiert sie ueber dart:ui
(instantiateImageCodec/toByteData), ohne zusaetzliches Bildpaket.
Elevation = R*256 + G + B/256 - 32768 pro Pixel; Kacheln werden pro
Route nur einmal geladen (nach Kachel gruppierte Sample-Punkte).
- ui/providers/terrain_provider.dart: cached das geladene Profil ueber
routeKeyForTerrain - reine Werteaenderungen (Hoehe/Speed) loesen keinen
erneuten Kachel-Download aus, nur eine tatsaechliche Ortsverschiebung.
- ui/widgets/full_value_chart.dart: _paintDangerBands/_paintTerrain
zeichnen rote Gefahrenbaender bzw. die braune Gelaende-Flaeche, in der
gleichen Reihenfolge wie im HTML-Demonstrator (dangerHtml, grid,
terrainHtml, dann Soll-Kurve obenauf).
- ui/widgets/waypoint_list_panel.dart: triggert ensureFor() beim
Wechsel auf den Altitude-Tab sowie bei Routenaenderungen waehrend
dieser aktiv ist (ref.listenManual auf currentMissionProvider).
Getestet: 13 neue Unit-Tests fuer die reine Geometrie/Sampling-Logik,
3 Service-Tests mit einer zur Testzeit synthetisch erzeugten PNG
(dart:ui-Encoding, keine Testasset-Datei noetig). Alle 80 Tests sowie
flutter analyze bestehen. Manuell auf dem Pixel_10a-Emulator verifiziert:
Altitude-Tab zeigt das reale Amsterdam-Gelaendeprofil (nahe Meereshoehe)
korrekt als Flaeche unter der Sollhoehen-Linie.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Portiert das Wind-System des HTML-Demonstrators (Doku 3.8) vollstaendig:
Open-Meteo-Winddaten, interpoliert zwischen Druckflaechen- und festen
Nabenhoehen-Stuetzpunkten auf die tatsaechliche Zielhoehe.
Domain (lib/domain/wind/wind_math.dart, reines Dart): Hoehen-
Interpolation (linear + zirkulaer fuer Windrichtung), Peilung zwischen
zwei Punkten, Kopf-/Rueckenwind-Komponente pro Flugleg - 1:1 uebernommen
aus interpolateWindAtHeight()/bearingBetween()/headTailwindComponent()
des Demonstrators, per Unit-Tests abgesichert.
Service (lib/services/wind/wind_service.dart): zwei getrennte,
fokussierte Open-Meteo-Requests statt eines kombinierten (Doku 4.10 -
ein 35-Variablen-Request verursachte im Demonstrator Modellauswahl-
Fehler). Drei Verbraucher: Kopfleisten-Pille (120 m AGL ueber
Referenzort), gebuendelter Wegpunkt-Fetch (ein Request fuer alle
Koordinaten via Open-Meteos Mehrfach-Location-Syntax) und das
Validierungspanel (rohe Messwerte je Nabenhoehe/Druckflaeche). Per
MockClient getestet (kein echter Netzwerkzugriff in Tests).
UI:
- HeaderWindPill (TopModeBar, nur ausserhalb Fly-Modus): zeigt
Windrichtung/-geschwindigkeit, Antippen schaltet perspektivisch die
Wind-Marker der Wegpunkte um (Zustand vorbereitet, HTML-Demonstrator:
showPerWaypointWind).
- WaypointListPanel: "Fetch wind"/"Validate"-Buttons im Panel-Header,
neue WIND-Spalte in der Liste.
- WindValidatePanel: neue Vollbild-Route (gleiches Muster wie
WaypointListPanel) mit Rohdaten je Hoehenstufe inkl. Boeen (Doku 4.9:
Boeen nur hier, nicht in der Wegpunktanzeige) und Interpolationsergebnis.
- FullValueChart (Speed-Tab): Kopf-/Rueckenwind-Diamanten pro Leg samt
gestrichelter Nulllinie sowie gestrichelte Bodengeschwindigkeits-
Stufenlinie.
Bug gefunden und gefixt waehrend der Emulator-Verifikation: die
Y-Achsen-Autoskalierung klemmte weiterhin auf den gueltigen Drehrad-
Wertebereich (13-25 m/s), wodurch nahe Null liegende Windkomponenten
(bei schwachem Wind der Normalfall) ausserhalb des sichtbaren Bereichs
lagen und weder Diamanten noch Nulllinie zu sehen waren. Der HTML-
Demonstrator klemmt computeSpeedChartRange() bewusst nicht auf SPD_MIN/
SPD_MAX; das Klemmen in FullValueChart._range() jetzt entsprechend
entfernt.
Waypoint-Modell um windSpeedMs/windDirFromDeg/windElevationM erweitert
(nullable, reine Planungshilfe - der Encoder liest diese Felder nie,
Doku 3.2). currentMissionProvider bekommt replaceAll() fuer den
gebuendelten Wind-Fetch, der alle Wegpunkte gleichzeitig aktualisiert.
Verifiziert: flutter analyze (0 issues), flutter test (44/44, davon 14
neue Wind-Mathematik- und 6 neue WindService-Tests), manuell auf
Pixel_10a-Emulator mit echtem Open-Meteo-Netzwerkzugriff - Kopfleisten-
Pille zeigt Live-Wind, Fetch/Validate fuellen Liste bzw. oeffnen das
Validierungspanel mit echten Messwerten, Speed-Plot zeigt nach dem Fix
Diamanten/Nulllinie/Bodengeschwindigkeit korrekt skaliert.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- RouteGeometry (lib/domain/mission/route_geometry.dart): physically
grounded flight path - straight segments + tangential arcs at each
course change, sized from the drone's minimum turn radius (doc 3.7/4.6),
replacing a naive spline that would suggest unrealistic turn radii.
Marks a vertex "bad" (turn angle >= 160°, tangent length exceeding 90%
of either adjacent leg, or exceeding the waypoint's catch radius) and a
leg "bad" when the required climb/descent rate exceeds the drone's
max climb/descent rate. Pure Dart, no Flutter dependency, so it stays
usable if the mission domain is ever split into its own package (4.22).
Wind-based turn-radius correction from the prototype isn't ported yet -
the wind system (doc 3.8) doesn't exist in the Flutter app.
- MissionMap now draws each RouteSegment as its own Polyline, colored red
when bad instead of a single plain white line; waypoint markers turn red
too when their vertex is bad.
- ValueWheel: vertical drag-to-adjust tape control (HTML prototype's
#altWheel/#spdWheel), custom-painted tick marks, blue center indicator,
gradient fade at top/bottom. Wired into PlanScreen on both screen edges
with Alt/Speed readouts.
- curAlt/curSpeed now live in PlanScreen state instead of fixed constants:
dropping a new waypoint uses whatever the wheels are currently set to;
entering editing mode on an existing waypoint loads its values into the
wheels; adjusting a wheel while editing writes live into that waypoint
(provider gained setAltitude/setSpeed to match the existing
moveWaypoint/toggleAction pattern).
Added dedicated unit tests for the geometry (empty list, straight line,
feasible 90° turn producing a line-arc-line segment sequence, an
infeasible near-180° turn, and an infeasible descent rate) rather than
relying on eyeballing it on the emulator - this is exactly the kind of
ported-math correctness that's hard to verify visually but easy to get
subtly wrong. All 11 tests (previous 6 + these 5) and flutter analyze
pass. Also manually verified the wheels on the Pixel_10a emulator: drag
changes the value and the on-screen readout in real time.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>