diff --git a/app.py b/app.py index 5ff505f..4c23541 100644 --- a/app.py +++ b/app.py @@ -25,6 +25,7 @@ from typing import Any from flask import Flask, render_template, jsonify, send_file, Response, request +from config import VERSION from utils.dependencies import check_tool, check_all_dependencies, TOOL_DEPENDENCIES from utils.process import cleanup_stale_processes from utils.sdr import SDRFactory @@ -91,7 +92,6 @@ bt_services = {} # MAC -> list of services adsb_aircraft = {} # ICAO hex -> aircraft info # Satellite state -iridium_bursts = [] # List of detected Iridium bursts satellite_passes = [] # Predicted satellite passes @@ -107,7 +107,7 @@ def index() -> str: 'rtl_433': check_tool('rtl_433') } devices = [d.to_dict() for d in SDRFactory.detect_devices()] - return render_template('index.html', tools=tools, devices=devices) + return render_template('index.html', tools=tools, devices=devices, version=VERSION) @app.route('/favicon.svg') diff --git a/config.py b/config.py index cfbc701..ce25fd4 100644 --- a/config.py +++ b/config.py @@ -6,6 +6,9 @@ import logging import os import sys +# Application version +VERSION = "1.0.0" + def _get_env(key: str, default: str) -> str: """Get environment variable with default.""" @@ -56,10 +59,6 @@ DEFAULT_DEVICE = _get_env('DEFAULT_DEVICE', '0') # Pager defaults DEFAULT_PAGER_FREQ = _get_env('PAGER_FREQ', '929.6125M') -# Iridium defaults -DEFAULT_IRIDIUM_FREQ = _get_env('IRIDIUM_FREQ', '1626.0') -DEFAULT_IRIDIUM_SAMPLE_RATE = _get_env('IRIDIUM_SAMPLE_RATE', '2.048e6') - # Timeouts PROCESS_TIMEOUT = _get_env_int('PROCESS_TIMEOUT', 5) SOCKET_TIMEOUT = _get_env_int('SOCKET_TIMEOUT', 5) @@ -82,9 +81,6 @@ SATELLITE_UPDATE_INTERVAL = _get_env_int('SATELLITE_UPDATE_INTERVAL', 30) SATELLITE_TRAJECTORY_POINTS = _get_env_int('SATELLITE_TRAJECTORY_POINTS', 30) SATELLITE_ORBIT_MINUTES = _get_env_int('SATELLITE_ORBIT_MINUTES', 45) -# Maximum burst count for Iridium monitoring -IRIDIUM_MAX_BURSTS = _get_env_int('IRIDIUM_MAX_BURSTS', 100) - def configure_logging() -> None: """Configure application logging.""" diff --git a/docs/FEATURES.md b/docs/FEATURES.md index aa220b2..f6ac64e 100644 --- a/docs/FEATURES.md +++ b/docs/FEATURES.md @@ -41,7 +41,6 @@ Complete feature list for all modules. - **Celestrak integration** - fetch by category (Amateur, Weather, ISS, Starlink, etc.) - **Next pass countdown** - time remaining, visibility duration, max elevation - **Telemetry panel** - real-time azimuth, elevation, range, velocity -- **Iridium burst detection** monitoring (demo mode) - **Multiple satellite tracking** simultaneously ## WiFi Reconnaissance diff --git a/docs/USAGE.md b/docs/USAGE.md index 28b57b4..e5e4435 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -90,7 +90,6 @@ The system highlights aircraft transmitting emergency squawks: 4. **View Sky Plot** - Polar plot shows satellite positions in real-time 5. **Ground Track** - Map displays satellite orbit path and current position 6. **Full Dashboard** - Click "Full Screen Dashboard" for dedicated satellite view -7. **Iridium Mode** - Switch tabs to monitor for Iridium burst transmissions ### Adding Satellites from Celestrak diff --git a/intercept.py b/intercept.py index cc24c57..0498def 100755 --- a/intercept.py +++ b/intercept.py @@ -6,7 +6,7 @@ A comprehensive signal intelligence tool featuring: - Pager decoding (POCSAG/FLEX) - 433MHz sensor monitoring - ADS-B aircraft tracking with WarGames-style display -- Satellite pass prediction and Iridium burst detection +- Satellite pass prediction - WiFi reconnaissance and drone detection - Bluetooth scanning @@ -25,6 +25,12 @@ if sys.version_info < (3, 9): print(" - Or use pyenv to install a newer version") sys.exit(1) +# Handle --version early before other imports +if '--version' in sys.argv or '-V' in sys.argv: + from config import VERSION + print(f"INTERCEPT v{VERSION}") + sys.exit(0) + import site # Ensure user site-packages is available (may be disabled when running as root/sudo) diff --git a/routes/__init__.py b/routes/__init__.py index 1a3a5d3..c25ee36 100644 --- a/routes/__init__.py +++ b/routes/__init__.py @@ -8,7 +8,6 @@ def register_blueprints(app): from .bluetooth import bluetooth_bp from .adsb import adsb_bp from .satellite import satellite_bp - from .iridium import iridium_bp from .gps import gps_bp app.register_blueprint(pager_bp) @@ -17,5 +16,4 @@ def register_blueprints(app): app.register_blueprint(bluetooth_bp) app.register_blueprint(adsb_bp) app.register_blueprint(satellite_bp) - app.register_blueprint(iridium_bp) app.register_blueprint(gps_bp) diff --git a/routes/iridium.py b/routes/iridium.py deleted file mode 100644 index d219ba3..0000000 --- a/routes/iridium.py +++ /dev/null @@ -1,205 +0,0 @@ -"""Iridium monitoring routes. - -NOTE: This module is currently in DEMO MODE. The burst detection generates -simulated data for demonstration purposes. Real Iridium decoding requires -gr-iridium or iridium-toolkit which are not yet integrated. -""" - -from __future__ import annotations - -import json -import queue -import random -import shutil -import subprocess -import threading -import time -from datetime import datetime -from typing import Any, Generator - -from flask import Blueprint, jsonify, request, Response - -import app as app_module -from utils.logging import iridium_logger as logger -from utils.validation import validate_frequency, validate_device_index, validate_gain -from utils.sse import format_sse -from utils.sdr import SDRFactory, SDRType - -iridium_bp = Blueprint('iridium', __name__, url_prefix='/iridium') - -# Flag indicating this is demo mode (simulated data) -DEMO_MODE = True - - -def monitor_iridium(process): - """ - Monitor Iridium capture and detect bursts. - - NOTE: Currently generates SIMULATED data for demonstration. - Real Iridium decoding is not yet implemented. - """ - try: - burst_count = 0 - # Send initial demo mode warning - app_module.satellite_queue.put({ - 'type': 'info', - 'message': '⚠️ DEMO MODE: Generating simulated Iridium bursts for demonstration' - }) - - while process.poll() is None: - data = process.stdout.read(1024) - if data: - if len(data) > 0 and burst_count < 100: - # DEMO: Generate simulated bursts (1% chance per read) - if random.random() < 0.01: - burst = { - 'type': 'burst', - 'demo': True, # Flag as demo data - 'time': datetime.now().strftime('%H:%M:%S.%f')[:-3], - 'frequency': f"{1616 + random.random() * 10:.3f}", - 'data': f"[SIMULATED] Frame data - Burst #{burst_count + 1}" - } - app_module.satellite_queue.put(burst) - app_module.iridium_bursts.append(burst) - burst_count += 1 - - time.sleep(0.1) - except Exception as e: - logger.error(f"Monitor error: {e}") - - -@iridium_bp.route('/tools') -def check_iridium_tools(): - """Check for Iridium decoding tools.""" - has_iridium = shutil.which('iridium-extractor') is not None or shutil.which('iridium-parser') is not None - has_rtl = shutil.which('rtl_fm') is not None - return jsonify({ - 'available': has_iridium or has_rtl, - 'demo_mode': DEMO_MODE, - 'message': 'Demo mode active - generating simulated data' if DEMO_MODE else None - }) - - -@iridium_bp.route('/start', methods=['POST']) -def start_iridium(): - """Start Iridium burst capture (DEMO MODE - simulated data).""" - with app_module.satellite_lock: - if app_module.satellite_process and app_module.satellite_process.poll() is None: - return jsonify({'status': 'error', 'message': 'Iridium capture already running'}), 409 - - data = request.json or {} - - # Validate inputs - try: - freq = validate_frequency(data.get('freq', '1626.0'), min_mhz=1610.0, max_mhz=1650.0) - gain = validate_gain(data.get('gain', '40')) - device = validate_device_index(data.get('device', '0')) - except ValueError as e: - return jsonify({'status': 'error', 'message': str(e)}), 400 - - sample_rate = data.get('sampleRate', '2.048e6') - # Validate sample rate format - try: - float(sample_rate.replace('e', 'E')) - except (ValueError, AttributeError): - return jsonify({'status': 'error', 'message': 'Invalid sample rate format'}), 400 - - # Get SDR type from request - sdr_type_str = data.get('sdr_type', 'rtlsdr') - try: - sdr_type = SDRType(sdr_type_str) - except ValueError: - sdr_type = SDRType.RTL_SDR - - # Check for required tools based on SDR type - if sdr_type == SDRType.RTL_SDR: - if not shutil.which('iridium-extractor') and not shutil.which('rtl_fm'): - return jsonify({ - 'status': 'error', - 'message': 'Iridium tools not found. Requires rtl_fm or iridium-extractor.' - }), 503 - else: - if not shutil.which('rx_fm'): - return jsonify({ - 'status': 'error', - 'message': f'rx_fm not found for {sdr_type.value}. Install SoapySDR tools.' - }), 503 - - try: - # Create device object and build command via abstraction layer - sdr_device = SDRFactory.create_default_device(sdr_type, index=device) - builder = SDRFactory.get_builder(sdr_type) - - # Parse sample rate - sample_rate_hz = int(float(sample_rate)) - - # Build FM demodulation command - cmd = builder.build_fm_demod_command( - device=sdr_device, - frequency_mhz=float(freq), - sample_rate=sample_rate_hz, - gain=float(gain), - ppm=None, - modulation='fm', - squelch=None - ) - - app_module.satellite_process = subprocess.Popen( - cmd, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) - - thread = threading.Thread(target=monitor_iridium, args=(app_module.satellite_process,), daemon=True) - thread.start() - - return jsonify({ - 'status': 'started', - 'demo_mode': DEMO_MODE, - 'message': 'Demo mode active - data is simulated' if DEMO_MODE else None - }) - except FileNotFoundError as e: - logger.error(f"Tool not found: {e}") - return jsonify({'status': 'error', 'message': f'Tool not found: {e.filename}'}), 503 - except Exception as e: - logger.error(f"Start error: {e}") - return jsonify({'status': 'error', 'message': str(e)}), 500 - - -@iridium_bp.route('/stop', methods=['POST']) -def stop_iridium(): - """Stop Iridium capture.""" - with app_module.satellite_lock: - if app_module.satellite_process: - app_module.satellite_process.terminate() - try: - app_module.satellite_process.wait(timeout=5) - except subprocess.TimeoutExpired: - app_module.satellite_process.kill() - app_module.satellite_process = None - - return jsonify({'status': 'stopped'}) - - -@iridium_bp.route('/stream') -def stream_iridium(): - """SSE stream for Iridium bursts.""" - def generate(): - last_keepalive = time.time() - keepalive_interval = 30.0 - - while True: - try: - msg = app_module.satellite_queue.get(timeout=1) - last_keepalive = time.time() - yield format_sse(msg) - except queue.Empty: - now = time.time() - if now - last_keepalive >= keepalive_interval: - yield format_sse({'type': 'keepalive'}) - last_keepalive = now - - response = Response(generate(), mimetype='text/event-stream') - response.headers['Cache-Control'] = 'no-cache' - response.headers['X-Accel-Buffering'] = 'no' - return response diff --git a/static/css/index.css b/static/css/index.css index 7c3b903..40d9db7 100644 --- a/static/css/index.css +++ b/static/css/index.css @@ -96,6 +96,16 @@ header h1 { text-shadow: 0 0 30px var(--accent-cyan-dim); } +.version-badge { + font-size: 0.35em; + font-weight: 400; + letter-spacing: 1px; + color: var(--text-muted); + vertical-align: middle; + margin-left: 5px; + opacity: 0.7; +} + header p { color: var(--text-secondary); font-size: 14px; @@ -2059,46 +2069,6 @@ header p { color: var(--accent-cyan); } -/* Iridium Burst Styles */ -.iridium-warning { - padding: 12px; - margin-bottom: 15px; - background: rgba(255, 102, 0, 0.1); - border: 1px solid var(--accent-orange); - border-radius: 4px; - color: var(--accent-orange); - font-size: 12px; -} - -.iridium-warning strong { - display: block; - margin-bottom: 4px; -} - -.burst-card { - padding: 10px; - margin-bottom: 6px; - background: var(--bg-secondary); - border: 1px solid var(--border-color); - border-left: 3px solid #9370DB; - font-family: monospace; - font-size: 11px; -} - -.burst-time { - color: var(--text-secondary); -} - -.burst-freq { - color: #9370DB; - font-weight: 600; -} - -.burst-data { - color: var(--text-primary); - word-break: break-all; -} - /* Popout window styles */ .popout-container { position: fixed; diff --git a/templates/index.html b/templates/index.html index d9218c5..c071110 100644 --- a/templates/index.html +++ b/templates/index.html @@ -101,7 +101,7 @@ -

INTERCEPT

+

INTERCEPT v{{ version }}

Signal Intelligence // by smittix PAGER

@@ -814,8 +814,7 @@
Full Screen Dashboard
- - +
@@ -919,47 +918,6 @@
- -
-
- ⚠️ Hardware Required - Iridium burst detection requires:
- • RTL-SDR dongle
- • L-band patch antenna (1616-1626 MHz)
- • Low Noise Amplifier (LNA)
- • Clear view of sky -
- -
-

Iridium Settings

-
- - -
-
- - -
-
- - -
-
- -
- iridium-extractor: Checking... -
- - - -
- -
-
- Iridium bursts will appear here when detected. -
-
- - @@ -1435,7 +1378,6 @@ let isRunning = false; let isSensorRunning = false; let isAdsbRunning = false; - let isIridiumRunning = false; let isWifiRunning = false; let isBtRunning = false; let currentMode = 'pager'; @@ -1492,7 +1434,6 @@ // Satellite stats document.getElementById('headerPassCount').textContent = document.getElementById('passCount')?.textContent || '0'; - document.getElementById('headerBurstCount').textContent = document.getElementById('burstCount')?.textContent || '0'; } // Sync stats periodically setInterval(syncHeaderStats, 500); @@ -1717,8 +1658,6 @@ let satellitePasses = []; let selectedPass = null; let selectedPassIndex = 0; - let iridiumBursts = []; - let iridiumEventSource = null; let countdownInterval = null; // Start satellite countdown timer @@ -1857,7 +1796,6 @@ if (isWifiRunning) stopWifiScan(); if (isBtRunning) stopBtScan(); if (isAdsbRunning) stopAdsbScan(); - if (isIridiumRunning) stopIridiumCapture(); currentMode = mode; document.querySelectorAll('.mode-tab').forEach(tab => { @@ -1958,7 +1896,6 @@ } else if (mode === 'satellite') { initPolarPlot(); initSatelliteList(); - checkIridiumTools(); } } @@ -6234,16 +6171,6 @@ // SATELLITE MODE FUNCTIONS // ============================================ - function switchSatelliteTab(tab) { - document.querySelectorAll('.satellite-tab').forEach(t => t.classList.remove('active')); - document.querySelectorAll('.satellite-content').forEach(c => c.classList.remove('active')); - document.querySelector(`.satellite-tab:nth-child(${tab === 'predictor' ? 1 : 2})`).classList.add('active'); - document.getElementById(tab === 'predictor' ? 'predictorTab' : 'iridiumTab').classList.add('active'); - - // Toggle Iridium burst log visibility - document.getElementById('iridiumBurstLog').style.display = tab === 'iridium' ? 'block' : 'none'; - } - function getLocation() { if (navigator.geolocation) { navigator.geolocation.getCurrentPosition( @@ -7277,99 +7204,6 @@ } } - // Iridium functions - function checkIridiumTools() { - fetch('/iridium/tools') - .then(r => r.json()) - .then(data => { - const status = document.getElementById('iridiumExtractorStatus'); - status.textContent = data.available ? 'OK' : 'Not found'; - status.className = 'tool-status ' + (data.available ? 'ok' : 'missing'); - }); - } - - function startIridiumCapture() { - const freq = document.getElementById('iridiumFreq').value; - const gain = document.getElementById('iridiumGain').value; - const sampleRate = document.getElementById('iridiumSampleRate').value; - const device = getSelectedDevice(); - const sdr_type = getSelectedSDRType(); - - fetch('/iridium/start', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ freq, gain, sampleRate, device, sdr_type }) - }) - .then(r => r.json()) - .then(data => { - if (data.status === 'started') { - isIridiumRunning = true; - document.getElementById('startIridiumBtn').style.display = 'none'; - document.getElementById('stopIridiumBtn').style.display = 'block'; - document.getElementById('statusDot').className = 'status-dot active'; - document.getElementById('statusText').textContent = 'Iridium Capture'; - startIridiumStream(); - } else { - alert('Error: ' + data.message); - } - }); - } - - function stopIridiumCapture() { - fetch('/iridium/stop', { method: 'POST' }) - .then(r => r.json()) - .then(data => { - isIridiumRunning = false; - document.getElementById('startIridiumBtn').style.display = 'block'; - document.getElementById('stopIridiumBtn').style.display = 'none'; - document.getElementById('statusDot').className = 'status-dot'; - document.getElementById('statusText').textContent = 'Idle'; - if (iridiumEventSource) { - iridiumEventSource.close(); - iridiumEventSource = null; - } - }); - } - - function startIridiumStream() { - if (iridiumEventSource) iridiumEventSource.close(); - iridiumEventSource = new EventSource('/iridium/stream'); - - iridiumEventSource.onmessage = function(e) { - const data = JSON.parse(e.data); - if (data.type === 'burst') { - iridiumBursts.unshift(data); - document.getElementById('burstCount').textContent = iridiumBursts.length; - addBurstToLog(data); - } - }; - } - - function addBurstToLog(burst) { - const container = document.getElementById('burstList'); - const placeholder = container.querySelector('div[style*="color: #666"]'); - if (placeholder) placeholder.remove(); - - const card = document.createElement('div'); - card.className = 'burst-card'; - card.innerHTML = ` -
${burst.time}
-
${burst.frequency} MHz
-
${burst.data || 'No payload data'}
- `; - container.insertBefore(card, container.firstChild); - - while (container.children.length > 100) { - container.removeChild(container.lastChild); - } - } - - function clearIridiumLog() { - iridiumBursts = []; - document.getElementById('burstCount').textContent = '0'; - document.getElementById('burstList').innerHTML = '
Iridium bursts will appear here when detected.
'; - } - // Utility function function showInfo(message) { // Simple notification - could be enhanced @@ -7513,7 +7347,6 @@
  • Add satellites manually or fetch from Celestrak by category
  • Categories: Amateur, Weather, ISS, Starlink, GPS, and more
  • View next pass predictions with elevation and duration
  • -
  • Monitor for Iridium satellite bursts
  • 📶 WiFi Mode

    diff --git a/utils/__init__.py b/utils/__init__.py index 73e0717..aee0517 100644 --- a/utils/__init__.py +++ b/utils/__init__.py @@ -19,7 +19,6 @@ from .logging import ( bluetooth_logger, adsb_logger, satellite_logger, - iridium_logger, ) from .validation import ( escape_html, diff --git a/utils/dependencies.py b/utils/dependencies.py index 5f30bf3..bd5625c 100644 --- a/utils/dependencies.py +++ b/utils/dependencies.py @@ -189,18 +189,6 @@ TOOL_DEPENDENCIES = { } } }, - 'iridium': { - 'name': 'Iridium Monitoring', - 'tools': { - 'iridium-extractor': { - 'required': False, - 'description': 'Iridium burst extractor', - 'install': { - 'manual': 'https://github.com/muccc/gr-iridium' - } - } - } - }, 'sdr_hardware': { 'name': 'SDR Hardware Support', 'tools': { diff --git a/utils/logging.py b/utils/logging.py index c1e2738..c404c2d 100644 --- a/utils/logging.py +++ b/utils/logging.py @@ -27,4 +27,3 @@ wifi_logger = get_logger('intercept.wifi') bluetooth_logger = get_logger('intercept.bluetooth') adsb_logger = get_logger('intercept.adsb') satellite_logger = get_logger('intercept.satellite') -iridium_logger = get_logger('intercept.iridium') diff --git a/utils/sdr/base.py b/utils/sdr/base.py index 7f6f49a..e03c2ef 100644 --- a/utils/sdr/base.py +++ b/utils/sdr/base.py @@ -83,7 +83,7 @@ class CommandBuilder(ABC): squelch: Optional[int] = None ) -> list[str]: """ - Build FM demodulation command (for pager, iridium). + Build FM demodulation command (for pager decoding). Args: device: The SDR device to use diff --git a/utils/sdr/hackrf.py b/utils/sdr/hackrf.py index 7abcef8..cf14bbe 100644 --- a/utils/sdr/hackrf.py +++ b/utils/sdr/hackrf.py @@ -65,7 +65,7 @@ class HackRFCommandBuilder(CommandBuilder): """ Build SoapySDR rx_fm command for FM demodulation. - For pager decoding and iridium capture with HackRF. + For pager decoding with HackRF. """ device_str = self._build_device_string(device) diff --git a/utils/sdr/limesdr.py b/utils/sdr/limesdr.py index 4f8fcb2..400f343 100644 --- a/utils/sdr/limesdr.py +++ b/utils/sdr/limesdr.py @@ -46,7 +46,7 @@ class LimeSDRCommandBuilder(CommandBuilder): """ Build SoapySDR rx_fm command for FM demodulation. - For pager decoding and iridium capture with LimeSDR. + For pager decoding with LimeSDR. """ device_str = self._build_device_string(device) diff --git a/utils/sdr/rtlsdr.py b/utils/sdr/rtlsdr.py index 3c58918..a2997a2 100644 --- a/utils/sdr/rtlsdr.py +++ b/utils/sdr/rtlsdr.py @@ -40,7 +40,7 @@ class RTLSDRCommandBuilder(CommandBuilder): """ Build rtl_fm command for FM demodulation. - Used for pager decoding and iridium capture. + Used for pager decoding. """ cmd = [ 'rtl_fm',