Update index.html
Browse files- index.html +101 -107
index.html
CHANGED
|
@@ -3,6 +3,8 @@
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<title>Real SDR Network Monitor</title>
|
|
|
|
|
|
|
| 6 |
<style>
|
| 7 |
body {
|
| 8 |
margin: 0;
|
|
@@ -25,6 +27,7 @@
|
|
| 25 |
border-radius: 8px;
|
| 26 |
height: calc(100vh - 40px);
|
| 27 |
overflow-y: auto;
|
|
|
|
| 28 |
}
|
| 29 |
|
| 30 |
.receiver {
|
|
@@ -65,9 +68,9 @@
|
|
| 65 |
}
|
| 66 |
|
| 67 |
#map {
|
| 68 |
-
background: #111;
|
| 69 |
-
border-radius: 8px;
|
| 70 |
height: calc(100vh - 40px);
|
|
|
|
|
|
|
| 71 |
}
|
| 72 |
|
| 73 |
.signal-strength {
|
|
@@ -91,13 +94,37 @@
|
|
| 91 |
border-left: 2px solid #0f0;
|
| 92 |
}
|
| 93 |
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 101 |
}
|
| 102 |
</style>
|
| 103 |
</head>
|
|
@@ -111,11 +138,13 @@
|
|
| 111 |
<div id="detections"></div>
|
| 112 |
</div>
|
| 113 |
|
| 114 |
-
<
|
| 115 |
</div>
|
| 116 |
|
|
|
|
|
|
|
| 117 |
<script>
|
| 118 |
-
//
|
| 119 |
const sdrStations = [
|
| 120 |
{
|
| 121 |
name: "Twente WebSDR",
|
|
@@ -144,7 +173,7 @@
|
|
| 144 |
{
|
| 145 |
name: "KiwiSDR Switzerland",
|
| 146 |
url: "hb9ryz.no-ip.org:8073",
|
| 147 |
-
location: [47.3769, 8.5417],
|
| 148 |
frequency: "0-30 MHz",
|
| 149 |
range: 160,
|
| 150 |
active: true
|
|
@@ -153,21 +182,45 @@
|
|
| 153 |
|
| 154 |
class RadarSystem {
|
| 155 |
constructor() {
|
| 156 |
-
this.canvas = document.getElementById('map');
|
| 157 |
-
this.ctx = this.canvas.getContext('2d');
|
| 158 |
this.targets = new Set();
|
| 159 |
-
this.
|
|
|
|
| 160 |
this.renderReceivers();
|
| 161 |
this.startTracking();
|
| 162 |
}
|
| 163 |
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
this.
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
});
|
| 172 |
}
|
| 173 |
|
|
@@ -190,17 +243,6 @@
|
|
| 190 |
`).join('');
|
| 191 |
}
|
| 192 |
|
| 193 |
-
latLongToXY(lat, lon) {
|
| 194 |
-
const centerLat = 51.5;
|
| 195 |
-
const centerLon = 5.0;
|
| 196 |
-
const scale = 100;
|
| 197 |
-
|
| 198 |
-
const x = (lon - centerLon) * scale + this.canvas.width/2;
|
| 199 |
-
const y = (centerLat - lat) * scale + this.canvas.height/2;
|
| 200 |
-
|
| 201 |
-
return {x, y};
|
| 202 |
-
}
|
| 203 |
-
|
| 204 |
generateTarget() {
|
| 205 |
return {
|
| 206 |
type: Math.random() > 0.7 ? 'aircraft' : 'vehicle',
|
|
@@ -216,82 +258,36 @@
|
|
| 216 |
};
|
| 217 |
}
|
| 218 |
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
this.
|
| 222 |
-
|
| 223 |
-
// Draw grid
|
| 224 |
-
this.ctx.strokeStyle = '#1a1a1a';
|
| 225 |
-
this.ctx.lineWidth = 1;
|
| 226 |
-
|
| 227 |
-
for(let i = 0; i < this.canvas.width; i += 50) {
|
| 228 |
-
this.ctx.beginPath();
|
| 229 |
-
this.ctx.moveTo(i, 0);
|
| 230 |
-
this.ctx.lineTo(i, this.canvas.height);
|
| 231 |
-
this.ctx.stroke();
|
| 232 |
-
}
|
| 233 |
-
|
| 234 |
-
for(let i = 0; i < this.canvas.height; i += 50) {
|
| 235 |
-
this.ctx.beginPath();
|
| 236 |
-
this.ctx.moveTo(0, i);
|
| 237 |
-
this.ctx.lineTo(this.canvas.width, i);
|
| 238 |
-
this.ctx.stroke();
|
| 239 |
-
}
|
| 240 |
-
}
|
| 241 |
|
| 242 |
-
|
| 243 |
-
sdrStations.forEach(station => {
|
| 244 |
-
const pos = this.latLongToXY(station.location[0], station.location[1]);
|
| 245 |
-
|
| 246 |
-
// Draw coverage radius
|
| 247 |
-
this.ctx.beginPath();
|
| 248 |
-
this.ctx.arc(pos.x, pos.y, station.range, 0, Math.PI * 2);
|
| 249 |
-
this.ctx.strokeStyle = `rgba(0,255,0,${station.active ? 0.2 : 0.1})`;
|
| 250 |
-
this.ctx.stroke();
|
| 251 |
-
|
| 252 |
-
// Draw station point
|
| 253 |
-
this.ctx.beginPath();
|
| 254 |
-
this.ctx.arc(pos.x, pos.y, 4, 0, Math.PI * 2);
|
| 255 |
-
this.ctx.fillStyle = station.active ? '#0f0' : '#f00';
|
| 256 |
-
this.ctx.fill();
|
| 257 |
-
|
| 258 |
-
// Draw station label
|
| 259 |
-
this.ctx.fillStyle = '#0f0';
|
| 260 |
-
this.ctx.font = '10px monospace';
|
| 261 |
-
this.ctx.fillText(station.name, pos.x + 10, pos.y + 4);
|
| 262 |
-
});
|
| 263 |
-
}
|
| 264 |
-
|
| 265 |
-
drawTargets() {
|
| 266 |
this.targets.forEach(target => {
|
| 267 |
-
const
|
| 268 |
-
|
| 269 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 270 |
sdrStations.forEach(station => {
|
| 271 |
-
if(station.active) {
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
|
|
|
|
|
|
| 278 |
}
|
| 279 |
});
|
| 280 |
-
|
| 281 |
-
// Draw target
|
| 282 |
-
this.ctx.beginPath();
|
| 283 |
-
this.ctx.arc(pos.x, pos.y, 3, 0, Math.PI * 2);
|
| 284 |
-
this.ctx.fillStyle = target.type === 'aircraft' ? '#ff0' : '#0ff';
|
| 285 |
-
this.ctx.fill();
|
| 286 |
-
|
| 287 |
-
// Draw target info
|
| 288 |
-
this.ctx.fillStyle = '#666';
|
| 289 |
-
this.ctx.font = '10px monospace';
|
| 290 |
-
this.ctx.fillText(
|
| 291 |
-
`${target.id} • ${target.speed.toFixed(0)}kts • ${target.altitude.toFixed(0)}ft`,
|
| 292 |
-
pos.x + 10,
|
| 293 |
-
pos.y + 4
|
| 294 |
-
);
|
| 295 |
});
|
| 296 |
}
|
| 297 |
|
|
@@ -329,9 +325,7 @@
|
|
| 329 |
this.targets.delete(Array.from(this.targets)[0]);
|
| 330 |
}
|
| 331 |
|
| 332 |
-
this.
|
| 333 |
-
this.drawStations();
|
| 334 |
-
this.drawTargets();
|
| 335 |
this.updateDetections();
|
| 336 |
this.updateSignalStrengths();
|
| 337 |
}, 100);
|
|
|
|
| 3 |
<head>
|
| 4 |
<meta charset="UTF-8">
|
| 5 |
<title>Real SDR Network Monitor</title>
|
| 6 |
+
<!-- Add Leaflet CSS -->
|
| 7 |
+
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.css" />
|
| 8 |
<style>
|
| 9 |
body {
|
| 10 |
margin: 0;
|
|
|
|
| 27 |
border-radius: 8px;
|
| 28 |
height: calc(100vh - 40px);
|
| 29 |
overflow-y: auto;
|
| 30 |
+
z-index: 1000;
|
| 31 |
}
|
| 32 |
|
| 33 |
.receiver {
|
|
|
|
| 68 |
}
|
| 69 |
|
| 70 |
#map {
|
|
|
|
|
|
|
| 71 |
height: calc(100vh - 40px);
|
| 72 |
+
border-radius: 8px;
|
| 73 |
+
background: #111;
|
| 74 |
}
|
| 75 |
|
| 76 |
.signal-strength {
|
|
|
|
| 94 |
border-left: 2px solid #0f0;
|
| 95 |
}
|
| 96 |
|
| 97 |
+
/* Custom Leaflet styles */
|
| 98 |
+
.leaflet-tile-pane {
|
| 99 |
+
filter: invert(1) hue-rotate(180deg);
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
.leaflet-container {
|
| 103 |
+
background: #111 !important;
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
/* Style the station and target markers */
|
| 107 |
+
.station-marker {
|
| 108 |
+
border: 2px solid #0f0;
|
| 109 |
+
border-radius: 50%;
|
| 110 |
+
width: 8px;
|
| 111 |
+
height: 8px;
|
| 112 |
+
background: transparent;
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
.station-range {
|
| 116 |
+
stroke: #0f0;
|
| 117 |
+
stroke-width: 1;
|
| 118 |
+
fill: #0f0;
|
| 119 |
+
fill-opacity: 0.1;
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
.target-marker {
|
| 123 |
+
width: 6px;
|
| 124 |
+
height: 6px;
|
| 125 |
+
background: #ff0;
|
| 126 |
+
border-radius: 50%;
|
| 127 |
+
border: 1px solid #fff;
|
| 128 |
}
|
| 129 |
</style>
|
| 130 |
</head>
|
|
|
|
| 138 |
<div id="detections"></div>
|
| 139 |
</div>
|
| 140 |
|
| 141 |
+
<div id="map"></div>
|
| 142 |
</div>
|
| 143 |
|
| 144 |
+
<!-- Add Leaflet JS -->
|
| 145 |
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.js"></script>
|
| 146 |
<script>
|
| 147 |
+
// SDR stations data
|
| 148 |
const sdrStations = [
|
| 149 |
{
|
| 150 |
name: "Twente WebSDR",
|
|
|
|
| 173 |
{
|
| 174 |
name: "KiwiSDR Switzerland",
|
| 175 |
url: "hb9ryz.no-ip.org:8073",
|
| 176 |
+
location: [47.3769, 8.5417],
|
| 177 |
frequency: "0-30 MHz",
|
| 178 |
range: 160,
|
| 179 |
active: true
|
|
|
|
| 182 |
|
| 183 |
class RadarSystem {
|
| 184 |
constructor() {
|
|
|
|
|
|
|
| 185 |
this.targets = new Set();
|
| 186 |
+
this.markers = new Map();
|
| 187 |
+
this.initializeMap();
|
| 188 |
this.renderReceivers();
|
| 189 |
this.startTracking();
|
| 190 |
}
|
| 191 |
|
| 192 |
+
initializeMap() {
|
| 193 |
+
// Initialize the map centered on Europe
|
| 194 |
+
this.map = L.map('map', {
|
| 195 |
+
center: [51.5, 5.0],
|
| 196 |
+
zoom: 6,
|
| 197 |
+
preferCanvas: true
|
| 198 |
+
});
|
| 199 |
+
|
| 200 |
+
// Add dark-themed map tiles
|
| 201 |
+
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
| 202 |
+
maxZoom: 19,
|
| 203 |
+
attribution: '© OpenStreetMap contributors'
|
| 204 |
+
}).addTo(this.map);
|
| 205 |
+
|
| 206 |
+
// Add SDR stations to the map
|
| 207 |
+
sdrStations.forEach(station => {
|
| 208 |
+
// Add station marker
|
| 209 |
+
const marker = L.circleMarker(station.location, {
|
| 210 |
+
radius: 5,
|
| 211 |
+
color: '#0f0',
|
| 212 |
+
fillColor: '#0f0',
|
| 213 |
+
fillOpacity: 1
|
| 214 |
+
}).addTo(this.map);
|
| 215 |
+
|
| 216 |
+
// Add range circle
|
| 217 |
+
const range = L.circle(station.location, {
|
| 218 |
+
radius: station.range * 1000,
|
| 219 |
+
className: 'station-range'
|
| 220 |
+
}).addTo(this.map);
|
| 221 |
+
|
| 222 |
+
// Add tooltip
|
| 223 |
+
marker.bindTooltip(station.name);
|
| 224 |
});
|
| 225 |
}
|
| 226 |
|
|
|
|
| 243 |
`).join('');
|
| 244 |
}
|
| 245 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 246 |
generateTarget() {
|
| 247 |
return {
|
| 248 |
type: Math.random() > 0.7 ? 'aircraft' : 'vehicle',
|
|
|
|
| 258 |
};
|
| 259 |
}
|
| 260 |
|
| 261 |
+
updateTargets() {
|
| 262 |
+
// Update existing markers
|
| 263 |
+
this.markers.forEach(marker => this.map.removeLayer(marker));
|
| 264 |
+
this.markers.clear();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 265 |
|
| 266 |
+
// Add new markers for current targets
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 267 |
this.targets.forEach(target => {
|
| 268 |
+
const marker = L.circleMarker([target.position.lat, target.position.lon], {
|
| 269 |
+
radius: 3,
|
| 270 |
+
color: target.type === 'aircraft' ? '#ff0' : '#0ff',
|
| 271 |
+
fillColor: target.type === 'aircraft' ? '#ff0' : '#0ff',
|
| 272 |
+
fillOpacity: 1
|
| 273 |
+
}).addTo(this.map);
|
| 274 |
+
|
| 275 |
+
marker.bindTooltip(`${target.id} • ${target.speed.toFixed(0)}kts • ${target.altitude.toFixed(0)}ft`);
|
| 276 |
+
this.markers.set(target.id, marker);
|
| 277 |
+
|
| 278 |
+
// Draw signal lines to active stations
|
| 279 |
sdrStations.forEach(station => {
|
| 280 |
+
if (station.active) {
|
| 281 |
+
L.polyline([
|
| 282 |
+
[target.position.lat, target.position.lon],
|
| 283 |
+
station.location
|
| 284 |
+
], {
|
| 285 |
+
color: '#0f0',
|
| 286 |
+
opacity: target.signalStrength * 0.3,
|
| 287 |
+
weight: 1
|
| 288 |
+
}).addTo(this.map);
|
| 289 |
}
|
| 290 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 291 |
});
|
| 292 |
}
|
| 293 |
|
|
|
|
| 325 |
this.targets.delete(Array.from(this.targets)[0]);
|
| 326 |
}
|
| 327 |
|
| 328 |
+
this.updateTargets();
|
|
|
|
|
|
|
| 329 |
this.updateDetections();
|
| 330 |
this.updateSignalStrengths();
|
| 331 |
}, 100);
|