1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3
4use crate::{error::ParsingError, frequency::Frequency};
5
6#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Hash, Ord, Eq)]
11#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
12pub enum Observable {
13 PseudoRange(Frequency),
17
18 UnambiguousPhaseRange(Frequency),
24
25 Power(Frequency),
29
30 Pressure,
32
33 Temperature,
35
36 HumidityRate,
38
39 FrequencyRatio,
41}
42
43impl Default for Observable {
44 fn default() -> Self {
45 Self::PseudoRange(Default::default())
46 }
47}
48
49impl Observable {
50 pub fn same_frequency(&self, rhs: &Observable) -> bool {
52 match self {
53 Self::PseudoRange(freq) | Self::Power(freq) | Self::UnambiguousPhaseRange(freq) => {
54 match rhs {
55 Self::PseudoRange(rhs)
56 | Self::Power(rhs)
57 | Self::UnambiguousPhaseRange(rhs) => rhs == freq,
58 _ => false,
59 }
60 },
61 _ => false,
62 }
63 }
64
65 pub fn same_physics(&self, rhs: &Observable) -> bool {
68 match self {
69 Self::UnambiguousPhaseRange(_) => matches!(rhs, Self::UnambiguousPhaseRange(_)),
70 Self::PseudoRange(_) => matches!(rhs, Self::PseudoRange(_)),
71 Self::Power(_) => matches!(rhs, Self::Power(_)),
72 Self::Pressure => matches!(rhs, Self::Pressure),
73 Self::Temperature => matches!(rhs, Self::Temperature),
74 Self::HumidityRate => matches!(rhs, Self::HumidityRate),
75 Self::FrequencyRatio => matches!(rhs, Self::FrequencyRatio),
76 }
77 }
78
79 pub fn is_phase_range_observable(&self) -> bool {
81 matches!(self, Self::UnambiguousPhaseRange(_))
82 }
83
84 pub fn is_pseudo_range_observable(&self) -> bool {
86 matches!(self, Self::PseudoRange(_))
87 }
88
89 pub fn is_power_observable(&self) -> bool {
90 matches!(self, Self::Power(_))
91 }
92}
93
94impl std::fmt::Display for Observable {
95 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
96 match self {
97 Self::Pressure => write!(f, "Pressure"),
98 Self::Temperature => write!(f, "Temperature"),
99 Self::HumidityRate => write!(f, "Moisture rate"),
100 Self::FrequencyRatio => write!(f, "Frequency ratio"),
101 Self::PseudoRange(freq) => write!(f, "C{}", freq),
102 Self::UnambiguousPhaseRange(freq) => write!(f, "L{}", freq),
103 Self::Power(freq) => write!(f, "W{}", freq),
104 }
105 }
106}
107
108impl std::fmt::LowerHex for Observable {
109 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
110 match self {
111 Self::Pressure => write!(f, "P"),
112 Self::Temperature => write!(f, "T"),
113 Self::HumidityRate => write!(f, "H"),
114 Self::FrequencyRatio => write!(f, "F"),
115 Self::PseudoRange(freq) => write!(f, "C{}", freq),
116 Self::UnambiguousPhaseRange(freq) => write!(f, "L{}", freq),
117 Self::Power(freq) => write!(f, "W{}", freq),
118 }
119 }
120}
121
122impl std::str::FromStr for Observable {
123 type Err = ParsingError;
124
125 fn from_str(content: &str) -> Result<Self, Self::Err> {
126 let content = content.to_uppercase();
127 let content = content.trim();
128 match content {
129 "P" | "PRESSURE" => Ok(Self::Pressure),
130 "T" | "TEMPERATURE" => Ok(Self::Temperature),
131 "H" | "MOISTURE RATE" => Ok(Self::HumidityRate),
132 "F" | "FREQUENCY RATIO" => Ok(Self::FrequencyRatio),
133 _ => {
134 let frequency = Frequency::from_str(&content[1..])?;
135 if content.starts_with('L') {
136 Ok(Self::UnambiguousPhaseRange(frequency))
137 } else if content.starts_with('C') {
138 Ok(Self::PseudoRange(frequency))
139 } else if content.starts_with('W') {
140 Ok(Self::Power(frequency))
141 } else {
142 Err(ParsingError::Observable)
143 }
144 },
145 }
146 }
147}
148
149#[cfg(test)]
150mod test {
151 use crate::prelude::{Frequency, Observable};
152 use std::str::FromStr;
153
154 #[test]
155 fn test_default_observable() {
156 let default = Observable::default();
157
158 assert_eq!(default, Observable::PseudoRange(Frequency::DORIS1));
159
160 let formatted = default.to_string();
161
162 let parsed = Observable::from_str(&formatted).unwrap_or_else(|e| {
163 panic!("Failed to parse observable from \"{}\"", formatted);
164 });
165
166 assert_eq!(parsed, default);
167 }
168
169 #[test]
170 fn observable_parsing() {
171 for (observable, expected, formatted) in [
172 (
173 "L1",
174 Observable::UnambiguousPhaseRange(Frequency::DORIS1),
175 "L1",
176 ),
177 (
178 "L2",
179 Observable::UnambiguousPhaseRange(Frequency::DORIS2),
180 "L2",
181 ),
182 ("C1", Observable::PseudoRange(Frequency::DORIS1), "C1"),
183 ("C2", Observable::PseudoRange(Frequency::DORIS2), "C2"),
184 ("W1", Observable::Power(Frequency::DORIS1), "W1"),
185 ("W2", Observable::Power(Frequency::DORIS2), "W2"),
186 ("T", Observable::Temperature, "Temperature"),
187 ("P", Observable::Pressure, "Pressure"),
188 ("H", Observable::HumidityRate, "Moisture rate"),
189 ] {
190 let parsed = Observable::from_str(observable).unwrap_or_else(|e| {
191 panic!("failed to parse observable from \"{}\": {}", observable, e);
192 });
193
194 assert_eq!(parsed, expected);
195 assert_eq!(parsed.to_string(), formatted);
196 }
197
198 let l1 = Observable::UnambiguousPhaseRange(Frequency::DORIS1);
199 let l2 = Observable::UnambiguousPhaseRange(Frequency::DORIS2);
200 let c1 = Observable::PseudoRange(Frequency::DORIS1);
201 let c2 = Observable::PseudoRange(Frequency::DORIS2);
202
203 assert!(l1.same_physics(&l1));
204 assert!(l1.same_physics(&l2));
205
206 assert!(c1.same_physics(&c1));
207 assert!(c1.same_physics(&c2));
208
209 assert!(!l1.same_physics(&c1));
210 assert!(!l1.same_physics(&c2));
211 }
212}