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>
101 lines
3.4 KiB
Dart
101 lines
3.4 KiB
Dart
import 'package:flutter/material.dart';
|
||
|
||
import '../../../fly/fly_prefs.dart';
|
||
import '../../../fly/hud_features.dart';
|
||
import '../../../fly/state/aircraft_state.dart';
|
||
import '../../../fly/units.dart';
|
||
import '../hud_kit.dart';
|
||
|
||
/// The bottom telemetry strip (plan P2): H · D · VS (with arrow) · HS · VPS,
|
||
/// in metres or feet as Settings → General → Units says.
|
||
class TelemetryStrip extends StatelessWidget {
|
||
const TelemetryStrip({super.key, required this.aircraft});
|
||
|
||
final AircraftState aircraft;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return ListenableBuilder(
|
||
listenable: Listenable.merge(<Listenable>[aircraft, FlyPrefs.units]),
|
||
builder: (BuildContext context, _) {
|
||
final AircraftState a = aircraft;
|
||
final UnitSystem u = FlyPrefs.units.value;
|
||
final double? vs = a.verticalSpeed;
|
||
// Under 0.1 m/s is hover noise; no arrow for it.
|
||
final int dir = vs == null || vs.abs() < 0.1 ? 0 : vs.sign.toInt();
|
||
double? len(double? m) => m == null ? null : Units.lengthValue(m, u);
|
||
double? spd(double? ms) => ms == null ? null : Units.speedValue(ms, u);
|
||
final String l = Units.lengthUnit(u), s = Units.speedUnit(u);
|
||
return GlassPill(
|
||
height: 40,
|
||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||
child: Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
|
||
_Field('H', fmtNum(len(a.altitude)), l),
|
||
_Field('D', fmtNum(len(a.homeDistance), digits: 0), l),
|
||
_Field('VS', fmtNum(spd(vs?.abs())), s, arrow: dir),
|
||
_Field('HS', fmtNum(spd(a.horizontalSpeed)), s),
|
||
Requires(HudFeatures.vpsReadout, child: _Field('VPS', fmtNum(len(a.vpsHeight)), l)),
|
||
]),
|
||
);
|
||
},
|
||
);
|
||
}
|
||
}
|
||
|
||
class _Field extends StatelessWidget {
|
||
const _Field(this.label, this.value, this.unit, {this.arrow = 0});
|
||
|
||
final String label;
|
||
final String value;
|
||
final String unit;
|
||
|
||
/// 1 climbing, −1 descending, 0 no arrow. Painted: Space Mono has no ▲ / ▼.
|
||
final int arrow;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
return Padding(
|
||
padding: const EdgeInsets.symmetric(horizontal: 6),
|
||
child: Column(mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[
|
||
Text(label, style: HudText.label),
|
||
const SizedBox(height: 2),
|
||
Row(mainAxisSize: MainAxisSize.min, children: <Widget>[
|
||
if (arrow != 0)
|
||
Padding(
|
||
padding: const EdgeInsets.only(right: 2),
|
||
child: CustomPaint(size: const Size(7, 6), painter: _ArrowPainter(up: arrow > 0)),
|
||
),
|
||
Text.rich(TextSpan(children: <TextSpan>[
|
||
TextSpan(text: value, style: HudText.value),
|
||
TextSpan(text: ' $unit', style: HudText.unit),
|
||
])),
|
||
]),
|
||
]),
|
||
);
|
||
}
|
||
}
|
||
|
||
class _ArrowPainter extends CustomPainter {
|
||
const _ArrowPainter({required this.up});
|
||
|
||
final bool up;
|
||
|
||
@override
|
||
void paint(Canvas canvas, Size size) {
|
||
final double w = size.width, h = size.height;
|
||
final Path p = up
|
||
? (Path()
|
||
..moveTo(w / 2, 0)
|
||
..lineTo(w, h)
|
||
..lineTo(0, h))
|
||
: (Path()
|
||
..moveTo(0, 0)
|
||
..lineTo(w, 0)
|
||
..lineTo(w / 2, h));
|
||
canvas.drawPath(p..close(), Paint()..color = HudColors.ready);
|
||
}
|
||
|
||
@override
|
||
bool shouldRepaint(_ArrowPainter old) => old.up != up;
|
||
}
|