Files
alro65 700756c16f sprint-0: foundations -- data model, seed library, tests, demo
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>
2026-05-17 23:57:18 -04:00

63 lines
2.2 KiB
Python

"""Tests for ``arautopilot.library.loader`` and the seed assets shipped in Sprint 0."""
from __future__ import annotations
from arautopilot.core.actuator_config import ActuatorType
from arautopilot.library.loader import (
list_actuator_profiles,
list_default_tunings,
load_actuator_profile,
load_default_tuning,
)
def test_seed_actuators_present() -> None:
profiles = list_actuator_profiles()
assert "hydraulic_reversible" in profiles
assert "electric_dc_reversible" in profiles
assert len(profiles) >= 2
def test_seed_tunings_present() -> None:
tunings = list_default_tunings()
assert "yacht_motor_planeo_30m" in tunings
assert "yacht_motor_planeo_40m" in tunings
assert len(tunings) >= 2
def test_load_hydraulic_actuator_profile() -> None:
cfg = load_actuator_profile("hydraulic_reversible")
assert cfg.type is ActuatorType.HYDRAULIC_REVERSIBLE
assert cfg.feedback_required is True
assert 0 < cfg.deadband_pct < 30
def test_load_electric_dc_actuator_profile() -> None:
cfg = load_actuator_profile("electric_dc_reversible")
assert cfg.type is ActuatorType.ELECTRIC_DC_REVERSIBLE
assert cfg.feedback_required is True
def test_load_30m_tuning_has_three_point_schedule() -> None:
pid = load_default_tuning("yacht_motor_planeo_30m")
assert len(pid.gain_schedule) == 3
# Brief: outer kp should drop as speed goes up.
speeds = [p.speed_knots for p in pid.gain_schedule]
kps = [p.gains.kp for p in pid.gain_schedule]
assert speeds == sorted(speeds)
assert kps[0] > kps[-1]
# Inner loop faster than outer.
assert pid.inner_loop_freq_hz > pid.outer_loop_freq_hz
# Adaptive disabled at ship time.
assert pid.adaptive_enabled is False
def test_load_40m_tuning_is_more_damped_than_30m() -> None:
"""Larger vessel should have higher kd at cruise speed (more anticipation)."""
pid30 = load_default_tuning("yacht_motor_planeo_30m")
pid40 = load_default_tuning("yacht_motor_planeo_40m")
# Cruise-speed point (~15 kn) is the second entry in both schedules.
assert pid40.gain_schedule[1].gains.kd > pid30.gain_schedule[1].gains.kd
# And kp at cruise should not be higher than 30 m.
assert pid40.gain_schedule[1].gains.kp <= pid30.gain_schedule[1].gains.kp