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}