From 345c694ea744270d8496cfa1bf0a9036a19648e5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicol=C3=B2=20Caruso?= <nicolo.caruso@skywarder.eu>
Date: Tue, 22 Apr 2025 09:04:15 +0200
Subject: [PATCH] [fr-api] Added response deserialization

Added response deserialization from the flight radar API response.
Also added the creation of the ROCKET_STATS_TM and ROCKET_FLIGHT_TM packets to be later send on to track the aircraft.
---
 on-host/src/fr_api.rs | 206 ++++++++++++++++++++++++++++--------------
 1 file changed, 137 insertions(+), 69 deletions(-)

diff --git a/on-host/src/fr_api.rs b/on-host/src/fr_api.rs
index f771edd..78a4e99 100644
--- a/on-host/src/fr_api.rs
+++ b/on-host/src/fr_api.rs
@@ -1,69 +1,137 @@
-use std::time::Duration;
-
-use reqwest::{header, redirect::Policy};
-
-use log::{error, info};
-
-pub async fn api_request(flight_nr: String) -> () {
-    // TODO: Set your token
-    let token: String = String::from("YOUR API TOKEN");
-
-    // TODO: remove after changing your API
-    error!("Just to remind you to change the API token ;)\n");
-
-    let mut token_str: String = String::from("Bearer ");
-    token_str.push_str(&token);
-
-    loop {
-        // Headers for the CURL request, needed from FR-API and can be set
-        let mut headers = header::HeaderMap::new();
-        headers.insert("Accept", "application/json".parse().unwrap());
-        headers.insert("Accept-Version", "v1".parse().unwrap());
-        headers.insert("Authorization", token_str.parse().unwrap());
-
-        // Creating a new client
-        let client = reqwest::Client::builder()
-            .redirect(Policy::none())
-            .build()
-            .unwrap();
-
-        // Url for the light-flight telemetry from FR24
-        let mut url: String = String::from(
-            "https://fr24api.flightradar24.com/api/live/flight-positions/light?flights=",
-        );
-        url.push_str(&flight_nr);
-        info!("URL: {}", url);
-
-        print!("{}", url);
-
-        // Waiting for a response
-        let response = client.get(url).headers(headers).send().await;
-
-        info!("Got response");
-
-        // The response
-        // TODO: For now just printed, then used as telemetry
-        match response {
-            Ok(res) => {
-                info!("Ok");
-
-                let message = res.text().await;
-                match message {
-                    Ok(string) => {
-                        print!("{}", string);
-                        info!("Got response {}", string);
-                    }
-                    Err(err) => {
-                        error!("An error occurred in the message {}", err)
-                    }
-                }
-            }
-            Err(err) => {
-                error!("An error occurred in the message {}", err)
-            }
-        }
-
-        // Sleeping since request costs!
-        std::thread::sleep(Duration::from_millis(10000));
-    }
-}
+use std::time::Duration;
+
+use reqwest::{header, redirect::Policy};
+
+use log::{error, info};
+
+use serde::Deserialize;
+use skyward_mavlink::lyra::{MavMessage, ROCKET_FLIGHT_TM_DATA, ROCKET_STATS_TM_DATA};
+use crate::packet::TimedMessage;
+// The message structure from the FR-API
+#[derive(Debug, Deserialize)]
+struct Message{
+    message: String,
+    details: String,
+    data: String
+}
+
+// The sub-message with the information about the aircraft position
+#[derive(Debug, Deserialize)]
+struct LightTelemetryMessage{
+    fr24_id: String,
+    hex: String,
+    callsign: String,
+    lat: f32,
+    lon: f32,
+    track: i8,  // Heading 0-360 [deg]
+    alt:f32,
+    gspeed: i32,    // Ground speed in knots
+    vspeed: i32,    // Vertical speed in feet
+    squawk: String,
+    timestamp: String,
+    source: String
+}
+
+pub async fn api_request(flight_nr: String) -> () {
+    // TODO: Set your token
+    let token: String = String::from("YOUR API TOKEN");
+
+    // TODO: remove after changing your API
+    error!("Just to remind you to change the API token ;)\n");
+
+    let mut token_str: String = String::from("Bearer ");
+    token_str.push_str(&token);
+
+    loop {
+        // Headers for the CURL request, needed from FR-API and can be set
+        let mut headers = header::HeaderMap::new();
+        headers.insert("Accept", "application/json".parse().unwrap());
+        headers.insert("Accept-Version", "v1".parse().unwrap());
+        headers.insert("Authorization", token_str.parse().unwrap());
+
+        // Creating a new client
+        let client = reqwest::Client::builder()
+            .redirect(Policy::none())
+            .build()
+            .unwrap();
+
+        // Url for the light-flight telemetry from FR24
+        let mut url: String = String::from(
+            "https://fr24api.flightradar24.com/api/live/flight-positions/light?flights=",
+        );
+        url.push_str(&flight_nr);
+        info!("URL: {}", url);
+
+        print!("{}", url);
+
+        // Waiting for a response
+        let response = client.get(url).headers(headers).send().await;
+
+        info!("Got response");
+
+        // The response
+        // TODO: For now just printed, then used as telemetry
+        match response {
+            Ok(res) => {
+                info!("Ok");
+
+                let message = res.text().await;
+                match message {
+                    Ok(string) => {
+                        print!("{}", string);
+                        info!("Got response {}", string);
+                        let msg_payload : Result<Message, serde_json::Error> = serde_json::from_str(&string);
+                        match msg_payload {
+                            Ok(payload) =>{
+                                println!("Message received is: {:?}\n", payload);
+                                println!("Singular fields: msg: {:?} dts: {:?} data:{:?}\n", payload.message, payload.details, payload.data);
+                                let flight_data: Result<LightTelemetryMessage, serde_json::Error> = serde_json::from_str(&payload.data);
+                                match flight_data {
+                                    Ok(data) =>{
+                                        let mut packets = Vec::new();
+
+                                        // Send the current GPS as origin for the NAS
+                                        let origin_data = ROCKET_STATS_TM_DATA {
+                                            ref_lat: data.lat as f32,
+                                            ref_lon: data.lon as f32,
+                                            ref_alt: 0 as f32,
+                                            ..Default::default()
+                                        };    
+                                        let mav_msg = MavMessage::ROCKET_STATS_TM(origin_data);
+                                        packets.push(TimedMessage::new(0, mav_msg));
+
+                                        // Send as NAS the Down from altitude, NE=0 and speed considering the heading
+                                        let nas_data = ROCKET_FLIGHT_TM_DATA{
+                                            nas_n: 0 as f32,
+                                            nas_e: 0 as f32,
+                                            nas_d: -data.alt  as f32,
+                                            nas_vn: (data.track as f32).cos()*(data.gspeed as f32)/2 as f32,    // From heading and knots to m/s
+                                            nas_ve: (data.track as f32).sin()*(data.gspeed as f32)/2  as f32,   // From heading and knots to m/s
+                                            nas_vd: -60.0*(data.vspeed as f32)/3.2808399 as f32,                  // Converting feet/min to m/s
+                                            ..Default::default()
+                                        };
+
+                                        let mav_msg = MavMessage::ROCKET_FLIGHT_TM(nas_data);
+                                        packets.push(TimedMessage::new(0, mav_msg));
+                                    }
+                                    Err(err)=>{error!("An error occured deserializing the data{}", err)}
+                                }
+                            }
+                            Err(err) =>{error!("An error occurred deserializing the message {}", err)}
+                        }
+
+                    }
+                    Err(err) => {
+                        error!("An error occurred in the message {}", err)
+                    }
+                }
+            }
+            Err(err) => {
+                error!("An error occurred in the message {}", err)
+            }
+        }
+
+        // Sleeping since request costs!
+        std::thread::sleep(Duration::from_millis(10000));
+    }
+}
-- 
GitLab