diff --git a/intercept_agent.py b/intercept_agent.py index beabeaf..381c82f 100644 --- a/intercept_agent.py +++ b/intercept_agent.py @@ -2357,6 +2357,37 @@ class ModeManager: # ACARS MODE (acarsdec) # ------------------------------------------------------------------------- + def _detect_acarsdec_fork(self, acarsdec_path: str) -> str: + """Detect which acarsdec fork is installed. + + Returns: + '--output' for f00b4r0 fork (DragonOS) + '-j' for TLeconte v4+ + '-o' for TLeconte v3.x + """ + try: + result = subprocess.run( + [acarsdec_path], + capture_output=True, + text=True, + timeout=5 + ) + output = result.stdout + result.stderr + + # f00b4r0 fork uses --output instead of -j/-o + if '--output' in output: + return '--output' + + # Parse version for TLeconte + import re + version_match = re.search(r'acarsdec[^\d]*v?(\d+)\.(\d+)', output, re.IGNORECASE) + if version_match: + major = int(version_match.group(1)) + return '-j' if major >= 4 else '-o' + except Exception: + pass + return '-j' # Default to TLeconte v4+ + def _start_acars(self, params: dict) -> dict: """Start ACARS decoding using acarsdec.""" gain = params.get('gain', '40') @@ -2367,10 +2398,24 @@ class ModeManager: if not acarsdec_path: return {'status': 'error', 'message': 'acarsdec not found. Install acarsdec.'} - # Build command with JSON output - cmd = [acarsdec_path, '-j', '-r', str(device), '-g', str(gain)] - for freq in frequencies: - cmd.append(freq) + # Detect fork and build appropriate command + fork_type = self._detect_acarsdec_fork(acarsdec_path) + cmd = [acarsdec_path] + + if fork_type == '--output': + # f00b4r0 fork (DragonOS): different syntax + cmd.extend(['--output', 'json:file']) # stdout + cmd.extend(['-g', str(gain)]) + cmd.extend(['-m', '256']) # 3.2 MS/s for wider bandwidth + cmd.extend(['--rtlsdr', str(device)]) + elif fork_type == '-j': + # TLeconte v4+ + cmd.extend(['-j', '-g', str(gain), '-r', str(device)]) + else: + # TLeconte v3.x + cmd.extend(['-o', '4', '-g', str(gain), '-r', str(device)]) + + cmd.extend(frequencies) logger.info(f"Starting acarsdec: {' '.join(cmd)}") diff --git a/static/js/core/agents.js b/static/js/core/agents.js index 5c073aa..c29779f 100644 --- a/static/js/core/agents.js +++ b/static/js/core/agents.js @@ -678,7 +678,8 @@ function syncModeUI(mode, isRunning, agentId = null) { 'pager': 'setPagerRunning', 'adsb': 'setADSBRunning', 'wifi': 'setWiFiRunning', - 'bluetooth': 'setBluetoothRunning' + 'bluetooth': 'setBluetoothRunning', + 'acars': 'setAcarsRunning' }; const setterName = uiSetters[mode]; diff --git a/templates/adsb_dashboard.html b/templates/adsb_dashboard.html index 4a4b77b..119f6b2 100644 --- a/templates/adsb_dashboard.html +++ b/templates/adsb_dashboard.html @@ -3763,6 +3763,30 @@ sudo make install }); } + // Sync ACARS UI state (called by syncModeUI in agents.js) + function setAcarsRunning(running, agentId = null) { + isAcarsRunning = running; + const btn = document.getElementById('acarsToggleBtn'); + const indicator = document.getElementById('acarsPanelIndicator'); + + if (running) { + acarsCurrentAgent = agentId; + btn.textContent = '■ STOP ACARS'; + btn.classList.add('active'); + if (indicator) indicator.classList.add('active'); + // Start stream if not already running + if (!acarsEventSource && !acarsPollTimer) { + startAcarsStream(agentId !== null); + } + } else { + btn.textContent = '▶ START ACARS'; + btn.classList.remove('active'); + if (indicator) indicator.classList.remove('active'); + } + } + // Expose to global scope for syncModeUI + window.setAcarsRunning = setAcarsRunning; + function startAcarsStream(isAgentMode = false) { if (acarsEventSource) acarsEventSource.close();