# Zoom-Out Sweep Pattern

**Task**: AZ-654_gimbal_zoom_out_sweep
**Name**: Zoom-out sweep pattern primitive
**Description**: Run the zoom-out sweep pattern when `scan_controller` is in `ZoomedOut`. The exact pattern (pendulum / raster / lawn-mower) is gated by `architecture.md §8 Q1`; this task implements one selectable default with the pattern enum and exposes the choice through config.
**Complexity**: 3 points
**Dependencies**: AZ-640_initial_structure, AZ-653_gimbal_a40_transport
**Component**: gimbal_controller
**Tracker**: AZ-654
**Epic**: AZ-634

## Problem

In `ZoomedOut`, the gimbal must sweep its FOV continuously to maximise coverage. The exact pattern is an open architecture question (Q1); this task implements the `SweepEngine` abstraction and ships `Pendulum` as the safe default, with `Raster` and `LawnMower` enum variants reserved. Switching pattern is config-only — no API change to consumers.

## Outcome

- `SweepEngine::next_step(state) -> GimbalCommand` produces a sequence of yaw / pitch / zoom commands implementing the configured sweep pattern with bounded jitter and no overshoot beyond configured FOV bounds.
- Default pattern is `Pendulum`; `Raster` and `LawnMower` are wired as enum variants (one implemented; the others reserved).
- Sweep config (FOV per zoom tier, dwell time per direction, step size) is loaded from startup config.

## Scope

### Included
- `SweepPattern` enum with all three variants declared; default impl for `Pendulum`.
- `SweepEngine` struct holding the current direction + dwell counter.
- Bounded-jitter command emission.
- FOV-bound enforcement.

### Excluded
- The pattern selection rationale (Q1 — resolved separately).
- Smooth-pan plan execution (task 16).
- Centre-on-target (task 17).

## Acceptance Criteria

**AC-1: Pendulum sweep emits a bounded-jitter command stream**
Given `SweepEngine::new(SweepPattern::Pendulum, config)`
When `next_step()` is called 100 times
Then the yaw values stay within `[config.min_yaw, config.max_yaw]`, never overshoot, and reverse direction at each bound.

**AC-2: Dwell at bounds is respected**
Given a config with `dwell_ms = 500`
When the sweep reaches a yaw bound
Then `next_step()` returns the same yaw for at least 500 ms before reversing direction.

**AC-3: Pattern enum exhaustiveness**
Given the `SweepPattern` enum
When match-exhausting it in client code
Then the compiler covers `Pendulum`, `Raster`, `LawnMower` — unimplemented variants return `Err(NotImplemented)` at runtime, never silently fall back.

## Non-Functional Requirements

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

**Reliability**
- Bounded jitter; no overshoot.

## Runtime Completeness

- **Named capability**: zoom-out sweep pattern (default `Pendulum`).
- **Production code that must exist**: real bounded sweep state machine.
- **Unacceptable substitutes**: random walk is not acceptable — sweep coverage must be deterministic and bounded.
