diff --git a/src/whisker/node_info.rs b/src/whisker/node_info.rs index 73c8fd8ea96acc95ad9a27029d0f5864b1dbf6ba..7d4bd4727ca1ce57118f134c33435f87278e66a5 100644 --- a/src/whisker/node_info.rs +++ b/src/whisker/node_info.rs @@ -9,6 +9,11 @@ pub struct NodeInfo { voltage: Option<u8>, xcvr_temperature: Option<i8>, battery_charge: Option<u8>, + altitude: Option<f32>, + balloon: bool, + ambient_temperature: Option<i8>, + ambient_humidity: Option<u8>, + ambient_pressure: Option<u16>, } impl NodeInfo { @@ -59,6 +64,32 @@ impl NodeInfo { self.battery_charge.map(|b| b as f64 / 2.55) } + /// Higher precision altitude than what's available in the GPS whisker. Takes precedent if set. Useful for high altitude balloons. + /// Meters + pub fn altitude(&self) -> Option<f32> { + self.altitude + } + + /// If true, this whisker was emitted by a high altitude balloon payload + pub fn is_balloon(&self) -> bool { + self.balloon + } + + /// Degrees C + pub fn ambient_temperature(&self) -> Option<i8> { + self.ambient_temperature + } + + /// Relative humidity, percent + pub fn ambient_humidity(&self) -> Option<f64> { + self.ambient_humidity.map(|x| x as f64 / 2.55) + } + + /// Decapascal (daPa) + pub fn ambient_pressure(&self) -> Option<u16> { + self.ambient_pressure + } + pub fn encode<'a>(&self, buf: &'a mut [u8]) -> Option<&'a [u8]> { let mut bitmask: u32 = 0; @@ -128,6 +159,38 @@ impl NodeInfo { i += 1; } + if let Some(x) = self.altitude { + bitmask |= 512; + + buf.get_mut(i..(i + 4))?.copy_from_slice(&x.to_le_bytes()); + i += 4; + } + + if self.balloon { + bitmask |= 1024; + } + + if let Some(x) = self.ambient_temperature { + bitmask |= 2048; + + *buf.get_mut(i)? = x.to_le_bytes()[0]; + i += 1; + } + + if let Some(x) = self.ambient_humidity { + bitmask |= 4096; + + *buf.get_mut(i)? = x; + i += 1; + } + + if let Some(x) = self.ambient_pressure { + bitmask |= 8192; + + buf.get_mut(i..(i + 2))?.copy_from_slice(&x.to_le_bytes()); + i += 2; + } + buf[0] = (i - 1).try_into().ok()?; buf[1..4].copy_from_slice(&bitmask.to_be_bytes()[1..]); @@ -141,17 +204,17 @@ impl NodeInfo { let mut i = 4; if bitmask & 1 > 0 { - builder = builder.hardware_id(u16::from_le_bytes([*data.get(i)?, *data.get(i + 1)?])); + builder.hardware_id = Some(u16::from_le_bytes([*data.get(i)?, *data.get(i + 1)?])); i += 2; } if bitmask & 2 > 0 { - builder = builder.software_id(*data.get(i)?); + builder.software_id = Some(*data.get(i)?); i += 1; } if bitmask & 4 > 0 { - builder = builder.uptime(u32::from_le_bytes([ + builder.uptime = Some(u32::from_le_bytes([ *data.get(i)?, *data.get(i + 1)?, *data.get(i + 2)?, @@ -162,7 +225,7 @@ impl NodeInfo { } if bitmask & 8 > 0 { - builder = builder.antenna_height(*data.get(i)?); + builder.antenna_height = Some(*data.get(i)?); i += 1; } @@ -182,14 +245,44 @@ impl NodeInfo { } if bitmask & 128 > 0 { - builder = builder.xcvr_temperature(i8::from_le_bytes([*data.get(i)?])); + builder.xcvr_temperature = Some(i8::from_le_bytes([*data.get(i)?])); i += 1; } if bitmask & 256 > 0 { builder.battery_charge = Some(*data.get(i)?); + i += 1; } + if bitmask & 512 > 0 { + builder.altitude = Some(f32::from_le_bytes( + data.get(i..(i + 4))?.try_into().unwrap(), + )); + i += 4; + } + + builder.balloon = bitmask & 1024 > 0; + + if bitmask & 2048 > 0 { + builder.ambient_temperature = Some(i8::from_le_bytes([*data.get(i)?])); + i += 1; + } + + if bitmask & 4096 > 0 { + builder.ambient_humidity = Some(*data.get(i)?); + i += 1; + } + + if bitmask & 8192 > 0 { + builder.ambient_pressure = Some(u16::from_le_bytes( + data.get(i..(i + 2))?.try_into().unwrap(), + )); + i += 2; + } + + // prevent unused variable warning + let _ = i; + Some(builder.build()) } } @@ -205,6 +298,11 @@ pub struct NodeInfoBuilder { voltage: Option<u8>, xcvr_temperature: Option<i8>, battery_charge: Option<u8>, + altitude: Option<f32>, + balloon: bool, + ambient_temperature: Option<i8>, + ambient_humidity: Option<u8>, + ambient_pressure: Option<u16>, } impl NodeInfoBuilder { @@ -219,6 +317,11 @@ impl NodeInfoBuilder { voltage, xcvr_temperature, battery_charge, + altitude, + balloon, + ambient_temperature, + ambient_humidity, + ambient_pressure, } = self; NodeInfo { @@ -231,6 +334,11 @@ impl NodeInfoBuilder { voltage, xcvr_temperature, battery_charge, + altitude, + balloon, + ambient_temperature, + ambient_humidity, + ambient_pressure, } } @@ -294,6 +402,43 @@ impl NodeInfoBuilder { self } + + /// Higher precision altitude than what's available in the GPS whisker. Takes precedent if set. Useful for high altitude balloons. + /// Meters + pub fn altitude(mut self, val: f32) -> Self { + self.altitude = Some(val); + + self + } + + /// If true, this whisker was emitted by a high altitude balloon payload + /// Please only set this on actual balloon payloads. Otherwise it can cause issues for downstream users! + pub fn set_balloon(mut self) -> Self { + self.balloon = true; + + self + } + + /// Degrees C + pub fn ambient_temperature(mut self, val: i8) -> Self { + self.ambient_temperature = Some(val); + + self + } + + /// Relative humidity, percent + pub fn ambient_humidity(mut self, val: f64) -> Self { + self.ambient_humidity = Some((val * 2.55).min(255.0) as u8); + + self + } + + /// Decapascal (daPa) + pub fn ambient_pressure(mut self, val: u16) -> Self { + self.ambient_pressure = Some(val); + + self + } } #[cfg(test)]