mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 06:40:00 -07:00
feat: ship waterfall receiver overhaul and platform mode updates
This commit is contained in:
@@ -1,211 +0,0 @@
|
||||
<!-- ANALYTICS MODE -->
|
||||
<div id="analyticsMode" class="mode-content">
|
||||
{# Analytics Dashboard Sidebar Panel #}
|
||||
|
||||
<div class="section">
|
||||
<h3 class="section-header collapsible" onclick="toggleSection(this)">
|
||||
<span>Summary</span>
|
||||
<span class="collapse-icon">▼</span>
|
||||
</h3>
|
||||
<div class="section-content">
|
||||
<div class="analytics-grid" id="analyticsSummaryCards">
|
||||
<div class="analytics-card" data-mode="adsb">
|
||||
<div class="card-count" id="analyticsCountAdsb">0</div>
|
||||
<div class="card-label">Aircraft</div>
|
||||
<div class="card-sparkline" id="analyticsSparkAdsb"></div>
|
||||
</div>
|
||||
<div class="analytics-card" data-mode="ais">
|
||||
<div class="card-count" id="analyticsCountAis">0</div>
|
||||
<div class="card-label">Vessels</div>
|
||||
<div class="card-sparkline" id="analyticsSparkAis"></div>
|
||||
</div>
|
||||
<div class="analytics-card" data-mode="wifi">
|
||||
<div class="card-count" id="analyticsCountWifi">0</div>
|
||||
<div class="card-label">WiFi</div>
|
||||
<div class="card-sparkline" id="analyticsSparkWifi"></div>
|
||||
</div>
|
||||
<div class="analytics-card" data-mode="bluetooth">
|
||||
<div class="card-count" id="analyticsCountBt">0</div>
|
||||
<div class="card-label">Bluetooth</div>
|
||||
<div class="card-sparkline" id="analyticsSparkBt"></div>
|
||||
</div>
|
||||
<div class="analytics-card" data-mode="dsc">
|
||||
<div class="card-count" id="analyticsCountDsc">0</div>
|
||||
<div class="card-label">DSC</div>
|
||||
<div class="card-sparkline" id="analyticsSparkDsc"></div>
|
||||
</div>
|
||||
<div class="analytics-card" data-mode="acars">
|
||||
<div class="card-count" id="analyticsCountAcars">0</div>
|
||||
<div class="card-label">ACARS</div>
|
||||
<div class="card-sparkline" id="analyticsSparkAcars"></div>
|
||||
</div>
|
||||
<div class="analytics-card" data-mode="vdl2">
|
||||
<div class="card-count" id="analyticsCountVdl2">0</div>
|
||||
<div class="card-label">VDL2</div>
|
||||
<div class="card-sparkline" id="analyticsSparkVdl2"></div>
|
||||
</div>
|
||||
<div class="analytics-card" data-mode="aprs">
|
||||
<div class="card-count" id="analyticsCountAprs">0</div>
|
||||
<div class="card-label">APRS</div>
|
||||
<div class="card-sparkline" id="analyticsSparkAprs"></div>
|
||||
</div>
|
||||
<div class="analytics-card" data-mode="meshtastic">
|
||||
<div class="card-count" id="analyticsCountMesh">0</div>
|
||||
<div class="card-label">Mesh</div>
|
||||
<div class="card-sparkline" id="analyticsSparkMesh"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3 class="section-header collapsible" onclick="toggleSection(this)">
|
||||
<span>Operational Insights</span>
|
||||
<span class="collapse-icon">▼</span>
|
||||
</h3>
|
||||
<div class="section-content">
|
||||
<div class="analytics-insight-grid" id="analyticsInsights">
|
||||
<div class="analytics-empty">Insights loading...</div>
|
||||
</div>
|
||||
<div class="analytics-top-changes">
|
||||
<div class="analytics-section-header">Top Changes</div>
|
||||
<div id="analyticsTopChanges">
|
||||
<div class="analytics-empty">No change signals yet</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3 class="section-header collapsible" onclick="toggleSection(this)">
|
||||
<span>Mode Health</span>
|
||||
<span class="collapse-icon">▼</span>
|
||||
</h3>
|
||||
<div class="section-content">
|
||||
<div class="analytics-health" id="analyticsHealth"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section" id="analyticsSquawkSection" style="display:none;">
|
||||
<h3 class="section-header collapsible" onclick="toggleSection(this)">
|
||||
<span>Emergency Squawks</span>
|
||||
<span class="collapse-icon">▼</span>
|
||||
</h3>
|
||||
<div class="section-content">
|
||||
<div class="squawk-emergency" id="analyticsSquawkPanel">
|
||||
<div class="squawk-title">Active Emergency Codes</div>
|
||||
<div id="analyticsSquawkList"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3 class="section-header collapsible" onclick="toggleSection(this)">
|
||||
<span>Temporal Patterns</span>
|
||||
<span class="collapse-icon">▼</span>
|
||||
</h3>
|
||||
<div class="section-content">
|
||||
<div id="analyticsPatternList">
|
||||
<div class="analytics-empty">No recurring patterns detected</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3 class="section-header collapsible" onclick="toggleSection(this)">
|
||||
<span>Recent Alerts</span>
|
||||
<span class="collapse-icon">▼</span>
|
||||
</h3>
|
||||
<div class="section-content">
|
||||
<div class="analytics-alert-feed" id="analyticsAlertFeed">
|
||||
<div class="analytics-empty">No recent alerts</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3 class="section-header collapsible" onclick="toggleSection(this)">
|
||||
<span>Correlations</span>
|
||||
<span class="collapse-icon">▼</span>
|
||||
</h3>
|
||||
<div class="section-content">
|
||||
<div id="analyticsCorrelations">
|
||||
<div class="analytics-empty">No correlations detected</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3 class="section-header collapsible" onclick="toggleSection(this)">
|
||||
<span>Geofences</span>
|
||||
<span class="collapse-icon">▼</span>
|
||||
</h3>
|
||||
<div class="section-content">
|
||||
<div id="analyticsGeofenceList"></div>
|
||||
<button class="btn btn-sm" onclick="Analytics.addGeofence()" style="margin-top:8px; font-size:10px; padding:4px 10px; background:var(--accent-cyan); color:#fff; border:none; border-radius:4px; cursor:pointer;">
|
||||
+ Add Zone
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3 class="section-header collapsible" onclick="toggleSection(this)">
|
||||
<span>Target View</span>
|
||||
<span class="collapse-icon">▼</span>
|
||||
</h3>
|
||||
<div class="section-content">
|
||||
<div class="analytics-target-toolbar">
|
||||
<input id="analyticsTargetQuery" type="text" placeholder="Search callsign, ICAO, MMSI, MAC, SSID, node..." onkeydown="if(event.key==='Enter'){Analytics.searchTarget();}">
|
||||
<button onclick="Analytics.searchTarget()">Search</button>
|
||||
</div>
|
||||
<div id="analyticsTargetSummary" class="analytics-target-summary">Search to correlate entities across modes</div>
|
||||
<div id="analyticsTargetResults">
|
||||
<div class="analytics-empty">No target selected</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3 class="section-header collapsible" onclick="toggleSection(this)">
|
||||
<span>Session Replay</span>
|
||||
<span class="collapse-icon">▼</span>
|
||||
</h3>
|
||||
<div class="section-content">
|
||||
<div class="analytics-replay-toolbar">
|
||||
<select id="analyticsReplaySelect"></select>
|
||||
<button onclick="Analytics.loadReplay()">Load</button>
|
||||
<button onclick="Analytics.playReplay()">Play</button>
|
||||
<button onclick="Analytics.pauseReplay()">Pause</button>
|
||||
<button onclick="Analytics.stepReplay()">Step</button>
|
||||
</div>
|
||||
<div id="analyticsReplayMeta" class="analytics-target-summary">No replay loaded</div>
|
||||
<div id="analyticsReplayTimeline">
|
||||
<div class="analytics-empty">Select a recording to replay key events</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3 class="section-header collapsible" onclick="toggleSection(this)">
|
||||
<span>Export Data</span>
|
||||
<span class="collapse-icon">▼</span>
|
||||
</h3>
|
||||
<div class="section-content">
|
||||
<div class="export-controls">
|
||||
<select id="exportMode">
|
||||
<option value="adsb">ADS-B</option>
|
||||
<option value="ais">AIS</option>
|
||||
<option value="wifi">WiFi</option>
|
||||
<option value="bluetooth">Bluetooth</option>
|
||||
<option value="dsc">DSC</option>
|
||||
</select>
|
||||
<select id="exportFormat">
|
||||
<option value="json">JSON</option>
|
||||
<option value="csv">CSV</option>
|
||||
</select>
|
||||
<button onclick="Analytics.exportData()">Export</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
115
templates/partials/modes/fingerprint.html
Normal file
115
templates/partials/modes/fingerprint.html
Normal file
@@ -0,0 +1,115 @@
|
||||
<!-- FINGERPRINT MODE -->
|
||||
<div id="fingerprintMode" class="mode-content">
|
||||
|
||||
<!-- Intro -->
|
||||
<div class="section">
|
||||
<div style="font-size:11px; color:var(--text-dim); line-height:1.6;">
|
||||
RF Fingerprinting captures the baseline radio environment at a location.
|
||||
Record a baseline when the environment is "clean", then compare later to
|
||||
detect new transmitters, surveillance devices, or signal anomalies.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Workflow tab selector -->
|
||||
<div class="section">
|
||||
<h3>Workflow</h3>
|
||||
<div style="display:flex; gap:4px;">
|
||||
<button class="fp-tab-btn active" id="fpTabRecord" onclick="Fingerprint.showTab('record')">
|
||||
1 — Record
|
||||
</button>
|
||||
<button class="fp-tab-btn" id="fpTabCompare" onclick="Fingerprint.showTab('compare')">
|
||||
2 — Compare
|
||||
</button>
|
||||
</div>
|
||||
<div id="fpTabHint" style="margin-top:8px; font-size:11px; color:var(--text-dim); line-height:1.5;">
|
||||
Record a <strong style="color:var(--text-secondary);">baseline</strong> in a known-clean RF environment, then use <strong style="color:var(--text-secondary);">Compare</strong> later to detect new or anomalous signals.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Record tab -->
|
||||
<div id="fpRecordPanel">
|
||||
<div class="section">
|
||||
<h3>Step 1 — Select Device</h3>
|
||||
<div class="form-group">
|
||||
<label>SDR Device</label>
|
||||
<select id="fpDevice">
|
||||
<option value="">Loading…</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>Step 2 — Scanner Status</h3>
|
||||
<div style="display:flex; align-items:center; gap:8px; padding:6px 0;">
|
||||
<span id="fpScannerDot" style="width:8px; height:8px; border-radius:50%; background:rgba(255,255,255,0.2); flex-shrink:0;"></span>
|
||||
<span id="fpScannerStatusText" style="font-size:11px; color:var(--text-secondary); flex:1;">Checking…</span>
|
||||
</div>
|
||||
<div style="display:flex; gap:6px;">
|
||||
<button class="run-btn" id="fpScannerStartBtn" onclick="Fingerprint.startScanner()" style="flex:1;">Start Scanner</button>
|
||||
<button class="stop-btn" id="fpScannerStopBtn" onclick="Fingerprint.stopScanner()" style="flex:1; display:none;">Stop Scanner</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>Step 3 — Record Baseline</h3>
|
||||
<div class="form-group">
|
||||
<label>Session Name</label>
|
||||
<input type="text" id="fpSessionName" placeholder="e.g. Office — Mon morning">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Location <span style="color:var(--text-dim); font-weight:normal;">(optional)</span></label>
|
||||
<input type="text" id="fpSessionLocation" placeholder="e.g. 3rd floor, room 301">
|
||||
</div>
|
||||
<div style="display:flex; align-items:center; gap:10px; margin:6px 0;">
|
||||
<span style="font-size:10px; color:var(--text-dim); text-transform:uppercase; letter-spacing:.05em;">Observations</span>
|
||||
<span id="fpObsCount" style="font-size:14px; font-family:var(--font-mono); color:var(--accent-cyan, #4aa3ff);">0</span>
|
||||
</div>
|
||||
<div id="fpRecordStatus" style="font-size:11px; color:var(--text-dim); margin-bottom:6px; min-height:14px;"></div>
|
||||
<button class="run-btn" id="fpStartBtn" onclick="Fingerprint.startRecording()">Start Recording</button>
|
||||
<button class="stop-btn" id="fpStopBtn" style="display:none;" onclick="Fingerprint.stopRecording()">Stop & Save</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Compare tab -->
|
||||
<div id="fpComparePanel" style="display:none;">
|
||||
<div class="section">
|
||||
<h3>How It Works</h3>
|
||||
<div style="font-size:11px; color:var(--text-dim); line-height:1.6;">
|
||||
<div style="display:flex; gap:8px; align-items:flex-start; margin-bottom:6px;">
|
||||
<span style="color:var(--accent-cyan); font-weight:700; flex-shrink:0;">1.</span>
|
||||
<span>Ensure the scanner is running (switch to Record tab to start it).</span>
|
||||
</div>
|
||||
<div style="display:flex; gap:8px; align-items:flex-start; margin-bottom:6px;">
|
||||
<span style="color:var(--accent-cyan); font-weight:700; flex-shrink:0;">2.</span>
|
||||
<span>Select a previously recorded baseline below.</span>
|
||||
</div>
|
||||
<div style="display:flex; gap:8px; align-items:flex-start; margin-bottom:6px;">
|
||||
<span style="color:var(--accent-cyan); font-weight:700; flex-shrink:0;">3.</span>
|
||||
<span>Click <strong style="color:var(--text-secondary);">Compare Now</strong> — a 3-second live scan is collected.</span>
|
||||
</div>
|
||||
<div style="display:flex; gap:8px; align-items:flex-start;">
|
||||
<span style="color:var(--accent-cyan); font-weight:700; flex-shrink:0;">4.</span>
|
||||
<span>Anomalies are scored by z-score. <span style="color:#ef4444;">Red = strong deviation</span>, <span style="color:#a855f7;">purple = new signal</span>.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>Baseline</h3>
|
||||
<div class="form-group">
|
||||
<label>Session</label>
|
||||
<select id="fpBaselineSelect">
|
||||
<option value="">No baselines saved yet</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="fpCompareStatus" style="font-size:11px; color:var(--text-dim); margin-bottom:6px; min-height:14px;"></div>
|
||||
<button class="run-btn" onclick="Fingerprint.compareNow()">Compare Now</button>
|
||||
</div>
|
||||
|
||||
<div class="section" id="fpAnomalyList" style="display:none;">
|
||||
<h3>Anomalies</h3>
|
||||
<div id="fpAnomalyItems"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
126
templates/partials/modes/rfheatmap.html
Normal file
126
templates/partials/modes/rfheatmap.html
Normal file
@@ -0,0 +1,126 @@
|
||||
<!-- RF HEATMAP MODE -->
|
||||
<div id="rfheatmapMode" class="mode-content">
|
||||
|
||||
<!-- What is this? -->
|
||||
<div class="section">
|
||||
<h3>RF Heatmap</h3>
|
||||
<div style="background:rgba(74,163,255,0.07); border:1px solid rgba(74,163,255,0.2); border-radius:6px; padding:10px; font-size:11px; color:var(--text-secondary); line-height:1.6;">
|
||||
Walk around with INTERCEPT running. Your GPS position and the current signal strength are saved as a point on the map every few metres. The result is a <strong style="color:var(--accent-cyan);">coverage heatmap</strong> — bright areas have strong signal, dark areas are weak or absent.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 1 — Signal source -->
|
||||
<div class="section">
|
||||
<h3><span style="color:var(--accent-cyan); margin-right:6px;">1</span>What to Map</h3>
|
||||
<div class="form-group">
|
||||
<label>Signal Source</label>
|
||||
<select id="rfhmSource" onchange="RFHeatmap.setSource(this.value); RFHeatmap.onSourceChange()">
|
||||
<option value="wifi">WiFi — RSSI of nearby networks</option>
|
||||
<option value="bluetooth">Bluetooth — RSSI of nearby devices</option>
|
||||
<option value="scanner">SDR Scanner — broadband RF power</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<!-- SDR device picker — only shown for Scanner source -->
|
||||
<div id="rfhmDeviceGroup" style="display:none;">
|
||||
<div class="form-group">
|
||||
<label>SDR Device</label>
|
||||
<select id="rfhmDevice">
|
||||
<option value="">Loading…</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="rfhmSourceHint" style="font-size:11px; color:var(--text-dim); margin-top:4px; line-height:1.5;">
|
||||
Walk near WiFi access points — their signal strength at each location is recorded.
|
||||
</div>
|
||||
|
||||
<!-- Source running status + inline start/stop -->
|
||||
<div id="rfhmSourceStatusRow" style="margin-top:10px; padding:8px 10px; background:rgba(0,0,0,0.3); border-radius:6px;">
|
||||
<div style="display:flex; align-items:center; gap:7px; margin-bottom:6px;">
|
||||
<span id="rfhmSourceDot" style="width:7px; height:7px; border-radius:50%; background:rgba(255,255,255,0.2); flex-shrink:0;"></span>
|
||||
<span id="rfhmSourceStatusText" style="font-size:11px; color:var(--text-dim);">Checking…</span>
|
||||
</div>
|
||||
<button id="rfhmSourceStartBtn" class="run-btn" style="padding:6px; font-size:11px;" onclick="RFHeatmap.startSource()">Start Scanner</button>
|
||||
<button id="rfhmSourceStopBtn" class="stop-btn" style="display:none; padding:6px; font-size:11px;" onclick="RFHeatmap.stopSource()">Stop Scanner</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 2 — Location -->
|
||||
<div class="section">
|
||||
<h3><span style="color:var(--accent-cyan); margin-right:6px;">2</span>Your Location</h3>
|
||||
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; padding:6px 0; border-bottom:1px solid rgba(255,255,255,0.06);">
|
||||
<span style="font-size:10px; color:var(--text-muted); text-transform:uppercase; letter-spacing:.05em;">GPS</span>
|
||||
<span id="rfhmGpsPill" style="font-family:var(--font-mono); font-size:11px; color:var(--text-dim);">No Fix</span>
|
||||
</div>
|
||||
|
||||
<div style="margin-top:8px;">
|
||||
<div style="font-size:10px; color:var(--text-muted); margin-bottom:6px; line-height:1.5;">
|
||||
No GPS? Enter a fixed location to map signals from a stationary point.
|
||||
</div>
|
||||
<div style="display:flex; gap:6px;">
|
||||
<div class="form-group" style="flex:1; margin-bottom:0;">
|
||||
<label>Latitude</label>
|
||||
<input type="number" id="rfhmManualLat" step="0.0001" placeholder="37.7749" oninput="RFHeatmap.setManualCoords()">
|
||||
</div>
|
||||
<div class="form-group" style="flex:1; margin-bottom:0;">
|
||||
<label>Longitude</label>
|
||||
<input type="number" id="rfhmManualLon" step="0.0001" placeholder="-122.4194" oninput="RFHeatmap.setManualCoords()">
|
||||
</div>
|
||||
</div>
|
||||
<button class="preset-btn" onclick="RFHeatmap.useObserverLocation()" style="font-size:10px; margin-top:5px;">
|
||||
Use Saved Observer Location
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 3 — Verify live signal -->
|
||||
<div class="section">
|
||||
<h3><span style="color:var(--accent-cyan); margin-right:6px;">3</span>Live Signal</h3>
|
||||
<div style="background:rgba(0,0,0,0.3); border-radius:6px; padding:10px;">
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:8px;">
|
||||
<span style="font-size:10px; color:var(--text-muted); text-transform:uppercase; letter-spacing:.05em;">Current</span>
|
||||
<span id="rfhmLiveSignal" style="font-family:var(--font-mono); font-size:16px; color:var(--text-dim);">— dBm</span>
|
||||
</div>
|
||||
<!-- Signal strength bar -->
|
||||
<div style="height:4px; background:rgba(255,255,255,0.08); border-radius:2px; overflow:hidden;">
|
||||
<div id="rfhmSignalBar" style="height:100%; width:0%; background:var(--accent-cyan); border-radius:2px; transition:width 0.3s ease;"></div>
|
||||
</div>
|
||||
<div id="rfhmSignalStatus" style="font-size:10px; color:var(--text-dim); margin-top:5px;">Waiting for signal data…</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Step 4 — Record -->
|
||||
<div class="section">
|
||||
<h3><span style="color:var(--accent-cyan); margin-right:6px;">4</span>Record</h3>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Sample Every</label>
|
||||
<div style="display:flex; align-items:center; gap:8px;">
|
||||
<input type="range" id="rfhmMinDist" min="1" max="50" value="5" step="1" style="flex:1;"
|
||||
oninput="document.getElementById('rfhmMinDistVal').textContent=this.value+'m'; RFHeatmap.setMinDist(parseInt(this.value))">
|
||||
<span id="rfhmMinDistVal" style="font-family:var(--font-mono); font-size:11px; color:var(--accent-cyan); min-width:28px; text-align:right;">5m</span>
|
||||
</div>
|
||||
<div style="font-size:10px; color:var(--text-dim); margin-top:3px;">A new point is added after you move this distance.</div>
|
||||
</div>
|
||||
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; padding:6px 0; margin-bottom:4px; border-top:1px solid rgba(255,255,255,0.06);">
|
||||
<span style="font-size:10px; color:var(--text-muted); text-transform:uppercase; letter-spacing:.05em;">Points Captured</span>
|
||||
<span id="rfhmPointCount" style="font-family:var(--font-mono); font-size:14px; color:var(--accent-cyan);">0</span>
|
||||
</div>
|
||||
|
||||
<button class="run-btn" id="rfhmRecordBtn" onclick="RFHeatmap.startRecording()">Start Recording</button>
|
||||
<button class="stop-btn" id="rfhmStopBtn" style="display:none;" onclick="RFHeatmap.stopRecording()">Stop Recording</button>
|
||||
</div>
|
||||
|
||||
<!-- Map actions -->
|
||||
<div class="section">
|
||||
<h3>Map</h3>
|
||||
<div style="display:flex; gap:6px;">
|
||||
<button class="preset-btn" style="flex:1;" onclick="RFHeatmap.clearPoints()">Clear</button>
|
||||
<button class="preset-btn" style="flex:1;" onclick="RFHeatmap.exportGeoJSON()">Export GeoJSON</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
142
templates/partials/modes/waterfall.html
Normal file
142
templates/partials/modes/waterfall.html
Normal file
@@ -0,0 +1,142 @@
|
||||
<!-- WATERFALL MODE -->
|
||||
<div id="waterfallMode" class="mode-content">
|
||||
<div class="section">
|
||||
<h3>Spectrum Waterfall</h3>
|
||||
<div style="font-size:11px; color:var(--text-secondary); line-height:1.45;">
|
||||
Click spectrum or waterfall to tune. Scroll to step-tune. Ctrl/Cmd + scroll to zoom span.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>Device</h3>
|
||||
<div class="form-group">
|
||||
<label>SDR Device</label>
|
||||
<select id="wfDevice" onchange="Waterfall && Waterfall.onDeviceChange && Waterfall.onDeviceChange()">
|
||||
<option value="">Loading devices...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="wfDeviceInfo" style="display:none; background:rgba(0,0,0,0.32); border:1px solid rgba(74,163,255,0.22); border-radius:6px; padding:8px; margin-top:6px; font-size:11px;">
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:4px;">
|
||||
<span style="color:var(--text-muted); text-transform:uppercase; font-size:10px; letter-spacing:.05em;">Type</span>
|
||||
<span id="wfDeviceType" style="color:var(--accent-cyan); font-family:var(--font-mono);">--</span>
|
||||
</div>
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:4px;">
|
||||
<span style="color:var(--text-muted); text-transform:uppercase; font-size:10px; letter-spacing:.05em;">Range</span>
|
||||
<span id="wfDeviceRange" style="color:var(--text-secondary); font-family:var(--font-mono);">--</span>
|
||||
</div>
|
||||
<div style="display:flex; justify-content:space-between; align-items:center;">
|
||||
<span style="color:var(--text-muted); text-transform:uppercase; font-size:10px; letter-spacing:.05em;">Capture SR</span>
|
||||
<span id="wfDeviceBw" style="color:var(--text-secondary); font-family:var(--font-mono);">--</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>Tuning</h3>
|
||||
<div class="form-group">
|
||||
<label>Center Frequency (MHz)</label>
|
||||
<input type="number" id="wfCenterFreq" value="100.0000" step="0.001" min="0.001" max="6000">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Span (MHz)</label>
|
||||
<input type="number" id="wfSpanMhz" value="2.4" step="0.1" min="0.05" max="30">
|
||||
</div>
|
||||
<div class="button-group" style="display:grid; grid-template-columns:1fr 1fr; gap:6px;">
|
||||
<button class="preset-btn" onclick="Waterfall.applyPreset('fm')">FM Broadcast</button>
|
||||
<button class="preset-btn" onclick="Waterfall.applyPreset('air')">Airband</button>
|
||||
<button class="preset-btn" onclick="Waterfall.applyPreset('marine')">Marine</button>
|
||||
<button class="preset-btn" onclick="Waterfall.applyPreset('ham2m')">2m Ham</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>Capture</h3>
|
||||
<div class="form-group">
|
||||
<label>Gain <span style="color:var(--text-dim); font-weight:normal;">(dB or AUTO)</span></label>
|
||||
<input type="text" id="wfGain" value="AUTO" placeholder="AUTO or numeric">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>FFT Size</label>
|
||||
<select id="wfFftSize">
|
||||
<option value="256">256</option>
|
||||
<option value="512">512</option>
|
||||
<option value="1024" selected>1024</option>
|
||||
<option value="2048">2048</option>
|
||||
<option value="4096">4096</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Frame Rate</label>
|
||||
<select id="wfFps">
|
||||
<option value="10">10 fps</option>
|
||||
<option value="20" selected>20 fps</option>
|
||||
<option value="30">30 fps</option>
|
||||
<option value="40">40 fps</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>FFT Averaging</label>
|
||||
<select id="wfAvgCount">
|
||||
<option value="1">1 (none)</option>
|
||||
<option value="2">2</option>
|
||||
<option value="4" selected>4</option>
|
||||
<option value="8">8</option>
|
||||
<option value="16">16</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>PPM Correction</label>
|
||||
<input type="number" id="wfPpm" value="0" step="1" min="-200" max="200" placeholder="0">
|
||||
</div>
|
||||
<div class="checkbox-group" style="margin-top:8px;">
|
||||
<label>
|
||||
<input type="checkbox" id="wfBiasT">
|
||||
Bias-T (antenna power)
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>Display</h3>
|
||||
<div class="form-group">
|
||||
<label>Color Palette</label>
|
||||
<select id="wfPalette" onchange="Waterfall.setPalette(this.value)">
|
||||
<option value="turbo" selected>Turbo</option>
|
||||
<option value="plasma">Plasma</option>
|
||||
<option value="inferno">Inferno</option>
|
||||
<option value="viridis">Viridis</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Noise Floor (dB)</label>
|
||||
<input type="number" id="wfDbMin" value="-100" step="5" disabled>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Ceiling (dB)</label>
|
||||
<input type="number" id="wfDbMax" value="-20" step="5" disabled>
|
||||
</div>
|
||||
<div class="checkbox-group" style="margin-top:8px;">
|
||||
<label>
|
||||
<input type="checkbox" id="wfPeakHold" onchange="Waterfall.togglePeakHold(this.checked)">
|
||||
Peak Hold
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" id="wfBandAnnotations" checked onchange="Waterfall.toggleAnnotations(this.checked)">
|
||||
Band Labels
|
||||
</label>
|
||||
<label>
|
||||
<input type="checkbox" id="wfAutoRange" checked onchange="Waterfall.toggleAutoRange(this.checked)">
|
||||
Auto Range
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<button class="run-btn" id="wfStartBtn" onclick="Waterfall.start()">Start Waterfall</button>
|
||||
<button class="stop-btn" id="wfStopBtn" style="display:none;" onclick="Waterfall.stop()">Stop Waterfall</button>
|
||||
<div id="wfStatus" style="margin-top:8px; font-size:11px; color:var(--text-dim);"></div>
|
||||
<div style="margin-top:6px; font-size:10px; color:var(--text-muted);">
|
||||
Tune with click. Use Monitor in the top strip for audio listen.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -67,6 +67,7 @@
|
||||
{{ mode_item('rtlamr', 'Meters', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>') }}
|
||||
{{ mode_item('listening', 'Listening Post', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18"/><path d="M9 21V9"/></svg>') }}
|
||||
{{ mode_item('subghz', 'SubGHz', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12h6l3-9 3 18 3-9h5"/></svg>') }}
|
||||
{{ mode_item('waterfall', 'Waterfall', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12h4l3-8 3 16 3-8h4"/><path d="M2 18h20" opacity="0.4"/><path d="M2 21h20" opacity="0.2"/></svg>') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -133,9 +134,10 @@
|
||||
|
||||
<div class="mode-nav-dropdown-menu">
|
||||
{{ 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('analytics', 'Analytics', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4Z"/></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('rfheatmap', 'RF Heatmap', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/><path d="M2 10h4M18 10h4" opacity="0.4"/></svg>') }}
|
||||
{{ mode_item('fingerprint', 'RF Fingerprint', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M2 12C2 6.5 6.5 2 12 2a10 10 0 0 1 8 4"/><path d="M5 19.5C5.5 18 6 15 6 12c0-.7.12-1.37.34-2"/><path d="M17.29 21.02c.12-.6.43-2.3.5-3.02"/><path d="M12 10a2 2 0 0 0-2 2c0 1.02-.1 2.51-.26 4"/><path d="M8.65 22c.21-.66.45-1.32.57-2"/><path d="M14 13.12c0 2.38 0 6.38-1 8.88"/></svg>') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -177,6 +179,12 @@
|
||||
<button type="button" class="nav-tool-btn" onclick="showSettings()" title="Settings" aria-label="Open settings">
|
||||
<span class="icon"><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="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg></span>
|
||||
</button>
|
||||
<button type="button" class="nav-tool-btn" id="voiceMuteBtn" onclick="window.VoiceAlerts && VoiceAlerts.toggleMute()" title="Toggle voice alerts" aria-label="Toggle voice alerts">
|
||||
<span class="icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/><path d="M19.07 4.93a10 10 0 0 1 0 14.14"/><path d="M15.54 8.46a5 5 0 0 1 0 7.07"/></svg></span>
|
||||
</button>
|
||||
<button type="button" class="nav-tool-btn" onclick="window.KeyboardShortcuts && KeyboardShortcuts.showHelp()" title="Keyboard shortcuts (Alt+K)" aria-label="Keyboard shortcuts">
|
||||
<span class="icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M6 8h.01M10 8h.01M14 8h.01M18 8h.01M8 12h.01M12 12h.01M16 12h.01M7 16h10"/></svg></span>
|
||||
</button>
|
||||
<button type="button" class="nav-tool-btn" onclick="showHelp()" title="Help & Documentation" aria-label="Open help">?</button>
|
||||
<button type="button" class="nav-tool-btn" onclick="logout(event)" title="Logout" aria-label="Logout">
|
||||
<span class="power-icon icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg></span>
|
||||
@@ -215,9 +223,12 @@
|
||||
{{ mobile_item('meshtastic', 'Mesh', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="3"/><path d="M12 2v4m0 12v4M2 12h4m12 0h4"/></svg>') }}
|
||||
{# Intel #}
|
||||
{{ mobile_item('tscm', 'TSCM', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>') }}
|
||||
{{ mobile_item('analytics', 'Analytics', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/><path d="M18 12a2 2 0 0 0 0 4h4v-4Z"/></svg>') }}
|
||||
{{ mobile_item('spystations', 'Spy', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4.9 19.1C1 15.2 1 8.8 4.9 4.9"/><circle cx="12" cy="12" r="2"/><path d="M19.1 4.9C23 8.8 23 15.1 19.1 19"/></svg>') }}
|
||||
{{ mobile_item('websdr', 'WebSDR', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><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>') }}
|
||||
{# New modes #}
|
||||
{{ mobile_item('waterfall', 'Waterfall', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M2 12h4l3-8 3 16 3-8h4"/></svg>') }}
|
||||
{{ mobile_item('rfheatmap', 'RF Map', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/><circle cx="12" cy="10" r="3"/></svg>') }}
|
||||
{{ mobile_item('fingerprint', 'Fprint', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M2 12C2 6.5 6.5 2 12 2a10 10 0 0 1 8 4"/><path d="M14 13.12c0 2.38 0 6.38-1 8.88"/></svg>') }}
|
||||
</nav>
|
||||
|
||||
{# JavaScript stub for pages that don't have switchMode defined #}
|
||||
|
||||
@@ -284,6 +284,93 @@
|
||||
|
||||
<!-- Alerts Section -->
|
||||
<div id="settings-alerts" class="settings-section">
|
||||
<div class="settings-group">
|
||||
<div class="settings-group-title">Voice Alerts</div>
|
||||
<p style="color: var(--text-dim); margin-bottom: 10px; font-size: 12px;">
|
||||
Configure which events trigger spoken alerts and adjust voice settings.
|
||||
</p>
|
||||
|
||||
<div class="settings-row">
|
||||
<div class="settings-label">
|
||||
<span class="settings-label-text">Pager Messages</span>
|
||||
<span class="settings-label-desc">Speak decoded pager messages</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="voiceCfgPager" checked onchange="saveVoiceAlertConfig()">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="settings-row">
|
||||
<div class="settings-label">
|
||||
<span class="settings-label-text">TSCM Alerts</span>
|
||||
<span class="settings-label-desc">Speak counter-surveillance detections</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="voiceCfgTscm" checked onchange="saveVoiceAlertConfig()">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="settings-row">
|
||||
<div class="settings-label">
|
||||
<span class="settings-label-text">Tracker Detection</span>
|
||||
<span class="settings-label-desc">Speak when AirTag, Tile, or SmartTag found</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="voiceCfgTracker" checked onchange="saveVoiceAlertConfig()">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="settings-row">
|
||||
<div class="settings-label">
|
||||
<span class="settings-label-text">Emergency Squawks</span>
|
||||
<span class="settings-label-desc">Speak aircraft emergency transponder codes</span>
|
||||
</div>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="voiceCfgSquawk" checked onchange="saveVoiceAlertConfig()">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="settings-row">
|
||||
<div class="settings-label">
|
||||
<span class="settings-label-text">Voice</span>
|
||||
<span class="settings-label-desc">Speech synthesis voice</span>
|
||||
</div>
|
||||
<select id="voiceCfgVoice" class="settings-select" style="width: 200px;" onchange="saveVoiceAlertConfig()">
|
||||
<option value="">Default</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="settings-row">
|
||||
<div class="settings-label">
|
||||
<span class="settings-label-text">Rate</span>
|
||||
<span class="settings-label-desc">Speech speed (0.5 – 2.0)</span>
|
||||
</div>
|
||||
<div style="display:flex; align-items:center; gap:8px; width:200px;">
|
||||
<input type="range" id="voiceCfgRate" min="0.5" max="2.0" step="0.1" value="1.1" style="flex:1;" oninput="document.getElementById('voiceCfgRateVal').textContent=this.value; saveVoiceAlertConfig();">
|
||||
<span id="voiceCfgRateVal" style="font-family:var(--font-mono); font-size:11px; color:var(--text-dim); min-width:28px; text-align:right;">1.1</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-row">
|
||||
<div class="settings-label">
|
||||
<span class="settings-label-text">Pitch</span>
|
||||
<span class="settings-label-desc">Voice pitch (0.5 – 2.0)</span>
|
||||
</div>
|
||||
<div style="display:flex; align-items:center; gap:8px; width:200px;">
|
||||
<input type="range" id="voiceCfgPitch" min="0.5" max="2.0" step="0.1" value="0.9" style="flex:1;" oninput="document.getElementById('voiceCfgPitchVal').textContent=this.value; saveVoiceAlertConfig();">
|
||||
<span id="voiceCfgPitchVal" style="font-family:var(--font-mono); font-size:11px; color:var(--text-dim); min-width:28px; text-align:right;">0.9</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 8px;">
|
||||
<button class="check-assets-btn" onclick="testVoiceAlert()">Test Voice</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="settings-group">
|
||||
<div class="settings-group-title">Alert Feed <span id="alertsFeedCount" style="color: var(--text-dim); font-weight: 500;"></span></div>
|
||||
<div id="alertsFeedList" class="settings-feed">
|
||||
@@ -316,7 +403,6 @@
|
||||
<option value="acars">ACARS</option>
|
||||
<option value="vdl2">VDL2</option>
|
||||
<option value="aprs">APRS</option>
|
||||
<option value="dsc">DSC</option>
|
||||
<option value="meshtastic">Meshtastic</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -392,7 +478,6 @@
|
||||
<option value="bluetooth">Bluetooth</option>
|
||||
<option value="adsb">ADS-B</option>
|
||||
<option value="ais">AIS</option>
|
||||
<option value="dsc">DSC</option>
|
||||
<option value="acars">ACARS</option>
|
||||
<option value="aprs">APRS</option>
|
||||
<option value="rtlamr">RTLAMR</option>
|
||||
|
||||
Reference in New Issue
Block a user