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