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}