Forked from
CATS / ham-cats
19 commits behind the upstream repository.
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
}
/// dBi
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
}
/// dBi
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
);
}
}