rinex_qc_traits/processing/
mod.rs

1//! Processing toolkit, including filter designer.
2use std::str::FromStr;
3use thiserror::Error;
4
5mod item;
6pub use item::{FilterItem, ItemError};
7
8mod mask;
9pub use mask::{Error as MaskError, MaskFilter, MaskOperand, Masking};
10
11mod decim;
12pub use decim::{Decimate, DecimationFilter, DecimationFilterType, Error as DecimationError};
13
14/// Preprocessing Trait is usually implemented by GNSS data
15/// to preprocess prior further analysis.
16pub trait Preprocessing: Masking + Decimate {
17    /// Apply [Filter] algorithm on immutable dataset.
18    fn filter(&self, filter: &Filter) -> Self
19    where
20        Self: Sized,
21    {
22        match filter {
23            Filter::Mask(f) => self.mask(f),
24            Filter::Decimation(f) => self.decimate(f),
25        }
26    }
27    /// Apply [Filter] algorithm on mutable dataset.
28    fn filter_mut(&mut self, filter: &Filter) {
29        match filter {
30            Filter::Mask(f) => self.mask_mut(f),
31            Filter::Decimation(f) => self.decimate_mut(f),
32        }
33    }
34}
35
36/// Repair
37#[derive(Debug, Copy, Clone)]
38pub enum Repair {
39    /// Repairs all zero values.
40    Zero,
41}
42
43pub trait RepairTrait {
44    fn repair(&self, r: Repair) -> Self;
45    fn repair_mut(&mut self, r: Repair);
46}
47
48#[derive(Error, Debug)]
49pub enum Error {
50    #[error("invalid filter")]
51    InvalidFilter,
52    #[error("unknown filter type \"{0}\"")]
53    UnknownFilterType(String),
54    #[error("invalid mask filter")]
55    MaskFilterParsing(#[from] MaskError),
56    #[error("invalid filter item")]
57    FilterItemError(#[from] ItemError),
58    #[error("invalid decimation filter")]
59    DecimationFilterParsing(#[from] DecimationError),
60}
61
62/// Preprocessing filters, to preprocess RINEX data prior further analysis.
63/// Filters can apply either on entire RINEX or subsets.
64/// Refer to [TargetItem] definition to understand which data subsets exist.  
65#[derive(Debug, Clone, PartialEq)]
66pub enum Filter {
67    /// Mask filter, to focus on specific data subsets
68    Mask(MaskFilter),
69    /// Decimation filter, filters to reduce sample rate
70    Decimation(DecimationFilter),
71    // /// Interpolation filter is work in progress and cannot be used at the moment
72    // Interp(InterpFilter),
73}
74
75impl Filter {
76    /// Builds new [MaskFilter] from given specs
77    pub fn mask(operand: MaskOperand, item: FilterItem) -> Self {
78        Self::Mask(MaskFilter { operand, item })
79    }
80    /// Builds new [MaskFilter] with Equals operand
81    /// from following [FilterItem] description
82    pub fn equals(item: &str) -> Result<Self, ItemError> {
83        let item = FilterItem::from_str(item)?;
84        Ok(Self::mask(MaskOperand::Equals, item))
85    }
86    /// Builds new [MaskFilter] with !Equals operand
87    /// from following [FilterItem] description
88    pub fn not_equals(item: &str) -> Result<Self, ItemError> {
89        let item = FilterItem::from_str(item)?;
90        Ok(Self::mask(MaskOperand::NotEquals, item))
91    }
92    /// Builds new [MaskFilter] with GreaterThan operand
93    /// from following [FilterItem] description
94    pub fn greater_than(item: &str) -> Result<Self, ItemError> {
95        let item = FilterItem::from_str(item)?;
96        Ok(Self::mask(MaskOperand::GreaterThan, item))
97    }
98    /// Builds new [MaskFilter] with GreaterEquals operand
99    /// from following [FilterItem] description
100    pub fn greater_equals(item: &str) -> Result<Self, ItemError> {
101        let item = FilterItem::from_str(item)?;
102        Ok(Self::mask(MaskOperand::GreaterEquals, item))
103    }
104    /// Builds new [MaskFilter] with LowerEquals operand
105    /// from following [FilterItem] description
106    pub fn lower_equals(item: &str) -> Result<Self, ItemError> {
107        let item = FilterItem::from_str(item)?;
108        Ok(Self::mask(MaskOperand::LowerEquals, item))
109    }
110    /// Builds new [MaskFilter] with LowerThan operand
111    /// from following [FilterItem] description
112    pub fn lower_than(item: &str) -> Result<Self, ItemError> {
113        let item = FilterItem::from_str(item)?;
114        Ok(Self::mask(MaskOperand::LowerThan, item))
115    }
116}
117
118impl From<MaskFilter> for Filter {
119    fn from(mask: MaskFilter) -> Self {
120        Self::Mask(mask)
121    }
122}
123
124impl std::ops::Not for Filter {
125    type Output = Self;
126    fn not(self) -> Self {
127        match self {
128            Self::Mask(f) => Self::Mask(!f),
129            _ => self.clone(), // does not apply
130        }
131    }
132}
133
134impl From<DecimationFilter> for Filter {
135    fn from(decim: decim::DecimationFilter) -> Self {
136        Self::Decimation(decim)
137    }
138}
139
140impl std::str::FromStr for Filter {
141    type Err = Error;
142    fn from_str(content: &str) -> Result<Self, Self::Err> {
143        let items: Vec<&str> = content.split(':').collect();
144
145        let identifier = items[0].trim();
146        if identifier.eq("decim") {
147            let offset = 6; //"decim:"
148            Ok(Self::Decimation(DecimationFilter::from_str(
149                content[offset..].trim(),
150            )?))
151        } else if identifier.eq("mask") {
152            let offset = 5; //"mask:"
153            Ok(Self::Mask(MaskFilter::from_str(content[offset..].trim())?))
154        } else {
155            // assume Mask (omitted identifier)
156            if let Ok(f) = MaskFilter::from_str(content.trim()) {
157                Ok(Self::Mask(f))
158            } else {
159                Err(Error::UnknownFilterType(content.to_string()))
160            }
161        }
162    }
163}
164
165#[cfg(test)]
166mod test {
167    use super::*;
168    use std::str::FromStr;
169    #[test]
170    fn from_str() {
171        /*
172         * MASK FILTER description
173         */
174        for descriptor in [
175            "GPS",
176            "=GPS",
177            " != GPS",
178            "G08, G09, G10",
179            "=G08, G09, G10",
180            "!= GPS, GAL",
181            ">G08, G09",
182            "iode",
183            "iode,gps",
184            "iode,crs,gps",
185            "iode,crs",
186            ">2020-01-14T00:31:55 UTC",
187        ] {
188            assert!(
189                Filter::from_str(descriptor).is_ok(),
190                "Filter::from_str failed on \"{}\"",
191                descriptor
192            );
193        }
194        /*
195         * DECIMATION FILTER description
196         */
197        for desc in [
198            "decim:10",
199            "decim:10 min",
200            "decim:1 hour",
201            "decim:10 min:l1c",
202            "decim:1 hour:L1C,L2C,L3C",
203        ] {
204            let filt = Filter::from_str(desc);
205            assert!(filt.is_ok(), "Filter::from_str failed on \"{}\"", desc);
206        }
207        /*
208         * SMOOTHING FILTER description
209         */
210        for desc in [
211            "smooth:mov:10 min",
212            "smooth:mov:1 hour",
213            "smooth:mov:1 hour:l1c",
214            "smooth:mov:10 min:clk",
215            "smooth:hatch",
216            "smooth:hatch:l1c",
217        ] {
218            let filt = Filter::from_str(desc);
219            assert!(filt.is_ok(), "Filter::from_str failed on \"{}\"", desc);
220        }
221    }
222}