feat: Add GPS auto-connect for AIS dashboard via gpsd

Automatically connects to gpsd on page load if available. Updates
observer location in real-time with GPS indicator in top bar.
Includes auto-reconnect on visibility change.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Smittix
2026-01-28 22:35:49 +00:00
parent f3c5d124b5
commit 069e87f9ba
2 changed files with 141 additions and 1 deletions

View File

@@ -1201,3 +1201,33 @@ body {
font-size: 18px;
}
}
/* GPS Indicator */
.gps-indicator {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 8px;
background: rgba(34, 197, 94, 0.15);
border: 1px solid #22c55e;
border-radius: 12px;
font-size: 10px;
font-weight: 600;
color: #22c55e;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-left: 10px;
}
.gps-indicator .gps-dot {
width: 6px;
height: 6px;
background: #22c55e;
border-radius: 50%;
animation: gps-pulse 2s ease-in-out infinite;
}
@keyframes gps-pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.5; transform: scale(0.8); }
}

View File

@@ -84,6 +84,7 @@
<span id="trackingStatus">STANDBY</span>
</div>
<div class="strip-time" id="utcTime">--:--:-- UTC</div>
<span id="gpsIndicator" class="gps-indicator" style="display: none;" title="GPS connected via gpsd"><span class="gps-dot"></span> GPS</span>
</div>
</div>
@@ -217,6 +218,11 @@
let rangeRingsLayer = null;
let observerMarker = null;
// GPS state
let gpsConnected = false;
let gpsEventSource = null;
let gpsReconnectTimeout = null;
// Statistics
let stats = {
totalVesselsSeen: new Set(),
@@ -948,6 +954,106 @@
document.getElementById('utcTime').textContent = utc + ' UTC';
}
// ============================================
// GPS FUNCTIONS (gpsd auto-connect)
// ============================================
async function autoConnectGps() {
try {
const response = await fetch('/gps/auto-connect', { method: 'POST' });
const data = await response.json();
if (data.status === 'connected') {
gpsConnected = true;
startGpsStream();
showGpsIndicator(true);
console.log('GPS: Auto-connected to gpsd');
if (data.position) {
updateLocationFromGps(data.position);
}
} else {
console.log('GPS: gpsd not available -', data.message);
}
} catch (e) {
console.log('GPS: Auto-connect failed -', e.message);
}
}
function startGpsStream() {
if (gpsEventSource) {
gpsEventSource.close();
}
if (gpsReconnectTimeout) {
clearTimeout(gpsReconnectTimeout);
gpsReconnectTimeout = null;
}
gpsEventSource = new EventSource('/gps/stream');
gpsEventSource.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
if (data.type === 'position' && data.latitude && data.longitude) {
updateLocationFromGps(data);
}
} catch (e) {
console.error('GPS parse error:', e);
}
};
gpsEventSource.onerror = (e) => {
// Don't log every error - connection suspends are normal
if (gpsEventSource) {
gpsEventSource.close();
gpsEventSource = null;
}
// Auto-reconnect after 5 seconds if still connected
if (gpsConnected && !gpsReconnectTimeout) {
gpsReconnectTimeout = setTimeout(() => {
gpsReconnectTimeout = null;
if (gpsConnected) {
startGpsStream();
}
}, 5000);
}
};
}
// Reconnect GPS stream when tab becomes visible
document.addEventListener('visibilitychange', () => {
if (!document.hidden && gpsConnected && !gpsEventSource) {
startGpsStream();
}
});
function updateLocationFromGps(position) {
observerLocation.lat = position.latitude;
observerLocation.lon = position.longitude;
document.getElementById('obsLat').value = position.latitude.toFixed(4);
document.getElementById('obsLon').value = position.longitude.toFixed(4);
// Update observer marker position
if (observerMarker) {
observerMarker.setLatLng([position.latitude, position.longitude]);
}
// Center map on GPS location (on first fix)
if (vesselMap && !vesselMap._gpsInitialized) {
vesselMap.setView([position.latitude, position.longitude], vesselMap.getZoom());
vesselMap._gpsInitialized = true;
}
// Redraw range rings at new position
drawRangeRings();
// Save to localStorage
localStorage.setItem('ais_observerLocation', JSON.stringify(observerLocation));
}
function showGpsIndicator(show) {
const indicator = document.getElementById('gpsIndicator');
if (indicator) {
indicator.style.display = show ? 'inline-flex' : 'none';
}
}
// Session timer functions
function startSessionTimer() {
if (!stats.sessionStart) {
@@ -1344,7 +1450,11 @@
}
// Initialize
document.addEventListener('DOMContentLoaded', initMap);
document.addEventListener('DOMContentLoaded', function() {
initMap();
// Auto-connect to gpsd if available
autoConnectGps();
});
</script>
<!-- Agent styles -->