1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
use crate::{processing::TargetItem, Duration};
use thiserror::Error;

#[derive(Error, Debug)]
pub enum Error {
    #[error("unknown decimation target")]
    TargetError(#[from] crate::algorithm::target::Error),
    #[error("failed to parse decimation attribute \"{0}\"")]
    AttributeParsingError(String),
}

/// Decimation Filters type
#[derive(Clone, Debug, PartialEq)]
pub enum DecimationType {
    /// Decimates Dataset by given factor.
    DecimByRatio(u32),
    /// Decimates Dataset so sampling rate matches given duration
    DecimByInterval(Duration),
}

#[derive(Clone, Debug, PartialEq)]
pub struct DecimationFilter {
    /// Optional data subset
    pub target: Option<TargetItem>,
    /// Type of decimation filter
    pub dtype: DecimationType,
}

pub trait Decimate {
    /// Decimate by a constant ratio.
    /// For example, if we decimate epochs {e_0, e_1, .., e_k, ..., e_n}
    /// by 2, we get {e_0, e_2, ..., e_k, e_k+2, ..}.
    /// Header sampling interval (if any) is automatically updated.
    /// ```
    /// use rinex::prelude::*;
    /// use rinex::processing::*;
    /// let mut rnx = Rinex::from_file("../test_resources/OBS/V2/delf0010.21o")
    ///     .unwrap();
    /// assert_eq!(rnx.epochs().len(), 105);
    /// assert_eq!(rnx.decimate_by_ratio(2).epochs().len(), 53);
    /// ```
    fn decimate_by_ratio(&self, r: u32) -> Self;
    /// [decimate_by_ratio] mutable implementation.
    fn decimate_by_ratio_mut(&mut self, r: u32);
    /// Decimate Dataset so sampling interval matches given duration.
    /// Successive epochs |e_k+1 - e_k| < interval that do not fit
    /// within this minimal interval are discarded.
    /// Header sampling interval (if any) is automatically update.
    /// ```
    /// use rinex::prelude::*;
    /// use rinex::processing::*; // Decimation
    /// let mut rinex = Rinex::from_file("../test_resources/NAV/V3/AMEL00NLD_R_20210010000_01D_MN.rnx")
    ///     .unwrap();
    ///
    /// let initial_epochs = rinex.epochs();
    ///
    /// // reduce to 10s sampling interval
    /// rinex.decimate_by_interval_mut(Duration::from_seconds(10.0));
    /// assert_eq!(rinex.epochs(), initial_epochs); // unchanged: dt is too short
    ///
    /// // reduce to 1hour sampling interval
    /// rinex.decimate_by_interval_mut(Duration::from_hours(1.0));
    /// assert_eq!(rinex.epochs().len(), initial_epochs.len()-2);
    /// ```
    fn decimate_by_interval(&self, dt: Duration) -> Self;
    /// [decimate_by_interval] mutable implementation
    fn decimate_by_interval_mut(&mut self, dt: Duration);
    /// Decimate Dataset so sampling matches given `rhs` sampling.
    /// Both types must match.
    fn decimate_match(&self, rhs: &Self) -> Self;
    /// [decimate_match] mutable implementation
    fn decimate_match_mut(&mut self, rhs: &Self);
}

impl std::str::FromStr for DecimationFilter {
    type Err = Error;
    fn from_str(content: &str) -> Result<Self, Self::Err> {
        let items: Vec<&str> = content.trim().split(":").collect();
        if let Ok(dt) = Duration::from_str(items[0].trim()) {
            Ok(Self {
                target: {
                    if items.len() > 1 {
                        let target = TargetItem::from_str(items[1].trim())?;
                        Some(target)
                    } else {
                        None // no subset description
                    }
                },
                dtype: DecimationType::DecimByInterval(dt),
            })
        } else if let Ok(r) = u32::from_str_radix(items[0].trim(), 10) {
            Ok(Self {
                target: {
                    if items.len() > 1 {
                        let target = TargetItem::from_str(items[1].trim())?;
                        Some(target)
                    } else {
                        None
                    }
                },
                dtype: DecimationType::DecimByRatio(r),
            })
        } else {
            Err(Error::AttributeParsingError(items[0].to_string()))
        }
    }
}