From 17b46a13c2b8825bd181c717c690cb39e261cbf8 Mon Sep 17 00:00:00 2001 From: Smittix Date: Thu, 29 Jan 2026 17:25:08 +0000 Subject: [PATCH] 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 --- app.py | 16 +++++++++ routes/satellite.py | 83 ++++++++++++++++++++++++++------------------- 2 files changed, 64 insertions(+), 35 deletions(-) diff --git a/app.py b/app.py index f78ad50..3d8e113 100644 --- a/app.py +++ b/app.py @@ -714,6 +714,22 @@ def main() -> None: from routes import register_blueprints 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 try: from routes.audio_websocket import init_audio_websocket diff --git a/routes/satellite.py b/routes/satellite.py index 8ed62a0..3b597af 100644 --- a/routes/satellite.py +++ b/routes/satellite.py @@ -412,56 +412,69 @@ def get_satellite_position(): }) -@satellite_bp.route('/update-tle', methods=['POST']) -def update_tle(): - """Update TLE data from CelesTrak.""" +def refresh_tle_data() -> list: + """ + 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 - try: - name_mappings = { - 'ISS (ZARYA)': 'ISS', - 'NOAA 15': 'NOAA-15', - 'NOAA 18': 'NOAA-18', - 'NOAA 19': 'NOAA-19', - 'METEOR-M 2': 'METEOR-M2', - 'METEOR-M2 3': 'METEOR-M2-3' - } + name_mappings = { + 'ISS (ZARYA)': 'ISS', + 'NOAA 15': 'NOAA-15', + 'NOAA 18': 'NOAA-18', + 'NOAA 19': 'NOAA-19', + 'NOAA 20 (JPSS-1)': 'NOAA-20', + 'NOAA 21 (JPSS-2)': 'NOAA-21', + 'METEOR-M 2': 'METEOR-M2', + 'METEOR-M2 3': 'METEOR-M2-3' + } - updated = [] + updated = [] - for group in ['stations', 'weather']: - url = f'https://celestrak.org/NORAD/elements/gp.php?GROUP={group}&FORMAT=tle' - try: - with urllib.request.urlopen(url, timeout=10) as response: - content = response.read().decode('utf-8') - lines = content.strip().split('\n') + for group in ['stations', 'weather', 'noaa']: + url = f'https://celestrak.org/NORAD/elements/gp.php?GROUP={group}&FORMAT=tle' + try: + with urllib.request.urlopen(url, timeout=15) as response: + content = response.read().decode('utf-8') + lines = content.strip().split('\n') - i = 0 - while i + 2 < len(lines): - name = lines[i].strip() - line1 = lines[i + 1].strip() - line2 = lines[i + 2].strip() + i = 0 + while i + 2 < len(lines): + name = lines[i].strip() + line1 = lines[i + 1].strip() + line2 = lines[i + 2].strip() - if not (line1.startswith('1 ') and line2.startswith('2 ')): - i += 1 - continue + if not (line1.startswith('1 ') and line2.startswith('2 ')): + i += 1 + continue - internal_name = name_mappings.get(name, name) + internal_name = name_mappings.get(name, name) - if internal_name in _tle_cache: - _tle_cache[internal_name] = (name, line1, line2) + if internal_name in _tle_cache: + _tle_cache[internal_name] = (name, line1, line2) + if internal_name not in updated: updated.append(internal_name) - i += 3 - except Exception as e: - logger.error(f"Error fetching {group}: {e}") - continue + i += 3 + except Exception as e: + logger.warning(f"Error fetching TLE group {group}: {e}") + 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({ 'status': 'success', 'updated': updated }) - except Exception as e: return jsonify({'status': 'error', 'message': str(e)})