1use crate::error::CoreError;
44use serde::{Deserialize, Serialize};
45use std::{fmt::Display, ops::Range, str::FromStr};
46use uom::{
47 fmt::DisplayStyle,
48 si::{
49 f64::{Frequency as BaseFrequency, Length as BaseLength},
50 frequency as frequency_unit, length as length_unit,
51 },
52};
53
54#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd, Deserialize, Serialize)]
63pub struct Frequency(BaseFrequency);
64
65#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd, Deserialize, Serialize)]
66pub struct Wavelength(BaseLength);
67
68pub const SPEED_OF_LIGHT: f64 = 299792458.0; #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
71pub struct FrequencyRange(Range<Frequency>);
72
73impl Display for Frequency {
90 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91 if f.alternate() {
92 match self.value() {
93 0.0..1_000.0 => self
94 .0
95 .into_format_args(frequency_unit::hertz, DisplayStyle::Description)
96 .fmt(f),
97 1_000.0..1_000_000.0 => self
98 .0
99 .into_format_args(frequency_unit::kilohertz, DisplayStyle::Description)
100 .fmt(f),
101 1_000_000.0..1_000_000_000.0 => self
102 .0
103 .into_format_args(frequency_unit::megahertz, DisplayStyle::Description)
104 .fmt(f),
105 1_000_000_000.0..1_000_000_000_000.0 => self
106 .0
107 .into_format_args(frequency_unit::gigahertz, DisplayStyle::Description)
108 .fmt(f),
109 _ => self
110 .0
111 .into_format_args(frequency_unit::terahertz, DisplayStyle::Description)
112 .fmt(f),
113 }
114 } else {
115 self.0
116 .into_format_args(frequency_unit::megahertz, DisplayStyle::Abbreviation)
117 .fmt(f)
118 }
119 }
120}
121
122impl FromStr for Frequency {
123 type Err = CoreError;
124
125 fn from_str(s: &str) -> Result<Self, Self::Err> {
126 BaseFrequency::from_str(s)
127 .map(Self)
128 .map_err(|_| CoreError::InvalidValueFromStr(s.to_string(), "Frequency"))
129 }
130}
131
132impl From<BaseFrequency> for Frequency {
133 fn from(value: BaseFrequency) -> Self {
134 Self(value)
135 }
136}
137
138impl From<f64> for Frequency {
139 fn from(value: f64) -> Self {
140 Frequency::megahertz(value)
141 }
142}
143
144impl From<Frequency> for BaseFrequency {
145 fn from(value: Frequency) -> Self {
146 value.0
147 }
148}
149
150impl From<Frequency> for f64 {
151 fn from(value: Frequency) -> Self {
152 value.0.value
153 }
154}
155
156impl AsRef<BaseFrequency> for Frequency {
157 fn as_ref(&self) -> &BaseFrequency {
158 &self.0
159 }
160}
161
162impl Frequency {
163 pub fn gigahertz(value: f64) -> Self {
164 Self(BaseFrequency::new::<frequency_unit::gigahertz>(value))
165 }
166 pub fn megahertz(value: f64) -> Self {
167 Self(BaseFrequency::new::<frequency_unit::megahertz>(value))
168 }
169 pub fn kilohertz(value: f64) -> Self {
170 Self(BaseFrequency::new::<frequency_unit::kilohertz>(value))
171 }
172 pub fn hertz(value: f64) -> Self {
173 Self(BaseFrequency::new::<frequency_unit::hertz>(value))
174 }
175
176 pub const fn value(&self) -> f64 {
177 self.0.value
178 }
179
180 pub fn to_wavelength(&self) -> Wavelength {
181 Wavelength::meters(SPEED_OF_LIGHT / self.value())
182 }
183}
184
185impl Display for FrequencyRange {
190 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
191 self.0.start.fmt(f)?;
192 write!(f, " {} ", if f.alternate() { "to" } else { "-" },)?;
193 self.0.end.fmt(f)
194 }
195}
196
197impl From<Range<Frequency>> for FrequencyRange {
198 fn from(range: Range<Frequency>) -> Self {
199 Self(range)
200 }
201}
202
203impl From<Range<f64>> for FrequencyRange {
204 fn from(range: Range<f64>) -> Self {
205 Self::new(
206 Frequency::megahertz(range.start),
207 Frequency::megahertz(range.end),
208 )
209 }
210}
211
212impl From<(Frequency, Frequency)> for FrequencyRange {
213 fn from(range: (Frequency, Frequency)) -> Self {
214 Self::new(range.0, range.1)
215 }
216}
217
218impl From<(f64, f64)> for FrequencyRange {
219 fn from(range: (f64, f64)) -> Self {
220 Self::new(Frequency::megahertz(range.0), Frequency::megahertz(range.1))
221 }
222}
223
224impl From<FrequencyRange> for Range<Frequency> {
225 fn from(range: FrequencyRange) -> Self {
226 range.0
227 }
228}
229
230impl From<FrequencyRange> for Range<f64> {
231 fn from(range: FrequencyRange) -> Self {
232 Range {
233 start: range.0.start.value(),
234 end: range.0.end.value(),
235 }
236 }
237}
238
239impl FrequencyRange {
240 pub fn new(start: Frequency, end: Frequency) -> Self {
241 assert!(start <= end);
242 Self(Range { start, end })
243 }
244 pub fn new_mhz(start: f64, end: f64) -> Self {
245 Self::new(Frequency::megahertz(start), Frequency::megahertz(end))
246 }
247
248 pub const fn start(&self) -> Frequency {
249 self.0.start
250 }
251
252 pub const fn end(&self) -> Frequency {
253 self.0.end
254 }
255
256 pub fn bandwidth(&self) -> Frequency {
257 Frequency::hertz(self.0.end.value() - self.0.start.value())
258 }
259
260 pub fn mid_band(&self) -> Frequency {
261 Frequency::hertz(self.0.start.value() + (self.bandwidth().value() / 2.0))
262 }
263
264 pub fn contains(&self, frequency: Frequency) -> bool {
265 self.0.contains(&frequency)
266 }
267
268 pub fn contains_mhz(&self, frequency: f64) -> bool {
269 self.0.contains(&Frequency::megahertz(frequency))
270 }
271
272 pub fn is_empty(&self) -> bool {
273 self.0.is_empty()
274 }
275
276 pub fn starts_before(&self, other: &Self) -> bool {
277 self.start() < other.start()
278 }
279
280 pub fn starts_with(&self, other: &Self) -> bool {
281 self.start() == other.start() && self.contains(other.end())
282 }
283
284 pub fn ends_before(&self, other: &Self) -> bool {
285 self.end() < other.end()
286 }
287
288 pub fn ends_with(&self, other: &Self) -> bool {
289 self.contains(other.start()) && self.end() == other.end()
290 }
291
292 pub fn is_subrange(&self, other: &Self) -> bool {
293 self.start() <= other.start() && self.end() >= other.end()
294 }
295
296 pub fn is_overlapping(&self, other: &Self) -> bool {
297 self.contains(other.start()) || self.contains(other.end())
298 }
299}
300
301impl Display for Wavelength {
306 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
307 if f.alternate() {
308 match self.value() {
309 0.001..0.01 => self
310 .0
311 .into_format_args(length_unit::millimeter, DisplayStyle::Description)
312 .fmt(f),
313 0.01..1.0 => self
314 .0
315 .into_format_args(length_unit::centimeter, DisplayStyle::Description)
316 .fmt(f),
317 1_000.0.. => self
318 .0
319 .into_format_args(length_unit::kilometer, DisplayStyle::Description)
320 .fmt(f),
321 _ => self
322 .0
323 .into_format_args(length_unit::meter, DisplayStyle::Description)
324 .fmt(f),
325 }
326 } else {
327 self.0
328 .into_format_args(length_unit::meter, DisplayStyle::Abbreviation)
329 .fmt(f)
330 }
331 }
332}
333
334impl FromStr for Wavelength {
335 type Err = CoreError;
336
337 fn from_str(s: &str) -> Result<Self, Self::Err> {
338 BaseLength::from_str(s)
339 .map(Self)
340 .map_err(|_| CoreError::InvalidValueFromStr(s.to_string(), "Wavelength"))
341 }
342}
343
344impl From<BaseLength> for Wavelength {
345 fn from(value: BaseLength) -> Self {
346 Self(value)
347 }
348}
349
350impl From<f64> for Wavelength {
351 fn from(value: f64) -> Self {
352 Wavelength::meters(value)
353 }
354}
355
356impl From<Wavelength> for BaseLength {
357 fn from(value: Wavelength) -> Self {
358 value.0
359 }
360}
361
362impl From<Wavelength> for f64 {
363 fn from(value: Wavelength) -> Self {
364 value.0.value
365 }
366}
367
368impl AsRef<BaseLength> for Wavelength {
369 fn as_ref(&self) -> &BaseLength {
370 &self.0
371 }
372}
373
374impl Wavelength {
375 pub fn millimeters(value: f64) -> Self {
376 Self(BaseLength::new::<length_unit::millimeter>(value))
377 }
378
379 pub fn centimeters(value: f64) -> Self {
380 Self(BaseLength::new::<length_unit::centimeter>(value))
381 }
382
383 pub fn meters(value: f64) -> Self {
384 Self(BaseLength::new::<length_unit::meter>(value))
385 }
386
387 pub fn kilometers(value: f64) -> Self {
388 Self(BaseLength::new::<length_unit::kilometer>(value))
389 }
390
391 pub const fn value(&self) -> f64 {
392 self.0.value
393 }
394
395 pub fn to_frequency(&self) -> Frequency {
396 Frequency::hertz(SPEED_OF_LIGHT / self.value())
397 }
398}
399
400pub fn gigahertz(value: f64) -> Frequency {
405 Frequency::gigahertz(value)
406}
407
408pub fn megahertz(value: f64) -> Frequency {
409 Frequency::megahertz(value)
410}
411
412pub fn kilohertz(value: f64) -> Frequency {
413 Frequency::kilohertz(value)
414}
415
416pub fn hertz(value: f64) -> Frequency {
417 Frequency::hertz(value)
418}
419
420pub fn meters(value: f64) -> Wavelength {
421 Wavelength::meters(value)
422}
423
424#[cfg(test)]
437mod tests {
438 use super::{Frequency, FrequencyRange};
439 use pretty_assertions::assert_eq;
440
441 #[test]
442 fn test_default_fmt_frequency() {
443 assert_eq!("0.000001 MHz", &Frequency::hertz(1.0).to_string());
444 assert_eq!("0.001 MHz", &Frequency::kilohertz(1.0).to_string());
445 assert_eq!("1 MHz", &Frequency::megahertz(1.0).to_string());
446 assert_eq!("1000 MHz", &Frequency::gigahertz(1.0).to_string());
447 }
448
449 #[test]
450 fn test_default_fmt_frequency_precision() {
451 assert_eq!("0.000001 MHz", &format!("{:.6}", Frequency::hertz(1.0)));
452 assert_eq!("0.001000 MHz", &format!("{:.6}", Frequency::kilohertz(1.0)));
453 assert_eq!("1.000000 MHz", &format!("{:.6}", Frequency::megahertz(1.0)));
454 assert_eq!(
455 "1000.000000 MHz",
456 &format!("{:.6}", Frequency::gigahertz(1.0))
457 );
458 }
459
460 #[test]
461 fn test_default_fmt_frequency_width_and_precision() {
462 assert_eq!(
463 " 0.000001 MHz",
464 &format!("{:>14.6}", Frequency::hertz(1.0))
465 );
466 assert_eq!(
467 " 0.001000 MHz",
468 &format!("{:>14.6}", Frequency::kilohertz(1.0))
469 );
470 assert_eq!(
471 " 1.000000 MHz",
472 &format!("{:>14.6}", Frequency::megahertz(1.0))
473 );
474 assert_eq!(
475 " 1000.000000 MHz",
476 &format!("{:>14.6}", Frequency::gigahertz(1.0))
477 );
478 }
479
480 #[test]
481 fn test_alternate_fmt_frequency() {
482 assert_eq!("1 hertz", &format!("{:#}", Frequency::hertz(1.0)));
483 assert_eq!("1 kilohertz", &format!("{:#}", Frequency::kilohertz(1.0)));
484 assert_eq!("1 megahertz", &format!("{:#}", Frequency::megahertz(1.0)));
485 assert_eq!("1 gigahertz", &format!("{:#}", Frequency::gigahertz(1.0)));
486 }
487
488 #[test]
489 fn test_default_fmt_range() {
490 assert_eq!(
491 "0.000001 MHz - 0.00001 MHz",
492 &FrequencyRange::new(Frequency::hertz(1.0), Frequency::hertz(10.0)).to_string()
493 );
494 assert_eq!(
495 "0.001 MHz - 0.01 MHz",
496 &FrequencyRange::new(Frequency::kilohertz(1.0), Frequency::kilohertz(10.0)).to_string()
497 );
498 assert_eq!(
499 "1 MHz - 10 MHz",
500 &FrequencyRange::new(Frequency::megahertz(1.0), Frequency::megahertz(10.0)).to_string()
501 );
502 assert_eq!(
503 "1000 MHz - 10000 MHz",
504 &FrequencyRange::new(Frequency::gigahertz(1.0), Frequency::gigahertz(10.0)).to_string()
505 );
506 }
507
508 #[test]
509 fn test_frequency_wavelength_roundtrip() {
510 let f = Frequency::megahertz(144.0);
511 let wl = f.to_wavelength();
512 let f2 = wl.to_frequency();
513 assert!((f.value() - f2.value()).abs() < 1e-6);
514 }
515
516 #[test]
517 fn test_frequency_range_bandwidth() {
518 let r = FrequencyRange::new_mhz(144.0, 148.0);
519 assert!((r.bandwidth().value() - 4_000_000.0).abs() < 1.0);
521 }
522
523 #[test]
524 fn test_frequency_range_mid_band() {
525 let r = FrequencyRange::new_mhz(144.0, 148.0);
526 assert!((r.mid_band().value() - 146_000_000.0).abs() < 1.0);
528 }
529
530 #[test]
531 fn test_frequency_range_contains() {
532 let r = FrequencyRange::new_mhz(144.0, 148.0);
533 assert!(r.contains_mhz(144.0));
534 assert!(r.contains_mhz(146.52));
535 assert!(!r.contains_mhz(148.0)); assert!(!r.contains_mhz(150.0));
537 }
538
539 #[test]
540 fn test_frequency_range_overlap() {
541 let a = FrequencyRange::new_mhz(144.0, 148.0);
542 let b = FrequencyRange::new_mhz(146.0, 150.0);
543 let c = FrequencyRange::new_mhz(150.0, 160.0);
544 assert!(a.is_overlapping(&b));
545 assert!(!a.is_overlapping(&c));
546 }
547
548 #[test]
549 fn test_frequency_range_subrange() {
550 let outer = FrequencyRange::new_mhz(144.0, 148.0);
551 let inner = FrequencyRange::new_mhz(145.0, 147.0);
552 assert!(outer.is_subrange(&inner));
553 assert!(!inner.is_subrange(&outer));
554 }
555
556 #[test]
557 fn test_frequency_from_f64_is_megahertz() {
558 let f: Frequency = 146.52_f64.into();
559 assert_eq!(f.to_string(), "146.52 MHz");
560 }
561}