feat: Auto-update TLE data on app startup

- Add refresh_tle_data() function for reusable TLE updates
- Automatically fetch fresh TLE from CelesTrak when app starts
- Runs in background thread to avoid slowing down startup
- Includes NOAA-20 and NOAA-21 in name mappings
- Gracefully handles failures (uses cached data if offline)
- Existing /update-tle endpoint now uses shared function

This ensures satellite tracking data is always fresh, fixing
inaccurate positions caused by stale TLE data.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Smittix
2026-01-29 17:25:08 +00:00
parent 71d075082d
commit cdb63d0ab2
2 changed files with 64 additions and 35 deletions
+16
View File
@@ -714,6 +714,22 @@ def main() -> None:
from routes import register_blueprints from routes import register_blueprints
register_blueprints(app) register_blueprints(app)
# Update TLE data in background thread (non-blocking)
def update_tle_background():
try:
from routes.satellite import refresh_tle_data
print("Updating satellite TLE data from CelesTrak...")
updated = refresh_tle_data()
if updated:
print(f"TLE data updated for: {', '.join(updated)}")
else:
print("TLE update: No satellites updated (may be offline)")
except Exception as e:
print(f"TLE update failed (will use cached data): {e}")
tle_thread = threading.Thread(target=update_tle_background, daemon=True)
tle_thread.start()
# Initialize WebSocket for audio streaming # Initialize WebSocket for audio streaming
try: try:
from routes.audio_websocket import init_audio_websocket from routes.audio_websocket import init_audio_websocket
+48 -35
View File
@@ -412,56 +412,69 @@ def get_satellite_position():
}) })
@satellite_bp.route('/update-tle', methods=['POST']) def refresh_tle_data() -> list:
def update_tle(): """
"""Update TLE data from CelesTrak.""" Refresh TLE data from CelesTrak.
This can be called at startup or periodically to keep TLE data fresh.
Returns list of satellite names that were updated.
"""
global _tle_cache global _tle_cache
try: name_mappings = {
name_mappings = { 'ISS (ZARYA)': 'ISS',
'ISS (ZARYA)': 'ISS', 'NOAA 15': 'NOAA-15',
'NOAA 15': 'NOAA-15', 'NOAA 18': 'NOAA-18',
'NOAA 18': 'NOAA-18', 'NOAA 19': 'NOAA-19',
'NOAA 19': 'NOAA-19', 'NOAA 20 (JPSS-1)': 'NOAA-20',
'METEOR-M 2': 'METEOR-M2', 'NOAA 21 (JPSS-2)': 'NOAA-21',
'METEOR-M2 3': 'METEOR-M2-3' 'METEOR-M 2': 'METEOR-M2',
} 'METEOR-M2 3': 'METEOR-M2-3'
}
updated = [] updated = []
for group in ['stations', 'weather']: for group in ['stations', 'weather', 'noaa']:
url = f'https://celestrak.org/NORAD/elements/gp.php?GROUP={group}&FORMAT=tle' url = f'https://celestrak.org/NORAD/elements/gp.php?GROUP={group}&FORMAT=tle'
try: try:
with urllib.request.urlopen(url, timeout=10) as response: with urllib.request.urlopen(url, timeout=15) as response:
content = response.read().decode('utf-8') content = response.read().decode('utf-8')
lines = content.strip().split('\n') lines = content.strip().split('\n')
i = 0 i = 0
while i + 2 < len(lines): while i + 2 < len(lines):
name = lines[i].strip() name = lines[i].strip()
line1 = lines[i + 1].strip() line1 = lines[i + 1].strip()
line2 = lines[i + 2].strip() line2 = lines[i + 2].strip()
if not (line1.startswith('1 ') and line2.startswith('2 ')): if not (line1.startswith('1 ') and line2.startswith('2 ')):
i += 1 i += 1
continue continue
internal_name = name_mappings.get(name, name) internal_name = name_mappings.get(name, name)
if internal_name in _tle_cache: if internal_name in _tle_cache:
_tle_cache[internal_name] = (name, line1, line2) _tle_cache[internal_name] = (name, line1, line2)
if internal_name not in updated:
updated.append(internal_name) updated.append(internal_name)
i += 3 i += 3
except Exception as e: except Exception as e:
logger.error(f"Error fetching {group}: {e}") logger.warning(f"Error fetching TLE group {group}: {e}")
continue continue
return updated
@satellite_bp.route('/update-tle', methods=['POST'])
def update_tle():
"""Update TLE data from CelesTrak (API endpoint)."""
try:
updated = refresh_tle_data()
return jsonify({ return jsonify({
'status': 'success', 'status': 'success',
'updated': updated 'updated': updated
}) })
except Exception as e: except Exception as e:
return jsonify({'status': 'error', 'message': str(e)}) return jsonify({'status': 'error', 'message': str(e)})