mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 06:40:00 -07:00
chore: commit all changes and remove large IQ captures from tracking
Add .gitignore entry for data/subghz/captures/ to prevent large IQ recording files from being committed. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -63,6 +63,7 @@
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/modes/sstv.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/modes/weather-satellite.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/modes/sstv-general.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/modes/subghz.css') }}?v={{ version }}&r=subghz_layout9">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/settings.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/components/function-strip.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/components/toast.css') }}">
|
||||
@@ -190,9 +191,9 @@
|
||||
<span class="mode-icon 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="10"/><circle cx="12" cy="12" r="3"/><path d="M12 2v4m0 12v4M2 12h4m12 0h4"/></svg></span>
|
||||
<span class="mode-name">Meshtastic</span>
|
||||
</button>
|
||||
<button class="mode-card mode-card-sm" onclick="selectMode('dmr')">
|
||||
<span class="mode-icon icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="22"/></svg></span>
|
||||
<span class="mode-name">Digital Voice</span>
|
||||
<button class="mode-card mode-card-sm" onclick="selectMode('subghz')">
|
||||
<span class="mode-icon icon"><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></span>
|
||||
<span class="mode-name">SubGHz</span>
|
||||
</button>
|
||||
<button class="mode-card mode-card-sm" onclick="selectMode('websdr')">
|
||||
<span class="mode-icon 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="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></span>
|
||||
@@ -387,6 +388,10 @@
|
||||
<div class="container">
|
||||
<div class="main-content">
|
||||
<div class="sidebar mobile-drawer" id="mainSidebar">
|
||||
<button class="sidebar-collapse-btn" id="sidebarCollapseBtn" onclick="toggleMainSidebarCollapse()" title="Collapse sidebar">
|
||||
<span class="icon icon--sm"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="15 18 9 12 15 6"/></svg></span>
|
||||
Collapse Sidebar
|
||||
</button>
|
||||
<!-- Agent Selector -->
|
||||
<div class="section" id="agentSection">
|
||||
<h3>Signal Source</h3>
|
||||
@@ -547,6 +552,8 @@
|
||||
|
||||
{% include 'partials/modes/websdr.html' %}
|
||||
|
||||
{% include 'partials/modes/subghz.html' %}
|
||||
|
||||
<button class="preset-btn" onclick="killAll()"
|
||||
style="width: 100%; margin-top: 10px; border-color: #ff3366; color: #ff3366;">
|
||||
Kill All Processes
|
||||
@@ -554,6 +561,9 @@
|
||||
</div>
|
||||
|
||||
<div class="output-panel">
|
||||
<button class="sidebar-expand-handle" id="sidebarExpandHandle" onclick="toggleMainSidebarCollapse()" title="Expand sidebar">
|
||||
<span class="icon icon--sm"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="9 18 15 12 9 6"/></svg></span>
|
||||
</button>
|
||||
<div class="output-header">
|
||||
<h3 id="outputTitle">Pager Decoder</h3>
|
||||
<div class="header-controls">
|
||||
@@ -1759,6 +1769,301 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SubGHz Transceiver Dashboard -->
|
||||
<div id="subghzVisuals" class="subghz-visuals-container" style="display: none;">
|
||||
|
||||
<!-- Stats Strip -->
|
||||
<div class="subghz-stats-strip">
|
||||
<div class="subghz-strip-group">
|
||||
<span class="subghz-strip-device-badge" id="subghzStripDevice">
|
||||
<span class="subghz-strip-device-dot" id="subghzStripDeviceDot"></span>
|
||||
HackRF
|
||||
</span>
|
||||
<div class="subghz-strip-status">
|
||||
<span class="subghz-strip-dot" id="subghzStripDot"></span>
|
||||
<span class="subghz-strip-status-text" id="subghzStripStatus">Idle</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="subghz-strip-divider"></div>
|
||||
<div class="subghz-strip-group">
|
||||
<div class="subghz-strip-stat">
|
||||
<span class="subghz-strip-value accent-cyan" id="subghzStripFreq">--</span>
|
||||
<span class="subghz-strip-label">MHZ</span>
|
||||
</div>
|
||||
<div class="subghz-strip-stat">
|
||||
<span class="subghz-strip-value" id="subghzStripMode">--</span>
|
||||
<span class="subghz-strip-label">MODE</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="subghz-strip-divider"></div>
|
||||
<div class="subghz-strip-group">
|
||||
<div class="subghz-strip-stat">
|
||||
<span class="subghz-strip-value accent-green" id="subghzStripSignals">0</span>
|
||||
<span class="subghz-strip-label">SIGNALS</span>
|
||||
</div>
|
||||
<div class="subghz-strip-stat">
|
||||
<span class="subghz-strip-value accent-orange" id="subghzStripCaptures">0</span>
|
||||
<span class="subghz-strip-label">CAPTURES</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="subghz-strip-divider"></div>
|
||||
<div class="subghz-strip-group">
|
||||
<span class="subghz-strip-timer" id="subghzStripTimer"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Signal Console (collapsible) -->
|
||||
<div class="subghz-signal-console" id="subghzConsole" style="display: none;">
|
||||
<div class="subghz-console-header" onclick="SubGhz.toggleConsole()">
|
||||
<div class="subghz-phase-strip">
|
||||
<span class="subghz-phase-step" id="subghzPhaseTuning">TUNING</span>
|
||||
<span class="subghz-phase-arrow">▸</span>
|
||||
<span class="subghz-phase-step" id="subghzPhaseListening">LISTENING</span>
|
||||
<span class="subghz-phase-arrow">▸</span>
|
||||
<span class="subghz-phase-step" id="subghzPhaseDecoding">DECODING</span>
|
||||
</div>
|
||||
<div class="subghz-burst-indicator" id="subghzBurstIndicator" title="Live burst detector">
|
||||
<span class="subghz-burst-dot"></span>
|
||||
<span class="subghz-burst-text" id="subghzBurstText">NO BURST</span>
|
||||
</div>
|
||||
<button class="subghz-console-toggle" id="subghzConsoleToggleBtn">▼</button>
|
||||
</div>
|
||||
<div class="subghz-console-body" id="subghzConsoleBody">
|
||||
<div class="subghz-console-log" id="subghzConsoleLog"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Action Hub (idle state — 2x2 Flipper-style cards) -->
|
||||
<div class="subghz-action-hub" id="subghzActionHub">
|
||||
<div class="subghz-hub-header">
|
||||
<div class="subghz-hub-header-title">HackRF One</div>
|
||||
<div class="subghz-hub-header-sub">SubGHz Transceiver — 1 MHz - 6 GHz</div>
|
||||
</div>
|
||||
<div class="subghz-hub-grid">
|
||||
<div class="subghz-hub-card subghz-hub-card--green" onclick="SubGhz.hubAction('rx')">
|
||||
<div class="subghz-hub-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" width="32" height="32"><circle cx="12" cy="12" r="3"/><path d="M12 1v4m0 14v4M1 12h4m14 0h4"/><path d="M5.6 5.6l2.85 2.85m7.1 7.1l2.85 2.85M5.6 18.4l2.85-2.85m7.1-7.1l2.85-2.85"/></svg>
|
||||
</div>
|
||||
<div class="subghz-hub-title">Read RAW</div>
|
||||
<div class="subghz-hub-desc">Capture raw IQ via hackrf_transfer</div>
|
||||
</div>
|
||||
<div class="subghz-hub-card subghz-hub-card--red" onclick="SubGhz.hubAction('txselect')">
|
||||
<div class="subghz-hub-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" width="32" height="32"><path d="M5 19h14"/><path d="M12 5v11"/><path d="M8 9l4-4 4 4"/></svg>
|
||||
</div>
|
||||
<div class="subghz-hub-title">Transmit</div>
|
||||
<div class="subghz-hub-desc">Replay a saved capture</div>
|
||||
</div>
|
||||
<div class="subghz-hub-card subghz-hub-card--orange" onclick="SubGhz.hubAction('sweep')">
|
||||
<div class="subghz-hub-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" width="32" height="32"><path d="M3 20h18"/><path d="M3 17l3-7 3 4 3-9 3 6 3-3 3 9"/></svg>
|
||||
</div>
|
||||
<div class="subghz-hub-title">Freq Analyzer</div>
|
||||
<div class="subghz-hub-desc">Wideband sweep via hackrf_sweep</div>
|
||||
</div>
|
||||
<div class="subghz-hub-card subghz-hub-card--purple" onclick="SubGhz.hubAction('saved')">
|
||||
<div class="subghz-hub-icon">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" width="32" height="32"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
|
||||
</div>
|
||||
<div class="subghz-hub-title">Saved</div>
|
||||
<div class="subghz-hub-desc">Signal library & replay</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Operation Panels (one visible at a time, replaces hub) -->
|
||||
|
||||
<!-- RX (Raw Capture) Panel -->
|
||||
<div class="subghz-op-panel" id="subghzPanelRx" style="display: none;">
|
||||
<div class="subghz-op-panel-header">
|
||||
<button class="subghz-op-back-btn" onclick="SubGhz.backToHub()" title="Back to hub">◀ Back</button>
|
||||
<span class="subghz-op-panel-title">Read RAW — Signal Capture</span>
|
||||
<div class="subghz-op-panel-actions">
|
||||
<button class="subghz-btn start" id="subghzRxStartBtnPanel" onclick="SubGhz.startRx()">Start</button>
|
||||
<button class="subghz-btn stop" id="subghzRxStopBtnPanel" onclick="SubGhz.stopRx()" disabled>Stop</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="subghz-rx-display">
|
||||
<div class="subghz-rx-recording" id="subghzRxRecording" style="display: none;">
|
||||
<span class="subghz-rx-rec-dot"></span>
|
||||
<span>RECORDING</span>
|
||||
</div>
|
||||
<div class="subghz-rx-info-grid">
|
||||
<div class="subghz-rx-info-item">
|
||||
<span class="subghz-rx-info-label">FREQUENCY</span>
|
||||
<span class="subghz-rx-info-value accent-cyan" id="subghzRxFreq">--</span>
|
||||
</div>
|
||||
<div class="subghz-rx-info-item">
|
||||
<span class="subghz-rx-info-label">LNA GAIN</span>
|
||||
<span class="subghz-rx-info-value" id="subghzRxLna">--</span>
|
||||
</div>
|
||||
<div class="subghz-rx-info-item">
|
||||
<span class="subghz-rx-info-label">VGA GAIN</span>
|
||||
<span class="subghz-rx-info-value" id="subghzRxVga">--</span>
|
||||
</div>
|
||||
<div class="subghz-rx-info-item">
|
||||
<span class="subghz-rx-info-label">SAMPLE RATE</span>
|
||||
<span class="subghz-rx-info-value" id="subghzRxSampleRate">--</span>
|
||||
</div>
|
||||
<div class="subghz-rx-info-item">
|
||||
<span class="subghz-rx-info-label">FILE SIZE</span>
|
||||
<span class="subghz-rx-info-value" id="subghzRxFileSize">0 KB</span>
|
||||
</div>
|
||||
<div class="subghz-rx-info-item">
|
||||
<span class="subghz-rx-info-label">DATA RATE</span>
|
||||
<span class="subghz-rx-info-value" id="subghzRxRate">0 KB/s</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="subghz-rx-level-wrapper">
|
||||
<span class="subghz-rx-level-label">SIGNAL</span>
|
||||
<span class="subghz-rx-burst-pill" id="subghzRxBurstPill">IDLE</span>
|
||||
<div class="subghz-rx-level-bar">
|
||||
<div class="subghz-rx-level-fill" id="subghzRxLevel" style="width: 0%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="subghz-rx-hint" id="subghzRxHint">
|
||||
<span class="subghz-rx-hint-label">ANALYSIS</span>
|
||||
<span class="subghz-rx-hint-text" id="subghzRxHintText">No modulation hint yet</span>
|
||||
<span class="subghz-rx-hint-confidence" id="subghzRxHintConfidence">--</span>
|
||||
</div>
|
||||
<div class="subghz-rx-scope-wrap">
|
||||
<span class="subghz-rx-scope-label">WAVEFORM</span>
|
||||
<div class="subghz-rx-scope">
|
||||
<canvas id="subghzRxScope"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="subghz-rx-scope-wrap">
|
||||
<div class="subghz-rx-waterfall-header">
|
||||
<span class="subghz-rx-scope-label">WATERFALL</span>
|
||||
<div class="subghz-rx-waterfall-controls">
|
||||
<div class="subghz-wf-control">
|
||||
<span>FLOOR</span>
|
||||
<input type="range" id="subghzWfFloor" min="0" max="200" value="20" oninput="SubGhz.setWaterfallFloor(this.value)">
|
||||
<span class="subghz-wf-value" id="subghzWfFloorVal">20</span>
|
||||
</div>
|
||||
<div class="subghz-wf-control">
|
||||
<span>RANGE</span>
|
||||
<input type="range" id="subghzWfRange" min="16" max="255" value="180" oninput="SubGhz.setWaterfallRange(this.value)">
|
||||
<span class="subghz-wf-value" id="subghzWfRangeVal">180</span>
|
||||
</div>
|
||||
<button class="subghz-wf-pause-btn" id="subghzWfPauseBtn" onclick="SubGhz.toggleWaterfall()">PAUSE</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="subghz-rx-waterfall">
|
||||
<canvas id="subghzRxWaterfall"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sweep Panel -->
|
||||
<div class="subghz-op-panel" id="subghzPanelSweep" style="display: none;">
|
||||
<div class="subghz-op-panel-header">
|
||||
<button class="subghz-op-back-btn" onclick="SubGhz.backToHub()" title="Back to hub">◀ Back</button>
|
||||
<span class="subghz-op-panel-title">Frequency Analyzer</span>
|
||||
<div class="subghz-op-panel-actions">
|
||||
<button class="subghz-btn start" id="subghzSweepStartBtnPanel" onclick="SubGhz.startSweep()">Start</button>
|
||||
<button class="subghz-btn stop" id="subghzSweepStopBtnPanel" onclick="SubGhz.stopSweep()" disabled>Stop</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="subghz-sweep-layout">
|
||||
<div class="subghz-sweep-chart-wrapper" id="subghzSweepChartWrapper">
|
||||
<canvas id="subghzSweepCanvas"></canvas>
|
||||
</div>
|
||||
<div class="subghz-sweep-peaks-sidebar" id="subghzSweepPeaksSidebar">
|
||||
<div class="subghz-sweep-peaks-title">PEAKS</div>
|
||||
<div class="subghz-peak-list" id="subghzSweepPeakList"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TX Panel -->
|
||||
<div class="subghz-op-panel" id="subghzPanelTx" style="display: none;">
|
||||
<div class="subghz-op-panel-header">
|
||||
<button class="subghz-op-back-btn" onclick="SubGhz.backToHub()" title="Back to hub">◀ Back</button>
|
||||
<span class="subghz-op-panel-title">Transmit</span>
|
||||
</div>
|
||||
<div class="subghz-tx-display" id="subghzTxDisplay">
|
||||
<div class="subghz-tx-pulse-ring">
|
||||
<div class="subghz-tx-pulse-dot"></div>
|
||||
</div>
|
||||
<div class="subghz-tx-label" id="subghzTxStateLabel">READY</div>
|
||||
<div class="subghz-tx-info-grid">
|
||||
<div class="subghz-tx-info-item">
|
||||
<span class="subghz-tx-info-label">FREQUENCY</span>
|
||||
<span class="subghz-tx-info-value accent-red" id="subghzTxFreqDisplay">--</span>
|
||||
</div>
|
||||
<div class="subghz-tx-info-item">
|
||||
<span class="subghz-tx-info-label">TX GAIN</span>
|
||||
<span class="subghz-tx-info-value" id="subghzTxGainDisplay">--</span>
|
||||
</div>
|
||||
<div class="subghz-tx-info-item">
|
||||
<span class="subghz-tx-info-label">ELAPSED</span>
|
||||
<span class="subghz-tx-info-value" id="subghzTxElapsed">0s</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="subghz-btn-row" style="max-width: 420px; margin: 16px auto 0;">
|
||||
<button class="subghz-btn" id="subghzTxChooseCaptureBtn" onclick="SubGhz.showPanel('saved')">Choose Capture</button>
|
||||
<button class="subghz-btn stop" id="subghzTxStopBtn" onclick="SubGhz.stopTx()">Stop Transmission</button>
|
||||
<button class="subghz-btn start" id="subghzTxReplayLastBtn" onclick="SubGhz.replayLastTx()" style="display: none;">Replay Last</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Saved Panel -->
|
||||
<div class="subghz-op-panel" id="subghzPanelSaved" style="display: none;">
|
||||
<div class="subghz-op-panel-header">
|
||||
<button class="subghz-op-back-btn" onclick="SubGhz.backToHub()" title="Back to hub">◀ Back</button>
|
||||
<span class="subghz-op-panel-title">Saved Captures</span>
|
||||
<div class="subghz-op-panel-actions subghz-saved-actions">
|
||||
<span class="subghz-saved-selection-count" id="subghzSavedSelectionCount" style="display: none;">0 selected</span>
|
||||
<button class="subghz-btn" id="subghzSavedSelectBtn" onclick="SubGhz.toggleCaptureSelectMode()">Select</button>
|
||||
<button class="subghz-btn" id="subghzSavedSelectAllBtn" onclick="SubGhz.selectAllCaptures()" style="display: none;">Select All</button>
|
||||
<button class="subghz-btn stop" id="subghzSavedDeleteSelectedBtn" onclick="SubGhz.deleteSelectedCaptures()" style="display: none;" disabled>Delete Selected</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="subghz-captures-list subghz-captures-list-main" id="subghzCapturesList" style="flex: 1; min-height: 0; max-height: none; overflow-y: auto;">
|
||||
<div class="subghz-empty" id="subghzCapturesEmpty">No captures yet</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TX Confirmation Modal -->
|
||||
<div id="subghzTxModalOverlay" class="subghz-tx-modal-overlay">
|
||||
<div class="subghz-tx-modal">
|
||||
<h3>Confirm Transmission</h3>
|
||||
<p>You are about to transmit a radio signal on:</p>
|
||||
<p class="tx-freq" id="subghzTxModalFreq">--- MHz</p>
|
||||
<p class="tx-duration">Capture duration: <span id="subghzTxModalDuration">--</span></p>
|
||||
<div class="subghz-tx-segment-box">
|
||||
<label class="subghz-tx-segment-toggle">
|
||||
<input type="checkbox" id="subghzTxSegmentEnabled" onchange="SubGhz.syncTxSegmentSelection()">
|
||||
Transmit selected segment only
|
||||
</label>
|
||||
<div class="subghz-tx-segment-grid">
|
||||
<label>Start (s)</label>
|
||||
<input type="number" id="subghzTxSegmentStart" min="0" step="0.01" value="0" disabled oninput="SubGhz.syncTxSegmentSelection('start')">
|
||||
<label>End (s)</label>
|
||||
<input type="number" id="subghzTxSegmentEnd" min="0" step="0.01" value="0" disabled oninput="SubGhz.syncTxSegmentSelection('end')">
|
||||
</div>
|
||||
<p class="subghz-tx-segment-summary" id="subghzTxSegmentSummary">Full capture</p>
|
||||
</div>
|
||||
<div class="subghz-tx-burst-assist" id="subghzTxBurstAssist" style="display: none;">
|
||||
<div class="subghz-tx-burst-title">Detected Bursts</div>
|
||||
<div class="subghz-tx-burst-timeline" id="subghzTxBurstTimeline"></div>
|
||||
<div class="subghz-tx-burst-range" id="subghzTxBurstRange">Drag on timeline to select TX segment</div>
|
||||
<div class="subghz-tx-burst-list" id="subghzTxBurstList"></div>
|
||||
</div>
|
||||
<p>Ensure you have proper authorization to transmit on this frequency.</p>
|
||||
<div class="subghz-tx-modal-actions">
|
||||
<button class="subghz-tx-cancel-btn" onclick="SubGhz.cancelTx()">Cancel</button>
|
||||
<button class="subghz-tx-trim-btn" id="subghzTxTrimBtn" onclick="SubGhz.trimCaptureSelection()">Trim + Save</button>
|
||||
<button class="subghz-tx-confirm-btn" onclick="SubGhz.confirmTx()">Transmit</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- WebSDR Dashboard -->
|
||||
<div id="websdrVisuals" style="display: none; padding: 12px; flex-direction: column; gap: 12px; flex: 1; min-height: 0; overflow: hidden;">
|
||||
<!-- Audio Control Bar (hidden until connected) -->
|
||||
@@ -2538,6 +2843,7 @@
|
||||
<script src="{{ url_for('static', filename='js/modes/sstv-general.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/modes/dmr.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/modes/websdr.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/modes/subghz.js') }}?v={{ version }}&r=subghz_layout9"></script>
|
||||
|
||||
<script>
|
||||
// ============================================
|
||||
@@ -2673,7 +2979,7 @@
|
||||
const validModes = new Set([
|
||||
'pager', 'sensor', 'rtlamr', 'aprs', 'listening',
|
||||
'spystations', 'meshtastic', 'wifi', 'bluetooth',
|
||||
'tscm', 'satellite', 'sstv', 'weathersat', 'sstv_general', 'dmr', 'websdr'
|
||||
'tscm', 'satellite', 'sstv', 'weathersat', 'sstv_general', 'websdr', 'subghz'
|
||||
]);
|
||||
|
||||
function getModeFromQuery() {
|
||||
@@ -3006,6 +3312,41 @@
|
||||
return parsed;
|
||||
}
|
||||
|
||||
const SIDEBAR_COLLAPSE_KEY = 'mainSidebarCollapsed';
|
||||
|
||||
function setMainSidebarCollapsed(collapsed) {
|
||||
const mainContent = document.querySelector('.main-content');
|
||||
const collapseBtn = document.getElementById('sidebarCollapseBtn');
|
||||
if (!mainContent) return;
|
||||
|
||||
mainContent.classList.toggle('sidebar-collapsed', collapsed);
|
||||
if (collapseBtn) {
|
||||
collapseBtn.setAttribute('aria-expanded', collapsed ? 'false' : 'true');
|
||||
}
|
||||
localStorage.setItem(SIDEBAR_COLLAPSE_KEY, collapsed ? 'true' : 'false');
|
||||
}
|
||||
|
||||
function toggleMainSidebarCollapse(forceState = null) {
|
||||
const mainContent = document.querySelector('.main-content');
|
||||
if (!mainContent || window.innerWidth < 1024) return;
|
||||
const collapsed = mainContent.classList.contains('sidebar-collapsed');
|
||||
const nextState = forceState === null ? !collapsed : !!forceState;
|
||||
setMainSidebarCollapsed(nextState);
|
||||
}
|
||||
|
||||
function applySidebarCollapsePreference() {
|
||||
const mainContent = document.querySelector('.main-content');
|
||||
if (!mainContent) return;
|
||||
if (window.innerWidth < 1024) {
|
||||
mainContent.classList.remove('sidebar-collapsed');
|
||||
return;
|
||||
}
|
||||
const savedCollapsed = localStorage.getItem(SIDEBAR_COLLAPSE_KEY) === 'true';
|
||||
setMainSidebarCollapsed(savedCollapsed);
|
||||
}
|
||||
|
||||
window.addEventListener('resize', applySidebarCollapsePreference);
|
||||
|
||||
// Make sections collapsible
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
document.querySelectorAll('.section h3').forEach(h3 => {
|
||||
@@ -3014,14 +3355,17 @@
|
||||
});
|
||||
});
|
||||
|
||||
// Collapse all sections by default (except Signal Source and SDR Device)
|
||||
document.querySelectorAll('.section').forEach((section, index) => {
|
||||
// Keep first two sections expanded (Signal Source, SDR Device), collapse rest
|
||||
if (index > 1) {
|
||||
// Collapse sidebar menu sections by default, but skip headerless utility blocks.
|
||||
document.querySelectorAll('.sidebar .section').forEach((section) => {
|
||||
if (section.querySelector('h3')) {
|
||||
section.classList.add('collapsed');
|
||||
} else {
|
||||
section.classList.remove('collapsed');
|
||||
}
|
||||
});
|
||||
|
||||
applySidebarCollapsePreference();
|
||||
|
||||
// Load bias-T setting from localStorage
|
||||
loadBiasTSetting();
|
||||
|
||||
@@ -3095,7 +3439,8 @@
|
||||
'tscm': 'security',
|
||||
'rtlamr': 'sdr', 'ais': 'sdr', 'spystations': 'sdr',
|
||||
'meshtastic': 'sdr',
|
||||
'satellite': 'space', 'sstv': 'space', 'weathersat': 'space', 'sstv_general': 'space'
|
||||
'satellite': 'space', 'sstv': 'space', 'weathersat': 'space', 'sstv_general': 'space',
|
||||
'subghz': 'sdr'
|
||||
};
|
||||
|
||||
// Remove has-active from all dropdowns
|
||||
@@ -3141,6 +3486,11 @@
|
||||
if (isTscmRunning) stopTscmSweep();
|
||||
}
|
||||
|
||||
// Clean up SubGHz SSE connection when leaving the mode
|
||||
if (typeof SubGhz !== 'undefined' && currentMode === 'subghz' && mode !== 'subghz') {
|
||||
SubGhz.destroy();
|
||||
}
|
||||
|
||||
currentMode = mode;
|
||||
if (updateUrl) {
|
||||
updateModeUrl(mode);
|
||||
@@ -3190,6 +3540,7 @@
|
||||
document.getElementById('meshtasticMode')?.classList.toggle('active', mode === 'meshtastic');
|
||||
document.getElementById('dmrMode')?.classList.toggle('active', mode === 'dmr');
|
||||
document.getElementById('websdrMode')?.classList.toggle('active', mode === 'websdr');
|
||||
document.getElementById('subghzMode')?.classList.toggle('active', mode === 'subghz');
|
||||
const pagerStats = document.getElementById('pagerStats');
|
||||
const sensorStats = document.getElementById('sensorStats');
|
||||
const satelliteStats = document.getElementById('satelliteStats');
|
||||
@@ -3227,7 +3578,8 @@
|
||||
'spystations': 'SPY STATIONS',
|
||||
'meshtastic': 'MESHTASTIC',
|
||||
'dmr': 'DIGITAL VOICE',
|
||||
'websdr': 'WEBSDR'
|
||||
'websdr': 'WEBSDR',
|
||||
'subghz': 'SUBGHZ'
|
||||
};
|
||||
const activeModeIndicator = document.getElementById('activeModeIndicator');
|
||||
if (activeModeIndicator) activeModeIndicator.innerHTML = '<span class="pulse-dot"></span>' + (modeNames[mode] || mode.toUpperCase());
|
||||
@@ -3244,6 +3596,7 @@
|
||||
const sstvGeneralVisuals = document.getElementById('sstvGeneralVisuals');
|
||||
const dmrVisuals = document.getElementById('dmrVisuals');
|
||||
const websdrVisuals = document.getElementById('websdrVisuals');
|
||||
const subghzVisuals = document.getElementById('subghzVisuals');
|
||||
if (wifiLayoutContainer) wifiLayoutContainer.style.display = mode === 'wifi' ? 'flex' : 'none';
|
||||
if (btLayoutContainer) btLayoutContainer.style.display = mode === 'bluetooth' ? 'flex' : 'none';
|
||||
if (satelliteVisuals) satelliteVisuals.style.display = mode === 'satellite' ? 'block' : 'none';
|
||||
@@ -3257,6 +3610,7 @@
|
||||
if (sstvGeneralVisuals) sstvGeneralVisuals.style.display = mode === 'sstv_general' ? 'flex' : 'none';
|
||||
if (dmrVisuals) dmrVisuals.style.display = mode === 'dmr' ? 'flex' : 'none';
|
||||
if (websdrVisuals) websdrVisuals.style.display = mode === 'websdr' ? 'flex' : 'none';
|
||||
if (subghzVisuals) subghzVisuals.style.display = mode === 'subghz' ? 'flex' : 'none';
|
||||
|
||||
// Hide sidebar by default for Meshtastic mode, show for others
|
||||
const mainContent = document.querySelector('.main-content');
|
||||
@@ -3292,7 +3646,8 @@
|
||||
'spystations': 'Spy Stations',
|
||||
'meshtastic': 'Meshtastic Mesh Monitor',
|
||||
'dmr': 'Digital Voice Decoder',
|
||||
'websdr': 'HF/Shortwave WebSDR'
|
||||
'websdr': 'HF/Shortwave WebSDR',
|
||||
'subghz': 'SubGHz Transceiver'
|
||||
};
|
||||
const outputTitle = document.getElementById('outputTitle');
|
||||
if (outputTitle) outputTitle.textContent = titles[mode] || 'Signal Monitor';
|
||||
@@ -3310,7 +3665,7 @@
|
||||
const reconBtn = document.getElementById('reconBtn');
|
||||
const intelBtn = document.querySelector('[onclick="exportDeviceDB()"]');
|
||||
const reconPanel = document.getElementById('reconPanel');
|
||||
if (mode === 'satellite' || mode === 'sstv' || mode === 'weathersat' || mode === 'sstv_general' || mode === 'listening' || mode === 'aprs' || mode === 'tscm' || mode === 'spystations' || mode === 'meshtastic' || mode === 'dmr' || mode === 'websdr') {
|
||||
if (mode === 'satellite' || mode === 'sstv' || mode === 'weathersat' || mode === 'sstv_general' || mode === 'listening' || mode === 'aprs' || mode === 'tscm' || mode === 'spystations' || mode === 'meshtastic' || mode === 'dmr' || mode === 'websdr' || mode === 'subghz') {
|
||||
if (reconPanel) reconPanel.style.display = 'none';
|
||||
if (reconBtn) reconBtn.style.display = 'none';
|
||||
if (intelBtn) intelBtn.style.display = 'none';
|
||||
@@ -3348,8 +3703,8 @@
|
||||
// Hide output console for modes with their own visualizations
|
||||
const outputEl = document.getElementById('output');
|
||||
const statusBar = document.querySelector('.status-bar');
|
||||
if (outputEl) outputEl.style.display = (mode === 'satellite' || mode === 'sstv' || mode === 'weathersat' || mode === 'sstv_general' || mode === 'aprs' || mode === 'wifi' || mode === 'bluetooth' || mode === 'listening' || mode === 'tscm' || mode === 'spystations' || mode === 'meshtastic' || mode === 'dmr' || mode === 'websdr') ? 'none' : 'block';
|
||||
if (statusBar) statusBar.style.display = (mode === 'satellite' || mode === 'websdr' || mode === 'dmr') ? 'none' : 'flex';
|
||||
if (outputEl) outputEl.style.display = (mode === 'satellite' || mode === 'sstv' || mode === 'weathersat' || mode === 'sstv_general' || mode === 'aprs' || mode === 'wifi' || mode === 'bluetooth' || mode === 'listening' || mode === 'tscm' || mode === 'spystations' || mode === 'meshtastic' || mode === 'dmr' || mode === 'websdr' || mode === 'subghz') ? 'none' : 'block';
|
||||
if (statusBar) statusBar.style.display = (mode === 'satellite' || mode === 'websdr' || mode === 'dmr' || mode === 'subghz') ? 'none' : 'flex';
|
||||
|
||||
// Restore sidebar when leaving Meshtastic mode (user may have collapsed it)
|
||||
if (mode !== 'meshtastic') {
|
||||
@@ -3409,6 +3764,8 @@
|
||||
if (typeof initDmrSynthesizer === 'function') setTimeout(initDmrSynthesizer, 100);
|
||||
} else if (mode === 'websdr') {
|
||||
if (typeof initWebSDR === 'function') initWebSDR();
|
||||
} else if (mode === 'subghz') {
|
||||
SubGhz.init();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4617,6 +4974,11 @@
|
||||
}
|
||||
|
||||
function fetchSdrStatus() {
|
||||
// Avoid probing SDR inventory while HackRF SubGHz mode is active.
|
||||
// Device discovery runs hackrf_info and can disrupt active HackRF streams.
|
||||
if (typeof currentMode !== 'undefined' && currentMode === 'subghz') {
|
||||
return;
|
||||
}
|
||||
fetch('/devices/status')
|
||||
.then(r => r.json())
|
||||
.then(devices => {
|
||||
@@ -9077,6 +9439,7 @@
|
||||
const region = document.getElementById('aprsStripRegion').value;
|
||||
const device = getSelectedDevice();
|
||||
const gain = document.getElementById('aprsStripGain').value;
|
||||
const sdrType = (typeof getSelectedSDRType === 'function') ? getSelectedSDRType() : 'rtlsdr';
|
||||
|
||||
// Check if using agent mode
|
||||
const isAgentMode = typeof currentAgent !== 'undefined' && currentAgent !== 'local';
|
||||
@@ -9086,7 +9449,8 @@
|
||||
const requestBody = {
|
||||
region,
|
||||
device: parseInt(device),
|
||||
gain: parseInt(gain)
|
||||
gain: parseInt(gain),
|
||||
sdr_type: sdrType
|
||||
};
|
||||
|
||||
// Add custom frequency if selected
|
||||
@@ -9431,6 +9795,41 @@
|
||||
updateAprsStationList(packet);
|
||||
}
|
||||
|
||||
function getAprsMarkerCategory(packet) {
|
||||
const symbolCode = (packet.symbol && packet.symbol.length > 1) ? packet.symbol[1] : '';
|
||||
const speed = parseFloat(packet.speed || 0);
|
||||
const vehicleSymbols = new Set(['>', 'k', 'u', 'v', '[', '<', 's', 'b', 'j']);
|
||||
|
||||
if ((Number.isFinite(speed) && speed > 2) || vehicleSymbols.has(symbolCode)) {
|
||||
return 'vehicle';
|
||||
}
|
||||
return 'tower';
|
||||
}
|
||||
|
||||
function getAprsMarkerSvg(category) {
|
||||
if (category === 'vehicle') {
|
||||
return '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 14l2-5a2 2 0 0 1 2-1h10a2 2 0 0 1 2 1l2 5v4h-2a2 2 0 0 1-4 0H9a2 2 0 0 1-4 0H3v-4z"/><circle cx="7" cy="18" r="1.7"/><circle cx="17" cy="18" r="1.7"/></svg>';
|
||||
}
|
||||
return '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3l3 7h-2l1 3h-2l1 8h-2l1-8h-2l1-3H9l3-7z"/><path d="M5 21h14" fill="none" stroke="currentColor" stroke-width="1.5"/></svg>';
|
||||
}
|
||||
|
||||
function buildAprsMarkerIcon(packet) {
|
||||
const category = getAprsMarkerCategory(packet);
|
||||
const callsign = packet.callsign || 'UNKNOWN';
|
||||
const html = `
|
||||
<div class="aprs-map-marker ${category}">
|
||||
<span class="aprs-map-marker-icon">${getAprsMarkerSvg(category)}</span>
|
||||
<span class="aprs-map-marker-label">${callsign}</span>
|
||||
</div>
|
||||
`;
|
||||
return L.divIcon({
|
||||
className: 'aprs-map-marker-wrap',
|
||||
html,
|
||||
iconSize: [110, 24],
|
||||
iconAnchor: [55, 12]
|
||||
});
|
||||
}
|
||||
|
||||
function updateAprsMarker(packet) {
|
||||
const callsign = packet.callsign;
|
||||
|
||||
@@ -9444,6 +9843,7 @@
|
||||
if (aprsMarkers[callsign]) {
|
||||
// Update existing marker position and popup
|
||||
aprsMarkers[callsign].setLatLng([packet.lat, packet.lon]);
|
||||
aprsMarkers[callsign].setIcon(buildAprsMarkerIcon(packet));
|
||||
aprsMarkers[callsign].setPopupContent(`
|
||||
<div style="font-family: monospace;">
|
||||
<strong>${callsign}</strong><br>
|
||||
@@ -9461,14 +9861,7 @@
|
||||
document.getElementById('aprsStationCount').textContent = aprsStationCount;
|
||||
document.getElementById('aprsStripStations').textContent = aprsStationCount;
|
||||
|
||||
const icon = L.divIcon({
|
||||
className: 'aprs-marker',
|
||||
html: `<div style="background: var(--accent-cyan); color: #000; padding: 2px 6px; border-radius: 3px; font-size: 10px; font-weight: bold; white-space: nowrap;">${callsign}</div>`,
|
||||
iconSize: [80, 20],
|
||||
iconAnchor: [40, 10]
|
||||
});
|
||||
|
||||
const marker = L.marker([packet.lat, packet.lon], { icon: icon }).addTo(aprsMap);
|
||||
const marker = L.marker([packet.lat, packet.lon], { icon: buildAprsMarkerIcon(packet) }).addTo(aprsMap);
|
||||
|
||||
marker.bindPopup(`
|
||||
<div style="font-family: monospace;">
|
||||
@@ -10230,9 +10623,6 @@
|
||||
// Satellite management
|
||||
let trackedSatellites = [
|
||||
{ id: 'ISS', name: 'ISS (ZARYA)', norad: '25544', builtin: true, checked: true },
|
||||
{ id: 'NOAA-15', name: 'NOAA 15', norad: '25338', builtin: true, checked: true },
|
||||
{ id: 'NOAA-18', name: 'NOAA 18', norad: '28654', builtin: true, checked: true },
|
||||
{ id: 'NOAA-19', name: 'NOAA 19', norad: '33591', builtin: true, checked: true },
|
||||
{ id: 'METEOR-M2', name: 'Meteor-M 2', norad: '40069', builtin: true, checked: true }
|
||||
];
|
||||
|
||||
@@ -15146,7 +15536,6 @@
|
||||
style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; max-height: 300px; overflow-y: auto;">
|
||||
<button class="preset-btn" onclick="fetchCelestrakCategory('stations')">Space Stations</button>
|
||||
<button class="preset-btn" onclick="fetchCelestrakCategory('weather')">Weather</button>
|
||||
<button class="preset-btn" onclick="fetchCelestrakCategory('noaa')">NOAA</button>
|
||||
<button class="preset-btn" onclick="fetchCelestrakCategory('goes')">GOES</button>
|
||||
<button class="preset-btn" onclick="fetchCelestrakCategory('amateur')">Amateur</button>
|
||||
<button class="preset-btn" onclick="fetchCelestrakCategory('cubesat')">CubeSats</button>
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
<!-- BLUETOOTH MODE -->
|
||||
<div id="bluetoothMode" class="mode-content">
|
||||
<!-- Capability Status -->
|
||||
<div id="btCapabilityStatus" class="section" style="display: none;">
|
||||
<!-- Populated by JavaScript with capability warnings -->
|
||||
</div>
|
||||
|
||||
<!-- Show All Agents option (visible when agents are available) -->
|
||||
<div id="btShowAllAgentsContainer" class="section" style="display: none; padding: 8px;">
|
||||
<label class="inline-checkbox" style="font-size: 10px;">
|
||||
<input type="checkbox" id="btShowAllAgents" onchange="if(typeof BluetoothMode !== 'undefined') BluetoothMode.toggleShowAllAgents(this.checked)">
|
||||
Show devices from all agents
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>Scanner Configuration</h3>
|
||||
<!-- Populated by JavaScript with capability warnings -->
|
||||
<div id="btCapabilityStatus" style="display: none; margin-bottom: 8px;"></div>
|
||||
|
||||
<!-- Show All Agents option (visible when agents are available) -->
|
||||
<div id="btShowAllAgentsContainer" style="display: none; margin-bottom: 8px;">
|
||||
<label class="inline-checkbox" style="font-size: 10px;">
|
||||
<input type="checkbox" id="btShowAllAgents" onchange="if(typeof BluetoothMode !== 'undefined') BluetoothMode.toggleShowAllAgents(this.checked)">
|
||||
Show devices from all agents
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Adapter</label>
|
||||
<select id="btAdapterSelect">
|
||||
@@ -61,7 +59,7 @@
|
||||
Stop Scanning
|
||||
</button>
|
||||
|
||||
<div class="section" style="margin-top: 10px;">
|
||||
<div class="section">
|
||||
<h3>Export</h3>
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<button class="preset-btn" onclick="btExport('csv')" style="flex: 1;">
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
<div id="dmrMode" class="mode-content">
|
||||
<div class="section">
|
||||
<h3>Digital Voice</h3>
|
||||
<div class="alpha-mode-notice">
|
||||
ALPHA: Digital Voice decoding is still in active development. Expect occasional decode instability and false protocol locks.
|
||||
</div>
|
||||
|
||||
<!-- Dependency Warning -->
|
||||
<div id="dmrToolsWarning" style="display: none; background: rgba(255, 100, 100, 0.1); border: 1px solid var(--accent-red); border-radius: 4px; padding: 10px; margin-bottom: 10px;">
|
||||
@@ -19,13 +22,16 @@
|
||||
<div class="form-group">
|
||||
<label>Protocol</label>
|
||||
<select id="dmrProtocol">
|
||||
<option value="auto" selected>Auto Detect</option>
|
||||
<option value="auto" selected>Auto Detect (DMR/P25/D-STAR)</option>
|
||||
<option value="dmr">DMR</option>
|
||||
<option value="p25">P25</option>
|
||||
<option value="nxdn">NXDN</option>
|
||||
<option value="dstar">D-STAR</option>
|
||||
<option value="provoice">ProVoice</option>
|
||||
</select>
|
||||
<span style="font-size: 0.75em; color: var(--text-muted); display: block; margin-top: 2px;">
|
||||
For NXDN and ProVoice, use manual protocol selection for best lock reliability
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@@ -70,14 +76,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<button class="run-btn" id="startDmrBtn" onclick="startDmr()" style="margin-top: 12px;">
|
||||
Start Decoder
|
||||
</button>
|
||||
<button class="stop-btn" id="stopDmrBtn" onclick="stopDmr()" style="display: none; margin-top: 12px;">
|
||||
Stop Decoder
|
||||
</button>
|
||||
|
||||
<!-- Current Call -->
|
||||
<div class="section" style="margin-top: 12px;">
|
||||
<h3>Current Call</h3>
|
||||
@@ -104,4 +102,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mode-actions-bottom">
|
||||
<button class="run-btn" id="startDmrBtn" onclick="startDmr()">
|
||||
Start Decoder
|
||||
</button>
|
||||
<button class="stop-btn" id="stopDmrBtn" onclick="stopDmr()" style="display: none;">
|
||||
Stop Decoder
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
175
templates/partials/modes/subghz.html
Normal file
175
templates/partials/modes/subghz.html
Normal file
@@ -0,0 +1,175 @@
|
||||
<!-- SUBGHZ TRANSCEIVER MODE -->
|
||||
<div id="subghzMode" class="mode-content">
|
||||
<div class="section">
|
||||
<h3>SubGHz Transceiver</h3>
|
||||
<p class="info-text" style="font-size: 11px; color: var(--text-dim); margin-bottom: 12px;">
|
||||
HackRF One SubGHz transceiver. Capture raw signals, replay saved bursts,
|
||||
and scan wideband activity with frequency analysis.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Device -->
|
||||
<div class="section">
|
||||
<h3>HackRF Device</h3>
|
||||
<div class="subghz-device-status" id="subghzDeviceStatus">
|
||||
<div class="subghz-device-row">
|
||||
<span class="subghz-device-dot" id="subghzDeviceDot"></span>
|
||||
<span class="subghz-device-label" id="subghzDeviceLabel">Checking...</span>
|
||||
</div>
|
||||
<div class="subghz-device-tools" id="subghzDeviceTools">
|
||||
<span class="subghz-tool-badge" id="subghzToolHackrf" title="hackrf_transfer">HackRF</span>
|
||||
<span class="subghz-tool-badge" id="subghzToolSweep" title="hackrf_sweep">Sweep</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" style="margin-top: 8px;">
|
||||
<label>Device Serial <span style="color: var(--text-dim); font-weight: normal;">(optional)</span></label>
|
||||
<input type="text" id="subghzDeviceSerial" placeholder="auto-detect" style="font-family: 'JetBrains Mono', monospace; font-size: 11px;">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Status -->
|
||||
<div class="subghz-status-row" id="subghzStatusRow">
|
||||
<div class="subghz-status-dot" id="subghzStatusDot"></div>
|
||||
<span class="subghz-status-text" id="subghzStatusText">Idle</span>
|
||||
<span class="subghz-status-timer" id="subghzStatusTimer"></span>
|
||||
</div>
|
||||
|
||||
<!-- Frequency -->
|
||||
<div class="section">
|
||||
<h3>Frequency</h3>
|
||||
<div class="form-group">
|
||||
<label>Frequency (MHz)</label>
|
||||
<input type="number" id="subghzFrequency" value="433.92" step="0.001" min="1" max="6000">
|
||||
</div>
|
||||
<div class="subghz-preset-btns">
|
||||
<button class="subghz-preset-btn" onclick="SubGhz.setFreq(315)">315M</button>
|
||||
<button class="subghz-preset-btn" onclick="SubGhz.setFreq(433.92)">433.92M</button>
|
||||
<button class="subghz-preset-btn" onclick="SubGhz.setFreq(868)">868M</button>
|
||||
<button class="subghz-preset-btn" onclick="SubGhz.setFreq(915)">915M</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Gain -->
|
||||
<div class="section">
|
||||
<h3>Gain</h3>
|
||||
<div class="form-group">
|
||||
<label>LNA Gain (0-40 dB)</label>
|
||||
<input type="range" id="subghzLnaGain" min="0" max="40" value="24" step="8" oninput="document.getElementById('subghzLnaVal').textContent=this.value">
|
||||
<span id="subghzLnaVal" style="font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--text-secondary);">24</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>VGA Gain (0-62 dB)</label>
|
||||
<input type="range" id="subghzVgaGain" min="0" max="62" value="20" step="2" oninput="document.getElementById('subghzVgaVal').textContent=this.value">
|
||||
<span id="subghzVgaVal" style="font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--text-secondary);">20</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Sample Rate</label>
|
||||
<select id="subghzSampleRate" class="mode-select">
|
||||
<option value="2000000" selected>2 MHz</option>
|
||||
<option value="4000000">4 MHz</option>
|
||||
<option value="8000000">8 MHz</option>
|
||||
<option value="10000000">10 MHz</option>
|
||||
<option value="20000000">20 MHz</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tabs: Receive RAW / Sweep -->
|
||||
<div class="section">
|
||||
<div class="subghz-tabs">
|
||||
<button class="subghz-tab active" data-tab="rx" onclick="SubGhz.switchTab('rx')">Read RAW</button>
|
||||
<button class="subghz-tab" data-tab="sweep" onclick="SubGhz.switchTab('sweep')">Sweep</button>
|
||||
</div>
|
||||
|
||||
<!-- RX Tab -->
|
||||
<div class="subghz-tab-content active" id="subghzTabRx">
|
||||
<p style="font-size: 11px; color: var(--text-dim); margin-bottom: 10px;">
|
||||
Capture raw IQ data to file. Saved captures can be replayed or analyzed.
|
||||
</p>
|
||||
<div class="subghz-trigger-box">
|
||||
<label class="subghz-trigger-toggle">
|
||||
<input type="checkbox" id="subghzTriggerEnabled" onchange="SubGhz.syncTriggerControls()">
|
||||
Smart Trigger Capture
|
||||
</label>
|
||||
<div class="subghz-trigger-grid">
|
||||
<label>Pre-roll (ms)</label>
|
||||
<input type="number" id="subghzTriggerPreMs" min="50" max="5000" step="50" value="350">
|
||||
<label>Post-roll (ms)</label>
|
||||
<input type="number" id="subghzTriggerPostMs" min="100" max="10000" step="50" value="700">
|
||||
</div>
|
||||
<p class="subghz-trigger-help">Auto-stops after burst + post-roll and trims capture window.</p>
|
||||
</div>
|
||||
<div class="subghz-btn-row">
|
||||
<button class="subghz-btn start" id="subghzRxStartBtn" onclick="SubGhz.startRx()">Start Capture</button>
|
||||
<button class="subghz-btn stop" id="subghzRxStopBtn" onclick="SubGhz.stopRx()" disabled>Stop Capture</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sweep Tab -->
|
||||
<div class="subghz-tab-content" id="subghzTabSweep">
|
||||
<p style="font-size: 11px; color: var(--text-dim); margin-bottom: 10px;">
|
||||
Wideband spectrum analyzer using hackrf_sweep.
|
||||
</p>
|
||||
<div class="form-group">
|
||||
<label>Frequency Range (MHz)</label>
|
||||
<div class="subghz-sweep-range">
|
||||
<input type="number" id="subghzSweepStart" value="300" min="1" max="6000" step="1">
|
||||
<span>to</span>
|
||||
<input type="number" id="subghzSweepEnd" value="928" min="1" max="6000" step="1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="subghz-btn-row">
|
||||
<button class="subghz-btn start" id="subghzSweepStartBtn" onclick="SubGhz.startSweep()">Start Sweep</button>
|
||||
<button class="subghz-btn stop" id="subghzSweepStopBtn" onclick="SubGhz.stopSweep()" disabled>Stop Sweep</button>
|
||||
</div>
|
||||
<div style="margin-top: 10px;">
|
||||
<label style="font-size: 10px; color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.5px;">Detected Peaks</label>
|
||||
<div class="subghz-peak-list" id="subghzPeakList"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TX Settings (collapsible) -->
|
||||
<div class="section">
|
||||
<h3 style="cursor: pointer;" onclick="document.getElementById('subghzTxSection').classList.toggle('active')">
|
||||
Transmit Settings <span style="font-size: 10px; color: var(--text-dim);">▼</span>
|
||||
</h3>
|
||||
<div id="subghzTxSection" style="display: none;">
|
||||
<div class="subghz-tx-warning">
|
||||
WARNING: Transmitting radio signals may be illegal without proper authorization.
|
||||
Only transmit on frequencies you are licensed for and within ISM band limits.
|
||||
TX is restricted to ISM bands: 300-348, 387-464, 779-928 MHz.
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>TX VGA Gain (0-47 dB)</label>
|
||||
<input type="range" id="subghzTxGain" min="0" max="47" value="20" step="1" oninput="document.getElementById('subghzTxGainVal').textContent=this.value">
|
||||
<span id="subghzTxGainVal" style="font-family: 'JetBrains Mono', monospace; font-size: 11px; color: var(--text-secondary);">20</span>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Max Duration (seconds)</label>
|
||||
<input type="number" id="subghzTxMaxDuration" value="10" min="1" max="30" step="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Saved Signals Library -->
|
||||
<div class="section">
|
||||
<h3>Saved Signals</h3>
|
||||
<div class="subghz-captures-list" id="subghzSidebarCaptures" style="max-height: 220px; overflow-y: auto;">
|
||||
<div class="subghz-empty" id="subghzSidebarCapturesEmpty">No saved captures yet</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Toggle TX section visibility
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const h3 = document.querySelector('#subghzTxSection')?.previousElementSibling;
|
||||
if (h3) {
|
||||
h3.addEventListener('click', function() {
|
||||
const section = document.getElementById('subghzTxSection');
|
||||
if (section) section.style.display = section.style.display === 'none' ? 'block' : 'none';
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -2,7 +2,7 @@
|
||||
<div id="tscmMode" class="mode-content">
|
||||
<!-- Configuration -->
|
||||
<div class="section">
|
||||
<h3 style="display: flex; align-items: center; gap: 8px; margin-bottom: 12px;">TSCM Sweep <span style="font-size: 9px; font-weight: normal; background: var(--accent-orange); color: #000; padding: 2px 6px; border-radius: 3px; text-transform: uppercase; letter-spacing: 0.5px;">Alpha</span></h3>
|
||||
<h3>TSCM Sweep <span style="font-size: 9px; font-weight: normal; background: var(--accent-orange); color: #000; padding: 2px 6px; border-radius: 3px; text-transform: uppercase; letter-spacing: 0.5px;">Alpha</span></h3>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Sweep Type</label>
|
||||
@@ -65,14 +65,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<button class="run-btn" id="startTscmBtn" onclick="startTscmSweep()" style="margin-top: 12px;">
|
||||
Start Sweep
|
||||
</button>
|
||||
<button class="stop-btn" id="stopTscmBtn" onclick="stopTscmSweep()" style="display: none; margin-top: 12px;">
|
||||
Stop Sweep
|
||||
</button>
|
||||
|
||||
<!-- Futuristic Scanner Progress -->
|
||||
<div id="tscmProgress" class="tscm-scanner-progress" style="display: none; margin-top: 12px;">
|
||||
<div class="scanner-ring">
|
||||
@@ -115,8 +107,8 @@
|
||||
</div>
|
||||
|
||||
<!-- Advanced -->
|
||||
<div class="section" style="margin-top: 12px;">
|
||||
<h3 style="margin-bottom: 12px;">Advanced</h3>
|
||||
<div class="section">
|
||||
<h3>Advanced</h3>
|
||||
|
||||
<div style="margin-bottom: 16px;">
|
||||
<label style="display: block; font-size: 11px; font-weight: 600; margin-bottom: 6px; color: var(--text-secondary);">Baseline Recording</label>
|
||||
@@ -156,8 +148,8 @@
|
||||
</div>
|
||||
|
||||
<!-- Tools -->
|
||||
<div class="section" style="margin-top: 12px;">
|
||||
<h3 style="margin-bottom: 10px;">Tools</h3>
|
||||
<div class="section">
|
||||
<h3>Tools</h3>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 6px;">
|
||||
<button class="preset-btn" onclick="tscmShowCapabilities()" style="font-size: 10px; padding: 8px;">
|
||||
Capabilities
|
||||
@@ -182,4 +174,13 @@
|
||||
|
||||
<!-- Device Warnings -->
|
||||
<div id="tscmDeviceWarnings" style="display: none; margin-top: 8px; padding: 8px; background: rgba(255,153,51,0.1); border: 1px solid rgba(255,153,51,0.3); border-radius: 4px;"></div>
|
||||
|
||||
<div class="mode-actions-bottom">
|
||||
<button class="run-btn" id="startTscmBtn" onclick="startTscmSweep()">
|
||||
Start Sweep
|
||||
</button>
|
||||
<button class="stop-btn" id="stopTscmBtn" onclick="stopTscmSweep()" style="display: none;">
|
||||
Stop Sweep
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,22 +1,26 @@
|
||||
<!-- WEATHER SATELLITE MODE -->
|
||||
<div id="weatherSatMode" class="mode-content">
|
||||
<div class="section">
|
||||
<h3>Weather Satellite Decoder</h3>
|
||||
<p class="info-text" style="font-size: 11px; color: var(--text-dim); margin-bottom: 12px;">
|
||||
Receive and decode weather images from NOAA and Meteor satellites.
|
||||
Uses SatDump for live SDR capture and image processing.
|
||||
</p>
|
||||
</div>
|
||||
<div id="weatherSatMode" class="mode-content">
|
||||
<div class="section">
|
||||
<h3>Weather Satellite Decoder</h3>
|
||||
<div class="alpha-mode-notice">
|
||||
ALPHA: Weather Satellite capture is experimental and may fail depending on SatDump version, SDR driver support, and pass conditions.
|
||||
</div>
|
||||
<p class="info-text" style="font-size: 11px; color: var(--text-dim); margin-bottom: 12px;">
|
||||
Receive and decode weather images from NOAA and Meteor satellites.
|
||||
Uses SatDump for live SDR capture and image processing.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<h3>Satellite</h3>
|
||||
<div class="form-group">
|
||||
<label>Select Satellite</label>
|
||||
<select id="weatherSatSelect" class="mode-select">
|
||||
<option value="NOAA-15">NOAA-15 (137.620 MHz APT)</option>
|
||||
<option value="NOAA-18" selected>NOAA-18 (137.9125 MHz APT)</option>
|
||||
<option value="NOAA-19">NOAA-19 (137.100 MHz APT)</option>
|
||||
<option value="METEOR-M2-3">Meteor-M2-3 (137.900 MHz LRPT)</option>
|
||||
<option value="METEOR-M2-3" selected>Meteor-M2-3 (137.900 MHz LRPT)</option>
|
||||
<option value="METEOR-M2-4">Meteor-M2-4 (137.900 MHz LRPT)</option>
|
||||
<option value="NOAA-15" disabled>NOAA-15 (137.620 MHz APT) [DEFUNCT]</option>
|
||||
<option value="NOAA-18" disabled>NOAA-18 (137.9125 MHz APT) [DEFUNCT]</option>
|
||||
<option value="NOAA-19" disabled>NOAA-19 (137.100 MHz APT) [DEFUNCT]</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
@@ -187,10 +191,11 @@
|
||||
<div class="form-group">
|
||||
<label>Satellite</label>
|
||||
<select id="wxsatTestSatSelect" class="mode-select">
|
||||
<option value="METEOR-M2-3" selected>Meteor-M2-3 (LRPT)</option>
|
||||
<option value="METEOR-M2-4">Meteor-M2-4 (LRPT)</option>
|
||||
<option value="NOAA-15">NOAA-15 (APT)</option>
|
||||
<option value="NOAA-18" selected>NOAA-18 (APT)</option>
|
||||
<option value="NOAA-18">NOAA-18 (APT)</option>
|
||||
<option value="NOAA-19">NOAA-19 (APT)</option>
|
||||
<option value="METEOR-M2-3">Meteor-M2-3 (LRPT)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<!-- WiFi MODE -->
|
||||
<div id="wifiMode" class="mode-content">
|
||||
<!-- Scan Mode Tabs -->
|
||||
<div class="section" style="padding: 8px;">
|
||||
<div class="section">
|
||||
<h3>Signal Source</h3>
|
||||
<div class="wifi-scan-mode-tabs" style="display: flex; gap: 4px;">
|
||||
<button id="wifiScanModeQuick" class="wifi-mode-tab active" style="flex: 1; padding: 8px; font-size: 11px; background: var(--accent-green); color: #000; border: none; border-radius: 4px; cursor: pointer;">
|
||||
Quick Scan
|
||||
@@ -168,29 +169,8 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- v2 Scan Buttons -->
|
||||
<div style="display: flex; gap: 8px; margin-bottom: 8px;">
|
||||
<button class="run-btn" id="wifiQuickScanBtn" onclick="WiFiMode.startQuickScan()" style="flex: 1;">
|
||||
Quick Scan
|
||||
</button>
|
||||
<button class="run-btn" id="wifiDeepScanBtn" onclick="WiFiMode.startDeepScan()" style="flex: 1; background: var(--accent-orange);">
|
||||
Deep Scan
|
||||
</button>
|
||||
</div>
|
||||
<button class="stop-btn" id="wifiStopScanBtn" onclick="WiFiMode.stopScan()" style="display: none; width: 100%;">
|
||||
Stop Scanning
|
||||
</button>
|
||||
|
||||
<!-- Legacy Scan Buttons (hidden, for backwards compatibility) -->
|
||||
<button class="run-btn" id="startWifiBtn" onclick="startWifiScan()" style="display: none;">
|
||||
Start Scanning (Legacy)
|
||||
</button>
|
||||
<button class="stop-btn" id="stopWifiBtn" onclick="stopWifiScan()" style="display: none;">
|
||||
Stop Scanning (Legacy)
|
||||
</button>
|
||||
|
||||
<!-- Export Section -->
|
||||
<div class="section" style="margin-top: 10px;">
|
||||
<div class="section">
|
||||
<h3>Export</h3>
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<button class="preset-btn" onclick="WiFiMode.exportData('csv')" style="flex: 1;">
|
||||
@@ -201,4 +181,27 @@
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mode-actions-bottom">
|
||||
<!-- v2 Scan Buttons -->
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<button class="run-btn" id="wifiQuickScanBtn" onclick="WiFiMode.startQuickScan()" style="flex: 1;">
|
||||
Quick Scan
|
||||
</button>
|
||||
<button class="run-btn" id="wifiDeepScanBtn" onclick="WiFiMode.startDeepScan()" style="flex: 1; background: var(--accent-orange);">
|
||||
Deep Scan
|
||||
</button>
|
||||
</div>
|
||||
<button class="stop-btn" id="wifiStopScanBtn" onclick="WiFiMode.stopScan()" style="display: none; width: 100%;">
|
||||
Stop Scanning
|
||||
</button>
|
||||
|
||||
<!-- Legacy Scan Buttons (hidden, for backwards compatibility) -->
|
||||
<button class="run-btn" id="startWifiBtn" onclick="startWifiScan()" style="display: none;">
|
||||
Start Scanning (Legacy)
|
||||
</button>
|
||||
<button class="stop-btn" id="stopWifiBtn" onclick="stopWifiScan()" style="display: none;">
|
||||
Stop Scanning (Legacy)
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -71,8 +71,8 @@
|
||||
{{ 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('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('meshtastic', 'Meshtastic', '<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"/><circle cx="12" cy="12" r="3"/><path d="M12 2v4m0 12v4M2 12h4m12 0h4"/></svg>') }}
|
||||
{{ mode_item('dmr', 'Digital Voice', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="22"/></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('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>') }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -191,8 +191,8 @@
|
||||
{{ mobile_item('listening', 'Scanner', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18"/><path d="M9 21V9"/></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('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>') }}
|
||||
{{ mobile_item('dmr', 'DMR', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="22"/></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>') }}
|
||||
{{ mobile_item('subghz', 'SubGHz', '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M2 12h6l3-9 3 18 3-9h5"/></svg>') }}
|
||||
</nav>
|
||||
|
||||
{# JavaScript stub for pages that don't have switchMode defined #}
|
||||
|
||||
@@ -323,7 +323,6 @@
|
||||
<option value="acars">ACARS</option>
|
||||
<option value="aprs">APRS</option>
|
||||
<option value="rtlamr">RTLAMR</option>
|
||||
<option value="dmr">DMR</option>
|
||||
<option value="tscm">TSCM</option>
|
||||
<option value="sstv">SSTV</option>
|
||||
<option value="sstv_general">SSTV General</option>
|
||||
|
||||
@@ -107,12 +107,8 @@
|
||||
<label>TARGET:</label>
|
||||
<select id="satSelect" onchange="onSatelliteChange()">
|
||||
<option value="25544">ISS (ZARYA)</option>
|
||||
<option value="25338">NOAA 15</option>
|
||||
<option value="28654">NOAA 18</option>
|
||||
<option value="33591">NOAA 19</option>
|
||||
<option value="40069">METEOR-M2</option>
|
||||
<option value="43013">NOAA 20</option>
|
||||
<option value="54234">METEOR-M2-3</option>
|
||||
<option value="57166">METEOR-M2-3</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
@@ -275,12 +271,8 @@
|
||||
|
||||
const satellites = {
|
||||
25544: { name: 'ISS (ZARYA)', color: '#00ffff' },
|
||||
25338: { name: 'NOAA 15', color: '#00ff00' },
|
||||
28654: { name: 'NOAA 18', color: '#ff6600' },
|
||||
33591: { name: 'NOAA 19', color: '#ff3366' },
|
||||
40069: { name: 'METEOR-M2', color: '#9370DB' },
|
||||
43013: { name: 'NOAA 20', color: '#00ffaa' },
|
||||
54234: { name: 'METEOR-M2-3', color: '#ff00ff' }
|
||||
57166: { name: 'METEOR-M2-3', color: '#ff00ff' }
|
||||
};
|
||||
|
||||
function onSatelliteChange() {
|
||||
|
||||
Reference in New Issue
Block a user