Files
PilotVault/Fly App/lib/fly/state/calibration_state.dart
T
tajniak81andClaude Opus 5.5 a892bfb209 Give the Fly App GO 4-style calibration wizards
P6b: a new CalibrationBridge owns every calibration call and publishes
a `calibration` telemetry group (IMU per-unit and per-side progress,
gravity centre, lens, sensor cleaning, RC calibration state and live
sticks). Guided full-screen wizards cover compass, IMU, remote
controller, gimbal auto calibration, lens, sensor cleaning, centre of
gravity, propellers and a vision-sensor guide; gimbal adjust is a
horizon-line overlay on the live video. Every start goes through
SafetyGate.runMaintenance, and closing mid-run stops what the SDK can
stop.

Reachable from Settings > Safety > Calibration & maintenance, the
Aircraft Status calibrate buttons and the gimbal sheet. Not yet run on
an aircraft; the RC key sequence still needs checking per remote.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 21:41:19 +02:00

139 lines
5.3 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 'parse.dart';
/// One IMU unit's calibration report.
@immutable
class ImuUnit {
const ImuUnit({required this.index, this.state, this.progress, this.gyroscope, this.accelerometer, this.connected});
factory ImuUnit.fromMap(Map<String, dynamic> m) => ImuUnit(
index: asInt(m['index']) ?? 0,
state: asString(m['state']),
progress: asInt(m['progress']),
gyroscope: asString(m['gyroscope']),
accelerometer: asString(m['accelerometer']),
connected: asBool(m['connected']),
);
final int index;
/// `NONE` / `CALIBRATING` / `SUCCESSFUL` / `FAILED` / `UNKNOWN`.
final String? state;
final int? progress; // 0..100
/// `SensorState` names: `NORMAL_BIAS`, `WARMING_UP`, `IN_MOTION`, `LARGE_BIAS` …
final String? gyroscope;
final String? accelerometer;
final bool? connected;
}
/// The RC wizard's live sticks (−660…660) and dials.
@immutable
class RcHardware {
const RcHardware({this.lx, this.ly, this.rx, this.ry, this.leftDial, this.rightDial});
factory RcHardware.fromMap(Map<String, dynamic> m) => RcHardware(
lx: asInt(m['lx']),
ly: asInt(m['ly']),
rx: asInt(m['rx']),
ry: asInt(m['ry']),
leftDial: asInt(m['leftDial']),
rightDial: asInt(m['rightDial']),
);
final int? lx, ly, rx, ry, leftDial, rightDial;
}
/// The calibration and maintenance wizards' state (plan P6b), from the
/// `calibration` group of the telemetry tick (`CalibrationBridge.kt`). The
/// compass reports through [AircraftState] and the gimbal through
/// [GimbalState]; everything else is here.
class CalibrationState extends ChangeNotifier {
// ── What the aircraft offers; null = not known yet ─────────────────────────
int? compassCount;
int? imuCount;
bool? gravityCenterSupported;
bool? propellerSupported;
bool? lensSupported;
bool? sensorCleaningSupported;
bool? rcSupported;
// ── IMU ────────────────────────────────────────────────────────────────────
List<ImuUnit> imuUnits = const <ImuUnit>[];
/// The aircraft wants to be turned onto several sides.
bool? imuMultiSide;
/// `CALIBRATING` / `COMPLETED` / `UNKNOWN` for the multi-side sequence.
String? imuOrientationState;
/// `CalibrationOrientation` names: `BOTTOM_DOWN`, `NOSE_DOWN` …
List<String> imuToCalibrate = const <String>[];
List<String> imuCalibrated = const <String>[];
// ── Gravity centre / lens / sensor cleaning ───────────────────────────────
/// `NONE` / `CALCULATING` / `SUCCESSFUL` / `FAILED`.
String? gravityCenterState;
String? gravityCenterError;
/// `NOT_CALIBRATE` / `CALIBRATING` / `CALIBRATED` / `WAITING_FOR_CALIBRATE`.
String? lensState;
/// `IDLE` / `COMPLETED` / `FAIL_NORMAL` / `DATA_ERROR`.
String? lensResult;
/// `SensorCleaningState` name: `IDLE`, `INITIATING`, `WAITING_FOR_LENS_REMOVAL` …
String? sensorCleaning;
// ── Remote controller ─────────────────────────────────────────────────────
/// `NORMAL` / `MIDDLE` / `LIMITS` / `QUIT` / `TIMEOUT` / `DISCONNECT`.
String? rcState;
int? rcSegments;
/// Per-axis calibration status (`A`…`H`) as the remote reports it.
Map<String, Object?> rcAxes = const <String, Object?>{};
/// Only while the RC wizard watches the hardware.
RcHardware? rcHardware;
void apply(Map<String, dynamic>? g) {
final Map<String, dynamic> support = asMap(g?['support']) ?? const <String, dynamic>{};
compassCount = asInt(support['compassCount']);
imuCount = asInt(support['imuCount']);
gravityCenterSupported = asBool(support['gravityCenter']);
propellerSupported = asBool(support['propeller']);
lensSupported = asBool(support['lens']);
sensorCleaningSupported = asBool(support['sensorCleaning']);
rcSupported = asBool(support['rc']);
final Map<String, dynamic>? imu = asMap(g?['imu']);
imuUnits = <ImuUnit>[for (final Map<String, dynamic> u in asMapList(imu?['units'])) ImuUnit.fromMap(u)];
imuMultiSide = asBool(imu?['multiSide']);
imuOrientationState = asString(imu?['orientationState']);
imuToCalibrate = _strings(imu?['toCalibrate']);
imuCalibrated = _strings(imu?['calibrated']);
final Map<String, dynamic>? gc = asMap(g?['gravityCenter']);
gravityCenterState = asString(gc?['state']);
gravityCenterError = asString(gc?['error']);
final Map<String, dynamic>? lens = asMap(g?['lens']);
lensState = asString(lens?['state']);
lensResult = asString(lens?['result']);
sensorCleaning = asString(g?['sensorCleaning']);
final Map<String, dynamic>? rc = asMap(g?['rc']);
rcState = asString(rc?['state']);
rcSegments = asInt(rc?['segments']);
rcAxes = asMap(rc?['axes']) ?? const <String, Object?>{};
final Map<String, dynamic>? hw = asMap(g?['rcHardware']);
rcHardware = hw == null ? null : RcHardware.fromMap(hw);
notifyListeners();
}
void clear() => apply(null);
static List<String> _strings(Object? v) => v is List ? <String>[for (final Object? e in v) if (e is String) e] : const <String>[];
}