embassy/embassy-stm32/src/subghz/mod_params.rs
Ulf Lilleengen fb697a2657 Updates
2021-09-15 12:46:20 +02:00

1007 lines
30 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/// 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
}
/// 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.
Sf10 = 0x0A,
/// Spreading factor 11.
Sf11 = 0x0B,
/// Spreading factor 12.
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);
}
}