use radio::{State as _, Channel as _, Power as _, Interrupts as _};
use crate::{Sx127x, Error};
use crate::base::Base as Sx127xBase;
use crate::device::{self, State, Modem, regs};
use crate::device::lora::*;
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct LoRaConfig {
pub channel: Channel,
pub preamble_len: u16,
pub symbol_timeout: u16,
pub payload_len: PayloadLength,
pub payload_crc: PayloadCrc,
pub frequency_hop: FrequencyHopping,
pub pa_output: PaSelect,
pub power: i8,
pub invert_iq: bool,
}
impl Default for LoRaConfig {
fn default() -> Self {
LoRaConfig {
channel: Channel::default(),
preamble_len: 0x8,
symbol_timeout: 0x64,
payload_len: PayloadLength::Variable,
payload_crc: PayloadCrc::Enabled,
frequency_hop: FrequencyHopping::Disabled,
pa_output: PaSelect::Boost,
power: 10,
invert_iq: false,
}
}
}
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Channel {
pub frequency: u32,
pub bandwidth: Bandwidth,
pub sf: SpreadingFactor,
pub coderate: Coderate,
}
impl Default for Channel {
fn default() -> Self {
Channel {
frequency: 434e6 as u32,
bandwidth: Bandwidth::Bandwidth125kHz,
sf: SpreadingFactor::Sf7,
coderate: Coderate::CodingRate1,
}
}
}
#[derive(Clone, PartialEq, Debug)]
pub struct Info {
pub rssi: i16,
pub snr: i16,
}
impl Default for Info {
fn default() -> Self {
Info {
rssi: 0,
snr: 0,
}
}
}
impl<Base, CommsError, PinError> Sx127x<Base, CommsError, PinError, LoRaConfig>
where
Base: Sx127xBase<CommsError, PinError>,
{
pub fn configure(&mut self, config: &LoRaConfig) -> Result<(), Error<CommsError, PinError>> {
debug!("Configuring lora mode");
self.set_state(State::Sleep)?;
self.set_modem(Modem::LoRa)?;
self.set_channel(&config.channel)?;
self.write_reg(regs::LoRa::SYMBTIMEOUTLSB, (config.symbol_timeout & 0xFF) as u8 )?;
self.write_reg(regs::LoRa::PREAMBLEMSB, (config.preamble_len >> 8) as u8 )?;
self.write_reg(regs::LoRa::PREAMBLELSB, (config.preamble_len & 0xFF) as u8 )?;
if let PayloadLength::Constant(len) = config.payload_len {
debug!("Using constant length mode with length: {}", len);
self.write_reg(regs::LoRa::PAYLOADLENGTH, len as u8 )?;
}
if let FrequencyHopping::Enabled(symbol_time) = config.frequency_hop {
debug!("Enabling frequency hopping with symbol time: {}", symbol_time);
self.update_reg(regs::Common::PLLHOP, PLLHOP_FASTHOP_MASK, PLLHOP_FASTHOP_ON)?;
}
match config.pa_output {
PaSelect::Rfo(max) => {
self.update_reg(regs::Common::PACONFIG,
PASELECT_MASK | MAXPOWER_MASK,
PASELECT_RFO | ((max << MAXPOWER_SHIFT) & MAXPOWER_MASK)
)?;
},
PaSelect::Boost => {
self.update_reg(regs::Common::PACONFIG, PASELECT_MASK, PASELECT_PA_BOOST)?;
}
}
self.set_power(config.power)?;
self.config = *config;
Ok(())
}
fn process_rssi_snr(&self, rssi: i16, snr: i16) -> (i16, i16) {
let snr = if snr & 0x80 != 0 {
-((( snr + 1 ) & 0xFF) >> 2)
} else {
(snr & 0xFF) >> 2
};
let rssi = if snr < 0 {
if self.config.channel.frequency > RF_MID_BAND_THRESH {
device::RSSI_OFFSET_HF + rssi + (rssi >> 4) + snr
} else {
device::RSSI_OFFSET_LF + rssi + (rssi >> 4) + snr
}
} else {
if self.config.channel.frequency > RF_MID_BAND_THRESH {
device::RSSI_OFFSET_HF + rssi + (rssi >> 4)
} else {
device::RSSI_OFFSET_LF + rssi + (rssi >> 4) + snr
}
};
(rssi, snr)
}
}
impl<Base, CommsError, PinError> radio::Interrupts for Sx127x<Base, CommsError, PinError, LoRaConfig>
where
Base: Sx127xBase<CommsError, PinError>,
{
type Irq = Irq;
type Error = Error<CommsError, PinError>;
fn get_interrupts(&mut self, clear: bool) -> Result<Self::Irq, Self::Error> {
let reg = self.read_reg(regs::LoRa::IRQFLAGS)?;
let irq = Irq::from_bits(reg).unwrap();
if clear {
self.write_reg(regs::LoRa::IRQFLAGS, reg)?;
}
Ok(irq)
}
}
impl<Base, CommsError, PinError> radio::Power for Sx127x<Base, CommsError, PinError, LoRaConfig>
where
Base: Sx127xBase<CommsError, PinError>,
{
type Error = Error<CommsError, PinError>;
fn set_power(&mut self, power: i8) -> Result<(), Error<CommsError, PinError>> {
let power = core::cmp::max(power, 0);
let config = self.read_reg(regs::Common::PACONFIG)?;
match config & PASELECT_MASK {
PASELECT_RFO => {
let max = ((config & MAXPOWER_MASK) >> MAXPOWER_SHIFT) as i8;
let power = core::cmp::min(power, 17i8);
let value = power - max + 15;
let v = self.update_reg(regs::Common::PACONFIG, OUTPUTPOWER_MASK, value as u8)?;
debug!("Updated RFO PA_CONFIG for: {} dBm to: {:b}", power, v);
},
PASELECT_PA_BOOST => {
let power = core::cmp::min(power, 20i8);
let value = power - 17 + 15;
self.update_reg(regs::Common::PACONFIG, OUTPUTPOWER_MASK, value as u8)?;
let pa_dac_enable = if power > 17 { PADAC_20DBM_ON } else { PADAC_20DBM_OFF };
let v = self.update_reg(regs::Common::PADAC, PADAC_MASK, pa_dac_enable)?;
debug!("Updated BOOST PA_CONFIG for: {} dBm to: {:b}", power, v);
},
_ => ()
}
Ok(())
}
}
impl<Base, CommsError, PinError> radio::Channel for Sx127x<Base, CommsError, PinError, LoRaConfig>
where
Base: Sx127xBase<CommsError, PinError>,
{
type Channel = Channel;
type Error = Error<CommsError, PinError>;
fn set_channel(&mut self, channel: &Channel) -> Result<(), Error<CommsError, PinError>> {
use device::lora::{SpreadingFactor::*, Bandwidth::*};
self.set_frequency(channel.frequency)?;
let low_dr_optimise = if ((channel.bandwidth as u8) < (Bandwidth125kHz as u8))
|| (channel.bandwidth as u8 == Bandwidth125kHz as u8 && (channel.sf == Sf11 || channel.sf == Sf12))
|| (channel.bandwidth as u8 == Bandwidth250kHz as u8 && channel.sf == Sf12) {
debug!("Using low data rate optimization");
LowDatarateOptimise::Enabled
} else {
LowDatarateOptimise::Disabled
};
let implicit_header = match self.config.payload_len {
PayloadLength::Variable => IMPLICITHEADER_DISABLE,
PayloadLength::Constant(_len) => IMPLICITHEADER_ENABLE,
};
self.update_reg(regs::LoRa::MODEMCONFIG1,
BANDWIDTH_MASK | CODERATE_MASK | IMPLICITHEADER_MASK,
channel.bandwidth as u8 | channel.coderate as u8 | implicit_header)?;
let symbol_timeout_msb = (self.config.symbol_timeout >> 8) & 0b0011;
let payload_crc = self.config.payload_crc;
self.update_reg(regs::LoRa::MODEMCONFIG2,
SPREADING_FACTOR_MASK | RXPAYLOADCRC_MASK | SYMBTIMEOUTMSB_MASK,
channel.sf as u8 | payload_crc as u8 | symbol_timeout_msb as u8 )?;
self.update_reg(regs::LoRa::MODEMCONFIG3,
LOWDATARATEOPTIMIZE_MASK,
low_dr_optimise as u8 )?;
if channel.bandwidth == Bandwidth500kHz {
if channel.frequency > RF_MID_BAND_THRESH {
self.write_reg(regs::LoRa::TEST36, 0x02)?;
self.write_reg(regs::LoRa::TEST3A, 0x64)?;
} else {
self.write_reg(regs::LoRa::TEST36, 0x02)?;
self.write_reg(regs::LoRa::TEST3A, 0x7F)?;
}
} else {
self.write_reg(regs::LoRa::TEST36, 0x03)?;
}
if channel.sf == Sf6 {
self.update_reg(regs::LoRa::DETECTOPTIMIZE, DETECTIONOPTIMIZE_MASK, DetectionOptimize::Sf6 as u8)?;
} else {
self.update_reg(regs::LoRa::DETECTOPTIMIZE, DETECTIONOPTIMIZE_MASK, DetectionOptimize::Sf7To12 as u8)?;
}
self.config.channel = *channel;
Ok(())
}
}
impl<Base, CommsError, PinError> radio::Transmit for Sx127x<Base, CommsError, PinError, LoRaConfig>
where
Base: Sx127xBase<CommsError, PinError>,
{
type Error = Error<CommsError, PinError>;
fn start_transmit(&mut self, data: &[u8]) -> Result<(), Self::Error> {
debug!("Starting send (data: {:?})", data);
assert!(data.len() < 255);
self.set_state_checked(State::Standby)?;
#[cfg(feature="nope")]
{
if self.config.invert_iq {
self.update_reg(regs::LoRa::INVERTIQ, INVERTIQ_TX_MASK | INVERTIQ_RX_MASK, INVERTIQ_RX_OFF | INVERTIQ_TX_ON)?;
self.write_reg(regs::LoRa::INVERTIQ2, INVERTIQ2_ON)?;
} else {
self.update_reg(regs::LoRa::INVERTIQ, INVERTIQ_TX_MASK | INVERTIQ_RX_MASK, INVERTIQ_RX_OFF | INVERTIQ_TX_OFF)?;
self.write_reg(regs::LoRa::INVERTIQ2, INVERTIQ2_OFF)?;
}
}
self.write_reg(regs::LoRa::FIFOTXBASEADDR, 0x00)?;
self.write_reg(regs::LoRa::FIFOADDRPTR, 0x00)?;
self.hal.write_buff(data)?;
self.write_reg(regs::LoRa::PAYLOADLENGTH, data.len() as u8)?;
self.set_state_checked(State::Tx)?;
Ok(())
}
fn check_transmit(&mut self) -> Result<bool, Error<CommsError, PinError>> {
let irq = self.get_interrupts(true)?;
debug!("Poll check send, irq: {:?}", irq);
if irq.contains(Irq::TX_DONE) {
debug!("Send complete!");
Ok(true)
} else {
debug!("Send pending");
Ok(false)
}
}
}
impl<Base, CommsError, PinError> radio::Receive for Sx127x<Base, CommsError, PinError, LoRaConfig>
where
Base: Sx127xBase<CommsError, PinError>,
{
type Info = Info;
type Error = Error<CommsError, PinError>;
fn start_receive(&mut self) -> Result<(), Self::Error> {
use device::lora::Bandwidth::*;
debug!("Starting receive");
self.set_state_checked(State::Standby)?;
#[cfg(feature="nope")]
{
if self.config.invert_iq {
self.update_reg(regs::LoRa::INVERTIQ, INVERTIQ_TX_MASK | INVERTIQ_RX_MASK, INVERTIQ_RX_ON | INVERTIQ_TX_OFF)?;
self.write_reg(regs::LoRa::INVERTIQ2, INVERTIQ2_ON)?;
} else {
self.update_reg(regs::LoRa::INVERTIQ, INVERTIQ_TX_MASK | INVERTIQ_RX_MASK, INVERTIQ_RX_OFF | INVERTIQ_TX_OFF)?;
self.write_reg(regs::LoRa::INVERTIQ2, INVERTIQ2_OFF)?;
}
}
match self.config.channel.bandwidth {
Bandwidth125kHz => {
self.write_reg(regs::LoRa::TEST2F, 0x40)?;
},
Bandwidth250kHz => {
self.write_reg(regs::LoRa::TEST2F, 0x40)?;
},
_ => {
self.update_reg(regs::LoRa::DETECTOPTIMIZE, AUTOMATICIF_MASK, AUTOMATICIF_ON)?;
}
}
self.write_reg(regs::LoRa::FIFORXBASEADDR, 0x00)?;
self.write_reg(regs::LoRa::FIFOADDRPTR, 0x00)?;
self.write_reg(regs::LoRa::PAYLOADMAXLENGTH, 254)?;
self.set_state_checked(State::Rx)?;
Ok(())
}
fn check_receive(&mut self, restart: bool) -> Result<bool, Self::Error> {
let irq = self.get_interrupts(true)?;
let mut res = Ok(false);
if !irq.is_empty() {
debug!("Poll check receive, irq: {:?}", irq);
}
if irq.contains(Irq::RX_DONE) {
debug!("RX complete");
res = Ok(true);
} else if irq.contains(Irq::CRC_ERROR) {
debug!("RX CRC error");
res = Err(Error::Crc);
} else if irq.contains(Irq::RX_TIMEOUT) {
debug!("RX timeout");
res = Err(Error::Timeout);
} else {
trace!("RX poll");
}
match (restart, res) {
(true, Err(_)) => {
debug!("RX restarting");
self.start_receive()?;
Ok(false)
},
(_, r) => r
}
}
fn get_received(&mut self, info: &mut Self::Info, data: &mut[u8]) -> Result<usize, Self::Error> {
let n = self.read_reg(regs::LoRa::RXNBBYTES)? as usize;
let r = self.read_reg(regs::LoRa::FIFORXCURRENTADDR)?;
let rssi = self.read_reg(regs::LoRa::PKTRSSIVALUE)? as i16;
let snr = self.read_reg(regs::LoRa::PKTSNRVALUE)? as i16;
let (rssi, snr) = self.process_rssi_snr(rssi, snr);
info.rssi = rssi;
info.snr = snr;
debug!("FIFO RX {} bytes with fifo rx ptr: {}", n, r);
self.write_reg(regs::LoRa::FIFOADDRPTR, r)?;
self.hal.read_buff(&mut data[0..n])?;
debug!("Read data: {:?}", &data[0..n]);
Ok(n)
}
}
impl<Base, CommsError, PinError> radio::Rssi for Sx127x<Base, CommsError, PinError, LoRaConfig>
where
Base: Sx127xBase<CommsError, PinError>,
{
type Error = Error<CommsError, PinError>;
fn poll_rssi(&mut self) -> Result<i16, Error<CommsError, PinError>> {
let raw = self.read_reg(regs::LoRa::RSSIVALUE)?;
let rssi = match false {
true => raw as i16 - 157,
false => raw as i16 - 164,
};
Ok(rssi)
}
}