Skip to main content

rust_gnc/telemetry/
telemetry.rs

1//! # Telemetry
2//! 
3//! Handles the serialization and transmission of flight data snapshots.
4//! Optimized for high-frequency binary logging in `no_std` environments.
5
6use crate::{Attitude, Position};
7use crate::control::mixer::QuadMotorSignals;
8
9/// A packed, fixed-size snapshot of the vehicle state.
10/// 
11/// ### Binary Layout
12/// This struct uses `#[repr(C, packed)]` to ensure a stable, predictable memory layout
13/// across different platforms. It is exactly 37 bytes (36 for data + 1 for flags).
14/// 
15/// | Field | Type | Offset |
16/// | :--- | :--- | :--- |
17/// | `timestamp_ms` | `u32` | 0 |
18/// | `roll`..`pos_z` | `f32` x 4 | 4-19 |
19/// | `motor_fl`..`rr` | `f32` x 4 | 20-35 |
20/// | `status_flags` | `u8` | 36 |
21#[repr(C, packed)]
22#[derive(Debug, Clone, Copy)]
23pub struct TelemetryPacket {
24    /// System uptime in milliseconds.
25    pub timestamp_ms: u32,
26    /// Current Euler angles (Radians).
27    pub roll: f32,
28    pub pitch: f32,
29    pub yaw: f32,
30    /// Vertical position (Altitude) in meters (NED frame).
31    pub pos_z: f32,
32    /// Normalized motor output [0.0 - 1.0].
33    pub motor_fl: f32,
34    pub motor_fr: f32,
35    pub motor_rl: f32,
36    pub motor_rr: f32,
37    /// Bitmask for system status:
38    /// - Bit 0: Armed state (1 = Armed, 0 = Disarmed)
39    /// - Bit 1: Failsafe active
40    /// - Bit 2: Battery critical
41    pub status_flags: u8, 
42}
43
44impl TelemetryPacket {
45    /// Constructs a new telemetry snapshot from high-level GNC types.
46    pub fn new(
47        timestamp_ms: u32,
48        attitude: Attitude,
49        pos: Position,
50        motors: QuadMotorSignals,
51        armed: bool
52    ) -> Self {
53        Self {
54            timestamp_ms,
55            roll: attitude.roll.0,
56            pitch: attitude.pitch.0,
57            yaw: attitude.yaw.0,
58            pos_z: pos.z,
59            motor_fl: motors.front_left,
60            motor_fr: motors.front_right,
61            motor_rl: motors.rear_left,
62            motor_rr: motors.rear_right,
63            status_flags: if armed { 1 } else { 0 },
64        }
65    }
66
67    /// Views the packet as a byte slice for transmission.
68    /// 
69    /// # Safety
70    /// This uses `unsafe` to cast the struct directly to a byte slice. 
71    /// This is safe as long as:
72    /// 1. The struct is `#[repr(C, packed)]`.
73    /// 2. The slice does not outlive the struct instance.
74    pub fn as_bytes(&self) -> &[u8] {
75        unsafe {
76            core::slice::from_raw_parts(
77                (self as *const Self) as *const u8,
78                core::mem::size_of::<Self>(),
79            )
80        }
81    }
82}