#[derive(Debug, Clone, Copy, PartialEq)]
pub struct SnrFloor {
pub db: f32,
}
impl SnrFloor {
pub const fn new(db: f32) -> Self {
Self { db }
}
#[inline]
pub fn is_sub_threshold(&self, snr_db: f32) -> bool {
snr_db < self.db
}
}
impl Default for SnrFloor {
fn default() -> Self {
Self { db: -10.0 }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WaveformState {
Operational,
Transition,
PostTransitionGuard {
remaining: u16
},
Calibration,
TransmitInhibit,
}
impl WaveformState {
#[inline]
pub fn admissibility_multiplier(&self) -> f32 {
match self {
WaveformState::Operational => 1.0,
WaveformState::Transition => f32::INFINITY,
WaveformState::PostTransitionGuard { .. } => f32::INFINITY,
WaveformState::Calibration => f32::INFINITY,
WaveformState::TransmitInhibit => f32::INFINITY,
}
}
#[inline]
pub fn is_suppressed(&self) -> bool {
matches!(
self,
WaveformState::Transition
| WaveformState::PostTransitionGuard { .. }
| WaveformState::Calibration
| WaveformState::TransmitInhibit
)
}
#[must_use]
pub fn tick(self) -> Self {
match self {
WaveformState::PostTransitionGuard { remaining: 0 } => WaveformState::Operational,
WaveformState::PostTransitionGuard { remaining } => {
WaveformState::PostTransitionGuard { remaining: remaining - 1 }
}
other => other,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct PlatformContext {
pub snr_db: f32,
pub waveform_state: WaveformState,
pub post_transition_guard: u16,
}
impl PlatformContext {
pub const fn operational() -> Self {
Self {
snr_db: 20.0,
waveform_state: WaveformState::Operational,
post_transition_guard: 5,
}
}
pub const fn with_snr(snr_db: f32) -> Self {
Self {
snr_db,
waveform_state: WaveformState::Operational,
post_transition_guard: 5,
}
}
pub const fn transition() -> Self {
Self {
snr_db: f32::NAN,
waveform_state: WaveformState::Transition,
post_transition_guard: 5,
}
}
}
impl Default for PlatformContext {
fn default() -> Self {
Self::operational()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn snr_floor_sub_threshold() {
let floor = SnrFloor::new(-10.0);
assert!(floor.is_sub_threshold(-15.0));
assert!(!floor.is_sub_threshold(-5.0));
assert!(!floor.is_sub_threshold(0.0));
}
#[test]
fn transition_multiplier_is_infinite() {
assert!(WaveformState::Transition.admissibility_multiplier().is_infinite());
assert!(WaveformState::Calibration.admissibility_multiplier().is_infinite());
assert_eq!(WaveformState::Operational.admissibility_multiplier(), 1.0);
}
#[test]
fn guard_ticks_to_operational() {
let s = WaveformState::PostTransitionGuard { remaining: 2 };
let s1 = s.tick();
assert_eq!(s1, WaveformState::PostTransitionGuard { remaining: 1 });
let s2 = s1.tick();
assert_eq!(s2, WaveformState::PostTransitionGuard { remaining: 0 });
let s3 = s2.tick();
assert_eq!(s3, WaveformState::Operational);
}
#[test]
fn suppressed_states() {
assert!(WaveformState::Transition.is_suppressed());
assert!(WaveformState::Calibration.is_suppressed());
assert!(WaveformState::PostTransitionGuard { remaining: 3 }.is_suppressed());
assert!(!WaveformState::Operational.is_suppressed());
}
}