use embedded_hal::digital::OutputPin;
use embedded_hal_async::spi::SpiBus;
pub use super::cmd::cmd_lora::*;
pub use super::cmd::cmd_regmem::*;
use super::{BusyPin, Lr1120, Lr1120Error};
#[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 exit_mode: ExitMode,
pub timeout: u32,
pub thr: 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) -> Self {
let nb_symbols = nb_symbols.clamp(1,15);
let thr = lora_cad_thr(sf, nb_symbols);
LoraCadParams {
nb_symbols,
exit_mode: ExitMode::CadOnly,
timeout: 0,
thr
}
}
pub fn new_auto(sf: Sf, nb_symbols: u8, exit_mode: ExitMode, timeout: u32) -> Self {
let nb_symbols = nb_symbols.clamp(1,15);
let thr = lora_cad_thr(sf, nb_symbols);
LoraCadParams {nb_symbols,exit_mode,timeout,thr}
}
pub fn new(nb_symbols: u8, thr: u8, exit_mode: ExitMode, timeout: u32) -> Self {
let nb_symbols = nb_symbols.clamp(1,15);
LoraCadParams {nb_symbols,exit_mode,timeout,thr}
}
}
const RANGING_DELAY : [u32; 24] = [
19115, 19113, 19121, 19127, 19141, 19178, 19242, 19370,
20265, 20266, 20279, 20292, 20236, 20305, 20433, 20689,
20154, 20268, 20298, 20319, 20323, 20314, 20570, 21082,
];
#[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
}
impl<O,SPI, M> Lr1120<O,SPI, M> where
O: OutputPin, SPI: SpiBus<u8>, M: BusyPin
{
pub async fn set_lora_modulation(&mut self, params: &LoraModulationParams) -> Result<(), Lr1120Error> {
let req = set_lora_modulation_params_cmd(params.sf, params.bw, params.cr, params.ldro);
self.cmd_wr(&req).await
}
pub async fn set_lora_packet(&mut self, params: &LoraPacketParams) -> Result<(), Lr1120Error> {
let req = set_lora_packet_params_cmd(params.pbl_len, params.header_type, params.payload_len, params.crc_en, params.invert_iq);
self.cmd_wr(&req).await
}
pub async fn set_lora_syncword(&mut self, syncword: u8) -> Result<(), Lr1120Error> {
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<(), Lr1120Error> {
let reg_val = ((s1&0x1F) as u32) | (((s2&0x1F) as u32) << 8);
let req = write_reg_mem_mask32_cmd(0xF20460, 0x1FFF, reg_val);
self.cmd_wr(&req).await
}
pub async fn set_lora_synch_timeout(&mut self, timeout: u8) -> Result<(), Lr1120Error> {
let req = set_lora_synch_timeout_cmd(timeout);
self.cmd_wr(&req).await
}
pub async fn get_lora_rx_header_info(&mut self) -> Result<LoraRxHeaderInfosRsp, Lr1120Error> {
let req = get_lora_rx_header_infos_req();
let mut rsp = LoraRxHeaderInfosRsp::new();
self.cmd_rd(&req, rsp.as_mut()).await?;
Ok(rsp)
}
pub async fn get_lora_packet_status(&mut self) -> Result<LoraPacketStatusRsp, Lr1120Error> {
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 set_lora_cad_params(&mut self, params: LoraCadParams) -> Result<(), Lr1120Error> {
let req = set_lora_cad_params_cmd(params.nb_symbols, params.thr, 10, params.exit_mode, params.timeout);
self.cmd_wr(&req).await
}
pub async fn set_lora_cad(&mut self) -> Result<(), Lr1120Error> {
let req = set_lora_cad_cmd();
self.cmd_wr(&req).await
}
pub async fn comp_sx127x_sf6(&mut self, en: bool) -> Result<(), Lr1120Error> {
let req = write_reg_mem_mask32_cmd(0xF20414, 0x00040000, en as u32);
self.cmd_wr(&req).await
}
#[allow(clippy::get_first)]
pub async fn set_lora_sidedet_cfg(&mut self, cfg: &[SidedetCfg]) -> Result<(), Lr1120Error> {
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<(), Lr1120Error> {
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_ranging_dev_addr(&mut self, addr: u32, length: Option<CheckLength>) -> Result<(), Lr1120Error> {
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<(), Lr1120Error> {
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<(), Lr1120Error> {
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::Bw500 => 0,
LoraBw::Bw250 => 8,
LoraBw::Bw125 => 16,
_ => 32,
};
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, nb_symbols: u8) -> Result<(), Lr1120Error> {
let req = set_ranging_parameter_cmd(0, nb_symbols);
self.cmd_wr(&req).await
}
pub async fn get_ranging_result(&mut self) -> Result<RangingResultRsp, Lr1120Error> {
let req = get_ranging_result_req(RangingResKind::Distance);
let mut rsp = RangingResultRsp::new();
self.cmd_rd(&req, rsp.as_mut()).await?;
Ok(rsp)
}
pub async fn get_ranging_rssi(&mut self) -> Result<RangingRssiRsp, Lr1120Error> {
let req = get_ranging_result_req(RangingResKind::Rssi);
let mut rsp = RangingRssiRsp::new();
self.cmd_rd(&req, rsp.as_mut()).await?;
Ok(rsp)
}
}