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