diff --git a/src/packet.rs b/src/packet.rs index bb43dce53d35bc075f710797facf2738477faaaf..12288e7bb9c680ce6be2258126f1fb2038070491 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -292,7 +292,7 @@ impl<'a, const N: usize> Packet<'a, N> { .iter() .filter(|r| match r { RouteNode::Internet => false, - RouteNode::Identity(_, _, is_future) => !is_future, + RouteNode::Identity(ident) => !ident.is_future(), }) .count(); @@ -302,7 +302,9 @@ impl<'a, const N: usize> Packet<'a, N> { let already_digipeated = route.iter().any(|r| match r { RouteNode::Internet => false, - RouteNode::Identity(rc, rs, is_future) => rc == callsign && rs == ssid && !is_future, + RouteNode::Identity(ident) => { + ident.callsign() == callsign && ident.ssid() == ssid && !ident.is_future() + } }); if already_digipeated { @@ -310,14 +312,14 @@ impl<'a, const N: usize> Packet<'a, N> { } let next_node = route.iter().find_map(|r| match r { - RouteNode::Identity(c, s, is_future) if is_future => Some((c, s)), + RouteNode::Identity(ident) if ident.is_future() => { + Some((ident.callsign(), ident.ssid())) + } _ => None, }); match next_node { - Some((rc, rs)) if rc != callsign || rs != ssid => { - return Err(DigipeatError::SetDestiny) - } + Some((rc, rs)) if rc != callsign || rs != ssid => Err(DigipeatError::SetDestiny), _ => Ok(()), } } @@ -327,10 +329,11 @@ impl<'a, const N: usize> Packet<'a, N> { &mut self, callsign: &str, ssid: u8, + rssi: Option<f64>, ) -> Result<(), PacketRouteAppendError> { let mut route = self.route().ok_or(PacketRouteAppendError::NoRouteWhisker)?; route - .append_node(callsign, ssid) + .append_node(callsign, ssid, rssi) .map_err(|error| PacketRouteAppendError::Route { error })?; self.clear_route(); diff --git a/src/whisker/mod.rs b/src/whisker/mod.rs index 3352b4ee64950fd9d4570d3292228bf96fdb4d43..dfb94dfa0a48c774f0506f2cf1370fadbe067ab1 100644 --- a/src/whisker/mod.rs +++ b/src/whisker/mod.rs @@ -184,7 +184,7 @@ impl<'a> Iterator for ValidatedWhiskerIter<'a> { mod tests { use half::f16; - use crate::whisker::route::RouteNode; + use crate::whisker::route::{RouteIdentity, RouteNode}; use super::*; @@ -287,40 +287,58 @@ mod tests { #[test] fn route_push_and_iter() { let mut route = Route::new(34); - route.push_callsign("VE2XYZ", 23, false).unwrap(); + route + .push_callsign(RouteIdentity::new("VE2XYZ", 23, None, false)) + .unwrap(); route.push_internet().unwrap(); route.push_internet().unwrap(); route.push_internet().unwrap(); - route.push_callsign("VE9AAAAA", 94, true).unwrap(); - assert!(route.push_callsign("VE9AAAAA", 94, false).is_none()); + route + .push_callsign(RouteIdentity::new("VE9AAAAA", 94, None, true)) + .unwrap(); + // past after future - not allowed + assert!(route + .push_callsign(RouteIdentity::new("VE9AAAAA", 94, None, false)) + .is_none()); // too long assert!(route - .push_callsign( + .push_callsign(RouteIdentity::new( "lsdfjslkdfjlksdjflksfjsdklfjsdklfjsdklfjsdklfjsklfsef;jklsdfjkl;sdf;klsdf;klsjdfJSDJFSKL:DFJDSL:KFskldfj;slkdfjsdkl;fjdskl;fjsdfl;kjsdfl;ksdjfkl;ssdfl;kjsdfl;ksdjf;sdklsd;lfkjsdlfk;jsdl;fkjsd;klfjsd;fljsf;oidfjgwper0tujdfgndfjkl;gjnergjol;kehfgo;dijge;oghdfkl;gjdfkl;gjdeior;lgjedr;ioghjdorighndeklo;grjiop[", 20, + None, true, - ).is_none()); + )).is_none()); route - .push_callsign("This is the last callsign", 0, true) + .push_callsign(RouteIdentity::new( + "This is the last callsign", + 0, + None, + true, + )) .unwrap(); route.push_internet().unwrap(); let mut iter = route.iter(); assert_eq!( - RouteNode::Identity("VE2XYZ", 23, false), + RouteNode::Identity(RouteIdentity::new("VE2XYZ", 23, None, false)), iter.next().unwrap() ); assert_eq!(RouteNode::Internet, iter.next().unwrap()); assert_eq!(RouteNode::Internet, iter.next().unwrap()); assert_eq!(RouteNode::Internet, iter.next().unwrap()); assert_eq!( - RouteNode::Identity("VE9AAAAA", 94, true), + RouteNode::Identity(RouteIdentity::new("VE9AAAAA", 94, None, true)), iter.next().unwrap() ); assert_eq!( - RouteNode::Identity("This is the last callsign", 0, true), + RouteNode::Identity(RouteIdentity::new( + "This is the last callsign", + 0, + None, + true + )), iter.next().unwrap() ); assert_eq!(RouteNode::Internet, iter.next().unwrap()); @@ -332,13 +350,22 @@ mod tests { #[test] fn route_e2e() { let mut route = Route::new(34); - route.push_callsign("VE2XYZ", 23, false).unwrap(); + route + .push_callsign(RouteIdentity::new("VE2XYZ", 23, Some(0.0), false)) + .unwrap(); route.push_internet().unwrap(); route.push_internet().unwrap(); route.push_internet().unwrap(); - route.push_callsign("VE9AAAAA", 94, true).unwrap(); route - .push_callsign("This is the last callsign", 0, true) + .push_callsign(RouteIdentity::new("VE9AAAAA", 94, Some(10.0), true)) + .unwrap(); + route + .push_callsign(RouteIdentity::new( + "This is the last callsign", + 0, + Some(-159.0), + true, + )) .unwrap(); route.push_internet().unwrap(); @@ -352,44 +379,46 @@ mod tests { #[test] fn route_doc_examples() { let mut ex1 = Route::new(4); - ex1.push_callsign("VE1ABC", 0, false); - ex1.push_callsign("VE2DEF", 234, false); - ex1.push_callsign("VE3XYZ", 14, false); + ex1.push_callsign(RouteIdentity::new("VE1ABC", 0, Some(-96.0), false)); + ex1.push_callsign(RouteIdentity::new("VE2DEF", 234, Some(-13.0), false)); + ex1.push_callsign(RouteIdentity::new("VE3XYZ", 14, Some(-106.0), false)); let mut buf = [0; 256]; let encoded = ex1.encode(&mut buf).unwrap(); assert_eq!( &[ - 0x19, 0x04, 0x56, 0x45, 0x31, 0x41, 0x42, 0x43, 0xFF, 0x00, 0x56, 0x45, 0x32, 0x44, - 0x45, 0x46, 0xFF, 0xEA, 0x56, 0x45, 0x33, 0x58, 0x59, 0x5A, 0xFF, 0x0E + 0x1C, 0x04, 0x56, 0x45, 0x31, 0x41, 0x42, 0x43, 0xFF, 0x00, 0x60, 0x56, 0x45, 0x32, + 0x44, 0x45, 0x46, 0xFF, 0xEA, 0xDC, 0x56, 0x45, 0x33, 0x58, 0x59, 0x5A, 0xFF, 0x0E, + 0x51 ], &encoded ); let mut ex2 = Route::new(3); - ex2.push_callsign("VE1ABC", 0, false); + ex2.push_callsign(RouteIdentity::new("VE1ABC", 0, None, false)); ex2.push_internet(); - ex2.push_callsign("VE2DEF", 234, false); + ex2.push_callsign(RouteIdentity::new("VE2DEF", 234, Some(-86.5), false)); ex2.push_internet(); - ex2.push_callsign("VE3XYZ", 14, false); + ex2.push_callsign(RouteIdentity::new("VE3XYZ", 14, Some(-65.0), false)); let encoded = ex2.encode(&mut buf).unwrap(); assert_eq!( &[ - 0x1B, 0x03, 0x56, 0x45, 0x31, 0x41, 0x42, 0x43, 0xFF, 0x00, 0xFE, 0x56, 0x45, 0x32, - 0x44, 0x45, 0x46, 0xFF, 0xEA, 0xFE, 0x56, 0x45, 0x33, 0x58, 0x59, 0x5A, 0xFF, 0x0E + 0x1E, 0x03, 0x56, 0x45, 0x31, 0x41, 0x42, 0x43, 0xFF, 0x00, 0x00, 0xFE, 0x56, 0x45, + 0x32, 0x44, 0x45, 0x46, 0xFF, 0xEA, 0x6E, 0xFE, 0x56, 0x45, 0x33, 0x58, 0x59, 0x5A, + 0xFF, 0x0E, 0x8E ], &encoded ); let mut ex3 = Route::new(0); - ex3.push_callsign("VE1ABC", 0, false); + ex3.push_callsign(RouteIdentity::new("VE1ABC", 0, Some(-42.5), false)); ex3.push_internet(); ex3.push_internet(); let encoded = ex3.encode(&mut buf).unwrap(); assert_eq!( - &[0x0B, 0x00, 0x56, 0x45, 0x31, 0x41, 0x42, 0x43, 0xFF, 0x00, 0xFE, 0xFE], + &[0x0C, 0x00, 0x56, 0x45, 0x31, 0x41, 0x42, 0x43, 0xFF, 0x00, 0xB0, 0xFE, 0xFE], &encoded ); } diff --git a/src/whisker/route.rs b/src/whisker/route.rs index 33ef8c29aa360a69a352bfd4d18820ee5a5a73e1..af3df817d39863157fd7526fdcb5ce731ee812c1 100644 --- a/src/whisker/route.rs +++ b/src/whisker/route.rs @@ -25,25 +25,26 @@ impl Route { /// is_future=false when this callsign has digipeated /// is_future=true when this callsign is expected to be part of the route, but isn't yet /// see the specs for more information - pub fn push_callsign(&mut self, callsign: &str, ssid: u8, is_future: bool) -> Option<()> { - let len = callsign.as_bytes().len() + 2; + pub fn push_callsign(&mut self, ident: RouteIdentity) -> Option<()> { + let len = ident.callsign.as_bytes().len() + 3; let free_space = self.path.capacity() - self.path.len(); if len > free_space { return None; } - self.has_future = self.has_future || is_future; - if self.has_future && !is_future { + self.has_future = self.has_future || ident.is_future; + if self.has_future && !ident.is_future { return None; } // safe to unwrap since we already did a length check self.path - .try_extend_from_slice(callsign.as_bytes()) + .try_extend_from_slice(ident.callsign.as_bytes()) .unwrap(); - self.path.push(if is_future { 0xFD } else { 0xFF }); - self.path.push(ssid); + self.path.push(if ident.is_future { 0xFD } else { 0xFF }); + self.path.push(ident.ssid); + self.path.push(ident.rssi); Some(()) } @@ -65,7 +66,12 @@ impl Route { /// I.e. replace future node if possible /// Returns an Err if the route is out of space, or appending the node doesn't make logical sense /// (there's a future node that doesn't match) - pub fn append_node(&mut self, callsign: &str, ssid: u8) -> Result<(), AppendNodeError> { + pub fn append_node( + &mut self, + callsign: &str, + ssid: u8, + rssi: Option<f64>, + ) -> Result<(), AppendNodeError> { let mut new_route = Route::new(self.max_hops); let mut already_inserted = false; @@ -74,37 +80,34 @@ impl Route { RouteNode::Internet => new_route .push_internet() .ok_or(AppendNodeError::RouteOverflow)?, - RouteNode::Identity(rc, rs, is_future) => { - let us = rc == callsign && rs == ssid; + RouteNode::Identity(ident) => { + let us = ident.callsign() == callsign && ident.ssid() == ssid; - if is_future { + if ident.is_future() { if us { if already_inserted { return Err(AppendNodeError::DuplicateNode); } else { - new_route.push_callsign(callsign, ssid, false); + new_route + .push_callsign(RouteIdentity::new(callsign, ssid, rssi, false)); already_inserted = true; } + } else if already_inserted { + new_route.push_callsign(ident); } else { - if already_inserted { - new_route.push_callsign(rc, rs, true); - } else { - return Err(AppendNodeError::SetFuture); - } + return Err(AppendNodeError::SetFuture); } + } else if us { + return Err(AppendNodeError::DuplicateNode); } else { - if us { - return Err(AppendNodeError::DuplicateNode); - } else { - new_route.push_callsign(rc, rs, false); - } + new_route.push_callsign(ident); } } } } if !already_inserted { - new_route.push_callsign(callsign, ssid, false); + new_route.push_callsign(RouteIdentity::new(callsign, ssid, rssi, false)); } if new_route.iter().count() > usize::from(new_route.max_hops) { @@ -157,7 +160,51 @@ impl Route { #[derive(Debug, Eq, PartialEq)] pub enum RouteNode<'a> { Internet, - Identity(&'a str, u8, bool), + Identity(RouteIdentity<'a>), +} + +#[derive(Debug, Eq, PartialEq)] +pub struct RouteIdentity<'a> { + callsign: &'a str, + ssid: u8, + rssi: u8, + is_future: bool, +} + +impl<'a> RouteIdentity<'a> { + pub fn new(callsign: &'a str, ssid: u8, rssi: Option<f64>, is_future: bool) -> Self { + let rssi = match rssi { + Some(rssi) => (rssi * 1.5 + 240.0).max(1.0) as u8, + None => 0, + }; + + Self { + callsign, + ssid, + rssi, + is_future, + } + } + + pub fn callsign(&self) -> &'a str { + self.callsign + } + + pub fn ssid(&self) -> u8 { + self.ssid + } + + pub fn rssi(&self) -> Option<f64> { + if self.rssi == 0 { + None + } else { + Some(((self.rssi as f64) - 240.0) / 1.5) + } + } + + pub fn is_future(&self) -> bool { + self.is_future + } } #[derive(Debug)] @@ -201,11 +248,16 @@ impl<'a> UntrustedRouteIter<'a> { } self.seen_future |= is_future; - self.i += 1; - let ssid = *self.route.path.get(self.i).ok_or(())?; - self.i += 1; + let ssid = *self.route.path.get(self.i + 1).ok_or(())?; + let rssi = *self.route.path.get(self.i + 2).ok_or(())?; + self.i += 3; - Ok(RouteNode::Identity(callsign, ssid, is_future)) + Ok(RouteNode::Identity(RouteIdentity { + callsign, + ssid, + rssi, + is_future, + })) } } @@ -258,94 +310,164 @@ mod tests { #[test] fn append_fails_when_existing_future() { let mut r = Route::new(5); - r.push_callsign("C1", 0, false); - r.push_callsign("C2", 0, false); - r.push_callsign("C3", 0, true); + r.push_callsign(RouteIdentity::new("C1", 0, None, false)) + .unwrap(); + r.push_callsign(RouteIdentity::new("C2", 0, None, false)) + .unwrap(); + r.push_callsign(RouteIdentity::new("C3", 0, None, true)) + .unwrap(); assert_eq!( AppendNodeError::SetFuture, - r.append_node("C3", 1).unwrap_err() + r.append_node("C3", 1, None).unwrap_err() ); assert_eq!( AppendNodeError::SetFuture, - r.append_node("C4", 0).unwrap_err() + r.append_node("C4", 0, None).unwrap_err() ); } #[test] fn append_fails_when_would_exceed_max_hops() { let mut r = Route::new(3); - r.append_node("C1", 0).unwrap(); - r.append_node("C2", 0).unwrap(); - r.append_node("C2", 1).unwrap(); + r.append_node("C1", 0, None).unwrap(); + r.append_node("C2", 0, None).unwrap(); + r.append_node("C2", 1, None).unwrap(); assert_eq!( AppendNodeError::HopsOverflow, - r.append_node("C4", 0).unwrap_err() + r.append_node("C4", 0, None).unwrap_err() ); } #[test] fn append_fails_when_already_in_route() { let mut r = Route::new(3); - r.append_node("C1", 0).unwrap(); + r.append_node("C1", 0, None).unwrap(); assert_eq!( AppendNodeError::DuplicateNode, - r.append_node("C1", 0).unwrap_err() + r.append_node("C1", 0, None).unwrap_err() ); } #[test] fn append_overwrites_future() { let mut r = Route::new(5); - r.push_callsign("C1", 0, false); - r.push_callsign("C2", 0, false); - r.push_callsign("C3", 0, true); - r.push_callsign("C4", 0, true); + r.push_callsign(RouteIdentity::new("C1", 0, None, false)) + .unwrap(); + r.push_callsign(RouteIdentity::new("C2", 0, None, false)) + .unwrap(); + r.push_callsign(RouteIdentity::new("C3", 0, None, true)) + .unwrap(); + r.push_callsign(RouteIdentity::new("C4", 0, None, true)) + .unwrap(); - r.append_node("C3", 0).unwrap(); + r.append_node("C3", 0, None).unwrap(); let mut iter = r.iter(); - assert_eq!(Some(RouteNode::Identity("C1", 0, false)), iter.next()); - assert_eq!(Some(RouteNode::Identity("C2", 0, false)), iter.next()); - assert_eq!(Some(RouteNode::Identity("C3", 0, false)), iter.next()); - assert_eq!(Some(RouteNode::Identity("C4", 0, true)), iter.next()); + assert_eq!( + Some(RouteNode::Identity(RouteIdentity::new( + "C1", 0, None, false + ))), + iter.next() + ); + assert_eq!( + Some(RouteNode::Identity(RouteIdentity::new( + "C2", 0, None, false + ))), + iter.next() + ); + assert_eq!( + Some(RouteNode::Identity(RouteIdentity::new( + "C3", 0, None, false + ))), + iter.next() + ); + assert_eq!( + Some(RouteNode::Identity(RouteIdentity::new("C4", 0, None, true))), + iter.next() + ); assert_eq!(None, iter.next()); } #[test] fn append_overwrites_future_even_when_at_hop_limit() { let mut r = Route::new(4); - r.push_callsign("C1", 0, false); - r.push_callsign("C2", 0, false); - r.push_callsign("C3", 0, true); - r.push_callsign("C4", 0, true); + r.push_callsign(RouteIdentity::new("C1", 0, None, false)) + .unwrap(); + r.push_callsign(RouteIdentity::new("C2", 0, None, false)) + .unwrap(); + r.push_callsign(RouteIdentity::new("C3", 0, None, true)) + .unwrap(); + r.push_callsign(RouteIdentity::new("C4", 0, None, true)) + .unwrap(); - r.append_node("C3", 0).unwrap(); + r.append_node("C3", 0, None).unwrap(); let mut iter = r.iter(); - assert_eq!(Some(RouteNode::Identity("C1", 0, false)), iter.next()); - assert_eq!(Some(RouteNode::Identity("C2", 0, false)), iter.next()); - assert_eq!(Some(RouteNode::Identity("C3", 0, false)), iter.next()); - assert_eq!(Some(RouteNode::Identity("C4", 0, true)), iter.next()); + assert_eq!( + Some(RouteNode::Identity(RouteIdentity::new( + "C1", 0, None, false + ))), + iter.next() + ); + assert_eq!( + Some(RouteNode::Identity(RouteIdentity::new( + "C2", 0, None, false + ))), + iter.next() + ); + assert_eq!( + Some(RouteNode::Identity(RouteIdentity::new( + "C3", 0, None, false + ))), + iter.next() + ); + assert_eq!( + Some(RouteNode::Identity(RouteIdentity::new("C4", 0, None, true))), + iter.next() + ); assert_eq!(None, iter.next()); } #[test] fn append_appends_when_no_future() { let mut r = Route::new(5); - r.push_callsign("C1", 0, false); - r.push_callsign("C2", 0, false); - r.push_callsign("C3", 0, false); + r.push_callsign(RouteIdentity::new("C1", 0, None, false)) + .unwrap(); + r.push_callsign(RouteIdentity::new("C2", 0, None, false)) + .unwrap(); + r.push_callsign(RouteIdentity::new("C3", 0, None, false)) + .unwrap(); - r.append_node("C4", 0).unwrap(); + r.append_node("C4", 0, None).unwrap(); let mut iter = r.iter(); - assert_eq!(Some(RouteNode::Identity("C1", 0, false)), iter.next()); - assert_eq!(Some(RouteNode::Identity("C2", 0, false)), iter.next()); - assert_eq!(Some(RouteNode::Identity("C3", 0, false)), iter.next()); - assert_eq!(Some(RouteNode::Identity("C4", 0, false)), iter.next()); + assert_eq!( + Some(RouteNode::Identity(RouteIdentity::new( + "C1", 0, None, false + ))), + iter.next() + ); + assert_eq!( + Some(RouteNode::Identity(RouteIdentity::new( + "C2", 0, None, false + ))), + iter.next() + ); + assert_eq!( + Some(RouteNode::Identity(RouteIdentity::new( + "C3", 0, None, false + ))), + iter.next() + ); + assert_eq!( + Some(RouteNode::Identity(RouteIdentity::new( + "C4", 0, None, false + ))), + iter.next() + ); assert_eq!(None, iter.next()); } }