gnss_qc_traits/processing/
item.rs1use std::{num::ParseFloatError, str::FromStr};
2use thiserror::Error;
3
4use gnss_rs::{
5 constellation::ParsingError as ConstellationParsingError,
6 prelude::{Constellation, SV},
7 sv::ParsingError as SVParsingError,
8};
9
10use hifitime::{Duration, Epoch, ParsingError as EpochParsingError};
11
12#[derive(Debug, Error)]
13pub enum ItemError {
14 #[error("unknown filter item \"{0}\"")]
15 UnknownItem(String),
16 #[error("item guessing error: {0}")]
17 TypeGuessingError(String),
18 #[error("two valid epochs are required to describe a duration")]
19 InvalidDuration,
20 #[error("invalid epoch description")]
21 InvalidEpoch,
22 #[error("invalid SNR description")]
23 InvalidSNR,
24 #[error("invalid elevation angle (0 <= e <= 90)")]
25 InvalidElevationAngle,
26 #[error("invalid azimuth angle description (0 <= a <= 360)")]
27 InvalidAzimuthAngle,
28 #[error("invalid float number")]
29 FloatParsing(#[from] ParseFloatError),
30 #[error("sv item parsing")]
31 SVParsing(#[from] SVParsingError),
32 #[error("constellation item parsing")]
33 ConstellationParing(#[from] ConstellationParsingError),
34 #[error("duration item parsing")]
35 InvalidDurationItem(#[from] EpochParsingError),
36}
37
38#[derive(Clone, Debug, PartialEq, PartialOrd)]
41pub enum FilterItem {
42 EpochItem(Epoch),
44 DurationItem(Duration),
46 SNRItem(f64),
48 ElevationItem(f64),
50 AzimuthItem(f64),
52 SvItem(Vec<SV>),
54 ConstellationItem(Vec<Constellation>),
56 ClockItem,
58 ComplexItem(Vec<String>),
60}
61
62impl std::ops::BitOrAssign for FilterItem {
63 fn bitor_assign(&mut self, rhs: Self) {
64 *self = self.clone() | rhs;
65 }
66}
67
68impl std::ops::BitOr for FilterItem {
69 type Output = Self;
70 fn bitor(self, rhs: Self) -> Self {
71 match self {
72 Self::SvItem(ref lhs) => match rhs {
73 Self::SvItem(rhs) => {
74 let mut lhs = lhs.clone();
75 for r in rhs {
76 lhs.push(r);
77 }
78 Self::SvItem(lhs)
79 }
80 _ => self.clone(),
81 },
82 Self::ConstellationItem(ref lhs) => match rhs {
83 Self::ConstellationItem(rhs) => {
84 let mut lhs = lhs.clone();
85 for r in rhs {
86 lhs.push(r);
87 }
88 Self::ConstellationItem(lhs)
89 }
90 _ => self.clone(),
91 },
92 _ => self.clone(),
93 }
94 }
95}
96
97pub(crate) fn parse_sv_list(items: Vec<&str>) -> Result<Vec<SV>, SVParsingError> {
98 let mut ret: Vec<SV> = Vec::with_capacity(items.len());
99 for item in items {
100 let sv = SV::from_str(item.trim())?;
101 ret.push(sv);
102 }
103 Ok(ret)
104}
105
106pub(crate) fn parse_gnss_list(
107 items: Vec<&str>,
108) -> Result<Vec<Constellation>, ConstellationParsingError> {
109 let mut ret: Vec<Constellation> = Vec::with_capacity(items.len());
110 for item in items {
111 let c = Constellation::from_str(item.trim())?;
112 ret.push(c);
113 }
114 Ok(ret)
115}
116
117fn parse_float_payload(content: &str) -> Result<f64, ParseFloatError> {
118 f64::from_str(content.trim())
119}
120
121impl FilterItem {
122 pub(crate) fn from_elevation(content: &str) -> Result<Self, ItemError> {
123 if let Ok(float) = parse_float_payload(content) {
124 if float >= 0.0 && float <= 90.0 {
125 return Ok(Self::AzimuthItem(float));
126 }
127 }
128 Err(ItemError::InvalidElevationAngle)
129 }
130 pub(crate) fn from_azimuth(content: &str) -> Result<Self, ItemError> {
131 if let Ok(float) = parse_float_payload(content) {
132 if float >= 0.0 && float <= 360.0 {
133 return Ok(Self::AzimuthItem(float));
134 }
135 }
136 Err(ItemError::InvalidAzimuthAngle)
137 }
138 pub(crate) fn from_snr(content: &str) -> Result<Self, ItemError> {
139 if let Ok(float) = parse_float_payload(content) {
140 Ok(Self::SNRItem(float))
141 } else {
142 Err(ItemError::InvalidSNR)
143 }
144 }
145}
146
147impl std::str::FromStr for FilterItem {
150 type Err = ItemError;
151 fn from_str(content: &str) -> Result<Self, Self::Err> {
152 let c = content.trim();
156 let items: Vec<&str> = c.split(',').collect();
157 if let Ok(start) = Epoch::from_str(items[0].trim()) {
161 if items.len() == 1 {
162 Ok(Self::EpochItem(start))
163 } else if items.len() == 2 {
164 if let Ok(end) = Epoch::from_str(items[1].trim()) {
165 Ok(Self::DurationItem(end - start))
166 } else {
167 Err(ItemError::InvalidEpoch)
168 }
169 } else {
170 Err(ItemError::InvalidDuration)
171 }
172 } else if SV::from_str(items[0].trim()).is_ok() {
176 Ok(Self::SvItem(parse_sv_list(items)?))
179 } else if Constellation::from_str(items[0].trim()).is_ok() {
183 Ok(Self::ConstellationItem(parse_gnss_list(items)?))
186 } else {
187 Ok(Self::ComplexItem(
189 items.iter().map(|s| s.to_string()).collect(),
190 ))
191 }
192 }
193}
194
195impl From<Epoch> for FilterItem {
196 fn from(e: Epoch) -> Self {
197 Self::EpochItem(e)
198 }
199}
200
201impl From<Duration> for FilterItem {
202 fn from(dt: Duration) -> Self {
203 Self::DurationItem(dt)
204 }
205}
206
207impl From<SV> for FilterItem {
208 fn from(sv: SV) -> Self {
209 Self::SvItem(vec![sv])
210 }
211}
212
213impl From<Vec<SV>> for FilterItem {
214 fn from(sv: Vec<SV>) -> Self {
215 Self::SvItem(sv.clone())
216 }
217}
218
219impl From<Constellation> for FilterItem {
220 fn from(c: Constellation) -> Self {
221 Self::ConstellationItem(vec![c])
222 }
223}
224
225impl From<Vec<Constellation>> for FilterItem {
226 fn from(c: Vec<Constellation>) -> Self {
227 Self::ConstellationItem(c.clone())
228 }
229}
230
231impl std::fmt::Display for FilterItem {
232 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
233 match self {
234 Self::ConstellationItem(gnss) => {
235 write!(f, "gnss: {:?}", gnss)
236 }
237 Self::SvItem(svs) => {
238 write!(f, "sv: {:?}", svs)
239 }
240 _ => Ok(()),
241 }
242 }
243}
244
245#[cfg(test)]
246mod test {
247 use super::*;
248 use gnss_rs::prelude::{Constellation, SV};
249 use std::str::FromStr;
250 #[test]
251 fn algo_target_item() {
252 let e = Epoch::default();
253 let target: FilterItem = e.into();
254 assert_eq!(target, FilterItem::EpochItem(e));
255
256 assert_eq!(
257 FilterItem::from_str("g08,g09,R03").unwrap(),
258 FilterItem::SvItem(vec![
259 SV::from_str("G08").unwrap(),
260 SV::from_str("G09").unwrap(),
261 SV::from_str("R03").unwrap()
262 ])
263 );
264
265 assert_eq!(
266 FilterItem::from_str("GPS , BDS").unwrap(),
267 FilterItem::ConstellationItem(vec![Constellation::GPS, Constellation::BeiDou])
268 );
269
270 let dt = Duration::from_str("1 d").unwrap();
271 let target: FilterItem = dt.into();
272 assert_eq!(target, FilterItem::DurationItem(dt));
273 }
274 #[test]
275 fn test_from_elevation() {
276 let desc = "90";
277 assert!(
278 FilterItem::from_elevation(desc).is_ok(),
279 "Failed to parse Elevation Target Item"
280 );
281 }
282 #[test]
283 fn test_from_azimuth() {
284 let desc = " 12.34 ";
285 assert!(
286 FilterItem::from_azimuth(desc).is_ok(),
287 "Failed to parse Azimuth Target Item"
288 );
289 }
290 #[test]
291 fn test_from_snr() {
292 let desc = " 12.34 ";
293 assert!(
294 FilterItem::from_snr(desc).is_ok(),
295 "Failed to parse SNR Target Item"
296 );
297 }
298}