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