Files
PilotVault/Fly App/lib/fly/modes/intelligent_modes.dart
T
tajniak81andClaude Opus 5.5 636da1ac2c Give the Fly App every remaining GO 4 flight mode and the missing settings
Intelligent modes: Point of Interest (orbit on the map, live radius and
speed), Follow Me (follows the phone), QuickShots on a boxed subject,
Course Lock / Home Lock, Tripod and Cinematic, a Free hyperlapse, and
Waypoints 2.0 for V2-only aircraft. ActiveTrack gains Spotlight Pro and
Circle. Every mode has a first-use tutorial and stops from the rail.
Waypoints can resume an interrupted mission, change cruise speed in
flight and check terrain clearance (Open-Meteo, on tap).

Settings: units, C1/C2 actions, dual-operator master/slave, HDMI/SDI
output, a link spectrum chart, Wi-Fi name and password, aircraft name,
video cache, battery warning history and pairing, non-smart cell
voltages. Calibration: compass interference meter, per-IMU calibration,
lens infinity focus and a first-manual-focus prompt. Also an RTK chip,
gimbal movement presets and motor tuning, Precision Takeoff gated on the
flight controller, and the precise GEO database's server version.

Built and unit/widget-tested only; nothing has flown yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 23:06:16 +02:00

250 lines
9.9 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import '../product_profile.dart';
/// The Intelligent Flight Modes picker's catalogue (plan P7, GO 4's
/// `DJILeftMenuNavigationView` + `gs_main_view`).
///
/// Which modes a product has comes from [ProductProfile.modes] (the product
/// matrix until runtime capture confirms it); which of those this app can fly
/// is [ModeInfo.built]. A mode that is supported but not built is listed
/// greyed out rather than hidden, so the pilot sees what is coming.
class ModeInfo {
const ModeInfo({
required this.mode,
required this.label,
required this.icon,
required this.group,
required this.blurb,
required this.terms,
this.tutorial = const <String>[],
this.built = false,
});
final IntelligentMode mode;
final String label;
/// A `PVIcon` name.
final String icon;
/// `Map`, `Vision`, `Shots` or `Orientation`, as the picker groups them.
final String group;
/// One line under the tile.
final String blurb;
/// The first-use disclaimer: what the pilot stays responsible for.
final List<String> terms;
/// GO 4's first-use tutorial: how to fly the mode, step by step. Shown once
/// after the terms, and again from the mode's panel.
final List<String> tutorial;
/// This app can fly the mode.
final bool built;
}
abstract final class ModeCatalog {
static const List<String> _common = <String>[
'You remain responsible for the flight. Keep the aircraft in sight and be ready to take over with the remote.',
'The aircraft flies itself in this mode. Push the remote\'s pause button, or tap Stop, to make it hover.',
];
static const List<String> _waypointTutorial = <String>[
'Tap the map to drop waypoints; tap a waypoint to edit its altitude, speed, heading, gimbal and camera action.',
'Long-press and drag a waypoint to move it. The + between two waypoints inserts one there.',
'ROUTE sets the cruise speed, the heading and what happens at the end; LIBRARY saves the route.',
'START checks the route against the aircraft, uploads it and flies it. PAUSE holds the aircraft where it is.',
];
static const List<ModeInfo> all = <ModeInfo>[
ModeInfo(
mode: IntelligentMode.waypoint,
label: 'Waypoints',
icon: 'route',
group: 'Map',
blurb: 'Plan a route on the map and fly it.',
built: true,
terms: <String>[
..._common,
'The route flies straight between waypoints at the altitudes you set. It does not avoid terrain, trees or buildings: check the route and its altitudes before you start.',
'If the remote loses signal the aircraft follows the route\'s signal-lost setting.',
],
tutorial: _waypointTutorial,
),
ModeInfo(
mode: IntelligentMode.waypointV2,
label: 'Waypoints 2.0',
icon: 'route',
group: 'Map',
blurb: 'Enterprise waypoint missions.',
built: true,
terms: <String>[
..._common,
'Waypoints 2.0 flies the route\'s positions, altitudes, speeds and headings. Camera actions are not sent to the aircraft yet: run the camera yourself.',
],
tutorial: _waypointTutorial,
),
ModeInfo(
mode: IntelligentMode.pointOfInterest,
label: 'Point of Interest',
icon: 'circle',
group: 'Map',
blurb: 'Circle a subject at a set radius.',
built: true,
terms: <String>[
..._common,
'The aircraft circles at a fixed height and does not brake for obstacles on the circle: check the whole circle is clear.',
],
tutorial: <String>[
'Tap the map on the subject, or AIRCRAFT to use where the aircraft is now.',
'Set the radius, the altitude and the speed; pick the direction and which way the aircraft faces.',
'GO flies to the circle and starts circling. Radius and speed can change while it circles.',
'The remote\'s sticks nudge the orbit; PAUSE holds position, STOP ends it.',
],
),
ModeInfo(
mode: IntelligentMode.followMe,
label: 'Follow Me',
icon: 'user',
group: 'Map',
blurb: 'The aircraft follows this phone.',
built: true,
terms: <String>[
..._common,
'The aircraft follows the phone\'s GPS, which can be off by several metres, and it does not avoid obstacles while it follows.',
'Keep the phone with you and its screen on: if the app stops sending your position the aircraft stops following and hovers.',
],
tutorial: <String>[
'Take off and climb to at least 10 m with open space around you.',
'Set the height to follow at and whether the aircraft faces you or the remote steers its heading.',
'GO: the aircraft keeps its distance as you walk. Keep the app open.',
'STOP, or the remote\'s pause button, ends it.',
],
),
ModeInfo(
mode: IntelligentMode.activeTrack,
label: 'ActiveTrack',
icon: 'crosshair',
group: 'Vision',
blurb: 'Draw a box around a subject to follow it.',
built: true,
terms: <String>[
..._common,
'ActiveTrack follows what the camera sees. It can lose the subject behind trees or in crowds, and it does not see obstacles to the sides or behind on every aircraft.',
'Do not use it to follow cars or boats at speed, or over water and roads you cannot see the end of.',
],
tutorial: <String>[
'Drag a box round the subject on the video (or tap a person-sized one), then GO.',
'Trace follows behind, Profile alongside, Spotlight keeps the camera on it while you fly.',
'A green box is locked on, amber is unsure; confirm with ✓ when the aircraft asks.',
'Gestures: with Gesture mode on (Settings → Vision), raise your arms to be tracked, frame your fingers for a photo.',
],
),
ModeInfo(
mode: IntelligentMode.tapFly,
label: 'TapFly',
icon: 'chevronsRight',
group: 'Vision',
blurb: 'Tap the video to fly that way.',
built: true,
terms: <String>[
..._common,
'TapFly keeps flying towards the point until you stop it. Obstacle avoidance only works in the direction the sensors face, and not at all in Free.',
],
tutorial: <String>[
'Tap where to fly on the video, then GO.',
'Forward flies towards the point; Backward away from it; Free lets you turn the aircraft while it flies.',
'Set the speed with − / +; tap a new point to change course.',
],
),
ModeInfo(
mode: IntelligentMode.quickShots,
label: 'QuickShots',
icon: 'dronie',
group: 'Shots',
blurb: 'Dronie, Rocket, Circle, Helix…',
built: true,
terms: <String>[
..._common,
'A QuickShot flies a set path around the subject, up to 120 m away and high: keep that space clear.',
],
tutorial: <String>[
'Pick the shot, draw a box round the subject on the video, then GO.',
'The aircraft records the video, flies the path and comes back to where it started.',
'STOP, the sticks or the remote\'s pause button end it early.',
],
),
ModeInfo(
mode: IntelligentMode.hyperlapse,
label: 'Hyperlapse',
icon: 'hyperlapse',
group: 'Shots',
blurb: 'A moving timelapse.',
built: true,
terms: <String>[
'You fly the aircraft during a Free hyperlapse; the camera only shoots the frames. Fly slowly and steadily.',
'Stop the sequence before landing or flying home.',
],
tutorial: <String>[
'Set the interval between frames and how long the finished video should be.',
'START shoots the frames; fly a slow, steady line meanwhile.',
'The frames are on the SD card; assemble them into the video on a computer.',
],
),
ModeInfo(
mode: IntelligentMode.courseLock,
label: 'Course Lock',
icon: 'compass',
group: 'Orientation',
blurb: 'Forward stays the current heading.',
built: true,
terms: <String>[
'Course Lock changes what the sticks do: forward is the locked course, whichever way the aircraft faces. Turn it off before flying normally again.',
],
tutorial: <String>[
'Point the aircraft where "forward" should be, then turn Course Lock on.',
'Turn the aircraft freely: the sticks keep flying the locked course.',
'LOCK HEADING takes the current heading as the new course.',
],
),
ModeInfo(
mode: IntelligentMode.homeLock,
label: 'Home Lock',
icon: 'home',
group: 'Orientation',
blurb: 'Pulling back always flies home.',
built: true,
terms: <String>[
'Home Lock changes what the sticks do: back is always towards home, whichever way the aircraft faces. Turn it off before flying normally again.',
],
tutorial: <String>[
'Fly at least 10 m from home, then turn Home Lock on.',
'Pull the stick back to come home, push it to fly away, whichever way the aircraft points.',
],
),
ModeInfo(
mode: IntelligentMode.tripod,
label: 'Tripod',
icon: 'gauge',
group: 'Orientation',
blurb: 'Slow, precise control for framing.',
built: true,
terms: <String>[
'Tripod mode limits the speed to about 1 m/s and makes the sticks very gentle. Turn it off to fly away from danger quickly.',
],
tutorial: <String>[
'Tripod: about 1 m/s and gentle sticks, for framing and indoor-tight spaces.',
'Cinematic: smooth braking and slow turns for steady video; set how gently it brakes and turns.',
],
),
];
static const List<String> groups = <String>['Map', 'Vision', 'Shots', 'Orientation'];
static ModeInfo of(IntelligentMode m) => all.firstWhere((ModeInfo i) => i.mode == m);
/// The modes to list for [p]: every mode when no product is connected (a
/// preview, like the rest of the HUD), else the product's own.
static List<ModeInfo> forProfile(ProductProfile p) =>
<ModeInfo>[for (final ModeInfo i in all) if (!p.connected || p.supportsMode(i.mode)) i];
}