Spaces:
Running
Running
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 Download index.html from Smithanator/aethercanvas: direct link, hf CLI and curl.
- Browser
- Download file 46.8 kB
-
https://huggingface.co/spaces/Smithanator/aethercanvas/resolve/main/index.html
- Command line
-
hf download hf://spaces/Smithanator/aethercanvas/index.html
-
curl -L -o index.html https://huggingface.co/spaces/Smithanator/aethercanvas/resolve/main/index.html
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>
<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;