doris_rs/record/
parsing.rs

1use std::io::{BufRead, BufReader, Read};
2
3use crate::{
4    epoch::parse_in_timescale as parse_epoch_in_timescale,
5    error::ParsingError,
6    prelude::{
7        ClockOffset, Duration, Epoch, EpochFlag, GroundStation, Header, Key, Matcher, Measurements,
8        Observable, Observation, Record, TimeScale, SNR,
9    },
10};
11
12#[cfg(feature = "log")]
13use log::{debug, error};
14
15impl Record {
16    /// Parses the DORIS [Record] content by consuming the [Reader] until the end of stream.
17    /// This requires reference to previously parsed [Header] section.
18    pub fn parse<R: Read>(
19        header: &mut Header,
20        reader: &mut BufReader<R>,
21    ) -> Result<Self, ParsingError> {
22        const EPOCH_SIZE: usize = "YYYY MM DD HH MM SS.NNNNNNNNN  0".len();
23        const CLOCK_OFFSET: usize = 38;
24        const CLOCK_SIZE: usize = 19;
25        const MIN_EPOCH_SIZE: usize = EPOCH_SIZE + CLOCK_SIZE + 2;
26        const OBSERVABLE_WIDTH: usize = 14;
27
28        // eos reached: process pending buffer & exit
29        let mut eos = false;
30
31        // current line storage
32        let mut buf_len = 0;
33        let mut line_buf = String::with_capacity(128);
34
35        // epoch storage
36        let mut epoch_buf = String::with_capacity(1024);
37
38        let mut record = Record::default();
39
40        let observables = &header.observables;
41        let nb_observables = observables.len();
42
43        // Iterate and consume, one line at a time
44        while let Ok(size) = reader.read_line(&mut line_buf) {
45            if size == 0 {
46                // reached EOS: consume buffer & exit
47                eos |= true;
48            }
49
50            let line_len = line_buf.len();
51
52            if line_len > 60 {
53                if line_buf.contains("COMMENT") {
54                    // Comments are stored as is
55                    let comment = line_buf.split_at(60).0.trim_end();
56                    record.comments.push(comment.to_string());
57
58                    line_buf.clear();
59                    continue; // skip parsing
60                }
61            }
62
63            // tries to assemble a complete epoch
64            let mut new_epoch = false;
65
66            // new epoch
67            if line_buf.starts_with('>') || eos {
68                new_epoch = true;
69
70                let mut obs_ptr = 0;
71                let mut epoch = Epoch::default();
72                let flag = EpochFlag::default();
73                let mut station = Option::<&GroundStation>::None;
74                let mut clock_offset = Option::<ClockOffset>::None;
75
76                for (nth, line) in epoch_buf.lines().enumerate() {
77                    let line_len = line.len();
78
79                    if nth == 0 {
80                        // parse date & time
81                        if line_len < MIN_EPOCH_SIZE {
82                            continue;
83                        }
84
85                        epoch = parse_epoch_in_timescale(&line[2..2 + EPOCH_SIZE], TimeScale::TAI)?;
86
87                        let mut measurement = Measurements::default();
88
89                        // parse clock offset, if any
90                        if line_len >= CLOCK_OFFSET + CLOCK_SIZE {
91                            let clock_offset_secs = &line[CLOCK_OFFSET..CLOCK_OFFSET + CLOCK_SIZE]
92                                .trim()
93                                .parse::<f64>()
94                                .map_err(|_| ParsingError::ClockOffset)?;
95
96                            let dt = Duration::from_seconds(*clock_offset_secs);
97                            clock_offset = Some(ClockOffset::from_measured_offset(dt));
98
99                            // clock extrapolation flag
100                            if line_len > CLOCK_OFFSET + CLOCK_SIZE {
101                                if line[CLOCK_OFFSET + CLOCK_SIZE..].trim().eq("1") {
102                                    if let Some(clock_offset) = &mut clock_offset {
103                                        clock_offset.extrapolated = true;
104                                    }
105                                }
106                            }
107                            measurement.satellite_clock_offset = clock_offset;
108                        }
109                    } else {
110                        if line.starts_with("D") {
111                            // new station starting
112                            obs_ptr = 0;
113
114                            // station identification
115                            let station_id = line[1..3]
116                                .trim()
117                                .parse::<u16>()
118                                .map_err(|_| ParsingError::StationFormat)?;
119
120                            let matcher = Matcher::ID(station_id);
121
122                            // identification
123                            if let Some(matching) = header
124                                .ground_stations
125                                .iter()
126                                .filter(|station| station.matches(&matcher))
127                                .reduce(|k, _| k)
128                            {
129                                station = Some(matching);
130                            } else {
131                                #[cfg(feature = "log")]
132                                debug!("unidentified station: #{:02}", station_id);
133                            }
134                        }
135
136                        // station must be identified
137                        if let Some(station) = station {
138                            // identified
139                            let key = Key { epoch, flag };
140
141                            let mut offset = 3;
142
143                            loop {
144                                if offset + OBSERVABLE_WIDTH + 1 < line_len {
145                                    let slice = &line[offset..offset + OBSERVABLE_WIDTH];
146
147                                    match slice.trim().parse::<f64>() {
148                                        Ok(mut value) => {
149                                            let mut observation = Observation::default();
150
151                                            if observables[obs_ptr] == Observable::FrequencyRatio {
152                                                value *= 1.0E-11;
153                                            }
154
155                                            observation.value = value;
156                                            if let Some(measurements) =
157                                                record.measurements.get_mut(&key)
158                                            {
159                                                measurements.add_observation(
160                                                    station.clone(),
161                                                    observables[obs_ptr],
162                                                    observation,
163                                                );
164                                            } else {
165                                                let mut measurements = Measurements::default();
166
167                                                measurements.add_observation(
168                                                    station.clone(),
169                                                    observables[obs_ptr],
170                                                    observation,
171                                                );
172
173                                                measurements.satellite_clock_offset = clock_offset;
174
175                                                record
176                                                    .measurements
177                                                    .insert(key.clone(), measurements);
178                                            }
179                                        },
180                                        #[cfg(feature = "log")]
181                                        Err(e) => {
182                                            error!("observation parsing error: {}", e);
183                                        },
184                                        #[cfg(not(feature = "log"))]
185                                        Err(_) => {},
186                                    }
187                                }
188
189                                offset += OBSERVABLE_WIDTH;
190
191                                if offset + 1 < line_len {
192                                    let slice = &line[offset..offset + 1];
193                                    // println!("slice \"{}\"", slice);
194
195                                    if let Ok(snr) = slice.trim().parse::<SNR>() {
196                                        if let Some(measurements) =
197                                            record.measurements.get_mut(&key)
198                                        {
199                                            // if let Some(observation) = measurements
200                                            //     .observations
201                                            //     .get_mut(&observables[obs_ptr])
202                                            // {
203                                            //     observation.snr = Some(snr);
204                                            // }
205                                        }
206                                    }
207                                }
208
209                                offset += 1;
210
211                                if offset + 1 < line_len {
212                                    // let slice = &line[offset..offset + 1];
213                                    // println!("slice \"{}\"", slice);
214
215                                    // if let Ok(flag) = slice.trim().parse::<Flag>() {
216                                    //     if let Some(measurements) =
217                                    //         record.measurements.get_mut(&key)
218                                    //     {
219                                    //         if let Some(observation) = measurements
220                                    //             .observations
221                                    //             .get_mut(&observables[obs_ptr])
222                                    //         {
223                                    //             observation.phase_flag = Some(flag);
224                                    //         }
225                                    //     }
226                                    // }
227                                }
228
229                                offset += 1;
230                                obs_ptr += 1;
231
232                                if offset >= line_len {
233                                    break;
234                                }
235
236                                // detect potential errors
237                                if obs_ptr >= nb_observables {
238                                    break;
239                                }
240                            }
241                        }
242                    }
243                } // epoch parsing
244            } // new epoch
245
246            // clear on new epoch detection
247            if new_epoch {
248                buf_len = 0;
249                epoch_buf.clear();
250            }
251
252            // always stack new content
253            epoch_buf.push_str(&line_buf);
254            buf_len += size;
255
256            line_buf.clear(); // always clear newline buf
257
258            if eos {
259                break;
260            }
261        } //while
262
263        Ok(record)
264    }
265}