Skip to main content

rfham_antennas/
lib.rs

1//! Antenna types and calculations for RF-Ham.
2//!
3//! This crate provides the [`AntennaForm`] enum classifying common amateur antenna
4//! styles, and the [`dipole`] module with [`dipole::SimpleDipole`] — a classical
5//! half-wave dipole calculator that can produce Markdown documentation of its design.
6//!
7//! # Examples
8//!
9//! ```rust
10//! use rfham_antennas::SimpleDipole;
11//! use rfham_itu::allocations::FrequencyAllocation;
12//!
13//! let dipole = SimpleDipole::new(FrequencyAllocation::Band2M);
14//! // Half-wave length for 2m mid-band (≈146 MHz) is ~1.027 m
15//! let length = dipole.antenna_length().unwrap();
16//! assert!(length.value() > 1.0 && length.value() < 1.1);
17//! ```
18
19use rfham_core::error::CoreError;
20use std::{fmt::Display, str::FromStr};
21
22// ------------------------------------------------------------------------------------------------
23// Public Types
24// ------------------------------------------------------------------------------------------------
25
26#[derive(Clone, Copy, Debug, PartialEq, Eq)]
27pub enum AntennaForm {
28    Dipole,
29    Dish,
30    Vertical,
31    EndFed,
32    RandomWire,
33    Yagi,
34    Other,
35}
36
37// ------------------------------------------------------------------------------------------------
38// Implementations
39// ------------------------------------------------------------------------------------------------
40
41impl Display for AntennaForm {
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43        write!(
44            f,
45            "{}",
46            match self {
47                Self::Dipole => "dipole",
48                Self::Dish => "dish",
49                Self::Vertical => "vertical",
50                Self::EndFed => "efhw",
51                Self::RandomWire => "random",
52                Self::Yagi => "yagi",
53                Self::Other => "other",
54            }
55        )
56    }
57}
58
59impl FromStr for AntennaForm {
60    type Err = CoreError;
61
62    fn from_str(s: &str) -> Result<Self, Self::Err> {
63        match s {
64            "dipole" => Ok(Self::Dipole),
65            "dish" => Ok(Self::Dish),
66            "vertical" => Ok(Self::Vertical),
67            "efhw" => Ok(Self::EndFed),
68            "random" => Ok(Self::RandomWire),
69            "yagi" => Ok(Self::Yagi),
70            "other" => Ok(Self::Other),
71            _ => Err(CoreError::InvalidValueFromStr(s.to_string(), "AntennaForm")),
72        }
73    }
74}
75
76// ------------------------------------------------------------------------------------------------
77// Modules
78// ------------------------------------------------------------------------------------------------
79
80pub mod dipoles;
81pub use dipoles::SimpleDipole;
82
83// ------------------------------------------------------------------------------------------------
84// Unit Tests
85// ------------------------------------------------------------------------------------------------
86
87#[cfg(test)]
88mod tests {
89    use super::AntennaForm;
90    use pretty_assertions::assert_eq;
91    use std::str::FromStr;
92
93    #[test]
94    fn test_display_roundtrip() {
95        for (s, form) in [
96            ("dipole", AntennaForm::Dipole),
97            ("vertical", AntennaForm::Vertical),
98            ("efhw", AntennaForm::EndFed),
99            ("yagi", AntennaForm::Yagi),
100        ] {
101            assert_eq!(s, form.to_string());
102            assert_eq!(form, AntennaForm::from_str(s).unwrap());
103        }
104    }
105
106    #[test]
107    fn test_from_str_invalid() {
108        assert!("loop".parse::<AntennaForm>().is_err());
109    }
110}