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);
|