embassy/embassy-stm32/src/subghz/mod_params.rs

1007 lines
30 KiB
Rust
Raw Normal View History

/// Bandwidth options for [`FskModParams`].
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FskBandwidth {
/// 4.8 kHz double-sideband
Bw4 = 0x1F,
/// 5.8 kHz double-sideband
Bw5 = 0x17,
/// 7.3 kHz double-sideband
Bw7 = 0x0F,
/// 9.7 kHz double-sideband
Bw9 = 0x1E,
/// 11.7 kHz double-sideband
Bw11 = 0x16,
/// 14.6 kHz double-sideband
Bw14 = 0x0E,
/// 19.5 kHz double-sideband
Bw19 = 0x1D,
/// 23.4 kHz double-sideband
Bw23 = 0x15,
/// 29.3 kHz double-sideband
Bw29 = 0x0D,
/// 39.0 kHz double-sideband
Bw39 = 0x1C,
/// 46.9 kHz double-sideband
Bw46 = 0x14,
/// 58.6 kHz double-sideband
Bw58 = 0x0C,
/// 78.2 kHz double-sideband
Bw78 = 0x1B,
/// 93.8 kHz double-sideband
Bw93 = 0x13,
/// 117.3 kHz double-sideband
Bw117 = 0x0B,
/// 156.2 kHz double-sideband
Bw156 = 0x1A,
/// 187.2 kHz double-sideband
Bw187 = 0x12,
/// 234.3 kHz double-sideband
Bw234 = 0x0A,
/// 312.0 kHz double-sideband
Bw312 = 0x19,
/// 373.6 kHz double-sideband
Bw373 = 0x11,
/// 467.0 kHz double-sideband
Bw467 = 0x09,
}
impl FskBandwidth {
/// Get the bandwidth in hertz.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::FskBandwidth;
///
/// assert_eq!(FskBandwidth::Bw4.hertz(), 4_800);
/// assert_eq!(FskBandwidth::Bw5.hertz(), 5_800);
/// assert_eq!(FskBandwidth::Bw7.hertz(), 7_300);
/// assert_eq!(FskBandwidth::Bw9.hertz(), 9_700);
/// assert_eq!(FskBandwidth::Bw11.hertz(), 11_700);
/// assert_eq!(FskBandwidth::Bw14.hertz(), 14_600);
/// assert_eq!(FskBandwidth::Bw19.hertz(), 19_500);
/// assert_eq!(FskBandwidth::Bw23.hertz(), 23_400);
/// assert_eq!(FskBandwidth::Bw29.hertz(), 29_300);
/// assert_eq!(FskBandwidth::Bw39.hertz(), 39_000);
/// assert_eq!(FskBandwidth::Bw46.hertz(), 46_900);
/// assert_eq!(FskBandwidth::Bw58.hertz(), 58_600);
/// assert_eq!(FskBandwidth::Bw78.hertz(), 78_200);
/// assert_eq!(FskBandwidth::Bw93.hertz(), 93_800);
/// assert_eq!(FskBandwidth::Bw117.hertz(), 117_300);
/// assert_eq!(FskBandwidth::Bw156.hertz(), 156_200);
/// assert_eq!(FskBandwidth::Bw187.hertz(), 187_200);
/// assert_eq!(FskBandwidth::Bw234.hertz(), 234_300);
/// assert_eq!(FskBandwidth::Bw312.hertz(), 312_000);
/// assert_eq!(FskBandwidth::Bw373.hertz(), 373_600);
/// assert_eq!(FskBandwidth::Bw467.hertz(), 467_000);
/// ```
pub const fn hertz(&self) -> u32 {
match self {
FskBandwidth::Bw4 => 4_800,
FskBandwidth::Bw5 => 5_800,
FskBandwidth::Bw7 => 7_300,
FskBandwidth::Bw9 => 9_700,
FskBandwidth::Bw11 => 11_700,
FskBandwidth::Bw14 => 14_600,
FskBandwidth::Bw19 => 19_500,
FskBandwidth::Bw23 => 23_400,
FskBandwidth::Bw29 => 29_300,
FskBandwidth::Bw39 => 39_000,
FskBandwidth::Bw46 => 46_900,
FskBandwidth::Bw58 => 58_600,
FskBandwidth::Bw78 => 78_200,
FskBandwidth::Bw93 => 93_800,
FskBandwidth::Bw117 => 117_300,
FskBandwidth::Bw156 => 156_200,
FskBandwidth::Bw187 => 187_200,
FskBandwidth::Bw234 => 234_300,
FskBandwidth::Bw312 => 312_000,
FskBandwidth::Bw373 => 373_600,
FskBandwidth::Bw467 => 467_000,
}
}
/// Convert from a raw bit value.
///
/// Invalid values will be returned in the `Err` variant of the result.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::FskBandwidth;
///
/// assert_eq!(FskBandwidth::from_bits(0x1F), Ok(FskBandwidth::Bw4));
/// assert_eq!(FskBandwidth::from_bits(0x17), Ok(FskBandwidth::Bw5));
/// assert_eq!(FskBandwidth::from_bits(0x0F), Ok(FskBandwidth::Bw7));
/// assert_eq!(FskBandwidth::from_bits(0x1E), Ok(FskBandwidth::Bw9));
/// assert_eq!(FskBandwidth::from_bits(0x16), Ok(FskBandwidth::Bw11));
/// assert_eq!(FskBandwidth::from_bits(0x0E), Ok(FskBandwidth::Bw14));
/// assert_eq!(FskBandwidth::from_bits(0x1D), Ok(FskBandwidth::Bw19));
/// assert_eq!(FskBandwidth::from_bits(0x15), Ok(FskBandwidth::Bw23));
/// assert_eq!(FskBandwidth::from_bits(0x0D), Ok(FskBandwidth::Bw29));
/// assert_eq!(FskBandwidth::from_bits(0x1C), Ok(FskBandwidth::Bw39));
/// assert_eq!(FskBandwidth::from_bits(0x14), Ok(FskBandwidth::Bw46));
/// assert_eq!(FskBandwidth::from_bits(0x0C), Ok(FskBandwidth::Bw58));
/// assert_eq!(FskBandwidth::from_bits(0x1B), Ok(FskBandwidth::Bw78));
/// assert_eq!(FskBandwidth::from_bits(0x13), Ok(FskBandwidth::Bw93));
/// assert_eq!(FskBandwidth::from_bits(0x0B), Ok(FskBandwidth::Bw117));
/// assert_eq!(FskBandwidth::from_bits(0x1A), Ok(FskBandwidth::Bw156));
/// assert_eq!(FskBandwidth::from_bits(0x12), Ok(FskBandwidth::Bw187));
/// assert_eq!(FskBandwidth::from_bits(0x0A), Ok(FskBandwidth::Bw234));
/// assert_eq!(FskBandwidth::from_bits(0x19), Ok(FskBandwidth::Bw312));
/// assert_eq!(FskBandwidth::from_bits(0x11), Ok(FskBandwidth::Bw373));
/// assert_eq!(FskBandwidth::from_bits(0x09), Ok(FskBandwidth::Bw467));
/// assert_eq!(FskBandwidth::from_bits(0x00), Err(0x00));
/// ```
pub const fn from_bits(bits: u8) -> Result<Self, u8> {
match bits {
0x1F => Ok(Self::Bw4),
0x17 => Ok(Self::Bw5),
0x0F => Ok(Self::Bw7),
0x1E => Ok(Self::Bw9),
0x16 => Ok(Self::Bw11),
0x0E => Ok(Self::Bw14),
0x1D => Ok(Self::Bw19),
0x15 => Ok(Self::Bw23),
0x0D => Ok(Self::Bw29),
0x1C => Ok(Self::Bw39),
0x14 => Ok(Self::Bw46),
0x0C => Ok(Self::Bw58),
0x1B => Ok(Self::Bw78),
0x13 => Ok(Self::Bw93),
0x0B => Ok(Self::Bw117),
0x1A => Ok(Self::Bw156),
0x12 => Ok(Self::Bw187),
0x0A => Ok(Self::Bw234),
0x19 => Ok(Self::Bw312),
0x11 => Ok(Self::Bw373),
0x09 => Ok(Self::Bw467),
x => Err(x),
}
}
}
impl Ord for FskBandwidth {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.hertz().cmp(&other.hertz())
}
}
impl PartialOrd for FskBandwidth {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.hertz().cmp(&other.hertz()))
}
}
/// Pulse shaping options for [`FskModParams`].
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum FskPulseShape {
/// No filtering applied.
None = 0b00,
/// Gaussian BT 0.3
Bt03 = 0x08,
/// Gaussian BT 0.5
Bt05 = 0x09,
/// Gaussian BT 0.7
Bt07 = 0x0A,
/// Gaussian BT 1.0
Bt10 = 0x0B,
}
/// Bitrate argument for [`FskModParams::set_bitrate`] and
/// [`BpskModParams::set_bitrate`].
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct FskBitrate {
bits: u32,
}
impl FskBitrate {
/// Create a new `FskBitrate` from a bitrate in bits per second.
///
/// This the resulting value will be rounded down, and will saturate if
/// `bps` is outside of the theoretical limits.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::FskBitrate;
///
/// const BITRATE: FskBitrate = FskBitrate::from_bps(9600);
/// assert_eq!(BITRATE.as_bps(), 9600);
/// ```
pub const fn from_bps(bps: u32) -> Self {
const MAX: u32 = 0x00FF_FFFF;
if bps == 0 {
Self { bits: MAX }
} else {
let bits: u32 = 32 * 32_000_000 / bps;
if bits > MAX {
Self { bits: MAX }
} else {
Self { bits }
}
}
}
/// Create a new `FskBitrate` from a raw bit value.
///
/// bits = 32 × 32 MHz / bitrate
///
/// **Note:** Only the first 24 bits of the `u32` are used, the `bits`
/// argument will be masked.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::FskBitrate;
///
/// const BITRATE: FskBitrate = FskBitrate::from_raw(0x7D00);
/// assert_eq!(BITRATE.as_bps(), 32_000);
/// ```
pub const fn from_raw(bits: u32) -> Self {
Self {
bits: bits & 0x00FF_FFFF,
}
}
/// Return the bitrate in bits per second, rounded down.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::FskBitrate;
///
/// const BITS_PER_SEC: u32 = 9600;
/// const BITRATE: FskBitrate = FskBitrate::from_bps(BITS_PER_SEC);
/// assert_eq!(BITRATE.as_bps(), BITS_PER_SEC);
/// ```
pub const fn as_bps(&self) -> u32 {
if self.bits == 0 {
0
} else {
32 * 32_000_000 / self.bits
}
}
pub(crate) const fn into_bits(self) -> u32 {
self.bits
}
}
impl Ord for FskBitrate {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.as_bps().cmp(&other.as_bps())
}
}
impl PartialOrd for FskBitrate {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.as_bps().cmp(&other.as_bps()))
}
}
/// Frequency deviation argument for [`FskModParams::set_fdev`]
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FskFdev {
bits: u32,
}
impl FskFdev {
/// Create a new `FskFdev` from a frequency deviation in hertz, rounded
/// down.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::FskFdev;
///
/// const FDEV: FskFdev = FskFdev::from_hertz(31_250);
/// assert_eq!(FDEV.as_hertz(), 31_250);
/// ```
pub const fn from_hertz(hz: u32) -> Self {
Self {
bits: ((hz as u64) * (1 << 25) / 32_000_000) as u32 & 0x00FF_FFFF,
}
}
/// Create a new `FskFdev` from a raw bit value.
///
/// bits = fdev × 2<sup>25</sup> / 32 MHz
///
/// **Note:** Only the first 24 bits of the `u32` are used, the `bits`
/// argument will be masked.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::FskFdev;
///
/// const FDEV: FskFdev = FskFdev::from_raw(0x8000);
/// assert_eq!(FDEV.as_hertz(), 31_250);
/// ```
pub const fn from_raw(bits: u32) -> Self {
Self {
bits: bits & 0x00FF_FFFF,
}
}
/// Return the frequency deviation in hertz, rounded down.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::FskFdev;
///
/// const HERTZ: u32 = 31_250;
/// const FDEV: FskFdev = FskFdev::from_hertz(HERTZ);
/// assert_eq!(FDEV.as_hertz(), HERTZ);
/// ```
pub const fn as_hertz(&self) -> u32 {
((self.bits as u64) * 32_000_000 / (1 << 25)) as u32
}
pub(crate) const fn into_bits(self) -> u32 {
self.bits
}
}
/// (G)FSK modulation paramters.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct FskModParams {
buf: [u8; 9],
}
impl FskModParams {
/// Create a new `FskModParams` struct.
///
/// This is the same as `default`, but in a `const` function.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::FskModParams;
///
/// const MOD_PARAMS: FskModParams = FskModParams::new();
/// ```
pub const fn new() -> FskModParams {
FskModParams {
buf: [
super::OpCode::SetModulationParams as u8,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
],
}
.set_bitrate(FskBitrate::from_bps(50_000))
.set_pulse_shape(FskPulseShape::None)
.set_bandwidth(FskBandwidth::Bw58)
.set_fdev(FskFdev::from_hertz(25_000))
}
/// Get the bitrate.
///
/// # Example
///
/// Setting the bitrate to 32,000 bits per second.
///
/// ```
/// use stm32wl_hal::subghz::{FskBitrate, FskModParams};
///
/// const BITRATE: FskBitrate = FskBitrate::from_bps(32_000);
/// const MOD_PARAMS: FskModParams = FskModParams::new().set_bitrate(BITRATE);
/// assert_eq!(MOD_PARAMS.bitrate(), BITRATE);
/// ```
pub const fn bitrate(&self) -> FskBitrate {
let raw: u32 = u32::from_be_bytes([0, self.buf[1], self.buf[2], self.buf[3]]);
FskBitrate::from_raw(raw)
}
/// Set the bitrate.
///
/// # Example
///
/// Setting the bitrate to 32,000 bits per second.
///
/// ```
/// use stm32wl_hal::subghz::{FskBitrate, FskModParams};
///
/// const BITRATE: FskBitrate = FskBitrate::from_bps(32_000);
/// const MOD_PARAMS: FskModParams = FskModParams::new().set_bitrate(BITRATE);
/// # assert_eq!(MOD_PARAMS.as_slice()[1], 0x00);
/// # assert_eq!(MOD_PARAMS.as_slice()[2], 0x7D);
/// # assert_eq!(MOD_PARAMS.as_slice()[3], 0x00);
/// ```
#[must_use = "set_bitrate returns a modified FskModParams"]
pub const fn set_bitrate(mut self, bitrate: FskBitrate) -> FskModParams {
let bits: u32 = bitrate.into_bits();
self.buf[1] = ((bits >> 16) & 0xFF) as u8;
self.buf[2] = ((bits >> 8) & 0xFF) as u8;
self.buf[3] = (bits & 0xFF) as u8;
self
}
/// Set the pulse shaping.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::{FskModParams, FskPulseShape};
///
/// const MOD_PARAMS: FskModParams = FskModParams::new().set_pulse_shape(FskPulseShape::Bt03);
/// # assert_eq!(MOD_PARAMS.as_slice()[4], 0x08);
/// ```
#[must_use = "set_pulse_shape returns a modified FskModParams"]
pub const fn set_pulse_shape(mut self, shape: FskPulseShape) -> FskModParams {
self.buf[4] = shape as u8;
self
}
/// Get the bandwidth.
///
/// Values that do not correspond to a valid [`FskBandwidth`] will be
/// returned in the `Err` variant of the result.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::{FskBandwidth, FskModParams};
///
/// const MOD_PARAMS: FskModParams = FskModParams::new().set_bandwidth(FskBandwidth::Bw9);
/// assert_eq!(MOD_PARAMS.bandwidth(), Ok(FskBandwidth::Bw9));
/// ```
pub const fn bandwidth(&self) -> Result<FskBandwidth, u8> {
FskBandwidth::from_bits(self.buf[5])
}
/// Set the bandwidth.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::{FskBandwidth, FskModParams};
///
/// const MOD_PARAMS: FskModParams = FskModParams::new().set_bandwidth(FskBandwidth::Bw9);
/// # assert_eq!(MOD_PARAMS.as_slice()[5], 0x1E);
/// ```
#[must_use = "set_pulse_shape returns a modified FskModParams"]
pub const fn set_bandwidth(mut self, bw: FskBandwidth) -> FskModParams {
self.buf[5] = bw as u8;
self
}
/// Get the frequency deviation.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::{FskFdev, FskModParams};
///
/// const FDEV: FskFdev = FskFdev::from_hertz(31_250);
/// const MOD_PARAMS: FskModParams = FskModParams::new().set_fdev(FDEV);
/// assert_eq!(MOD_PARAMS.fdev(), FDEV);
/// ```
pub const fn fdev(&self) -> FskFdev {
let raw: u32 = u32::from_be_bytes([0, self.buf[6], self.buf[7], self.buf[8]]);
FskFdev::from_raw(raw)
}
/// Set the frequency deviation.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::{FskFdev, FskModParams};
///
/// const FDEV: FskFdev = FskFdev::from_hertz(31_250);
/// const MOD_PARAMS: FskModParams = FskModParams::new().set_fdev(FDEV);
/// # assert_eq!(MOD_PARAMS.as_slice()[6], 0x00);
/// # assert_eq!(MOD_PARAMS.as_slice()[7], 0x80);
/// # assert_eq!(MOD_PARAMS.as_slice()[8], 0x00);
/// ```
#[must_use = "set_fdev returns a modified FskModParams"]
pub const fn set_fdev(mut self, fdev: FskFdev) -> FskModParams {
let bits: u32 = fdev.into_bits();
self.buf[6] = ((bits >> 16) & 0xFF) as u8;
self.buf[7] = ((bits >> 8) & 0xFF) as u8;
self.buf[8] = (bits & 0xFF) as u8;
self
}
/// Returns `true` if the modulation parameters are valid.
///
/// The bandwidth must be chosen so that:
///
/// [`FskBandwidth`] > [`FskBitrate`] + 2 × [`FskFdev`] + frequency error
///
/// Where frequency error = 2 × HSE32<sub>FREQ</sub> error.
///
/// The datasheet (DS13293 Rev 1) gives these requirements for the HSE32
/// frequency tolerance:
///
/// * Initial: ±10 ppm
/// * Over temperature (-20 to 70 °C): ±10 ppm
/// * Aging over 10 years: ±10 ppm
///
/// # Example
///
/// Checking valid parameters at compile-time
///
/// ```
/// extern crate static_assertions as sa;
/// use stm32wl_hal::subghz::{FskBandwidth, FskBitrate, FskFdev, FskModParams, FskPulseShape};
///
/// const MOD_PARAMS: FskModParams = FskModParams::new()
/// .set_bitrate(FskBitrate::from_bps(20_000))
/// .set_pulse_shape(FskPulseShape::Bt03)
/// .set_bandwidth(FskBandwidth::Bw58)
/// .set_fdev(FskFdev::from_hertz(10_000));
///
/// // 30 PPM is wost case (if the HSE32 crystal meets requirements)
/// sa::const_assert!(MOD_PARAMS.is_valid(30));
/// ```
#[must_use = "the return value indicates if the modulation parameters are valid"]
pub const fn is_valid(&self, ppm: u8) -> bool {
let bw: u32 = match self.bandwidth() {
Ok(bw) => bw.hertz(),
Err(_) => return false,
};
let br: u32 = self.bitrate().as_bps();
let fdev: u32 = self.fdev().as_hertz();
let hse_err: u32 = 32 * (ppm as u32);
let freq_err: u32 = 2 * hse_err;
bw > br + 2 * fdev + freq_err
}
2021-09-14 14:58:37 +02:00
/// Returns `true` if the modulation parameters are valid for a worst-case
/// crystal tolerance.
///
/// This is equivalent to [`is_valid`](Self::is_valid) with a `ppm` argument
/// of 30.
#[must_use = "the return value indicates if the modulation parameters are valid"]
pub const fn is_valid_worst_case(&self) -> bool {
self.is_valid(30)
}
/// Extracts a slice containing the packet.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::{FskBandwidth, FskBitrate, FskFdev, FskModParams, FskPulseShape};
///
/// const BITRATE: FskBitrate = FskBitrate::from_bps(20_000);
/// const PULSE_SHAPE: FskPulseShape = FskPulseShape::Bt03;
/// const BW: FskBandwidth = FskBandwidth::Bw58;
/// const FDEV: FskFdev = FskFdev::from_hertz(10_000);
///
/// const MOD_PARAMS: FskModParams = FskModParams::new()
/// .set_bitrate(BITRATE)
/// .set_pulse_shape(PULSE_SHAPE)
/// .set_bandwidth(BW)
/// .set_fdev(FDEV);
///
/// assert_eq!(
/// MOD_PARAMS.as_slice(),
/// &[0x8B, 0x00, 0xC8, 0x00, 0x08, 0x0C, 0x00, 0x28, 0xF5]
/// );
/// ```
pub const fn as_slice(&self) -> &[u8] {
&self.buf
}
}
impl Default for FskModParams {
fn default() -> Self {
Self::new()
}
}
/// LoRa spreading factor.
///
/// Argument of [`LoRaModParams::set_sf`].
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum SpreadingFactor {
/// Spreading factor 5.
Sf5 = 0x05,
/// Spreading factor 6.
Sf6 = 0x06,
/// Spreading factor 7.
Sf7 = 0x07,
/// Spreading factor 8.
Sf8 = 0x08,
/// Spreading factor 9.
Sf9 = 0x09,
/// Spreading factor 10.
2021-09-14 14:58:37 +02:00
Sf10 = 0x0A,
/// Spreading factor 11.
2021-09-14 14:58:37 +02:00
Sf11 = 0x0B,
/// Spreading factor 12.
2021-09-14 14:58:37 +02:00
Sf12 = 0x0C,
}
impl From<SpreadingFactor> for u8 {
fn from(sf: SpreadingFactor) -> Self {
sf as u8
}
}
/// LoRa bandwidth.
///
/// Argument of [`LoRaModParams::set_bw`].
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum LoRaBandwidth {
/// 7.81 kHz
Bw7 = 0x00,
/// 10.42 kHz
Bw10 = 0x08,
/// 15.63 kHz
Bw15 = 0x01,
/// 20.83 kHz
Bw20 = 0x09,
/// 31.25 kHz
Bw31 = 0x02,
/// 41.67 kHz
Bw41 = 0x0A,
/// 62.50 kHz
Bw62 = 0x03,
/// 125 kHz
Bw125 = 0x04,
/// 250 kHz
Bw250 = 0x05,
/// 500 kHz
Bw500 = 0x06,
}
impl LoRaBandwidth {
/// Get the bandwidth in hertz.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::LoRaBandwidth;
///
/// assert_eq!(LoRaBandwidth::Bw7.hertz(), 7_810);
/// assert_eq!(LoRaBandwidth::Bw10.hertz(), 10_420);
/// assert_eq!(LoRaBandwidth::Bw15.hertz(), 15_630);
/// assert_eq!(LoRaBandwidth::Bw20.hertz(), 20_830);
/// assert_eq!(LoRaBandwidth::Bw31.hertz(), 31_250);
/// assert_eq!(LoRaBandwidth::Bw41.hertz(), 41_670);
/// assert_eq!(LoRaBandwidth::Bw62.hertz(), 62_500);
/// assert_eq!(LoRaBandwidth::Bw125.hertz(), 125_000);
/// assert_eq!(LoRaBandwidth::Bw250.hertz(), 250_000);
/// assert_eq!(LoRaBandwidth::Bw500.hertz(), 500_000);
/// ```
pub const fn hertz(&self) -> u32 {
match self {
LoRaBandwidth::Bw7 => 7_810,
LoRaBandwidth::Bw10 => 10_420,
LoRaBandwidth::Bw15 => 15_630,
LoRaBandwidth::Bw20 => 20_830,
LoRaBandwidth::Bw31 => 31_250,
LoRaBandwidth::Bw41 => 41_670,
LoRaBandwidth::Bw62 => 62_500,
LoRaBandwidth::Bw125 => 125_000,
LoRaBandwidth::Bw250 => 250_000,
LoRaBandwidth::Bw500 => 500_000,
}
}
}
impl Ord for LoRaBandwidth {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.hertz().cmp(&other.hertz())
}
}
impl PartialOrd for LoRaBandwidth {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.hertz().cmp(&other.hertz()))
}
}
/// LoRa forward error correction coding rate.
///
/// Argument of [`LoRaModParams::set_cr`].
#[derive(Debug, PartialEq, Eq, Clone, Copy, PartialOrd, Ord)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum CodingRate {
/// No forward error correction coding rate 4/4
Cr44 = 0x00,
/// Forward error correction coding rate 4/5
Cr45 = 0x1,
/// Forward error correction coding rate 4/6
Cr46 = 0x2,
/// Forward error correction coding rate 4/7
Cr47 = 0x3,
/// Forward error correction coding rate 4/8
Cr48 = 0x4,
}
/// LoRa modulation paramters.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct LoRaModParams {
buf: [u8; 5],
}
impl LoRaModParams {
/// Create a new `LoRaModParams` struct.
///
/// This is the same as `default`, but in a `const` function.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::LoRaModParams;
///
/// const MOD_PARAMS: LoRaModParams = LoRaModParams::new();
/// assert_eq!(MOD_PARAMS, LoRaModParams::default());
/// ```
pub const fn new() -> LoRaModParams {
LoRaModParams {
buf: [
super::OpCode::SetModulationParams as u8,
0x05, // valid spreading factor
0x00,
0x00,
0x00,
],
}
}
/// Set the spreading factor.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::{LoRaModParams, SpreadingFactor};
///
/// const MOD_PARAMS: LoRaModParams = LoRaModParams::new().set_sf(SpreadingFactor::Sf7);
/// # assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x07, 0x00, 0x00, 0x00]);
/// ```
#[must_use = "set_sf returns a modified LoRaModParams"]
pub const fn set_sf(mut self, sf: SpreadingFactor) -> Self {
self.buf[1] = sf as u8;
self
}
/// Set the bandwidth.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::{LoRaBandwidth, LoRaModParams};
///
/// const MOD_PARAMS: LoRaModParams = LoRaModParams::new().set_bw(LoRaBandwidth::Bw125);
/// # assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x05, 0x04, 0x00, 0x00]);
/// ```
#[must_use = "set_bw returns a modified LoRaModParams"]
pub const fn set_bw(mut self, bw: LoRaBandwidth) -> Self {
self.buf[2] = bw as u8;
self
}
/// Set the forward error correction coding rate.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::{CodingRate, LoRaModParams};
///
/// const MOD_PARAMS: LoRaModParams = LoRaModParams::new().set_cr(CodingRate::Cr45);
/// # assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x05, 0x00, 0x01, 0x00]);
/// ```
#[must_use = "set_cr returns a modified LoRaModParams"]
pub const fn set_cr(mut self, cr: CodingRate) -> Self {
self.buf[3] = cr as u8;
self
}
/// Set low data rate optimization enable.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::LoRaModParams;
///
/// const MOD_PARAMS: LoRaModParams = LoRaModParams::new().set_ldro_en(true);
/// # assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x05, 0x00, 0x00, 0x01]);
/// ```
#[must_use = "set_ldro_en returns a modified LoRaModParams"]
pub const fn set_ldro_en(mut self, en: bool) -> Self {
self.buf[4] = en as u8;
self
}
/// Extracts a slice containing the packet.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::{CodingRate, LoRaBandwidth, LoRaModParams, SpreadingFactor};
///
/// const MOD_PARAMS: LoRaModParams = LoRaModParams::new()
/// .set_sf(SpreadingFactor::Sf7)
/// .set_bw(LoRaBandwidth::Bw125)
/// .set_cr(CodingRate::Cr45)
/// .set_ldro_en(false);
///
/// assert_eq!(MOD_PARAMS.as_slice(), &[0x8B, 0x07, 0x04, 0x01, 0x00]);
/// ```
pub const fn as_slice(&self) -> &[u8] {
&self.buf
}
}
impl Default for LoRaModParams {
fn default() -> Self {
Self::new()
}
}
/// BPSK modulation paramters.
///
/// **Note:** There is no method to set the pulse shape because there is only
/// one valid pulse shape (Gaussian BT 0.5).
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct BpskModParams {
buf: [u8; 5],
}
impl BpskModParams {
/// Create a new `BpskModParams` struct.
///
/// This is the same as `default`, but in a `const` function.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::BpskModParams;
///
/// const MOD_PARAMS: BpskModParams = BpskModParams::new();
/// assert_eq!(MOD_PARAMS, BpskModParams::default());
/// ```
pub const fn new() -> BpskModParams {
const OPCODE: u8 = super::OpCode::SetModulationParams as u8;
BpskModParams {
buf: [OPCODE, 0x1A, 0x0A, 0xAA, 0x16],
}
}
/// Set the bitrate.
///
/// # Example
///
/// Setting the bitrate to 600 bits per second.
///
/// ```
/// use stm32wl_hal::subghz::{BpskModParams, FskBitrate};
///
/// const BITRATE: FskBitrate = FskBitrate::from_bps(600);
/// const MOD_PARAMS: BpskModParams = BpskModParams::new().set_bitrate(BITRATE);
/// # assert_eq!(MOD_PARAMS.as_slice()[1], 0x1A);
/// # assert_eq!(MOD_PARAMS.as_slice()[2], 0x0A);
/// # assert_eq!(MOD_PARAMS.as_slice()[3], 0xAA);
/// ```
#[must_use = "set_bitrate returns a modified BpskModParams"]
pub const fn set_bitrate(mut self, bitrate: FskBitrate) -> BpskModParams {
let bits: u32 = bitrate.into_bits();
self.buf[1] = ((bits >> 16) & 0xFF) as u8;
self.buf[2] = ((bits >> 8) & 0xFF) as u8;
self.buf[3] = (bits & 0xFF) as u8;
self
}
/// Extracts a slice containing the packet.
///
/// # Example
///
/// ```
/// use stm32wl_hal::subghz::{BpskModParams, FskBitrate};
///
/// const BITRATE: FskBitrate = FskBitrate::from_bps(100);
/// const MOD_PARAMS: BpskModParams = BpskModParams::new().set_bitrate(BITRATE);
/// assert_eq!(MOD_PARAMS.as_slice(), [0x8B, 0x9C, 0x40, 0x00, 0x16]);
/// ```
pub const fn as_slice(&self) -> &[u8] {
&self.buf
}
}
impl Default for BpskModParams {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod test {
use super::{FskBandwidth, FskBitrate, FskFdev, LoRaBandwidth};
#[test]
fn fsk_bw_ord() {
assert!((FskBandwidth::Bw4 as u8) > (FskBandwidth::Bw5 as u8));
assert!(FskBandwidth::Bw4 < FskBandwidth::Bw5);
assert!(FskBandwidth::Bw5 > FskBandwidth::Bw4);
}
#[test]
fn lora_bw_ord() {
assert!((LoRaBandwidth::Bw10 as u8) > (LoRaBandwidth::Bw15 as u8));
assert!(LoRaBandwidth::Bw10 < LoRaBandwidth::Bw15);
assert!(LoRaBandwidth::Bw15 > LoRaBandwidth::Bw10);
}
#[test]
fn fsk_bitrate_ord() {
assert!(FskBitrate::from_bps(9600) > FskBitrate::from_bps(4800));
assert!(FskBitrate::from_bps(4800) < FskBitrate::from_bps(9600));
}
#[test]
fn fsk_bitrate_as_bps_limits() {
const ZERO: FskBitrate = FskBitrate::from_raw(0);
const ONE: FskBitrate = FskBitrate::from_raw(1);
const MAX: FskBitrate = FskBitrate::from_raw(u32::MAX);
assert_eq!(ZERO.as_bps(), 0);
assert_eq!(ONE.as_bps(), 1_024_000_000);
assert_eq!(MAX.as_bps(), 61);
}
#[test]
fn fsk_bitrate_from_bps_limits() {
const ZERO: FskBitrate = FskBitrate::from_bps(0);
const ONE: FskBitrate = FskBitrate::from_bps(1);
const MAX: FskBitrate = FskBitrate::from_bps(u32::MAX);
assert_eq!(ZERO.as_bps(), 61);
assert_eq!(ONE.as_bps(), 61);
assert_eq!(MAX.as_bps(), 0);
}
#[test]
fn fsk_fdev_ord() {
assert!(FskFdev::from_hertz(30_000) > FskFdev::from_hertz(20_000));
assert!(FskFdev::from_hertz(20_000) < FskFdev::from_hertz(30_000));
}
#[test]
fn fsk_fdev_as_hertz_limits() {
const ZERO: FskFdev = FskFdev::from_raw(0);
const ONE: FskFdev = FskFdev::from_raw(1);
const MAX: FskFdev = FskFdev::from_raw(u32::MAX);
assert_eq!(ZERO.as_hertz(), 0);
assert_eq!(ONE.as_hertz(), 0);
assert_eq!(MAX.as_hertz(), 15_999_999);
}
#[test]
fn fsk_fdev_from_hertz_limits() {
const ZERO: FskFdev = FskFdev::from_hertz(0);
const ONE: FskFdev = FskFdev::from_hertz(1);
const MAX: FskFdev = FskFdev::from_hertz(u32::MAX);
assert_eq!(ZERO.as_hertz(), 0);
assert_eq!(ONE.as_hertz(), 0);
assert_eq!(MAX.as_hertz(), 6_967_294);
}
}