diff --git a/config.py b/config.py index d642c96..b468f07 100644 --- a/config.py +++ b/config.py @@ -139,7 +139,7 @@ def _get_env_bool(key: str, default: bool) -> bool: # Logging configuration -_log_level_str = _get_env('LOG_LEVEL', 'WARNING').upper() +_log_level_str = _get_env('LOG_LEVEL', 'INFO').upper() LOG_LEVEL = getattr(logging, _log_level_str, logging.WARNING) LOG_FORMAT = _get_env('LOG_FORMAT', '%(asctime)s - %(levelname)s - %(message)s') diff --git a/static/css/modes/weather-satellite.css b/static/css/modes/weather-satellite.css index 7712f41..d2589b5 100644 --- a/static/css/modes/weather-satellite.css +++ b/static/css/modes/weather-satellite.css @@ -787,4 +787,169 @@ .wxsat-gallery-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); } + + .wxsat-phase-indicator { + display: none; + } +} + +/* ===== Signal Console ===== */ +.wxsat-signal-console { + display: none; + flex-direction: column; + border-bottom: 1px solid var(--border-color, #2a3040); + background: var(--bg-secondary, #141820); +} + +.wxsat-signal-console.active { + display: flex; +} + +.wxsat-console-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 6px 16px; + background: var(--bg-tertiary, #1a1f2e); + border-bottom: 1px solid var(--border-color, #2a3040); + min-height: 32px; +} + +.wxsat-console-title-group { + display: flex; + align-items: center; + gap: 12px; + flex: 1; + min-width: 0; +} + +.wxsat-console-title { + font-size: 10px; + font-weight: 600; + color: var(--text-dim, #666); + text-transform: uppercase; + letter-spacing: 1px; + flex-shrink: 0; +} + +.wxsat-phase-indicator { + display: flex; + align-items: center; + gap: 4px; + font-family: 'JetBrains Mono', monospace; +} + +.wxsat-phase-step { + font-size: 9px; + padding: 2px 6px; + border-radius: 3px; + color: var(--text-dim, #555); + background: transparent; + border: 1px solid var(--border-color, #2a3040); + transition: all 0.3s ease; + letter-spacing: 0.5px; +} + +.wxsat-phase-step.active { + color: #00ff88; + border-color: #00ff88; + background: rgba(0, 255, 136, 0.1); + box-shadow: 0 0 8px rgba(0, 255, 136, 0.2); +} + +.wxsat-phase-step.completed { + color: var(--accent-cyan, #00d4ff); + border-color: rgba(0, 212, 255, 0.3); + background: rgba(0, 212, 255, 0.05); + opacity: 0.7; +} + +.wxsat-phase-step.error { + color: #ff4444; + border-color: #ff4444; + background: rgba(255, 68, 68, 0.1); + box-shadow: 0 0 8px rgba(255, 68, 68, 0.2); +} + +.wxsat-phase-arrow { + font-size: 8px; + color: var(--text-dim, #444); +} + +#wxsatConsoleToggle { + font-size: 10px; + width: 28px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + padding: 0; + flex-shrink: 0; + transition: transform 0.2s; +} + +#wxsatConsoleToggle.collapsed { + transform: rotate(-90deg); +} + +.wxsat-console-body { + max-height: 160px; + overflow: hidden; + transition: max-height 0.3s ease; +} + +.wxsat-console-body.collapsed { + max-height: 0; +} + +.wxsat-console-log { + overflow-y: auto; + max-height: 160px; + padding: 6px 12px; + background: var(--bg-primary, #0d1117); + font-family: 'JetBrains Mono', monospace; + font-size: 10px; + line-height: 1.6; +} + +.wxsat-console-entry { + padding: 1px 0 1px 8px; + border-left: 2px solid transparent; + color: var(--text-secondary, #999); + word-break: break-all; +} + +.wxsat-console-entry.wxsat-log-info { + border-left-color: var(--border-color, #2a3040); + color: var(--text-dim, #777); +} + +.wxsat-console-entry.wxsat-log-signal { + border-left-color: #00ff88; + color: #00ff88; +} + +.wxsat-console-entry.wxsat-log-progress { + border-left-color: var(--accent-cyan, #00d4ff); + color: var(--accent-cyan, #00d4ff); +} + +.wxsat-console-entry.wxsat-log-save { + border-left-color: #ffbb00; + color: #ffbb00; +} + +.wxsat-console-entry.wxsat-log-error { + border-left-color: #ff4444; + color: #ff4444; +} + +.wxsat-console-entry.wxsat-log-warning { + border-left-color: #ff8800; + color: #ff8800; +} + +.wxsat-console-entry.wxsat-log-debug { + border-left-color: transparent; + color: var(--text-dim, #555); } diff --git a/static/js/modes/weather-satellite.js b/static/js/modes/weather-satellite.js index 8601380..a4a6327 100644 --- a/static/js/modes/weather-satellite.js +++ b/static/js/modes/weather-satellite.js @@ -17,6 +17,10 @@ const WeatherSat = (function() { let groundMap = null; let groundTrackLayer = null; let observerMarker = null; + let consoleEntries = []; + let consoleCollapsed = false; + let currentPhase = 'idle'; + let consoleAutoHideTimer = null; /** * Initialize the Weather Satellite mode @@ -160,6 +164,10 @@ const WeatherSat = (function() { const biasT = biasTInput?.checked || false; const device = parseInt(deviceSelect?.value || '0', 10); + clearConsole(); + showConsole(true); + updatePhaseIndicator('tuning'); + addConsoleEntry('Starting capture...', 'info'); updateStatusUI('connecting', 'Starting...'); try { @@ -313,6 +321,11 @@ const WeatherSat = (function() { if (captureElapsed) captureElapsed.textContent = formatElapsed(data.elapsed_seconds || 0); if (progressBar) progressBar.style.width = (data.progress || 0) + '%'; + // Console updates + showConsole(true); + if (data.message) addConsoleEntry(data.message, data.log_type || 'info'); + if (data.capture_phase) updatePhaseIndicator(data.capture_phase); + } else if (data.status === 'complete') { if (data.image) { images.unshift(data.image); @@ -327,12 +340,20 @@ const WeatherSat = (function() { if (!schedulerEnabled) stopStream(); updateStatusUI('idle', 'Capture complete'); if (captureStatus) captureStatus.classList.remove('active'); + + addConsoleEntry('Capture complete', 'signal'); + updatePhaseIndicator('complete'); + consoleAutoHideTimer = setTimeout(() => showConsole(false), 30000); } } else if (data.status === 'error') { updateStatusUI('idle', 'Error'); showNotification('Weather Sat', data.message || 'Capture error'); if (captureStatus) captureStatus.classList.remove('active'); + + if (data.message) addConsoleEntry(data.message, 'error'); + updatePhaseIndicator('error'); + consoleAutoHideTimer = setTimeout(() => showConsole(false), 15000); } } @@ -1084,6 +1105,108 @@ const WeatherSat = (function() { } } + // ======================== + // Decoder Console + // ======================== + + /** + * Add an entry to the decoder console log + */ + function addConsoleEntry(message, logType) { + const log = document.getElementById('wxsatConsoleLog'); + if (!log) return; + + const entry = document.createElement('div'); + entry.className = `wxsat-console-entry wxsat-log-${logType || 'info'}`; + entry.textContent = message; + log.appendChild(entry); + + consoleEntries.push(entry); + + // Cap at 200 entries + while (consoleEntries.length > 200) { + const old = consoleEntries.shift(); + if (old.parentNode) old.parentNode.removeChild(old); + } + + // Auto-scroll to bottom + log.scrollTop = log.scrollHeight; + } + + /** + * Update the phase indicator steps + */ + function updatePhaseIndicator(phase) { + if (!phase || phase === currentPhase) return; + currentPhase = phase; + + const phases = ['tuning', 'listening', 'signal_detected', 'decoding', 'complete']; + const phaseIndex = phases.indexOf(phase); + const isError = phase === 'error'; + + document.querySelectorAll('#wxsatPhaseIndicator .wxsat-phase-step').forEach(step => { + const stepPhase = step.dataset.phase; + const stepIndex = phases.indexOf(stepPhase); + + step.classList.remove('active', 'completed', 'error'); + + if (isError) { + if (stepPhase === currentPhase || stepIndex === phaseIndex) { + step.classList.add('error'); + } + } else if (stepIndex === phaseIndex) { + step.classList.add('active'); + } else if (stepIndex < phaseIndex && phaseIndex >= 0) { + step.classList.add('completed'); + } + }); + } + + /** + * Show or hide the decoder console + */ + function showConsole(visible) { + const el = document.getElementById('wxsatSignalConsole'); + if (el) el.classList.toggle('active', visible); + + if (consoleAutoHideTimer) { + clearTimeout(consoleAutoHideTimer); + consoleAutoHideTimer = null; + } + } + + /** + * Toggle console body collapsed state + */ + function toggleConsole() { + const body = document.getElementById('wxsatConsoleBody'); + const btn = document.getElementById('wxsatConsoleToggle'); + if (!body) return; + + consoleCollapsed = !consoleCollapsed; + body.classList.toggle('collapsed', consoleCollapsed); + if (btn) btn.classList.toggle('collapsed', consoleCollapsed); + } + + /** + * Clear console entries and reset phase indicator + */ + function clearConsole() { + const log = document.getElementById('wxsatConsoleLog'); + if (log) log.innerHTML = ''; + consoleEntries = []; + currentPhase = 'idle'; + + document.querySelectorAll('#wxsatPhaseIndicator .wxsat-phase-step').forEach(step => { + step.classList.remove('active', 'completed', 'error'); + }); + + if (consoleAutoHideTimer) { + clearTimeout(consoleAutoHideTimer); + consoleAutoHideTimer = null; + } + } + // Public API return { init, @@ -1098,6 +1221,7 @@ const WeatherSat = (function() { useGPS, toggleScheduler, invalidateMap, + toggleConsole, }; })(); diff --git a/templates/index.html b/templates/index.html index b75029b..e6dd575 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1970,6 +1970,33 @@ + +