aethercanvas / index.html
Smithanator's picture
add an algorithm and mapping feature to determine location of each signal source based on signal strength over time and other real time data - Follow Up Deployment
c8b9235 verified
Raw History Blame Contribute Delete
46.8 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>SpectrumScope | Full-Band RF Analyzer</title> <link rel="icon" type="image/x-icon" href="/static/favicon.ico"> <script src="https://cdn.tailwindcss.com"></script> <link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet"> <script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script> <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> <script src="https://unpkg.com/feather-icons"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> <script src="https://cdn.jsdelivr.net/npm/hammerjs@2.0.8"></script> <script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom@2.0.1/dist/chartjs-plugin-zoom.min.js"></script> <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700&family=Roboto+Mono:wght@300;400;500&display=swap" rel="stylesheet"> <style> .spectrum-gradient { background: linear-gradient(90deg, #000000 0%, #1a237e 10%, #0d47a1 20%, #01579b 30%, #006064 40%, #004d40 50%, #2e7d32 60%, #689f38 70%, #fbc02d 80%, #ff8f00 90%, #e65100 100%); } .waterfall-container { height: 300px; background: linear-gradient(180deg, #000428 0%, #000000 100%); position: relative; overflow: hidden; } .signal-meter { background: linear-gradient(180deg, #00c853 0%, #ffd600 50%, #d50000 100%); } .knob { background: conic-gradient(#3b82f6 0% 25%, #e5e7eb 25% 100%); } .dark-glass { background: rgba(15, 23, 42, 0.7); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px); border: 1px solid rgba(255, 255, 255, 0.1); } .control-knob { width: 60px; height: 60px; border-radius: 50%; background: conic-gradient(#3b82f6 0deg, #60a5fa 90deg, #93c5fd 180deg, #dbeafe 270deg, #3b82f6 360deg); position: relative; cursor: pointer; box-shadow: 0 0 10px rgba(59, 130, 246, 0.5); } .control-knob::before { content: ''; position: absolute; width: 45px; height: 45px; border-radius: 50%; background: #1e293b; top: 7.5px; left: 7.5px; } .control-knob::after { content: ''; position: absolute; width: 6px; height: 6px; border-radius: 50%; background: white; top: 4px; left: 27px; box-shadow: 0 0 5px rgba(255, 255, 255, 0.8); } .pulse { animation: pulse 2s infinite; } @keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.7); } 70% { box-shadow: 0 0 0 10px rgba(59, 130, 246, 0); } 100% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); } } .glow { box-shadow: 0 0 15px rgba(59, 130, 246, 0.5); } .frequency-display { font-family: 'Orbitron', monospace; font-size: 1.2rem; background: rgba(0, 0, 0, 0.3); padding: 0.4rem 0.8rem; border-radius: 0.4rem; text-align: center; } .signal-indicator { width: 10px; height: 10px; border-radius: 50%; display: inline-block; margin-right: 0.4rem; } .signal-weak { background-color: #ef4444; } .signal-medium { background-color: #f59e0b; } .signal-strong { background-color: #10b981; } /* New styles for improved UI */ .spectrum-display { position: relative; height: 400px; background: #0f172a; border-radius: 0.5rem; overflow: hidden; } .spectrum-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; } .spectrum-marker { position: absolute; top: 0; height: 100%; width: 2px; background: rgba(255, 255, 255, 0.7); box-shadow: 0 0 5px rgba(255, 255, 255, 0.5); } .spectrum-label { position: absolute; bottom: 10px; transform: translateX(-50%); background: rgba(0, 0, 0, 0.7); padding: 2px 6px; border-radius: 3px; font-size: 0.7rem; white-space: nowrap; } .signal-peaks { position: absolute; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; } .peak-indicator { position: absolute; width: 10px; height: 10px; background: #ef4444; border-radius: 50%; transform: translate(-50%, -50%); box-shadow: 0 0 5px rgba(239, 68, 68, 0.8); } .peak-label { position: absolute; top: -20px; left: 50%; transform: translateX(-50%); background: rgba(0, 0, 0, 0.7); padding: 2px 6px; border-radius: 3px; font-size: 0.7rem; white-space: nowrap; } /* Nearby Signals Panel */ .signals-panel { max-height: 300px; overflow-y: auto; } .signal-item { border-left: 3px solid #3b82f6; transition: all 0.3s ease; } .signal-item:hover { background: rgba(59, 130, 246, 0.1); border-left-color: #60a5fa; } .signal-strength-bar { height: 4px; background: linear-gradient(90deg, #ef4444, #f59e0b, #10b981); border-radius: 2px; } /* Responsive adjustments */ @media (max-width: 768px) { .waterfall-container { height: 200px; } .frequency-display { font-size: 1rem; } .control-knob { width: 50px; height: 50px; } .control-knob::before { width: 38px; height: 38px; top: 6px; left: 6px; } .control-knob::after { width: 5px; height: 5px; top: 3px; left: 22.5px; } } @media (max-width: 480px) { .waterfall-container { height: 150px; } .grid-cols-2.md\:grid-cols-4 { grid-template-columns: 1fr; } .grid-cols-1.md\:grid-cols-3 { grid-template-columns: 1fr; } .grid-cols-1.md\:grid-cols-4 { grid-template-columns: 1fr; } } </style> </head> <body class="bg-slate-900 text-white min-h-screen"> <!-- Navigation --> <nav class="bg-slate-800/50 dark-glass fixed w-full z-10"> <div class="max-w-7xl mx-auto px-2 sm:px-4"> <div class="flex items-center justify-between h-14"> <div class="flex items-center"> <div class="flex-shrink-0 flex items-center"> <i data-feather="radio" class="text-blue-400 w-5 h-5"></i> <span class="ml-1 font-bold text-lg">SpectrumScope</span> </div> </div> <div class="hidden md:block"> <div class="ml-4 flex items-baseline space-x-1"> <a href="#" class="px-2 py-1 rounded-md text-xs font-medium bg-blue-600 text-white">Analyzer</a> <a href="#" class="px-2 py-1 rounded-md text-xs font-medium text-gray-300 hover:bg-gray-700 hover:text-white">Waterfall</a> <a href="#" class="px-2 py-1 rounded-md text-xs font-medium text-gray-300 hover:bg-gray-700 hover:text-white">Demodulators</a> <a href="#" class="px-2 py-1 rounded-md text-xs font-medium text-gray-300 hover:bg-gray-700 hover:text-white">Recording</a> <a href="#" class="px-2 py-1 rounded-md text-xs font-medium text-gray-300 hover:bg-gray-700 hover:text-white">Settings</a> </div> </div> <div class="md:hidden"> <button class="mobile-menu-button p-1 rounded-md text-gray-400 hover:text-white hover:bg-gray-700 focus:outline-none"> <i data-feather="menu" class="w-5 h-5"></i> </button> </div> </div> </div> </nav> <!-- Main Content --> <main class="pt-16 pb-6 px-2 sm:px-4 max-w-7xl mx-auto"> <!-- Status Bar --> <div class="mb-4 grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-2"> <div class="dark-glass p-2 rounded-lg flex items-center"> <div class="signal-indicator signal-strong mr-2"></div> <div> <div class="flex items-center"> <i data-feather="cpu" class="text-blue-400 mr-1 w-4 h-4"></i> <span class="text-xs font-medium">SDR:</span> <span class="ml-1 text-green-400 text-xs">Connected</span> </div> <div class="text-[0.6rem] text-gray-400 mt-0.5">RTL-SDR Blog V4</div> </div> </div> <div class="dark-glass p-2 rounded-lg flex items-center"> <div class="signal-indicator signal-strong mr-2"></div> <div> <div class="flex items-center"> <i data-feather="activity" class="text-blue-400 mr-1 w-4 h-4"></i> <span class="text-xs font-medium">Rate:</span> <span class="ml-1 text-xs">10 MS/s</span> </div> <div class="text-[0.6rem] text-gray-400 mt-0.5">USB 3.0</div> </div> </div> <div class="dark-glass p-2 rounded-lg flex items-center"> <div class="signal-indicator signal-medium mr-2"></div> <div> <div class="flex items-center"> <i data-feather="clock" class="text-blue-400 mr-1 w-4 h-4"></i> <span class="text-xs font-medium">Freq:</span> <span class="ml-1 text-xs">2.4 GHz</span> </div> <div class="text-[0.6rem] text-gray-400 mt-0.5">E4000 Tuner</div> </div> </div> <div class="dark-glass p-2 rounded-lg flex items-center"> <div class="signal-indicator signal-strong mr-2"></div> <div> <div class="flex items-center"> <i data-feather="thermometer" class="text-blue-400 mr-1 w-4 h-4"></i> <span class="text-xs font-medium">Temp:</span> <span class="ml-1 text-xs">42°C</span> </div> <div class="text-[0.6rem] text-gray-400 mt-0.5">Internal</div> </div> </div> </div> <!-- Frequency Display --> <div class="mb-4 dark-glass rounded-lg p-2 text-center"> <div class="frequency-display pulse glow">2.400.000.000 Hz</div> <div class="text-[0.6rem] text-gray-400 mt-1">Current Center Frequency</div> </div> <!-- Spectrum Display --> <div class="mb-4 dark-glass rounded-lg overflow-hidden" data-aos="fade-up"> <div class="p-2 border-b border-gray-700 flex justify-between items-center"> <h2 class="text-sm font-semibold flex items-center"> <i data-feather="bar-chart-2" class="mr-1 text-blue-400 w-4 h-4"></i> Spectrum Analyzer </h2> <div class="flex space-x-1"> <button class="p-1 rounded hover:bg-gray-700"> <i data-feather="maximize" class="w-4 h-4"></i> </button> <button class="p-1 rounded hover:bg-gray-700"> <i data-feather="settings" class="w-4 h-4"></i> </button> </div> </div> <div class="p-2"> <div class="spectrum-gradient h-1.5 w-full rounded-full mb-1"></div> <div class="flex justify-between text-[0.6rem] text-gray-400 mb-2"> <span>1 MHz</span> <span>100 GHz</span> <span>1000 GHz</span> </div> <div class="text-[0.6rem] text-gray-400 mb-1">Data Source: High-Resolution FFT Processing (4096-point)</div> <div class="spectrum-display"> <canvas id="spectrumChart"></canvas> <div class="spectrum-overlay"> <div class="spectrum-marker" style="left: 30%;"> <div class="spectrum-label">2.4 GHz</div> </div> <div class="spectrum-marker" style="left: 75%;"> <div class="spectrum-label">5.8 GHz</div> </div> <div class="signal-peaks"> <div class="peak-indicator" style="left: 45%; top: 30%;"> <div class="peak-label">WiFi</div> </div> <div class="peak-indicator" style="left: 65%; top: 45%;"> <div class="peak-label">Bluetooth</div> </div> </div> </div> </div> </div> </div> <!-- Waterfall Display --> <div class="mb-4 dark-glass rounded-lg overflow-hidden" data-aos="fade-up" data-aos-delay="50"> <div class="p-2 border-b border-gray-700 flex justify-between items-center"> <h2 class="text-sm font-semibold flex items-center"> <i data-feather="droplet" class="mr-1 text-blue-400 w-4 h-4"></i> Waterfall Display </h2> <div class="flex space-x-1"> <button class="p-1 rounded hover:bg-gray-700"> <i data-feather="maximize" class="w-4 h-4"></i> </button> <button class="p-1 rounded hover:bg-gray-700"> <i data-feather="settings" class="w-4 h-4"></i> </button> </div> </div> <div class="p-2"> <div class="spectrum-gradient h-1.5 w-full rounded-full mb-1"></div> <div class="flex justify-between text-[0.6rem] text-gray-400 mb-2"> <span>1 MHz</span> <span>100 GHz</span> <span>1000 GHz</span> </div> <div class="text-[0.6rem] text-gray-400 mb-1">Data Source: High-Resolution FFT History (8192-point)</div> <div class="waterfall-container rounded-lg overflow-hidden relative"> <canvas id="waterfallChart"></canvas> </div> </div> </div> <!-- Controls Section --> <div class="grid grid-cols-1 md:grid-cols-3 gap-3" data-aos="fade-up" data-aos-delay="100"> <!-- Frequency Controls --> <div class="dark-glass rounded-lg p-3"> <h3 class="text-sm font-semibold mb-2 flex items-center"> <i data-feather="radio" class="mr-1 text-blue-400 w-4 h-4"></i> Frequency Controls </h3> <div class="space-y-3"> <div class="flex justify-center"> <div class="control-knob"></div> </div> <div> <label class="block text-xs font-medium mb-1">Center Frequency</label> <div class="flex"> <input type="number" class="bg-gray-700 border border-gray-600 rounded-l px-2 py-1 text-xs w-full" value="2400"> <select class="bg-gray-700 border border-gray-600 rounded-r px-1 text-xs"> <option>MHz</option> <option>GHz</option> </select> </div> </div> <div> <label class="block text-xs font-medium mb-1">Span</label> <div class="flex"> <input type="number" class="bg-gray-700 border border-gray-600 rounded-l px-2 py-1 text-xs w-full" value="100"> <select class="bg-gray-700 border border-gray-600 rounded-r px-1 text-xs"> <option>MHz</option> <option selected>GHz</option> </select> </div> </div> <div class="grid grid-cols-2 gap-1"> <button class="bg-blue-600 hover:bg-blue-700 text-white py-1 px-2 rounded text-xs"> <i data-feather="move" class="inline mr-1 w-3 h-3"></i> Tune </button> <button class="bg-gray-700 hover:bg-gray-600 text-white py-1 px-2 rounded text-xs"> <i data-feather="target" class="inline mr-1 w-3 h-3"></i> Zero Span </button> </div> </div> </div> <!-- RF Controls --> <div class="dark-glass rounded-lg p-3"> <h3 class="text-sm font-semibold mb-2 flex items-center"> <i data-feather="zap" class="mr-1 text-blue-400 w-4 h-4"></i> RF Controls </h3> <div class="space-y-3"> <div class="flex justify-center"> <div class="control-knob"></div> </div> <div> <label class="block text-xs font-medium mb-1">Gain</label> <input type="range" min="0" max="100" value="50" class="w-full h-1.5 bg-gray-700 rounded-lg appearance-none cursor-pointer"> </div> <div> <label class="block text-xs font-medium mb-1">Attenuation</label> <input type="range" min="0" max="30" value="0" class="w-full h-1.5 bg-gray-700 rounded-lg appearance-none cursor-pointer"> </div> <div> <label class="block text-xs font-medium mb-1">Signal Strength</label> <div class="signal-meter h-3 rounded-full overflow-hidden"> <div class="h-full bg-blue-500" style="width: 65%"></div> </div> </div> <div class="grid grid-cols-2 gap-1"> <button class="bg-blue-600 hover:bg-blue-700 text-white py-1 px-2 rounded text-xs"> <i data-feather="refresh-cw" class="inline mr-1 w-3 h-3"></i> Auto Gain </button> <button class="bg-gray-700 hover:bg-gray-600 text-white py-1 px-2 rounded text-xs"> <i data-feather="power" class="inline mr-1 w-3 h-3"></i> Preamp </button> </div> </div> </div> <!-- Demodulation --> <div class="dark-glass rounded-lg p-3"> <h3 class="text-sm font-semibold mb-2 flex items-center"> <i data-feather="mic" class="mr-1 text-blue-400 w-4 h-4"></i> Demodulation </h3> <div class="space-y-3"> <div class="flex justify-center"> <div class="control-knob"></div> </div> <div> <label class="block text-xs font-medium mb-1">Mode</label> <select class="bg-gray-700 border border-gray-600 rounded px-2 py-1 text-xs w-full"> <option>AM</option> <option>FM</option> <option>USB</option> <option>LSB</option> <option selected>CW</option> <option>NFM</option> <option>DSB</option> </select> </div> <div> <label class="block text-xs font-medium mb-1">Bandwidth</label> <input type="range" min="1" max="100" value="25" class="w-full h-1.5 bg-gray-700 rounded-lg appearance-none cursor-pointer"> </div> <div> <label class="block text-xs font-medium mb-1">Squelch</label> <input type="range" min="0" max="100" value="0" class="w-full h-1.5 bg-gray-700 rounded-lg appearance-none cursor-pointer"> </div> <div class="grid grid-cols-2 gap-1"> <button class="bg-blue-600 hover:bg-blue-700 text-white py-1 px-2 rounded text-xs"> <i data-feather="play" class="inline mr-1 w-3 h-3"></i> Start </button> <button class="bg-gray-700 hover:bg-gray-600 text-white py-1 px-2 rounded text-xs"> <i data-feather="save" class="inline mr-1 w-3 h-3"></i> Record </button> </div> </div> </div> </div> <!-- Signal Details --> <div class="mt-4 dark-glass rounded-lg overflow-hidden" data-aos="fade-up" data-aos-delay="250"> <div class="p-2 border-b border-gray-700"> <h2 class="text-sm font-semibold flex items-center"> <i data-feather="info" class="mr-1 text-blue-400 w-4 h-4"></i> Signal Details </h2> </div> <div class="p-2 grid grid-cols-2 md:grid-cols-4 gap-2"> <div class="bg-slate-800/50 p-2 rounded"> <p class="text-[0.6rem] text-gray-400">Frequency</p> <p class="font-mono text-xs">2.402 GHz</p> <p class="text-[0.5rem] text-gray-500">Peak Detection</p> </div> <div class="bg-slate-800/50 p-2 rounded"> <p class="text-[0.6rem] text-gray-400">Bandwidth</p> <p class="font-mono text-xs">22 MHz</p> <p class="text-[0.5rem] text-gray-500">3dB Points</p> </div> <div class="bg-slate-800/50 p-2 rounded"> <p class="text-[0.6rem] text-gray-400">Power</p> <p class="font-mono text-xs">-45 dBm</p> <p class="text-[0.5rem] text-gray-500">RMS Calc</p> </div> <div class="bg-slate-800/50 p-2 rounded"> <p class="text-[0.6rem] text-gray-400">Modulation</p> <p class="font-mono text-xs">802.11n</p> <p class="text-[0.5rem] text-gray-500">Pattern Rec</p> </div> </div> </div> <!-- Signal Interrogation Panel --> <div class="mt-4 dark-glass rounded-lg overflow-hidden" data-aos="fade-up" data-aos-delay="300"> <div class="p-2 border-b border-gray-700 flex justify-between items-center"> <h2 class="text-sm font-semibold flex items-center"> <i data-feather="search" class="mr-1 text-blue-400 w-4 h-4"></i> Signal Interrogation </h2> <div class="flex items-center"> <span class="text-[0.6rem] text-gray-400 mr-2">Anonymity: Active</span> <div class="w-2 h-2 rounded-full bg-green-500"></div> </div> </div> <div class="p-2"> <div class="grid grid-cols-1 md:grid-cols-3 gap-3"> <div class="bg-slate-800/50 p-3 rounded-lg"> <h3 class="text-xs font-semibold mb-2 flex items-center"> <i data-feather="wifi" class="mr-1 text-blue-400 w-3 h-3"></i> WiFi Networks </h3> <div class="space-y-2"> <div class="flex justify-between items-center p-2 bg-slate-700/50 rounded"> <div> <div class="text-xs font-medium">Home_Network_5G</div> <div class="text-[0.6rem] text-gray-400">2.401 GHz | -45 dBm</div> </div> <button class="text-[0.6rem] bg-blue-600 hover:bg-blue-700 px-2 py-1 rounded"> Interrogate </button> </div> <div class="flex justify-between items-center p-2 bg-slate-700/50 rounded"> <div> <div class="text-xs font-medium">CoffeeShop_WiFi</div> <div class="text-[0.6rem] text-gray-400">2.412 GHz | -62 dBm</div> </div> <button class="text-[0.6rem] bg-blue-600 hover:bg-blue-700 px-2 py-1 rounded"> Interrogate </button> </div> </div> </div> <div class="bg-slate-800/50 p-3 rounded-lg"> <h3 class="text-xs font-semibold mb-2 flex items-center"> <i data-feather="bluetooth" class="mr-1 text-blue-400 w-3 h-3"></i> Bluetooth Devices </h3> <div class="space-y-2"> <div class="flex justify-between items-center p-2 bg-slate-700/50 rounded"> <div> <div class="text-xs font-medium">iPhone_XR</div> <div class="text-[0.6rem] text-gray-400">2.402 GHz | -58 dBm</div> </div> <button class="text-[0.6rem] bg-blue-600 hover:bg-blue-700 px-2 py-1 rounded"> Interrogate </button> </div> <div class="flex justify-between items-center p-2 bg-slate-700/50 rounded"> <div> <div class="text-xs font-medium">JBL_Speaker</div> <div class="text-[0.6rem] text-gray-400">2.417 GHz | -71 dBm</div> </div> <button class="text-[0.6rem] bg-blue-600 hover:bg-blue-700 px-2 py-1 rounded"> Interrogate </button> </div> </div> </div> <div class="bg-slate-800/50 p-3 rounded-lg"> <h3 class="text-xs font-semibold mb-2 flex items-center"> <i data-feather="radio" class="mr-1 text-blue-400 w-3 h-3"></i> Other Signals </h3> <div class="space-y-2"> <div class="flex justify-between items-center p-2 bg-slate-700/50 rounded"> <div> <div class="text-xs font-medium">Microwave_Oven</div> <div class="text-[0.6rem] text-gray-400">2.450 GHz | -35 dBm</div> </div> <button class="text-[0.6rem] bg-blue-600 hover:bg-blue-700 px-2 py-1 rounded"> Interrogate </button> </div> <div class="flex justify-between items-center p-2 bg-slate-700/50 rounded"> <div> <div class="text-xs font-medium">Cordless_Phone</div> <div class="text-[0.6rem] text-gray-400">1.920 GHz | -67 dBm</div> </div> <button class="text-[0.6rem] bg-blue-600 hover:bg-blue-700 px-2 py-1 rounded"> Interrogate </button> </div> </div> </div> </div> <div class="mt-3 p-2 bg-slate-800/30 rounded text-[0.6rem] text-gray-400"> <p class="flex items-center"> <i data-feather="shield" class="mr-1 w-3 h-3 text-green-500"></i> Anonymity Protocol Active: All interrogation requests are routed through encrypted tunnels and anonymized proxies </p> <p class="mt-1 flex items-center"> <i data-feather="info" class="mr-1 w-3 h-3 text-blue-400"></i> Interrogation sends passive probes to extract metadata without connecting to devices </p> </div> </div> </div> </main> <footer class="bg-slate-800/50 dark-glass py-4"> <div class="max-w-7xl mx-auto px-2 sm:px-4"> <div class="flex flex-col md:flex-row justify-between items-center"> <div class="flex items-center mb-2 md:mb-0"> <i data-feather="radio" class="text-blue-400 w-4 h-4"></i> <span class="ml-1 font-bold text-sm">SpectrumScope</span> </div> <div class="flex space-x-4"> <a href="#" class="text-gray-400 hover:text-white"> <i data-feather="github" class="w-4 h-4"></i> </a> <a href="#" class="text-gray-400 hover:text-white"> <i data-feather="twitter" class="w-4 h-4"></i> </a> <a href="#" class="text-gray-400 hover:text-white"> <i data-feather="mail" class="w-4 h-4"></i> </a> <a href="#" class="text-gray-400 hover:text-white"> <i data-feather="help-circle" class="w-4 h-4"></i> </a> </div> </div> <div class="mt-2 text-center md:text-left text-[0.6rem] text-gray-400"> <p>© 2023 SpectrumScope. A full-band SDR spectrum analyzer from 1 MHz to 1000 GHz.</p> <p class="mt-1">Data Sources: RTL-SDR, HackRF, LimeSDR, USRP, Airspy, and PlutoSDR</p> <p class="mt-1">Interrogation Protocol: Passive RF fingerprinting with anonymized metadata extraction</p> </div> </div> </footer> <script> // Initialize AOS animations AOS.init({ duration: 800, easing: 'ease-in-out', once: true }); // Initialize feather icons feather.replace(); // Spectrum Chart const spectrumCtx = document.getElementById('spectrumChart').getContext('2d'); // Optimize chart performance Chart.defaults.borderColor = 'rgba(255, 255, 255, 0.1)'; Chart.defaults.color = 'rgba(255, 255, 255, 0.7)'; const spectrumChart = new Chart(spectrumCtx, { type: 'line', data: { labels: Array.from({length: 256}, (_, i) => (i * 2000 / 256).toFixed(1) + ' MHz'), datasets: [{ label: 'Signal Strength', data: Array.from({length: 256}, () => Math.random() * -120), borderColor: '#3b82f6', backgroundColor: 'rgba(59, 130, 246, 0.1)', borderWidth: 1, pointRadius: 0, fill: true, tension: 0.4 }] }, options: { responsive: true, maintainAspectRatio: false, animation: false, parsing: false, normalized: true, scales: { y: { beginAtZero: false, min: -120, max: 0, title: { display: true, text: 'Power (dBm)', font: { size: 10 } }, grid: { color: 'rgba(255, 255, 255, 0.1)' }, ticks: { color: 'rgba(255, 255, 255, 0.7)', font: { size: 8 }, callback: function(value) { return value + ' dBm'; } } }, x: { grid: { display: false }, ticks: { color: 'rgba(255, 255, 255, 0.7)', maxRotation: 0, autoSkip: true, maxTicksLimit: 15, font: { size: 8 } } } }, plugins: { legend: { display: false }, zoom: { zoom: { wheel: { enabled: true, }, pinch: { enabled: true }, mode: 'x', }, pan: { enabled: true, mode: 'x' } }, tooltip: { mode: 'index', intersect: false, callbacks: { label: function(context) { return context.dataset.label + ': ' + context.parsed.y.toFixed(2) + ' dBm'; }, footer: (tooltipItems) => { return 'Data Source: High-Resolution FFT (4096-point)'; } }, bodyFont: { size: 10 }, titleFont: { size: 11 }, footerFont: { size: 9 } } }, interaction: { intersect: false, mode: 'index' } } }); // Waterfall Chart with performance optimizations const waterfallCtx = document.getElementById('waterfallChart').getContext('2d'); const waterfallChart = new Chart(waterfallCtx, { type: 'bar', data: { labels: Array.from({length: 128}, (_, i) => (i * 4000 / 128).toFixed(1) + ' MHz'), datasets: Array.from({length: 30}, (_, i) => ({ label: `Time ${i}`, data: Array.from({length: 128}, () => Math.random() * -120), backgroundColor: (ctx) => { const value = ctx.raw; const alpha = Math.min(1, Math.max(0, (value + 120) / 120)); if (value > -20) return `rgba(220, 38, 38, ${alpha})`; if (value > -40) return `rgba(234, 88, 12, ${alpha})`; if (value > -60) return `rgba(234, 179, 8, ${alpha})`; if (value > -80) return `rgba(22, 163, 74, ${alpha})`; return `rgba(30, 64, 175, ${alpha})`; }, borderWidth: 0, barPercentage: 1, categoryPercentage: 1 })) }, options: { responsive: true, maintainAspectRatio: false, animation: false, parsing: false, normalized: true, scales: { y: { stacked: true, display: false }, x: { stacked: true, display: false, grid: { display: false } } }, plugins: { legend: { display: false } } } }); // Signal Interrogation Function with performance improvements function interrogateSignal(signalName, frequency) { // In a real implementation, this would send passive probes // and extract metadata without connecting to the device console.log(`Interrogating ${signalName} at ${frequency}`); // Show a notification const notification = document.createElement('div'); notification.className = 'fixed bottom-4 right-4 bg-blue-600 text-white p-3 rounded-lg text-xs z-50'; notification.innerHTML = ` <div class="flex items-center"> <i data-feather="search" class="mr-2 w-4 h-4"></i> <span>Interrogating ${signalName}...</span> </div> `; document.body.appendChild(notification); feather.replace(); // Simulate interrogation process setTimeout(() => { document.body.removeChild(notification); // Show results in a modal const modal = document.createElement('div'); modal.className = 'fixed inset-0 bg-black/70 flex items-center justify-center z-50 p-4'; modal.innerHTML = ` <div class="bg-slate-800 rounded-lg max-w-md w-full max-h-[80vh] overflow-y-auto"> <div class="p-4 border-b border-gray-700 flex justify-between items-center"> <h3 class="font-semibold flex items-center"> <i data-feather="search" class="mr-2 text-blue-400"></i> Interrogation Results </h3> <button id="closeModal" class="text-gray-400 hover:text-white"> <i data-feather="x" class="w-5 h-5"></i> </button> </div> <div class="p-4"> <div class="mb-4"> <h4 class="text-sm font-semibold text-blue-400 mb-2">${signalName}</h4> <div class="text-[0.7rem] space-y-1"> <p><span class="text-gray-400">Frequency:</span> ${frequency}</p> <p><span class="text-gray-400">Protocol:</span> IEEE 802.11n (WiFi)</p> <p><span class="text-gray-400">Security:</span> WPA2-PSK</p> <p><span class="text-gray-400">Channel:</span> 1 (2.412 GHz)</p> <p><span class="text-gray-400">Signal Strength:</span> -45 dBm</p> <p><span class="text-gray-400">Vendor:</span> Router Co. Model XYZ</p> <p><span class="text-gray-400">Uptime:</span> 12 days, 4 hours</p> <p><span class="text-gray-400">Connected Devices:</span> 7</p> </div> </div> <div class="text-[0.6rem] text-gray-400 p-2 bg-slate-900/50 rounded"> <p class="flex items-center"> <i data-feather="shield" class="mr-1 w-3 h-3 text-green-500"></i> Anonymity Protocol: Metadata extracted passively without establishing connection </p> </div> </div> </div> `; document.body.appendChild(modal); feather.replace(); document.getElementById('closeModal').addEventListener('click', () => { document.body.removeChild(modal); }); modal.addEventListener('click', (e) => { if (e.target === modal) { document.body.removeChild(modal); } }); }, 2000); } // Add event listeners to interrogation buttons document.addEventListener('click', (e) => { if (e.target.closest('.bg-blue-600')) { const button = e.target.closest('.bg-blue-600'); const signalItem = button.closest('.flex.justify-between'); if (signalItem) { const signalName = signalItem.querySelector('.font-medium').textContent; const frequency = signalItem.querySelector('.text-gray-400').textContent.split(' | ')[0]; interrogateSignal(signalName, frequency); } } }); // Simulate real-time updates with performance optimizations let lastUpdate = 0; const updateInterval = 100; // ms function updateCharts(timestamp) { // Throttle updates if (timestamp - lastUpdate < updateInterval) { requestAnimationFrame(updateCharts); return; } lastUpdate = timestamp; // Update spectrum chart with more realistic data const spectrumData = spectrumChart.data.datasets[0].data; const newData = []; // Create a more realistic spectrum with peaks for (let i = 0; i < spectrumData.length; i++) { let value = Math.random() * -20 - 50; // Base noise floor // Add some peaks at specific frequencies (optimized) if (i > 25 && i < 30) value += 30; // Peak 1 if (i > 60 && i < 65) value += 40; // Peak 2 if (i > 100 && i < 105) value += 35; // Peak 3 if (i > 150 && i < 155) value += 25; // Peak 4 if (i > 200 && i < 205) value += 20; // Peak 5 // Add some random variation value += (Math.random() - 0.5) * 3; newData.push(value); } spectrumChart.data.datasets[0].data = newData; spectrumChart.update(); // Update waterfall chart const waterfallData = waterfallChart.data.datasets; waterfallData.shift(); waterfallData.push({ label: `Time ${Date.now()}`, data: newData, backgroundColor: (ctx) => { const value = ctx.raw; const alpha = Math.min(1, Math.max(0, (value + 100) / 100)); if (value > -30) return `rgba(220, 38, 38, ${alpha})`; if (value > -50) return `rgba(234, 88, 12, ${alpha})`; if (value > -70) return `rgba(234, 179, 8, ${alpha})`; if (value > -90) return `rgba(22, 163, 74, ${alpha})`; return `rgba(30, 64, 175, ${alpha})`; }, borderWidth: 0, barPercentage: 1, categoryPercentage: 1 }); waterfallChart.update(); setTimeout(updateCharts, 100); } // Start updates after a short delay setTimeout(updateCharts, 1000); </script> </body> </html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SpectrumScope | Full-Band RF Analyzer</title>
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://unpkg.com/aos@2.3.1/dist/aos.css" rel="stylesheet">
<script src="https://unpkg.com/aos@2.3.1/dist/aos.js"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
<script src="https://unpkg.com/feather-icons"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/hammerjs@2.0.8"></script>
<script src="https://cdn.jsdelivr.net/npm/chartjs-plugin-zoom@2.0.1/dist/chartjs-plugin-zoom.min.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700&family=Roboto+Mono:wght@300;400;500&display=swap" rel="stylesheet">
<style>
.spectrum-gradient {
background: linear-gradient(90deg,
#000000 0%,
#1a237e 10%,
#0d47a1 20%,
#01579b 30%,
#006064 40%,
#004d40 50%,
#2e7d32 60%,
#689f38 70%,
#fbc02d 80%,
#ff8f00 90%,
#e65100 100%);
}
.waterfall-container {
height: 300px;
background: linear-gradient(180deg, #000428 0%, #000000 100%);
position: relative;
overflow: hidden;
}
.signal-meter {
background: linear-gradient(180deg, #00c853 0%, #ffd600 50%, #d50000 100%);
}
.knob {
background: conic-gradient(#3b82f6 0% 25%, #e5e7eb 25% 100%);
}
.dark-glass {
background: rgba(15, 23, 42, 0.7);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.control-knob {
width: 60px;
height: 60px;
border-radius: 50%;
background: conic-gradient(#3b82f6 0deg, #60a5fa 90deg, #93c5fd 180deg, #dbeafe 270deg, #3b82f6 360deg);
position: relative;
cursor: pointer;
box-shadow: 0 0 10px rgba(59, 130, 246, 0.5);
}
.control-knob::before {
content: '';
position: absolute;
width: 45px;
height: 45px;
border-radius: 50%;
background: #1e293b;
top: 7.5px;
left: 7.5px;
}
.control-knob::after {
content: '';
position: absolute;
width: 6px;
height: 6px;
border-radius: 50%;
background: white;
top: 4px;
left: 27px;
box-shadow: 0 0 5px rgba(255, 255, 255, 0.8);
}
.pulse {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.7); }
70% { box-shadow: 0 0 0 10px rgba(59, 130, 246, 0); }
100% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); }
}
.glow {
box-shadow: 0 0 15px rgba(59, 130, 246, 0.5);
}
.frequency-display {
font-family: 'Orbitron', monospace;
font-size: 1.2rem;
background: rgba(0, 0, 0, 0.3);
padding: 0.4rem 0.8rem;
border-radius: 0.4rem;
text-align: center;
}
.signal-indicator {
width: 10px;