mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 06:40:00 -07:00
Fix satellite dashboard refresh flows
This commit is contained in:
@@ -48,11 +48,18 @@ _tle_cache = dict(TLE_SATELLITES)
|
||||
|
||||
# Ground track cache: key=(sat_name, tle_line1[:20]) -> (track_data, computed_at_timestamp)
|
||||
# TTL is 1800 seconds (30 minutes)
|
||||
_track_cache: dict = {}
|
||||
_TRACK_CACHE_TTL = 1800
|
||||
_track_cache: dict = {}
|
||||
_TRACK_CACHE_TTL = 1800
|
||||
|
||||
_BUILTIN_NORAD_TO_KEY = {
|
||||
25544: 'ISS',
|
||||
40069: 'METEOR-M2',
|
||||
57166: 'METEOR-M2-3',
|
||||
59051: 'METEOR-M2-4',
|
||||
}
|
||||
|
||||
|
||||
def _load_db_satellites_into_cache():
|
||||
def _load_db_satellites_into_cache():
|
||||
"""Load user-tracked satellites from DB into the TLE cache."""
|
||||
global _tle_cache
|
||||
try:
|
||||
@@ -67,8 +74,102 @@ def _load_db_satellites_into_cache():
|
||||
loaded += 1
|
||||
if loaded:
|
||||
logger.info(f"Loaded {loaded} user-tracked satellites into TLE cache")
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to load DB satellites into TLE cache: {e}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to load DB satellites into TLE cache: {e}")
|
||||
|
||||
|
||||
def _normalize_satellite_name(value: object) -> str:
|
||||
"""Normalize satellite identifiers for loose name matching."""
|
||||
return str(value or '').strip().replace(' ', '-').upper()
|
||||
|
||||
|
||||
def _get_tracked_satellite_maps() -> tuple[dict[int, dict], dict[str, dict]]:
|
||||
"""Return tracked satellites indexed by NORAD ID and normalized name."""
|
||||
by_norad: dict[int, dict] = {}
|
||||
by_name: dict[str, dict] = {}
|
||||
try:
|
||||
for sat in get_tracked_satellites():
|
||||
try:
|
||||
norad_id = int(sat['norad_id'])
|
||||
except (TypeError, ValueError):
|
||||
continue
|
||||
by_norad[norad_id] = sat
|
||||
by_name[_normalize_satellite_name(sat.get('name'))] = sat
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to read tracked satellites for lookup: {e}")
|
||||
return by_norad, by_name
|
||||
|
||||
|
||||
def _resolve_satellite_request(sat: object, tracked_by_norad: dict[int, dict], tracked_by_name: dict[str, dict]) -> tuple[str, int | None, tuple[str, str, str] | None]:
|
||||
"""Resolve a satellite request to display name, NORAD ID, and TLE data."""
|
||||
norad_id: int | None = None
|
||||
sat_key: str | None = None
|
||||
tracked: dict | None = None
|
||||
|
||||
if isinstance(sat, int):
|
||||
norad_id = sat
|
||||
elif isinstance(sat, str):
|
||||
stripped = sat.strip()
|
||||
if stripped.isdigit():
|
||||
norad_id = int(stripped)
|
||||
else:
|
||||
sat_key = stripped
|
||||
|
||||
if norad_id is not None:
|
||||
tracked = tracked_by_norad.get(norad_id)
|
||||
sat_key = _BUILTIN_NORAD_TO_KEY.get(norad_id) or (tracked.get('name') if tracked else str(norad_id))
|
||||
else:
|
||||
normalized = _normalize_satellite_name(sat_key)
|
||||
tracked = tracked_by_name.get(normalized)
|
||||
if tracked:
|
||||
try:
|
||||
norad_id = int(tracked['norad_id'])
|
||||
except (TypeError, ValueError):
|
||||
norad_id = None
|
||||
sat_key = tracked.get('name') or sat_key
|
||||
|
||||
tle_data = None
|
||||
candidate_keys: list[str] = []
|
||||
if sat_key:
|
||||
candidate_keys.extend([
|
||||
sat_key,
|
||||
_normalize_satellite_name(sat_key),
|
||||
])
|
||||
if tracked and tracked.get('name'):
|
||||
candidate_keys.extend([
|
||||
tracked['name'],
|
||||
_normalize_satellite_name(tracked['name']),
|
||||
])
|
||||
|
||||
seen: set[str] = set()
|
||||
for key in candidate_keys:
|
||||
norm = _normalize_satellite_name(key)
|
||||
if norm in seen:
|
||||
continue
|
||||
seen.add(norm)
|
||||
if key in _tle_cache:
|
||||
tle_data = _tle_cache[key]
|
||||
break
|
||||
if norm in _tle_cache:
|
||||
tle_data = _tle_cache[norm]
|
||||
break
|
||||
|
||||
if tle_data is None and tracked and tracked.get('tle_line1') and tracked.get('tle_line2'):
|
||||
display_name = tracked.get('name') or sat_key or str(norad_id or 'UNKNOWN')
|
||||
tle_data = (display_name, tracked['tle_line1'], tracked['tle_line2'])
|
||||
_tle_cache[_normalize_satellite_name(display_name)] = tle_data
|
||||
|
||||
if tle_data is None and sat_key:
|
||||
normalized = _normalize_satellite_name(sat_key)
|
||||
for key, value in _tle_cache.items():
|
||||
if key == normalized or _normalize_satellite_name(value[0]) == normalized:
|
||||
tle_data = value
|
||||
break
|
||||
|
||||
display_name = _BUILTIN_NORAD_TO_KEY.get(norad_id or -1)
|
||||
if not display_name:
|
||||
display_name = (tracked.get('name') if tracked else None) or (tle_data[0] if tle_data else None) or (sat_key if sat_key else str(norad_id or 'UNKNOWN'))
|
||||
return display_name, norad_id, tle_data
|
||||
|
||||
|
||||
def _start_satellite_tracker():
|
||||
@@ -328,45 +429,30 @@ def predict_passes():
|
||||
except ValueError as e:
|
||||
return api_error(str(e), 400)
|
||||
|
||||
norad_to_name = {
|
||||
25544: 'ISS',
|
||||
40069: 'METEOR-M2',
|
||||
57166: 'METEOR-M2-3',
|
||||
59051: 'METEOR-M2-4',
|
||||
}
|
||||
|
||||
sat_input = data.get('satellites', ['ISS', 'METEOR-M2-3', 'METEOR-M2-4'])
|
||||
satellites = []
|
||||
for sat in sat_input:
|
||||
if isinstance(sat, int) and sat in norad_to_name:
|
||||
satellites.append(norad_to_name[sat])
|
||||
else:
|
||||
satellites.append(sat)
|
||||
|
||||
passes = []
|
||||
passes = []
|
||||
colors = {
|
||||
'ISS': '#00ffff',
|
||||
'METEOR-M2': '#9370DB',
|
||||
'METEOR-M2-3': '#ff00ff',
|
||||
'METEOR-M2-4': '#00ff88',
|
||||
}
|
||||
name_to_norad = {v: k for k, v in norad_to_name.items()}
|
||||
|
||||
ts = _get_timescale()
|
||||
observer = wgs84.latlon(lat, lon)
|
||||
t0 = ts.now()
|
||||
t1 = ts.utc(t0.utc_datetime() + timedelta(hours=hours))
|
||||
|
||||
for sat_name in satellites:
|
||||
if sat_name not in _tle_cache:
|
||||
continue
|
||||
|
||||
tle_data = _tle_cache[sat_name]
|
||||
|
||||
# Current position for map marker (computed once per satellite)
|
||||
current_pos = None
|
||||
try:
|
||||
satellite = EarthSatellite(tle_data[1], tle_data[2], tle_data[0], ts)
|
||||
tracked_by_norad, tracked_by_name = _get_tracked_satellite_maps()
|
||||
|
||||
ts = _get_timescale()
|
||||
observer = wgs84.latlon(lat, lon)
|
||||
t0 = ts.now()
|
||||
t1 = ts.utc(t0.utc_datetime() + timedelta(hours=hours))
|
||||
|
||||
for sat in sat_input:
|
||||
sat_name, norad_id, tle_data = _resolve_satellite_request(sat, tracked_by_norad, tracked_by_name)
|
||||
if not tle_data:
|
||||
continue
|
||||
|
||||
# Current position for map marker (computed once per satellite)
|
||||
current_pos = None
|
||||
try:
|
||||
satellite = EarthSatellite(tle_data[1], tle_data[2], tle_data[0], ts)
|
||||
geo = satellite.at(ts.now())
|
||||
sp = wgs84.subpoint(geo)
|
||||
current_pos = {
|
||||
@@ -376,14 +462,14 @@ def predict_passes():
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
sat_passes = _predict_passes(tle_data, observer, ts, t0, t1, min_el=min_el)
|
||||
for p in sat_passes:
|
||||
p['satellite'] = sat_name
|
||||
p['norad'] = name_to_norad.get(sat_name, 0)
|
||||
p['color'] = colors.get(sat_name, '#00ff00')
|
||||
if current_pos:
|
||||
p['currentPos'] = current_pos
|
||||
passes.extend(sat_passes)
|
||||
sat_passes = _predict_passes(tle_data, observer, ts, t0, t1, min_el=min_el)
|
||||
for p in sat_passes:
|
||||
p['satellite'] = sat_name
|
||||
p['norad'] = norad_id or 0
|
||||
p['color'] = colors.get(sat_name, '#00ff00')
|
||||
if current_pos:
|
||||
p['currentPos'] = current_pos
|
||||
passes.extend(sat_passes)
|
||||
|
||||
passes.sort(key=lambda p: p['startTimeISO'])
|
||||
|
||||
@@ -413,35 +499,23 @@ def get_satellite_position():
|
||||
sat_input = data.get('satellites', [])
|
||||
include_track = bool(data.get('includeTrack', True))
|
||||
|
||||
norad_to_name = {
|
||||
25544: 'ISS',
|
||||
40069: 'METEOR-M2',
|
||||
57166: 'METEOR-M2-3',
|
||||
59051: 'METEOR-M2-4',
|
||||
}
|
||||
|
||||
satellites = []
|
||||
for sat in sat_input:
|
||||
if isinstance(sat, int) and sat in norad_to_name:
|
||||
satellites.append(norad_to_name[sat])
|
||||
else:
|
||||
satellites.append(sat)
|
||||
|
||||
ts = _get_timescale()
|
||||
observer = wgs84.latlon(lat, lon)
|
||||
now = ts.now()
|
||||
now_dt = now.utc_datetime()
|
||||
|
||||
positions = []
|
||||
|
||||
for sat_name in satellites:
|
||||
# Special handling for ISS - use real-time API for accurate position
|
||||
if sat_name == 'ISS':
|
||||
iss_data = _fetch_iss_realtime(lat, lon)
|
||||
if iss_data:
|
||||
# Add orbit track if requested (using TLE for track prediction)
|
||||
if include_track and 'ISS' in _tle_cache:
|
||||
try:
|
||||
ts = _get_timescale()
|
||||
observer = wgs84.latlon(lat, lon)
|
||||
now = ts.now()
|
||||
now_dt = now.utc_datetime()
|
||||
tracked_by_norad, tracked_by_name = _get_tracked_satellite_maps()
|
||||
|
||||
positions = []
|
||||
|
||||
for sat in sat_input:
|
||||
sat_name, norad_id, tle_data = _resolve_satellite_request(sat, tracked_by_norad, tracked_by_name)
|
||||
# Special handling for ISS - use real-time API for accurate position
|
||||
if norad_id == 25544 or sat_name == 'ISS':
|
||||
iss_data = _fetch_iss_realtime(lat, lon)
|
||||
if iss_data:
|
||||
# Add orbit track if requested (using TLE for track prediction)
|
||||
if include_track and 'ISS' in _tle_cache:
|
||||
try:
|
||||
tle_data = _tle_cache['ISS']
|
||||
satellite = EarthSatellite(tle_data[1], tle_data[2], tle_data[0], ts)
|
||||
orbit_track = []
|
||||
@@ -460,16 +534,15 @@ def get_satellite_position():
|
||||
iss_data['track'] = orbit_track
|
||||
except Exception:
|
||||
pass
|
||||
positions.append(iss_data)
|
||||
continue
|
||||
|
||||
# Other satellites - use TLE data
|
||||
if sat_name not in _tle_cache:
|
||||
continue
|
||||
|
||||
tle_data = _tle_cache[sat_name]
|
||||
try:
|
||||
satellite = EarthSatellite(tle_data[1], tle_data[2], tle_data[0], ts)
|
||||
positions.append(iss_data)
|
||||
continue
|
||||
|
||||
# Other satellites - use TLE data
|
||||
if not tle_data:
|
||||
continue
|
||||
|
||||
try:
|
||||
satellite = EarthSatellite(tle_data[1], tle_data[2], tle_data[0], ts)
|
||||
|
||||
geocentric = satellite.at(now)
|
||||
subpoint = wgs84.subpoint(geocentric)
|
||||
@@ -480,7 +553,7 @@ def get_satellite_position():
|
||||
|
||||
pos_data = {
|
||||
'satellite': sat_name,
|
||||
'norad_id': next((nid for nid, name in norad_to_name.items() if name == sat_name), None),
|
||||
'norad_id': norad_id,
|
||||
'lat': float(subpoint.latitude.degrees),
|
||||
'lon': float(subpoint.longitude.degrees),
|
||||
'altitude': float(geocentric.distance().km - 6371),
|
||||
|
||||
Reference in New Issue
Block a user