use ham_cats::{buffer::Buffer, identity::Identity, packet::Packet}; use rand::{rngs::SmallRng, Rng, SeedableRng}; 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 systick_monotonic::fugit::Duration; use crate::{app::monotonics::MyMono::now, config::Config, MAX_PACKET_LEN, SYS_TICK_FREQ}; 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, rng: SmallRng, } impl<'a> RadioManager<'a> { pub fn new( spi: Spi<SPI1>, sdn: SdnPin, cs: CsPin, delay: MyDelay, buf: &'a mut [u8; MAX_PACKET_LEN], config: &Config, ) -> 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); let enable_digipeating = config.enable_digipeating; let seed = rand_seed_from_str(&config.callsign) ^ u64::from(config.ssid); let rng = SmallRng::seed_from_u64(seed); Some(Self { radio, buf, enable_digipeating, rng, }) } pub fn sleep(&mut self) { //self.radio.sleep() } pub fn get_temp(&mut self) -> Option<i8> { self.radio .get_temp() .ok() .map(|x| x.clamp(i8::MIN as f32, i8::MAX as f32) as i8) } // 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) { let rssi = data.rssi(); self.handle_packet_rx(led, packet, callsign, ssid, rssi); } } } 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<()> { // ensures we don't tx over a packet, // and adds some random delay so that every node // if offset slightly self.tx_delay(led, ident)?; 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 tx_delay<P: OutputPin, M: Mutex<T = P>>( &mut self, led: &mut M, ident: Option<(&str, u8)>, ) -> Option<()> { loop { let delay_ms = self.rng.gen_range(0..50); let mut rx = false; let thres = now() + Duration::<u64, 1, { SYS_TICK_FREQ }>::millis(delay_ms); while now() < thres { self.tick(led, ident).ok()?; while self.radio.is_busy_rxing().ok()? { rx = true; self.tick(led, ident).ok()?; } if rx { // if we rx'd a packet, we need to draw a new random time and start again break; } } if !rx { // didn't rx a packet, so we're safe to leave break 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, rssi: f64, ) { if packet .should_digipeat(Identity::new(callsign, ssid)) .is_ok() { if packet.append_to_route(callsign, ssid, Some(rssi)).is_err() { 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); } } } } fn rand_seed_from_str(s: &str) -> u64 { let mut out = 0; for (i, &b) in s.as_bytes().iter().enumerate() { let i = i % 4; let b: u64 = b.into(); out ^= b << (i * 8); } out }