mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 06:40:00 -07:00
Merge branch 'smittix:main' into main
This commit is contained in:
@@ -612,8 +612,6 @@
|
||||
|
||||
{% include 'partials/modes/meshtastic.html' %}
|
||||
|
||||
{% include 'partials/modes/dmr.html' %}
|
||||
|
||||
{% include 'partials/modes/websdr.html' %}
|
||||
|
||||
{% include 'partials/modes/subghz.html' %}
|
||||
@@ -1883,76 +1881,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DMR / Digital Voice Dashboard -->
|
||||
<div id="dmrVisuals" style="display: none; padding: 12px; flex-direction: column; gap: 12px; flex: 1; min-height: 0;">
|
||||
<!-- DMR Synthesizer -->
|
||||
<div class="radio-module-box" style="padding: 10px;">
|
||||
<div class="module-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; font-size: 10px;">
|
||||
<span>SIGNAL ACTIVITY</span>
|
||||
<span id="dmrSynthStatus" style="color: var(--text-muted); font-size: 9px; font-family: var(--font-mono);">IDLE</span>
|
||||
</div>
|
||||
<canvas id="dmrSynthCanvas" style="width: 100%; height: 70px; background: rgba(0,0,0,0.4); border-radius: 4px; display: block;"></canvas>
|
||||
</div>
|
||||
<!-- Audio Output -->
|
||||
<div class="radio-module-box" style="padding: 8px 12px;">
|
||||
<audio id="dmrAudioPlayer" style="display: none;"></audio>
|
||||
<div style="display: flex; align-items: center; gap: 10px;">
|
||||
<span style="font-size: 10px; color: var(--text-muted); text-transform: uppercase;">AUDIO</span>
|
||||
<span id="dmrAudioStatus" style="font-size: 9px; font-family: var(--font-mono); color: var(--text-muted);">OFF</span>
|
||||
<div style="display: flex; align-items: center; gap: 4px; margin-left: auto;">
|
||||
<span style="font-size: 10px; color: var(--text-muted);">VOL</span>
|
||||
<input type="range" id="dmrAudioVolume" min="0" max="100" value="80" style="width: 80px;" oninput="setDmrAudioVolume(this.value)">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px;">
|
||||
<!-- Call History Panel -->
|
||||
<div class="radio-module-box" style="padding: 10px;">
|
||||
<div class="module-header" style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; font-size: 10px;">
|
||||
<span>CALL HISTORY</span>
|
||||
<span id="dmrHistoryCount" style="color: var(--accent-cyan);">0 calls</span>
|
||||
</div>
|
||||
<div style="max-height: 400px; overflow-y: auto;">
|
||||
<table style="width: 100%; font-size: 10px; border-collapse: collapse;">
|
||||
<thead>
|
||||
<tr style="color: var(--text-muted); border-bottom: 1px solid var(--border-color);">
|
||||
<th style="text-align: left; padding: 4px;">Time</th>
|
||||
<th style="text-align: left; padding: 4px;">Talkgroup</th>
|
||||
<th style="text-align: left; padding: 4px;">Source</th>
|
||||
<th style="text-align: left; padding: 4px;">Protocol</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="dmrHistoryBody">
|
||||
<tr><td colspan="4" style="padding: 15px; text-align: center; color: var(--text-muted);">No calls recorded</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Protocol Info Panel -->
|
||||
<div class="radio-module-box" style="padding: 10px;">
|
||||
<div class="module-header" style="margin-bottom: 8px; font-size: 10px;">
|
||||
<span>DECODER STATUS</span>
|
||||
</div>
|
||||
<div style="display: flex; flex-direction: column; gap: 12px; padding: 10px;">
|
||||
<div style="text-align: center;">
|
||||
<div style="font-size: 9px; color: var(--text-muted); text-transform: uppercase; letter-spacing: 2px; margin-bottom: 4px;">PROTOCOL</div>
|
||||
<div id="dmrMainProtocol" style="font-size: 28px; font-weight: bold; color: var(--accent-cyan); font-family: var(--font-mono);">--</div>
|
||||
</div>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 8px;">
|
||||
<div style="text-align: center; padding: 8px; background: rgba(0,0,0,0.3); border-radius: 4px;">
|
||||
<div style="font-size: 9px; color: var(--text-muted);">CALLS</div>
|
||||
<div id="dmrMainCallCount" style="font-size: 22px; font-weight: bold; color: var(--accent-green); font-family: var(--font-mono);">0</div>
|
||||
</div>
|
||||
<div style="text-align: center; padding: 8px; background: rgba(0,0,0,0.3); border-radius: 4px;">
|
||||
<div style="font-size: 9px; color: var(--text-muted);">SYNCS</div>
|
||||
<div id="dmrSyncCount" style="font-size: 22px; font-weight: bold; color: var(--accent-orange); font-family: var(--font-mono);">0</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- SubGHz Transceiver Dashboard -->
|
||||
<div id="subghzVisuals" class="subghz-visuals-container" style="display: none;">
|
||||
|
||||
@@ -3270,7 +3198,6 @@
|
||||
<script src="{{ url_for('static', filename='js/modes/weather-satellite.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/modes/sstv-general.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/modes/gps.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 src="{{ url_for('static', filename='js/modes/bt_locate.js') }}?v={{ version }}&r=btlocate4"></script>
|
||||
@@ -4016,7 +3943,6 @@
|
||||
document.getElementById('aisMode')?.classList.toggle('active', mode === 'ais');
|
||||
document.getElementById('spystationsMode')?.classList.toggle('active', mode === 'spystations');
|
||||
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');
|
||||
document.getElementById('analyticsMode')?.classList.toggle('active', mode === 'analytics');
|
||||
@@ -4057,7 +3983,6 @@
|
||||
const weatherSatVisuals = document.getElementById('weatherSatVisuals');
|
||||
const sstvGeneralVisuals = document.getElementById('sstvGeneralVisuals');
|
||||
const gpsVisuals = document.getElementById('gpsVisuals');
|
||||
const dmrVisuals = document.getElementById('dmrVisuals');
|
||||
const websdrVisuals = document.getElementById('websdrVisuals');
|
||||
const subghzVisuals = document.getElementById('subghzVisuals');
|
||||
const btLocateVisuals = document.getElementById('btLocateVisuals');
|
||||
@@ -4074,12 +3999,16 @@
|
||||
if (weatherSatVisuals) weatherSatVisuals.style.display = mode === 'weathersat' ? 'flex' : 'none';
|
||||
if (sstvGeneralVisuals) sstvGeneralVisuals.style.display = mode === 'sstv_general' ? 'flex' : 'none';
|
||||
if (gpsVisuals) gpsVisuals.style.display = mode === 'gps' ? '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';
|
||||
if (btLocateVisuals) btLocateVisuals.style.display = mode === 'bt_locate' ? 'flex' : 'none';
|
||||
if (spaceWeatherVisuals) spaceWeatherVisuals.style.display = mode === 'spaceweather' ? 'flex' : 'none';
|
||||
|
||||
// Prevent Leaflet heatmap redraws on hidden BT Locate map containers.
|
||||
if (typeof BtLocate !== 'undefined' && BtLocate.setActiveMode) {
|
||||
BtLocate.setActiveMode(mode === 'bt_locate');
|
||||
}
|
||||
|
||||
// Hide sidebar by default for Meshtastic mode, show for others
|
||||
const mainContent = document.querySelector('.main-content');
|
||||
if (mainContent) {
|
||||
@@ -4134,7 +4063,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 === 'gps' || mode === 'listening' || mode === 'aprs' || mode === 'tscm' || mode === 'spystations' || mode === 'meshtastic' || mode === 'dmr' || mode === 'websdr' || mode === 'subghz' || mode === 'analytics' || mode === 'spaceweather') {
|
||||
if (mode === 'satellite' || mode === 'sstv' || mode === 'weathersat' || mode === 'sstv_general' || mode === 'gps' || mode === 'listening' || mode === 'aprs' || mode === 'tscm' || mode === 'spystations' || mode === 'meshtastic' || mode === 'websdr' || mode === 'subghz' || mode === 'analytics' || mode === 'spaceweather') {
|
||||
if (reconPanel) reconPanel.style.display = 'none';
|
||||
if (reconBtn) reconBtn.style.display = 'none';
|
||||
if (intelBtn) intelBtn.style.display = 'none';
|
||||
@@ -4154,7 +4083,7 @@
|
||||
|
||||
// Show RTL-SDR device section for modes that use it
|
||||
const rtlDeviceSection = document.getElementById('rtlDeviceSection');
|
||||
if (rtlDeviceSection) rtlDeviceSection.style.display = (mode === 'pager' || mode === 'sensor' || mode === 'rtlamr' || mode === 'listening' || mode === 'aprs' || mode === 'sstv' || mode === 'weathersat' || mode === 'sstv_general' || mode === 'dmr') ? 'block' : 'none';
|
||||
if (rtlDeviceSection) rtlDeviceSection.style.display = (mode === 'pager' || mode === 'sensor' || mode === 'rtlamr' || mode === 'listening' || mode === 'aprs' || mode === 'sstv' || mode === 'weathersat' || mode === 'sstv_general') ? 'block' : 'none';
|
||||
|
||||
// Show waterfall panel if running in listening mode
|
||||
const waterfallPanel = document.getElementById('waterfallPanel');
|
||||
@@ -4172,8 +4101,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' || mode === 'subghz' || mode === 'analytics' || mode === 'spaceweather') ? 'none' : 'block';
|
||||
if (statusBar) statusBar.style.display = (mode === 'satellite' || mode === 'websdr' || mode === 'dmr' || mode === 'subghz' || mode === 'spaceweather') ? '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 === 'websdr' || mode === 'subghz' || mode === 'analytics' || mode === 'spaceweather') ? 'none' : 'block';
|
||||
if (statusBar) statusBar.style.display = (mode === 'satellite' || mode === 'websdr' || mode === 'subghz' || mode === 'spaceweather') ? 'none' : 'flex';
|
||||
|
||||
// Restore sidebar when leaving Meshtastic mode (user may have collapsed it)
|
||||
if (mode !== 'meshtastic') {
|
||||
@@ -4232,10 +4161,6 @@
|
||||
SSTVGeneral.init();
|
||||
} else if (mode === 'gps') {
|
||||
GPS.init();
|
||||
} else if (mode === 'dmr') {
|
||||
if (typeof checkDmrTools === 'function') checkDmrTools();
|
||||
if (typeof checkDmrStatus === 'function') checkDmrStatus();
|
||||
if (typeof initDmrSynthesizer === 'function') setTimeout(initDmrSynthesizer, 100);
|
||||
} else if (mode === 'websdr') {
|
||||
if (typeof initWebSDR === 'function') initWebSDR();
|
||||
} else if (mode === 'subghz') {
|
||||
@@ -10432,7 +10357,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
const lines = tleText.split('\\n').map(l => l.trim()).filter(l => l);
|
||||
const lines = tleText.split(/\r?\n/).map(l => l.trim()).filter(l => l);
|
||||
const toAdd = [];
|
||||
|
||||
for (let i = 0; i < lines.length; i += 3) {
|
||||
@@ -10483,7 +10408,7 @@
|
||||
|
||||
fetch('/satellite/celestrak/' + category)
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
.then(async data => {
|
||||
if (data.status === 'success' && data.satellites) {
|
||||
const toAdd = data.satellites
|
||||
.filter(sat => !trackedSatellites.find(s => s.norad === String(sat.norad)))
|
||||
@@ -10500,27 +10425,36 @@
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/satellite/tracked', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(toAdd)
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(result => {
|
||||
if (result.status === 'success') {
|
||||
_loadSatellitesFromAPI();
|
||||
status.innerHTML = `<span style="color: var(--accent-green);">Added ${result.added} satellites (${data.satellites.length} total in category)</span>`;
|
||||
const batchSize = 250;
|
||||
let addedTotal = 0;
|
||||
|
||||
for (let i = 0; i < toAdd.length; i += batchSize) {
|
||||
const batch = toAdd.slice(i, i + batchSize);
|
||||
const completed = Math.min(i + batch.length, toAdd.length);
|
||||
status.innerHTML = `<span style="color: var(--accent-cyan);">Importing ${completed}/${toAdd.length} from ${category}...</span>`;
|
||||
|
||||
const resp = await fetch('/satellite/tracked', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(batch)
|
||||
});
|
||||
const result = await resp.json().catch(() => ({}));
|
||||
|
||||
if (!resp.ok || result.status !== 'success') {
|
||||
throw new Error(result.message || result.error || `HTTP ${resp.status}`);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
status.innerHTML = `<span style="color: var(--accent-red);">Failed to save satellites</span>`;
|
||||
});
|
||||
addedTotal += Number(result.added || 0);
|
||||
}
|
||||
|
||||
_loadSatellitesFromAPI();
|
||||
status.innerHTML = `<span style="color: var(--accent-green);">Added ${addedTotal} satellites (${data.satellites.length} total in category)</span>`;
|
||||
} else {
|
||||
status.innerHTML = `<span style="color: var(--accent-red);">Error: ${data.message || 'Failed to fetch'}</span>`;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
status.innerHTML = `<span style="color: var(--accent-red);">Network error</span>`;
|
||||
.catch((err) => {
|
||||
const msg = err && err.message ? err.message : 'Network error';
|
||||
status.innerHTML = `<span style="color: var(--accent-red);">Import failed: ${msg}</span>`;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,114 +0,0 @@
|
||||
<!-- DMR / DIGITAL VOICE MODE -->
|
||||
<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;">
|
||||
<p style="color: var(--accent-red); margin: 0; font-size: 0.85em;">
|
||||
<strong>Missing:</strong><br>
|
||||
<span id="dmrToolsWarningText"></span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Frequency (MHz)</label>
|
||||
<input type="number" id="dmrFrequency" value="462.5625" step="0.0001" style="width: 100%;">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Protocol</label>
|
||||
<select id="dmrProtocol">
|
||||
<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">
|
||||
<label>Gain</label>
|
||||
<input type="number" id="dmrGain" value="40" min="0" max="50" style="width: 100%;">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>PPM Correction</label>
|
||||
<input type="number" id="dmrPPM" value="0" min="-200" max="200" step="1" style="width: 100%;"
|
||||
title="Frequency error correction for your RTL-SDR dongle. Digital voice is very sensitive to frequency offset.">
|
||||
</div>
|
||||
|
||||
<div class="form-group" style="margin-top: 4px;">
|
||||
<label style="display: flex; align-items: center; gap: 8px; cursor: pointer;">
|
||||
<input type="checkbox" id="dmrRelaxCrc" style="width: auto; accent-color: var(--accent-cyan);">
|
||||
<span>Relax CRC (weak signals)</span>
|
||||
</label>
|
||||
<span style="font-size: 0.75em; color: var(--text-muted); display: block; margin-top: 2px;">
|
||||
Allows more frames through on marginal signals at the cost of occasional errors
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bookmarks -->
|
||||
<div class="section" style="margin-top: 8px;">
|
||||
<h3>Bookmarks</h3>
|
||||
<div style="display: flex; gap: 4px; margin-bottom: 6px;">
|
||||
<input type="number" id="dmrBookmarkFreq" placeholder="Freq MHz" step="0.0001"
|
||||
style="flex: 1; font-size: 11px; padding: 4px 6px;">
|
||||
<button class="preset-btn" onclick="addDmrBookmark()" style="font-size: 10px; padding: 4px 8px;"
|
||||
title="Add bookmark">+</button>
|
||||
</div>
|
||||
<div style="display: flex; gap: 4px; margin-bottom: 6px;">
|
||||
<input type="text" id="dmrBookmarkLabel" placeholder="Label (optional)"
|
||||
style="flex: 1; font-size: 11px; padding: 4px 6px;">
|
||||
<button class="preset-btn" onclick="addCurrentDmrFreqBookmark()" style="font-size: 9px; padding: 4px 6px;"
|
||||
title="Save current frequency">Save current</button>
|
||||
</div>
|
||||
<div id="dmrBookmarksList" style="max-height: 150px; overflow-y: auto;">
|
||||
<div style="color: var(--text-muted); text-align: center; padding: 10px; font-size: 11px;">No bookmarks saved</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Current Call -->
|
||||
<div class="section" style="margin-top: 12px;">
|
||||
<h3>Current Call</h3>
|
||||
<div id="dmrCurrentCall" style="background: rgba(0,0,0,0.3); border-radius: 6px; padding: 10px; font-size: 11px;">
|
||||
<div style="color: var(--text-muted); text-align: center;">No active call</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Status -->
|
||||
<div class="section" style="margin-top: 12px;">
|
||||
<h3>Status</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: 6px;">
|
||||
<span style="font-size: 10px; color: var(--text-muted); text-transform: uppercase;">Status</span>
|
||||
<span id="dmrStatus" style="font-size: 11px; color: var(--accent-cyan);">IDLE</span>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px;">
|
||||
<span style="font-size: 10px; color: var(--text-muted); text-transform: uppercase;">Protocol</span>
|
||||
<span id="dmrActiveProtocol" style="font-size: 11px; color: var(--text-primary);">--</span>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||
<span style="font-size: 10px; color: var(--text-muted); text-transform: uppercase;">Calls</span>
|
||||
<span id="dmrCallCount" style="font-size: 14px; font-weight: bold; color: var(--accent-green);">0</span>
|
||||
</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>
|
||||
Reference in New Issue
Block a user