Skip to content
Snippets Groups Projects
packet.rs 34.2 KiB
Newer Older
use core::fmt::Debug;

use crc::{Crc, CRC_16_IBM_SDLC};
Stephen D's avatar
Stephen D committed
use labrador_ldpc::decoder::DecodeFrom;

use crate::{
Stephen D's avatar
Stephen D committed
    buffer::{Buffer, BufferOverflow},
Stephen D's avatar
Stephen D committed
    error::{CommentError, DecodeError, DigipeatError, EncodeError, PacketRouteAppendError},
    identity::Identity,
Stephen D's avatar
Stephen D committed
    interleaver, ldpc, utf8,
    whisker::{
        Arbitrary, Comment, Destination, Gps, Identification, NodeInfo, PastHop, Route, RouteHop,
Stephen D's avatar
Stephen D committed
        Timestamp, ValidatedWhiskerIter, Whisker, WhiskerIter, COMMENT_TYPE,
Stephen D's avatar
Stephen D committed
    whitener,
};

const X25: Crc<u16> = Crc::<u16>::new(&CRC_16_IBM_SDLC);

macro_rules! uniq_whisker {
    ($t:meta) => {
        ::paste::paste! {
Stephen D's avatar
Stephen D committed
            pub fn [<$t:snake:lower>](&self) -> Option<$t> {
                self.iter().find_map(|w| match w {
                    Whisker::$t(x) => Some(x),
                    _ => None,
                })
            }

Stephen D's avatar
Stephen D committed
            pub fn [<add_ $t:snake:lower>](&mut self, w: $t) -> Result<(), EncodeError> {
                if self.[<$t:snake:lower>]().is_some() {
                    return Err(EncodeError::DuplicateData);
                }

                try_lock(&mut self.buf, |data| {
					let mut buf = [0; 256];
                    // safe to unwrap since we know we have enough space
                    let out = w.encode(&mut buf).unwrap();

Stephen D's avatar
Stephen D committed
                    data.try_push(crate::whisker::[<$t:snake:upper _TYPE>]).ok().ok_or(EncodeError::CatsOverflow)?;
                    data.try_extend_from_slice(out).ok().ok_or(EncodeError::CatsOverflow)?;

                    Ok(())
                })?;

                Ok(())
            }
Stephen D's avatar
Stephen D committed
			pub fn [<clear_ $t:snake:lower>](&mut self) {
				self.clear_by_type(crate::whisker::[<$t:snake:upper _TYPE>], false);
        }
    };
}

macro_rules! poly_whisker {
    ($t: meta) => {
        ::paste::paste! {
            pub fn [<$t:lower _iter>](&self) -> core::iter::FilterMap<ValidatedWhiskerIter, fn(Whisker) -> Option<$t>> {
				fn filt(w: Whisker) -> Option<$t> {
                    match w {
                        Whisker::$t(x) => Some(x),
                        _ => None
                    }
				}

                self.iter().filter_map(filt)
            }

			pub fn [<add_ $t:lower>](&mut self, w: $t) -> Result<(), EncodeError> {
				try_lock(&mut self.buf, |data| {
					let mut buf = [0; 256];
                    // safe to unwrap since we know we have enough space
                    let out = w.encode(&mut buf).unwrap();

                    data.try_push(crate::whisker::[<$t:upper _TYPE>]).ok().ok_or(EncodeError::CatsOverflow)?;
                    data.try_extend_from_slice(out).ok().ok_or(EncodeError::CatsOverflow)?;

                    Ok(())
                })?;

                Ok(())
			}

			pub fn [<clear_ $t:lower>](&mut self) {
				self.clear_by_type(crate::whisker::[<$t:upper _TYPE>], true);
			}
Stephen D's avatar
Stephen D committed
/// N is the maximum packet size we can handle
/// Panics if N >= 8191
Stephen D's avatar
Stephen D committed
pub struct Packet<'a, const N: usize> {
    buf: Buffer<'a, N>,
Stephen D's avatar
Stephen D committed
impl<'a, const N: usize> Packet<'a, N> {
    pub fn new(buf: &'a mut [u8; N]) -> Self {
        Self { buf: buf.into() }
    }

Stephen D's avatar
Stephen D committed
    pub fn clone_backing<'b>(&self, buf: &'b mut [u8; N]) -> Packet<'b, N> {
        Packet {
            buf: self.buf.clone_backing(buf),
        }
    }

Stephen D's avatar
Stephen D committed
    /// Expects bytes in the `buf`
    /// `buf` is used as the backing buffer for the Packet
    pub fn decode(buf: Buffer<'a, N>) -> Result<Self, DecodeError> {
Stephen D's avatar
Stephen D committed
        assert!(N <= 8191);

        // validate the data
Stephen D's avatar
Stephen D committed
        for w in WhiskerIter::new(&buf) {
Stephen D's avatar
Stephen D committed
        let comment_iter = WhiskerIter::new(&buf)
Stephen D's avatar
Stephen D committed
            .filter_map(|w| match w.unwrap() {
                Whisker::Comment(c) => Some(c.internal_data().clone()),
                _ => None,
            })
            .flatten();

        if !utf8::validate(comment_iter) {
            return Err(DecodeError::InvalidComment);
        }

Stephen D's avatar
Stephen D committed
        Ok(Self { buf })
    }

    pub fn encode(&self) -> &[u8] {
Stephen D's avatar
Stephen D committed
        &self.buf
    }

    /// Directly after the CRC block in The Pipeline
Stephen D's avatar
Stephen D committed
    pub fn semi_encode(mut self) -> Result<Buffer<'a, N>, (EncodeError, Self)> {
        let crc = X25.checksum(&self.buf).to_le_bytes();
Stephen D's avatar
Stephen D committed
        let res: Result<(), BufferOverflow> = try_lock(&mut self.buf, |data| {
            data.try_push(crc[0])?;
            data.try_push(crc[1])?;

            Ok(())
        });

        match res {
Stephen D's avatar
Stephen D committed
            Ok(()) => Ok(self.buf),
            Err(_) => Err((EncodeError::CatsOverflow, self)),
        }
    }

    /// Directly after the CRC block in The Pipeline
Stephen D's avatar
Stephen D committed
    /// Expects bytes in the `buf`
    /// `buf` is used as the backing buffer for the Packet
    pub fn semi_decode(mut buf: Buffer<'a, N>) -> Result<Self, DecodeError> {
Stephen D's avatar
Stephen D committed
        let crc1 = buf.pop().ok_or(DecodeError::UnexpectedEndOfInput)?;
        let crc0 = buf.pop().ok_or(DecodeError::UnexpectedEndOfInput)?;
        let crc_expected = u16::from_le_bytes([crc0, crc1]);
Stephen D's avatar
Stephen D committed
        let crc_actual = X25.checksum(&buf);

        if crc_expected != crc_actual {
            return Err(DecodeError::CrcMismatch);
        }

Stephen D's avatar
Stephen D committed
        Self::decode(buf)
Stephen D's avatar
Stephen D committed
    /// Encodes packet for transmission on the air.
    /// Includes the data length L, but does not include the preamble or sync word.
Stephen D's avatar
Stephen D committed
    pub fn fully_encode<const M: usize>(self, out: &mut Buffer<M>) -> Result<(), EncodeError> {
Stephen D's avatar
Stephen D committed
        let mut data = self.semi_encode().map_err(|(err, _)| err)?;
        whitener::whiten(&mut data);
        ldpc::encode(&mut data)?;
        // safe to unwrap - length must be below 8191
        out.try_extend_from_slice(&u16::try_from(data.len()).unwrap().to_le_bytes())
            .map_err(|_| EncodeError::CatsOverflow)?;
        interleaver::interleave(&data, out)?;
Stephen D's avatar
Stephen D committed

Stephen D's avatar
Stephen D committed
    }

    /// Decodes packet that was received over the air.
    /// Packet shouldn't have preamble, sync word, or data langth L.
Stephen D's avatar
Stephen D committed
    /// Expects bytes in `data`
Stephen D's avatar
Stephen D committed
    /// `buf` is used as the backing buffer for the Packet
    pub fn fully_decode(data: &[u8], buf: &'a mut [u8; N]) -> Result<Self, DecodeError> {
        let mut buf = Buffer::new_empty(buf);
        interleaver::uninterleave(data, &mut buf).map_err(|_| DecodeError::Overflow)?;
        ldpc::decode(&mut buf).ok_or(DecodeError::LdpcError)?;
        whitener::whiten(&mut buf);
Stephen D's avatar
Stephen D committed

        Self::semi_decode(buf)
Stephen D's avatar
Stephen D committed
    /// Expects soft bits in `data`. Bits should be LLR, with positive numbers more likely to be 0.
    /// Returns `DecodeError::Overflow` if `M` is less than `data.len()`.
    pub fn fully_decode_soft<const M: usize, T: DecodeFrom>(
        data: &mut [T],
        buf: &'a mut [u8; N],
    ) -> Result<Self, DecodeError> {
        let mut out = [T::zero(); M];
        let mut out = Buffer::new_empty(&mut out);
        interleaver::uninterleave_soft(data, &mut out).map_err(|_| DecodeError::Overflow)?;
        let mut buf = Buffer::new_empty(buf);
        ldpc::decode_soft(&mut out, &mut buf).ok_or(DecodeError::LdpcError)?;
        whitener::whiten(&mut buf);

        Self::semi_decode(buf)
    }

    pub fn iter(&self) -> ValidatedWhiskerIter {
        ValidatedWhiskerIter::new(&self.buf)
    uniq_whisker!(Identification);
    uniq_whisker!(Timestamp);
    uniq_whisker!(Gps);
    uniq_whisker!(Route);
Stephen D's avatar
Stephen D committed
    uniq_whisker!(NodeInfo);
    poly_whisker!(Destination);
    poly_whisker!(Arbitrary);
    pub fn comment<'b>(&self, buf: &'b mut [u8]) -> Result<&'b str, CommentError> {
        let iter = self.iter().filter_map(|w| match w {
            Whisker::Comment(c) => Some(c),
            _ => None,
        });

        let mut i = 0;
        let mut has_comment = false;
        for c in iter {
            has_comment = true;
            let data = c.internal_data();
            buf.get_mut(i..(i + data.len()))
                .ok_or(CommentError::BufferOverflow)?
                .copy_from_slice(data);
        if !has_comment {
            return Err(CommentError::NoComment);
        }

Stephen D's avatar
Stephen D committed
        // Safe to unwrap since the comment was pre-validated
        Ok(core::str::from_utf8(&buf[0..i]).unwrap())
    }

    pub fn add_comment(&mut self, comment: &str) -> Result<(), EncodeError> {
        if self.iter().any(|w| matches!(w, Whisker::Comment(_))) {
            // we already have a comment
            return Err(EncodeError::DuplicateData);
        }

        try_lock(&mut self.buf, |data| {
            let mut comment = comment.as_bytes();

            while comment.len() > 255 {
                let c = Comment::new(&comment[0..255]).unwrap();
                let mut buf = [0; 256];
                let out = c.encode(&mut buf).unwrap();

                data.try_push(COMMENT_TYPE)
                    .map_err(|_| EncodeError::CatsOverflow)?;
                data.try_extend_from_slice(out)
                    .map_err(|_| EncodeError::CatsOverflow)?;

                comment = &comment[255..];
            }

            let c = Comment::new(comment).unwrap();
            let mut buf = [0; 256];
            let out = c.encode(&mut buf).unwrap();

            data.try_push(COMMENT_TYPE)
                .map_err(|_| EncodeError::CatsOverflow)?;
            data.try_extend_from_slice(out)
                .map_err(|_| EncodeError::CatsOverflow)?;

            Ok(())
        })?;

        Ok(())
    }

    pub fn clear_comment(&mut self) {
        self.clear_by_type(COMMENT_TYPE, true);
    }

Stephen D's avatar
Stephen D committed
    /// Given the callsign and ssid of a node, should it digipeat this packet?
    /// Takes into account things such as if we've digipeated it already, the max hops, etc.
    pub fn should_digipeat(&self, identity: Identity) -> Result<(), DigipeatError> {
Stephen D's avatar
Stephen D committed
        let route = match self.route() {
            Some(x) => x,
            None => {
                return Err(DigipeatError::NoRoute);
            }
        };

        if let Some(ident) = self.identification() {
            if &ident.callsign == identity.callsign() && ident.ssid == identity.ssid() {
Stephen D's avatar
Stephen D committed
                return Err(DigipeatError::Us);
            }
        }

        let max_hops: usize = route.max_hops.into();
        let cur_hops = route
            .iter()
            .filter(|r| match r {
                RouteHop::Internet => false,
                RouteHop::Past(_) => true,
                RouteHop::Future(_) => false,
Stephen D's avatar
Stephen D committed
            })
            .count();

        if max_hops <= cur_hops {
            return Err(DigipeatError::MaxHops);
        }

        let already_digipeated = route.iter().any(|r| match r {
            RouteHop::Internet => false,
            RouteHop::Past(past_hop) => past_hop.identity() == identity,
            RouteHop::Future(_) => false,
Stephen D's avatar
Stephen D committed
        });

        if already_digipeated {
            return Err(DigipeatError::AlreadyDigipeated);
        }

        let next_node = route.iter().find_map(|r| match r {
            RouteHop::Future(x) => Some(x),
Stephen D's avatar
Stephen D committed
            _ => None,
        });

        match next_node {
            Some(ident) if ident != identity => Err(DigipeatError::SetDestiny),
Stephen D's avatar
Stephen D committed
            _ => Ok(()),
        }
    }

    /// Note that if this fails due to a CATS overflow, it will wipe the route off of the packet
    pub fn append_to_route(
        &mut self,
        callsign: &str,
        ssid: u8,
Stephen D's avatar
Stephen D committed
        rssi: Option<f64>,
Stephen D's avatar
Stephen D committed
    ) -> Result<(), PacketRouteAppendError> {
        let mut route = self.route().ok_or(PacketRouteAppendError::NoRouteWhisker)?;
        route
            .append_hop(PastHop::new(Identity::new(callsign, ssid), rssi))
Stephen D's avatar
Stephen D committed
            .map_err(|error| PacketRouteAppendError::Route { error })?;
        self.clear_route();

        self.add_route(route)
            .map_err(|_| PacketRouteAppendError::PacketOverflow)?;

        Ok(())
    }

    fn clear_by_type(&mut self, whisker_type: u8, all: bool) {
        let mut i = 0;
        while i < self.buf.len() {
            let t = self.buf[i];
            let step = usize::from(self.buf[i + 1]) + 2;

            if t == whisker_type {
                self.buf.drain(i, i + step);

                if !all {
                    return;
                }
            } else {
                i += step;
            }
        }
    }
Stephen D's avatar
Stephen D committed
impl<const N: usize> Debug for Packet<'_, N> {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        f.debug_list()
            .entries(ValidatedWhiskerIter::new(&self.buf))
            .finish()
    }
}

// if the function returns an error, we roll back the array
// by rolling it back, we really just pop bytes off the end until the length matches
Stephen D's avatar
Stephen D committed
fn try_lock<'a, const N: usize, T, E, F: Fn(&mut Buffer<'a, N>) -> Result<T, E>>(
    buf: &mut Buffer<'a, N>,
    f: F,
) -> Result<T, E> {
Stephen D's avatar
Stephen D committed
    let len = buf.len();
Stephen D's avatar
Stephen D committed
    match f(buf) {
        Ok(x) => Ok(x),
        Err(e) => {
Stephen D's avatar
Stephen D committed
            buf.truncate(len);
    use super::*;
    use crate::soft_bit::FromHardBit;
    use crate::whisker::NodeInfoBuilder;
    use arrayvec::ArrayString;
Stephen D's avatar
Stephen D committed
    use bitvec::{order::Msb0, view::BitView};
    #[test]
    fn dest() {
        let d1 = Destination::new(false, 7, "CALL1", 23).unwrap();
        let d2 = Destination::new(true, 23, "CALL2", 2).unwrap();

        let mut buf = [0; 1024];
        let mut packet: Packet<1024> = Packet::new(&mut buf);
        packet.add_destination(d1.clone()).unwrap();
        packet.add_destination(d2.clone()).unwrap();

        let mut dests = packet.destination_iter();
        assert_eq!(d1, dests.next().unwrap());
        assert_eq!(d2, dests.next().unwrap());
        assert_eq!(None, dests.next());
    }

    #[test]
    fn route_clear() {
        let mut buf = [0; 1024];
        let mut p: Packet<1024> = Packet::new(&mut buf);
        p.add_identification(Identification::new("call", 43, 123).unwrap())
            .unwrap();
        let mut r = Route::new(8);
        r.push_internet().unwrap();
        p.add_route(r).unwrap();
        p.add_comment("This is a comment").unwrap();

        p.clear_route();

        assert_eq!(
            Identification::new("call", 43, 123).unwrap(),
            p.identification().unwrap()
        );
        assert_eq!(None, p.route());
        let mut buf = [0; 32];
        assert_eq!("This is a comment", p.comment(&mut buf).unwrap());
    }

    #[test]
    fn semi_e2e() {
        let comment = "Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.";

        let mut buf = [0; 2048];
        let mut packet = Packet::new(&mut buf);
        packet
            .add_identification(Identification {
                icon: 123,
                callsign: ArrayString::from("ABCXYZ_LONG_CALL").unwrap(),
                ssid: 43,
            })
            .unwrap();

        packet.add_comment(comment).unwrap();

        let res = packet.add_identification(Identification {
            icon: 456,
            callsign: ArrayString::from("NOPE").unwrap(),
            ssid: 0,
        });
        assert!(matches!(res, Err(EncodeError::DuplicateData)));

        let semi = packet.semi_encode().unwrap();

        let packet2 = Packet::semi_decode(semi).unwrap();
        assert_eq!(
            Identification {
                icon: 123,
                callsign: ArrayString::from("ABCXYZ_LONG_CALL").unwrap(),
                ssid: 43,
            },
            packet2.identification().unwrap()
        );

        let mut buf = [0; 1024];
        assert_eq!(comment, packet2.comment(&mut buf).unwrap());
    }
Stephen D's avatar
Stephen D committed

    #[test]
    fn full_e2e() {
        let comment = "Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.";

        let mut buf = [0; 4096];
        let mut packet = Packet::new(&mut buf);
Stephen D's avatar
Stephen D committed
        packet
            .add_identification(Identification {
                icon: 123,
                callsign: ArrayString::from("ABCXYZ_LONG_CALL").unwrap(),
                ssid: 43,
            })
            .unwrap();

        packet.add_comment(comment).unwrap();

        let res = packet.add_identification(Identification {
            icon: 456,
            callsign: ArrayString::from("NOPE").unwrap(),
            ssid: 0,
        });
        assert!(matches!(res, Err(EncodeError::DuplicateData)));

        let mut buf2 = [0; 4096];
        let mut fully = Buffer::new_empty(&mut buf2);
        packet.fully_encode(&mut fully).unwrap();
Stephen D's avatar
Stephen D committed

        fully[40] ^= 0x55;
        fully[844] ^= 0x7B;

        // exclude length
        let mut buf3 = [0; 8191];
        let packet2: Packet<8191> = Packet::fully_decode(&fully[2..], &mut buf3).unwrap();
Stephen D's avatar
Stephen D committed
        assert_eq!(
            Identification {
                icon: 123,
                callsign: ArrayString::from("ABCXYZ_LONG_CALL").unwrap(),
                ssid: 43,
            },
            packet2.identification().unwrap()
Stephen D's avatar
Stephen D committed
        );

        let mut buf = [0; 1024];
        assert_eq!(comment, packet2.comment(&mut buf).unwrap());
    }

    #[test]
    fn full_e2e_soft_decode() {
        let comment = "Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.Hello world! This is a comment. It's long so that it needs to be split across more than one whisker.";

        let mut buf = [0; 4096];
        let mut packet = Packet::new(&mut buf);
        packet
            .add_identification(Identification {
                icon: 123,
                callsign: ArrayString::from("ABCXYZ_LONG_CALL").unwrap(),
                ssid: 43,
            })
            .unwrap();

        packet.add_comment(comment).unwrap();

        let res = packet.add_identification(Identification {
            icon: 456,
            callsign: ArrayString::from("NOPE").unwrap(),
            ssid: 0,
        });
        assert!(matches!(res, Err(EncodeError::DuplicateData)));

        let mut buf2 = [0; 4096];
        let mut fully = Buffer::new_empty(&mut buf2);
        packet.fully_encode(&mut fully).unwrap();

        fully[40] ^= 0x55;
        fully[844] ^= 0x7B;

        let mut soft = [0.0; 8191 * 8];
        let mut soft = Buffer::new_empty(&mut soft);
        for b in fully.view_bits::<Msb0>().iter() {
            soft.push(f32::from_hard_bit(*b));
        }
        let soft = &mut soft[16..];

        let mut buf3 = [0; 8191];
        // exclude length
        let packet2: Packet<8191> =
            Packet::fully_decode_soft::<{ 8191 * 8 }, _>(soft, &mut buf3).unwrap();
        assert_eq!(
            Identification {
                icon: 123,
                callsign: ArrayString::from("ABCXYZ_LONG_CALL").unwrap(),
                ssid: 43,
            },
            packet2.identification().unwrap()
Stephen D's avatar
Stephen D committed
        );

        let mut buf = [0; 1024];
        assert_eq!(comment, packet2.comment(&mut buf).unwrap());
    }
Stephen D's avatar
Stephen D committed

Stephen D's avatar
Stephen D committed
    #[test]
    fn node_info_e2e() {
        let mut buf = [0; 4096];
        let mut packet = Packet::new(&mut buf);
        packet
            .add_node_info(
                NodeInfoBuilder::default()
                    .hardware_id(0xBEEF)
                    .software_id(0xBC)
                    .uptime(2304)
                    .antenna_height(5)
                    .antenna_gain(3.0)
                    .tx_power(30.0)
                    .voltage(12.6)
                    .xcvr_temperature(-15)
                    .battery_charge(65.0)
                    .build(),
            )
            .unwrap();

        let mut buf2 = [0; 4096];
        let mut encoded = Buffer::new_empty(&mut buf2);
        packet.fully_encode(&mut encoded).unwrap();

        let mut buf3 = [0; 4096];
        let packet2 = Packet::fully_decode(&encoded[2..], &mut buf3).unwrap();

        let node_info = packet2.node_info().unwrap();
        assert_eq!(0xBEEF, node_info.hardware_id().unwrap());
        assert_eq!(0xBC, node_info.software_id().unwrap());
        assert_eq!(2304, node_info.uptime().unwrap());
        assert_eq!(5, node_info.antenna_height().unwrap());
        assert_eq!(3.0, node_info.antenna_gain().unwrap());
        assert_eq!(30.0, node_info.tx_power().unwrap());
        assert_eq!(12.6, node_info.voltage().unwrap());
        assert_eq!(-15, node_info.xcvr_temperature().unwrap());
        assert_eq!(64.70588235294117, node_info.battery_charge().unwrap());
    }

    #[test]
    fn node_info_e2e_some_unpopulated() {
        let mut buf = [0; 4096];
        let mut packet = Packet::new(&mut buf);
        packet
            .add_node_info(
                NodeInfoBuilder::default()
                    .software_id(0xBC)
                    .uptime(2304)
                    .antenna_gain(3.0)
                    .voltage(12.6)
                    .xcvr_temperature(-15)
                    .build(),
            )
            .unwrap();

        let mut buf2 = [0; 4096];
        let mut encoded = Buffer::new_empty(&mut buf2);
        packet.fully_encode(&mut encoded).unwrap();

        let mut buf3 = [0; 4096];
        let packet2 = Packet::fully_decode(&encoded[2..], &mut buf3).unwrap();

        let node_info = packet2.node_info().unwrap();
        assert_eq!(None, node_info.hardware_id());
        assert_eq!(0xBC, node_info.software_id().unwrap());
        assert_eq!(2304, node_info.uptime().unwrap());
        assert_eq!(None, node_info.antenna_height());
        assert_eq!(3.0, node_info.antenna_gain().unwrap());
        assert_eq!(None, node_info.tx_power());
        assert_eq!(12.6, node_info.voltage().unwrap());
        assert_eq!(-15, node_info.xcvr_temperature().unwrap());
        assert_eq!(None, node_info.battery_charge());
    }

Stephen D's avatar
Stephen D committed
    #[test]
    fn fully_decode_fuzz_tests() {
        let data = [
            112, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 74, 0, 0, 0, 0, 41, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 114, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 10, 112, 0, 0, 0, 0, 0, 0, 2, 1, 0,
            0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 74, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39,
            114, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 247, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 118, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 7, 0, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 118, 118, 145, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
            59, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 7,
            0, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 118, 59, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 228, 0, 0, 0, 64, 0, 65, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 7, 0, 118, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 118, 1, 0, 0, 10, 118, 118, 118, 118, 118, 118, 118, 118, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 228, 0, 0, 0, 64,
            0, 65, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 7, 0, 118, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 1, 0, 0, 10, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 118, 118, 1, 2, 118, 118, 118, 118, 118, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 118, 0, 0, 118, 118, 118, 118,
            118, 118, 118, 118, 118, 118, 118, 0, 0, 0, 0, 96, 96,
        ];

        let mut buf = [0; 1024];
        let _ = Packet::<1024>::fully_decode(&data, &mut buf);
Stephen D's avatar
Stephen D committed
    }

    #[test]
    fn semi_decode_fuzz_tests() {
        let cases = [
Stephen D's avatar
Stephen D committed
            &[
                42, 64, 64, 64, 229, 40, 64, 64, 0, 0, 173, 173, 173, 173, 173, 173, 173, 173, 64,
                64, 0, 0, 173, 187, 187, 187, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173,
                173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 187, 187,
                187, 101, 157, 138, 74, 101, 157, 138, 74, 0, 0, 0, 0, 1, 126, 255, 255, 255, 187,
                187, 187, 187, 187, 187, 187, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173,
                173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 187, 101, 157, 138, 74, 101,
                157, 138, 106, 0, 0, 0, 0, 1, 126, 255, 255, 255, 0, 212, 0, 0, 0, 0, 1, 187, 187,
                187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
                187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 173, 187, 187, 187, 101,
                157, 138, 74, 101, 157, 138, 74, 0, 0, 0, 0, 1, 126, 255, 255, 255, 0, 212, 0, 0,
                0, 0, 1, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
                187, 187, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 157, 138, 74, 0, 0, 0,
                0, 1, 126, 255, 255, 255, 0, 212, 0, 0, 0, 0, 1, 187, 187, 187, 187, 187, 187, 187,
                187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
                187, 187, 187, 187, 187, 187, 173, 187, 187, 64, 187, 101, 157, 138, 74, 101, 157,
                50, 138, 74, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
                255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
                255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
                255, 255, 255, 255, 255, 1, 126, 255, 255, 255, 0, 212, 0, 0, 0, 0, 1, 187, 187,
                187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
                187, 187, 187, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173,
                173, 173, 173, 173, 173, 173, 64, 64, 0, 0, 173, 173, 173, 173, 173, 173, 173, 173,
                173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173,
                173, 187, 187, 187, 101, 157, 138, 74, 101, 157, 138, 74, 0, 0, 0, 0, 1, 126, 255,
                255, 255, 0, 212, 0, 0, 0, 0, 1, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
                187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
                187, 187, 187, 173, 187, 187, 187, 101, 157, 138, 74, 101, 157, 138, 74, 0, 0, 0,
                0, 1, 126, 255, 255, 255, 0, 212, 0, 0, 0, 0, 1, 187, 187, 0, 0, 192, 192, 0, 187,
                187, 187, 187, 187, 187, 187, 187, 187, 187, 173, 173, 173, 173, 173, 173, 173,
                173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173,
                173, 64, 64, 0, 0, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173,
                173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 173, 187, 187, 187, 101,
                157, 138, 74, 101, 157, 138, 74, 0, 0, 0, 0, 1, 126, 255, 255, 255, 0, 212, 0, 0,
                0, 0, 1, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
                187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
                187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187,
                187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 187, 126,
            ][..],
            &[
                48, 5, 4, 255, 5, 5, 5, 5, 5, 5, 5, 37, 5, 7, 5, 5, 35, 5, 5, 5, 5, 4, 5, 7, 5, 5,
                5, 5, 5, 5, 5, 5, 11, 126, 3, 101, 5, 3, 3, 96, 192, 128, 192, 192,
            ][..],
        ];
Stephen D's avatar
Stephen D committed

        for data in cases {
            let mut buf = [0; 1024];
            let mut buf = Buffer::new_empty(&mut buf);
            buf.extend(data);
            let _ = Packet::<1024>::semi_decode(buf);
        }
Stephen D's avatar
Stephen D committed
    }

    #[test]
    fn decode_fuzz_tests() {
        let cases = [&[4, 0, 0, 0][..], &[4, 5, 0, 0, 0, 10, 255, 255, 0, 0][..]];
Stephen D's avatar
Stephen D committed

        for data in cases {
            let mut buf = [0; 1024];
            let mut buf = Buffer::new_empty(&mut buf);
Stephen D's avatar
Stephen D committed
            buf.extend(data);
            let _ = Packet::<1024>::decode(buf);
        }
Stephen D's avatar
Stephen D committed
    }

    #[test]
    fn fully_decode_soft_fuzz_tests() {
        // When adding to this, don't forget to do the u8 -> i8 conversion
        let cases = [
            &mut [
                -39, -39, -39, -118, -58, -58, -58, -58, -89, -39, -118, -58, -58, -58, 34, 34, 34,
                34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, -58, -58,
                127, 81, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127,
                127, 127, 127, 127, 127, 127, 127, 127, 127, 127, -86, 127, 127, 127, 127, 127,
                127, 127, 127, 127, 127, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, -58, -58, 127,
                81, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 34, 34, 34, 34, 34, 34, 34,
                34, 34, 34, 34, -58, -58, 127, 81, 127, 127, 127, 127, 127, 127, 127, 127, 127,
                127, 127, 127, 127, 127, 127, -128, -128, -128, -128, -128, -128, -128, -128, -128,
                -128, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, -128, -128,
            ][..],
            &mut [
                -73, -73, -73, -73, -75, -76, -73, -73, -73, -73, -73, -73, -73, -73, 73, 72, 72,
                72, 72, 72, 72, 62, -73, -118, 120, 127, 127, 121, 127, 112, 127, 127,
            ],
        ];

        for data in cases {
            let mut buf = [0; 1024];
            let _ = Packet::<1024>::fully_decode_soft::<8192, i8>(data, &mut buf);
        }
    }