1use std::{collections::HashMap, str::FromStr};
2
3use crate::{
4 antex::{Antenna, AntennaSpecific, Calibration, CalibrationMethod, RxAntenna, SvAntenna},
5 linspace::Linspace,
6 prelude::{Carrier, Epoch, ParsingError, COSPAR, SV},
7};
8
9#[cfg(feature = "serde")]
10use serde::Serialize;
11
12pub(crate) fn is_new_epoch(content: &str) -> bool {
17 content.contains("START OF ANTENNA")
18}
19
20#[derive(Debug, Clone, PartialEq, PartialOrd)]
23#[cfg_attr(feature = "serde", derive(Serialize))]
24pub enum AntennaPhasePattern {
25 AzimuthIndependentPattern(Vec<f64>),
27}
28
29impl Default for AntennaPhasePattern {
30 fn default() -> Self {
31 Self::AzimuthIndependentPattern(Vec::<f64>::new())
32 }
33}
34
35#[derive(Debug, Default, Clone, PartialEq, PartialOrd)]
36#[cfg_attr(feature = "serde", derive(Serialize))]
37pub struct FrequencyDependentData {
38 pub apc_eccentricity: (f64, f64, f64),
43 pub phase_pattern: AntennaPhasePattern,
46}
47
48pub type Record = Vec<(Antenna, HashMap<Carrier, FrequencyDependentData>)>;
89
90fn parse_datetime(content: &str) -> Result<Epoch, ParsingError> {
91 let mut parser = content.split('-');
92
93 let year = parser.next().ok_or(ParsingError::DatetimeFormat)?;
94
95 let year = year
96 .parse::<i32>()
97 .map_err(|_| ParsingError::DatetimeParsing)?;
98
99 let month = parser.next().ok_or(ParsingError::DatetimeFormat)?;
100
101 let month = match month {
102 "JAN" | "Jan" => 1,
103 "FEB" | "Feb" => 2,
104 "MAR" | "Mar" => 3,
105 "APR" | "Apr" => 4,
106 "MAY" | "May" => 5,
107 "JUN" | "Jun" => 6,
108 "JUL" | "Jul" => 7,
109 "AUG" | "Aug" => 8,
110 "SEP" | "Sep" => 9,
111 "OCT" | "Oct" => 10,
112 "NOV" | "Nov" => 11,
113 "DEC" | "Dec" => 12,
114 _ => {
115 return Err(ParsingError::DatetimeParsing);
116 },
117 };
118
119 let day = parser.next().ok_or(ParsingError::DatetimeFormat)?;
120 let day = day
121 .parse::<u8>()
122 .map_err(|_| ParsingError::DatetimeParsing)?;
123
124 Ok(Epoch::from_gregorian_utc_at_midnight(
125 2000 + year,
126 month,
127 day,
128 ))
129}
130
131fn parse_validity_epoch(content: &str) -> Result<Epoch, ParsingError> {
135 let mut items = content.split_ascii_whitespace();
136
137 let year = items.next().ok_or(ParsingError::DatetimeFormat)?;
138
139 let year = year
140 .parse::<i32>()
141 .map_err(|_| ParsingError::DatetimeParsing)?;
142
143 let month = items.next().ok_or(ParsingError::DatetimeFormat)?;
144
145 let month = month
146 .parse::<u8>()
147 .map_err(|_| ParsingError::DatetimeParsing)?;
148
149 let day = items.next().ok_or(ParsingError::DatetimeFormat)?;
150 let day = day
151 .parse::<u8>()
152 .map_err(|_| ParsingError::DatetimeParsing)?;
153
154 let hh = items.next().ok_or(ParsingError::DatetimeFormat)?;
155 let hh = hh
156 .parse::<u8>()
157 .map_err(|_| ParsingError::DatetimeParsing)?;
158
159 let mm = items.next().ok_or(ParsingError::DatetimeFormat)?;
160 let mm = mm.parse::<u8>().map_err(|_| ParsingError::DatetimeFormat)?;
161
162 let ss = items.next().ok_or(ParsingError::DatetimeParsing)?;
163
164 let secs: u8;
165 let mut nanos = 0_u32;
166
167 if let Some(dot) = ss.find('.') {
168 secs = ss[..dot]
169 .trim()
170 .parse::<u8>()
171 .map_err(|_| ParsingError::DatetimeParsing)?;
172
173 nanos = ss[dot + 1..]
174 .trim()
175 .parse::<u32>()
176 .map_err(|_| ParsingError::DatetimeParsing)?;
177 } else {
178 secs = ss
179 .parse::<u8>()
180 .map_err(|_| ParsingError::DatetimeParsing)?;
181 }
182
183 Ok(Epoch::from_gregorian_utc(
184 year, month, day, hh, mm, secs, nanos,
185 ))
186}
187
188pub(crate) fn parse_antenna(
191 content: &str,
192) -> Result<(Antenna, HashMap<Carrier, FrequencyDependentData>), ParsingError> {
193 let lines = content.lines();
194 let mut antenna = Antenna::default();
195 let mut inner = HashMap::<Carrier, FrequencyDependentData>::new();
196 let mut frequency = Carrier::default();
197 let mut freq_data = FrequencyDependentData::default();
198 let mut valid_from = Epoch::default();
199
200 for line in lines {
201 let (content, marker) = line.split_at(60);
202 if marker.contains("TYPE / SERIAL NO") {
203 let (ant_igs, rem) = content.split_at(16); let (block1, rem) = rem.split_at(20 + 4);
205 let (block2, rem) = rem.split_at(10);
206 let (block3, _rem) = rem.split_at(10);
207
208 let (block1, block2, block3) = (block1.trim(), block2.trim(), block3.trim());
209 let specificities = match block2.is_empty() && block3.is_empty() {
213 false => AntennaSpecific::SvAntenna(SvAntenna {
214 igs_type: ant_igs.trim().to_string(),
215 sv: SV::from_str(block1)?,
216 cospar: COSPAR::from_str(block3)?,
217 }),
218 true => AntennaSpecific::RxAntenna(RxAntenna {
219 igs_type: ant_igs.trim().to_string(),
220 serial_number: {
221 if !block1.is_empty() && !block1.eq("NONE") {
222 Some(block1.to_string())
223 } else {
224 None
225 }
226 },
227 }),
228 };
229 antenna = antenna.with_specificities(specificities);
230 } else if marker.contains("METH / BY / # / DATE") {
231 let (method, rem) = content.split_at(20);
232 let (agency, rem) = rem.split_at(20);
233 let (number, rem) = rem.split_at(10); let (date, _) = rem.split_at(10);
235
236 let cal = Calibration {
237 method: CalibrationMethod::from_str(method.trim()).unwrap(),
238 number: number
239 .trim()
240 .parse::<u16>()
241 .map_err(|_| ParsingError::AntexAntennaCalibrationNumber)?,
242 agency: agency.trim().to_string(),
243 date: parse_datetime(date.trim())?,
244 validity_period: None,
245 };
246
247 antenna.calibration = cal.clone();
248 } else if marker.contains("VALID FROM") {
249 valid_from = parse_validity_epoch(content.trim())?;
250 } else if marker.contains("VALID UNTIL") {
251 let valid_until = parse_validity_epoch(content.trim())?;
252
253 antenna = antenna.with_validity_period(valid_from, valid_until);
254 } else if marker.contains("SINEX CODE") {
255 let sinex = content.split_at(20).0;
256
257 antenna.sinex_code = sinex.trim().to_string();
258 } else if marker.contains("DAZI") {
259 } else if marker.contains("# OF FREQUENCIES") {
264 } else if marker.contains("START OF FREQUENCY") {
269 let svnn = content.split_at(10).0;
270 let sv = SV::from_str(svnn.trim())?;
271 frequency = Carrier::from_sv(sv)?;
272 } else if marker.contains("NORTH / EAST / UP") {
273 let (north, rem) = content.split_at(10);
274 let (east, rem) = rem.split_at(10);
275 let (up, _) = rem.split_at(10);
276
277 let north = north
278 .trim()
279 .parse::<f64>()
280 .map_err(|_| ParsingError::AntexAPCCoordinates)?;
281
282 let east = east
283 .trim()
284 .parse::<f64>()
285 .map_err(|_| ParsingError::AntexAPCCoordinates)?;
286
287 let up = up
288 .trim()
289 .parse::<f64>()
290 .map_err(|_| ParsingError::AntexAPCCoordinates)?;
291
292 freq_data.apc_eccentricity = (north, east, up);
293 } else if marker.contains("ZEN1 / ZEN2 / DZEN") {
294 let (start, rem) = content.split_at(8);
295 let (end, rem) = rem.split_at(6);
296 let (spacing, _) = rem.split_at(6);
297
298 let start = start
299 .trim()
300 .parse::<f64>()
301 .map_err(|_| ParsingError::AntexZenithGrid)?;
302
303 let end = end
304 .trim()
305 .parse::<f64>()
306 .map_err(|_| ParsingError::AntexZenithGrid)?;
307
308 let spacing = spacing
309 .trim()
310 .parse::<f64>()
311 .map_err(|_| ParsingError::AntexZenithGrid)?;
312
313 antenna.zenith_grid = Linspace {
314 start,
315 end,
316 spacing,
317 };
318 } else if marker.contains("END OF FREQUENCY") {
319 inner.insert(frequency, freq_data.clone());
320 } else if marker.contains("END OF ANTENNA") {
321 break; } else {
324 }
326 }
338
339 Ok((antenna, inner))
340}
341
342#[cfg(test)]
343mod test {
344 use super::*;
345 #[test]
346 fn test_new_epoch() {
347 let content = " START OF ANTENNA";
348 assert!(is_new_epoch(content));
349 let content =
350 "TROSAR25.R4 LEIT727259 TYPE / SERIAL NO";
351 assert!(!is_new_epoch(content));
352 let content =
353 " 26 # OF FREQUENCIES";
354 assert!(!is_new_epoch(content));
355 let content =
356 " G01 START OF FREQUENCY";
357 assert!(!is_new_epoch(content));
358 }
359}