Select Git revision
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
}