Skip to main content

r_d_inclita_sofware/telemetry/
data.rs

1use defmt::Format;
2use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
3use embassy_sync::channel::Channel;
4use embassy_sync::mutex::Mutex;
5use serde::Serialize;
6
7///Magnetometer data structure
8#[derive(Format, Serialize, Clone)]
9pub struct MagnetometerData {
10    pub mag_x: f32,
11    pub mag_y: f32,
12    pub mag_z: f32,
13    pub timestamp_ms: u32,
14}
15
16/// IMUN DAta struct to use
17#[derive(Format, Serialize, Clone)]
18pub struct ImuData {
19    pub yaw: f32,
20    pub pitch: f32,
21    pub roll: f32,
22    pub mag_x: f32,
23    pub mag_y: f32,
24    pub mag_z: f32,
25    pub gyro_x: f32,
26    pub gyro_y: f32,
27    pub gyro_z: f32,
28    pub lin_accel_n: f32,
29    pub lin_accel_e: f32,
30    pub lin_accel_d: f32,
31    pub timestamp_ms: u32,
32}
33
34#[derive(Format, Serialize, Clone)]
35pub struct AccelData {
36    pub raw_x: f32,
37    pub raw_y: f32,
38    pub raw_z: f32,
39    pub timestamp_ms: u32,
40}
41
42#[derive(Format, Serialize, Clone)]
43pub struct AltimeterData {
44    pub pressure: f32,
45    pub altitude: f32,
46    pub temperature: f32,
47    pub timestamp_ms: u32,
48}
49
50#[derive(Format, Serialize, Clone, Copy, PartialEq)]
51pub enum GpsFix {
52    NoFix = 0,
53    Standard = 1,   // Standard GPS Fix
54    Dgps = 2,       // Differential GPS (More accurate)
55    Pps = 3,        // Precise Positioning Service
56    RtkInteger = 4, // Real-Time Kinematic (Highest accuracy)
57    RtkFloat = 5,
58    Estimated = 6,
59    Manual = 7,
60    Simulation = 8,
61    Unknown, // Safety fallback for corrupted data
62}
63
64impl GpsFix {
65    // A clean way to convert the raw u8 from the GPS into our type-safe enum
66    pub fn from_u8(val: u8) -> Self {
67        match val {
68            0 => GpsFix::NoFix,
69            1 => GpsFix::Standard,
70            2 => GpsFix::Dgps,
71            3 => GpsFix::Pps,
72            4 => GpsFix::RtkInteger,
73            5 => GpsFix::RtkFloat,
74            6 => GpsFix::Estimated,
75            7 => GpsFix::Manual,
76            8 => GpsFix::Simulation,
77            _ => GpsFix::Unknown,
78        }
79    }
80}
81#[derive(Format, Serialize, Clone)]
82pub struct GnggaMessage {
83    /// UTC Time (HHMMSS.SS)
84    pub utc_time: UtcTime,
85    /// Latitude in decimal degrees (Positive = North, Negative = South)
86    pub latitude: f64,
87    /// Longitude in decimal degrees (Positive = East, Negative = West)
88    pub longitude: f64,
89    /// Quality of the fix
90    pub fix: GpsFix,
91
92    /// Altitude above mean sea level (Meters)
93    pub altitude: f32,
94
95    pub timestamp_ms: u32,
96}
97
98#[derive(Format, Serialize, Clone)]
99pub struct UtcTime {
100    pub hours: u8,
101    pub minutes: u8,
102    pub seconds: f32, // f32 because NMEA includes milliseconds (e.g., 14.50s)
103}
104// The wrapping envelope
105#[derive(Format, Serialize)]
106pub enum LogEvent {
107    Imu(ImuData),
108    Baro(AltimeterData),
109    GPS(GnggaMessage),
110    Mag(MagnetometerData),
111    ACCEL(AccelData),
112}
113//The Channel (Our FreeRTOS StreamBuffer equivalent)
114// the channel can hold up to 25 readings before the mock feeder has to wait.
115pub static DATA_CHANNEL: Channel<ThreadModeRawMutex, LogEvent, 100> = Channel::new();
116
117// LoRa smaller structs
118
119#[derive(Format, Serialize, Clone)]
120pub struct ImuTx {
121    pub yaw: f32,
122    pub pitch: f32,
123    pub roll: f32,
124    pub mag_x: f32,
125    pub mag_y: f32,
126    pub mag_z: f32,
127    pub lin_accel_n: f32,
128    pub lin_accel_e: f32,
129    pub lin_accel_d: f32,
130}
131
132// Quick converter from SD struct to the LoRa struct
133impl From<ImuData> for ImuTx {
134    fn from(d: ImuData) -> Self {
135        Self {
136            yaw: d.yaw,
137            pitch: d.pitch,
138            roll: d.roll,
139            mag_x: d.mag_x,
140            mag_y: d.mag_y,
141            mag_z: d.mag_z,
142            lin_accel_n: d.lin_accel_n,
143            lin_accel_e: d.lin_accel_e,
144            lin_accel_d: d.lin_accel_d,
145        }
146    }
147}
148
149#[derive(Format, Serialize, Clone)]
150pub struct AltimeterTx {
151    pub pressure: f32,
152    pub altitude: f32,
153    pub temperature: f32,
154}
155
156impl From<AltimeterData> for AltimeterTx {
157    fn from(d: AltimeterData) -> Self {
158        Self {
159            pressure: d.pressure,
160            altitude: d.altitude,
161            temperature: d.temperature,
162        }
163    }
164}
165
166#[derive(Format, Serialize, Clone)]
167pub struct GpsTx {
168    pub latitude: f64,
169    pub longitude: f64,
170    pub fix: GpsFix,
171    pub altitude: f32,
172}
173
174impl From<GnggaMessage> for GpsTx {
175    fn from(d: GnggaMessage) -> Self {
176        Self {
177            latitude: d.latitude,
178            longitude: d.longitude,
179            fix: d.fix,
180            altitude: d.altitude,
181        }
182    }
183}
184
185// This struct groups the latest data.
186// Option allows us to transmit even if some sensors haven't fired yet.
187#[derive(Format, Serialize, Clone)]
188pub struct DownlinkPacket {
189    pub imu: Option<ImuData>,
190    pub baro: Option<AltimeterData>,
191    pub gps: Option<GnggaMessage>,
192}
193
194// A global, thread-safe variable to hold the latest state
195// INFO: We use RefCell cause, in a no std environment, Mutex does not implement interior mutablity
196// embassy_sync has signal. it seems more appropriate here! all my homies hate RefCell in no_std! use embassy_sync instead! theres always a sync primitive there you can use!
197// plus it has async support, so you can await on it!
198pub static LATEST_TELEMETRY: Mutex<ThreadModeRawMutex, DownlinkPacket> =
199    Mutex::new(DownlinkPacket {
200        imu: None,
201        baro: None,
202        gps: None,
203    });