#[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>,
altitude: Option<f32>,
balloon: bool,
ambient_temperature: Option<i8>,
ambient_humidity: Option<u8>,
ambient_pressure: Option<u16>,
}
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
}
pub fn uptime(&self) -> Option<u32> {
self.uptime
}
pub fn antenna_height(&self) -> Option<u8> {
self.antenna_height
}
pub fn antenna_gain(&self) -> Option<f64> {
self.antenna_gain.map(|g| g as f64 / 4.0)
}
pub fn tx_power(&self) -> Option<f64> {
self.tx_power.map(|p| p as f64 / 4.0)
}
pub fn voltage(&self) -> Option<f64> {
self.voltage.map(|v| v as f64 / 10.0)
}
pub fn xcvr_temperature(&self) -> Option<i8> {
self.xcvr_temperature
}
pub fn battery_charge(&self) -> Option<f64> {
self.battery_charge.map(|b| b as f64 / 2.55)
}
pub fn altitude(&self) -> Option<f32> {
self.altitude
}
pub fn is_balloon(&self) -> bool {
self.balloon
}
pub fn ambient_temperature(&self) -> Option<i8> {
self.ambient_temperature
}
pub fn ambient_humidity(&self) -> Option<f64> {
self.ambient_humidity.map(|x| x as f64 / 2.55)
}
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;
let mut i = 4;
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;
}
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..]);
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.hardware_id = Some(u16::from_le_bytes([*data.get(i)?, *data.get(i + 1)?]));
i += 2;
}
if bitmask & 2 > 0 {
builder.software_id = Some(*data.get(i)?);
i += 1;
}
if bitmask & 4 > 0 {
builder.uptime = Some(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.antenna_height = Some(*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.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;
}
let _ = 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>,
altitude: Option<f32>,
balloon: bool,
ambient_temperature: Option<i8>,
ambient_humidity: Option<u8>,
ambient_pressure: Option<u16>,
}
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,
altitude,
balloon,
ambient_temperature,
ambient_humidity,
ambient_pressure,
} = self;
NodeInfo {
hardware_id,
software_id,
uptime,
antenna_height,
antenna_gain,
tx_power,
voltage,
xcvr_temperature,
battery_charge,
altitude,
balloon,
ambient_temperature,
ambient_humidity,
ambient_pressure,
}
}
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
}
pub fn uptime(mut self, val: u32) -> Self {
self.uptime = Some(val);
self
}
pub fn antenna_height(mut self, val: u8) -> Self {
self.antenna_height = Some(val);
self
}
pub fn antenna_gain(mut self, val: f64) -> Self {
self.antenna_gain = Some((val * 4.0).min(255.0) as u8);
self
}
pub fn tx_power(mut self, val: f64) -> Self {
self.tx_power = Some((val * 4.0).min(255.0) as u8);
self
}
pub fn voltage(mut self, val: f64) -> Self {
self.voltage = Some((val * 10.0).min(255.0) as u8);
self
}
pub fn xcvr_temperature(mut self, val: i8) -> Self {
self.xcvr_temperature = Some(val);
self
}
pub fn battery_charge(mut self, val: f64) -> Self {
self.battery_charge = Some((val * 2.55).min(255.0) as u8);
self
}
pub fn altitude(mut self, val: f32) -> Self {
self.altitude = Some(val);
self
}
pub fn set_balloon(mut self) -> Self {
self.balloon = true;
self
}
pub fn ambient_temperature(mut self, val: i8) -> Self {
self.ambient_temperature = Some(val);
self
}
pub fn ambient_humidity(mut self, val: f64) -> Self {
self.ambient_humidity = Some((val * 2.55).min(255.0) as u8);
self
}
pub fn ambient_pressure(mut self, val: u16) -> Self {
self.ambient_pressure = Some(val);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[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
);
}
}