doris_rs/header/
parsing.rs

1use crate::{
2    error::ParsingError,
3    header::{Antenna, Header, Receiver, Version},
4    observable::Observable,
5    prelude::{Duration, Epoch, TimeScale, COSPAR},
6    station::GroundStation,
7    Comments,
8};
9
10use std::{
11    collections::HashMap,
12    io::{BufRead, BufReader, Read},
13    str::FromStr,
14};
15
16impl Header {
17    /// Parse [Header] by consuming [BufReader] until end of this section
18    pub fn parse<R: Read>(reader: &mut BufReader<R>) -> Result<Self, ParsingError> {
19        let mut version = Version::default();
20
21        let mut satellite = String::with_capacity(16);
22        let mut program = Option::<String>::None;
23        let mut run_by = Option::<String>::None;
24        let mut date = Option::<String>::None;
25        let mut observer = Option::<String>::None;
26        let mut agency = Option::<String>::None;
27        let mut license = Option::<String>::None;
28        let mut doi = Option::<String>::None;
29        let mut receiver = Option::<Receiver>::None;
30        let mut antenna = Option::<Antenna>::None;
31        let mut cospar = Option::<COSPAR>::None;
32        let mut l1_l2_date_offset = Duration::default();
33        let mut ground_stations = Vec::with_capacity(8);
34        let mut scaling_factors = HashMap::new();
35        let mut time_of_first_observation = Option::<Epoch>::None;
36        let mut time_of_last_observation = Option::<Epoch>::None;
37
38        let mut observables = Vec::<Observable>::with_capacity(8);
39        let mut observables_continuation = false;
40
41        let mut comments = Comments::default();
42
43        for line in reader.lines() {
44            if line.is_err() {
45                // invalid file op
46                continue;
47            }
48
49            let line = line.unwrap();
50
51            if line.len() < 60 {
52                // invalid content
53                continue;
54            }
55
56            let (content, marker) = line.split_at(60);
57            let marker = marker.trim();
58
59            if marker.eq("END OF HEADER") {
60                // special marker: done parsing
61                break;
62            }
63
64            if marker.eq("COMMENT") {
65                // Comments are stored as is.
66                comments.push(content.trim().to_string());
67                continue;
68            } else if marker.eq("RINEX VERSION / TYPE") {
69                let (vers, rem) = line.split_at(20);
70                let (type_str, rem) = rem.split_at(20);
71                let (constell_str, _) = rem.split_at(20);
72
73                let vers = vers.trim();
74                let type_str = type_str.trim();
75                let constell_str = constell_str.trim();
76
77                if !type_str.eq("O") {
78                    return Err(ParsingError::InvalidDoris);
79                }
80
81                if !constell_str.eq("D") {
82                    return Err(ParsingError::InvalidDoris);
83                }
84
85                // version string
86                version = Version::from_str(vers).or(Err(ParsingError::Version))?;
87            } else if marker.eq("PGM / RUN BY / DATE") {
88                let (pgm, rem) = line.split_at(20);
89                let pgm = pgm.trim();
90                if pgm.len() > 0 {
91                    program = Some(pgm.to_string());
92                }
93
94                let (runby, rem) = rem.split_at(20);
95
96                let runby = runby.trim();
97                if runby.len() > 0 {
98                    run_by = Some(runby.to_string());
99                }
100
101                let date_str = rem.split_at(20).0.trim();
102                if date_str.len() > 0 {
103                    date = Some(date_str.to_string());
104                }
105            } else if marker.eq("SATELLITE NAME") {
106                let name = content.split_at(20).0.trim();
107                satellite = name.to_string();
108            } else if marker.eq("OBSERVER / AGENCY") {
109                let (obs, ag) = content.split_at(20);
110                let obs = obs.trim();
111                let ag = ag.trim();
112
113                if obs.len() > 0 {
114                    observer = Some(obs.to_string());
115                }
116
117                if ag.len() > 0 {
118                    agency = Some(ag.to_string());
119                }
120            } else if marker.eq("REC # / TYPE / VERS") {
121                if let Ok(rx) = Receiver::from_str(content) {
122                    receiver = Some(rx);
123                }
124            } else if marker.eq("SYS / SCALE FACTOR") {
125                // // Parse scaling factor
126                // let (factor, rem) = rem.split_at(6);
127                // let factor = factor.trim();
128                // let scaling = factor
129                //     .parse::<u16>()
130                //     .or(Err(ParsingError::SystemScalingFactor))?;
131
132                // // parse end of line
133                // let (_num, rem) = rem.split_at(3);
134                // for observable_str in rem.split_ascii_whitespace() {
135                //     let observable = Observable::from_str(observable_str)?;
136
137                //     // latch scaling value
138                //     if rinex_type == Type::DORIS {
139                //         doris.with_scaling(observable, scaling);
140                //     } else {
141                //         observation.with_scaling(constell, observable, scaling);
142                //     }
143                // }
144            } else if marker.eq("LICENSE OF USE") {
145                let lic = content.split_at(40).0.trim();
146                if lic.len() > 0 {
147                    license = Some(lic.to_string());
148                }
149            } else if marker.eq("DOI") {
150                let content = content.split_at(40).0.trim();
151
152                if content.len() > 0 {
153                    doi = Some(content.to_string());
154                }
155            } else if marker.eq("ANT # / TYPE") {
156                let (sn, rem) = content.split_at(20);
157                let (model, _) = rem.split_at(20);
158
159                antenna = Some(
160                    Antenna::default()
161                        .with_model(model.trim())
162                        .with_serial_number(sn.trim()),
163                );
164            } else if marker.eq("# OF STATIONS") {
165            } else if marker.eq("TIME OF FIRST OBS") {
166                time_of_first_observation = Some(Self::parse_time_of_obs(content)?);
167            } else if marker.eq("TIME OF LAST OBS") {
168                time_of_last_observation = Some(Self::parse_time_of_obs(content)?);
169            } else if marker.eq("SYS / # / OBS TYPES") {
170                if observables_continuation {
171                    for item in content.split_ascii_whitespace() {
172                        if let Ok(observable) = Observable::from_str(item) {
173                            observables.push(observable);
174                        }
175                    }
176                } else {
177                    Self::parse_observables(content, &mut observables);
178                    observables_continuation = true;
179                }
180            } else if marker.eq("COSPAR NUMBER") {
181                cospar = Some(COSPAR::from_str(content.trim())?);
182            } else if marker.eq("L2 / L1 DATE OFFSET") {
183                // DORIS special case
184                let content = content[1..].trim();
185
186                let time_offset_us = content
187                    .parse::<f64>()
188                    .or(Err(ParsingError::DorisL1L2DateOffset))?;
189
190                l1_l2_date_offset = Duration::from_microseconds(time_offset_us);
191            } else if marker.eq("STATION REFERENCE") {
192                // DORIS special case
193                let station = GroundStation::from_str(content.trim())?;
194                ground_stations.push(station);
195            }
196        }
197
198        Ok(Header {
199            version,
200            comments,
201            program,
202            run_by,
203            date,
204            agency,
205            observer,
206            license,
207            doi,
208            receiver,
209            antenna,
210            cospar,
211            satellite,
212            scaling_factors,
213            l1_l2_date_offset,
214            observables,
215            ground_stations,
216            time_of_first_observation,
217            time_of_last_observation,
218        })
219    }
220
221    fn parse_observables(line: &str, observables: &mut Vec<Observable>) {
222        let items = line.split_at(6).1;
223        for item in items.split_ascii_whitespace() {
224            if let Ok(observable) = Observable::from_str(item) {
225                observables.push(observable);
226            }
227        }
228    }
229
230    fn parse_time_of_obs(content: &str) -> Result<Epoch, ParsingError> {
231        let (_, rem) = content.split_at(2);
232        let (y, rem) = rem.split_at(4);
233        let (m, rem) = rem.split_at(6);
234        let (d, rem) = rem.split_at(6);
235        let (hh, rem) = rem.split_at(6);
236        let (mm, rem) = rem.split_at(6);
237        let (ss, rem) = rem.split_at(5);
238        let (_dot, rem) = rem.split_at(1);
239        let (ns, rem) = rem.split_at(8);
240
241        // println!("Y \"{}\" M \"{}\" D \"{}\" HH \"{}\" MM \"{}\" SS \"{}\" NS \"{}\"", y, m, d, hh, mm, ss, ns); // DEBUG
242        let mut y = y
243            .trim()
244            .parse::<u32>()
245            .map_err(|_| ParsingError::EpochFormat)?;
246
247        // handle OLD RINEX problem
248        if y >= 79 && y <= 99 {
249            y += 1900;
250        } else if y < 79 {
251            y += 2000;
252        }
253
254        let m = m
255            .trim()
256            .parse::<u8>()
257            .map_err(|_| ParsingError::EpochFormat)?;
258
259        let d = d
260            .trim()
261            .parse::<u8>()
262            .map_err(|_| ParsingError::EpochFormat)?;
263
264        let hh = hh
265            .trim()
266            .parse::<u8>()
267            .map_err(|_| ParsingError::EpochFormat)?;
268
269        let mm = mm
270            .trim()
271            .parse::<u8>()
272            .map_err(|_| ParsingError::EpochFormat)?;
273
274        let ss = ss
275            .trim()
276            .parse::<u8>()
277            .map_err(|_| ParsingError::EpochFormat)?;
278
279        let ns = ns
280            .trim()
281            .parse::<u32>()
282            .map_err(|_| ParsingError::EpochFormat)?;
283
284        /*
285         * We set TAI as "default" Timescale.
286         * Timescale might be omitted in Old RINEX formats,
287         * In this case, we exit with "TAI" and handle that externally.
288         */
289        let mut ts = TimeScale::TAI;
290        let rem = rem.trim();
291
292        /*
293         * Handles DORIS measurement special case,
294         * offset from TAI, that we will convert back to TAI later
295         */
296        if !rem.is_empty() && rem != "DOR" {
297            ts = TimeScale::from_str(rem.trim())?;
298        }
299
300        Epoch::from_str(&format!(
301            "{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:08} {}",
302            y, m, d, hh, mm, ss, ns, ts
303        ))
304        .map_err(|_| ParsingError::EpochFormat)
305    }
306}
307
308#[cfg(test)]
309mod test {
310    use crate::prelude::{Epoch, Header};
311    use std::str::FromStr;
312
313    #[test]
314    fn parse_time_of_obs() {
315        let content = "  2021    12    21     0     0    0.0000000     GPS";
316        let parsed = Header::parse_time_of_obs(&content).unwrap();
317        assert_eq!(parsed, Epoch::from_str("2021-12-21T00:00:00 GPST").unwrap());
318
319        let content = "  1995    01    01    00    00   00.000000             ";
320        let parsed = Header::parse_time_of_obs(&content).unwrap();
321        assert_eq!(parsed, Epoch::from_str("1995-01-01T00:00:00 TAI").unwrap());
322    }
323}