sim_lib_interference_core/
quantity.rs1use std::f64::consts::{PI, TAU};
4
5use crate::InterferenceError;
6
7fn invalid(name: &'static str, value: f64) -> InterferenceError {
8 InterferenceError::InvalidQuantity { name, value }
9}
10
11fn finite(name: &'static str, value: f64) -> Result<f64, InterferenceError> {
12 value
13 .is_finite()
14 .then_some(canonical_zero(value))
15 .ok_or_else(|| invalid(name, value))
16}
17
18fn positive(name: &'static str, value: f64) -> Result<f64, InterferenceError> {
19 (value.is_finite() && value > 0.0)
20 .then_some(value)
21 .ok_or_else(|| invalid(name, value))
22}
23
24fn non_negative(name: &'static str, value: f64) -> Result<f64, InterferenceError> {
25 (value.is_finite() && value >= 0.0)
26 .then_some(canonical_zero(value))
27 .ok_or_else(|| invalid(name, value))
28}
29
30fn canonical_zero(value: f64) -> f64 {
31 if value == 0.0 { 0.0 } else { value }
32}
33
34#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
36pub struct Metres(f64);
37
38impl Metres {
39 pub const NAME: &'static str = "distance-m";
41
42 pub fn new(value: f64) -> Result<Self, InterferenceError> {
44 finite(Self::NAME, value).map(Self)
45 }
46
47 pub fn get(self) -> f64 {
49 self.0
50 }
51}
52
53#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
55pub struct PositiveMetres(f64);
56
57impl PositiveMetres {
58 pub const NAME: &'static str = "positive-distance-m";
60
61 pub fn new(value: f64) -> Result<Self, InterferenceError> {
63 positive(Self::NAME, value).map(Self)
64 }
65
66 pub fn get(self) -> f64 {
68 self.0
69 }
70}
71
72#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
74pub struct Hertz(f64);
75
76impl Hertz {
77 pub const NAME: &'static str = "frequency-hz";
79
80 pub fn new(value: f64) -> Result<Self, InterferenceError> {
82 positive(Self::NAME, value).map(Self)
83 }
84
85 pub fn get(self) -> f64 {
87 self.0
88 }
89}
90
91#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
93pub struct MetresPerSecond(f64);
94
95impl MetresPerSecond {
96 pub const NAME: &'static str = "speed-m-s";
98
99 pub fn new(value: f64) -> Result<Self, InterferenceError> {
101 positive(Self::NAME, value).map(Self)
102 }
103
104 pub fn get(self) -> f64 {
106 self.0
107 }
108}
109
110#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
112pub struct NepersPerMetre(f64);
113
114impl NepersPerMetre {
115 pub const NAME: &'static str = "attenuation-np-m";
117
118 pub fn new(value: f64) -> Result<Self, InterferenceError> {
120 non_negative(Self::NAME, value).map(Self)
121 }
122
123 pub fn get(self) -> f64 {
125 self.0
126 }
127}
128
129#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
131pub struct Radians(f64);
132
133impl Radians {
134 pub const NAME: &'static str = "phase-rad";
136
137 pub fn new(value: f64) -> Result<Self, InterferenceError> {
139 let value = finite(Self::NAME, value)?;
140 let positive_turn = value.rem_euclid(TAU);
141 let normalized = if positive_turn >= PI {
142 positive_turn - TAU
143 } else {
144 positive_turn
145 };
146 Ok(Self(canonical_zero(normalized)))
147 }
148
149 pub fn get(self) -> f64 {
151 self.0
152 }
153}
154
155#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
160pub struct FieldAmplitude(f64);
161
162impl FieldAmplitude {
163 pub const NAME: &'static str = "field-amplitude";
165
166 pub fn new(value: f64) -> Result<Self, InterferenceError> {
168 non_negative(Self::NAME, value).map(Self)
169 }
170
171 pub fn get(self) -> f64 {
173 self.0
174 }
175}