1extern crate nom;
2
3use nom::{
4 bytes::complete::{tag, take, take_until},
5 combinator::{map, map_opt, map_parser, map_res, opt, rest},
6 sequence::tuple,
7 IResult,
8};
9use serde::{Deserialize, Serialize};
10use std::{error, fmt};
11
12#[derive(Debug)]
13pub struct TLEError;
14
15pub type Result<T> = std::result::Result<T, TLEError>;
16
17impl fmt::Display for TLEError {
18 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
19 write!(f, "Invalid TLE Format")
20 }
21}
22
23impl error::Error for TLEError {
24 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
25 None
27 }
28}
29
30#[derive(Debug, PartialEq, Serialize, Deserialize)]
31pub struct TLE {
32 pub name: String,
33 pub satellite_number: u32,
34 pub classification: char,
35 pub international_designator: String,
36 pub epoch: String,
38 pub first_derivative_mean_motion: f64,
39 pub second_derivative_mean_motion: f64,
40 pub drag_term: f64,
41 pub ephemeris_type: u32,
42 pub element_number: u32,
43 pub inclination: f64,
44 pub right_ascension: f64,
45 pub eccentricity: f64,
46 pub argument_of_perigee: f64,
47 pub mean_anomaly: f64,
48 pub mean_motion: f64,
49 pub revolution_number: u32,
50}
51
52impl TLE {
53 pub fn to_json(&self) -> String {
54 let json = serde_json::to_string_pretty(&self).unwrap();
55 json
56 }
57}
58
59fn ugly_float_parser(input: &str) -> IResult<&str, f64> {
61 map_res(
62 tuple((opt(tag("-")), take_until("-"), tag("-"), rest)),
63 |(sign, a, _, b): (Option<&str>, &str, &str, &str)| {
64 format!("{}0.{}e-{}", sign.unwrap_or(""), a, b).parse::<f64>()
65 },
66 )(input)
67}
68
69fn satellite_number_parser(input: &str) -> IResult<&str, u32> {
70 map_res(take(5usize), |i: &str| i.parse::<u32>())(input)
71}
72
73fn one_space_parser(input: &str) -> IResult<&str, &str> {
74 tag(" ")(input)
75}
76
77fn take_and_trim_parser(num: usize) -> impl Fn(&str) -> IResult<&str, &str> {
78 move |x| map(take(num), |i: &str| i.trim())(x)
79}
80
81pub fn parse(raw_tle: &str) -> Result<TLE> {
82 let (
83 _,
84 (
85 name,
86 _,
87 (
88 _,
89 _,
90 satellite_number,
91 classification,
92 _,
93 international_designator,
94 _,
95 epoch,
96 _,
97 first_derivative_mean_motion,
98 _,
99 second_derivative_mean_motion,
100 _,
101 drag_term,
102 _,
103 ephemeris_type,
104 _,
105 element_number,
106 _check_sum,
107 ),
108 _,
109 (
111 _,
112 _,
113 _satellite_number,
114 _,
115 inclination,
116 _,
117 right_ascension,
118 _,
119 eccentricity,
120 _,
121 argument_of_perigee,
122 _,
123 mean_anomaly,
124 _,
125 mean_motion,
126 revolution_number,
127 _,
128 ),
129 ),
130 ) = tuple((
131 take_until("\n"),
132 tag("\n"),
133 map_parser(
135 take_until("\n"),
136 tuple((
137 tag("1"),
138 one_space_parser,
139 satellite_number_parser,
140 map_opt(take(1usize), |i: &str| i.chars().nth(0usize)),
141 one_space_parser,
142 take_and_trim_parser(8usize),
143 one_space_parser,
144 map(take(14usize), |i: &str| i.trim()),
145 one_space_parser,
146 map_res(take_and_trim_parser(10usize), |i: &str| i.parse::<f64>()),
147 one_space_parser,
148 map_parser(take_and_trim_parser(8usize), ugly_float_parser),
149 one_space_parser,
150 map_parser(take_and_trim_parser(8usize), ugly_float_parser),
151 one_space_parser,
152 map_res(take(1usize), |i: &str| i.parse::<u32>()),
153 one_space_parser,
154 map_res(take_and_trim_parser(4usize), |i: &str| i.parse::<u32>()),
155 map_res(take(1usize), |i: &str| i.parse::<u32>()),
156 )),
157 ),
158 tag("\n"),
159 tuple((
161 tag("2"),
162 one_space_parser,
163 satellite_number_parser,
164 one_space_parser,
165 map_res(take_and_trim_parser(8usize), |i: &str| i.parse::<f64>()),
166 one_space_parser,
167 map_res(take_and_trim_parser(8usize), |i: &str| i.parse::<f64>()),
168 one_space_parser,
169 map_res(take(7usize), |i: &str| format!("0.{}", i).parse::<f64>()),
170 one_space_parser,
171 map_res(take_and_trim_parser(8usize), |i: &str| i.parse::<f64>()),
172 one_space_parser,
173 map_res(take_and_trim_parser(8usize), |i: &str| i.parse::<f64>()),
174 one_space_parser,
175 map_res(take_and_trim_parser(11usize), |i: &str| i.parse::<f64>()),
176 map_res(take_and_trim_parser(5usize), |i: &str| i.parse::<u32>()),
177 map_res(take(1usize), |i: &str| i.parse::<u32>()),
178 )),
179 ))(raw_tle)
180 .map_err(|e| {
181 println!("🤔 Error - {}", e);
182 TLEError
183 })?;
184
185 Ok(TLE {
186 name: String::from(name),
187 satellite_number,
188 classification,
189 international_designator: String::from(international_designator),
190 epoch: String::from(epoch),
191 first_derivative_mean_motion,
192 second_derivative_mean_motion,
193 drag_term,
194 ephemeris_type,
195 element_number,
196 inclination,
197 right_ascension,
198 eccentricity,
199 argument_of_perigee,
200 mean_anomaly,
201 mean_motion,
202 revolution_number,
203 })
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn parse_ugly_float() {
212 let (_, f) = ugly_float_parser("36258-4").unwrap();
213 assert_eq!(f, 0.36258e-4);
214
215 let (_, f) = ugly_float_parser("00000-0").unwrap();
216 assert_eq!(f, 0.0);
217
218 let (_, f) = ugly_float_parser("-36258-4").unwrap();
219 assert_eq!(f, -0.36258e-4);
220 }
221
222 #[test]
223 fn parse_grus_tle() {
224 let raw_tle = "GRUS-1A
2251 43890U 18111Q 20044.88470557 .00000320 00000-0 36258-4 0 9993
2262 43890 97.7009 312.6237 0003899 7.8254 352.3026 14.92889838 61757";
227
228 let tle = parse(raw_tle).unwrap();
229
230 assert_eq!(tle.name, "GRUS-1A");
231 assert_eq!(tle.satellite_number, 43890);
232 assert_eq!(tle.classification, 'U');
233 assert_eq!(tle.international_designator, "18111Q");
234 assert_eq!(tle.epoch, "20044.88470557");
235 assert_eq!(tle.first_derivative_mean_motion, 0.00000320);
236 assert_eq!(tle.second_derivative_mean_motion, 0.0);
237 assert_eq!(tle.drag_term, 0.36258e-4);
238 assert_eq!(tle.ephemeris_type, 0);
239 assert_eq!(tle.element_number, 999);
240 assert_eq!(tle.inclination, 97.7009);
242 assert_eq!(tle.right_ascension, 312.6237);
243 assert_eq!(tle.eccentricity, 0.0003899);
244 assert_eq!(tle.argument_of_perigee, 7.8254);
245 assert_eq!(tle.mean_anomaly, 352.3026);
246 assert_eq!(tle.mean_motion, 14.92889838);
247 assert_eq!(tle.revolution_number, 6175);
248 }
249
250 #[test]
251 fn parse_iss_tle() {
252 let raw_tle = "ISS (ZARYA)
2531 25544U 98067A 20045.18587073 .00000950 00000-0 25302-4 0 9990
2542 25544 51.6443 242.0161 0004885 264.6060 207.3845 15.49165514212791";
255
256 let expected = TLE {
257 name: String::from("ISS (ZARYA)"),
258 satellite_number: 25544,
259 classification: 'U',
260 international_designator: String::from("98067A"),
261 epoch: String::from("20045.18587073"),
262 first_derivative_mean_motion: 0.00000950,
263 second_derivative_mean_motion: 0.0,
264 drag_term: 0.25302e-4,
265 ephemeris_type: 0,
266 element_number: 999,
267 inclination: 51.6443,
268 right_ascension: 242.0161,
269 eccentricity: 0.0004885,
270 argument_of_perigee: 264.6060,
271 mean_anomaly: 207.3845,
272 mean_motion: 15.49165514,
273 revolution_number: 21279,
274 };
275
276 let tle = parse(&raw_tle).unwrap();
277
278 assert_eq!(tle, expected);
279 }
280}