700756c16f
Initial commit. Delivers what the brief calls 'Sprint 0 - Foundations' (see docs/AR_Autopilot_brief.md section 12): - Complete repository structure (arautopilot package + firmware, display, installer, tools placeholders + docs). - Core data model (Pydantic v2): modes, alarms, actuator config, PID config + gain scheduling, vessel config, knob state machine, project config with YAML/JSON serialisation. - Seed library: 2 actuator profiles (hydraulic & electric DC reversible) and 2 default tunings (yacht motor planeo 30 m and 40 m). Conservative literature values, NOT the integrator's production tuning IP. - Firmware skeleton: only src/hal/pinout.h with the 21 I/O contract for the AR-NMEA-IO v1.0 board. No drivers, no main loop. - Studio stubs (real PySide6 app starts in Sprint 4). - pytest suite (80 tests, all green): modes, alarms, actuator, PID (incl. gain interpolation and the +/-50% adaptive bound from brief section 6), vessel, knob state, project config, library loader, end-to-end roundtrip. - examples/sprint0_demo.py - the acceptance demo from the brief. Acceptance criteria met: - pytest green (80/80) - demo creates, saves (YAML + JSON), reloads, and verifies a full ProjectConfig using the seed library - repository ready for tag `sprint-0-approved` See CHANGELOG.md for the detailed scope. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
77 lines
2.1 KiB
Python
77 lines
2.1 KiB
Python
"""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 Enum
|
|
|
|
|
|
class AutopilotMode(str, Enum):
|
|
"""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
|