kolaslab commited on
Commit
80347b4
·
verified ·
1 Parent(s): 4253062

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +262 -237
index.html CHANGED
@@ -3,6 +3,7 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <title>Global SDR Network Monitor</title>
 
6
  <style>
7
  body {
8
  margin: 0;
@@ -17,13 +18,14 @@
17
  display: grid;
18
  grid-template-columns: 300px 1fr;
19
  gap: 20px;
 
20
  }
21
 
22
  .sidebar {
23
  background: #111;
24
  padding: 15px;
25
  border-radius: 8px;
26
- height: calc(100vh - 40px);
27
  overflow-y: auto;
28
  z-index: 1000;
29
  }
@@ -65,10 +67,32 @@
65
  background: #f00;
66
  }
67
 
68
- #map {
69
- background: #111;
 
70
  border-radius: 8px;
71
- height: calc(100vh - 40px);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
  }
73
 
74
  .signal-strength {
@@ -111,6 +135,20 @@
111
  50% { opacity: 0.3; }
112
  100% { opacity: 0.7; }
113
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
  </style>
115
  </head>
116
  <body>
@@ -123,9 +161,13 @@
123
  <div id="detections"></div>
124
  </div>
125
 
126
- <canvas id="map"></canvas>
 
 
 
127
  </div>
128
 
 
129
  <script>
130
  // Global SDR stations data
131
  const sdrStations = [
@@ -138,7 +180,190 @@
138
  range: 200,
139
  active: true
140
  },
141
- // [이전과 동일한 나머지 스테이션 데이터...]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
  ];
143
 
144
  class RadarSystem {
@@ -147,19 +372,40 @@
147
  this.ctx = this.canvas.getContext('2d');
148
  this.targets = new Set();
149
  this.signalLines = new Set();
 
150
  this.setupCanvas();
151
  this.renderReceivers();
152
  this.startTracking();
 
 
153
  }
154
 
155
- setupCanvas() {
156
- this.canvas.width = this.canvas.offsetWidth;
157
- this.canvas.height = this.canvas.offsetHeight;
158
-
159
- window.addEventListener('resize', () => {
160
- this.canvas.width = this.canvas.offsetWidth;
161
- this.canvas.height = this.canvas.offsetHeight;
 
162
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  }
164
 
165
  renderReceivers() {
@@ -182,14 +428,8 @@
182
  }
183
 
184
  latLongToXY(lat, lon) {
185
- const centerLat = 20;
186
- const centerLon = 0;
187
- const scale = 4;
188
-
189
- const x = (lon - centerLon) * scale + this.canvas.width/2;
190
- const y = (centerLat - lat) * scale + this.canvas.height/2;
191
-
192
- return {x, y};
193
  }
194
 
195
  generateTarget() {
@@ -200,219 +440,4 @@
200
  position: {
201
  lat: station.location[0] + (Math.random() - 0.5) * range,
202
  lon: station.location[1] + (Math.random() - 0.5) * range
203
- },
204
- speed: Math.random() * 500 + 200,
205
- altitude: Math.random() * 35000 + 5000,
206
- heading: Math.random() * 360,
207
- id: Math.random().toString(36).substr(2, 6).toUpperCase(),
208
- signalStrength: Math.random(),
209
- createdAt: Date.now()
210
- };
211
- }
212
-
213
- drawBackground() {
214
- this.ctx.fillStyle = '#111';
215
- this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
216
-
217
- this.ctx.strokeStyle = '#1a1a1a';
218
- this.ctx.lineWidth = 1;
219
-
220
- for(let i = 0; i < this.canvas.width; i += 50) {
221
- this.ctx.beginPath();
222
- this.ctx.moveTo(i, 0);
223
- this.ctx.lineTo(i, this.canvas.height);
224
- this.ctx.stroke();
225
- }
226
-
227
- for(let i = 0; i < this.canvas.height; i += 50) {
228
- this.ctx.beginPath();
229
- this.ctx.moveTo(0, i);
230
- this.ctx.lineTo(this.canvas.width, i);
231
- this.ctx.stroke();
232
- }
233
- }
234
-
235
- drawStations() {
236
- sdrStations.forEach(station => {
237
- const pos = this.latLongToXY(station.location[0], station.location[1]);
238
- const visualRange = station.range * 0.2;
239
-
240
- const gradient = this.ctx.createRadialGradient(
241
- pos.x, pos.y, 0,
242
- pos.x, pos.y, visualRange
243
- );
244
- gradient.addColorStop(0, `rgba(0,255,0,${station.active ? 0.2 : 0.05})`);
245
- gradient.addColorStop(1, 'rgba(0,255,0,0)');
246
-
247
- this.ctx.beginPath();
248
- this.ctx.arc(pos.x, pos.y, visualRange, 0, Math.PI * 2);
249
- this.ctx.fillStyle = gradient;
250
- this.ctx.fill();
251
- this.ctx.strokeStyle = `rgba(0,255,0,${station.active ? 0.4 : 0.1})`;
252
- this.ctx.stroke();
253
-
254
- this.ctx.beginPath();
255
- this.ctx.arc(pos.x, pos.y, 4, 0, Math.PI * 2);
256
- this.ctx.fillStyle = station.active ? '#0f0' : '#f00';
257
- this.ctx.fill();
258
-
259
- if(station.active) {
260
- this.ctx.beginPath();
261
- this.ctx.arc(pos.x, pos.y, 6, 0, Math.PI * 2);
262
- this.ctx.strokeStyle = 'rgba(0,255,0,0.5)';
263
- this.ctx.stroke();
264
- }
265
-
266
- this.ctx.fillStyle = '#0f0';
267
- this.ctx.font = 'bold 10px monospace';
268
- this.ctx.fillText(station.name, pos.x + 10, pos.y + 4);
269
- });
270
- }
271
-
272
- drawTargets() {
273
- this.targets.forEach(target => {
274
- const pos = this.latLongToXY(target.position.lat, target.position.lon);
275
-
276
- sdrStations.forEach(station => {
277
- if(station.active) {
278
- const stationPos = this.latLongToXY(station.location[0], station.location[1]);
279
-
280
- const dx = pos.x - stationPos.x;
281
- const dy = pos.y - stationPos.y;
282
- const distance = Math.sqrt(dx * dx + dy * dy);
283
- const maxDistance = station.range * 0.2;
284
-
285
- if (distance <= maxDistance) {
286
- const signalStrength = Math.max(0.1, 1 - (distance / maxDistance));
287
-
288
- const gradient = this.ctx.createLinearGradient(
289
- stationPos.x, stationPos.y, pos.x, pos.y
290
- );
291
- gradient.addColorStop(0, `rgba(0,255,0,${signalStrength * 0.7})`);
292
- gradient.addColorStop(1, 'rgba(0,255,0,0)');
293
-
294
- this.ctx.beginPath();
295
- this.ctx.moveTo(stationPos.x, stationPos.y);
296
- this.ctx.lineTo(pos.x, pos.y);
297
- this.ctx.strokeStyle = gradient;
298
- this.ctx.lineWidth = 2;
299
- this.ctx.setLineDash([5, 15]);
300
- this.ctx.stroke();
301
- this.ctx.setLineDash([]);
302
- }
303
- }
304
- });
305
-
306
- const targetGlow = this.ctx.createRadialGradient(
307
- pos.x, pos.y, 2,
308
- pos.x, pos.y, 8
309
- );
310
-
311
- const targetColor = target.type === 'aircraft' ?
312
- ['rgba(255,255,0,0.8)', 'rgba(255,255,0,0)'] :
313
- ['rgba(0,255,255,0.8)', 'rgba(0,255,255,0)'];
314
-
315
- targetGlow.addColorStop(0, targetColor[0]);
316
- targetGlow.addColorStop(1, targetColor[1]);
317
-
318
- this.ctx.beginPath();
319
- this.ctx.arc(pos.x, pos.y, 8, 0, Math.PI * 2);
320
- this.ctx.fillStyle = targetGlow;
321
- this.ctx.fill();
322
-
323
- this.ctx.beginPath();
324
- this.ctx.arc(pos.x, pos.y, 4, 0, Math.PI * 2);
325
- this.ctx.fillStyle = target.type === 'aircraft' ? '#ff0' : '#0ff';
326
- this.ctx.fill();
327
-
328
- const info = `${target.id} • ${target.speed.toFixed(0)}kts • ${target.altitude.toFixed(0)}ft`;
329
- this.ctx.font = 'bold 10px monospace';
330
- const textWidth = this.ctx.measureText(info).width;
331
-
332
- this.ctx.fillStyle = 'rgba(0,0,0,0.7)';
333
- this.ctx.fillRect(pos.x + 10, pos.y - 8, textWidth + 4, 16);
334
-
335
- this.ctx.fillStyle = target.type === 'aircraft' ? '#ff0' : '#0ff';
336
- this.ctx.fillText(info, pos.x + 12, pos.y + 4);
337
- });
338
- }
339
-
340
- updateDetections() {
341
- const detections = document.getElementById('detections');
342
- detections.innerHTML = Array.from(this.targets)
343
- .map(target => `
344
- <div class="detection">
345
- ${target.type === 'aircraft' ? '✈️' : '🚗'}
346
- <strong>${target.id}</strong><br>
347
- Speed: ${target.speed.toFixed(0)}kts
348
- ${target.type === 'aircraft' ? `<br>Alt: ${target.altitude.toFixed(0)}ft` : ''}
349
- <br>Signal: ${(target.signalStrength * 100).toFixed(0)}%
350
- </div>
351
- `).join('');
352
- }
353
-
354
- updateSignalStrengths() {
355
- sdrStations.forEach(station => {
356
- const bar = document.querySelector(`#rx-${station.url.split(':')[0]} .signal-bar`);
357
- if(bar) {
358
- let maxSignalStrength = 0;
359
- this.targets.forEach(target => {
360
- const stationPos = this.latLongToXY(station.location[0], station.location[1]);
361
- const targetPos = this.latLongToXY(target.position.lat, target.position.lon);
362
-
363
- const dx = targetPos.x - stationPos.x;
364
- const dy = targetPos.y - stationPos.y;
365
- const distance = Math.sqrt(dx * dx + dy * dy);
366
- const maxDistance = station.range * 0.2;
367
-
368
- if (distance <= maxDistance) {
369
- const strength = Math.max(0.1, 1 - (distance / maxDistance));
370
- maxSignalStrength = Math.max(maxSignalStrength, strength);
371
- }
372
- });
373
-
374
- const baseStrength = maxSignalStrength * 100;
375
- const fluctuation = Math.random() * 20 - 10;
376
- const finalStrength = Math.max(10, Math.min(100, baseStrength + fluctuation));
377
-
378
- bar.style.width = `${finalStrength}%`;
379
- bar.style.boxShadow = maxSignalStrength > 0.5 ? '0 0 10px #0f0' : 'none';
380
- bar.style.opacity = maxSignalStrength > 0 ? 1 : 0.3;
381
- }
382
- });
383
- }
384
-
385
- startTracking() {
386
- setInterval(() => {
387
- // Add new targets
388
- if(Math.random() < 0.1 && this.targets.size < 20) {
389
- const newTarget = this.generateTarget();
390
- newTarget.createdAt = Date.now();
391
- this.targets.add(newTarget);
392
- }
393
-
394
- // Remove old targets
395
- const currentTime = Date.now();
396
- Array.from(this.targets).forEach(target => {
397
- if(currentTime - target.createdAt > 10000) { // 10초 후 제거
398
- this.targets.delete(target);
399
- }
400
- });
401
-
402
- // Update display
403
- this.drawBackground();
404
- this.drawStations();
405
- this.drawTargets();
406
- this.updateDetections();
407
- this.updateSignalStrengths();
408
- }, 50); // 50ms interval for smooth animation
409
- }
410
- }
411
-
412
- // Initialize radar system when page loads
413
- window.addEventListener('load', () => {
414
- const radar = new RadarSystem();
415
- });
416
- </script>
417
- </body>
418
- </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <title>Global SDR Network Monitor</title>
6
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.css" />
7
  <style>
8
  body {
9
  margin: 0;
 
18
  display: grid;
19
  grid-template-columns: 300px 1fr;
20
  gap: 20px;
21
+ height: calc(100vh - 40px);
22
  }
23
 
24
  .sidebar {
25
  background: #111;
26
  padding: 15px;
27
  border-radius: 8px;
28
+ height: 100%;
29
  overflow-y: auto;
30
  z-index: 1000;
31
  }
 
67
  background: #f00;
68
  }
69
 
70
+ #map-container {
71
+ position: relative;
72
+ height: 100%;
73
  border-radius: 8px;
74
+ overflow: hidden;
75
+ }
76
+
77
+ canvas#map {
78
+ position: absolute;
79
+ top: 0;
80
+ left: 0;
81
+ width: 100%;
82
+ height: 100%;
83
+ z-index: 1;
84
+ }
85
+
86
+ #world-map {
87
+ position: absolute;
88
+ top: 0;
89
+ left: 0;
90
+ width: 100%;
91
+ height: 100%;
92
+ z-index: 0;
93
+ filter: invert(1) hue-rotate(180deg);
94
+ opacity: 0.5;
95
+ background: #111;
96
  }
97
 
98
  .signal-strength {
 
135
  50% { opacity: 0.3; }
136
  100% { opacity: 0.7; }
137
  }
138
+
139
+ /* Leaflet 스타일 오버라이드 */
140
+ .leaflet-container {
141
+ background: #111 !important;
142
+ }
143
+
144
+ .leaflet-control-attribution {
145
+ background: rgba(17, 17, 17, 0.8) !important;
146
+ color: #666 !important;
147
+ }
148
+
149
+ .leaflet-control-attribution a {
150
+ color: #888 !important;
151
+ }
152
  </style>
153
  </head>
154
  <body>
 
161
  <div id="detections"></div>
162
  </div>
163
 
164
+ <div id="map-container">
165
+ <div id="world-map"></div>
166
+ <canvas id="map"></canvas>
167
+ </div>
168
  </div>
169
 
170
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.js"></script>
171
  <script>
172
  // Global SDR stations data
173
  const sdrStations = [
 
180
  range: 200,
181
  active: true
182
  },
183
+ {
184
+ name: "TU Delft WebSDR",
185
+ url: "websdr.tudelft.nl:8901",
186
+ location: [51.9981, 4.3731],
187
+ frequency: "0-29.160 MHz",
188
+ range: 180,
189
+ active: true
190
+ },
191
+ {
192
+ name: "SUWS WebSDR UK",
193
+ url: "websdr.suws.org.uk",
194
+ location: [51.2785, -0.7642],
195
+ frequency: "0-30 MHz",
196
+ range: 150,
197
+ active: true
198
+ },
199
+ {
200
+ name: "KiwiSDR Switzerland",
201
+ url: "hb9ryz.no-ip.org:8073",
202
+ location: [47.3769, 8.5417],
203
+ frequency: "0-30 MHz",
204
+ range: 160,
205
+ active: true
206
+ },
207
+ // United States
208
+ {
209
+ name: "W6DRZ WebSDR",
210
+ url: "w6drz.sdr.us:8901",
211
+ location: [34.2847, -118.4429],
212
+ frequency: "0-30 MHz",
213
+ range: 170,
214
+ active: true
215
+ },
216
+ {
217
+ name: "K3FEF WebSDR",
218
+ url: "k3fef.sdr.us:8901",
219
+ location: [40.5697, -75.9363],
220
+ frequency: "0-30 MHz",
221
+ range: 160,
222
+ active: true
223
+ },
224
+ {
225
+ name: "WA2ZKD KiwiSDR",
226
+ url: "wa2zkd.sdr.us:8073",
227
+ location: [40.7128, -74.0060],
228
+ frequency: "0-30 MHz",
229
+ range: 150,
230
+ active: true
231
+ },
232
+ {
233
+ name: "W4AX WebSDR",
234
+ url: "w4ax.sdr.us:8901",
235
+ location: [33.7756, -84.3963],
236
+ frequency: "0-30 MHz",
237
+ range: 165,
238
+ active: true
239
+ },
240
+ // Japan
241
+ {
242
+ name: "JH7VHZ WebSDR",
243
+ url: "jh7vhz.sdr.jp:8901",
244
+ location: [38.2682, 140.8694],
245
+ frequency: "0-30 MHz",
246
+ range: 155,
247
+ active: true
248
+ },
249
+ {
250
+ name: "JA1GJB KiwiSDR",
251
+ url: "ja1gjb.sdr.jp:8073",
252
+ location: [35.6762, 139.6503],
253
+ frequency: "0-30 MHz",
254
+ range: 145,
255
+ active: true
256
+ },
257
+ {
258
+ name: "JA3ZOH WebSDR",
259
+ url: "ja3zoh.sdr.jp:8901",
260
+ location: [34.6937, 135.5023],
261
+ frequency: "0-30 MHz",
262
+ range: 150,
263
+ active: true
264
+ },
265
+ // Australia
266
+ {
267
+ name: "VK4YA KiwiSDR",
268
+ url: "vk4ya.sdr.au:8073",
269
+ location: [-27.4698, 153.0251],
270
+ frequency: "0-30 MHz",
271
+ range: 170,
272
+ active: true
273
+ },
274
+ {
275
+ name: "VK2RG WebSDR",
276
+ url: "vk2rg.sdr.au:8901",
277
+ location: [-33.8688, 151.2093],
278
+ frequency: "0-30 MHz",
279
+ range: 165,
280
+ active: true
281
+ },
282
+ // Russia
283
+ {
284
+ name: "RZ3DJR WebSDR",
285
+ url: "rz3djr.sdr.ru:8901",
286
+ location: [55.7558, 37.6173],
287
+ frequency: "0-30 MHz",
288
+ range: 180,
289
+ active: true
290
+ },
291
+ {
292
+ name: "UA9UDX WebSDR",
293
+ url: "ua9udx.sdr.ru:8901",
294
+ location: [55.0084, 82.9357],
295
+ frequency: "0-30 MHz",
296
+ range: 175,
297
+ active: true
298
+ },
299
+ // China
300
+ {
301
+ name: "BY1PK WebSDR",
302
+ url: "by1pk.sdr.cn:8901",
303
+ location: [39.9042, 116.4074],
304
+ frequency: "0-30 MHz",
305
+ range: 160,
306
+ active: true
307
+ },
308
+ {
309
+ name: "BG3MDO KiwiSDR",
310
+ url: "bg3mdo.sdr.cn:8073",
311
+ location: [23.1291, 113.2644],
312
+ frequency: "0-30 MHz",
313
+ range: 155,
314
+ active: true
315
+ },
316
+ // South Korea
317
+ {
318
+ name: "HL2WA KiwiSDR",
319
+ url: "hl2wa.sdr.kr:8073",
320
+ location: [37.5665, 126.9780],
321
+ frequency: "0-30 MHz",
322
+ range: 150,
323
+ active: true
324
+ },
325
+ {
326
+ name: "DS1URB WebSDR",
327
+ url: "ds1urb.sdr.kr:8901",
328
+ location: [35.1796, 129.0756],
329
+ frequency: "0-30 MHz",
330
+ range: 145,
331
+ active: true
332
+ },
333
+ // Canada
334
+ {
335
+ name: "VE3HOA WebSDR",
336
+ url: "ve3hoa.sdr.ca:8901",
337
+ location: [43.6532, -79.3832],
338
+ frequency: "0-30 MHz",
339
+ range: 165,
340
+ active: true
341
+ },
342
+ {
343
+ name: "VA3ROM KiwiSDR",
344
+ url: "va3rom.sdr.ca:8073",
345
+ location: [45.4215, -75.6972],
346
+ frequency: "0-30 MHz",
347
+ range: 160,
348
+ active: true
349
+ },
350
+ // Brazil
351
+ {
352
+ name: "PY2RDZ WebSDR",
353
+ url: "py2rdz.sdr.br:8901",
354
+ location: [-23.5505, -46.6333],
355
+ frequency: "0-30 MHz",
356
+ range: 170,
357
+ active: true
358
+ },
359
+ {
360
+ name: "PY1ZV KiwiSDR",
361
+ url: "py1zv.sdr.br:8073",
362
+ location: [-22.9068, -43.1729],
363
+ frequency: "0-30 MHz",
364
+ range: 165,
365
+ active: true
366
+ }
367
  ];
368
 
369
  class RadarSystem {
 
372
  this.ctx = this.canvas.getContext('2d');
373
  this.targets = new Set();
374
  this.signalLines = new Set();
375
+ this.setupWorldMap();
376
  this.setupCanvas();
377
  this.renderReceivers();
378
  this.startTracking();
379
+
380
+ window.addEventListener('resize', this.handleResize.bind(this));
381
  }
382
 
383
+ setupWorldMap() {
384
+ this.worldMap = L.map('world-map', {
385
+ center: [20, 0],
386
+ zoom: 2,
387
+ zoomControl: false,
388
+ dragging: false,
389
+ scrollWheelZoom: false,
390
+ doubleClickZoom: false
391
  });
392
+
393
+ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
394
+ attribution: '© OpenStreetMap contributors'
395
+ }).addTo(this.worldMap);
396
+ }
397
+
398
+ setupCanvas() {
399
+ const container = document.getElementById('map-container');
400
+ this.canvas.width = container.offsetWidth;
401
+ this.canvas.height = container.offsetHeight;
402
+ }
403
+
404
+ handleResize() {
405
+ const container = document.getElementById('map-container');
406
+ this.canvas.width = container.offsetWidth;
407
+ this.canvas.height = container.offsetHeight;
408
+ this.worldMap.invalidateSize();
409
  }
410
 
411
  renderReceivers() {
 
428
  }
429
 
430
  latLongToXY(lat, lon) {
431
+ const point = this.worldMap.latLngToContainerPoint([lat, lon]);
432
+ return {x: point.x, y: point.y};
 
 
 
 
 
 
433
  }
434
 
435
  generateTarget() {
 
440
  position: {
441
  lat: station.location[0] + (Math.random() - 0.5) * range,
442
  lon: station.location[1] + (Math.random() - 0.5) * range
443
+ },