1
+ # Import necessary modules
2
+ from datetime import datetime , timedelta
3
+ from skyfield .iokit import parse_tle_file
4
+ from skyfield .api import load
5
+ from selenium import webdriver
6
+ import folium
7
+ import time
8
+ import os
9
+
10
+
11
+ # Constants
12
+ TLE_FILENAME = "data_files/iss_zarya_tle.tle"
13
+ TLE_URL = "https://celestrak.org/NORAD/elements/gp.php?CATNR=25544&FORMAT=TLE"
14
+ MAP_FILENAME = "map/tracker_map.html"
15
+ MAP_ZOOM_START = 2
16
+ ORBIT_DURATION_MINUTES = 90
17
+ UPDATE_INTERVAL_SECONDS = 60
18
+
19
+
20
+ # Functions
21
+ def download_tle_file ():
22
+ """
23
+ If the TLE file is missing or is outdated, download the latest data.
24
+ """
25
+ if not load .exists (TLE_FILENAME ) or load .days_old (TLE_FILENAME ) > 1.0 :
26
+ try :
27
+ load .download (TLE_URL , filename = TLE_FILENAME )
28
+ except Exception as e :
29
+ print (f"ERROR: Failed to download the TLE data.{ e } " )
30
+ exit (1 )
31
+
32
+
33
+ def load_satellite_data ():
34
+ """
35
+ Load the satellite data from the TLE file.
36
+ """
37
+ with load .open (TLE_FILENAME ) as f :
38
+ satellites = list (parse_tle_file (f , load .timescale ()))
39
+ # Index ISS (ZARYA) by NORADID number.
40
+ return {sat .model .satnum : sat for sat in satellites }[25544 ]
41
+
42
+
43
+ def create_map (sat_lat , sat_lon ):
44
+ """
45
+ Create a new map with the ISS's current position.
46
+ """
47
+ # Create a map centered onto the ISS position.
48
+ iss_map = folium .Map (
49
+ location = [sat_lat , sat_lon ],
50
+ zoom_start = MAP_ZOOM_START
51
+ )
52
+ # Pinpoint the satellite's current position on the map.
53
+ folium .Marker (
54
+ location = [sat_lat , sat_lon ],
55
+ tooltip = f"ISS (Lat: { sat_lat } , Lon: { sat_lon } )" ,
56
+ popup = "International Space Station (ZARYA)" ,
57
+ icon = folium .Icon (color = "red" , icon = "satellite" , prefix = "fa" )
58
+ ).add_to (iss_map )
59
+ return iss_map
60
+
61
+
62
+ def predict_orbit (satellite , current_time ):
63
+ """
64
+ Predict the orbit of the satellite by predicting its future poitions.
65
+ ISS completes one orbit around the Earth in approximately 90 minutes.
66
+ """
67
+ # Add current position of the satellite
68
+ current_sat_lat = satellite .at (current_time ).subpoint ().latitude .degrees
69
+ current_sat_lon = satellite .at (current_time ).subpoint ().longitude .degrees
70
+ orbit_coordinates = [(current_sat_lat , current_sat_lon )]
71
+ for i in range (1 , ORBIT_DURATION_MINUTES + 1 ):
72
+ future_time = current_time + timedelta (minutes = i )
73
+ future_geocentric_pos = satellite .at (future_time )
74
+ future_sub_pos = future_geocentric_pos .subpoint ()
75
+ future_sat_lat = future_sub_pos .latitude .degrees
76
+ future_sat_lon = future_sub_pos .longitude .degrees
77
+ # Longitude Adjustment: Check for large jumps in longitude.
78
+ if abs (future_sat_lon - orbit_coordinates [- 1 ][1 ]) > 180 :
79
+ if future_sat_lon < orbit_coordinates [- 1 ][1 ]:
80
+ future_sat_lon += 360
81
+ else :
82
+ future_sat_lon -= 360
83
+ # Add the fixed coordinates to the list of orbit coordinates.
84
+ orbit_coordinates .append ((future_sat_lat , future_sat_lon ))
85
+ return orbit_coordinates
86
+
87
+
88
+ def main ():
89
+ download_tle_file ()
90
+ satellite = load_satellite_data ()
91
+ driver = webdriver .Firefox ()
92
+ driver .get (f"file:///{ os .path .abspath (MAP_FILENAME )} " )
93
+ while True :
94
+ current_time = datetime .utcnow ()
95
+ t = load .timescale ().utc (
96
+ current_time .year ,
97
+ current_time .month ,
98
+ current_time .day ,
99
+ current_time .hour ,
100
+ current_time .minute ,
101
+ current_time .second
102
+ )
103
+ geocentric_pos = satellite .at (t )
104
+ sub_pos = geocentric_pos .subpoint ()
105
+ sat_lat = sub_pos .latitude .degrees
106
+ sat_lon = sub_pos .longitude .degrees
107
+ iss_map = create_map (sat_lat , sat_lon )
108
+ orbit_coordinates = predict_orbit (satellite , t )
109
+ folium .PolyLine (
110
+ locations = orbit_coordinates ,
111
+ color = "black" ,
112
+ weight = 1 ,
113
+ dash_array = "5"
114
+ ).add_to (iss_map )
115
+ iss_map .save (MAP_FILENAME )
116
+ driver .refresh ()
117
+ time .sleep (UPDATE_INTERVAL_SECONDS )
118
+
119
+
120
+ # Ensure the "main" function is only executed when the script is run directly.
121
+ if __name__ == "__main__" :
122
+ main ()
0 commit comments