• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

GFZ / EL_PASO / 22960359727

11 Mar 2026 03:26PM UTC coverage: 65.049% (-1.2%) from 66.204%
22960359727

push

github

sahiljhawar
feat(tle): add TLE reader

0 of 46 new or added lines in 1 file covered. (0.0%)

1716 of 2638 relevant lines covered (65.05%)

1.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/el_paso/processing/tle.py
NEW
1
import logging
×
NEW
2
from dataclasses import dataclass, field
×
NEW
3
from datetime import datetime, timedelta, timezone
×
NEW
4
from pathlib import Path
×
5

NEW
6
import numpy as np
×
NEW
7
from numpy.typing import NDArray
×
NEW
8
from skyfield.api import EarthSatellite, load
×
9

NEW
10
logger = logging.getLogger(__name__)
×
11

12

NEW
13
@dataclass(frozen=True)
×
NEW
14
class TLE:
×
15
    """A class to track satellite positions using TLE data.
16

17
    Args:
18
        tle_filename (str | Path): The file path containing the TLE data.
19

20
    Attributes:
21
        tle_filename (str | Path): The file path containing the TLE data.
22
        tle_data (list of tuple): A list containing pairs of TLE lines for each
23
            satellite.
24
        satellite_name (str): The name of the satellite based on the first line of
25
            the TLE data.
26
    """
27

NEW
28
    tle_filename: str | Path
×
NEW
29
    tle_data: list[tuple[str, str]] = field(init=False)
×
NEW
30
    satellite_name: str = field(init=False)
×
NEW
31
    tle_time_list: list[datetime] = field(init=False)
×
32

NEW
33
    def __post_init__(self) -> None:
×
34
        """Initialize parsed and derived values from the input TLE file."""
NEW
35
        tle_path = Path(self.tle_filename)
×
NEW
36
        tle_data, satellite_name = self._read_tle_file(tle_path)
×
NEW
37
        tle_time_list = self.get_tle_time(tle_data)
×
38

NEW
39
        object.__setattr__(self, "tle_data", tle_data)
×
NEW
40
        object.__setattr__(self, "satellite_name", satellite_name)
×
NEW
41
        object.__setattr__(self, "tle_time_list", tle_time_list)
×
42

NEW
43
    def _read_tle_file(self, filename: Path) -> tuple[list[tuple[str, str]], str]:
×
44
        """Read TLE data from a file.
45

46
        Args:
47
            filename (Path): The file path containing the TLE data.
48

49
        Returns:
50
            tuple[list[tuple[str, str]], str]: A tuple containing:
51
                - tle_data: A list of satellite TLE line pairs.
52
                - satellite_name: The name of the satellite.
53
        """
NEW
54
        with filename.open() as file:
×
NEW
55
            lines = file.readlines()
×
56

NEW
57
        tle_data = [(lines[i].strip(), lines[i + 1].strip()) for i in range(0, len(lines), 2)]
×
NEW
58
        satellite_name = tle_data[0][0].split()[1]
×
59

NEW
60
        return tle_data, satellite_name
×
61

NEW
62
    def get_tle_time(self, tle_data: list[tuple[str, str]]) -> list[datetime]:
×
63
        """Generate a list of UTC datetime objects from the TLE data.
64

65
        Args:
66
            tle_data (list[tuple[str, str]]): The TLE data for the satellite.
67

68
        Returns:
69
            list[datetime]: A list of UTC datetime objects.
70
        """
NEW
71
        tle_times = []
×
NEW
72
        for tle in tle_data:
×
NEW
73
            year, doy = str(tle[0].split()[3])[:2], str(tle[0].split()[3])[2:]
×
NEW
74
            tle_times.append(
×
75
                datetime(2000 + int(year), 1, 1, 0, 0, 0, tzinfo=timezone.utc) + timedelta(days=float(doy))
76
            )
77

NEW
78
        return tle_times
×
79

NEW
80
    def calculate_geo_coords(self) -> NDArray[np.float64]:
×
81
        """Calculate GEO coordinates (x, y, z) in kilometers using Skyfield.
82

83
        Returns:
84
            NDArray[np.float64]: GEO coordinates for each TLE epoch in a shape
85
                `(n, 3)` array, where columns are `(x, y, z)` in kilometers.
86
        """
NEW
87
        timescale = load.timescale()
×
NEW
88
        geo_coordinates = []
×
89

NEW
90
        for tle_lines, tle_time in zip(self.tle_data, self.tle_time_list, strict=True):
×
NEW
91
            satellite = EarthSatellite(tle_lines[0], tle_lines[1], self.satellite_name)
×
NEW
92
            geocentric = satellite.at(timescale.from_datetime(tle_time))
×
NEW
93
            xyz = geocentric.xyz.km
×
NEW
94
            geo_coordinates.append(xyz)
×
95

NEW
96
        geo_coordinates = np.asarray(geo_coordinates, dtype=np.float64)
×
97

NEW
98
        if np.isnan(geo_coordinates).any():
×
NEW
99
            nan_indices = np.where(np.isnan(geo_coordinates).any(axis=1))[0]
×
NEW
100
            logger.warning(
×
101
                f"NaN values found in GEO coordinates at indices: {', '.join(str(idx) for idx in nan_indices)}. "
102
                "Check the TLE file at these indices."
103
            )
104

NEW
105
        return np.asarray(geo_coordinates, dtype=np.float64)
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc