Skip to content
Snippets Groups Projects
Select Git revision
  • 2736d91798c02c50bdf6f58a8e954cf3ca30cc61
  • master default protected
  • pi-builds
  • sensors
4 results

radio.rs

Blame
  • radio.rs 5.27 KiB
    use ham_cats::{buffer::Buffer, 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);
    
            radio.start_rx(None, false).ok()?;
    
            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 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(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
    }