# ViewPro A40 Vendor Transport

**Task**: AZ-653_gimbal_a40_transport
**Name**: ViewPro A40 vendor protocol UDP transport
**Description**: UDP transport, frame encode/decode, CRC16 (vendor spec), bounded retry on command timeout. Surface vendor faults to health.
**Complexity**: 5 points
**Dependencies**: AZ-640_initial_structure
**Component**: gimbal_controller
**Tracker**: AZ-653
**Epic**: AZ-634

## Problem

The gimbal is a ViewPro A40 vendor product reachable over UDP using a vendor-specified frame format with CRC16. The transport layer must encode and decode every command/response frame this codebase issues (yaw, pitch, zoom, feedback request, mode commands), validate CRC on inbound frames, and re-issue on timeout with bounded retry. The vendor protocol is fixed by the camera — the device's binary protocol is a `restrictions.md` constraint, not a design choice.

## Outcome

- `A40Transport::send(cmd) -> Result<A40Response, A40Error>` writes a CRC-correct vendor frame to the configured UDP endpoint and awaits the matching response within a deadline.
- Inbound frames are CRC-validated; mismatches are dropped and counted as `vendor_faults_total{kind="crc"}`.
- Bounded retry on timeout (default 3 attempts; configurable).
- Health surface: `commands_per_min`, `vendor_faults_total`, `last_command_in_flight`.

## Scope

### Included
- UDP socket (single endpoint).
- CRC16 (vendor polynomial) encode/decode helpers.
- Frame encoders for yaw / pitch / zoom commands + feedback request.
- Frame decoders for yaw / pitch / zoom feedback + vendor fault frames.
- Bounded retry on timeout.

### Excluded
- Sweep pattern primitive (task 15).
- Smooth-pan plan execution (task 16).
- Centre-on-target primitive (task 17).
- Vendor protocol *specification* — assumed to be reverse-engineered or vendor-supplied separately; this task implements against the documented frame layout in `misc/camera/a8/` (which is the predecessor model A8; A40 differs in command codes per architecture.md).

## Acceptance Criteria

**AC-1: CRC round-trip**
Given the encoder produces a yaw command frame for `yaw = 30°`
When the same frame is fed back through the decoder
Then the decoded command matches and `vendor_faults_total{kind="crc"} = 0`.

**AC-2: CRC mismatch counted**
Given an inbound frame with corrupted CRC
When the decoder consumes it
Then the frame is dropped and `vendor_faults_total{kind="crc"}` increments by 1.

**AC-3: Command timeout retries**
Given a fake A40 endpoint that drops the first command silently
When `send(yaw_cmd)` is called with default 3 attempts
Then the call succeeds on retry; `vendor_faults_total{kind="timeout"}` reports 1.

**AC-4: Cap exhaustion returns explicit error**
Given the endpoint never responds
When `send(yaw_cmd)` is called
Then after 3 attempts the call returns `Err(MaxRetriesExceeded)` and the error surfaces to the caller.

## Non-Functional Requirements

**Performance**
- Single command round-trip: ≤200 ms on a healthy link (well under the ≤500 ms decision-to-movement budget).

**Reliability**
- CRC mismatches counted, never silent.
- Bounded retry; no infinite retry.

## Constraints

- Vendor protocol is fixed; no negotiation.
- One A40 per autopilot instance.

## Runtime Completeness

- **Named capability**: ViewPro A40 vendor protocol on UDP.
- **Production code that must exist**: real CRC16; real UDP socket; real per-command encoder/decoder.
- **Allowed external stubs**: in tests, a UDP echo with vendor-frame replay can simulate the camera.
- **Unacceptable substitutes**: a generic "send raw bytes and assume success" path is unacceptable — the protocol's frame format and CRC are non-negotiable.
