//! `UavTelemetry` — projection of decoded MAVLink telemetry into a //! typed snapshot that downstream consumers (`scan_controller`, //! `movement_detector`, `telemetry_stream`, BIT) consume. //! //! Authoritative projection rules: //! //! - `position` from `GLOBAL_POSITION_INT` (id 33). Latitude/longitude //! are kept in their MAVLink-native E7 form so consumers that //! compare against waypoints (also E7) don't re-introduce float //! round-trip drift. Altitude is in metres (MSL + AGL relative). //! Velocities are in m/s, heading in degrees [0, 360). //! - `attitude` from `ATTITUDE` (id 30). Angles in radians per the //! MAVLink convention. //! - `mode` from `HEARTBEAT` (id 0). The `(base_mode, custom_mode)` //! pair is the canonical (vehicle-type-specific) discriminator; //! `system_status` is the MAV_STATE enum (`MAV_STATE_ACTIVE` etc.). //! - `sys_status` from `SYS_STATUS` (id 1). Battery + comms + sensor //! health bitfield — the bits consumers actually read are //! documented in `architecture.md §5.6`. //! - `monotonic_ts_ns` is the host monotonic timestamp captured the //! moment the originating MAVLink message was decoded. Strictly //! non-decreasing across snapshots. Boot-time-relative fields //! (`ts_boot_ms`) are kept on each sub-struct so consumers that //! already correlate against MAVLink time-bases (e.g. EKF logs) //! don't lose them. use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] pub struct UavPosition { pub lat_e7: i32, pub lon_e7: i32, pub alt_m: f32, pub relative_alt_m: f32, pub vx_mps: f32, pub vy_mps: f32, pub vz_mps: f32, pub heading_deg: f32, pub ts_boot_ms: u32, } #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] pub struct UavAttitude { pub roll: f32, pub pitch: f32, pub yaw: f32, pub rollspeed: f32, pub pitchspeed: f32, pub yawspeed: f32, pub ts_boot_ms: u32, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub struct UavMode { pub base_mode: u8, pub custom_mode: u32, pub system_status: u8, } #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] pub struct UavSysStatus { pub voltage_battery_mv: u16, pub current_battery_ca: i16, pub battery_remaining: i8, pub onboard_sensors_health: u32, pub errors_comm: u16, } #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] pub struct UavTelemetry { pub position: Option, pub attitude: Option, pub mode: Option, pub sys_status: Option, pub monotonic_ts_ns: u64, } impl UavTelemetry { /// Empty snapshot used as the initial value before any telemetry /// has arrived. pub fn empty() -> Self { Self { position: None, attitude: None, mode: None, sys_status: None, monotonic_ts_ns: 0, } } } impl Default for UavTelemetry { fn default() -> Self { Self::empty() } }