# Smooth-Pan Path-Tracking Plan Executor

**Task**: AZ-655_gimbal_smooth_pan_plan
**Name**: Smooth-pan plan executor (zoom-in path-follow)
**Description**: Accept a pan plan (sequence of yaw / pitch / zoom goals with timing) from `semantic_analyzer` via `scan_controller` and execute it smoothly. Used for follow-the-footpath behaviour during the zoom-in level.
**Complexity**: 3 points
**Dependencies**: AZ-640_initial_structure, AZ-653_gimbal_a40_transport
**Component**: gimbal_controller
**Tracker**: AZ-655
**Epic**: AZ-634

## Problem

When `scan_controller` is in `ZoomedIn` and `semantic_analyzer` recommends `PanFollowFootpath`, a sequence of yaw/pitch/zoom goals with timing arrives. The executor must interpolate between goals smoothly (no step jumps) and respect the vendor's command rate — if the plan is too dense, drop the lowest-priority goals rather than blocking the queue.

## Outcome

- `PlanExecutor::load(plan: PanPlan)` accepts an ordered sequence `Vec<(yaw, pitch, zoom, at_ts)>`.
- `next_step(now)` returns the interpolated `GimbalCommand` to issue at `now`; goals past their `at_ts` are skipped; goals before `at_ts` are extrapolated linearly.
- The executor self-throttles: emits at most one command per `min_cmd_interval_ms` (default 50 ms), dropping intermediate interpolations.
- Health: `plan_loaded_at`, `commands_emitted_total`, `commands_dropped_to_throttle_total`.

## Scope

### Included
- `PanPlan` data type (`data_model.md §PanPlan`).
- Linear interpolation between adjacent goals.
- Self-throttling.

### Excluded
- Generating the plan (`semantic_analyzer`).
- Sweep pattern (task 15).
- Centre-on-target (task 17).

## Acceptance Criteria

**AC-1: Linear interpolation between goals**
Given a plan with two goals 1 s apart and yaw `0° → 30°`
When `next_step(now=500ms)` is called
Then the returned `yaw` is `15°` ± a defined epsilon.

**AC-2: Self-throttle drops intermediate calls**
Given `min_cmd_interval_ms = 100`
When `next_step()` is called every 10 ms for 1 s
Then exactly ~10 commands are emitted (the rest counted as throttled).

**AC-3: Plan past its end clamps to last goal**
Given a plan whose last `at_ts` is in the past
When `next_step(now)` is called
Then the returned command equals the last goal's `(yaw, pitch, zoom)`; no error.

## Non-Functional Requirements

**Performance**
- `next_step()` p99 ≤1 ms.

**Reliability**
- Throttle drops are counted, never silent.

## Runtime Completeness

- **Named capability**: smooth-pan plan execution + interpolation.
- **Production code that must exist**: real interpolation; real self-throttle.
- **Unacceptable substitutes**: dispatching every plan goal directly without interpolation/throttling is not acceptable (causes jerky panning).
