mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 06:40:00 -07:00
Stabilize satellite target switching
This commit is contained in:
@@ -566,8 +566,9 @@ def get_satellite_position():
|
|||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
return api_error(str(e), 400)
|
return api_error(str(e), 400)
|
||||||
|
|
||||||
sat_input = data.get('satellites', [])
|
sat_input = data.get('satellites', [])
|
||||||
include_track = bool(data.get('includeTrack', True))
|
include_track = bool(data.get('includeTrack', True))
|
||||||
|
prefer_realtime_api = bool(data.get('preferRealtimeApi', False))
|
||||||
|
|
||||||
observer = wgs84.latlon(lat, lon)
|
observer = wgs84.latlon(lat, lon)
|
||||||
ts = None
|
ts = None
|
||||||
@@ -579,8 +580,9 @@ def get_satellite_position():
|
|||||||
|
|
||||||
for sat in sat_input:
|
for sat in sat_input:
|
||||||
sat_name, norad_id, tle_data = _resolve_satellite_request(sat, tracked_by_norad, tracked_by_name)
|
sat_name, norad_id, tle_data = _resolve_satellite_request(sat, tracked_by_norad, tracked_by_name)
|
||||||
# Special handling for ISS - prefer real-time API, but fall back to TLE if offline.
|
# Optional special handling for ISS. The dashboard does not enable this
|
||||||
if norad_id == 25544 or sat_name == 'ISS':
|
# because external API latency can make live updates stall.
|
||||||
|
if prefer_realtime_api and (norad_id == 25544 or sat_name == 'ISS'):
|
||||||
iss_data = _fetch_iss_realtime(lat, lon)
|
iss_data = _fetch_iss_realtime(lat, lon)
|
||||||
if iss_data:
|
if iss_data:
|
||||||
# Add orbit track if requested (using TLE for track prediction)
|
# Add orbit track if requested (using TLE for track prediction)
|
||||||
|
|||||||
@@ -775,6 +775,8 @@
|
|||||||
let agents = [];
|
let agents = [];
|
||||||
let _txRequestId = 0;
|
let _txRequestId = 0;
|
||||||
let _telemetryPollTimer = null;
|
let _telemetryPollTimer = null;
|
||||||
|
let _telemetryAbortController = null;
|
||||||
|
let _activeTelemetryRequestKey = null;
|
||||||
let _passRequestId = 0;
|
let _passRequestId = 0;
|
||||||
let _passAbortController = null;
|
let _passAbortController = null;
|
||||||
let _passTimeoutId = null;
|
let _passTimeoutId = null;
|
||||||
@@ -792,7 +794,8 @@
|
|||||||
const DASHBOARD_FETCH_TIMEOUT_MS = 30000;
|
const DASHBOARD_FETCH_TIMEOUT_MS = 30000;
|
||||||
const PASS_FETCH_TIMEOUT_MS = 90000;
|
const PASS_FETCH_TIMEOUT_MS = 90000;
|
||||||
const SAT_DRAWER_FETCH_TIMEOUT_MS = 15000;
|
const SAT_DRAWER_FETCH_TIMEOUT_MS = 15000;
|
||||||
const TELEMETRY_POLL_INTERVAL_MS = 3000;
|
const TELEMETRY_POLL_INTERVAL_MS = 5000;
|
||||||
|
const TELEMETRY_FETCH_TIMEOUT_MS = 8000;
|
||||||
const BUILTIN_TX_FALLBACK = {
|
const BUILTIN_TX_FALLBACK = {
|
||||||
25544: [
|
25544: [
|
||||||
{ description: 'APRS digipeater', downlink_low: 145.825, downlink_high: 145.825, uplink_low: null, uplink_high: null, mode: 'FM AX.25', baud: 1200, status: 'active', type: 'beacon', service: 'Packet' },
|
{ description: 'APRS digipeater', downlink_low: 145.825, downlink_high: 145.825, uplink_low: null, uplink_high: null, mode: 'FM AX.25', baud: 1200, status: 'active', type: 'beacon', service: 'Packet' },
|
||||||
@@ -913,13 +916,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getPassPredictionTargets() {
|
function getPassPredictionTargets() {
|
||||||
const targets = Object.keys(satellites || {})
|
if (Number.isFinite(selectedSatellite)) {
|
||||||
.map(id => parseInt(id, 10))
|
return [selectedSatellite];
|
||||||
.filter(id => Number.isFinite(id));
|
|
||||||
if (Number.isFinite(selectedSatellite) && !targets.includes(selectedSatellite)) {
|
|
||||||
targets.unshift(selectedSatellite);
|
|
||||||
}
|
}
|
||||||
return Array.from(new Set(targets));
|
return [25544];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getActivePassRequestKey(targetIds = getPassPredictionTargets()) {
|
function getActivePassRequestKey(targetIds = getPassPredictionTargets()) {
|
||||||
@@ -1170,6 +1170,11 @@
|
|||||||
selectedPass = null;
|
selectedPass = null;
|
||||||
passes = [];
|
passes = [];
|
||||||
latestLivePosition = null;
|
latestLivePosition = null;
|
||||||
|
if (_telemetryAbortController) {
|
||||||
|
_telemetryAbortController.abort();
|
||||||
|
_telemetryAbortController = null;
|
||||||
|
}
|
||||||
|
_activeTelemetryRequestKey = null;
|
||||||
|
|
||||||
if (groundMap) {
|
if (groundMap) {
|
||||||
if (trackLine) { groundMap.removeLayer(trackLine); trackLine = null; }
|
if (trackLine) { groundMap.removeLayer(trackLine); trackLine = null; }
|
||||||
@@ -1299,10 +1304,17 @@
|
|||||||
const lon = parseFloat(document.getElementById('obsLon')?.value);
|
const lon = parseFloat(document.getElementById('obsLon')?.value);
|
||||||
if (!Number.isFinite(lat) || !Number.isFinite(lon) || !selectedSatellite) return;
|
if (!Number.isFinite(lat) || !Number.isFinite(lon) || !selectedSatellite) return;
|
||||||
const requestedSatellite = selectedSatellite;
|
const requestedSatellite = selectedSatellite;
|
||||||
|
const requestKey = `telemetry:${requestedSatellite}:${lat.toFixed(3)}:${lon.toFixed(3)}`;
|
||||||
|
|
||||||
|
if (_telemetryAbortController && _activeTelemetryRequestKey === requestKey) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
const timeout = setTimeout(() => controller.abort(), DASHBOARD_FETCH_TIMEOUT_MS);
|
_telemetryAbortController = controller;
|
||||||
|
_activeTelemetryRequestKey = requestKey;
|
||||||
|
const timeout = setTimeout(() => controller.abort(), TELEMETRY_FETCH_TIMEOUT_MS);
|
||||||
const response = await fetch('/satellite/position', {
|
const response = await fetch('/satellite/position', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
credentials: 'same-origin',
|
credentials: 'same-origin',
|
||||||
@@ -1316,6 +1328,12 @@
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
|
if (_telemetryAbortController === controller) {
|
||||||
|
_telemetryAbortController = null;
|
||||||
|
}
|
||||||
|
if (_activeTelemetryRequestKey === requestKey) {
|
||||||
|
_activeTelemetryRequestKey = null;
|
||||||
|
}
|
||||||
if (!response.ok) return;
|
if (!response.ok) return;
|
||||||
const contentType = response.headers.get('Content-Type') || '';
|
const contentType = response.headers.get('Content-Type') || '';
|
||||||
if (!contentType.includes('application/json')) return;
|
if (!contentType.includes('application/json')) return;
|
||||||
@@ -1326,7 +1344,14 @@
|
|||||||
cacheLivePosition(requestedSatellite, pos);
|
cacheLivePosition(requestedSatellite, pos);
|
||||||
if (requestedSatellite !== selectedSatellite) return;
|
if (requestedSatellite !== selectedSatellite) return;
|
||||||
handleLivePositions(data.positions);
|
handleLivePositions(data.positions);
|
||||||
} catch (_) {}
|
} catch (_) {
|
||||||
|
if (_telemetryAbortController?.signal?.aborted) {
|
||||||
|
_telemetryAbortController = null;
|
||||||
|
}
|
||||||
|
if (_activeTelemetryRequestKey === requestKey) {
|
||||||
|
_activeTelemetryRequestKey = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function startTelemetryPolling() {
|
function startTelemetryPolling() {
|
||||||
@@ -1593,6 +1618,10 @@
|
|||||||
// Refresh satellite list when the window regains focus (e.g. after enabling sats in the sidebar)
|
// Refresh satellite list when the window regains focus (e.g. after enabling sats in the sidebar)
|
||||||
window.addEventListener('focus', loadDashboardSatellites);
|
window.addEventListener('focus', loadDashboardSatellites);
|
||||||
window.addEventListener('pagehide', () => {
|
window.addEventListener('pagehide', () => {
|
||||||
|
if (_telemetryAbortController) {
|
||||||
|
_telemetryAbortController.abort();
|
||||||
|
_telemetryAbortController = null;
|
||||||
|
}
|
||||||
if (_passAbortController) {
|
if (_passAbortController) {
|
||||||
_passAbortController.abort('pagehide');
|
_passAbortController.abort('pagehide');
|
||||||
_passAbortController = null;
|
_passAbortController = null;
|
||||||
|
|||||||
Reference in New Issue
Block a user