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}