pub const NANOS_PER_SECOND: u32 = 1_000_000_000;
pub const SUBSECONDS_PER_SECOND: u32 = 0x7FFF_FFFF;
pub const SUBSECONDS_TO_SECONDS: f32 = 1.0 / (SUBSECONDS_PER_SECOND as f32);
const NS_PER_S: u64 = NANOS_PER_SECOND as u64;
const SUBS_PER_S: u64 = SUBSECONDS_PER_SECOND as u64;
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Subseconds(u32);
impl Subseconds {
pub const MAX_VALUE: u32 = SUBSECONDS_PER_SECOND;
pub const MAX: Self = Self(SUBSECONDS_PER_SECOND);
pub const ZERO: Self = Self(0);
pub const fn new(value: u32) -> Option<Self> {
if value > SUBSECONDS_PER_SECOND as u32 {
None
} else {
Some(Self(value))
}
}
pub(crate) const fn new_unchecked(value: u32) -> Self {
Self(value)
}
pub const fn new_from_nanos(nanos: u32) -> Option<Self> {
if nanos >= NANOS_PER_SECOND as u32 {
return None;
}
let subseconds = ((nanos as u64 * SUBS_PER_S) + (NS_PER_S / 2)) / NS_PER_S;
Some(Subseconds::new_unchecked(subseconds as u32))
}
pub const fn nanos(&self) -> u32 {
let nanos = ((self.0 as u64 * NS_PER_S) + (SUBS_PER_S / 2)) / SUBS_PER_S;
nanos as u32
}
pub const fn raw(&self) -> u32 {
self.0
}
pub(crate) const fn hertz(&self) -> u32 {
SUBSECONDS_PER_SECOND as u32 / self.0
}
pub(crate) const fn nearest_increment(input_clk_hz: u32) -> Subseconds {
let hclk_half_subs = (SUBSECONDS_PER_SECOND as u32 + (input_clk_hz / 2)) / input_clk_hz;
Self::new_unchecked(hclk_half_subs)
}
}
impl core::ops::Add<Subseconds> for Subseconds {
type Output = Self;
fn add(self, rhs: Subseconds) -> Self::Output {
Self(self.0.wrapping_add(rhs.0) % (SUBSECONDS_PER_SECOND + 1))
}
}
impl core::ops::AddAssign<Subseconds> for Subseconds {
fn add_assign(&mut self, rhs: Subseconds) {
*self = *self + rhs;
}
}
impl core::ops::Sub<Subseconds> for Subseconds {
type Output = Self;
fn sub(self, rhs: Subseconds) -> Self::Output {
Self(self.0.wrapping_sub(rhs.0) % (SUBSECONDS_PER_SECOND + 1))
}
}
impl core::ops::SubAssign<Subseconds> for Subseconds {
fn sub_assign(&mut self, rhs: Subseconds) {
*self = *self - rhs;
}
}
#[cfg(all(test, not(target_os = "none")))]
mod test {
use super::*;
#[test]
fn correct_subsecond_increment() {
for i in (25_000..180_000).map(|v| v * 1_000) {
let subs = Subseconds::nearest_increment(i).raw();
assert!(subs > 0 && subs <= 255);
}
}
#[test]
fn from_nanos() {
for i in [0, 1, 2, 3, NANOS_PER_SECOND - 1] {
let subseconds = Subseconds::new_from_nanos(i).unwrap();
assert!(subseconds.raw() < SUBSECONDS_PER_SECOND);
}
assert!(Subseconds::new_from_nanos(NANOS_PER_SECOND).is_none());
assert!(Subseconds::new_from_nanos(u32::MAX).is_none());
}
#[test]
fn subsecond_math() {
let one = Subseconds::new(1).unwrap();
let two = Subseconds::new(2).unwrap();
let three = Subseconds::new(3).unwrap();
let max = Subseconds::new(SUBSECONDS_PER_SECOND).unwrap();
let zero = Subseconds::new(0).unwrap();
assert_eq!(one + two, three);
assert_eq!(two - one, one);
assert_eq!(one - max + max, one);
assert_eq!(one - two, max);
assert_eq!(one + max, zero);
assert_eq!(two + max, one);
}
}