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>
250 lines
9.9 KiB
Dart
250 lines
9.9 KiB
Dart
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];
|
||
}
|