# autopilot — Data Model **Status**: forward-looking design (Rust). This is the canonical entity catalogue. The autopilot binary itself has **one** persistent store: the on-device `mapobjects_store` (engine TBD — `architecture.md §8 Q3`). Everything else is in-memory only. Mission state and the central MapObjects state are pulled from the external `missions` API on start; there is no in-process mission database. The on-device `mapobjects_store` is a working copy of the central MapObjects state for the active mission's bounding box; the central observation log is the source of truth across missions (per `architecture.md §7.13`). --- ## 1. Entity Map ```mermaid erDiagram Frame ||--o{ Detection : "produced by detection_client" Frame ||--o{ MovementCandidate : "produced by movement_detector (zoom-out + zoom-in)" Detection ||--|| BoundingBox : "bbox_normalized" DetectionBatch ||--o{ Detection : "contains" POI ||--o| Tier2Evidence : "zoom-in Tier 2" POI ||--o| VlmAssessment : "Tier 3 (optional, zoom-in)" POI }o--o| MapObject : "lookup by H3 + class" POI }o--o| IgnoredItem : "decline_suppressed" MapObject ||--o{ MapObjectObservation : "history (central append-only log)" MapObjectsBundle ||--o{ MapObject : "pre-flight pull" MapObjectsBundle ||--o{ MapObjectObservation : "post-flight push" MapObjectsBundle ||--o{ IgnoredItem : "ignored items round-trip" OperatorCommand ||--o| POI : "confirm/decline target" MissionItem ||--o{ MissionWaypoint : "translates to" MissionItem ||--o{ Geofence : "carries" Geofence }o--o{ Coordinate : "polygon" MissionWaypoint ||--|| Coordinate : "at" ``` --- ## 2. Perception entities ### `Frame` A decoded video frame. Produced by `frame_ingest`; consumed by `detection_client`, `movement_detector`, `telemetry_stream`. | Field | Type | Notes | |---|---|---| | `seq` | u64 | Monotonic sequence number; primary key for cross-component correlation. | | `capture_ts_monotonic_ns` | u64 | Wall-clock-independent timestamp at the earliest practical point in the pipeline. | | `decode_ts_monotonic_ns` | u64 | When `frame_ingest` finished decoding. | | `pixels` | `Arc` | Raw pixel data; consumers do not copy. | | `width`, `height` | u32 | | | `pix_fmt` | enum | `NV12` \| `YUV420P` \| `RGB24` (decoder dependent). | | `ai_locked` | bool | If set, downstream consumers skip detection (operator-side or supervisor gating). | In-memory only. ### `BoundingBox` | Field | Type | Notes | |---|---|---| | `x_min`, `y_min`, `x_max`, `y_max` | f32 | Normalised to `[0.0, 1.0]` in image coordinates. | ### `Detection` One Tier-1 detection. Mirrors the `../detections` contract; carries through to operator overlay unchanged. | Field | Type | Notes | |---|---|---| | `class_id` | u32 | | | `class_name` | string | Human-readable label. | | `confidence` | f32 | 0.0–1.0. | | `bbox_normalized` | `BoundingBox` | | | `mask_or_polyline` | optional bytes | For polyline classes (e.g. footpaths). | | `source_frame_seq` | u64 | Foreign key into `Frame`. | ### `DetectionBatch` | Field | Type | Notes | |---|---|---| | `frame_seq` | u64 | | | `detections` | `Vec` | | | `latency_ms` | u32 | Tier-1 round-trip; observed for budget compliance. | | `model_version` | string | Reported by `../detections`; logged on change. | ### `MovementCandidate` A residual-motion cluster surviving ego-motion compensation in `movement_detector`. | Field | Type | Notes | |---|---|---| | `frame_seq` | u64 | | | `bbox_normalized` | `BoundingBox` | | | `residual_velocity_estimate` | optional struct | Direction + magnitude in image coords; used for prioritisation. | | `telemetry_quality` | enum | `synced` \| `degraded` \| `unsynced` (drives whether the candidate may be surfaced at all). | | `source_frame_ts_monotonic_ns` | u64 | | | `source_zoom_band` | enum | `zoomed_out` \| `zoomed_in`. Drives `scan_controller`'s queueing logic (per `system-flows.md §F2`): zoom-out candidates enter the POI queue normally; zoom-in candidates may bump current-ROI confidence or enter the queue with their own priority. | ### `Tier2Evidence` Output of `semantic_analyzer` for a single zoom-in ROI hold. | Field | Type | Notes | |---|---|---| | `roi_id` | uuid | Stable identifier within a zoom-in hold. | | `path_freshness` | f32 \| null | 0.0 = no path / not applicable; 1.0 = fresh. | | `endpoint_score` | f32 \| null | Concealed-position likelihood at an endpoint (branch pile / dark entrance). | | `concealment_score` | f32 \| null | General concealment-POI score. | | `recommended_next_action` | enum | `PanFollowFootpath` \| `HoldEndpoint` \| `PanBroad` \| `ReturnToZoomOut`. | | `source_detections` | `Vec` | For audit / replay. | | `status` | enum | `ok` \| `timeout` \| `oversize` \| `error`. | ### `VlmAssessment` Validated, structured response from `vlm_client`. Free-form VLM text is **not** a downstream API. | Field | Type | Notes | |---|---|---| | `label` | enum | `confirmed_concealed_position` \| `rejected` \| `inconclusive` \| `error`. | | `confidence` | f32 | 0.0–1.0; VLM-reported or derived. | | `evidence_spans` | `Vec` | Short justifications, bounded length. | | `reason` | string | One-line rationale; bounded length. | | `status` | enum | `ok` \| `timeout` \| `schema_invalid` \| `ipc_error` \| `disabled`. | | `latency_ms` | u32 | Round-trip including IPC. | | `model_version` | string | Reported by the NanoLLM process for the loaded weights; logged on change for forensic correlation. | `status` semantics: any value other than `ok` MUST result in `label = inconclusive` (or `error` for a critical failure). `scan_controller` MUST NOT promote a POI to a confirmed target on a non-`ok` `VlmAssessment`. --- ## 3. Decision entities ### `POI` A Point-of-Interest enqueued by `scan_controller`. Source: a Tier-1 detection, a movement candidate from `movement_detector`, or a Tier-2 semantic finding. | Field | Type | Notes | |---|---|---| | `id` | uuid | Stable for the POI's lifetime. | | `confidence` | f32 (0.0–1.0) | Composite of detection / motion / Tier-2 score. | | `mgrs` | string | MGRS coordinate from the GPS-Denied service or autopilot GPS. | | `class` | string | Concrete class. | | `class_group` | enum | Per `mapobjects_store` config (e.g. `military_vehicle_group`, `concealed_position_group`, `movement_candidate`). | | `source_detection_ids` | `Vec` | For audit / replay. | | `enqueued_at` | timestamp | For queue ageing. | | `priority` | f32 | `confidence × proximity_to_current_camera × age_factor`. | | `decline_suppressed` | bool | True if `(MGRS, class_group)` matches an existing `IgnoredItem`. | | `vlm_status` | enum | Mirrors `VlmAssessment.status` (or `not_requested` / `pending`). | | `tier2_evidence` | optional `Tier2Evidence` | | | `deadline` | timestamp | Per the confidence-scaled operator-decision window. | Field `queue_position` is **not** stored; it is computed at read time from `priority` + `enqueued_at`. ### `MapObject` A persisted map entry, indexed by H3 cell. Owned by `mapobjects_store`; written on each `NEW` / `MOVED` classification, read on each new detection. The on-device `MapObject` is a **working copy** of the central state for the active mission. | Field | Type | Notes | |---|---|---| | `h3_cell` | u64 | H3 cell index at the configured resolution (default `res 10`, ~15 m edge). | | `mgrs_key` | string | MGRS coordinate; together with `class` forms the hashtable composite key. | | `class` | string | Concrete class (not the group). | | `class_group` | string | Group used for matching during `EXISTING` / `MOVED` / `NEW` classification. | | `gps_lat`, `gps_lon` | f64 | For distance calculation against incoming detections. | | `size_width_m`, `size_length_m` | f32 | Bounding area on the ground. | | `confidence` | f32 | Latest observation confidence (or running average, per implementation). | | `first_seen`, `last_seen` | timestamp | Earliest and most recent observation; `last_seen` drives the `REMOVED` candidate diff at region-end. | | `mission_id` | string | For the `DELETE /missions/{id}` cascade. | | `source` | enum | `central_pulled` (came from pre-flight pull) \| `local_observed` (added during this mission). On post-flight push only `local_observed` records become new observations centrally. | | `pending_upload` | bool | True for any `local_observed` entry not yet pushed centrally. Cleared on successful `POST /missions/{id}/mapobjects` ack. | Persisted in `mapobjects_store` (engine TBD per `architecture.md §8 Q3`). ### `MapObjectObservation` A single per-detection record. The on-device store appends one of these per NEW / MOVED / EXISTING / REMOVED-CANDIDATE classification; the post-flight push uploads the unflushed list to the central `missions` API. The central side stores all observations append-only as the source of truth (per `architecture.md §7.13`). | Field | Type | Notes | |---|---|---| | `id` | uuid | Locally generated; stable across the mission. | | `h3_cell` | u64 | | | `class` | string | Concrete class. | | `class_group` | string | Group used for the diff. | | `mission_id` | string | | | `uav_id` | string | Identifies the airframe; assigned at provisioning. | | `observed_at_monotonic_ns` | u64 | Local monotonic at observation. | | `observed_at_wallclock` | timestamp | Bound from GPS or NTP per the wall-clock policy. | | `gps_lat`, `gps_lon` | f64 | | | `mgrs` | string | | | `size_width_m`, `size_length_m` | f32 | | | `confidence` | f32 | | | `diff_kind` | enum | `NEW` \| `MOVED` \| `EXISTING` \| `REMOVED_CANDIDATE`. | | `photo_ref` | string \| null | URL or compact reference; uploaded out-of-band per the central API contract (Q7). | | `raw_evidence` | json \| null | Audit payload; size-capped. | In-memory; durably persisted in `mapobjects_store` until the post-flight push acknowledges. On the central side, `map_object_observations` is the corresponding table (see `architecture.md §7.13`). ### `MapObjectsBundle` The wire shape for both the pre-flight pull (response body) and the post-flight push (request body) on `/missions/{id}/mapobjects`. | Field | Type | Notes | |---|---|---| | `schema_version` | string | Semver; mismatched versions are rejected. | | `mission_id` | string | | | `bbox` | `Coordinate[2]` (NW + SE) | The mission area; used by the central API to scope the response. | | `map_objects` | `Vec` | Pre-flight: current view from the central store. Post-flight push uses `MapObjectObservation` instead (see below). | | `observations` | `Vec` | Post-flight: the full pass diff. | | `ignored_items` | `Vec` | Pre-flight: union-merged from the central store. Post-flight: only items appended during this mission. | | `as_of` | timestamp | Pre-flight: when the central store snapshot was computed. Post-flight: when the on-device flush started. | | `freshness` | enum (pre-flight only) | `fresh` (≤ configured staleness window) \| `stale` (operator must acknowledge to use). | ### `IgnoredItem` A scene the operator declined; consulted by `scan_controller` before promoting any future detection to a POI. Union-merged across missions on the central side (per `architecture.md §7.13` conflict resolution). | Field | Type | Notes | |---|---|---| | `id` | uuid | Locally generated. | | `mgrs` | string | Decline location. | | `h3_cell` | u64 | For central-side indexing. | | `class_group` | string | Class group of the declined detection. | | `decline_time` | timestamp | Wall-clock at decline (operator-side). | | `operator_id` | string \| null | If known from the Ground Station session. | | `mission_id` | string | The mission during which the decline happened. | | `retention_scope` | enum | `mission` (cleared at mission end on-device, retained centrally indexed by mission) \| `session` (cleared at session end on-device) \| `until_expiry` (carries `expires_at`). | | `expires_at` | timestamp \| null | Required when `retention_scope = until_expiry`. | | `source` | enum | `central_pulled` (pre-flight pull) \| `local_appended` (during this mission). Only `local_appended` is uploaded to central in the post-flight push. | | `pending_upload` | bool | True for any `local_appended` entry not yet pushed centrally. | Lookup key: `(MGRS, class_group)` exact match (subject to the same H3 k-ring widening as `MapObject` lookups, when configured). Persisted in `mapobjects_store`. Central-side table: `map_object_ignored` per `architecture.md §7.13`. --- ## 4. Action / piloting entities ### `Coordinate` | Field | Type | Notes | |---|---|---| | `latitude` | f64 | Geographic; degrees. | | `longitude` | f64 | Geographic; degrees. | | `altitude_m` | f32 | Above ground or above home, depending on usage; the carrying entity defines the frame. | ### `Geofence` A polygon on the mission. Both INCLUSION and EXCLUSION are honoured by `mission_executor`. | Field | Type | Notes | |---|---|---| | `kind` | enum | `INCLUSION` \| `EXCLUSION`. | | `vertices` | `Vec` | Polygon vertices in order. | ### `MissionItem` The business-level mission item: what the `missions` API delivers and what the operator authored. **Owned by `mission-schema`**, the artefact shared with the `missions` repo (extraction location TBD — `architecture.md §8 Q5`). | Field | Type | Notes | |---|---|---| | `id` | uuid | | | `kind` | enum | `waypoint` \| `search` \| `region_search` \| `return` \| `target_follow_breakpoint`. | | `at` | optional `Coordinate` | For `waypoint` / `return`. | | `region` | optional polygon | For `region_search`. | | `cruise_speed_mps` | optional f32 | If set, `mission_executor` emits a `MAV_CMD_DO_CHANGE_SPEED` waypoint before the affected items. | | `target_classes` | optional `Vec` | Per-item search hint (e.g. `tank`, `artillery`). | ### `MissionWaypoint` The MAVLink-level wire item: what `mavlink_layer` sends to ArduPilot / PX4. **Owned by `mavlink_layer`**. | Field | Type | Notes | |---|---|---| | `seq` | u16 | MAVLink mission item sequence number. | | `frame` | enum | `MAV_FRAME_GLOBAL_RELATIVE_ALT` (system default; no terrain-following). | | `command` | enum | One of: `MAV_CMD_NAV_TAKEOFF`, `MAV_CMD_NAV_WAYPOINT`, `MAV_CMD_NAV_LAND`, `MAV_CMD_DO_CHANGE_SPEED`, `MAV_CMD_NAV_RETURN_TO_LAUNCH`, `MAV_CMD_DO_SET_MODE`. | | `current` | bool | True only for the very first item in a fresh upload. | | `auto_continue` | bool | True for everything except the final item. | | `param_1..param_4` | f32 | Command-specific. | | `lat_deg_e7`, `lon_deg_e7` | i32 | Scaled-integer geographic coordinates. | | `alt_m` | f32 | Above home (relative). | ### Translation contract — `MissionItem` → `MissionWaypoint` Owner: `mission_executor`, variant-aware (multirotor / fixed-wing). | Source `MissionItem.kind` | Resulting `MissionWaypoint`(s) | |---|---| | `waypoint` | exactly one `MAV_CMD_NAV_WAYPOINT` | | `region_search` | sequence of `MAV_CMD_NAV_WAYPOINT`s computed per the sweep pattern (`architecture.md §8 Q1`) | | `return` | one `MAV_CMD_NAV_RETURN_TO_LAUNCH` (or `MAV_CMD_NAV_LAND` at the explicit return point) | | `target_follow_breakpoint` | (none) — used only as a structural marker for re-upload; not sent to MAVLink | | (cruise speed carried by a `MissionItem`) | one `MAV_CMD_DO_CHANGE_SPEED` placed **before** the affected `MAV_CMD_NAV_WAYPOINT`s | Multirotor variants prepend `MAV_CMD_NAV_TAKEOFF` and append `MAV_CMD_NAV_LAND`. Fixed-wing variants do neither (the airframe is RC-launched and put into AUTO by the operator); they only upload + start the mission. The cruise-speed translation is required to **reach the autopilot**. If a `MissionItem` declares a cruise speed, the corresponding `MAV_CMD_DO_CHANGE_SPEED` MUST be present in the uploaded sequence with the speed in `param_1`. Conformance test in `deployment/ci_cd_pipeline.md §5`. ### `OperatorCommand` Every command from the Ground Station to autopilot is wrapped in this authenticated envelope. The principle is committed (`architecture.md §5`); the exact signature scheme is open per Q9. `operator_bridge` rejects any command that fails signature validation, replay-protection check, or session validation. | Field | Type | Notes | |---|---|---| | `command_id` | uuid | Idempotency key; cached for 60 s by `operator_bridge`. | | `session_token` | string | Opaque session token issued by the Ground Station at operator login; bound to `operator_id`. | | `sequence_number` | u64 | Monotonically increasing per-session; replay-protection. Lower-or-equal numbers per session are rejected. | | `issued_at_wallclock` | timestamp | Operator-side wall-clock. Used for forensic audit; not used for trust decisions. | | `kind` | enum | `confirm_poi` \| `decline_poi` \| `start_target_follow` \| `release_target_follow` \| `acknowledge_bit_degraded` \| `safety_override` \| `mission_abort`. | | `payload` | json | Action-specific body. | | `signature` | bytes | Signature over (`session_token`, `sequence_number`, `kind`, `payload`). Scheme TBD per Q9. | `scan_controller` and `mission_executor` see only the validated payload; the auth envelope is opaque to them. Audit logs record `command_id`, `operator_id` (resolved from session token), `kind`, and result. ### `GimbalState` | Field | Type | Notes | |---|---|---| | `yaw`, `pitch` | f32 | Degrees. | | `zoom` | f32 | Effective focal length or zoom factor (vendor-specific). | | `ts_monotonic_ns` | u64 | Stamp at the moment the gimbal feedback was received. | | `command_in_flight` | bool | True between command issuance and feedback that motion completed. | In-memory only; consumed by `frame_ingest` and `movement_detector` for telemetry-skew compensation. --- ## 5. Sync / wire formats ### MGRS sync message — wire format The operator round trip (`telemetry_stream` ⇄ Ground Station) uses MGRS-encoded payloads in both directions. Field separator is `::`. **Drone → Operator (detection report):** | Position | Field | Type | Notes | |---|---|---|---| | 1 | `missionId` | string | Server-assigned mission UUID. | | 2 | `MGRS(encoded)` | string | MGRS coordinate (compact, military-grid). | | 3 | `class` | string | Concrete detection class. | | 4 | `confidence` | f32 | 0.0–1.0. | | 5 | `size_width_m` | f32 | Ground-projected width. | | 6 | `size_length_m` | f32 | Ground-projected length. | | 7 | `photo_metadata` | string | URL or compact reference to the snapshot frame. | | 8 | `flags` | bitmask | Reserved (e.g. `target_follow_active`, `vlm_used`, `movement_origin`). | **Operator → Drone (command / acknowledgment):** | Position | Field | Type | Notes | |---|---|---|---| | 1 | `missionId` | string | Must match the drone-side mission. | | 2 | `Encoded(GroundMGRS :: Time)` | string | Operator's ground location + decision timestamp. | | 3 | (variable) | … | Action-specific payload (POI ID, action enum, follow-toggle, etc.). | | N | `missionId2` | string | Echo of `missionId` for stream-multiplexing safety. | The exact serialisation of position 3 (action payload) is left to the Ground Station API contract (open question; see `architecture.md §8 Q2`). --- ## 6. Persistence and lifecycle | Entity | Persisted? | Where | Lifecycle | |---|---|---|---| | `Frame`, `Detection`, `DetectionBatch`, `MovementCandidate`, `Tier2Evidence`, `VlmAssessment`, `GimbalState` | no | in-memory | per frame / per ROI / per command — dropped on state change. | | `POI` | no | in-memory inside `scan_controller` | enqueued, surfaced, decided (confirm / decline / timeout), then dropped. | | `MapObject` | yes | `mapobjects_store` (working copy of central state) | mission-scoped on-device; appended to central observation log via post-flight push (F8); cleared on `DELETE /missions/{id}` cascade. | | `MapObjectObservation` | yes | `mapobjects_store` until acknowledged centrally | per-detection append-log; durable across in-flight crash; cleared per record on `POST /missions/{id}/mapobjects` ack. | | `IgnoredItem` | yes | `mapobjects_store` (working copy + post-flight upload of locally-appended items) | per `retention_scope`; central side union-merged. | | `MissionItem` | no in autopilot | source of truth is the `missions` API | pulled on start; refreshed on middle-waypoint POST. | | `MissionWaypoint` | no | in-memory inside `mavlink_layer` | re-derived from `MissionItem`s on each upload / re-upload. | | `OperatorCommand` | partial | command-id cache (60 s) for idempotency; full audit log persisted on disk | per-command; audit-retained per configured policy. | --- ## 7. Versioning and contracts | Contract | Owner | Versioning | |---|---|---| | `mission-schema` (the `MissionItem` shape) | shared between `autopilot` and `missions` repos; extraction location TBD (`architecture.md §8 Q5`) | semantic versioning; `mission_client` validates `schema_version` on fetch. | | MapObjects bundle schema (`MapObjectsBundle` for pull/push, `MapObjectObservation` for the central observation log) | shared between `autopilot` and `missions` repos as part of the §7.13 endpoint extension | semantic versioning; `mission_client` validates `schema_version`; central side rejects mismatches with 4xx (`architecture.md §8 Q7`). | | `../detections` gRPC contract | `../detections` repo (per `../_docs/03_detections.md`) | versioned; `detection_client` rejects schema mismatches (`architecture.md §8 Q4`). | | `VlmAssessment` schema | autopilot-internal (this document is the source of truth) | versioned; `vlm_client` rejects schema-invalid responses. The `model_version` field correlates assessments with VLM weights. | | MGRS sync wire format | autopilot-internal (this document is the source of truth) | versioned; field-position changes are breaking. | | MAVLink command surface | per `architecture.md §7.7` | adding messages requires explicit design review. | | `OperatorCommand` envelope (signature scheme) | open per `architecture.md §8 Q9` | once chosen, versioned; both Ground Station and `operator_bridge` must agree. |