use ham_cats::{ buffer::Buffer, packet::Packet, whisker::{Route, RouteNode}, }; use rf4463::{config::RADIO_CONFIG_CATS, error::TransferError, Rf4463}; use rtic::Mutex; use stm32f4xx_hal::{ gpio, hal::digital::v2::OutputPin, pac::{SPI1, TIM5}, spi::{self, Spi}, timer::Delay, }; use crate::MAX_PACKET_LEN; type SdnPin = gpio::Pin<'B', 12, gpio::Output>; type CsPin = gpio::Pin<'A', 8, gpio::Output>; type MyDelay = Delay<TIM5, 1000000>; type Radio = Rf4463<Spi<SPI1>, SdnPin, CsPin, MyDelay>; pub struct RadioManager<'a> { radio: Radio, buf: &'a mut [u8; MAX_PACKET_LEN], enable_digipeating: bool, } impl<'a> RadioManager<'a> { pub fn new( spi: Spi<SPI1>, sdn: SdnPin, cs: CsPin, delay: MyDelay, buf: &'a mut [u8; MAX_PACKET_LEN], enable_digipeating: bool, ) -> Option<Self> { let mut radio = Rf4463::new(spi, sdn, cs, delay, &mut RADIO_CONFIG_CATS.clone()).ok()?; // sets us up for the default CATS frequency, 430.500 MHz radio.set_channel(20); radio.start_rx(None, false).ok()?; Some(Self { radio, buf, enable_digipeating, }) } // call me every 20-ish ms // technically needs to be every 100ms, tops // digipeats only if ident is Some, // otherwise the packet is discarded pub fn tick<P: OutputPin, M: Mutex<T = P>>( &mut self, led: &mut M, ident: Option<(&str, u8)>, ) -> Result<(), TransferError<spi::Error>> { if self.radio.is_idle() { self.radio .start_rx(None, false) .map_err(TransferError::SpiError)?; } self.radio.interrupt(Some(self.buf), None)?; if let Some(data) = self .radio .finish_rx(self.buf) .map_err(TransferError::SpiError)? { if self.enable_digipeating { if let Some((callsign, ssid)) = ident { let mut buf = [0; MAX_PACKET_LEN]; if let Ok(packet) = Packet::fully_decode(data.data(), &mut buf) { self.handle_packet_rx(led, packet, callsign, ssid); } } } self.radio .start_rx(None, false) .map_err(TransferError::SpiError)?; } Ok(()) } // digipeats only if ident is Some, // otherwise the rx'd packet is discarded pub fn tx<P: OutputPin, M: Mutex<T = P>>( &mut self, led: &mut M, data: &[u8], ident: Option<(&str, u8)>, ) -> Option<()> { // don't want to transmit on top of a packet while self.radio.is_busy_rxing().ok()? { self.tick(led, ident).ok()?; } led.lock(|l| l.set_high().ok()); self.radio.start_tx(data).ok()?; while !self.radio.is_idle() { self.radio.interrupt(None, Some(data)).ok(); } led.lock(|l| l.set_low().ok()); Some(()) } fn handle_packet_rx<P: OutputPin, M: Mutex<T = P>>( &mut self, led: &mut M, mut packet: Packet<MAX_PACKET_LEN>, callsign: &str, ssid: u8, ) { if should_digipeat(callsign, ssid, &packet) { if append_to_packet_route(callsign, ssid, &mut packet).is_none() { return; } let mut buf = [0; MAX_PACKET_LEN]; let mut buf = Buffer::new_empty(&mut buf); if packet.fully_encode(&mut buf).is_ok() { self.tx(led, &buf, None); } } } } pub fn should_digipeat(callsign: &str, ssid: u8, packet: &Packet<MAX_PACKET_LEN>) -> bool { let route = match packet.route() { Some(x) => x, None => { return false; } }; if let Some(ident) = packet.identification() { if &ident.callsign == callsign && ident.ssid == ssid { // this node is the source of the packet return false; } } let max_hops: usize = route.max_hops.into(); let cur_hops = route .iter() .filter(|r| match r { RouteNode::Internet => false, RouteNode::Identity(_, _, is_future) => !is_future, }) .count(); if max_hops <= cur_hops { // already hit our max hops return false; } let already_digipeated = route.iter().any(|r| match r { RouteNode::Internet => false, RouteNode::Identity(rc, rs, is_future) => rc == callsign && rs == ssid && !is_future, }); if already_digipeated { // this node has already digipeated this packet return false; } let next_node = route.iter().find_map(|r| match r { RouteNode::Identity(c, s, is_future) if is_future => Some((c, s)), _ => None, }); match next_node { Some((rc, rs)) => rc == callsign && rs == ssid, None => true, } } #[must_use] pub fn append_to_packet_route( callsign: &str, ssid: u8, packet: &mut Packet<MAX_PACKET_LEN>, ) -> Option<()> { let mut route = packet.route().unwrap_or(Route::new(0)); append_to_route(callsign, ssid, &mut route)?; packet.clear_route(); packet.add_route(route).ok()?; Some(()) } #[must_use] fn append_to_route(callsign: &str, ssid: u8, r: &mut Route) -> Option<()> { let replace_future = r.iter().any(|rn| match rn { RouteNode::Identity(rc, rs, is_future) => rc == callsign && rs == ssid && is_future, _ => false, }); if replace_future { let mut new_route = Route::new(r.max_hops); let mut already_replaced = false; for rn in r.iter() { match rn { RouteNode::Identity(rc, rs, is_future) if rc == callsign && rs == ssid && is_future && !already_replaced => { already_replaced = true; new_route.push_callsign(callsign, ssid, false)?; } RouteNode::Identity(rc, rs, is_future) => { new_route.push_callsign(rc, rs, is_future)?; } RouteNode::Internet => { new_route.push_internet()?; } } } *r = new_route; } else { r.push_callsign(callsign, ssid, false)?; } Some(()) }