use embedded_hal::digital::OutputPin;
use embedded_hal_async::spi::SpiBus;
use crate::constants::*;
use crate::system::DioNum;
pub use super::cmd::cmd_lora::*;
pub use super::cmd::cmd_ranging::*;
use super::{BusyPin, Lr2021, Lr2021Error};
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct LoraModulationParams {
pub sf: Sf,
pub bw: LoraBw,
pub cr: LoraCr,
pub ldro: Ldro,
}
impl LoraModulationParams {
pub fn basic(sf: Sf, bw: LoraBw) -> Self {
let ldro_en = (sf==Sf::Sf12 && !matches!(bw,LoraBw::Bw1000|LoraBw::Bw500))
|| (sf==Sf::Sf11 && !matches!(bw,LoraBw::Bw1000|LoraBw::Bw500|LoraBw::Bw250) );
Self {
sf, bw,
cr: LoraCr::Cr1Ham45Si,
ldro: if ldro_en {Ldro::On} else {Ldro::Off},
}
}
pub fn new(sf: Sf, bw: LoraBw, cr: LoraCr, ldro: Ldro) -> Self {
Self {sf, bw, cr, ldro}
}
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct LoraPacketParams {
pub pbl_len: u16,
pub payload_len: u8,
pub header_type: HeaderType,
pub crc_en: bool,
pub invert_iq: bool,
}
impl LoraPacketParams {
pub fn basic(payload_len: u8, modulation: &LoraModulationParams) -> Self {
Self {
pbl_len: if modulation.sf < Sf::Sf7 {12} else {8},
payload_len,
header_type: HeaderType::Explicit,
crc_en: true,
invert_iq: false
}
}
pub fn new(pbl_len: u16, payload_len: u8, header_type: HeaderType, crc_en: bool, invert_iq: bool) -> Self {
Self {pbl_len, payload_len, header_type, crc_en, invert_iq}
}
}
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct LoraCadParams {
nb_symbols: u8,
pub preamble_only: bool ,
pub exit_mode: ExitMode,
pub timeout: u32,
pub thr: u8,
pub delta: u8,
}
pub fn lora_cad_thr(sf: Sf, nb_symbols: u8) -> u8 {
let base_symb = match nb_symbols {
0 | 1 => 60,
2 => 56,
3 => 52,
_ => 51
};
let offset_sf = match sf {
Sf::Sf5 => 0,
Sf::Sf6 => 1,
Sf::Sf7 => 2,
Sf::Sf8 => 3,
Sf::Sf9 => 5,
Sf::Sf10 => 6,
Sf::Sf11 => 8,
Sf::Sf12 => 10,
};
base_symb + offset_sf
}
impl LoraCadParams {
pub fn new_cad_only(sf: Sf, nb_symbols: u8, fast: bool) -> Self {
let nb_symbols = nb_symbols.clamp(1,15);
let thr = lora_cad_thr(sf, nb_symbols);
LoraCadParams {
nb_symbols,
preamble_only: false,
exit_mode: ExitMode::CadOnly,
timeout: 0,
delta: if fast {8} else {0},
thr
}
}
pub fn new_auto(sf: Sf, nb_symbols: u8, exit_mode: ExitMode, timeout: u32, fast: bool) -> Self {
let nb_symbols = nb_symbols.clamp(1,15);
let thr = lora_cad_thr(sf, nb_symbols);
let delta = if fast {8} else {0};
let preamble_only = exit_mode==ExitMode::CadRx;
LoraCadParams {nb_symbols, preamble_only, exit_mode, timeout, thr, delta}
}
pub fn new(nb_symbols: u8, thr: u8, exit_mode: ExitMode, timeout: u32, delta: u8) -> Self {
let nb_symbols = nb_symbols.clamp(1,15);
let delta = delta&0xF;
let preamble_only = exit_mode==ExitMode::CadRx;
LoraCadParams {nb_symbols, preamble_only, exit_mode, timeout, thr, delta}
}
}
const RANGING_DELAY : [u32; 56] = [
21711, 21729, 21733, 21715, 21669, 21577, 21391, 21016,
25547, 25596, 25599, 25652, 25683, 25765, 25918, 26283,
20616, 20607, 20567, 20480, 20307, 19959, 19258, 17860,
24460, 24548, 24547, 24624, 24936, 25264, 26084, 27689,
20149, 20141, 20100, 20013, 19837, 19486, 18787, 17386,
23975, 24087, 24089, 24191, 24356, 24806, 25560, 27153,
19688, 19649, 19560, 19387, 19043, 18350, 16967, 14191,
];
#[derive(Debug, Clone, Copy)]
pub struct SidedetCfg(u8);
impl SidedetCfg {
pub fn new(sf: Sf, ldro: Ldro, inv: bool) -> Self{
let b = ((sf as u8) << 4) |
(ldro as u8) << 2 |
if inv {1} else {0};
Self(b)
}
pub fn to_byte(&self) -> u8 {
self.0
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct BlankingCfg {
pub snr_thr : u8,
pub thr_gain: u8,
pub symb_gain: u8,
pub rssi_thr: u8,
pub detect : bool,
}
impl BlankingCfg {
pub fn off() -> Self {
Self {
thr_gain: 0,
snr_thr : 0,
symb_gain: 0,
detect : false,
rssi_thr: 0,
}
}
pub fn symbol() -> Self {
Self {
thr_gain: 2,
snr_thr : 8,
symb_gain: 2,
detect : false,
rssi_thr: 0,
}
}
pub fn td_symb() -> Self {
Self {
thr_gain: 2,
snr_thr : 8,
symb_gain: 2,
detect : false,
rssi_thr: 7,
}
}
pub fn full() -> Self {
Self {
thr_gain: 2,
snr_thr : 8,
symb_gain: 2,
detect : true,
rssi_thr: 7,
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct RangingFei {
pub fei1: i32,
pub fei2: i32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TimingSyncPulseWidth {
W1 = 0, W5 = 1, W52 = 2, W520 = 3, W5200 = 4, W52k = 5, W260k = 6, W1024k = 7
}
#[derive(Debug, Clone, Copy, Default)]
pub enum FreqRange {#[default]
Narrow = 0,
Medium = 1,
Wide = 2,
}
impl<O,SPI, M> Lr2021<O,SPI, M> where
O: OutputPin, SPI: SpiBus<u8>, M: BusyPin
{
pub async fn set_lora_modulation(&mut self, params: &LoraModulationParams) -> Result<(), Lr2021Error> {
let req = set_lora_modulation_params_cmd(params.sf, params.bw, params.cr, params.ldro, LoraFilter::Auto);
self.cmd_wr(&req).await
}
pub async fn set_ranging_modulation(&mut self, params: &LoraModulationParams, is_initiator: bool) -> Result<(), Lr2021Error> {
let filter = match (params.bw.is_fractional(),is_initiator) {
(true, true) => LoraFilter::Dcc,
(true, false) => LoraFilter::DccF,
_ => LoraFilter::Auto,
};
let req = set_lora_modulation_params_cmd(params.sf, params.bw, params.cr, params.ldro, filter);
self.cmd_wr(&req).await
}
pub async fn set_lora_packet(&mut self, params: &LoraPacketParams) -> Result<(), Lr2021Error> {
let req = set_lora_packet_params_cmd(params.pbl_len, params.payload_len, params.header_type, params.crc_en, params.invert_iq);
self.cmd_wr(&req).await
}
pub async fn set_lora_syncword(&mut self, syncword: u8) -> Result<(), Lr2021Error> {
let req = set_lora_syncword_cmd(syncword);
self.cmd_wr(&req).await
}
pub async fn set_lora_syncword_ext(&mut self, s1: i8, s2: i8) -> Result<(), Lr2021Error> {
let req = set_lora_syncword_extended_cmd((s1&0x1F) as u8, (s2&0x1F) as u8);
self.cmd_wr(&req).await
}
pub async fn set_lora_synch_timeout(&mut self, timeout: u8, format: TimeoutFormat) -> Result<(), Lr2021Error> {
let req = set_lora_synch_timeout_cmd(timeout, format);
self.cmd_wr(&req).await
}
pub async fn set_lora_address(&mut self, len: AddrLen, pos: u8, addr: u64) -> Result<(), Lr2021Error> {
let req = set_lora_address_cmd(len, pos, addr);
let len = 2 + (len as usize);
self.cmd_wr(&req[..len]).await
}
pub async fn get_lora_packet_status(&mut self) -> Result<LoraPacketStatusRsp, Lr2021Error> {
let req = get_lora_packet_status_req();
let mut rsp = LoraPacketStatusRsp::new();
self.cmd_rd(&req, rsp.as_mut()).await?;
Ok(rsp)
}
pub async fn get_lora_rx_stats(&mut self) -> Result<LoraRxStatsRsp, Lr2021Error> {
let req = get_lora_rx_stats_req();
let mut rsp = LoraRxStatsRsp::new();
self.cmd_rd(&req, rsp.as_mut()).await?;
Ok(rsp)
}
pub async fn set_lora_cad_params(&mut self, params: &LoraCadParams) -> Result<(), Lr2021Error> {
let req = set_lora_cad_params_cmd(params.nb_symbols, params.preamble_only, params.delta, params.exit_mode, params.timeout, params.thr);
self.cmd_wr(&req).await
}
pub async fn set_lora_cad(&mut self) -> Result<(), Lr2021Error> {
let req = set_lora_cad_cmd();
self.cmd_wr(&req).await
}
pub async fn comp_sx127x_sf6_sw(&mut self, en: bool, ret_en: Option<u8>) -> Result<(), Lr2021Error> {
let value = if en {2} else {0};
self.wr_field(ADDR_LORA_PARAM, value, 18, 2).await?;
if let Some(slot) = ret_en {
self.add_register_to_retention(slot,ADDR_LORA_PARAM).await?;
}
Ok(())
}
pub async fn comp_sx127x_hopping(&mut self, en: bool, ret_en: Option<u8>) -> Result<(), Lr2021Error> {
let value = if en {1} else {0};
self.wr_field(ADDR_LORA_TX_CFG1, value, 18, 1).await?;
if let Some(slot) = ret_en {
self.add_register_to_retention(slot,ADDR_LORA_TX_CFG1).await?;
}
Ok(())
}
#[allow(clippy::get_first)]
pub async fn set_lora_sidedet_cfg(&mut self, cfg: &[SidedetCfg]) -> Result<(), Lr2021Error> {
let req = [
0x02, 0x24,
cfg.get(0).map(|c| c.to_byte()).unwrap_or(0),
cfg.get(1).map(|c| c.to_byte()).unwrap_or(0),
cfg.get(2).map(|c| c.to_byte()).unwrap_or(0),
];
let len = cfg.len() + 2;
self.cmd_wr(&req[..len]).await
}
#[allow(clippy::get_first)]
pub async fn set_lora_sidedet_syncword(&mut self, sw: &[u8]) -> Result<(), Lr2021Error> {
let req = [
0x02, 0x25,
sw.get(0).copied().unwrap_or(0x24),
sw.get(1).copied().unwrap_or(0x24),
sw.get(2).copied().unwrap_or(0x24),
];
let len = sw.len() + 2;
self.cmd_wr(&req[..len]).await
}
pub async fn set_lora_freq_range(&mut self, range: FreqRange) -> Result<(), Lr2021Error> {
self.wr_field(ADDR_LORA_RX_CFG, range as u32, 16, 2).await
}
pub async fn set_lora_preamble_modulation(&mut self, en: bool, dram_ret: u8, wakeup_time: u16, min_sleep_time: u32) -> Result<(), Lr2021Error> {
let req = config_lora_preamble_modulation_cmd(en, dram_ret, wakeup_time, min_sleep_time);
self.cmd_wr(&req).await
}
pub async fn set_lora_blanking(&mut self, cfg: BlankingCfg) -> Result<(), Lr2021Error> {
let req = set_lora_blanking_cmd(cfg.thr_gain, cfg.snr_thr, cfg.symb_gain, cfg.detect, cfg.rssi_thr);
self.cmd_wr(&req).await
}
pub async fn set_lora_hopping(&mut self, period: u16, freq_hops: &[u32]) -> Result<(), Lr2021Error> {
let buffer = self.buffer.as_mut();
buffer[0] = 0x02;
buffer[1] = 0x2C;
buffer[2] = if freq_hops.is_empty() {0} else {0x40 | ((period>>8) as u8 & 0x1F)};
buffer[3] = (period & 0xFF) as u8;
for (i, f) in freq_hops.iter().enumerate() {
buffer[4+4*i] = ((f >> 24) & 0xFF) as u8;
buffer[5+4*i] = ((f >> 16) & 0xFF) as u8;
buffer[6+4*i] = ((f >> 8) & 0xFF) as u8;
buffer[7+4*i] = ( f & 0xFF) as u8;
}
let len = 3 + 4*freq_hops.len();
self.cmd_buf_wr(len).await
}
pub async fn patch_ranging_rf(&mut self) -> Result<(), Lr2021Error> {
self.wr_reg_mask(ADDR_FREQ_RF, 0x7F, 0).await
}
pub async fn set_ranging_dev_addr(&mut self, addr: u32, length: Option<CheckLength>) -> Result<(), Lr2021Error> {
let req = set_ranging_addr_cmd(addr, length.unwrap_or(CheckLength::Addr32b));
self.cmd_wr(&req).await
}
pub async fn set_ranging_req_addr(&mut self, addr: u32) -> Result<(), Lr2021Error> {
let req = set_ranging_req_addr_cmd(addr);
self.cmd_wr(&req).await
}
pub async fn set_ranging_txrx_delay(&mut self, delay: u32) -> Result<(), Lr2021Error> {
let req = set_ranging_tx_rx_delay_cmd(delay);
self.cmd_wr(&req).await
}
pub fn get_ranging_base_delay(&self, modulation: &LoraModulationParams) -> u32 {
let offset = match modulation.bw {
LoraBw::Bw1000 => 0,
LoraBw::Bw812 => 8,
LoraBw::Bw500 => 16,
LoraBw::Bw406 => 24,
LoraBw::Bw250 => 32,
LoraBw::Bw203 => 40,
LoraBw::Bw125 => 48,
_ => 56
};
let idx = offset + (modulation.sf as usize - 5);
RANGING_DELAY.get(idx).copied().unwrap_or(18000 - (5600 >> (12 - modulation.sf as u32)))
}
pub async fn set_ranging_params(&mut self, extended: bool, spy_mode: bool, nb_symbols: u8) -> Result<(), Lr2021Error> {
let req = set_ranging_params_cmd(extended, spy_mode, nb_symbols);
self.cmd_wr(&req).await?;
if extended {
self.wr_field(ADDR_LORA_RANGING_EXTRA, 0, 24, 3).await?;
}
Ok(())
}
pub async fn get_ranging_result(&mut self) -> Result<RangingResultRsp, Lr2021Error> {
let req = get_ranging_result_req(RangingResKind::LatestRaw);
let mut rsp = RangingResultRsp::new();
self.cmd_rd(&req, rsp.as_mut()).await?;
Ok(rsp)
}
pub async fn get_ranging_ext_result(&mut self) -> Result<RangingExtResultRsp, Lr2021Error> {
let req = get_ranging_result_req(RangingResKind::ExtendedRaw);
let mut rsp = RangingExtResultRsp::new();
self.cmd_rd(&req, rsp.as_mut()).await?;
Ok(rsp)
}
pub async fn get_ranging_gain(&mut self) -> Result<RangingGainStepRsp, Lr2021Error> {
let req = get_ranging_result_req(RangingResKind::GainSteps);
let mut rsp = RangingGainStepRsp::new();
self.cmd_rd(&req, rsp.as_mut()).await?;
Ok(rsp)
}
pub async fn get_ranging_stats(&mut self) -> Result<RangingStatsRsp, Lr2021Error> {
let req = get_ranging_stats_req();
let mut rsp = RangingStatsRsp::new();
self.cmd_rd(&req, rsp.as_mut()).await?;
Ok(rsp)
}
pub async fn get_ranging_rssi_offset(&mut self) -> Result<i16, Lr2021Error> {
let gmax = (self.rd_reg(0xF301A4).await? & 0x3FF) as i16; let pwr_offset = (self.rd_reg(0xF30128).await? >> 6) & 0x3F;
let pwr_offset = pwr_offset as i16 - if (pwr_offset&0x20) !=0 {64} else {0}; let offset = 104 + ((gmax + 2*pwr_offset + 2) >> 2);
Ok(-offset)
}
pub async fn set_lora_timing_sync(&mut self, mode: TimingSyncMode, dio_num: DioNum) -> Result<(), Lr2021Error> {
let req = set_lora_tx_sync_cmd(mode, dio_num);
self.cmd_wr(&req).await
}
pub async fn set_lora_timing_sync_pulse(&mut self, delay: u32, width: TimingSyncPulseWidth) -> Result<(), Lr2021Error> {
let value = ((width as u32) << 29) | (delay & 0x7FF_FFFF) | 0x0800_0000;
self.wr_reg(ADDR_LORA_TIMING_SYNC, value).await
}
}