fix(adsb): fix aircraft photo display and add Drone Intelligence docs

- Fix stale DOM refs in fetchAircraftPhoto: elements were captured before
  await fetch(), but showAircraftDetails rebuilds innerHTML on every RAF
  update, leaving the async path writing to detached nodes. Now re-queries
  the DOM after await, and the cache (synchronous) path queries inline so
  refs are always fresh.
- Add thumbnail fallback in aircraft_photo route: fall back to thumbnail
  when thumbnail_large.src is absent rather than returning null.
- Add Drone Intelligence to nav, help modal, cheat sheets, README, and docs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
James Smith
2026-05-05 09:24:30 +01:00
parent 347659c275
commit 037c9c5aee
9 changed files with 654 additions and 469 deletions
+20 -10
View File
@@ -3555,17 +3555,15 @@ sudo make install</code>
const photoCache = {};
async function fetchAircraftPhoto(registration) {
const container = document.getElementById('aircraftPhotoContainer');
const img = document.getElementById('aircraftPhoto');
const link = document.getElementById('aircraftPhotoLink');
const credit = document.getElementById('aircraftPhotoCredit');
if (!container || !img) return;
// Check cache first
// Check cache first (synchronous path — DOM refs are always current here)
if (photoCache[registration]) {
const cached = photoCache[registration];
if (cached.thumbnail) {
const container = document.getElementById('aircraftPhotoContainer');
const img = document.getElementById('aircraftPhoto');
const link = document.getElementById('aircraftPhotoLink');
const credit = document.getElementById('aircraftPhotoCredit');
if (!container || !img) return;
img.src = cached.thumbnail;
link.href = cached.link || '#';
credit.textContent = cached.photographer ? `Photo: ${cached.photographer}` : '';
@@ -3574,13 +3572,24 @@ sudo make install</code>
return;
}
// Guard: bail early if the panel doesn't exist yet
if (!document.getElementById('aircraftPhotoContainer')) return;
try {
const response = await fetch(`/adsb/aircraft-photo/${encodeURIComponent(registration)}`);
const data = await response.json();
// Cache the result
// Cache before touching DOM — subsequent synchronous calls will hit this
photoCache[registration] = data;
// Re-query after the await: showAircraftDetails rebuilds innerHTML on every
// RAF update, so refs captured before the await may point to detached nodes.
const container = document.getElementById('aircraftPhotoContainer');
const img = document.getElementById('aircraftPhoto');
const link = document.getElementById('aircraftPhotoLink');
const credit = document.getElementById('aircraftPhotoCredit');
if (!container || !img) return;
if (data.success && data.thumbnail) {
img.src = data.thumbnail;
link.href = data.link || '#';
@@ -3591,7 +3600,8 @@ sudo make install</code>
}
} catch (err) {
console.debug('Failed to fetch aircraft photo:', err);
container.style.display = 'none';
const container = document.getElementById('aircraftPhotoContainer');
if (container) container.style.display = 'none';
}
}
+11
View File
@@ -270,6 +270,17 @@
<li><em style="color: var(--text-muted);">Note: This feature is in early development</em></li>
</ul>
<h3>Drone Intelligence Mode</h3>
<ul class="tip-list">
<li>Detects UAVs via three simultaneous vectors: Remote ID (WiFi/BLE), RTL-SDR 433/868 MHz RF, and HackRF 2.4/5.8 GHz</li>
<li>Parses ASTM F3411 Remote ID broadcast frames — captures drone ID, operator ID, and GPS position</li>
<li>RF fingerprinting on 433/868 MHz ISM bands and 2.4/5.8 GHz to detect drone control links and video downlinks</li>
<li>Correlates observations across all vectors into unified <em>DroneContact</em> entries with risk scoring</li>
<li>Risk levels: <strong>High</strong> (non-compliant / no Remote ID), <strong>Medium</strong> (multi-vector or RSSI delta &gt;15 dB), <strong>Low</strong> (compliant, single vector)</li>
<li>Live map shows last known position for Remote ID contacts with GPS data</li>
<li>Requires: WiFi adapter (monitor mode) for BLE Remote ID, RTL-SDR for 433/868 MHz, HackRF for 2.4/5.8 GHz</li>
</ul>
<h3>Network Monitor</h3>
<ul class="tip-list">
<li>Aggregates data from multiple remote INTERCEPT agents</li>
+1
View File
@@ -150,6 +150,7 @@
{{ mode_item('tscm', 'TSCM', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>') }}
{{ mode_item('spystations', 'Spy Stations', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4.9 19.1C1 15.2 1 8.8 4.9 4.9"/><path d="M7.8 16.2c-2.3-2.3-2.3-6.1 0-8.5"/><circle cx="12" cy="12" r="2"/><path d="M16.2 7.8c2.3 2.3 2.3 6.1 0 8.5"/><path d="M19.1 4.9C23 8.8 23 15.1 19.1 19"/></svg>') }}
{{ mode_item('websdr', 'WebSDR', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><line x1="2" y1="12" x2="22" y2="12"/><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"/></svg>') }}
{{ mode_item('drone', 'Drone Intel', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="6" cy="6" r="2"/><circle cx="18" cy="6" r="2"/><circle cx="6" cy="18" r="2"/><circle cx="18" cy="18" r="2"/><rect x="9" y="9" width="6" height="6" rx="1"/><line x1="8" y1="8" x2="9" y2="9"/><line x1="16" y1="8" x2="15" y2="9"/><line x1="8" y1="16" x2="9" y2="15"/><line x1="16" y1="16" x2="15" y2="15"/></svg>') }}
</div>
</div>