From 5732e5b398b757467031cbba0c9c9d5f79497ab8 Mon Sep 17 00:00:00 2001 From: ryan kurte Date: Sun, 21 Nov 2021 11:56:00 +1300 Subject: [PATCH 1/2] add shared modulation types for gfsk, lora --- src/config.rs | 20 +++++----------- src/lib.rs | 9 +++---- src/modulation/gfsk.rs | 15 ++++++++++++ src/modulation/lora.rs | 53 ++++++++++++++++++++++++++++++++++++++++++ src/modulation/mod.rs | 20 ++++++++++++++++ 5 files changed, 99 insertions(+), 18 deletions(-) create mode 100644 src/modulation/gfsk.rs create mode 100644 src/modulation/lora.rs create mode 100644 src/modulation/mod.rs diff --git a/src/config.rs b/src/config.rs index f89741d..f2dc1a2 100644 --- a/src/config.rs +++ b/src/config.rs @@ -34,30 +34,22 @@ pub enum ConfigOption { Promiscuous(bool), } -/// Radio configuration errors -/// This should be extended with errors generally relevant to configuration, -/// with radio-specific errors passed through the Other(E) field. -#[non_exhaustive] -#[derive(Clone, Debug, PartialEq)] -pub enum ConfigError { - /// Configuration option not supported - NotSupported, - - /// Other (device, non-configuration errors) - Other(E), +pub trait ConfigError { + /// Indicates a configuration option is not supported + fn not_supported() -> bool; } /// Configure trait implemented by configurable radios pub trait Configure { /// Radio error - type Error; + type Error: ConfigError; /// Set a configuration option /// Returns Ok(true) on set, Ok(false) for unsupported options, Err(Self::Error) for errors - fn set_option(&mut self, o: &ConfigOption) -> Result<(), ConfigError>; + fn set_option(&mut self, o: &ConfigOption) -> Result<(), Self::Error>; /// Fetch a configuration option /// This will overwrite the value of the provided option enum /// Returns Ok(true) on successful get, Ok(false) for unsupported options, Err(Self::Error) for errors - fn get_option(&mut self, o: &mut ConfigOption) -> Result<(), ConfigError>; + fn get_option(&mut self, o: &mut ConfigOption) -> Result<(), Self::Error>; } diff --git a/src/lib.rs b/src/lib.rs index 6854f8f..ffbf63f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,7 @@ use core::fmt::Debug; pub mod blocking; pub mod config; +pub mod modulation; #[cfg(feature = "helpers")] pub mod helpers; @@ -125,15 +126,15 @@ impl From for BasicChannel { } } -impl Into for BasicChannel { - fn into(self) -> u16 { - self.0 +impl From for u16 { + fn from(ch: BasicChannel) -> u16 { + ch.0 } } /// Channel trait for configuring radio channelization pub trait Channel { - /// Channel information + /// Radio channel type type Channel: Debug; /// Radio error type type Error: Debug; diff --git a/src/modulation/gfsk.rs b/src/modulation/gfsk.rs new file mode 100644 index 0000000..8a29164 --- /dev/null +++ b/src/modulation/gfsk.rs @@ -0,0 +1,15 @@ +//! Common GFSK modulation options + +/// Basic GFSK channel configuration +#[derive(Clone, PartialEq, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub struct GfskChannel { + /// Channel frequency in kHz + pub freq_khz: u32, + + /// Channel bandwidth in kHz + pub bw_khz: u16, + + /// Bitrate in bps + pub bitrate_bps: u32, +} diff --git a/src/modulation/lora.rs b/src/modulation/lora.rs new file mode 100644 index 0000000..a0d0a95 --- /dev/null +++ b/src/modulation/lora.rs @@ -0,0 +1,53 @@ +//! Common LoRa modulation options + +/// LoRa mode channel configuration +#[derive(Clone, PartialEq, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] + +pub struct LoRaChannel { + /// LoRa frequency in kHz + pub freq_khz: u32, + /// LoRa channel bandwidth + pub bw_khz: u16, + /// LoRa Spreading Factor + pub sf: SpreadingFactor, + /// LoRa Coding rate + pub cr: CodingRate, +} + +/// Spreading factor for LoRa mode +#[derive(Copy, Clone, PartialEq, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[non_exhaustive] +pub enum SpreadingFactor { + /// LoRa Spreading Factor 5, 32 chips / symbol + Sf5, + /// LoRa Spreading Factor 6, 64 chips / symbol + Sf6, + /// LoRa Spreading Factor 7, 128 chips / symbol + Sf7, + /// LoRa Spreading Factor 8, 256 chips / symbol + Sf8, + /// LoRa Spreading Factor 9, 512 chips / symbol + Sf9, + /// LoRa Spreading Factor 10 1024 chips / symbol + Sf10, + /// LoRa Spreading Factor 11 2048 chips / symbol + Sf11, + /// LoRa Spreading Factor 12 4096 chips / symbol + Sf12, +} + +#[derive(Copy, Clone, PartialEq, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[non_exhaustive] +pub enum CodingRate { + /// LoRa Coding rate 4/5 + Cr4_5, + /// LoRa Coding rate 4/6 + Cr4_6, + /// LoRa Coding rate 4/7 + Cr4_7, + /// LoRa Coding rate 4/8 + Cr4_8, +} diff --git a/src/modulation/mod.rs b/src/modulation/mod.rs new file mode 100644 index 0000000..ecbbdc8 --- /dev/null +++ b/src/modulation/mod.rs @@ -0,0 +1,20 @@ +//! Shared types for radio channel / modulation configuration + +use core::fmt::Debug; + +pub mod gfsk; + +pub mod lora; + +/// Common modulation configuration errors +/// +/// These are provided as a helper for `TryFrom` implementations, +/// and not intended to be prescriptive. +#[derive(Copy, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[non_exhaustive] +pub enum ModError { + UnsupportedBitrate, + UnsupportedFrequency, + UnsupportedBandwidth, +} From e7ebf07b3c2aa373e8ecc236cc35f8645eb3fb6c Mon Sep 17 00:00:00 2001 From: ryan kurte Date: Fri, 10 Dec 2021 09:53:01 +1300 Subject: [PATCH 2/2] added type, trait --- src/blocking.rs | 2 +- src/lib.rs | 17 ++--- src/modulation/gfsk.rs | 10 +-- src/modulation/lora.rs | 6 +- src/modulation/mod.rs | 142 ++++++++++++++++++++++++++++++++++++++++- 5 files changed, 161 insertions(+), 16 deletions(-) diff --git a/src/blocking.rs b/src/blocking.rs index ebdd65d..11a3e78 100644 --- a/src/blocking.rs +++ b/src/blocking.rs @@ -3,7 +3,7 @@ //! These implementations use the radio's DelayUs implementation to //! poll on completion of operations. //! -//! ## https://github.com/ryankurte/rust-radio +//! ## //! ## Copyright 2020 Ryan Kurte use core::fmt::Debug; diff --git a/src/lib.rs b/src/lib.rs index ffbf63f..f4b78cb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,9 @@ use core::convert::TryFrom; use core::fmt::Debug; +#[cfg(feature = "std")] +use std::str::FromStr; + pub mod blocking; pub mod config; pub mod modulation; @@ -23,13 +26,12 @@ pub mod mock; #[cfg(feature = "nonblocking")] pub mod nonblocking; -/// Radio trait combines Base, Configure, Send and Receive for a generic radio object +/// Radio trait combines Transmit, Receive, and State for a generic radio object pub trait Radio: Transmit + Receive + State {} - /// Transmit trait for radios that can transmit packets /// /// `start_transmit` should be called to load data into the radio, with `check_transmit` called -/// periodically (or using interrupts) to continue and finalise the transmission. +/// periodically (or triggered by interrupts) to continue and finalise the transmission. pub trait Transmit { /// Radio error type Error: Debug; @@ -48,8 +50,10 @@ pub trait Transmit { /// Receive trait for radios that can receive packets /// /// `start_receive` should be used to setup the radio in receive mode, with `check_receive` called -/// periodically (or using interrupts) to poll for packet reception. Once a packet has been received, -/// `get_received` fetches the received packet (and associated info) from the radio. +/// periodically (or triggered by interrupts) to poll for packet reception. Once a packet has been received, +/// `get_received` fetches the received packet (and associated information) from the radio. +/// +/// If you need to check for an receive operation in progress check out the [`Busy`] (or [`State`]) traits. pub trait Receive { /// Radio error type Error: Debug; @@ -251,9 +255,6 @@ pub trait Registers { } } -#[cfg(feature = "std")] -use std::str::FromStr; - #[cfg(feature = "std")] fn duration_from_str(s: &str) -> Result { let d = humantime::Duration::from_str(s)?; diff --git a/src/modulation/gfsk.rs b/src/modulation/gfsk.rs index 8a29164..24ea18c 100644 --- a/src/modulation/gfsk.rs +++ b/src/modulation/gfsk.rs @@ -1,14 +1,16 @@ //! Common GFSK modulation options +use super::Freq; + /// Basic GFSK channel configuration #[derive(Clone, PartialEq, Debug)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct GfskChannel { - /// Channel frequency in kHz - pub freq_khz: u32, + /// Channel frequency + pub freq: Freq, - /// Channel bandwidth in kHz - pub bw_khz: u16, + /// Channel bandwidth + pub bw_khz: Freq, /// Bitrate in bps pub bitrate_bps: u32, diff --git a/src/modulation/lora.rs b/src/modulation/lora.rs index a0d0a95..46f26d4 100644 --- a/src/modulation/lora.rs +++ b/src/modulation/lora.rs @@ -1,14 +1,16 @@ //! Common LoRa modulation options +use super::Freq; + /// LoRa mode channel configuration #[derive(Clone, PartialEq, Debug)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct LoRaChannel { /// LoRa frequency in kHz - pub freq_khz: u32, + pub freq: Freq, /// LoRa channel bandwidth - pub bw_khz: u16, + pub bw: Freq, /// LoRa Spreading Factor pub sf: SpreadingFactor, /// LoRa Coding rate diff --git a/src/modulation/mod.rs b/src/modulation/mod.rs index ecbbdc8..d8c05ca 100644 --- a/src/modulation/mod.rs +++ b/src/modulation/mod.rs @@ -15,6 +15,146 @@ pub mod lora; #[non_exhaustive] pub enum ModError { UnsupportedBitrate, - UnsupportedFrequency, + UnsupportedFreq, UnsupportedBandwidth, } + +/// Basic frequency type for use in radio definitions. +/// +/// This splits frequencies into integer `khz` and `hz` components to achieve Hz granularity with >>GHz range. +/// Users above ~4 GHz should prefer [`Freq::parts`] over integer conversions. +/// ```rust +/// # use radio::modulation::{Freq, Frequency}; +/// // Freq objects can be constructed from numeric types +/// let freq = 434.mhz(); +/// assert_eq!(freq, 434_000.khz()); +/// // And converted back into these numeric types as required +/// assert_eq!(434, freq.mhz()); +/// ``` +#[derive(Copy, Clone, Debug, PartialEq)] +pub struct Freq { + /// kHz component + khz: u32, + /// Hz portion (0-1000) + hz: u32, +} + +/// Frequency trait for type conversions. +/// See [`Freq`] implementations for details +pub trait Frequency { + fn hz(&self) -> T; + fn khz(&self) -> T; + fn mhz(&self) -> T; + fn ghz(&self) -> T; +} + +impl Freq { + /// Create a new frequency from kHz and Hz components + pub const fn from_parts(khz: u32, hz: u32) -> Option { + if hz >= 1000 { + return None; + } + Some(Self { khz, hz }) + } + + /// Fetch frequency kHz and Hz components + pub const fn parts(&self) -> (u32, u32) { + (self.khz, self.hz) + } +} + +/// Fetch u32 values from [`Freq`] types +impl Frequency for Freq { + /// Convert [`Freq`] to u32 Hz, note this will panic for frequencies over ~4GHz + /// ``` + /// # use radio::modulation::{Freq, Frequency}; + /// let f = Freq::from_parts(433_100, 200).unwrap(); + /// assert_eq!(f.hz(), 433_100_200); + /// ``` + fn hz(&self) -> u32 { + self.khz * 1000 + self.hz + } + + /// Convert [`Freq`] to integer kHz + /// ``` + /// # use radio::modulation::{Freq, Frequency}; + /// let f = Freq::from_parts(433_100, 200).unwrap(); + /// assert_eq!(f.khz(), 433_100); + /// ``` + fn khz(&self) -> u32 { + self.khz + } + + /// Convert [`Freq`] to integer MHz + /// ``` + /// # use radio::modulation::{Freq, Frequency}; + /// let f = Freq::from_parts(2_400_100, 200).unwrap(); + /// assert_eq!(f.mhz(), 2_400); + /// ``` + fn mhz(&self) -> u32 { + self.khz() / 1000 + } + + /// Convert [`Freq`] to integer GHz + /// ``` + /// # use radio::modulation::{Freq, Frequency}; + /// let f = Freq::from_parts(20_000_000, 200).unwrap(); + /// assert_eq!(f.ghz(), 20); + /// ``` + fn ghz(&self) -> u32 { + self.mhz() / 1000 + } +} + +/// Create [`Freq`] objects from [`u32`] frequencies +impl Frequency for u32 { + /// Create [`Freq`] from integer Hz + /// ``` + /// # use radio::modulation::{Freq, Frequency}; + /// let f = 434_100_200.hz(); + /// assert_eq!(f.khz(), 434_100); + /// assert_eq!(f.hz(), 434_100_200); + /// ``` + fn hz(&self) -> Freq { + Freq { + khz: self / 1000, + hz: self % 1000, + } + } + + /// Create [`Freq`] from integer kHz + /// ``` + /// # use radio::modulation::{Freq, Frequency}; + /// let f = 434_100.khz(); + /// assert_eq!(f.hz(), 434_100_000); + /// ``` + fn khz(&self) -> Freq { + Freq { khz: *self, hz: 0 } + } + + /// Create [`Freq`] from integer MHz + /// ``` + /// # use radio::modulation::{Freq, Frequency}; + /// let f = 2_450.mhz(); + /// assert_eq!(f.khz(), 2_450_000); + /// ``` + fn mhz(&self) -> Freq { + Freq { + khz: self * 1000, + hz: 0, + } + } + + /// Create [`Freq`] from integer GHz + /// ``` + /// # use radio::modulation::{Freq, Frequency}; + /// let f = 2.ghz(); + /// assert_eq!(f.mhz(), 2_000); + /// ``` + fn ghz(&self) -> Freq { + Freq { + khz: self * 1000 * 1000, + hz: 0, + } + } +}