Files
PilotVault/Fly App/lib/fly/state/link_extras.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

85 lines
2.5 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 'package:flutter/foundation.dart';
import '../rc_buttons.dart';
import 'parse.dart';
/// One press of a remote's custom button (`rcButton` event). [seq] grows with
/// every press, so two presses of the same button are two values.
@immutable
class RcButtonPress {
const RcButtonPress(this.button, this.seq);
final RcButton button;
final int seq;
@override
bool operator ==(Object other) => other is RcButtonPress && other.button == button && other.seq == seq;
@override
int get hashCode => Object.hash(button, seq);
}
/// A remote seen by master search, or linked as a slave (`rcMasters`, `getSlaves`).
@immutable
class RcPeer {
const RcPeer({required this.id, this.name, this.signal});
factory RcPeer.fromMap(Map<String, dynamic> m) => RcPeer(id: asInt(m['id']) ?? 0, name: asString(m['name']), signal: asInt(m['signal']));
final int id;
final String? name;
/// 0–100.
final int? signal;
@override
bool operator ==(Object other) => other is RcPeer && other.id == id && other.name == name && other.signal == signal;
@override
int get hashCode => Object.hash(id, name, signal);
}
/// One bar of the video link's spectrum chart.
@immutable
class SpectrumBar {
const SpectrumBar(this.label, this.power);
/// Channel number, or the band's centre in MHz.
final String label;
/// dBm (OcuSync RSSI, Lightbridge / Wi-Fi channel power); more negative is quieter.
final double power;
}
/// The video link's interference (`spectrum` events, while the chart is up).
@immutable
class Spectrum {
const Spectrum(this.link, this.bars);
/// `ocusync` / `lightbridge` / `wifi`.
final String link;
final List<SpectrumBar> bars;
factory Spectrum.fromEvent(Map<String, dynamic> e) {
final List<SpectrumBar> bars = <SpectrumBar>[
for (final Map<String, dynamic> b in asMapList(e['bands']))
if (finite(b['from']) case final double from)
if (finite(b['to']) case final double to)
if (finite(b['rssi']) case final double rssi) SpectrumBar(((from + to) / 2).toStringAsFixed(0), rssi),
for (final Map<String, dynamic> c in asMapList(e['channels']))
if (asInt(c['channel']) case final int ch)
if (finite(c['power']) case final double p) SpectrumBar('$ch', p),
];
return Spectrum(asString(e['link']) ?? '', bars);
}
/// The quietest bar, the channel to pick in Custom mode.
SpectrumBar? get quietest {
SpectrumBar? best;
for (final SpectrumBar b in bars) {
if (best == null || b.power < best.power) best = b;
}
return best;
}
}