doris_rs/header/
parsing.rs1use 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 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; }
53
54 let (content, marker) = line.split_at(60);
55
56 let marker = marker.trim();
57
58 if marker.eq("END OF HEADER") {
59 break;
61 }
62 if marker.trim().eq("COMMENT") {
63 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 = 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 } 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 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 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 let mut y = y
235 .trim()
236 .parse::<u32>()
237 .map_err(|_| ParsingError::EpochFormat)?;
238
239 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 let mut ts = TimeScale::TAI;
282 let rem = rem.trim();
283
284 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}