mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 06:40:00 -07:00
feat: add Generic OOK Signal Decoder module
New 'OOK Decoder' mode for capturing and decoding arbitrary OOK/ASK signals using rtl_433's flex decoder with fully configurable pulse timing. Covers PWM, PPM, and Manchester encoding schemes. Backend (utils/ook.py, routes/ook.py): - Configurable modulation: OOK_PWM, OOK_PPM, OOK_MC_ZEROBIT - Full rtl_433 flex spec builder with user-supplied pulse timings - Bit-inversion fallback for transmitters with swapped short/long mapping - Optional frame deduplication for repeated transmissions - SSE streaming via /ook/stream Frontend (static/js/modes/ook.js, templates/partials/modes/ook.html): - Live MSB/LSB bit-order toggle — re-renders all stored frames instantly without restarting the decoder - Full-detail frame display: timestamp, bit count, hex, dotted ASCII - Modulation selector buttons with encoding hint text - Full timing grid: short, long, gap/reset, tolerance, min bits - CSV export of captured frames - Global SDR device panel injection (device, SDR type, rtl_tcp, bias-T) Integration (app.py, routes/__init__.py, templates/): - Globals: ook_process, ook_queue, ook_lock - Registered blueprint, nav entries (desktop + mobile), welcome card - ookOutputPanel in visuals area with bit-order toolbar Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -287,6 +287,10 @@
|
||||
<span class="mode-icon icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="2" y1="12" x2="5" y2="12"/><line x1="7" y1="12" x2="13" y2="12"/><line x1="15" y1="12" x2="18" y2="12"/><line x1="20" y1="12" x2="22" y2="12"/></svg></span>
|
||||
<span class="mode-name">Morse</span>
|
||||
</button>
|
||||
<button class="mode-card mode-card-sm" onclick="selectMode('ook')">
|
||||
<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 12h3"/><path d="M19 12h3"/><rect x="5" y="8" width="4" height="8" rx="1"/><rect x="10" y="9" width="4" height="6" rx="1"/><rect x="15" y="7" width="4" height="10" rx="1"/></svg></span>
|
||||
<span class="mode-name">OOK Decoder</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -697,6 +701,8 @@
|
||||
|
||||
{% include 'partials/modes/morse.html' %}
|
||||
|
||||
{% include 'partials/modes/ook.html' %}
|
||||
|
||||
{% include 'partials/modes/space-weather.html' %}
|
||||
|
||||
{% include 'partials/modes/tscm.html' %}
|
||||
@@ -3285,6 +3291,29 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- OOK Decoder Output Panel -->
|
||||
<div id="ookOutputPanel" style="display: none; margin-bottom: 12px;">
|
||||
<div style="background: #0a0a0a; border: 1px solid #1a2e1a; border-radius: 6px; padding: 8px 10px;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; font-size: 10px; color: #555; text-transform: uppercase; letter-spacing: 1px;">
|
||||
<span>Decoded Frames</span>
|
||||
<div style="display: flex; gap: 6px; align-items: center;">
|
||||
<span style="color: var(--text-dim); font-size: 10px;">Bit order:</span>
|
||||
<button class="btn btn-sm btn-ghost" id="ookBitMSB"
|
||||
onclick="OokMode.setBitOrder('msb')"
|
||||
style="background: var(--accent); color: #000;">MSB</button>
|
||||
<button class="btn btn-sm btn-ghost" id="ookBitLSB"
|
||||
onclick="OokMode.setBitOrder('lsb')">LSB</button>
|
||||
<button class="btn btn-sm btn-ghost" onclick="OokMode.clearOutput()">Clear</button>
|
||||
<button class="btn btn-sm btn-ghost" onclick="OokMode.exportLog()">CSV</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="ookOutput" style="max-height: 400px; overflow-y: auto; font-family: var(--font-mono); font-size: 10px; color: var(--text-dim);"></div>
|
||||
</div>
|
||||
<div style="margin-top: 4px; font-size: 10px; color: #555; text-align: right;">
|
||||
<span id="ookStatusBarFrames">0 frames</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="output-content signal-feed" id="output">
|
||||
<div class="placeholder signal-empty-state">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
@@ -3356,6 +3385,7 @@
|
||||
<script src="{{ url_for('static', filename='js/modes/bt_locate.js') }}?v={{ version }}&r=btlocate4"></script>
|
||||
<script src="{{ url_for('static', filename='js/modes/wefax.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/modes/morse.js') }}?v={{ version }}&r=morse_iq12"></script>
|
||||
<script src="{{ url_for('static', filename='js/modes/ook.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/modes/space-weather.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/modes/system.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/modes/meteor.js') }}"></script>
|
||||
@@ -3515,6 +3545,7 @@
|
||||
waterfall: { label: 'Waterfall', indicator: 'WATERFALL', outputTitle: 'Spectrum Waterfall', group: 'signals' },
|
||||
morse: { label: 'Morse', indicator: 'MORSE', outputTitle: 'CW/Morse Decoder', group: 'signals' },
|
||||
system: { label: 'System', indicator: 'SYSTEM', outputTitle: 'System Health Monitor', group: 'system' },
|
||||
ook: { label: 'OOK Decoder', indicator: 'OOK', outputTitle: 'OOK Signal Decoder', group: 'signals' },
|
||||
};
|
||||
const validModes = new Set(Object.keys(modeCatalog));
|
||||
window.interceptModeCatalog = Object.assign({}, modeCatalog);
|
||||
@@ -4363,6 +4394,7 @@
|
||||
document.getElementById('morseMode')?.classList.toggle('active', mode === 'morse');
|
||||
document.getElementById('meteorMode')?.classList.toggle('active', mode === 'meteor');
|
||||
document.getElementById('systemMode')?.classList.toggle('active', mode === 'system');
|
||||
document.getElementById('ookMode')?.classList.toggle('active', mode === 'ook');
|
||||
|
||||
|
||||
const pagerStats = document.getElementById('pagerStats');
|
||||
@@ -4462,6 +4494,8 @@
|
||||
if (morseOutputPanel && mode !== 'morse') morseOutputPanel.style.display = 'none';
|
||||
const morseDiagLog = document.getElementById('morseDiagLog');
|
||||
if (morseDiagLog && mode !== 'morse') morseDiagLog.style.display = 'none';
|
||||
const ookOutputPanel = document.getElementById('ookOutputPanel');
|
||||
if (ookOutputPanel && mode !== 'ook') ookOutputPanel.style.display = 'none';
|
||||
|
||||
// Update output panel title based on mode
|
||||
const outputTitle = document.getElementById('outputTitle');
|
||||
@@ -4503,16 +4537,17 @@
|
||||
// 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 === 'aprs' || mode === 'sstv' || mode === 'weathersat' || mode === 'sstv_general' || mode === 'wefax' || mode === 'morse' || mode === 'radiosonde' || mode === 'meteor') ? 'block' : 'none';
|
||||
rtlDeviceSection.style.display = (mode === 'pager' || mode === 'sensor' || mode === 'rtlamr' || mode === 'aprs' || mode === 'sstv' || mode === 'weathersat' || mode === 'sstv_general' || mode === 'wefax' || mode === 'morse' || mode === 'radiosonde' || mode === 'meteor' || mode === 'ook') ? 'block' : 'none';
|
||||
// Save original sidebar position of SDR device section (once)
|
||||
if (!rtlDeviceSection._origParent) {
|
||||
rtlDeviceSection._origParent = rtlDeviceSection.parentNode;
|
||||
rtlDeviceSection._origNext = rtlDeviceSection.nextElementSibling;
|
||||
}
|
||||
// For morse/radiosonde/meteor modes, move SDR device section inside the panel after the title
|
||||
// For morse/radiosonde/meteor/ook modes, move SDR device section inside the panel after the title
|
||||
const morsePanel = document.getElementById('morseMode');
|
||||
const radiosondePanel = document.getElementById('radiosondeMode');
|
||||
const meteorPanel = document.getElementById('meteorMode');
|
||||
const ookPanel = document.getElementById('ookMode');
|
||||
if (mode === 'morse' && morsePanel) {
|
||||
const firstSection = morsePanel.querySelector('.section');
|
||||
if (firstSection) firstSection.after(rtlDeviceSection);
|
||||
@@ -4522,6 +4557,9 @@
|
||||
} else if (mode === 'meteor' && meteorPanel) {
|
||||
const firstSection = meteorPanel.querySelector('.section');
|
||||
if (firstSection) firstSection.after(rtlDeviceSection);
|
||||
} else if (mode === 'ook' && ookPanel) {
|
||||
const firstSection = ookPanel.querySelector('.section');
|
||||
if (firstSection) firstSection.after(rtlDeviceSection);
|
||||
} else if (rtlDeviceSection._origParent && rtlDeviceSection.parentNode !== rtlDeviceSection._origParent) {
|
||||
// Restore to original sidebar position when leaving morse mode
|
||||
if (rtlDeviceSection._origNext) {
|
||||
@@ -4626,6 +4664,8 @@
|
||||
MeteorScatter.init();
|
||||
} else if (mode === 'system') {
|
||||
SystemHealth.init();
|
||||
} else if (mode === 'ook') {
|
||||
OokMode.init();
|
||||
}
|
||||
|
||||
// Waterfall destroy is now handled by moduleDestroyMap above.
|
||||
|
||||
Reference in New Issue
Block a user