JulsdL commited on
Commit
fcec389
·
1 Parent(s): 7e71511

Add satellite ephemeris calculator app with Streamlit

Browse files

- Implement main app logic in and for satellite ephemeris calculation.
- Add utility functions in for computing ephemeris and fetching TLE data.
- Add utility functions in for timezone conversion.
- Include dependencies in .

Files changed (5) hide show
  1. app.py +120 -0
  2. app_copy.py +218 -0
  3. requirements.txt +9 -0
  4. utils/satellite_utils.py +96 -0
  5. utils/timezone_utils.py +13 -0
app.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import streamlit as st
3
+ import pandas as pd
4
+ from geopy.geocoders import Nominatim
5
+ from streamlit_folium import st_folium
6
+ import folium
7
+ from datetime import datetime
8
+ import pytz
9
+ from timezonefinder import TimezoneFinder
10
+
11
+ from utils.satellite_utils import compute_ephemeris, fetch_custom_tle
12
+ from utils.timezone_utils import get_abbreviated_timezone
13
+
14
+ def main():
15
+ st.title("Satellites Ephemeris Calculator")
16
+
17
+ # Dropdown for satellite category selection
18
+ satellite_categories = {
19
+ 'Starlink Generation 1': 'https://celestrak.org/NORAD/elements/supplemental/sup-gp.php?FILE=starlink&FORMAT=tle',
20
+ 'Starlink Generation 2': 'https://celestrak.org/NORAD/elements/supplemental/sup-gp.php?FILE=starlink&FORMAT=tle&VERSION=2',
21
+ 'OneWeb': 'https://celestrak.org/NORAD/elements/supplemental/sup-gp.php?FILE=oneweb&FORMAT=tle',
22
+ 'Kuiper': 'https://celestrak.org/NORAD/elements/supplemental/sup-gp.php?FILE=kuiper&FORMAT=tle',
23
+ 'Custom Satellite': None
24
+ }
25
+ satellite_type = st.selectbox("Select or Search Satellite", list(satellite_categories.keys()))
26
+
27
+ custom_tle = None
28
+ satellite_url = satellite_categories[satellite_type]
29
+
30
+ # If custom satellite is selected, ask for satellite name or NORAD ID
31
+ if satellite_type == 'Custom Satellite':
32
+ satellite_name_or_id = st.text_input("Enter Satellite Name or NORAD ID:")
33
+ if satellite_name_or_id:
34
+ custom_tle = fetch_custom_tle(satellite_name_or_id)
35
+
36
+ # Detect user's local time zone
37
+ local_timezone = datetime.now().astimezone().tzinfo
38
+
39
+ # Coordinates and time input
40
+ with st.container():
41
+ col1, col2 = st.columns([2, 1])
42
+ with col1:
43
+ use_address = st.checkbox("Use Address to Set Location", value=False)
44
+ if use_address:
45
+ address = st.text_input("Enter Your Address:")
46
+ if address:
47
+ geolocator = Nominatim(user_agent="satellite-observation-app")
48
+ try:
49
+ location = geolocator.geocode(address, timeout=10)
50
+ if location:
51
+ latitude = location.latitude
52
+ longitude = location.longitude
53
+ else:
54
+ st.write("Could not find the location. Please enter a valid address.")
55
+ except Exception as e:
56
+ st.error(f"Error fetching location data: {str(e)}")
57
+ else:
58
+ st.write("Select your location on the map:")
59
+ default_location = [37.7749, -122.4194]
60
+ map_display = folium.Map(location=default_location, zoom_start=2)
61
+ folium.Marker(default_location, tooltip="Default Location").add_to(map_display)
62
+ map_data = st_folium(map_display, width=350, height=300)
63
+ if map_data and 'last_clicked' in map_data and map_data['last_clicked']:
64
+ latitude = map_data['last_clicked']['lat']
65
+ longitude = map_data['last_clicked']['lng']
66
+
67
+ with col2:
68
+ latitude = st.text_input("Latitude:", value=str(latitude) if 'latitude' in locals() else "")
69
+ longitude = st.text_input("Longitude:", value=str(longitude) if 'longitude' in locals() else "")
70
+
71
+ # Determine time zone from GPS coordinates
72
+ if latitude and longitude:
73
+ try:
74
+ tf = TimezoneFinder()
75
+ timezone_str = tf.timezone_at(lng=float(longitude), lat=float(latitude))
76
+ timezone = pytz.timezone(timezone_str)
77
+ current_time = datetime.now()
78
+ timezone_abbr = get_abbreviated_timezone(timezone_str, current_time)
79
+ st.write(f"Detected Time Zone: {timezone_str} ({timezone_abbr})")
80
+ except Exception as e:
81
+ st.error(f"Error determining timezone: {str(e)}")
82
+ timezone = local_timezone
83
+ else:
84
+ timezone = local_timezone
85
+ timezone_abbr = timezone.tzname(datetime.now())
86
+ st.write(f"Using local time zone: {timezone} ({timezone_abbr})")
87
+
88
+ start_date_local = st.date_input("Start Date (Local Time):")
89
+ start_time_local = st.time_input("Start Time (Local Time):")
90
+ end_time_local = st.time_input("End Time (Local Time):")
91
+
92
+ # Convert local time to UTC
93
+ start_datetime_local = datetime.combine(start_date_local, start_time_local)
94
+ end_datetime_local = datetime.combine(start_date_local, end_time_local)
95
+ start_datetime_utc = start_datetime_local.astimezone(pytz.utc)
96
+ end_datetime_utc = end_datetime_local.astimezone(pytz.utc)
97
+
98
+ st.write(f"Start Time in UTC: {start_datetime_utc.strftime('%Y-%m-%d %H:%M:%S')}")
99
+ st.write(f"End Time in UTC: {end_datetime_utc.strftime('%Y-%m-%d %H:%M:%S')}")
100
+
101
+ compute_button = st.button("Compute Satellite Positions")
102
+
103
+ # Align the table with the button
104
+ if compute_button:
105
+ if latitude and longitude and start_date_local:
106
+ ephemeris_df = compute_ephemeris(
107
+ satellite_url, float(latitude), float(longitude),
108
+ start_datetime_utc.strftime('%Y-%m-%d'),
109
+ start_datetime_utc.time(), end_datetime_utc.time(),
110
+ custom_tle
111
+ )
112
+ if not ephemeris_df.empty:
113
+ st.dataframe(ephemeris_df, use_container_width=True)
114
+ else:
115
+ st.write("No visible satellites found for the specified time and location.")
116
+ else:
117
+ st.write("Please fill in all fields (latitude, longitude, and observation time).")
118
+
119
+ if __name__ == "__main__":
120
+ main()
app_copy.py ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import streamlit as st
3
+ from skyfield.api import Topos, load, EarthSatellite
4
+ import requests
5
+ import pandas as pd
6
+ from geopy.geocoders import Nominatim
7
+ from streamlit_folium import st_folium
8
+ import folium
9
+ import numpy as np
10
+ from datetime import datetime
11
+ import pytz
12
+ from timezonefinder import TimezoneFinder
13
+
14
+ # Function to convert azimuth degrees to cardinal direction
15
+ def get_cardinal_direction(azimuth_degrees):
16
+ directions = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'N']
17
+ index = int((azimuth_degrees + 22.5) // 45)
18
+ return directions[index % 8]
19
+
20
+ # Function to get timezone in an abbreviated format
21
+ def get_abbreviated_timezone(timezone_str, dt):
22
+ try:
23
+ tz = pytz.timezone(timezone_str)
24
+ return dt.astimezone(tz).tzname()
25
+ except Exception as e:
26
+ st.error(f"Error converting timezone: {str(e)}")
27
+ return timezone_str # Return the original string if conversion fails
28
+
29
+ # Generalized function to compute satellite ephemeris
30
+ def compute_ephemeris(satellite_url, latitude, longitude, start_date_utc, start_time, end_time, custom_tle=None):
31
+ try:
32
+ ts = load.timescale()
33
+ year, month, day = map(int, start_date_utc.split('-'))
34
+ observer = Topos(latitude, longitude)
35
+ start_hour, start_minute = start_time.hour, start_time.minute
36
+ end_hour, end_minute = end_time.hour, end_time.minute
37
+ start_datetime = datetime(year, month, day, start_hour, start_minute)
38
+ end_datetime = datetime(year, month, day, end_hour, end_minute)
39
+ total_minutes = int((end_datetime - start_datetime).total_seconds() // 60)
40
+ times = ts.utc(year, month, day, [start_hour] * total_minutes, np.arange(start_minute, start_minute + total_minutes))
41
+
42
+ # Fetch TLE data for custom satellite or use predefined URL
43
+ if custom_tle:
44
+ st.write("Using custom TLE data for satellite computation.")
45
+ satellites = [EarthSatellite(custom_tle[1], custom_tle[2], custom_tle[0], ts)]
46
+ else:
47
+ r = requests.get(satellite_url)
48
+ with open('satellite_data.txt', 'wb') as f:
49
+ f.write(r.content)
50
+ satellites = load.tle_file('satellite_data.txt')
51
+
52
+ # Prepare the ephemeris data
53
+ ephemeris_data = []
54
+ for satellite in satellites:
55
+ difference = satellite - observer
56
+ for ti in times:
57
+ topocentric = difference.at(ti)
58
+ ra, dec, distance = topocentric.radec()
59
+ alt, az, distance = topocentric.altaz()
60
+ if alt.degrees > 0: # Check if the satellite is above the horizon
61
+ # Convert Angle objects to string representations and add cardinal direction
62
+ azimuth_degrees = az.degrees
63
+ cardinal_direction = get_cardinal_direction(azimuth_degrees)
64
+ ephemeris_data.append({
65
+ "Date (UTC)": ti.utc_strftime('%Y-%m-%d %H:%M:%S'),
66
+ "R.A.": str(ra),
67
+ "Dec": str(dec),
68
+ "Altitude": f"{alt.degrees:.2f}°",
69
+ "Azimuth": f"{azimuth_degrees:.2f}° ({cardinal_direction})"
70
+ })
71
+
72
+ # Convert ephemeris data to DataFrame for display
73
+ ephemeris_df = pd.DataFrame(ephemeris_data)
74
+ if not custom_tle:
75
+ os.remove('satellite_data.txt')
76
+ return ephemeris_df
77
+ except Exception as e:
78
+ st.error(f"Error computing ephemeris: {str(e)}")
79
+ return pd.DataFrame() # Return an empty DataFrame on error
80
+
81
+ # Fetch TLE data for custom satellite by name or NORAD ID
82
+ def fetch_custom_tle(satellite_name_or_id):
83
+ try:
84
+ # Correct URL formats for NORAD ID and satellite name
85
+ if satellite_name_or_id.isdigit():
86
+ # Fetch TLE by NORAD ID
87
+ celestrak_url = f'https://celestrak.org/NORAD/elements/gp.php?CATNR={satellite_name_or_id}'
88
+ else:
89
+ # Fetch TLE by satellite name
90
+ celestrak_url = f'https://celestrak.org/NORAD/elements/gp.php?NAME={satellite_name_or_id}&FORMAT=TLE'
91
+
92
+ with st.spinner("Fetching TLE data..."):
93
+ response = requests.get(celestrak_url)
94
+
95
+ if response.status_code == 200 and len(response.text.splitlines()) >= 2:
96
+ lines = response.text.splitlines()
97
+ st.success("TLE data fetched successfully!")
98
+ st.text(f"Name: {lines[0].strip()}\nTLE Line 1: {lines[1].strip()}\nTLE Line 2: {lines[2].strip()}")
99
+ return lines[0].strip(), lines[1].strip(), lines[2].strip() # Name, TLE line 1, TLE line 2
100
+ else:
101
+ st.error("TLE data could not be fetched. Please check the satellite name or NORAD ID.")
102
+ return None
103
+ except Exception as e:
104
+ st.error(f"Error fetching TLE data: {str(e)}")
105
+ return None
106
+
107
+ # Streamlit app
108
+ def main():
109
+ st.title("Satellites Ephemeris Calculator")
110
+
111
+ # Dropdown for satellite category selection
112
+ satellite_categories = {
113
+ 'Starlink Generation 1': 'https://celestrak.org/NORAD/elements/supplemental/sup-gp.php?FILE=starlink&FORMAT=tle',
114
+ 'Starlink Generation 2': 'https://celestrak.org/NORAD/elements/supplemental/sup-gp.php?FILE=starlink&FORMAT=tle&VERSION=2',
115
+ 'OneWeb': 'https://celestrak.org/NORAD/elements/supplemental/sup-gp.php?FILE=oneweb&FORMAT=tle',
116
+ 'Kuiper': 'https://celestrak.org/NORAD/elements/supplemental/sup-gp.php?FILE=kuiper&FORMAT=tle',
117
+ 'Custom Satellite': None
118
+ }
119
+ satellite_type = st.selectbox("Select or Search Satellite", list(satellite_categories.keys()))
120
+
121
+ custom_tle = None
122
+ satellite_url = satellite_categories[satellite_type]
123
+
124
+ # If custom satellite is selected, ask for satellite name or NORAD ID
125
+ if satellite_type == 'Custom Satellite':
126
+ satellite_name_or_id = st.text_input("Enter Satellite Name or NORAD ID:")
127
+ if satellite_name_or_id:
128
+ custom_tle = fetch_custom_tle(satellite_name_or_id)
129
+
130
+ # Detect user's local time zone
131
+ local_timezone = datetime.now().astimezone().tzinfo
132
+
133
+ # Coordinates and time input
134
+ with st.container():
135
+ col1, col2 = st.columns([2, 1])
136
+ with col1:
137
+ use_address = st.checkbox("Use Address to Set Location", value=False)
138
+ if use_address:
139
+ address = st.text_input("Enter Your Address:")
140
+ if address:
141
+ # Add User-Agent to Nominatim geocoder
142
+ geolocator = Nominatim(user_agent="satellite-observation-app")
143
+ try:
144
+ location = geolocator.geocode(address, timeout=10)
145
+ if location:
146
+ latitude = location.latitude
147
+ longitude = location.longitude
148
+ else:
149
+ st.write("Could not find the location. Please enter a valid address.")
150
+ except Exception as e:
151
+ st.error(f"Error fetching location data: {str(e)}")
152
+ else:
153
+ st.write("Select your location on the map:")
154
+ default_location = [37.7749, -122.4194]
155
+ map_display = folium.Map(location=default_location, zoom_start=2)
156
+ folium.Marker(default_location, tooltip="Default Location").add_to(map_display)
157
+ map_data = st_folium(map_display, width=350, height=300)
158
+ if map_data and 'last_clicked' in map_data and map_data['last_clicked']:
159
+ latitude = map_data['last_clicked']['lat']
160
+ longitude = map_data['last_clicked']['lng']
161
+
162
+ with col2:
163
+ latitude = st.text_input("Latitude:", value=str(latitude) if 'latitude' in locals() else "")
164
+ longitude = st.text_input("Longitude:", value=str(longitude) if 'longitude' in locals() else "")
165
+
166
+ # Determine time zone from GPS coordinates
167
+ if latitude and longitude:
168
+ try:
169
+ tf = TimezoneFinder()
170
+ timezone_str = tf.timezone_at(lng=float(longitude), lat=float(latitude))
171
+ timezone = pytz.timezone(timezone_str)
172
+ # Display abbreviated timezone
173
+ current_time = datetime.now()
174
+ timezone_abbr = get_abbreviated_timezone(timezone_str, current_time)
175
+ st.write(f"Detected Time Zone: {timezone_str} ({timezone_abbr})")
176
+ except Exception as e:
177
+ st.error(f"Error determining timezone: {str(e)}")
178
+ timezone = local_timezone
179
+ else:
180
+ timezone = local_timezone
181
+ timezone_abbr = timezone.tzname(datetime.now())
182
+ st.write(f"Using local time zone: {timezone} ({timezone_abbr})")
183
+
184
+ start_date_local = st.date_input("Start Date (Local Time):")
185
+ start_time_local = st.time_input("Start Time (Local Time):")
186
+ end_time_local = st.time_input("End Time (Local Time):")
187
+
188
+ # Convert local time to UTC
189
+ start_datetime_local = datetime.combine(start_date_local, start_time_local)
190
+ end_datetime_local = datetime.combine(start_date_local, end_time_local)
191
+ start_datetime_utc = start_datetime_local.astimezone(pytz.utc)
192
+ end_datetime_utc = end_datetime_local.astimezone(pytz.utc)
193
+
194
+ st.write(f"Start Time in UTC: {start_datetime_utc.strftime('%Y-%m-%d %H:%M:%S')}")
195
+ st.write(f"End Time in UTC: {end_datetime_utc.strftime('%Y-%m-%d %H:%M:%S')}")
196
+
197
+ # Compute button
198
+ compute_button = st.button("Compute Satellite Positions")
199
+
200
+ # Align the table with the button
201
+ if compute_button:
202
+ if latitude and longitude and start_date_local:
203
+ ephemeris_df = compute_ephemeris(
204
+ satellite_url, float(latitude), float(longitude),
205
+ start_datetime_utc.strftime('%Y-%m-%d'),
206
+ start_datetime_utc.time(), end_datetime_utc.time(),
207
+ custom_tle
208
+ )
209
+ if not ephemeris_df.empty:
210
+ # Display the dataframe with use_container_width to match the app width
211
+ st.dataframe(ephemeris_df, use_container_width=True)
212
+ else:
213
+ st.write("No visible satellites found for the specified time and location.")
214
+ else:
215
+ st.write("Please fill in all fields (latitude, longitude, and observation time).")
216
+
217
+ if __name__ == "__main__":
218
+ main()
requirements.txt ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ streamlit
2
+ skyfield
3
+ geopy
4
+ pandas
5
+ numpy
6
+ pytz
7
+ timezonefinder
8
+ requests
9
+ folium
utils/satellite_utils.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import streamlit as st
3
+ from skyfield.api import Topos, load, EarthSatellite
4
+ import requests
5
+ import pandas as pd
6
+ import numpy as np
7
+ from datetime import datetime
8
+
9
+ def get_cardinal_direction(azimuth_degrees):
10
+ """
11
+ Converts azimuth degrees to cardinal direction.
12
+ """
13
+ directions = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW', 'N']
14
+ index = int((azimuth_degrees + 22.5) // 45)
15
+ return directions[index % 8]
16
+
17
+ def compute_ephemeris(satellite_url, latitude, longitude, start_date_utc, start_time, end_time, custom_tle=None):
18
+ """
19
+ Computes the ephemeris for the satellite.
20
+ """
21
+ try:
22
+ ts = load.timescale()
23
+ year, month, day = map(int, start_date_utc.split('-'))
24
+ observer = Topos(latitude, longitude)
25
+ start_hour, start_minute = start_time.hour, start_time.minute
26
+ end_hour, end_minute = end_time.hour, end_time.minute
27
+ start_datetime = datetime(year, month, day, start_hour, start_minute)
28
+ end_datetime = datetime(year, month, day, end_hour, end_minute)
29
+ total_minutes = int((end_datetime - start_datetime).total_seconds() // 60)
30
+ times = ts.utc(year, month, day, [start_hour] * total_minutes, np.arange(start_minute, start_minute + total_minutes))
31
+
32
+ # Fetch TLE data for custom satellite or use predefined URL
33
+ if custom_tle:
34
+ st.write("Using custom TLE data for satellite computation.")
35
+ st.write(f"Custom TLE Data:\nName: {custom_tle[0]}\nTLE Line 1: {custom_tle[1]}\nTLE Line 2: {custom_tle[2]}")
36
+ satellites = [EarthSatellite(custom_tle[1], custom_tle[2], custom_tle[0], ts)]
37
+ else:
38
+ r = requests.get(satellite_url)
39
+ with open('satellite_data.txt', 'wb') as f:
40
+ f.write(r.content)
41
+ satellites = load.tle_file('satellite_data.txt')
42
+
43
+ # Prepare the ephemeris data
44
+ ephemeris_data = []
45
+ for satellite in satellites:
46
+ difference = satellite - observer
47
+ for ti in times:
48
+ topocentric = difference.at(ti)
49
+ ra, dec, distance = topocentric.radec()
50
+ alt, az, distance = topocentric.altaz()
51
+ st.write(f"Checking time {ti.utc_iso()} - Altitude: {alt.degrees:.2f} degrees") # Debugging statement
52
+
53
+ if alt.degrees > 0: # Check if the satellite is above the horizon
54
+ azimuth_degrees = az.degrees
55
+ cardinal_direction = get_cardinal_direction(azimuth_degrees)
56
+ ephemeris_data.append({
57
+ "Date (UTC)": ti.utc_strftime('%Y-%m-%d %H:%M:%S'),
58
+ "R.A.": str(ra),
59
+ "Dec": str(dec),
60
+ "Altitude": f"{alt.degrees:.2f}°",
61
+ "Azimuth": f"{azimuth_degrees:.2f}° ({cardinal_direction})"
62
+ })
63
+
64
+ # Convert ephemeris data to DataFrame for display
65
+ ephemeris_df = pd.DataFrame(ephemeris_data)
66
+ if not custom_tle:
67
+ os.remove('satellite_data.txt')
68
+ return ephemeris_df
69
+ except Exception as e:
70
+ st.error(f"Error computing ephemeris: {str(e)}")
71
+ return pd.DataFrame() # Return an empty DataFrame on error
72
+
73
+ def fetch_custom_tle(satellite_name_or_id):
74
+ """
75
+ Fetches TLE data for a custom satellite by name or NORAD ID.
76
+ """
77
+ try:
78
+ if satellite_name_or_id.isdigit():
79
+ celestrak_url = f'https://celestrak.org/NORAD/elements/gp.php?CATNR={satellite_name_or_id}'
80
+ else:
81
+ celestrak_url = f'https://celestrak.org/NORAD/elements/gp.php?NAME={satellite_name_or_id}&FORMAT=TLE'
82
+
83
+ with st.spinner("Fetching TLE data..."):
84
+ response = requests.get(celestrak_url)
85
+
86
+ if response.status_code == 200 and len(response.text.splitlines()) >= 2:
87
+ lines = response.text.splitlines()
88
+ st.success("TLE data fetched successfully!")
89
+ st.text(f"Name: {lines[0].strip()}\nTLE Line 1: {lines[1].strip()}\nTLE Line 2: {lines[2].strip()}")
90
+ return lines[0].strip(), lines[1].strip(), lines[2].strip() # Name, TLE line 1, TLE line 2
91
+ else:
92
+ st.error("TLE data could not be fetched. Please check the satellite name or NORAD ID.")
93
+ return None
94
+ except Exception as e:
95
+ st.error(f"Error fetching TLE data: {str(e)}")
96
+ return None
utils/timezone_utils.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pytz
3
+
4
+ def get_abbreviated_timezone(timezone_str, dt):
5
+ """
6
+ Converts the timezone string to an abbreviated format.
7
+ """
8
+ try:
9
+ tz = pytz.timezone(timezone_str)
10
+ return dt.astimezone(tz).tzname()
11
+ except Exception as e:
12
+ st.error(f"Error converting timezone: {str(e)}")
13
+ return timezone_str # Return the original string if conversion fails