Skip to content
Snippets Groups Projects
gps.rs 2.6 KiB
Newer Older
use half::f16;

#[derive(Debug, PartialEq, Clone)]
pub struct Gps {
    latitude: i32,
    longitude: i32,
    pub altitude: f16,
    pub max_error: u8,
    heading: u8,
    pub speed: f16,
}

impl Gps {
    /// Constructs a new GPS whisker.
    ///
    /// latitude: degrees
    /// longitude: degrees
    /// altitude: meters
    /// max_error: maximum error distance from specified lat/lon/alt, meters
    /// heading: direction relative to north, degrees
    /// speed: meters per second
    pub fn new(
        latitude: f64,
        longitude: f64,
        altitude: f16,
        max_error: u8,
        heading: f64,
        speed: f16,
    ) -> Self {
        let latitude = latitude.clamp(-89.999, 89.999);
        let latitude = (latitude * ((1u32 << 31) as f64) / 90.0) as i32;

        let longitude = longitude.clamp(-179.999, 179.999);
        let longitude = (longitude * ((1u32 << 31) as f64) / 180.0) as i32;

        let heading = heading.clamp(0.0, 359.999);
        let heading = (heading * 128.0 / 360.0) as u8;

        Self {
            latitude,
            longitude,
            altitude,
            max_error,
            heading,
            speed,
        }
    }

    pub fn latitude(&self) -> f64 {
        (self.latitude as f64) / (1u32 << 31) as f64 * 90.0
    }

    pub fn longitude(&self) -> f64 {
        (self.longitude as f64) / (1u32 << 31) as f64 * 180.0
    }

    pub fn heading(&self) -> f64 {
        self.heading as f64 / 128.0 * 360.0
    }

    pub fn encode<'a>(&self, buf: &'a mut [u8]) -> Option<&'a [u8]> {
        let buf = buf.get_mut(0..15)?;
        buf[0] = 14;
        buf[1..5].copy_from_slice(&self.latitude.to_le_bytes());
        buf[5..9].copy_from_slice(&self.longitude.to_le_bytes());
        buf[9..11].copy_from_slice(&self.altitude.to_le_bytes());
        buf[11] = self.max_error;
        buf[12] = self.heading;
        buf[13..15].copy_from_slice(&self.speed.to_le_bytes());

        Some(buf)
    }

    pub fn decode(data: &[u8]) -> Option<Self> {
        let data = data.get(0..15)?;
        if data[0] != 14 {
            return None;
        }
        let latitude = i32::from_le_bytes(data[1..5].try_into().unwrap());
        let longitude = i32::from_le_bytes(data[5..9].try_into().unwrap());
        let altitude = f16::from_le_bytes(data[9..11].try_into().unwrap());
        let max_error = data[11];
        let heading = data[12];
        let speed = f16::from_le_bytes(data[13..15].try_into().unwrap());

        Some(Self {
            latitude,
            longitude,
            altitude,
            max_error,
            heading,
            speed,
        })
    }
}