use sgp4::Elements;
use crate::satellite::sgp4_wrapper::Sgp4Error;
pub fn parse_tle(tle_string: &str) -> Result<Elements, Sgp4Error> {
let lines: Vec<&str> = tle_string
.lines()
.map(|l| l.trim())
.filter(|l| !l.is_empty())
.collect();
match lines.len() {
2 => {
parse_2line_tle(lines[0], lines[1])
}
3 => {
parse_3line_tle(lines[0], lines[1], lines[2])
}
_ => Err(Sgp4Error::TleParsingFailed(format!(
"Expected 2 or 3 lines, got {}",
lines.len()
))),
}
}
fn parse_2line_tle(line1: &str, line2: &str) -> Result<Elements, Sgp4Error> {
let tle_string = format!("{line1}\n{line2}");
let elements_vec = sgp4::parse_2les(&tle_string)
.map_err(|e| Sgp4Error::TleParsingFailed(e.to_string()))?;
elements_vec.into_iter().next()
.ok_or_else(|| Sgp4Error::TleParsingFailed("No elements found in TLE".to_string()))
}
fn parse_3line_tle(name: &str, line1: &str, line2: &str) -> Result<Elements, Sgp4Error> {
let tle_string = format!("{name}\n{line1}\n{line2}");
let elements_vec = sgp4::parse_3les(&tle_string)
.map_err(|e| Sgp4Error::TleParsingFailed(e.to_string()))?;
elements_vec.into_iter().next()
.ok_or_else(|| Sgp4Error::TleParsingFailed("No elements found in TLE".to_string()))
}
pub fn validate_checksum(line: &str) -> bool {
if line.len() < 69 {
return false;
}
let checksum_char = line.chars().nth(68).unwrap_or('0');
let expected_checksum = checksum_char.to_digit(10).unwrap_or(0);
let mut sum = 0;
for c in line[..68].chars() {
if c.is_ascii_digit() {
sum += c.to_digit(10).unwrap();
} else if c == '-' {
sum += 1;
}
}
(sum % 10) == expected_checksum
}
#[cfg(test)]
mod tests {
use super::*;
const ISS_TLE_2LINE: &str = "1 25544U 98067A 08264.51782528 -.00002182 00000-0 -11606-4 0 2927
2 25544 51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537";
const ISS_TLE_3LINE: &str = "ISS (ZARYA)
1 25544U 98067A 08264.51782528 -.00002182 00000-0 -11606-4 0 2927
2 25544 51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537";
#[test]
fn test_parse_2line_tle() {
let elements = parse_tle(ISS_TLE_2LINE).unwrap();
assert_eq!(elements.norad_id, 25544);
assert_eq!(elements.international_designator, Some("1998-067A".to_string()));
assert!((elements.inclination - 51.6416).abs() < 0.001);
assert!((elements.eccentricity - 0.0006703).abs() < 0.0000001);
assert!((elements.mean_motion - 15.72125391).abs() < 0.00001);
}
#[test]
fn test_parse_3line_tle() {
let elements = parse_tle(ISS_TLE_3LINE).unwrap();
assert_eq!(elements.norad_id, 25544);
assert_eq!(elements.object_name, Some("ISS (ZARYA)".to_string()));
assert_eq!(elements.international_designator, Some("1998-067A".to_string()));
assert!((elements.inclination - 51.6416).abs() < 0.001);
}
#[test]
fn test_validate_checksum() {
let line1 = "1 25544U 98067A 08264.51782528 -.00002182 00000-0 -11606-4 0 2927";
let line2 = "2 25544 51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537";
assert!(validate_checksum(line1));
assert!(validate_checksum(line2));
}
#[test]
fn test_invalid_checksum() {
let bad_line = "1 25544U 98067A 08264.51782528 -.00002182 00000-0 -11606-4 0 2920";
assert!(!validate_checksum(bad_line));
}
#[test]
fn test_invalid_format() {
let result = parse_tle("1 25544U 98067A 08264.51782528 -.00002182 00000-0 -11606-4 0 2927");
assert!(result.is_err());
let result = parse_tle("Line1\nLine2\nLine3\nLine4");
assert!(result.is_err());
}
#[test]
fn test_different_satellite_name() {
let satellite_tle = "CUSTOM SATELLITE NAME
1 25544U 98067A 08264.51782528 -.00002182 00000-0 -11606-4 0 2927
2 25544 51.6416 247.4627 0006703 130.5360 325.0288 15.72125391563537";
let elements = parse_tle(satellite_tle).unwrap();
assert_eq!(elements.norad_id, 25544);
assert_eq!(elements.object_name, Some("CUSTOM SATELLITE NAME".to_string()));
assert!((elements.inclination - 51.6416).abs() < 0.001);
assert!((elements.eccentricity - 0.0006703).abs() < 0.0000001);
}
}