Skip to content
Snippets Groups Projects
Select Git revision
  • 2662fd88ededffad2598ece932a58e1049325aaa
  • master default protected
2 results

node_info.rs

Blame
  • Forked from CATS / ham-cats
    Source project has a limited visibility.
    node_info.rs 6.99 KiB
    #[derive(Copy, Clone, Debug)]
    pub struct NodeInfo {
        hardware_id: Option<u16>,
        software_id: Option<u8>,
        uptime: Option<u32>,
        antenna_height: Option<u8>,
        antenna_gain: Option<u8>,
        tx_power: Option<u8>,
        voltage: Option<u8>,
        xcvr_temperature: Option<i8>,
        battery_charge: Option<u8>,
    }
    
    impl NodeInfo {
        pub fn builder() -> NodeInfoBuilder {
            NodeInfoBuilder::default()
        }
    
        pub fn hardware_id(&self) -> Option<u16> {
            self.hardware_id
        }
    
        pub fn software_id(&self) -> Option<u8> {
            self.software_id
        }
    
        /// Seconds
        pub fn uptime(&self) -> Option<u32> {
            self.uptime
        }
    
        /// Meters
        pub fn antenna_height(&self) -> Option<u8> {
            self.antenna_height
        }
    
        /// dBd
        pub fn antenna_gain(&self) -> Option<f64> {
            self.antenna_gain.map(|g| g as f64 / 4.0)
        }
    
        /// dBm
        pub fn tx_power(&self) -> Option<f64> {
            self.tx_power.map(|p| p as f64 / 4.0)
        }
    
        /// Volts
        pub fn voltage(&self) -> Option<f64> {
            self.voltage.map(|v| v as f64 / 10.0)
        }
    
        /// Degrees C
        pub fn xcvr_temperature(&self) -> Option<i8> {
            self.xcvr_temperature
        }
    
        /// Percent
        pub fn battery_charge(&self) -> Option<f64> {
            self.battery_charge.map(|b| b as f64 / 2.55)
        }
    
        pub fn encode<'a>(&self, buf: &'a mut [u8]) -> Option<&'a [u8]> {
            let mut bitmask: u32 = 0;
    
            let mut i = 4;
    
            // TODO these could be macros
            if let Some(x) = self.hardware_id {
                bitmask |= 1;
    
                buf.get_mut(i..(i + 2))?.copy_from_slice(&x.to_le_bytes());
                i += 2;
            }
    
            if let Some(x) = self.software_id {
                bitmask |= 2;
    
                *buf.get_mut(i)? = x;
                i += 1;
            }
    
            if let Some(x) = self.uptime {
                bitmask |= 4;
    
                buf.get_mut(i..(i + 4))?.copy_from_slice(&x.to_le_bytes());
                i += 4;
            }
    
            if let Some(x) = self.antenna_height {
                bitmask |= 8;
    
                *buf.get_mut(i)? = x;
                i += 1;
            }
    
            if let Some(x) = self.antenna_gain {
                bitmask |= 16;
    
                *buf.get_mut(i)? = x;
                i += 1;
            }
    
            if let Some(x) = self.tx_power {
                bitmask |= 32;
    
                *buf.get_mut(i)? = x;
                i += 1;
            }
    
            if let Some(x) = self.voltage {
                bitmask |= 64;
    
                *buf.get_mut(i)? = x;
                i += 1;
            }
    
            if let Some(x) = self.xcvr_temperature {
                bitmask |= 128;
    
                *buf.get_mut(i)? = x.to_le_bytes()[0];
                i += 1;
            }
    
            if let Some(x) = self.battery_charge {
                bitmask |= 256;
    
                *buf.get_mut(i)? = x;
                i += 1;
            }
    
            buf[0] = (i - 1).try_into().ok()?;
            buf[1..4].copy_from_slice(&bitmask.to_be_bytes()[1..]);
    
            Some(&buf[..i])
        }
    
        pub fn decode(data: &[u8]) -> Option<Self> {
            let bitmask = u32::from_be_bytes([0, *data.get(1)?, *data.get(2)?, *data.get(3)?]);
    
            let mut builder = NodeInfoBuilder::default();
            let mut i = 4;
    
            if bitmask & 1 > 0 {
                builder = builder.hardware_id(u16::from_le_bytes([*data.get(i)?, *data.get(i + 1)?]));
                i += 2;
            }
    
            if bitmask & 2 > 0 {
                builder = builder.software_id(*data.get(i)?);
                i += 1;
            }
    
            if bitmask & 4 > 0 {
                builder = builder.uptime(u32::from_le_bytes([
                    *data.get(i)?,
                    *data.get(i + 1)?,
                    *data.get(i + 2)?,
                    *data.get(i + 3)?,
                ]));
    
                i += 4;
            }
    
            if bitmask & 8 > 0 {
                builder = builder.antenna_height(*data.get(i)?);
                i += 1;
            }
    
            if bitmask & 16 > 0 {
                builder.antenna_gain = Some(*data.get(i)?);
                i += 1;
            }
    
            if bitmask & 32 > 0 {
                builder.tx_power = Some(*data.get(i)?);
                i += 1;
            }
    
            if bitmask & 64 > 0 {
                builder.voltage = Some(*data.get(i)?);
                i += 1;
            }
    
            if bitmask & 128 > 0 {
                builder = builder.xcvr_temperature(i8::from_le_bytes([*data.get(i)?]));
                i += 1;
            }
    
            if bitmask & 256 > 0 {
                builder.battery_charge = Some(*data.get(i)?);
            }
    
            Some(builder.build())
        }
    }
    
    #[derive(Default, Copy, Clone)]
    pub struct NodeInfoBuilder {
        hardware_id: Option<u16>,
        software_id: Option<u8>,
        uptime: Option<u32>,
        antenna_height: Option<u8>,
        antenna_gain: Option<u8>,
        tx_power: Option<u8>,
        voltage: Option<u8>,
        xcvr_temperature: Option<i8>,
        battery_charge: Option<u8>,
    }
    
    impl NodeInfoBuilder {
        pub fn build(self) -> NodeInfo {
            let NodeInfoBuilder {
                hardware_id,
                software_id,
                uptime,
                antenna_height,
                antenna_gain,
                tx_power,
                voltage,
                xcvr_temperature,
                battery_charge,
            } = self;
    
            NodeInfo {
                hardware_id,
                software_id,
                uptime,
                antenna_height,
                antenna_gain,
                tx_power,
                voltage,
                xcvr_temperature,
                battery_charge,
            }
        }
    
        pub fn hardware_id(mut self, val: u16) -> Self {
            self.hardware_id = Some(val);
    
            self
        }
    
        pub fn software_id(mut self, val: u8) -> Self {
            self.software_id = Some(val);
    
            self
        }
    
        /// Seconds
        pub fn uptime(mut self, val: u32) -> Self {
            self.uptime = Some(val);
    
            self
        }
    
        /// Meters
        pub fn antenna_height(mut self, val: u8) -> Self {
            self.antenna_height = Some(val);
    
            self
        }
    
        /// dBd
        pub fn antenna_gain(mut self, val: f64) -> Self {
            self.antenna_gain = Some((val * 4.0).min(255.0) as u8);
    
            self
        }
    
        /// dBm
        pub fn tx_power(mut self, val: f64) -> Self {
            self.tx_power = Some((val * 4.0).min(255.0) as u8);
    
            self
        }
    
        /// Volts
        pub fn voltage(mut self, val: f64) -> Self {
            self.voltage = Some((val * 10.0).min(255.0) as u8);
    
            self
        }
    
        /// Degrees C
        pub fn xcvr_temperature(mut self, val: i8) -> Self {
            self.xcvr_temperature = Some(val);
    
            self
        }
    
        /// Percent
        pub fn battery_charge(mut self, val: f64) -> Self {
            self.battery_charge = Some((val * 2.55).min(255.0) as u8);
    
            self
        }
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        // verify examples in the standard doc
        #[test]
        fn node_info_doc_examples() {
            let node_info = NodeInfoBuilder::default()
                .hardware_id(7408)
                .uptime(98)
                .tx_power(30.0)
                .voltage(12.8)
                .build();
    
            let mut buf = [0; 255];
            let encoded = node_info.encode(&mut buf).unwrap();
    
            assert_eq!(
                [0x0B, 0x00, 0x00, 0x65, 0xF0, 0x1C, 0x62, 0x00, 0x00, 0x00, 0x78, 0x80],
                encoded
            );
        }
    }