mirror of
https://github.com/smittix/intercept.git
synced 2026-05-31 02:03:37 -07:00
8379f42ec3
SSE EventSource connections for AIS, ACARS, VDL2, and radiosonde were not closed when switching modes, causing fd exhaustion after repeated switches. Also fixes socket leaks on exception paths in AIS/ADS-B stream parsers, closes subprocess pipes in safe_terminate/cleanup, and caches skyfield timescale at module level to avoid per-request fd churn. Closes #169 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
191 lines
9.8 KiB
HTML
191 lines
9.8 KiB
HTML
<!-- AIS VESSEL TRACKING MODE -->
|
|
<div id="aisMode" class="mode-content" style="display: none;">
|
|
<div class="section">
|
|
<h3>AIS Vessel Tracking</h3>
|
|
<div class="info-text" style="margin-bottom: 15px;">
|
|
Track ships and vessels via AIS (Automatic Identification System) on 161.975 / 162.025 MHz.
|
|
</div>
|
|
<a href="/ais/dashboard" target="_blank" class="run-btn" style="display: inline-block; text-decoration: none; text-align: center; margin-bottom: 15px;">
|
|
Open AIS Dashboard
|
|
</a>
|
|
</div>
|
|
|
|
<div class="section">
|
|
<h3>Settings</h3>
|
|
<div class="form-group">
|
|
<label>Gain (dB, 0 = auto)</label>
|
|
<input type="number" id="aisGainInput" value="40" min="0" max="50" placeholder="0-50">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="section">
|
|
<h3>Status</h3>
|
|
<div id="aisStatusDisplay" class="info-text">
|
|
<p>Status: <span id="aisStatusText" style="color: var(--accent-yellow);">Standby</span></p>
|
|
<p>Vessels: <span id="aisVesselCount">0</span></p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Antenna Guide -->
|
|
<div class="section">
|
|
<h3>Antenna Guide</h3>
|
|
<div style="font-size: 11px; color: var(--text-dim); line-height: 1.5;">
|
|
<p style="margin-bottom: 8px; color: var(--accent-cyan); font-weight: 600;">
|
|
Marine VHF band (162 MHz) — stock SDR antenna will NOT work well
|
|
</p>
|
|
|
|
<div style="background: var(--bg-primary); border: 1px solid var(--border-color); border-radius: 4px; padding: 10px; margin-bottom: 10px;">
|
|
<strong style="color: var(--accent-cyan); font-size: 12px;">Simple Dipole (Cheapest)</strong>
|
|
<ul style="margin: 6px 0 0 14px; padding: 0;">
|
|
<li><strong style="color: var(--text-primary);">Element length:</strong> ~46 cm each (quarter-wave at 162 MHz)</li>
|
|
<li><strong style="color: var(--text-primary);">Material:</strong> Wire, coat hanger, or copper rod</li>
|
|
<li><strong style="color: var(--text-primary);">Orientation:</strong> Vertical (AIS is vertically polarized)</li>
|
|
<li><strong style="color: var(--text-primary);">Placement:</strong> As high as possible with clear view of the water/harbor</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div style="background: var(--bg-primary); border: 1px solid var(--border-color); border-radius: 4px; padding: 10px; margin-bottom: 10px;">
|
|
<strong style="color: var(--accent-cyan); font-size: 12px;">Commercial Options</strong>
|
|
<ul style="margin: 6px 0 0 14px; padding: 0;">
|
|
<li><strong style="color: var(--text-primary);">Marine VHF whip:</strong> ~$20–50, designed for 156–163 MHz band</li>
|
|
<li><strong style="color: var(--text-primary);">Discone:</strong> ~$30–50, wideband coverage including marine VHF</li>
|
|
<li><strong style="color: var(--text-primary);">Collinear:</strong> Higher gain (~6 dBi), best for coastal monitoring</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div style="background: var(--bg-primary); border: 1px solid var(--border-color); border-radius: 4px; padding: 10px; margin-bottom: 10px;">
|
|
<strong style="color: var(--accent-cyan); font-size: 12px;">Placement Tips</strong>
|
|
<ul style="margin: 6px 0 0 14px; padding: 0;">
|
|
<li><strong style="color: var(--text-primary);">Height is critical:</strong> AIS is line-of-sight. Roof or mast mount is ideal</li>
|
|
<li><strong style="color: var(--text-primary);">Range:</strong> At 10m height, expect ~25 NM (46 km) range over water</li>
|
|
<li><strong style="color: var(--text-primary);">LNA:</strong> Nooelec Lana or similar broadband LNA, mount at antenna</li>
|
|
<li><strong style="color: var(--text-primary);">Coax:</strong> Keep cable short. RG-58 loses ~4 dB per 10m at 162 MHz</li>
|
|
</ul>
|
|
</div>
|
|
|
|
<div style="background: var(--bg-primary); border: 1px solid var(--border-color); border-radius: 4px; padding: 10px;">
|
|
<strong style="color: var(--accent-cyan); font-size: 12px;">Quick Reference</strong>
|
|
<table style="width: 100%; margin-top: 6px; font-size: 10px; border-collapse: collapse;">
|
|
<tr style="border-bottom: 1px solid var(--border-color);">
|
|
<td style="padding: 3px 4px; color: var(--text-dim);">AIS Channel A</td>
|
|
<td style="padding: 3px 4px; color: var(--text-primary); text-align: right;">161.975 MHz</td>
|
|
</tr>
|
|
<tr style="border-bottom: 1px solid var(--border-color);">
|
|
<td style="padding: 3px 4px; color: var(--text-dim);">AIS Channel B</td>
|
|
<td style="padding: 3px 4px; color: var(--text-primary); text-align: right;">162.025 MHz</td>
|
|
</tr>
|
|
<tr style="border-bottom: 1px solid var(--border-color);">
|
|
<td style="padding: 3px 4px; color: var(--text-dim);">Quarter-wave length</td>
|
|
<td style="padding: 3px 4px; color: var(--text-primary); text-align: right;">46 cm</td>
|
|
</tr>
|
|
<tr style="border-bottom: 1px solid var(--border-color);">
|
|
<td style="padding: 3px 4px; color: var(--text-dim);">Modulation</td>
|
|
<td style="padding: 3px 4px; color: var(--text-primary); text-align: right;">GMSK 9600 baud</td>
|
|
</tr>
|
|
<tr style="border-bottom: 1px solid var(--border-color);">
|
|
<td style="padding: 3px 4px; color: var(--text-dim);">Bandwidth</td>
|
|
<td style="padding: 3px 4px; color: var(--text-primary); text-align: right;">25 kHz</td>
|
|
</tr>
|
|
<tr>
|
|
<td style="padding: 3px 4px; color: var(--text-dim);">Polarization</td>
|
|
<td style="padding: 3px 4px; color: var(--text-primary); text-align: right;">Vertical</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<button class="run-btn" id="startAisBtn" onclick="startAisTracking()">
|
|
Start AIS Tracking
|
|
</button>
|
|
<button class="stop-btn" id="stopAisBtn" onclick="stopAisTracking()" style="display: none;">
|
|
Stop AIS Tracking
|
|
</button>
|
|
</div>
|
|
|
|
<script>
|
|
let aisEventSource = null;
|
|
let aisVessels = {};
|
|
|
|
function startAisTracking() {
|
|
const gain = document.getElementById('aisGainInput').value || '40';
|
|
const device = document.getElementById('deviceSelect')?.value || '0';
|
|
|
|
fetch('/ais/start', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ device, gain, bias_t: typeof getBiasTEnabled === 'function' ? getBiasTEnabled() : false })
|
|
})
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.status === 'started' || data.status === 'already_running') {
|
|
document.getElementById('startAisBtn').style.display = 'none';
|
|
document.getElementById('stopAisBtn').style.display = 'block';
|
|
document.getElementById('aisStatusText').textContent = 'Tracking';
|
|
document.getElementById('aisStatusText').style.color = 'var(--accent-green)';
|
|
startAisSSE();
|
|
} else {
|
|
alert(data.message || 'Failed to start AIS tracking');
|
|
}
|
|
})
|
|
.catch(err => alert('Error: ' + err.message));
|
|
}
|
|
|
|
function stopAisTracking() {
|
|
fetch('/ais/stop', { method: 'POST' })
|
|
.then(r => r.json())
|
|
.then(() => {
|
|
document.getElementById('startAisBtn').style.display = 'block';
|
|
document.getElementById('stopAisBtn').style.display = 'none';
|
|
document.getElementById('aisStatusText').textContent = 'Standby';
|
|
document.getElementById('aisStatusText').style.color = 'var(--accent-yellow)';
|
|
document.getElementById('aisVesselCount').textContent = '0';
|
|
if (aisEventSource) {
|
|
aisEventSource.close();
|
|
aisEventSource = null;
|
|
}
|
|
aisVessels = {};
|
|
});
|
|
}
|
|
|
|
function startAisSSE() {
|
|
if (aisEventSource) aisEventSource.close();
|
|
|
|
aisEventSource = new EventSource('/ais/stream');
|
|
aisEventSource.onmessage = function(e) {
|
|
try {
|
|
const data = JSON.parse(e.data);
|
|
if (data.type === 'vessel') {
|
|
aisVessels[data.mmsi] = data;
|
|
document.getElementById('aisVesselCount').textContent = Object.keys(aisVessels).length;
|
|
}
|
|
} catch (err) {}
|
|
};
|
|
|
|
aisEventSource.onerror = function() {
|
|
setTimeout(() => {
|
|
const panel = document.getElementById('aisMode');
|
|
if (panel && panel.classList.contains('active') &&
|
|
document.getElementById('stopAisBtn').style.display === 'block') {
|
|
startAisSSE();
|
|
}
|
|
}, 2000);
|
|
};
|
|
}
|
|
|
|
// Check initial status
|
|
fetch('/ais/status')
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (data.tracking_active) {
|
|
document.getElementById('startAisBtn').style.display = 'none';
|
|
document.getElementById('stopAisBtn').style.display = 'block';
|
|
document.getElementById('aisStatusText').textContent = 'Tracking';
|
|
document.getElementById('aisStatusText').style.color = 'var(--accent-green)';
|
|
document.getElementById('aisVesselCount').textContent = data.vessel_count || 0;
|
|
startAisSSE();
|
|
}
|
|
})
|
|
.catch(() => {});
|
|
</script>
|