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}