feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
Vendored
+117
@@ -0,0 +1,117 @@
|
||||
//! Type aliases for common uses
|
||||
|
||||
use crate::Duration;
|
||||
use crate::Instant;
|
||||
use crate::Rate;
|
||||
|
||||
/// Alias for nanosecond duration
|
||||
pub type NanosDuration<T> = Duration<T, 1, 1_000_000_000>;
|
||||
|
||||
/// Alias for nanosecond duration (`u32` backing storage)
|
||||
pub type NanosDurationU32 = Duration<u32, 1, 1_000_000_000>;
|
||||
|
||||
/// Alias for nanosecond duration (`u64` backing storage)
|
||||
pub type NanosDurationU64 = Duration<u64, 1, 1_000_000_000>;
|
||||
|
||||
/// Alias for microsecond duration
|
||||
pub type MicrosDuration<T> = Duration<T, 1, 1_000_000>;
|
||||
|
||||
/// Alias for microsecond duration (`u32` backing storage)
|
||||
pub type MicrosDurationU32 = Duration<u32, 1, 1_000_000>;
|
||||
|
||||
/// Alias for microsecond duration (`u64` backing storage)
|
||||
pub type MicrosDurationU64 = Duration<u64, 1, 1_000_000>;
|
||||
|
||||
/// Alias for millisecond duration
|
||||
pub type MillisDuration<T> = Duration<T, 1, 1_000>;
|
||||
|
||||
/// Alias for millisecond duration (`u32` backing storage)
|
||||
pub type MillisDurationU32 = Duration<u32, 1, 1_000>;
|
||||
|
||||
/// Alias for millisecond duration (`u64` backing storage)
|
||||
pub type MillisDurationU64 = Duration<u64, 1, 1_000>;
|
||||
|
||||
/// Alias for second duration
|
||||
pub type SecsDuration<T> = Duration<T, 1, 1>;
|
||||
|
||||
/// Alias for second duration (`u32` backing storage)
|
||||
pub type SecsDurationU32 = Duration<u32, 1, 1>;
|
||||
|
||||
/// Alias for second duration (`u64` backing storage)
|
||||
pub type SecsDurationU64 = Duration<u64, 1, 1>;
|
||||
|
||||
/// Alias for minutes duration
|
||||
pub type MinutesDuration<T> = Duration<T, 60, 1>;
|
||||
|
||||
/// Alias for minutes duration (`u32` backing storage)
|
||||
pub type MinutesDurationU32 = Duration<u32, 60, 1>;
|
||||
|
||||
/// Alias for minutes duration (`u64` backing storage)
|
||||
pub type MinutesDurationU64 = Duration<u64, 60, 1>;
|
||||
|
||||
/// Alias for hours duration
|
||||
pub type HoursDuration<T> = Duration<T, 3_600, 1>;
|
||||
|
||||
/// Alias for hours duration (`u32` backing storage)
|
||||
pub type HoursDurationU32 = Duration<u32, 3_600, 1>;
|
||||
|
||||
/// Alias for hours duration (`u64` backing storage)
|
||||
pub type HoursDurationU64 = Duration<u64, 3_600, 1>;
|
||||
|
||||
/// Alias for durations that come from timers with a specific frequency
|
||||
pub type TimerDuration<T, const FREQ_HZ: u32> = Duration<T, 1, FREQ_HZ>;
|
||||
|
||||
/// Alias for durations that come from timers with a specific frequency (`u32` backing storage)
|
||||
pub type TimerDurationU32<const FREQ_HZ: u32> = Duration<u32, 1, FREQ_HZ>;
|
||||
|
||||
/// Alias for durations that come from timers with a specific frequency (`u64` backing storage)
|
||||
pub type TimerDurationU64<const FREQ_HZ: u32> = Duration<u64, 1, FREQ_HZ>;
|
||||
|
||||
// -------------------------------
|
||||
|
||||
/// Alias for instants that come from timers with a specific frequency
|
||||
pub type TimerInstant<T, const FREQ_HZ: u32> = Instant<T, 1, FREQ_HZ>;
|
||||
|
||||
/// Alias for instants that come from timers with a specific frequency (`u32` backing storage)
|
||||
pub type TimerInstantU32<const FREQ_HZ: u32> = Instant<u32, 1, FREQ_HZ>;
|
||||
|
||||
/// Alias for instants that come from timers with a specific frequency (`u64` backing storage)
|
||||
pub type TimerInstantU64<const FREQ_HZ: u32> = Instant<u64, 1, FREQ_HZ>;
|
||||
|
||||
// -------------------------------
|
||||
|
||||
/// Alias for hertz rate
|
||||
pub type Hertz<T> = Rate<T, 1, 1>;
|
||||
|
||||
/// Alias for hertz rate (`u32` backing storage)
|
||||
pub type HertzU32 = Rate<u32, 1, 1>;
|
||||
|
||||
/// Alias for hertz rate (`u64` backing storage)
|
||||
pub type HertzU64 = Rate<u64, 1, 1>;
|
||||
|
||||
/// Alias for kilohertz rate
|
||||
pub type Kilohertz<T> = Rate<T, 1_000, 1>;
|
||||
|
||||
/// Alias for kilohertz rate (`u32` backing storage)
|
||||
pub type KilohertzU32 = Rate<u32, 1_000, 1>;
|
||||
|
||||
/// Alias for kilohertz rate (`u64` backing storage)
|
||||
pub type KilohertzU64 = Rate<u64, 1_000, 1>;
|
||||
|
||||
/// Alias for megahertz rate
|
||||
pub type Megahertz<T> = Rate<T, 1_000_000, 1>;
|
||||
|
||||
/// Alias for megahertz rate (`u32` backing storage)
|
||||
pub type MegahertzU32 = Rate<u32, 1_000_000, 1>;
|
||||
|
||||
/// Alias for megahertz rate (`u64` backing storage)
|
||||
pub type MegahertzU64 = Rate<u64, 1_000_000, 1>;
|
||||
|
||||
/// Alias for rate that come from timers with a specific frequency
|
||||
pub type TimerRate<T, const FREQ_HZ: u32> = Rate<T, FREQ_HZ, 1>;
|
||||
|
||||
/// Alias for rate that come from timers with a specific frequency (`u32` backing storage)
|
||||
pub type TimerRateU32<const FREQ_HZ: u32> = Rate<u32, FREQ_HZ, 1>;
|
||||
|
||||
/// Alias for rate that come from timers with a specific frequency (`u64` backing storage)
|
||||
pub type TimerRateU64<const FREQ_HZ: u32> = Rate<u64, FREQ_HZ, 1>;
|
||||
Vendored
+965
@@ -0,0 +1,965 @@
|
||||
use crate::helpers::{self, Helpers};
|
||||
use crate::Rate;
|
||||
use core::cmp::Ordering;
|
||||
use core::convert;
|
||||
use core::ops;
|
||||
|
||||
/// Represents a duration of time.
|
||||
///
|
||||
/// The generic `T` can either be `u32` or `u64`, and the const generics represent the ratio of the
|
||||
/// ticks contained within the duration: `duration in seconds = NOM / DENOM * ticks`
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(
|
||||
feature = "postcard_max_size",
|
||||
derive(postcard::experimental::max_size::MaxSize)
|
||||
)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Duration<T, const NOM: u32, const DENOM: u32> {
|
||||
pub(crate) ticks: T,
|
||||
}
|
||||
|
||||
macro_rules! shorthand {
|
||||
($i:ty, $nom:literal, $denum:literal, $unit:ident, $to_unit:ident, $unital:ident, $unitstr:literal) => {
|
||||
#[doc = concat!("Convert the Duration to an integer number of ", $unitstr, ".")]
|
||||
#[inline]
|
||||
pub const fn $to_unit(&self) -> $i {
|
||||
(Helpers::<$nom, $denum, NOM, DENOM>::LD_TIMES_RN as $i * self.ticks)
|
||||
/ Helpers::<$nom, $denum, NOM, DENOM>::RD_TIMES_LN as $i
|
||||
}
|
||||
|
||||
#[doc = concat!("Shorthand for creating a duration which represents ", $unitstr, ".")]
|
||||
#[inline]
|
||||
pub const fn $unit(val: $i) -> Self {
|
||||
Self::from_ticks(
|
||||
(Helpers::<$nom, $denum, NOM, DENOM>::RD_TIMES_LN as $i * val)
|
||||
/ Helpers::<$nom, $denum, NOM, DENOM>::LD_TIMES_RN as $i
|
||||
)
|
||||
}
|
||||
|
||||
#[doc = concat!("Shorthand for creating a duration which represents ", $unitstr, " (ceil rounded).")]
|
||||
#[inline]
|
||||
pub const fn $unital(val: $i) -> Self {
|
||||
let mul = Helpers::<$nom, $denum, NOM, DENOM>::RD_TIMES_LN as $i * val;
|
||||
let ld_times_rn = Helpers::<$nom, $denum, NOM, DENOM>::LD_TIMES_RN as $i;
|
||||
Self::from_ticks(if mul % ld_times_rn == 0 {
|
||||
mul / ld_times_rn
|
||||
} else {
|
||||
mul / ld_times_rn + 1
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_duration_for_integer {
|
||||
($i:ty) => {
|
||||
impl<const NOM: u32, const DENOM: u32> Duration<$i, NOM, DENOM> {
|
||||
/// Create a `Duration` from a ticks value.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let _d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn from_ticks(ticks: $i) -> Self {
|
||||
helpers::greater_than_0::<NOM>();
|
||||
helpers::greater_than_0::<DENOM>();
|
||||
|
||||
Duration { ticks }
|
||||
}
|
||||
|
||||
/// Extract the ticks from a `Duration`.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(234);")]
|
||||
///
|
||||
/// assert_eq!(d.ticks(), 234);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn ticks(&self) -> $i {
|
||||
self.ticks
|
||||
}
|
||||
|
||||
/// Returns true if this `Duration` spans no time
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let zero = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(0);")]
|
||||
#[doc = concat!("let one = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
///
|
||||
/// assert_eq!(zero.is_zero(), true);
|
||||
/// assert_eq!(one.is_zero(), false);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn is_zero(&self) -> bool {
|
||||
self.ticks == 0
|
||||
}
|
||||
|
||||
/// Add two durations while checking for overflow.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
#[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
|
||||
#[doc = concat!("let d3 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(", stringify!($i), "::MAX);")]
|
||||
///
|
||||
/// assert_eq!(d1.checked_add(d2).unwrap().ticks(), 3);
|
||||
/// assert_eq!(d1.checked_add(d3), None);
|
||||
/// ```
|
||||
pub const fn checked_add<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
other: Duration<$i, O_NOM, O_DENOM>,
|
||||
) -> Option<Self> {
|
||||
if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
|
||||
if let Some(ticks) = self.ticks.checked_add(other.ticks) {
|
||||
Some(Duration::<$i, NOM, DENOM>::from_ticks(ticks))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
if let Some(lh) = other
|
||||
.ticks
|
||||
.checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
|
||||
{
|
||||
let ticks = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
|
||||
|
||||
if let Some(ticks) = self.ticks.checked_add(ticks) {
|
||||
Some(Duration::<$i, NOM, DENOM>::from_ticks(ticks))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Subtract two durations while checking for overflow.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
#[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
|
||||
#[doc = concat!("let d3 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(", stringify!($i), "::MAX);")]
|
||||
///
|
||||
/// assert_eq!(d2.checked_sub(d1).unwrap().ticks(), 1);
|
||||
/// assert_eq!(d1.checked_sub(d3), None);
|
||||
/// ```
|
||||
pub const fn checked_sub<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
other: Duration<$i, O_NOM, O_DENOM>,
|
||||
) -> Option<Self> {
|
||||
if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
|
||||
if let Some(ticks) = self.ticks.checked_sub(other.ticks) {
|
||||
Some(Duration::<$i, NOM, DENOM>::from_ticks(ticks))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
if let Some(lh) = other
|
||||
.ticks
|
||||
.checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
|
||||
{
|
||||
let ticks = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
|
||||
|
||||
if let Some(ticks) = self.ticks.checked_sub(ticks) {
|
||||
Some(Duration::<$i, NOM, DENOM>::from_ticks(ticks))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = concat!("Const `cmp` for ", stringify!($i))]
|
||||
#[inline(always)]
|
||||
const fn _const_cmp(a: $i, b: $i) -> Ordering {
|
||||
if a < b {
|
||||
Ordering::Less
|
||||
} else if a > b {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
/// Const partial comparison.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_00>::from_ticks(1);")]
|
||||
#[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
///
|
||||
/// assert_eq!(d1.const_partial_cmp(d2), Some(core::cmp::Ordering::Greater));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn const_partial_cmp<const R_NOM: u32, const R_DENOM: u32>(
|
||||
self,
|
||||
other: Duration<$i, R_NOM, R_DENOM>
|
||||
) -> Option<Ordering> {
|
||||
//
|
||||
// We want to check:
|
||||
//
|
||||
// n_lh / d_lh * lh_ticks {cmp} n_rh / d_rh * rh_ticks
|
||||
//
|
||||
// simplify to
|
||||
//
|
||||
// n_lh * d_rh * lh_ticks {cmp} n_rh * d_lh * rh_ticks
|
||||
//
|
||||
// find gdc(n_lh * d_rh, n_rh * d_lh) and use that to make the constants minimal (done
|
||||
// with the `helpers::Helpers` struct)
|
||||
//
|
||||
// then perform the comparison in a comparable basis
|
||||
//
|
||||
|
||||
if Helpers::<NOM, DENOM, R_NOM, R_DENOM>::SAME_BASE {
|
||||
// If we are in the same base, comparison in trivial
|
||||
Some(Self::_const_cmp(self.ticks, other.ticks))
|
||||
} else {
|
||||
let lh = self
|
||||
.ticks
|
||||
.checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::RD_TIMES_LN as $i);
|
||||
let rh = other
|
||||
.ticks
|
||||
.checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::LD_TIMES_RN as $i);
|
||||
|
||||
if let (Some(lh), Some(rh)) = (lh, rh) {
|
||||
Some(Self::_const_cmp(lh, rh))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Const equality check.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_00>::from_ticks(1);")]
|
||||
#[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(10);")]
|
||||
///
|
||||
/// assert!(d1.const_eq(d2));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn const_eq<const R_NOM: u32, const R_DENOM: u32>(
|
||||
self,
|
||||
other: Duration<$i, R_NOM, R_DENOM>
|
||||
) -> bool {
|
||||
if Helpers::<NOM, DENOM, R_NOM, R_DENOM>::SAME_BASE {
|
||||
// If we are in the same base, comparison in trivial
|
||||
self.ticks == other.ticks
|
||||
} else {
|
||||
let lh = self
|
||||
.ticks
|
||||
.checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::RD_TIMES_LN as $i);
|
||||
let rh = other
|
||||
.ticks
|
||||
.checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::LD_TIMES_RN as $i);
|
||||
|
||||
if let (Some(lh), Some(rh)) = (lh, rh) {
|
||||
lh == rh
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Const try from, checking for overflow.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_00>::from_ticks(1);")]
|
||||
#[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::const_try_from(d1);")]
|
||||
///
|
||||
/// assert_eq!(d2.unwrap().ticks(), 10);
|
||||
/// ```
|
||||
pub const fn const_try_from<const I_NOM: u32, const I_DENOM: u32>(
|
||||
duration: Duration<$i, I_NOM, I_DENOM>,
|
||||
) -> Option<Self> {
|
||||
if Helpers::<I_NOM, I_DENOM, NOM, DENOM>::SAME_BASE {
|
||||
Some(Self::from_ticks(duration.ticks))
|
||||
} else {
|
||||
if let Some(lh) = (duration.ticks as u64)
|
||||
.checked_mul(Helpers::<I_NOM, I_DENOM, NOM, DENOM>::RD_TIMES_LN as u64)
|
||||
{
|
||||
let ticks = lh / Helpers::<I_NOM, I_DENOM, NOM, DENOM>::LD_TIMES_RN as u64;
|
||||
|
||||
if ticks <= <$i>::MAX as u64 {
|
||||
Some(Self::from_ticks(ticks as $i))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Const try into, checking for overflow.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_00>::from_ticks(1);")]
|
||||
#[doc = concat!("let d2: Option<Duration::<", stringify!($i), ", 1, 1_000>> = d1.const_try_into();")]
|
||||
///
|
||||
/// assert_eq!(d2.unwrap().ticks(), 10);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn const_try_into<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
) -> Option<Duration<$i, O_NOM, O_DENOM>> {
|
||||
Duration::<$i, O_NOM, O_DENOM>::const_try_from(self)
|
||||
}
|
||||
|
||||
/// Const try into rate, checking for divide-by-zero.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
|
||||
#[doc = concat!("let r1: Option<Rate::<", stringify!($i), ", 1, 1>> = d1.try_into_rate();")]
|
||||
///
|
||||
/// assert_eq!(r1.unwrap().raw(), 500);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn try_into_rate<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
) -> Option<Rate<$i, O_NOM, O_DENOM>> {
|
||||
Rate::<$i, O_NOM, O_DENOM>::try_from_duration(self)
|
||||
}
|
||||
|
||||
/// Convert from duration to rate.
|
||||
#[inline]
|
||||
pub const fn into_rate<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
) -> Rate<$i, O_NOM, O_DENOM> {
|
||||
if let Some(v) = self.try_into_rate() {
|
||||
v
|
||||
} else {
|
||||
panic!("Into rate failed, divide-by-zero!");
|
||||
}
|
||||
}
|
||||
|
||||
/// Const try from rate, checking for divide-by-zero.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 1>::from_raw(1);")]
|
||||
#[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_000>::try_from_rate(r1);")]
|
||||
///
|
||||
/// assert_eq!(d1.unwrap().ticks(), 1_000);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn try_from_rate<const I_NOM: u32, const I_DENOM: u32>(
|
||||
rate: Rate<$i, I_NOM, I_DENOM>,
|
||||
) -> Option<Self> {
|
||||
if rate.raw > 0 {
|
||||
Some(Self::from_ticks(
|
||||
Helpers::<I_NOM, I_DENOM, NOM, DENOM>::RATE_TO_DURATION_NUMERATOR as $i
|
||||
/ rate.raw
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert from rate to duration.
|
||||
#[inline]
|
||||
pub const fn from_rate<const I_NOM: u32, const I_DENOM: u32>(
|
||||
rate: Rate<$i, I_NOM, I_DENOM>,
|
||||
) -> Self {
|
||||
if let Some(v) = Self::try_from_rate(rate) {
|
||||
v
|
||||
} else {
|
||||
panic!("From rate failed, divide-by-zero!");
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert between bases for a duration.
|
||||
///
|
||||
/// Unfortunately not a `From` impl due to collision with the std lib.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 100>::from_ticks(1);")]
|
||||
#[doc = concat!("let d2: Duration::<", stringify!($i), ", 1, 1_000> = d1.convert();")]
|
||||
///
|
||||
/// assert_eq!(d2.ticks(), 10);
|
||||
/// ```
|
||||
/// Can be used in const contexts. Compilation will fail if the conversion causes overflow
|
||||
/// ```compile_fail
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("const TICKS: ", stringify!($i), "= ", stringify!($i), "::MAX - 10;")]
|
||||
#[doc = concat!("const D1: Duration::<", stringify!($i), ", 1, 100> = Duration::<", stringify!($i), ", 1, 100>::from_ticks(TICKS);")]
|
||||
/// // Fails conversion due to tick overflow
|
||||
#[doc = concat!("const D2: Duration::<", stringify!($i), ", 1, 200> = D1.convert();")]
|
||||
#[inline]
|
||||
pub const fn convert<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
) -> Duration<$i, O_NOM, O_DENOM> {
|
||||
if let Some(v) = self.const_try_into() {
|
||||
v
|
||||
} else {
|
||||
panic!("Convert failed!");
|
||||
}
|
||||
}
|
||||
|
||||
shorthand!($i, 1, 1_000_000_000, nanos, to_nanos, nanos_at_least, "nanoseconds");
|
||||
shorthand!($i, 1, 1_000_000, micros, to_micros, micros_at_least, "microseconds");
|
||||
shorthand!($i, 1, 1_000, millis, to_millis, millis_at_least, "milliseconds");
|
||||
shorthand!($i, 1, 1, secs, to_secs, secs_at_least, "seconds");
|
||||
shorthand!($i, 60, 1, minutes, to_minutes, minutes_at_least, "minutes");
|
||||
shorthand!($i, 3600, 1, hours, to_hours, hours_at_least, "hours");
|
||||
|
||||
/// Shorthand for creating a duration which represents hertz.
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
pub const fn Hz(val: $i) -> Self {
|
||||
Self::from_rate(crate::Hertz::<$i>::from_raw(val))
|
||||
}
|
||||
|
||||
/// Shorthand for creating a duration which represents kilohertz.
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
pub const fn kHz(val: $i) -> Self {
|
||||
Self::from_rate(crate::Kilohertz::<$i>::from_raw(val))
|
||||
}
|
||||
|
||||
/// Shorthand for creating a duration which represents megahertz.
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
pub const fn MHz(val: $i) -> Self {
|
||||
Self::from_rate(crate::Megahertz::<$i>::from_raw(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialOrd<Duration<$i, R_NOM, R_DENOM>> for Duration<$i, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Duration<$i, R_NOM, R_DENOM>) -> Option<Ordering> {
|
||||
self.const_partial_cmp(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> Ord for Duration<$i, NOM, DENOM> {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
Self::_const_cmp(self.ticks, other.ticks)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialEq<Duration<$i, R_NOM, R_DENOM>> for Duration<$i, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &Duration<$i, R_NOM, R_DENOM>) -> bool {
|
||||
self.const_eq(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> Eq for Duration<$i, NOM, DENOM> {}
|
||||
|
||||
// Duration - Duration = Duration (only same base until const_generics_defaults is
|
||||
// stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Sub<Duration<$i, NOM, DENOM>>
|
||||
for Duration<$i, NOM, DENOM>
|
||||
{
|
||||
type Output = Duration<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: Duration<$i, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) = self.checked_sub(other) {
|
||||
v
|
||||
} else {
|
||||
panic!("Sub failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Duration -= Duration
|
||||
impl<const NOM: u32, const DENOM: u32> ops::SubAssign<Duration<$i, NOM, DENOM>>
|
||||
for Duration<$i, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: Self) {
|
||||
*self = *self - other;
|
||||
}
|
||||
}
|
||||
|
||||
// Duration + Duration = Duration (only same base until const_generics_defaults is
|
||||
// stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Add<Duration<$i, NOM, DENOM>>
|
||||
for Duration<$i, NOM, DENOM>
|
||||
{
|
||||
type Output = Duration<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: Duration<$i, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) = self.checked_add(other) {
|
||||
v
|
||||
} else {
|
||||
panic!("Add failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Duration += Duration
|
||||
impl<const NOM: u32, const DENOM: u32> ops::AddAssign<Duration<$i, NOM, DENOM>>
|
||||
for Duration<$i, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
*self = *self + other;
|
||||
}
|
||||
}
|
||||
|
||||
// integer * Duration = Duration
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Mul<Duration<$i, NOM, DENOM>> for u32 {
|
||||
type Output = Duration<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, mut other: Duration<$i, NOM, DENOM>) -> Self::Output {
|
||||
other.ticks *= self as $i;
|
||||
other
|
||||
}
|
||||
}
|
||||
|
||||
// Duration * integer = Duration
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Mul<u32> for Duration<$i, NOM, DENOM> {
|
||||
type Output = Duration<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn mul(mut self, other: u32) -> Self::Output {
|
||||
self.ticks *= other as $i;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// Duration *= integer
|
||||
impl<const NOM: u32, const DENOM: u32> ops::MulAssign<u32>
|
||||
for Duration<$i, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: u32) {
|
||||
*self = *self * other;
|
||||
}
|
||||
}
|
||||
|
||||
// Duration / integer = Duration
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Div<u32> for Duration<$i, NOM, DENOM> {
|
||||
type Output = Duration<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn div(mut self, other: u32) -> Self::Output {
|
||||
self.ticks /= other as $i;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// Duration /= integer
|
||||
impl<const NOM: u32, const DENOM: u32> ops::DivAssign<u32>
|
||||
for Duration<$i, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn div_assign(&mut self, other: u32) {
|
||||
*self = *self / other;
|
||||
}
|
||||
}
|
||||
|
||||
// Duration / Duration = integer
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32> ops::Div<Duration<$i, R_NOM, R_DENOM>>
|
||||
for Duration<$i, L_NOM, L_DENOM>
|
||||
{
|
||||
type Output = $i;
|
||||
|
||||
#[inline]
|
||||
fn div(self, other: Duration<$i, R_NOM, R_DENOM>) -> Self::Output {
|
||||
let conv: Duration<$i, R_NOM, R_DENOM> = self.convert();
|
||||
conv.ticks / other.ticks
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
impl<const NOM: u32, const DENOM: u32> defmt::Format for Duration<$i, NOM, DENOM>
|
||||
{
|
||||
fn format(&self, f: defmt::Formatter) {
|
||||
if NOM == 3_600 && DENOM == 1 {
|
||||
defmt::write!(f, "{} h", self.ticks)
|
||||
} else if NOM == 60 && DENOM == 1 {
|
||||
defmt::write!(f, "{} min", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1 {
|
||||
defmt::write!(f, "{} s", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000 {
|
||||
defmt::write!(f, "{} ms", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000_000 {
|
||||
defmt::write!(f, "{} us", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000_000_000 {
|
||||
defmt::write!(f, "{} ns", self.ticks)
|
||||
} else {
|
||||
defmt::write!(f, "{} ticks @ ({}/{})", self.ticks, NOM, DENOM)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> core::fmt::Display for Duration<$i, NOM, DENOM> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
if NOM == 3_600 && DENOM == 1 {
|
||||
write!(f, "{} h", self.ticks)
|
||||
} else if NOM == 60 && DENOM == 1 {
|
||||
write!(f, "{} min", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1 {
|
||||
write!(f, "{} s", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000 {
|
||||
write!(f, "{} ms", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000_000 {
|
||||
write!(f, "{} us", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000_000_000 {
|
||||
write!(f, "{} ns", self.ticks)
|
||||
} else {
|
||||
write!(f, "{} ticks @ ({}/{})", self.ticks, NOM, DENOM)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_duration_for_integer!(u32);
|
||||
impl_duration_for_integer!(u64);
|
||||
|
||||
//
|
||||
// Operations between u32 and u64 Durations
|
||||
//
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> From<Duration<u32, NOM, DENOM>>
|
||||
for Duration<u64, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn from(val: Duration<u32, NOM, DENOM>) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::from_ticks(val.ticks() as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> convert::TryFrom<Duration<u64, NOM, DENOM>>
|
||||
for Duration<u32, NOM, DENOM>
|
||||
{
|
||||
type Error = ();
|
||||
|
||||
#[inline]
|
||||
fn try_from(val: Duration<u64, NOM, DENOM>) -> Result<Duration<u32, NOM, DENOM>, ()> {
|
||||
Ok(Duration::<u32, NOM, DENOM>::from_ticks(
|
||||
val.ticks().try_into().map_err(|_| ())?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// Duration - Duration = Duration (to make shorthands work, until const_generics_defaults is
|
||||
// stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Sub<Duration<u32, NOM, DENOM>>
|
||||
for Duration<u64, NOM, DENOM>
|
||||
{
|
||||
type Output = Duration<u64, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: Duration<u32, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) =
|
||||
self.checked_sub(Duration::<u64, NOM, DENOM>::from_ticks(other.ticks() as u64))
|
||||
{
|
||||
v
|
||||
} else {
|
||||
panic!("Sub failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Duration -= Duration (to make shorthands work, until const_generics_defaults is stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::SubAssign<Duration<u32, NOM, DENOM>>
|
||||
for Duration<u64, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: Duration<u32, NOM, DENOM>) {
|
||||
*self = *self - other;
|
||||
}
|
||||
}
|
||||
|
||||
// Duration + Duration = Duration (to make shorthands work, until const_generics_defaults is
|
||||
// stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Add<Duration<u32, NOM, DENOM>>
|
||||
for Duration<u64, NOM, DENOM>
|
||||
{
|
||||
type Output = Duration<u64, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: Duration<u32, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) =
|
||||
self.checked_add(Duration::<u64, NOM, DENOM>::from_ticks(other.ticks() as u64))
|
||||
{
|
||||
v
|
||||
} else {
|
||||
panic!("Add failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Duration += Duration (to make shorthands work, until const_generics_defaults is stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::AddAssign<Duration<u32, NOM, DENOM>>
|
||||
for Duration<u64, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: Duration<u32, NOM, DENOM>) {
|
||||
*self = *self + other;
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialOrd<Duration<u32, R_NOM, R_DENOM>> for Duration<u64, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Duration<u32, R_NOM, R_DENOM>) -> Option<Ordering> {
|
||||
self.partial_cmp(&Duration::<u64, R_NOM, R_DENOM>::from_ticks(
|
||||
other.ticks() as u64
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialEq<Duration<u32, R_NOM, R_DENOM>> for Duration<u64, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &Duration<u32, R_NOM, R_DENOM>) -> bool {
|
||||
self.eq(&Duration::<u64, R_NOM, R_DENOM>::from_ticks(
|
||||
other.ticks() as u64
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialOrd<Duration<u64, R_NOM, R_DENOM>> for Duration<u32, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Duration<u64, R_NOM, R_DENOM>) -> Option<Ordering> {
|
||||
Duration::<u64, L_NOM, L_DENOM>::from_ticks(self.ticks as u64).partial_cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialEq<Duration<u64, R_NOM, R_DENOM>> for Duration<u32, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &Duration<u64, R_NOM, R_DENOM>) -> bool {
|
||||
Duration::<u64, L_NOM, L_DENOM>::from_ticks(self.ticks as u64).eq(other)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait for simple short-hands for u32 Durations
|
||||
pub trait ExtU32 {
|
||||
/// Shorthand for creating a duration which represents nanoseconds.
|
||||
fn nanos<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents microseconds.
|
||||
fn micros<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents milliseconds.
|
||||
fn millis<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents seconds.
|
||||
fn secs<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents minutes.
|
||||
fn minutes<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents hours.
|
||||
fn hours<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
}
|
||||
|
||||
impl ExtU32 for u32 {
|
||||
#[inline]
|
||||
fn nanos<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::nanos(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn micros<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::micros(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn millis<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::millis(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn secs<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::secs(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn minutes<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::minutes(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hours<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::hours(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait for simple short-hands for u32 Durations (ceil rounded)
|
||||
pub trait ExtU32Ceil {
|
||||
/// Shorthand for creating a duration which represents nanoseconds.
|
||||
fn nanos_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents microseconds.
|
||||
fn micros_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents milliseconds.
|
||||
fn millis_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents seconds.
|
||||
fn secs_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents minutes.
|
||||
fn minutes_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents hours.
|
||||
fn hours_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
}
|
||||
|
||||
impl ExtU32Ceil for u32 {
|
||||
#[inline]
|
||||
fn nanos_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::nanos_at_least(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn micros_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::micros_at_least(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn millis_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::millis_at_least(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn secs_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::secs_at_least(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn minutes_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::minutes_at_least(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hours_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::hours_at_least(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait for simple short-hands for u64 Durations
|
||||
pub trait ExtU64 {
|
||||
/// Shorthand for creating a duration which represents nanoseconds.
|
||||
fn nanos<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents microseconds.
|
||||
fn micros<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents milliseconds.
|
||||
fn millis<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents seconds.
|
||||
fn secs<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents minutes.
|
||||
fn minutes<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents hours.
|
||||
fn hours<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
}
|
||||
|
||||
impl ExtU64 for u64 {
|
||||
#[inline]
|
||||
fn nanos<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::nanos(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn micros<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::micros(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn millis<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::millis(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn secs<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::secs(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn minutes<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::minutes(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hours<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::hours(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait for simple short-hands for u64 Durations (ceil rounded)
|
||||
pub trait ExtU64Ceil {
|
||||
/// Shorthand for creating a duration which represents nanoseconds.
|
||||
fn nanos_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents microseconds.
|
||||
fn micros_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents milliseconds.
|
||||
fn millis_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents seconds.
|
||||
fn secs_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents minutes.
|
||||
fn minutes_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents hours.
|
||||
fn hours_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
}
|
||||
|
||||
impl ExtU64Ceil for u64 {
|
||||
#[inline]
|
||||
fn nanos_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::nanos_at_least(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn micros_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::micros_at_least(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn millis_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::millis_at_least(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn secs_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::secs_at_least(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn minutes_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::minutes_at_least(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hours_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::hours_at_least(self)
|
||||
}
|
||||
}
|
||||
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
/// Needed due to not being allowed to call const-fn in `PartialEq` fo some reasion
|
||||
/// get the error:
|
||||
///
|
||||
/// ```console
|
||||
/// error[E0401]: can't use generic parameters from outer function
|
||||
/// --> src/main.rs:25:47
|
||||
/// |
|
||||
/// 21 | impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
/// | ------- const parameter from outer function
|
||||
/// ...
|
||||
/// 25 | const TEST: u32 = gcd_binary_u32(L_DENOM, R_DENOM);
|
||||
/// | ^^^^^^^ use of generic parameter from outer function
|
||||
///
|
||||
/// For more information about this error, try `rustc --explain E0401`
|
||||
/// ```
|
||||
pub struct Helpers<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>;
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
Helpers<L_NOM, L_DENOM, R_NOM, R_DENOM>
|
||||
{
|
||||
/// Helper constants generated at compile time
|
||||
pub const DIVISOR: u64 =
|
||||
gcd::binary_u64(L_DENOM as u64 * R_NOM as u64, R_DENOM as u64 * L_NOM as u64);
|
||||
|
||||
/// Helper constants generated at compile time
|
||||
pub const DIVISOR_2: u64 =
|
||||
gcd::binary_u64(L_NOM as u64 * R_NOM as u64, R_DENOM as u64 * L_DENOM as u64);
|
||||
|
||||
/// Helper constants generated at compile time for Durations
|
||||
pub const RD_TIMES_LN: u64 = (R_DENOM as u64 * L_NOM as u64) / Self::DIVISOR;
|
||||
|
||||
/// Helper constants generated at compile time
|
||||
pub const LD_TIMES_RN: u64 = (L_DENOM as u64 * R_NOM as u64) / Self::DIVISOR;
|
||||
|
||||
/// Helper constants generated at compile time for Rates
|
||||
pub const LN_TIMES_RN: u64 = (L_NOM as u64 * R_NOM as u64) / Self::DIVISOR_2;
|
||||
|
||||
/// Helper constants generated at compile time for Rates
|
||||
pub const RD_TIMES_LD: u64 = (R_DENOM as u64 * L_DENOM as u64) / Self::DIVISOR_2;
|
||||
|
||||
/// Helper constants generated at compile time for Rates
|
||||
pub const RATE_TO_DURATION_NUMERATOR: u64 = Self::RD_TIMES_LD / Self::LN_TIMES_RN;
|
||||
|
||||
/// Helper constants generated at compile time
|
||||
pub const SAME_BASE: bool = Self::LD_TIMES_RN == Self::RD_TIMES_LN;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[allow(path_statements)]
|
||||
pub(crate) const fn greater_than_0<const N: u32>() {
|
||||
Assert::<N, 0>::GREATER;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// Const assert hack
|
||||
pub struct Assert<const L: u32, const R: u32>;
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl<const L: u32, const R: u32> Assert<L, R> {
|
||||
/// Const assert hack
|
||||
pub const GREATER_EQ: () = assert!(L >= R);
|
||||
|
||||
/// Const assert hack
|
||||
pub const LESS_EQ: () = assert!(L <= R);
|
||||
|
||||
/// Const assert hack
|
||||
pub const NOT_EQ: () = assert!(L != R);
|
||||
|
||||
/// Const assert hack
|
||||
pub const EQ: () = assert!(L == R);
|
||||
|
||||
/// Const assert hack
|
||||
pub const GREATER: () = assert!(L > R);
|
||||
|
||||
/// Const assert hack
|
||||
pub const LESS: () = assert!(L < R);
|
||||
|
||||
/// Const assert hack
|
||||
pub const POWER_OF_TWO: () = assert!(L.is_power_of_two());
|
||||
}
|
||||
Vendored
+442
@@ -0,0 +1,442 @@
|
||||
use crate::duration::Duration;
|
||||
use crate::helpers::{self, Helpers};
|
||||
use core::cmp::Ordering;
|
||||
use core::ops;
|
||||
|
||||
/// Represents an instant in time.
|
||||
///
|
||||
/// The generic `T` can either be `u32` or `u64`, and the const generics represent the ratio of the
|
||||
/// ticks contained within the instant: `instant in seconds = NOM / DENOM * ticks`
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(
|
||||
feature = "postcard_max_size",
|
||||
derive(postcard::experimental::max_size::MaxSize)
|
||||
)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Instant<T, const NOM: u32, const DENOM: u32> {
|
||||
ticks: T,
|
||||
}
|
||||
|
||||
macro_rules! impl_instant_for_integer {
|
||||
($i:ty) => {
|
||||
impl<const NOM: u32, const DENOM: u32> Instant<$i, NOM, DENOM> {
|
||||
/// Create an `Instant` from a ticks value.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let _i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn from_ticks(ticks: $i) -> Self {
|
||||
helpers::greater_than_0::<NOM>();
|
||||
helpers::greater_than_0::<DENOM>();
|
||||
|
||||
Instant { ticks }
|
||||
}
|
||||
|
||||
/// Extract the ticks from an `Instant`.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(234);")]
|
||||
///
|
||||
/// assert_eq!(i.ticks(), 234);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn ticks(&self) -> $i {
|
||||
self.ticks
|
||||
}
|
||||
|
||||
/// Const comparison of `Instant`s.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let i1 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
#[doc = concat!("let i2 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
|
||||
///
|
||||
/// assert_eq!(i1.const_cmp(i2), core::cmp::Ordering::Less);
|
||||
/// ```
|
||||
///
|
||||
/// This function takes into account that ticks might wrap around. If the absolute
|
||||
/// values of `self` and `other` differ by more than half the possible range, it is
|
||||
/// assumed that an overflow occured and the result is reversed:
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let i1 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(", stringify!($i),"::MAX);")]
|
||||
#[doc = concat!("let i2 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
///
|
||||
/// assert_eq!(i1.const_cmp(i2), core::cmp::Ordering::Less);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn const_cmp(self, other: Self) -> Ordering {
|
||||
if self.ticks == other.ticks {
|
||||
Ordering::Equal
|
||||
} else {
|
||||
let v = self.ticks.wrapping_sub(other.ticks);
|
||||
|
||||
// not using `v.cmp(<$i>::MAX / 2).reverse()` due to `cmp` being non-const
|
||||
if v > <$i>::MAX / 2 {
|
||||
Ordering::Less
|
||||
} else if v < <$i>::MAX / 2 {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Duration between since the start of the `Instant`. This assumes an instant which
|
||||
/// won't wrap within the execution of the program.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(11);")]
|
||||
///
|
||||
/// assert_eq!(i.duration_since_epoch().ticks(), 11);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn duration_since_epoch(self) -> Duration<$i, NOM, DENOM> {
|
||||
Duration::<$i, NOM, DENOM>::from_ticks(self.ticks())
|
||||
}
|
||||
|
||||
/// Duration between `Instant`s.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let i1 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
#[doc = concat!("let i2 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
|
||||
///
|
||||
/// assert_eq!(i1.checked_duration_since(i2), None);
|
||||
/// assert_eq!(i2.checked_duration_since(i1).unwrap().ticks(), 1);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn checked_duration_since(
|
||||
self,
|
||||
other: Self,
|
||||
) -> Option<Duration<$i, NOM, DENOM>> {
|
||||
match self.const_cmp(other) {
|
||||
Ordering::Greater | Ordering::Equal => {
|
||||
Some(Duration::<$i, NOM, DENOM>::from_ticks(
|
||||
self.ticks.wrapping_sub(other.ticks),
|
||||
))
|
||||
}
|
||||
Ordering::Less => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Subtract a `Duration` from an `Instant` while checking for overflow.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
#[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
///
|
||||
/// assert_eq!(i.checked_sub_duration(d).unwrap().ticks(), 0);
|
||||
/// ```
|
||||
pub const fn checked_sub_duration<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
other: Duration<$i, O_NOM, O_DENOM>,
|
||||
) -> Option<Self> {
|
||||
if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
|
||||
Some(Instant::<$i, NOM, DENOM>::from_ticks(
|
||||
self.ticks.wrapping_sub(other.ticks()),
|
||||
))
|
||||
} else {
|
||||
if let Some(lh) = other
|
||||
.ticks()
|
||||
.checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
|
||||
{
|
||||
let ticks = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
|
||||
|
||||
Some(Instant::<$i, NOM, DENOM>::from_ticks(
|
||||
self.ticks.wrapping_sub(ticks),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a `Duration` to an `Instant` while checking for overflow.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
#[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
///
|
||||
/// assert_eq!(i.checked_add_duration(d).unwrap().ticks(), 2);
|
||||
/// ```
|
||||
pub const fn checked_add_duration<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
other: Duration<$i, O_NOM, O_DENOM>,
|
||||
) -> Option<Self> {
|
||||
if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
|
||||
Some(Instant::<$i, NOM, DENOM>::from_ticks(
|
||||
self.ticks.wrapping_add(other.ticks()),
|
||||
))
|
||||
} else {
|
||||
if let Some(lh) = other
|
||||
.ticks()
|
||||
.checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
|
||||
{
|
||||
let ticks = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
|
||||
|
||||
Some(Instant::<$i, NOM, DENOM>::from_ticks(
|
||||
self.ticks.wrapping_add(ticks),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> PartialOrd for Instant<$i, NOM, DENOM> {
|
||||
/// This implementation deviates from the definition of
|
||||
/// [PartialOrd::partial_cmp](core::cmp::PartialOrd::partial_cmp):
|
||||
///
|
||||
/// It takes into account that ticks might wrap around. If the absolute
|
||||
/// values of `self` and `other` differ by more than half the possible range, it is
|
||||
/// assumed that an overflow occured and the result is reversed.
|
||||
///
|
||||
/// That breaks the transitivity invariant: a < b and b < c no longer implies a < c.
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.const_cmp(*other))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> Ord for Instant<$i, NOM, DENOM> {
|
||||
/// This implementation deviates from the definition of
|
||||
/// [Ord::cmp](core::cmp::Ord::cmp):
|
||||
///
|
||||
/// It takes into account that ticks might wrap around. If the absolute
|
||||
/// values of `self` and `other` differ by more than half the possible range, it is
|
||||
/// assumed that an overflow occured and the result is reversed.
|
||||
///
|
||||
/// That breaks the transitivity invariant: a < b and b < c no longer implies a < c.
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.const_cmp(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> PartialEq for Instant<$i, NOM, DENOM> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.ticks.eq(&other.ticks)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> Eq for Instant<$i, NOM, DENOM> {}
|
||||
|
||||
// Instant - Instant = Duration
|
||||
// We have limited this to use same numerator and denominator in both left and right hand sides,
|
||||
// this allows for the extension traits to work. For usage with different fraction, use
|
||||
// `checked_duration_since`.
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Sub<Instant<$i, NOM, DENOM>>
|
||||
for Instant<$i, NOM, DENOM>
|
||||
{
|
||||
type Output = Duration<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: Self) -> Self::Output {
|
||||
if let Some(v) = self.checked_duration_since(other) {
|
||||
v
|
||||
} else {
|
||||
panic!("Sub failed! Other > self");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Instant - Duration = Instant
|
||||
// We have limited this to use same numerator and denominator in both left and right hand sides,
|
||||
// this allows for the extension traits to work. For usage with different fraction, use
|
||||
// `checked_sub_duration`.
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Sub<Duration<$i, NOM, DENOM>>
|
||||
for Instant<$i, NOM, DENOM>
|
||||
{
|
||||
type Output = Instant<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: Duration<$i, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) = self.checked_sub_duration(other) {
|
||||
v
|
||||
} else {
|
||||
panic!("Sub failed! Overflow");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Instant -= Duration
|
||||
// We have limited this to use same numerator and denominator in both left and right hand sides,
|
||||
// this allows for the extension traits to work. For usage with different fraction, use
|
||||
// `checked_sub_duration`.
|
||||
impl<const NOM: u32, const DENOM: u32> ops::SubAssign<Duration<$i, NOM, DENOM>>
|
||||
for Instant<$i, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: Duration<$i, NOM, DENOM>) {
|
||||
*self = *self - other;
|
||||
}
|
||||
}
|
||||
|
||||
// Instant + Duration = Instant
|
||||
// We have limited this to use same numerator and denominator in both left and right hand sides,
|
||||
// this allows for the extension traits to work. For usage with different fraction, use
|
||||
// `checked_add_duration`.
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Add<Duration<$i, NOM, DENOM>>
|
||||
for Instant<$i, NOM, DENOM>
|
||||
{
|
||||
type Output = Instant<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: Duration<$i, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) = self.checked_add_duration(other) {
|
||||
v
|
||||
} else {
|
||||
panic!("Add failed! Overflow");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Instant += Duration
|
||||
// We have limited this to use same numerator and denominator in both left and right hand sides,
|
||||
// this allows for the extension traits to work. For usage with different fraction, use
|
||||
// `checked_add_duration`.
|
||||
impl<const NOM: u32, const DENOM: u32> ops::AddAssign<Duration<$i, NOM, DENOM>>
|
||||
for Instant<$i, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: Duration<$i, NOM, DENOM>) {
|
||||
*self = *self + other;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
impl<const NOM: u32, const DENOM: u32> defmt::Format for Instant<$i, NOM, DENOM> {
|
||||
fn format(&self, f: defmt::Formatter) {
|
||||
if NOM == 3_600 && DENOM == 1 {
|
||||
defmt::write!(f, "{} h", self.ticks)
|
||||
} else if NOM == 60 && DENOM == 1 {
|
||||
defmt::write!(f, "{} min", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1 {
|
||||
defmt::write!(f, "{} s", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000 {
|
||||
defmt::write!(f, "{} ms", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000_000 {
|
||||
defmt::write!(f, "{} us", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000_000_000 {
|
||||
defmt::write!(f, "{} ns", self.ticks)
|
||||
} else {
|
||||
defmt::write!(f, "{} ticks @ ({}/{})", self.ticks, NOM, DENOM)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> core::fmt::Display for Instant<$i, NOM, DENOM> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
if NOM == 3_600 && DENOM == 1 {
|
||||
write!(f, "{} h", self.ticks)
|
||||
} else if NOM == 60 && DENOM == 1 {
|
||||
write!(f, "{} min", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1 {
|
||||
write!(f, "{} s", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000 {
|
||||
write!(f, "{} ms", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000_000 {
|
||||
write!(f, "{} us", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000_000_000 {
|
||||
write!(f, "{} ns", self.ticks)
|
||||
} else {
|
||||
write!(f, "{} ticks @ ({}/{})", self.ticks, NOM, DENOM)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_instant_for_integer!(u32);
|
||||
impl_instant_for_integer!(u64);
|
||||
|
||||
//
|
||||
// Operations between u32 Duration and u64 Instant
|
||||
//
|
||||
|
||||
// Instant - Duration = Instant
|
||||
// We have limited this to use same numerator and denominator in both left and right hand sides,
|
||||
// this allows for the extension traits to work. For usage with different fraction, use
|
||||
// `checked_sub_duration`.
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Sub<Duration<u32, NOM, DENOM>>
|
||||
for Instant<u64, NOM, DENOM>
|
||||
{
|
||||
type Output = Instant<u64, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: Duration<u32, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) = self.checked_sub_duration(other.into()) {
|
||||
v
|
||||
} else {
|
||||
panic!("Sub failed! Overflow");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Instant -= Duration
|
||||
// We have limited this to use same numerator and denominator in both left and right hand sides,
|
||||
// this allows for the extension traits to work. For usage with different fraction, use
|
||||
// `checked_sub_duration`.
|
||||
impl<const NOM: u32, const DENOM: u32> ops::SubAssign<Duration<u32, NOM, DENOM>>
|
||||
for Instant<u64, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: Duration<u32, NOM, DENOM>) {
|
||||
*self = *self - other;
|
||||
}
|
||||
}
|
||||
|
||||
// Instant + Duration = Instant
|
||||
// We have limited this to use same numerator and denominator in both left and right hand sides,
|
||||
// this allows for the extension traits to work. For usage with different fraction, use
|
||||
// `checked_add_duration`.
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Add<Duration<u32, NOM, DENOM>>
|
||||
for Instant<u64, NOM, DENOM>
|
||||
{
|
||||
type Output = Instant<u64, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: Duration<u32, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) = self.checked_add_duration(other.into()) {
|
||||
v
|
||||
} else {
|
||||
panic!("Add failed! Overflow");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Instant += Duration
|
||||
// We have limited this to use same numerator and denominator in both left and right hand sides,
|
||||
// this allows for the extension traits to work. For usage with different fraction, use
|
||||
// `checked_add_duration`.
|
||||
impl<const NOM: u32, const DENOM: u32> ops::AddAssign<Duration<u32, NOM, DENOM>>
|
||||
for Instant<u64, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: Duration<u32, NOM, DENOM>) {
|
||||
*self = *self + other;
|
||||
}
|
||||
}
|
||||
|
||||
// impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
// ops::Add<Duration<u32, R_NOM, R_DENOM>> for Duration<u64, L_NOM, L_DENOM>
|
||||
// {
|
||||
// type Output = Duration<u64, L_NOM, L_DENOM>;
|
||||
//
|
||||
// #[inline]
|
||||
// fn add(self, other: Duration<u32, R_NOM, R_DENOM>) -> Self::Output {
|
||||
// self.add(Duration::<u64, L_NOM, L_DENOM>::from_ticks(
|
||||
// other.ticks() as u64
|
||||
// ))
|
||||
// }
|
||||
// }
|
||||
Vendored
+1348
File diff suppressed because it is too large.
Load diff
Vendored
+773
@@ -0,0 +1,773 @@
|
||||
use crate::helpers::{self, Helpers};
|
||||
use crate::Duration;
|
||||
use core::cmp::Ordering;
|
||||
use core::convert;
|
||||
use core::ops;
|
||||
|
||||
/// Represents a frequency.
|
||||
///
|
||||
/// The generic `T` can either be `u32` or `u64`, and the const generics represent the ratio of the
|
||||
/// raw contained within the rate: `rate in Hz = NOM / DENOM * raw`
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(
|
||||
feature = "postcard_max_size",
|
||||
derive(postcard::experimental::max_size::MaxSize)
|
||||
)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Rate<T, const NOM: u32, const DENOM: u32> {
|
||||
pub(crate) raw: T,
|
||||
}
|
||||
|
||||
macro_rules! impl_rate_for_integer {
|
||||
($i:ty) => {
|
||||
impl<const NOM: u32, const DENOM: u32> Rate<$i, NOM, DENOM> {
|
||||
/// Create a `Rate` from a raw value.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let _d = Rate::<", stringify!($i), ", 1, 1_000>::from_raw(1);")]
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn from_raw(raw: $i) -> Self {
|
||||
helpers::greater_than_0::<NOM>();
|
||||
helpers::greater_than_0::<DENOM>();
|
||||
|
||||
Rate { raw }
|
||||
}
|
||||
|
||||
/// Extract the raw value from a `Rate`.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d = Rate::<", stringify!($i), ", 1, 1_000>::from_raw(234);")]
|
||||
///
|
||||
/// assert_eq!(d.raw(), 234);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn raw(&self) -> $i {
|
||||
self.raw
|
||||
}
|
||||
|
||||
/// Add two rates while checking for overflow.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 1_000>::from_raw(1);")]
|
||||
#[doc = concat!("let r2 = Rate::<", stringify!($i), ", 1, 1_000>::from_raw(2);")]
|
||||
#[doc = concat!("let r3 = Rate::<", stringify!($i), ", 1, 1_000>::from_raw(", stringify!($i), "::MAX);")]
|
||||
///
|
||||
/// assert_eq!(r1.checked_add(r2).unwrap().raw(), 3);
|
||||
/// assert_eq!(r1.checked_add(r3), None);
|
||||
/// ```
|
||||
pub const fn checked_add<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
other: Rate<$i, O_NOM, O_DENOM>,
|
||||
) -> Option<Self> {
|
||||
if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
|
||||
if let Some(raw) = self.raw.checked_add(other.raw) {
|
||||
Some(Rate::<$i, NOM, DENOM>::from_raw(raw))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
if let Some(lh) = other
|
||||
.raw
|
||||
.checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
|
||||
{
|
||||
let raw = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
|
||||
|
||||
if let Some(raw) = self.raw.checked_add(raw) {
|
||||
Some(Rate::<$i, NOM, DENOM>::from_raw(raw))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Subtract two rates while checking for overflow.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 1_000>::from_raw(1);")]
|
||||
#[doc = concat!("let r2 = Rate::<", stringify!($i), ", 1, 1_000>::from_raw(2);")]
|
||||
#[doc = concat!("let r3 = Rate::<", stringify!($i), ", 1, 1_000>::from_raw(", stringify!($i), "::MAX);")]
|
||||
///
|
||||
/// assert_eq!(r2.checked_sub(r1).unwrap().raw(), 1);
|
||||
/// assert_eq!(r1.checked_sub(r3), None);
|
||||
/// ```
|
||||
pub const fn checked_sub<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
other: Rate<$i, O_NOM, O_DENOM>,
|
||||
) -> Option<Self> {
|
||||
if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
|
||||
if let Some(raw) = self.raw.checked_sub(other.raw) {
|
||||
Some(Rate::<$i, NOM, DENOM>::from_raw(raw))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
if let Some(lh) = other
|
||||
.raw
|
||||
.checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
|
||||
{
|
||||
let raw = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
|
||||
|
||||
if let Some(raw) = self.raw.checked_sub(raw) {
|
||||
Some(Rate::<$i, NOM, DENOM>::from_raw(raw))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = concat!("Const `cmp` for ", stringify!($i))]
|
||||
#[inline(always)]
|
||||
const fn _const_cmp(a: $i, b: $i) -> Ordering {
|
||||
if a < b {
|
||||
Ordering::Less
|
||||
} else if a > b {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
/// Const partial comparison.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 1_00>::from_raw(1);")]
|
||||
#[doc = concat!("let r2 = Rate::<", stringify!($i), ", 1, 1_000>::from_raw(1);")]
|
||||
///
|
||||
/// assert_eq!(r1.const_partial_cmp(r2), Some(core::cmp::Ordering::Greater));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn const_partial_cmp<const R_NOM: u32, const R_DENOM: u32>(
|
||||
self,
|
||||
other: Rate<$i, R_NOM, R_DENOM>
|
||||
) -> Option<Ordering> {
|
||||
if Helpers::<NOM, DENOM, R_NOM, R_DENOM>::SAME_BASE {
|
||||
// If we are in the same base, comparison in trivial
|
||||
Some(Self::_const_cmp(self.raw, other.raw))
|
||||
} else {
|
||||
let lh = self
|
||||
.raw
|
||||
.checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::RD_TIMES_LN as $i);
|
||||
let rh = other
|
||||
.raw
|
||||
.checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::LD_TIMES_RN as $i);
|
||||
|
||||
if let (Some(lh), Some(rh)) = (lh, rh) {
|
||||
Some(Self::_const_cmp(lh, rh))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Const equality check.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 1_00>::from_raw(1);")]
|
||||
#[doc = concat!("let r2 = Rate::<", stringify!($i), ", 1, 1_000>::from_raw(10);")]
|
||||
///
|
||||
/// assert!(r1.const_eq(r2));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn const_eq<const R_NOM: u32, const R_DENOM: u32>(
|
||||
self,
|
||||
other: Rate<$i, R_NOM, R_DENOM>
|
||||
) -> bool {
|
||||
if Helpers::<NOM, DENOM, R_NOM, R_DENOM>::SAME_BASE {
|
||||
// If we are in the same base, comparison in trivial
|
||||
self.raw == other.raw
|
||||
} else {
|
||||
let lh = self
|
||||
.raw
|
||||
.checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::RD_TIMES_LN as $i);
|
||||
let rh = other
|
||||
.raw
|
||||
.checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::LD_TIMES_RN as $i);
|
||||
|
||||
if let (Some(lh), Some(rh)) = (lh, rh) {
|
||||
lh == rh
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Const try from, checking for overflow.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 1_00>::from_raw(1);")]
|
||||
#[doc = concat!("let r2 = Rate::<", stringify!($i), ", 1, 1_000>::const_try_from(r1);")]
|
||||
///
|
||||
/// assert_eq!(r2.unwrap().raw(), 10);
|
||||
/// ```
|
||||
pub const fn const_try_from<const I_NOM: u32, const I_DENOM: u32>(
|
||||
rate: Rate<$i, I_NOM, I_DENOM>,
|
||||
) -> Option<Self> {
|
||||
if Helpers::<I_NOM, I_DENOM, NOM, DENOM>::SAME_BASE {
|
||||
Some(Self::from_raw(rate.raw))
|
||||
} else {
|
||||
if let Some(lh) = (rate.raw as u64)
|
||||
.checked_mul(Helpers::<I_NOM, I_DENOM, NOM, DENOM>::RD_TIMES_LN)
|
||||
{
|
||||
let raw = lh / Helpers::<I_NOM, I_DENOM, NOM, DENOM>::LD_TIMES_RN;
|
||||
|
||||
if raw <= <$i>::MAX as u64 {
|
||||
Some(Self::from_raw(raw as $i))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Const try into, checking for overflow.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 1_00>::from_raw(1);")]
|
||||
#[doc = concat!("let r2: Option<Rate::<", stringify!($i), ", 1, 1_000>> = r1.const_try_into();")]
|
||||
///
|
||||
/// assert_eq!(r2.unwrap().raw(), 10);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn const_try_into<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
) -> Option<Rate<$i, O_NOM, O_DENOM>> {
|
||||
Rate::<$i, O_NOM, O_DENOM>::const_try_from(self)
|
||||
}
|
||||
|
||||
/// Const try into duration, checking for divide-by-zero.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 1>::from_raw(1);")]
|
||||
#[doc = concat!("let d1: Option<Duration::<", stringify!($i), ", 1, 1_000>> = r1.try_into_duration();")]
|
||||
///
|
||||
/// assert_eq!(d1.unwrap().ticks(), 1_000);
|
||||
/// ```
|
||||
pub const fn try_into_duration<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
) -> Option<Duration<$i, O_NOM, O_DENOM>> {
|
||||
Duration::<$i, O_NOM, O_DENOM>::try_from_rate(self)
|
||||
}
|
||||
|
||||
/// Convert from rate to duration.
|
||||
pub const fn into_duration<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
) -> Duration<$i, O_NOM, O_DENOM> {
|
||||
if let Some(v) = self.try_into_duration() {
|
||||
v
|
||||
} else {
|
||||
panic!("Into duration failed, divide-by-zero!");
|
||||
}
|
||||
}
|
||||
|
||||
/// Const try from duration, checking for divide-by-zero.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
|
||||
#[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 1>::try_from_duration(d1);")]
|
||||
///
|
||||
/// assert_eq!(r1.unwrap().raw(), 500);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn try_from_duration<const I_NOM: u32, const I_DENOM: u32>(
|
||||
duration: Duration<$i, I_NOM, I_DENOM>,
|
||||
) -> Option<Self> {
|
||||
if duration.ticks > 0 {
|
||||
Some(Self::from_raw(
|
||||
Helpers::<I_NOM, I_DENOM, NOM, DENOM>::RATE_TO_DURATION_NUMERATOR as $i
|
||||
/ duration.ticks
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert from duration to rate.
|
||||
#[inline]
|
||||
pub const fn from_duration<const I_NOM: u32, const I_DENOM: u32>(
|
||||
duration: Duration<$i, I_NOM, I_DENOM>,
|
||||
) -> Self {
|
||||
if let Some(v) = Self::try_from_duration(duration) {
|
||||
v
|
||||
} else {
|
||||
panic!("From duration failed, divide-by-zero!");
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert between bases for a rate.
|
||||
///
|
||||
/// Unfortunately not a `From` impl due to collision with the std lib.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 100>::from_raw(1);")]
|
||||
#[doc = concat!("let r2: Rate::<", stringify!($i), ", 1, 1_000> = r1.convert();")]
|
||||
///
|
||||
/// assert_eq!(r2.raw(), 10);
|
||||
/// ```
|
||||
///
|
||||
/// Can be used in const contexts. Compilation will fail if the conversion causes overflow
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("const RAW: ", stringify!($i), "= ", stringify!($i), "::MAX - 10;")]
|
||||
#[doc = concat!("const R1: Rate::<", stringify!($i), ", 1, 100> = Rate::<", stringify!($i), ", 1, 100>::from_raw(RAW);")]
|
||||
/// // Fails conversion due to overflow
|
||||
#[doc = concat!("const R2: Rate::<", stringify!($i), ", 1, 200> = R1.convert();")]
|
||||
/// ```
|
||||
pub const fn convert<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
) -> Rate<$i, O_NOM, O_DENOM> {
|
||||
if let Some(v) = self.const_try_into() {
|
||||
v
|
||||
} else {
|
||||
panic!("Convert failed!");
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the Rate to an interger number of Hz.
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
pub const fn to_Hz(&self) -> $i {
|
||||
(Helpers::<1, 1, NOM, DENOM>::LD_TIMES_RN as $i * self.raw)
|
||||
/ Helpers::<1, 1, NOM, DENOM>::RD_TIMES_LN as $i
|
||||
}
|
||||
|
||||
/// Convert the Rate to an interger number of kHz.
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
pub const fn to_kHz(&self) -> $i {
|
||||
(Helpers::<1_000, 1, NOM, DENOM>::LD_TIMES_RN as $i * self.raw)
|
||||
/ Helpers::<1_000, 1, NOM, DENOM>::RD_TIMES_LN as $i
|
||||
}
|
||||
|
||||
/// Convert the Rate to an interger number of MHz.
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
pub const fn to_MHz(&self) -> $i {
|
||||
(Helpers::<1_000_000, 1, NOM, DENOM>::LD_TIMES_RN as $i * self.raw)
|
||||
/ Helpers::<1_000_000, 1, NOM, DENOM>::RD_TIMES_LN as $i
|
||||
}
|
||||
|
||||
/// Shorthand for creating a rate which represents hertz.
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
pub const fn Hz(val: $i) -> Self {
|
||||
Self::from_raw(
|
||||
(Helpers::<1, 1, NOM, DENOM>::RD_TIMES_LN as $i * val)
|
||||
/ Helpers::<1, 1, NOM, DENOM>::LD_TIMES_RN as $i,
|
||||
)
|
||||
}
|
||||
|
||||
/// Shorthand for creating a rate which represents kilohertz.
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
pub const fn kHz(val: $i) -> Self {
|
||||
Self::from_raw(
|
||||
(Helpers::<1_000, 1, NOM, DENOM>::RD_TIMES_LN as $i * val)
|
||||
/ Helpers::<1_000, 1, NOM, DENOM>::LD_TIMES_RN as $i,
|
||||
)
|
||||
}
|
||||
|
||||
/// Shorthand for creating a rate which represents megahertz.
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
pub const fn MHz(val: $i) -> Self {
|
||||
Self::from_raw(
|
||||
(Helpers::<1_000_000, 1, NOM, DENOM>::RD_TIMES_LN as $i * val)
|
||||
/ Helpers::<1_000_000, 1, NOM, DENOM>::LD_TIMES_RN as $i,
|
||||
)
|
||||
}
|
||||
|
||||
/// Shorthand for creating a rate which represents nanoseconds.
|
||||
#[inline]
|
||||
pub const fn nanos(val: $i) -> Self {
|
||||
Self::from_duration(crate::Duration::<$i, 1, 1_000_000_000>::from_ticks(val))
|
||||
}
|
||||
|
||||
/// Shorthand for creating a rate which represents microseconds.
|
||||
#[inline]
|
||||
pub const fn micros(val: $i) -> Self {
|
||||
Self::from_duration(crate::Duration::<$i, 1, 1_000_000>::from_ticks(val))
|
||||
}
|
||||
|
||||
/// Shorthand for creating a rate which represents milliseconds.
|
||||
#[inline]
|
||||
pub const fn millis(val: $i) -> Self {
|
||||
Self::from_duration(crate::Duration::<$i, 1, 1_000>::from_ticks(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialOrd<Rate<$i, R_NOM, R_DENOM>> for Rate<$i, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Rate<$i, R_NOM, R_DENOM>) -> Option<Ordering> {
|
||||
self.const_partial_cmp(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> Ord for Rate<$i, NOM, DENOM> {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
Self::_const_cmp(self.raw, other.raw)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialEq<Rate<$i, R_NOM, R_DENOM>> for Rate<$i, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &Rate<$i, R_NOM, R_DENOM>) -> bool {
|
||||
self.const_eq(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> Eq for Rate<$i, NOM, DENOM> {}
|
||||
|
||||
// Rate - Rate = Rate (only same base until const_generics_defaults is
|
||||
// stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Sub<Rate<$i, NOM, DENOM>>
|
||||
for Rate<$i, NOM, DENOM>
|
||||
{
|
||||
type Output = Rate<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: Rate<$i, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) = self.checked_sub(other) {
|
||||
v
|
||||
} else {
|
||||
panic!("Sub failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rate + Rate = Rate (only same base until const_generics_defaults is
|
||||
// stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Add<Rate<$i, NOM, DENOM>>
|
||||
for Rate<$i, NOM, DENOM>
|
||||
{
|
||||
type Output = Rate<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: Rate<$i, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) = self.checked_add(other) {
|
||||
v
|
||||
} else {
|
||||
panic!("Add failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rate += Rate
|
||||
impl<const NOM: u32, const DENOM: u32> ops::AddAssign<Rate<$i, NOM, DENOM>>
|
||||
for Rate<$i, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
*self = *self + other;
|
||||
}
|
||||
}
|
||||
|
||||
// integer * Rate = Rate
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Mul<Rate<$i, NOM, DENOM>> for u32 {
|
||||
type Output = Rate<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, mut other: Rate<$i, NOM, DENOM>) -> Self::Output {
|
||||
other.raw *= self as $i;
|
||||
other
|
||||
}
|
||||
}
|
||||
|
||||
// Rate * integer = Rate
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Mul<u32> for Rate<$i, NOM, DENOM> {
|
||||
type Output = Rate<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn mul(mut self, other: u32) -> Self::Output {
|
||||
self.raw *= other as $i;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// Rate *= integer
|
||||
impl<const NOM: u32, const DENOM: u32> ops::MulAssign<u32>
|
||||
for Rate<$i, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: u32) {
|
||||
*self = *self * other;
|
||||
}
|
||||
}
|
||||
|
||||
// Rate / integer = Rate
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Div<u32> for Rate<$i, NOM, DENOM> {
|
||||
type Output = Rate<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn div(mut self, other: u32) -> Self::Output {
|
||||
self.raw /= other as $i;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// Rate / Rate = integer
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32> ops::Div<Rate<$i, R_NOM, R_DENOM>>
|
||||
for Rate<$i, L_NOM, L_DENOM>
|
||||
{
|
||||
type Output = $i;
|
||||
|
||||
#[inline]
|
||||
fn div(self, other: Rate<$i, R_NOM, R_DENOM>) -> Self::Output {
|
||||
let conv: Rate<$i, R_NOM, R_DENOM> = self.convert();
|
||||
conv.raw / other.raw
|
||||
}
|
||||
}
|
||||
|
||||
// Rate /= integer
|
||||
impl<const NOM: u32, const DENOM: u32> ops::DivAssign<u32>
|
||||
for Rate<$i, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn div_assign(&mut self, other: u32) {
|
||||
*self = *self / other;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
impl<const NOM: u32, const DENOM: u32> defmt::Format for Rate<$i, NOM, DENOM>
|
||||
{
|
||||
fn format(&self, f: defmt::Formatter) {
|
||||
if NOM == 1 && DENOM == 1 {
|
||||
defmt::write!(f, "{} Hz", self.raw)
|
||||
} else if NOM == 1_000 && DENOM == 1 {
|
||||
defmt::write!(f, "{} kHz", self.raw)
|
||||
} else if NOM == 1_000_000 && DENOM == 1 {
|
||||
defmt::write!(f, "{} MHz", self.raw)
|
||||
} else if NOM == 1_000_000_000 && DENOM == 1 {
|
||||
defmt::write!(f, "{} GHz", self.raw)
|
||||
} else {
|
||||
defmt::write!(f, "{} raw @ ({}/{})", self.raw, NOM, DENOM)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> core::fmt::Display for Rate<$i, NOM, DENOM> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
if NOM == 1 && DENOM == 1 {
|
||||
write!(f, "{} Hz", self.raw)
|
||||
} else if NOM == 1_000 && DENOM == 1 {
|
||||
write!(f, "{} kHz", self.raw)
|
||||
} else if NOM == 1_000_000 && DENOM == 1 {
|
||||
write!(f, "{} MHz", self.raw)
|
||||
} else if NOM == 1_000_000_000 && DENOM == 1 {
|
||||
write!(f, "{} GHz", self.raw)
|
||||
} else {
|
||||
write!(f, "{} raw @ ({}/{})", self.raw, NOM, DENOM)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_rate_for_integer!(u32);
|
||||
impl_rate_for_integer!(u64);
|
||||
|
||||
//
|
||||
// Operations between u32 and u64 Rate
|
||||
//
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> From<Rate<u32, NOM, DENOM>> for Rate<u64, NOM, DENOM> {
|
||||
#[inline]
|
||||
fn from(val: Rate<u32, NOM, DENOM>) -> Rate<u64, NOM, DENOM> {
|
||||
Rate::<u64, NOM, DENOM>::from_raw(val.raw() as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> convert::TryFrom<Rate<u64, NOM, DENOM>>
|
||||
for Rate<u32, NOM, DENOM>
|
||||
{
|
||||
type Error = ();
|
||||
|
||||
#[inline]
|
||||
fn try_from(val: Rate<u64, NOM, DENOM>) -> Result<Rate<u32, NOM, DENOM>, ()> {
|
||||
Ok(Rate::<u32, NOM, DENOM>::from_raw(
|
||||
val.raw().try_into().map_err(|_| ())?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// Rate - Rate = Rate (to make shorthands work, until const_generics_defaults is
|
||||
// stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Sub<Rate<u32, NOM, DENOM>> for Rate<u64, NOM, DENOM> {
|
||||
type Output = Rate<u64, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: Rate<u32, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) = self.checked_sub(Rate::<u64, NOM, DENOM>::from_raw(other.raw() as u64)) {
|
||||
v
|
||||
} else {
|
||||
panic!("Sub failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rate -= Rate (to make shorthands work, until const_generics_defaults is stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::SubAssign<Rate<u32, NOM, DENOM>>
|
||||
for Rate<u64, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: Rate<u32, NOM, DENOM>) {
|
||||
*self = *self - other;
|
||||
}
|
||||
}
|
||||
|
||||
// Rate + Rate = Rate (to make shorthands work, until const_generics_defaults is
|
||||
// stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Add<Rate<u32, NOM, DENOM>> for Rate<u64, NOM, DENOM> {
|
||||
type Output = Rate<u64, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: Rate<u32, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) = self.checked_add(Rate::<u64, NOM, DENOM>::from_raw(other.raw() as u64)) {
|
||||
v
|
||||
} else {
|
||||
panic!("Add failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rate += Rate (to make shorthands work, until const_generics_defaults is stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::AddAssign<Rate<u32, NOM, DENOM>>
|
||||
for Rate<u64, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: Rate<u32, NOM, DENOM>) {
|
||||
*self = *self + other;
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialOrd<Rate<u32, R_NOM, R_DENOM>> for Rate<u64, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Rate<u32, R_NOM, R_DENOM>) -> Option<Ordering> {
|
||||
self.partial_cmp(&Rate::<u64, R_NOM, R_DENOM>::from_raw(other.raw() as u64))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialEq<Rate<u32, R_NOM, R_DENOM>> for Rate<u64, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &Rate<u32, R_NOM, R_DENOM>) -> bool {
|
||||
self.eq(&Rate::<u64, R_NOM, R_DENOM>::from_raw(other.raw() as u64))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialOrd<Rate<u64, R_NOM, R_DENOM>> for Rate<u32, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Rate<u64, R_NOM, R_DENOM>) -> Option<Ordering> {
|
||||
Rate::<u64, L_NOM, L_DENOM>::from_raw(self.raw as u64).partial_cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialEq<Rate<u64, R_NOM, R_DENOM>> for Rate<u32, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &Rate<u64, R_NOM, R_DENOM>) -> bool {
|
||||
Rate::<u64, L_NOM, L_DENOM>::from_raw(self.raw as u64).eq(other)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait for simple short-hands for u32 Rate
|
||||
pub trait ExtU32 {
|
||||
/// Shorthand for creating a rate which represents hertz.
|
||||
#[allow(non_snake_case)]
|
||||
fn Hz<const NOM: u32, const DENOM: u32>(self) -> Rate<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a rate which represents kilohertz.
|
||||
#[allow(non_snake_case)]
|
||||
fn kHz<const NOM: u32, const DENOM: u32>(self) -> Rate<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a rate which represents megahertz.
|
||||
#[allow(non_snake_case)]
|
||||
fn MHz<const NOM: u32, const DENOM: u32>(self) -> Rate<u32, NOM, DENOM>;
|
||||
}
|
||||
|
||||
impl ExtU32 for u32 {
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
fn Hz<const NOM: u32, const DENOM: u32>(self) -> Rate<u32, NOM, DENOM> {
|
||||
Rate::<u32, NOM, DENOM>::Hz(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
fn kHz<const NOM: u32, const DENOM: u32>(self) -> Rate<u32, NOM, DENOM> {
|
||||
Rate::<u32, NOM, DENOM>::kHz(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
fn MHz<const NOM: u32, const DENOM: u32>(self) -> Rate<u32, NOM, DENOM> {
|
||||
Rate::<u32, NOM, DENOM>::MHz(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait for simple short-hands for u64 Rate
|
||||
pub trait ExtU64 {
|
||||
/// Shorthand for creating a rate which represents hertz.
|
||||
#[allow(non_snake_case)]
|
||||
fn Hz<const NOM: u32, const DENOM: u32>(self) -> Rate<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a rate which represents kilohertz.
|
||||
#[allow(non_snake_case)]
|
||||
fn kHz<const NOM: u32, const DENOM: u32>(self) -> Rate<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a rate which represents megahertz.
|
||||
#[allow(non_snake_case)]
|
||||
fn MHz<const NOM: u32, const DENOM: u32>(self) -> Rate<u64, NOM, DENOM>;
|
||||
}
|
||||
|
||||
impl ExtU64 for u64 {
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
fn Hz<const NOM: u32, const DENOM: u32>(self) -> Rate<u64, NOM, DENOM> {
|
||||
Rate::<u64, NOM, DENOM>::Hz(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
fn kHz<const NOM: u32, const DENOM: u32>(self) -> Rate<u64, NOM, DENOM> {
|
||||
Rate::<u64, NOM, DENOM>::kHz(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
fn MHz<const NOM: u32, const DENOM: u32>(self) -> Rate<u64, NOM, DENOM> {
|
||||
Rate::<u64, NOM, DENOM>::MHz(self)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user