33import tkinter as tk
44from tkinter import Canvas
55import requests
6- from sgp4 .api import Satrec , jday
6+ from skyfield .api import load , EarthSatellite
77from datetime import datetime , timedelta
88import math
99import cartopy .crs as ccrs
@@ -16,16 +16,19 @@ class ISSTrackerApp:
1616 def __init__ (self , root ):
1717 self .root = root
1818 self .root .title ("ISS Tracker" )
19- self .canvas = Canvas ( root , width = 800 , height = 400 , bg = "black" )
20- self .canvas . pack ()
19+ self .width = 800
20+ self .height = 400
2121
22- self .image = self .generate_map ().resize ((800 , 400 ))
23- self .tk_image = ImageTk .PhotoImage (self .image )
24- self .canvas .create_image (0 , 0 , anchor = "nw" , image = self .tk_image )
22+ self .canvas = Canvas (root , width = self .width , height = self .height , bg = "black" )
23+ self .canvas .pack (fill = "both" , expand = True )
2524
26- self .time_offset = timedelta (seconds = 0 )
25+ self .original_map = self .generate_map ()
26+ self .tk_image = None
27+
28+ self .ts = load .timescale ()
2729 self .satellite = None
2830 self .last_tle_update = None
31+ self .time_offset = timedelta (seconds = 0 )
2932
3033 self .load_tle ()
3134 self .schedule_tle_refresh ()
@@ -34,6 +37,7 @@ def __init__(self, root):
3437 self .root .bind ("<Right>" , self .go_forward )
3538 self .root .bind ("<space>" , self .reset_time )
3639
40+ self .root .bind ("<Configure>" , self .resize )
3741 self .update_display ()
3842
3943 def generate_map (self ):
@@ -57,15 +61,14 @@ def load_tle(self):
5761 if "ISS (ZARYA)" in line :
5862 tle1 = lines [i + 1 ].strip ()
5963 tle2 = lines [i + 2 ].strip ()
60- self .satellite = Satrec . twoline2rv (tle1 , tle2 )
64+ self .satellite = EarthSatellite (tle1 , tle2 , 'ISS (ZARYA)' , self . ts )
6165 self .last_tle_update = datetime .utcnow ()
6266 print ("TLE updated:" , self .last_tle_update )
6367 return
6468 except Exception as e :
6569 print ("Error updating TLE:" , e )
6670
6771 def schedule_tle_refresh (self ):
68- # Refresh TLE every 10 minutes
6972 def tle_updater ():
7073 while True :
7174 now = datetime .utcnow ()
@@ -74,35 +77,38 @@ def tle_updater():
7477 threading .Event ().wait (60 )
7578 threading .Thread (target = tle_updater , daemon = True ).start ()
7679
80+ def resize (self , event ):
81+ self .width = event .width
82+ self .height = event .height
83+ self .update_display ()
84+
7785 def update_display (self ):
7886 self .canvas .delete ("all" )
87+
88+ # Resize map image
89+ resized_image = self .original_map .resize ((self .width , self .height ))
90+ self .tk_image = ImageTk .PhotoImage (resized_image )
7991 self .canvas .create_image (0 , 0 , anchor = "nw" , image = self .tk_image )
8092
8193 now = datetime .utcnow () + self .time_offset
82- jd , fr = jday (now .year , now .month , now .day , now .hour , now .minute , now .second + now .microsecond / 1e6 )
83- e , pos , vel = self .satellite .sgp4 (jd , fr )
84- lat , lon = self .eci_to_latlon (pos , jd )
85- iss_x , iss_y = self .latlon_to_xy (lat , lon , 800 , 400 )
94+ t_now = self .ts .utc (now )
95+
96+ geocentric = self .satellite .at (t_now )
97+ subpoint = geocentric .subpoint ()
98+ lat , lon = subpoint .latitude .degrees , subpoint .longitude .degrees
99+ iss_x , iss_y = self .latlon_to_xy (lat , lon , self .width , self .height )
86100
87- # Draw ISS current position
88101 self .canvas .create_oval (iss_x - 4 , iss_y - 4 , iss_x + 4 , iss_y + 4 , fill = "purple" , tags = "iss" )
89102
90- # Draw current orbit path
103+ # Draw upcoming path (next orbit)
91104 path_coords = []
92- prev_lon = None
93- for mins in range (- 10 , 90 , 2 ):
105+ for mins in range (- 10 , 90 , 1 ):
94106 future_time = now + timedelta (minutes = mins )
95- jd_fut , fr_fut = jday (future_time .year , future_time .month , future_time .day ,
96- future_time .hour , future_time .minute ,
97- future_time .second + future_time .microsecond / 1e6 )
98- e_fut , pos_fut , vel_fut = self .satellite .sgp4 (jd_fut , fr_fut )
99- lat_fut , lon_fut = self .eci_to_latlon (pos_fut , jd_fut )
100-
101- if prev_lon is not None :
102- lon_fut = self .smooth_longitude (prev_lon , lon_fut )
103- prev_lon = lon_fut
104-
105- x , y = self .latlon_to_xy (lat_fut , lon_fut , 800 , 400 )
107+ t_future = self .ts .utc (future_time )
108+ geocentric_future = self .satellite .at (t_future )
109+ subpoint_future = geocentric_future .subpoint ()
110+ lat_fut , lon_fut = subpoint_future .latitude .degrees , subpoint_future .longitude .degrees
111+ x , y = self .latlon_to_xy (lat_fut , lon_fut , self .width , self .height )
106112 path_coords .append ((x , y ))
107113
108114 for i in range (len (path_coords )- 1 ):
@@ -111,51 +117,9 @@ def update_display(self):
111117 fill = "blue" , width = 2 )
112118
113119 # Draw current UTC time
114- self .canvas .create_text (400 , 20 , text = now .strftime ("%Y-%m-%d %H:%M:%S UTC" ),
120+ self .canvas .create_text (self . width // 2 , 20 , text = now .strftime ("%Y-%m-%d %H:%M:%S UTC" ),
115121 font = ("Arial" , 16 , "bold" ), fill = "white" )
116122
117- self .root .after (1000 , self .update_display )
118-
119- def eci_to_latlon (self , pos , jd ):
120- T = (jd - 2451545.0 ) / 36525.0
121- GMST = 280.46061837 + 360.98564736629 * (jd - 2451545.0 ) + 0.000387933 * T ** 2 - T ** 3 / 38710000.0
122- GMST = GMST % 360.0
123- theta = math .radians (GMST )
124-
125- x = pos [0 ]* math .cos (theta ) + pos [1 ]* math .sin (theta )
126- y = - pos [0 ]* math .sin (theta ) + pos [1 ]* math .cos (theta )
127- z = pos [2 ]
128-
129- r = math .sqrt (x ** 2 + y ** 2 + z ** 2 )
130- lat = math .degrees (math .asin (z / r ))
131- lon = math .degrees (math .atan2 (y , x ))
132- if lon > 180 :
133- lon -= 360
134- return lat , lon
135-
136- def latlon_to_xy (self , lat , lon , width , height ):
137- x = (lon + 180 ) * (width / 360 )
138- y = (90 - lat ) * (height / 180 )
139- return x , y
140-
141- def smooth_longitude (self , prev_lon , curr_lon ):
142- delta = curr_lon - prev_lon
143- if delta > 180 :
144- curr_lon -= 360
145- elif delta < - 180 :
146- curr_lon += 360
147- return curr_lon
148-
149- def go_forward (self , event ):
150- self .time_offset += timedelta (minutes = 2 )
151-
152- def go_back (self , event ):
153- self .time_offset -= timedelta (minutes = 2 )
154-
155- def reset_time (self , event ):
156- self .time_offset = timedelta (seconds = 0 )
123+ self .root .after (100 , self .update_display )
157124
158- if __name__ == "__main__" :
159- root = tk .Tk ()
160- app = ISSTrackerApp (root )
161- root .mainloop ()
125+ de
0 commit comments