"""Autopilot operating modes. The brief (section 3) defines five Phase 1 modes plus three Phase 2 sail modes that appear greyed-out in the UI but are reserved here for forward compatibility. """ from __future__ import annotations from enum import StrEnum class AutopilotMode(StrEnum): """The complete set of autopilot operating modes, across both phases. Use :func:`is_available_in_phase` to query phase gating instead of hard-coding lists in the UI. """ # --- Phase 1 (Sprint 1+) --- STANDBY = "standby" """Pilot disengaged, helm is manual.""" HEADING_HOLD = "heading_hold" """Holds a fixed magnetic/true compass heading.""" TRUE_COURSE = "true_course" """Holds COG, compensating drift due to current and wind.""" TRACK_KEEPING = "track_keeping" """Follows an ECDIS waypoint route with smooth XTE correction.""" DODGE = "dodge" """Temporary deviation without losing the route; auto-returns when released.""" # --- Phase 2 (Sprint 10+, greyed in Phase 1 UI) --- APPARENT_WIND = "apparent_wind" """Holds constant apparent wind angle (vane mode). Sailboats only.""" TRUE_WIND = "true_wind" """Holds constant true wind angle. Sailboats only.""" AUTO_TACK = "auto_tack" """Automatically tacks at a target relative wind angle. Sailboats only.""" _PHASE_1: frozenset[AutopilotMode] = frozenset( { AutopilotMode.STANDBY, AutopilotMode.HEADING_HOLD, AutopilotMode.TRUE_COURSE, AutopilotMode.TRACK_KEEPING, AutopilotMode.DODGE, } ) _PHASE_2: frozenset[AutopilotMode] = frozenset( { AutopilotMode.APPARENT_WIND, AutopilotMode.TRUE_WIND, AutopilotMode.AUTO_TACK, } ) def is_available_in_phase(mode: AutopilotMode, phase: int) -> bool: """Return ``True`` if ``mode`` is available in the given product phase. Phase 1 is the launch product. Phase 2 adds the sailboat wind modes. Asking for any other phase number returns ``False``. """ if phase == 1: return mode in _PHASE_1 if phase == 2: return mode in _PHASE_1 or mode in _PHASE_2 return False