doris_rs/header/
mod.rs

1mod formatting;
2mod parsing;
3
4mod antenna;
5mod receiver;
6mod version;
7
8use itertools::Itertools;
9use std::collections::HashMap;
10
11use crate::{
12    prelude::{Duration, Epoch, GroundStation, Observable, COSPAR},
13    Comments,
14};
15
16#[cfg(doc)]
17use crate::prelude::Record;
18
19#[cfg(feature = "serde")]
20use serde::{Deserialize, Serialize};
21
22pub use antenna::Antenna;
23pub use receiver::Receiver;
24pub use version::Version;
25
26/// DORIS [Header]
27#[derive(Clone, Debug, PartialEq, Default)]
28#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
29pub struct Header {
30    /// [Version] describes the file revision
31    pub version: Version,
32
33    /// Comments found in the [Header] section
34    pub comments: Comments,
35
36    /// Name of the DORIS satellite
37    pub satellite: String,
38
39    /// Software program name.
40    pub program: Option<String>,
41
42    /// Software operator.
43    pub run_by: Option<String>,
44
45    /// Date of publication.
46    pub date: Option<String>,
47
48    /// Observer name
49    pub observer: Option<String>,
50
51    /// Production agency
52    pub agency: Option<String>,
53
54    /// Possible COSPAR number (launch information)
55    pub cospar: Option<COSPAR>,
56
57    /// Possible information about [Receiver] hardware
58    pub receiver: Option<Receiver>,
59
60    /// Possible information about receiver [Antenna]
61    pub antenna: Option<Antenna>,
62
63    /// Possible file license
64    pub license: Option<String>,
65
66    /// Possible Digital Object Identifier (DOI)
67    pub doi: Option<String>,
68
69    /// DORIS L1/L2 date offset
70    pub l1_l2_date_offset: Duration,
71
72    /// Describes measurements contained in following [Record]
73    pub observables: Vec<Observable>,
74
75    /// Possible scalings to apply to attached [Observable]s
76    pub scaling_factors: HashMap<Observable, f64>,
77
78    /// DORIS [GroundStation]s
79    pub ground_stations: Vec<GroundStation>,
80
81    /// Possible indication of the first measurement
82    pub time_of_first_observation: Option<Epoch>,
83
84    /// Possible indication of the last measurement
85    pub time_of_last_observation: Option<Epoch>,
86}
87
88impl Header {
89    /// Identify a [GroundStation] from [u16] (unique) identification
90    /// code, which is file or network dependent.
91    pub fn ground_station(&self, station_code: u16) -> Option<GroundStation> {
92        self.ground_stations
93            .iter()
94            .filter(|station| station.code == station_code)
95            .reduce(|k, _| k)
96            .cloned()
97    }
98
99    /// Formats the package version (possibly shortenned, in case of lengthy release)
100    /// to fit within a formatted COMMENT
101    pub(crate) fn format_pkg_version(version: &str) -> String {
102        version
103            .split('.')
104            .enumerate()
105            .filter_map(|(nth, v)| {
106                if nth < 2 {
107                    Some(v.to_string())
108                } else if nth == 2 {
109                    Some(
110                        v.split('-')
111                            .filter_map(|v| {
112                                if v == "rc" {
113                                    Some("rc".to_string())
114                                } else {
115                                    let mut s = String::new();
116                                    s.push_str(&v[0..1]);
117                                    Some(s)
118                                }
119                            })
120                            .join(""),
121                    )
122                } else {
123                    None
124                }
125            })
126            .join(".")
127    }
128
129    /// Generates the special "FILE MERGE" comment
130    pub(crate) fn merge_comment(pkg_version: &str, timestamp: Epoch) -> String {
131        let formatted_version = Self::format_pkg_version(pkg_version);
132
133        let (y, m, d, hh, mm, ss, _) = timestamp.to_gregorian_utc();
134
135        format!(
136            "doris-rs v{} {:>width$}          {}{:02}{:02} {:02}{:02}{:02} {:x}",
137            formatted_version,
138            "FILE MERGE",
139            y,
140            m,
141            d,
142            hh,
143            mm,
144            ss,
145            timestamp.time_scale,
146            width = 19 - formatted_version.len(),
147        )
148    }
149
150    /// Copies and returns [Header] with specific RINEX [Version]
151    pub fn with_version(&self, version: Version) -> Self {
152        let mut s = self.clone();
153        s.version = version;
154        s
155    }
156
157    /// Copies and returns [Header] with "Run By" field
158    pub fn with_run_by(&self, run_by: &str) -> Self {
159        let mut s = self.clone();
160        s.run_by = Some(run_by.to_string());
161        s
162    }
163
164    /// Copies and returns new [Header] with specific [Receiver]
165    pub fn with_receiver(&self, receiver: Receiver) -> Self {
166        let mut s = self.clone();
167        s.receiver = Some(receiver);
168        s
169    }
170
171    /// Adds one comment to mutable [Self]
172    pub fn push_comment(&mut self, comment: &str) {
173        self.comments.push(comment.to_string());
174    }
175
176    /// Copies and returns [Header] with one new comment.
177    pub fn with_comment(&self, comment: &str) -> Self {
178        let mut s = self.clone();
179        s.comments.push(comment.to_string());
180        s
181    }
182}