arvind7754 commited on
Commit
65c2cbb
·
verified ·
1 Parent(s): a0c7f84

Upload folder using huggingface_hub

Browse files
Files changed (1) hide show
  1. index.html +835 -969
index.html CHANGED
@@ -1,986 +1,852 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>GPS SDR Signal Generator | Educational Interface</title>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
- <!-- Leaflet CSS -->
9
- <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
- <!-- Font Awesome -->
12
- <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
- <style>
15
- :root {
16
- --primary: #00ff88;
17
- --secondary: #00ccff;
18
- --danger: #ff4444;
19
- --bg-dark: #0a0e17;
20
- --bg-card: rgba(20, 25, 40, 0.85);
21
- --border: rgba(0, 255, 136, 0.2);
22
- --text: #e0e6ed;
23
- --text-muted: #8b95a8;
24
- }
25
-
26
- * {
27
- margin: 0;
28
- padding: 0;
29
- box-sizing: border-box;
30
- }
31
-
32
- body {
33
- font-family: 'Segoe UI', Roboto, monospace;
34
- background: var(--bg-dark);
35
- color: var(--text);
36
- min-height: 100vh;
37
- overflow-x: hidden;
38
- }
39
-
40
- /* Animated Background */
41
- .bg-grid {
42
- position: fixed;
43
- top: 0;
44
- left: 0;
45
- width: 100%;
46
- height: 100%;
47
- background-image:
48
- linear-gradient(rgba(0, 255, 136, 0.03) 1px, transparent 1px),
49
- linear-gradient(90deg, rgba(0, 255, 136, 0.03) 1px, transparent 1px);
50
- background-size: 50px 50px;
51
- pointer-events: none;
52
- z-index: -1;
53
- }
54
-
55
- .bg-glow {
56
- position: fixed;
57
- width: 600px;
58
- height: 600px;
59
- background: radial-gradient(circle, rgba(0, 255, 136, 0.1) 0%, transparent 70%);
60
- border-radius: 50%;
61
- pointer-events: none;
62
- z-index: -1;
63
- animation: float 20s infinite ease-in-out;
64
- }
65
-
66
- .bg-glow:nth-child(2) {
67
- background: radial-gradient(circle, rgba(0, 204, 255, 0.1) 0%, transparent 70%);
68
- animation-delay: -10s;
69
- right: -200px;
70
- top: 20%;
71
- }
72
-
73
- @keyframes float {
74
- 0%, 100% { transform: translate(0, 0); }
75
- 50% { transform: translate(100px, 50px); }
76
- }
77
-
78
- /* Header */
79
- header {
80
- background: rgba(10, 14, 23, 0.95);
81
- border-bottom: 1px solid var(--border);
82
- padding: 1rem 2rem;
83
- position: sticky;
84
- top: 0;
85
- z-index: 1000;
86
- backdrop-filter: blur(10px);
87
- }
88
-
89
- .header-content {
90
- max-width: 1400px;
91
- margin: 0 auto;
92
- display: flex;
93
- justify-content: space-between;
94
- align-items: center;
95
- flex-wrap: wrap;
96
- gap: 1rem;
97
- }
98
-
99
- .logo {
100
- display: flex;
101
- align-items: center;
102
- gap: 0.75rem;
103
- font-size: 1.5rem;
104
- font-weight: 700;
105
- color: var(--primary);
106
- text-shadow: 0 0 20px rgba(0, 255, 136, 0.3);
107
- }
108
-
109
- .logo i {
110
- font-size: 1.75rem;
111
- }
112
-
113
- .built-with {
114
- font-size: 0.85rem;
115
- color: var(--text-muted);
116
- text-decoration: none;
117
- padding: 0.5rem 1rem;
118
- border: 1px solid var(--border);
119
- border-radius: 2rem;
120
- transition: all 0.3s;
121
- background: rgba(0, 255, 136, 0.05);
122
- }
123
-
124
- .built-with:hover {
125
- background: rgba(0, 255, 136, 0.1);
126
- border-color: var(--primary);
127
- color: var(--primary);
128
- transform: translateY(-2px);
129
- }
130
-
131
- /* Warning Banner */
132
- .warning-banner {
133
- background: linear-gradient(90deg, rgba(255, 68, 68, 0.1), rgba(255, 68, 68, 0.05));
134
- border-left: 4px solid var(--danger);
135
- padding: 1rem 2rem;
136
- margin: 1rem auto;
137
- max-width: 1400px;
138
- border-radius: 0 8px 8px 0;
139
- display: flex;
140
- align-items: center;
141
- gap: 1rem;
142
- animation: pulse 2s infinite;
143
- }
144
-
145
- @keyframes pulse {
146
- 0%, 100% { opacity: 1; }
147
- 50% { opacity: 0.9; }
148
- }
149
-
150
- .warning-banner i {
151
- color: var(--danger);
152
- font-size: 1.5rem;
153
- }
154
-
155
- .warning-text {
156
- flex: 1;
157
- }
158
-
159
- .warning-text h3 {
160
- color: var(--danger);
161
- margin-bottom: 0.25rem;
162
- font-size: 1rem;
163
- }
164
-
165
- .warning-text p {
166
- color: var(--text-muted);
167
- font-size: 0.9rem;
168
- margin: 0;
169
- }
170
-
171
- /* Main Container */
172
- .container {
173
- max-width: 1400px;
174
- margin: 0 auto;
175
- padding: 0 2rem 2rem;
176
- }
177
-
178
- /* Grid Layout */
179
- .grid {
180
- display: grid;
181
- grid-template-columns: 1fr 1fr;
182
- gap: 1.5rem;
183
- margin-top: 1.5rem;
184
- }
185
-
186
- @media (max-width: 1024px) {
187
- .grid {
188
- grid-template-columns: 1fr;
189
- }
190
- }
191
-
192
- /* Cards */
193
- .card {
194
- background: var(--bg-card);
195
- border: 1px solid var(--border);
196
- border-radius: 16px;
197
- padding: 1.5rem;
198
- backdrop-filter: blur(10px);
199
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
200
- transition: transform 0.3s, box-shadow 0.3s;
201
- }
202
-
203
- .card:hover {
204
- transform: translateY(-2px);
205
- box-shadow: 0 12px 40px rgba(0, 255, 136, 0.1);
206
- }
207
-
208
- .card-header {
209
- display: flex;
210
- align-items: center;
211
- gap: 0.75rem;
212
- margin-bottom: 1.5rem;
213
- padding-bottom: 1rem;
214
- border-bottom: 1px solid var(--border);
215
- }
216
-
217
- .card-header h2 {
218
- font-size: 1.25rem;
219
- color: var(--primary);
220
- font-weight: 600;
221
- }
222
-
223
- .card-header i {
224
- color: var(--secondary);
225
- font-size: 1.2rem;
226
- }
227
-
228
- /* Map Container */
229
- #map {
230
- height: 400px;
231
- border-radius: 12px;
232
- border: 1px solid var(--border);
233
- z-index: 1;
234
- }
235
-
236
- /* Form Elements */
237
- .form-group {
238
- margin-bottom: 1.25rem;
239
- }
240
-
241
- label {
242
- display: block;
243
- margin-bottom: 0.5rem;
244
- color: var(--text-muted);
245
- font-size: 0.9rem;
246
- font-weight: 500;
247
- }
248
-
249
- input, select, textarea {
250
- width: 100%;
251
- padding: 0.75rem 1rem;
252
- background: rgba(0, 0, 0, 0.3);
253
- border: 1px solid var(--border);
254
- border-radius: 8px;
255
- color: var(--text);
256
- font-family: inherit;
257
- font-size: 0.95rem;
258
- transition: all 0.3s;
259
- }
260
-
261
- input:focus, select:focus, textarea:focus {
262
- outline: none;
263
- border-color: var(--primary);
264
- box-shadow: 0 0 0 3px rgba(0, 255, 136, 0.1);
265
- }
266
-
267
- .input-group {
268
- display: flex;
269
- gap: 0.5rem;
270
- }
271
-
272
- .input-group input {
273
- flex: 1;
274
- }
275
-
276
- .btn {
277
- padding: 0.75rem 1.5rem;
278
- border: none;
279
- border-radius: 8px;
280
- font-family: inherit;
281
- font-weight: 600;
282
- cursor: pointer;
283
- transition: all 0.3s;
284
- display: inline-flex;
285
- align-items: center;
286
- gap: 0.5rem;
287
- font-size: 0.95rem;
288
- }
289
-
290
- .btn-primary {
291
- background: linear-gradient(135deg, var(--primary), var(--secondary));
292
- color: #000;
293
- }
294
-
295
- .btn-primary:hover {
296
- transform: translateY(-2px);
297
- box-shadow: 0 4px 20px rgba(0, 255, 136, 0.4);
298
- }
299
-
300
- .btn-secondary {
301
- background: rgba(0, 204, 255, 0.1);
302
- color: var(--secondary);
303
- border: 1px solid var(--secondary);
304
- }
305
-
306
- .btn-secondary:hover {
307
- background: rgba(0, 204, 255, 0.2);
308
- }
309
-
310
- .btn-danger {
311
- background: rgba(255, 68, 68, 0.1);
312
- color: var(--danger);
313
- border: 1px solid var(--danger);
314
- }
315
-
316
- /* Satellite Visualizer */
317
- .satellite-view {
318
- position: relative;
319
- height: 300px;
320
- background: radial-gradient(circle at center, rgba(0, 255, 136, 0.05) 0%, transparent 70%);
321
- border-radius: 12px;
322
- border: 1px solid var(--border);
323
- overflow: hidden;
324
- }
325
-
326
- .earth-center {
327
- position: absolute;
328
- top: 50%;
329
- left: 50%;
330
- transform: translate(-50%, -50%);
331
- width: 40px;
332
- height: 40px;
333
- background: var(--secondary);
334
- border-radius: 50%;
335
- box-shadow: 0 0 30px rgba(0, 204, 255, 0.5);
336
- z-index: 10;
337
- }
338
-
339
- .satellite {
340
- position: absolute;
341
- width: 12px;
342
- height: 12px;
343
- background: var(--primary);
344
- border-radius: 50%;
345
- box-shadow: 0 0 10px var(--primary);
346
- transition: all 0.5s;
347
- cursor: pointer;
348
- }
349
-
350
- .satellite::after {
351
- content: attr(data-prn);
352
- position: absolute;
353
- top: -20px;
354
- left: 50%;
355
- transform: translateX(-50%);
356
- font-size: 0.7rem;
357
- color: var(--text-muted);
358
- white-space: nowrap;
359
- }
360
-
361
- .orbit-ring {
362
- position: absolute;
363
- border: 1px solid rgba(0, 255, 136, 0.1);
364
- border-radius: 50%;
365
- top: 50%;
366
- left: 50%;
367
- transform: translate(-50%, -50%);
368
- }
369
-
370
- /* Console Output */
371
- .console {
372
- background: #050810;
373
- border: 1px solid var(--border);
374
- border-radius: 8px;
375
- padding: 1rem;
376
- font-family: 'Courier New', monospace;
377
- font-size: 0.85rem;
378
- color: var(--primary);
379
- height: 200px;
380
- overflow-y: auto;
381
- margin-top: 1rem;
382
- }
383
-
384
- .console-line {
385
- margin-bottom: 0.5rem;
386
- opacity: 0;
387
- animation: fadeIn 0.3s forwards;
388
- }
389
-
390
- @keyframes fadeIn {
391
- to { opacity: 1; }
392
- }
393
-
394
- .console-prompt {
395
- color: var(--secondary);
396
- }
397
-
398
- .console-error {
399
- color: var(--danger);
400
- }
401
-
402
- .console-success {
403
- color: #00ff00;
404
- }
405
-
406
- /* Command Block */
407
- .command-block {
408
- background: rgba(0, 0, 0, 0.5);
409
- border: 1px solid var(--border);
410
- border-radius: 8px;
411
- padding: 1rem;
412
- margin-top: 1rem;
413
- position: relative;
414
- overflow: hidden;
415
- }
416
-
417
- .command-block::before {
418
- content: '';
419
- position: absolute;
420
- left: 0;
421
- top: 0;
422
- height: 100%;
423
- width: 3px;
424
- background: var(--primary);
425
- }
426
-
427
- .command-text {
428
- font-family: 'Courier New', monospace;
429
- color: var(--primary);
430
- word-break: break-all;
431
- line-height: 1.5;
432
- }
433
-
434
- .copy-btn {
435
- position: absolute;
436
- top: 0.5rem;
437
- right: 0.5rem;
438
- background: rgba(255, 255, 255, 0.1);
439
- border: none;
440
- color: var(--text);
441
- padding: 0.25rem 0.75rem;
442
- border-radius: 4px;
443
- cursor: pointer;
444
- font-size: 0.8rem;
445
- }
446
-
447
- .copy-btn:hover {
448
- background: rgba(255, 255, 255, 0.2);
449
- }
450
-
451
- /* Status Indicators */
452
- .status-grid {
453
- display: grid;
454
- grid-template-columns: repeat(2, 1fr);
455
- gap: 1rem;
456
- margin-top: 1rem;
457
- }
458
-
459
- .status-item {
460
- background: rgba(0, 0, 0, 0.2);
461
- padding: 1rem;
462
- border-radius: 8px;
463
- border: 1px solid var(--border);
464
- }
465
-
466
- .status-label {
467
- font-size: 0.8rem;
468
- color: var(--text-muted);
469
- margin-bottom: 0.25rem;
470
- }
471
-
472
- .status-value {
473
- font-size: 1.1rem;
474
- font-weight: 600;
475
- color: var(--primary);
476
- font-family: monospace;
477
- }
478
-
479
- /* Tabs */
480
- .tabs {
481
- display: flex;
482
- gap: 0.5rem;
483
- margin-bottom: 1rem;
484
- border-bottom: 1px solid var(--border);
485
- padding-bottom: 0.5rem;
486
- }
487
-
488
- .tab {
489
- padding: 0.5rem 1rem;
490
- background: none;
491
- border: none;
492
- color: var(--text-muted);
493
- cursor: pointer;
494
- border-radius: 6px;
495
- transition: all 0.3s;
496
- font-size: 0.9rem;
497
- }
498
-
499
- .tab.active {
500
- background: rgba(0, 255, 136, 0.1);
501
- color: var(--primary);
502
- }
503
-
504
- .tab:hover:not(.active) {
505
- background: rgba(255, 255, 255, 0.05);
506
- color: var(--text);
507
- }
508
-
509
- .tab-content {
510
- display: none;
511
- }
512
-
513
- .tab-content.active {
514
- display: block;
515
- animation: fadeIn 0.3s;
516
- }
517
-
518
- /* Responsive */
519
- @media (max-width: 768px) {
520
- .header-content {
521
- flex-direction: column;
522
- text-align: center;
523
- }
524
 
525
- .status-grid {
526
- grid-template-columns: 1fr;
527
- }
 
528
 
529
- .input-group {
530
- flex-direction: column;
531
- }
532
- }
533
-
534
- /* Scrollbar */
535
- ::-webkit-scrollbar {
536
- width: 8px;
537
- height: 8px;
538
- }
539
-
540
- ::-webkit-scrollbar-track {
541
- background: var(--bg-dark);
542
- }
543
-
544
- ::-webkit-scrollbar-thumb {
545
- background: var(--border);
546
- border-radius: 4px;
547
- }
548
-
549
- ::-webkit-scrollbar-thumb:hover {
550
- background: var(--primary);
551
- }
552
-
553
- /* Checkbox styling */
554
- .checkbox-group {
555
- display: flex;
556
- align-items: center;
557
- gap: 0.5rem;
558
- margin-bottom: 0.75rem;
559
- }
560
-
561
- .checkbox-group input[type="checkbox"] {
562
- width: auto;
563
- accent-color: var(--primary);
564
- }
565
-
566
- .checkbox-group label {
567
- margin: 0;
568
- cursor: pointer;
569
- }
570
- </style>
571
- </head>
572
- <body>
573
- <div class="bg-grid"></div>
574
- <div class="bg-glow" style="left: -200px; top: 10%;"></div>
575
- <div class="bg-glow"></div>
576
-
577
- <header>
578
- <div class="header-content">
579
- <div class="logo">
580
- <i class="fas fa-satellite-dish"></i>
581
- <span>GPS SDR Generator</span>
582
- </div>
583
- <a href="https://huggingface.co/spaces/akhaliq/anycoder" target="_blank" class="built-with">
584
- <i class="fas fa-code"></i> Built with anycoder
585
- </a>
586
- </div>
587
- </header>
588
-
589
- <div class="warning-banner">
590
- <i class="fas fa-exclamation-triangle"></i>
591
- <div class="warning-text">
592
- <h3>LEGAL NOTICE & SAFETY WARNING</h3>
593
- <p>GPS signal transmission is regulated by law. This tool generates configuration for gps-sdr-sim for educational and authorized testing purposes only. Unauthorized GPS spoofing is illegal and dangerous. Always ensure you have proper authorization and are in a shielded environment before transmitting.</p>
594
- </div>
595
- </div>
596
-
597
- <div class="container">
598
- <div class="grid">
599
- <!-- Left Column: Map and Coordinates -->
600
- <div class="card">
601
- <div class="card-header">
602
- <i class="fas fa-map-marked-alt"></i>
603
- <h2>Target Coordinates</h2>
604
- </div>
605
-
606
- <div id="map"></div>
607
-
608
- <div class="status-grid" style="margin-top: 1rem;">
609
- <div class="status-item">
610
- <div class="status-label">Latitude</div>
611
- <div class="status-value" id="lat-display">40.7128° N</div>
612
- </div>
613
- <div class="status-item">
614
- <div class="status-label">Longitude</div>
615
- <div class="status-value" id="lon-display">74.0060° W</div>
616
- </div>
617
- <div class="status-item">
618
- <div class="status-label">Altitude</div>
619
- <div class="status-value" id="alt-display">10 m</div>
620
- </div>
621
- <div class="status-item">
622
- <div class="status-label">Satellites</div>
623
- <div class="status-value" id="sat-count">8 Visible</div>
624
- </div>
625
- </div>
626
-
627
- <div class="form-group" style="margin-top: 1rem;">
628
- <label>Manual Coordinates Input</label>
629
- <div class="input-group">
630
- <input type="number" id="manual-lat" placeholder="Latitude" step="0.0001" value="40.7128">
631
- <input type="number" id="manual-lon" placeholder="Longitude" step="0.0001" value="-74.0060">
632
- <button class="btn btn-secondary" onclick="updateMapFromInput()">
633
- <i class="fas fa-location-arrow"></i>
634
- </button>
635
- </div>
636
- </div>
637
- </div>
638
-
639
- <!-- Right Column: Configuration -->
640
- <div class="card">
641
- <div class="card-header">
642
- <i class="fas fa-sliders-h"></i>
643
- <h2>Signal Configuration</h2>
644
- </div>
645
-
646
- <div class="tabs">
647
- <button class="tab active" onclick="switchTab('basic')">Basic</button>
648
- <button class="tab" onclick="switchTab('advanced')">Advanced</button>
649
- <button class="tab" onclick="switchTab('trajectory')">Trajectory</button>
650
- </div>
651
-
652
- <div id="basic-tab" class="tab-content active">
653
- <div class="form-group">
654
- <label>Simulation Mode</label>
655
- <select id="sim-mode">
656
- <option value="static">Static Position</option>
657
- <option value="dynamic">Dynamic (User-defined)</option>
658
- <option value="circle">Circular Motion</option>
659
- <option value="line">Linear Path</option>
660
- </select>
661
- </div>
662
-
663
- <div class="form-group">
664
- <label>Signal Duration (seconds)</label>
665
- <input type="number" id="duration" value="300" min="1" max="3600">
666
- </div>
667
-
668
- <div class="form-group">
669
- <label>Sample Rate (MHz)</label>
670
- <select id="sample-rate">
671
- <option value="2.6">2.6 MHz (Default)</option>
672
- <option value="5.0">5.0 MHz</option>
673
- <option value="10.0">10.0 MHz</option>
674
- <option value="20.0">20.0 MHz</option>
675
- </select>
676
- </div>
677
-
678
- <div class="form-group">
679
- <label>GPS Date & Time</label>
680
- <input type="datetime-local" id="gps-time">
681
- </div>
682
- </div>
683
-
684
- <div id="advanced-tab" class="tab-content">
685
- <div class="form-group">
686
- <label>Signal Power (dB)</label>
687
- <input type="range" id="power" min="-100" max="-50" value="-80" style="width: 100%;">
688
- <div style="text-align: center; color: var(--primary); margin-top: 0.5rem;">
689
- <span id="power-value">-80</span> dBm
690
- </div>
691
- </div>
692
-
693
- <div class="checkbox-group">
694
- <input type="checkbox" id="iono-model" checked>
695
- <label for="iono-model">Enable Ionospheric Model</label>
696
- </div>
697
-
698
- <div class="checkbox-group">
699
- <input type="checkbox" id="tropo-model" checked>
700
- <label for="tropo-model">Enable Tropospheric Model</label>
701
- </div>
702
-
703
- <div class="checkbox-group">
704
- <input type="checkbox" id="noise" checked>
705
- <label for="noise">Add AWGN Noise</label>
706
- </div>
707
-
708
- <div class="form-group">
709
- <label>PRN Satellites (1-32, comma separated)</label>
710
- <input type="text" id="prn-list" placeholder="1,2,3,4,5,6,7,8" value="1,2,3,4,5,6,7,8">
711
- </div>
712
- </div>
713
-
714
- <div id="trajectory-tab" class="tab-content">
715
- <div class="form-group">
716
- <label>Movement Speed (m/s)</label>
717
- <input type="number" id="speed" value="5" min="0" max="100">
718
- </div>
719
-
720
- <div class="form-group">
721
- <label>Path Direction (degrees)</label>
722
- <input type="number" id="direction" value="0" min="0" max="360">
723
- </div>
724
-
725
- <div class="form-group">
726
- <label>Circle Radius (m) - For circular mode</label>
727
- <input type="number" id="radius" value="100" min="10" max="10000">
728
- </div>
729
- </div>
730
-
731
- <button class="btn btn-primary" style="width: 100%; margin-top: 1rem;" onclick="generateCommand()">
732
- <i class="fas fa-terminal"></i> Generate Windows Command
733
- </button>
734
- </div>
735
- </div>
736
-
737
- <!-- Satellite Visualization -->
738
- <div class="card" style="margin-top: 1.5rem;">
739
- <div class="card-header">
740
- <i class="fas fa-satellite"></i>
741
- <h2>GPS Constellation Visualization</h2>
742
- </div>
743
- <div class="satellite-view" id="sat-view">
744
- <div class="earth-center"></div>
745
- <!-- Orbit rings -->
746
- <div class="orbit-ring" style="width: 100px; height: 100px;"></div>
747
- <div class="orbit-ring" style="width: 150px; height: 150px;"></div>
748
- <div class="orbit-ring" style="width: 200px; height: 200px;"></div>
749
- <div class="orbit-ring" style="width: 250px; height: 250px;"></div>
750
- </div>
751
- <p style="margin-top: 1rem; color: var(--text-muted); font-size: 0.9rem;">
752
- <i class="fas fa-info-circle"></i>
753
- Visual representation of visible satellites based on selected location and time. Blue dot represents receiver position.
754
- </p>
755
- </div>
756
-
757
- <!-- Command Output -->
758
- <div class="card" style="margin-top: 1.5rem;">
759
- <div class="card-header">
760
- <i class="fas fa-code"></i>
761
- <h2>Generated Command</h2>
762
- </div>
763
 
764
- <div class="command-block">
765
- <button class="copy-btn" onclick="copyCommand()">
766
- <i class="fas fa-copy"></i> Copy
767
- </button>
768
- <div class="command-text" id="command-output">
769
- # Click "Generate Windows Command" to create gps-sdr-sim command...
770
- </div>
771
- </div>
772
-
773
- <div class="console" id="console">
774
- <div class="console-line">
775
- <span class="console-prompt">$</span> Ready. Configure parameters and generate command.
776
- </div>
777
- </div>
778
-
779
- <div style="margin-top: 1.5rem; padding: 1rem; background: rgba(0,0,0,0.3); border-radius: 8px; border-left: 3px solid var(--secondary);">
780
- <h4 style="color: var(--secondary); margin-bottom: 0.5rem;"><i class="fas fa-info-circle"></i> Windows Setup Instructions</h4>
781
- <ol style="color: var(--text-muted); padding-left: 1.2rem; line-height: 1.8; font-size: 0.9rem;">
782
- <li>Install <a href="https://github.com/osqzss/gps-sdr-sim" target="_blank" style="color: var(--primary);">gps-sdr-sim</a> for Windows (requires Visual Studio Build Tools)</li>
783
- <li>Install <a href="https://github.com/greatscottgadgets/hackrf/releases" target="_blank" style="color: var(--primary);">HackRF Tools</a> for Windows</li>
784
- <li>Ensure HackRF One is connected and drivers are installed (Zadig)</li>
785
- <li>Run the generated command in Command Prompt or PowerShell</li>
786
- <li><strong style="color: var(--danger);">Verify you are in a shielded room or have authorization before transmitting!</strong></li>
787
- </ol>
788
- </div>
789
- </div>
790
- </div>
791
-
792
- <!-- Leaflet JS -->
793
- <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
 
 
 
 
 
794
 
795
- <script>
796
- // Initialize map
797
- const map = L.map('map').setView([40.7128, -74.0060], 13);
798
-
799
- L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
800
- attribution: OpenStreetMap contributors',
801
- maxZoom: 19
802
- }).addTo(map);
803
-
804
- let marker = L.marker([40.7128, -74.0060], {
805
- draggable: true,
806
- icon: L.divIcon({
807
- className: 'custom-marker',
808
- html: '<div style="background: #00ff88; width: 20px; height: 20px; border-radius: 50%; border: 3px solid white; box-shadow: 0 0 10px rgba(0,255,136,0.5);"></div>',
809
- iconSize: [20, 20],
810
- iconAnchor: [10, 10]
811
- })
812
- }).addTo(map);
813
-
814
- // Update coordinates on marker drag
815
- marker.on('dragend', function(e) {
816
- const pos = marker.getLatLng();
817
- updateCoordinates(pos.lat, pos.lng);
818
- });
819
-
820
- // Map click to move marker
821
- map.on('click', function(e) {
822
- marker.setLatLng(e.latlng);
823
- updateCoordinates(e.latlng.lat, e.latlng.lng);
824
- });
825
-
826
- function updateCoordinates(lat, lon) {
827
- document.getElementById('lat-display').textContent = lat.toFixed(4) + '° ' + (lat >= 0 ? 'N' : 'S');
828
- document.getElementById('lon-display').textContent = Math.abs(lon).toFixed(4) + '° ' + (lon >= 0 ? 'E' : 'W');
829
- document.getElementById('manual-lat').value = lat.toFixed(4);
830
- document.getElementById('manual-lon').value = lon.toFixed(4);
831
- updateSatelliteView();
832
- }
833
-
834
- function updateMapFromInput() {
835
- const lat = parseFloat(document.getElementById('manual-lat').value);
836
- const lon = parseFloat(document.getElementById('manual-lon').value);
837
- if (!isNaN(lat) && !isNaN(lon)) {
838
- marker.setLatLng([lat, lon]);
839
- map.setView([lat, lon], 13);
840
- updateCoordinates(lat, lon);
841
- }
842
- }
843
-
844
- // Set default time to now
845
- const now = new Date();
846
- now.setMinutes(now.getMinutes() - now.getTimezoneOffset());
847
- document.getElementById('gps-time').value = now.toISOString().slice(0, 16);
848
-
849
- // Tab switching
850
- function switchTab(tabName) {
851
- document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
852
- document.querySelectorAll('.tab-content').forEach(t => t.classList.remove('active'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
853
 
854
- event.target.classList.add('active');
855
- document.getElementById(tabName + '-tab').classList.add('active');
856
- }
857
-
858
- // Power slider update
859
- document.getElementById('power').addEventListener('input', function(e) {
860
- document.getElementById('power-value').textContent = e.target.value;
861
- });
862
-
863
- // Satellite visualization
864
- function updateSatelliteView() {
865
- const container = document.getElementById('sat-view');
866
- // Clear existing satellites
867
- container.querySelectorAll('.satellite').forEach(s => s.remove());
868
 
869
- // Generate random satellite positions for visualization
870
- const satCount = 8;
871
- for (let i = 0; i < satCount; i++) {
872
- const sat = document.createElement('div');
873
- sat.className = 'satellite';
874
- sat.setAttribute('data-prn', 'G' + (i + 1));
875
-
876
- const angle = (i / satCount) * 2 * Math.PI;
877
- const radius = 60 + (i % 3) * 40;
878
- const x = 50 + (radius / 300 * 100) * Math.cos(angle);
879
- const y = 50 + (radius / 300 * 100) * Math.sin(angle);
880
-
881
- sat.style.left = x + '%';
882
- sat.style.top = y + '%';
883
- sat.style.animationDelay = (i * 0.2) + 's';
884
-
885
- container.appendChild(sat);
886
- }
887
- }
888
-
889
- updateSatelliteView();
890
-
891
- // Console logging
892
- function log(message, type = 'info') {
893
- const console = document.getElementById('console');
894
- const line = document.createElement('div');
895
- line.className = 'console-line';
896
 
897
- const timestamp = new Date().toLocaleTimeString();
898
- let prefix = '<span class="console-prompt">[' + timestamp + ']</span> ';
 
 
899
 
900
- if (type === 'error') {
901
- line.innerHTML = prefix + '<span class="console-error">' + message + '</span>';
902
- } else if (type === 'success') {
903
- line.innerHTML = prefix + '<span class="console-success">' + message + '</span>';
904
- } else {
905
- line.innerHTML = prefix + message;
906
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
907
 
908
- console.appendChild(line);
909
- console.scrollTop = console.scrollHeight;
910
- }
911
-
912
- // Generate command
913
- function generateCommand() {
914
- const lat = document.getElementById('manual-lat').value;
915
- const lon = document.getElementById('manual-lon').value;
916
- const mode = document.getElementById('sim-mode').value;
917
- const duration = document.getElementById('duration').value;
918
- const sampleRate = document.getElementById('sample-rate').value;
919
- const power = document.getElementById('power').value;
920
- const time = document.getElementById('gps-time').value;
921
 
922
- if (!lat || !lon) {
923
- log('Error: Please select coordinates on the map', 'error');
924
- return;
925
- }
926
-
927
- // Format time for gps-sdr-sim (YYYYMMDD HHMMSS)
928
- const dateObj = new Date(time);
929
- const dateStr = dateObj.getFullYear().toString() +
930
- String(dateObj.getMonth() + 1).padStart(2, '0') +
931
- String(dateObj.getDate()).padStart(2, '0');
932
- const timeStr = String(dateObj.getHours()).padStart(2, '0') +
933
- String(dateObj.getMinutes()).padStart(2, '0') +
934
- String(dateObj.getSeconds()).padStart(2, '0');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
935
 
936
- let command = 'gps-sdr-sim.exe';
937
-
938
- // Add parameters
939
- command += ' -e brdc3540.14n'; // Ephemeris file (example)
940
- command += ' -l ' + lat + ',' + lon + ',10'; // Lat, Lon, Alt
941
- command += ' -d ' + duration; // Duration
942
- command += ' -s ' + sampleRate; // Sample rate
943
-
944
- if (mode === 'dynamic') {
945
- command += ' -t ' + dateStr + ',' + timeStr; // Start time
946
- command += ' -v 5,0'; // Velocity (example)
947
- } else if (mode === 'circle') {
948
- const radius = document.getElementById('radius').value;
949
- command += ' -c ' + radius; // Circle radius
950
- }
951
-
952
- // Add gain/power adjustment via -g (if supported by version)
953
- command += ' -g ' + (parseInt(power) + 100); // Convert dB to gain value
954
-
955
- // Output file
956
- command += ' -o gpssim.bin';
957
-
958
- // HackRF transmit command (separate step)
959
- const hackrfCmd = '\n\n:: Then transmit with HackRF:\nhackrf_transfer.exe -t gpssim.bin -f 1575420000 -s ' + sampleRate + '000000 -a 1 -x 40';
960
 
961
- document.getElementById('command-output').textContent = command + hackrfCmd;
962
-
963
- log('Command generated successfully', 'success');
964
- log('Coordinates: ' + lat + ', ' + lon);
965
- log('Mode: ' + mode + ', Duration: ' + duration + 's');
966
- log('Ready for execution on Windows', 'success');
967
- }
968
 
969
- function copyCommand() {
970
- const text = document.getElementById('command-output').textContent;
971
- navigator.clipboard.writeText(text).then(() => {
972
- log('Command copied to clipboard', 'success');
973
- }).catch(() => {
974
- log('Failed to copy', 'error');
975
- });
976
- }
977
 
978
- // Initial log
979
- setTimeout(() => {
980
- log('GPS SDR Interface initialized');
981
- log('HackRF One detected on USB (simulated)');
982
- log('Waiting for user input...');
983
- }, 500);
984
- </script>
985
- </body>
986
- </html>
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ GPS SDR Signal Generator - Educational Interface
4
+ Built with anycoder - https://huggingface.co/spaces/akhaliq/anycoder
5
+ """
6
+
7
+ import tkinter as tk
8
+ from tkinter import ttk, messagebox, scrolledtext
9
+ from tkinter import font as tkfont
10
+ import webbrowser
11
+ import math
12
+ import random
13
+ from datetime import datetime, timedelta
14
+ import threading
15
+ import time
16
+
17
+
18
+ class GPSSDRApp:
19
+ def __init__(self, root):
20
+ self.root = root
21
+ self.root.title("GPS SDR Signal Generator | Educational Interface")
22
+ self.root.geometry("1400x900")
23
+ self.root.minsize(1200, 800)
24
+
25
+ # Colors - Cyberpunk/GPS theme
26
+ self.colors = {
27
+ 'bg_dark': '#0a0e17',
28
+ 'bg_card': '#141928',
29
+ 'primary': '#00ff88',
30
+ 'secondary': '#00ccff',
31
+ 'danger': '#ff4444',
32
+ 'text': '#e0e6ed',
33
+ 'text_muted': '#8b95a8',
34
+ 'border': '#00ff8820'
35
+ }
36
+
37
+ # Configure root
38
+ self.root.configure(bg=self.colors['bg_dark'])
39
+
40
+ # Custom fonts
41
+ self.fonts = {
42
+ 'header': tkfont.Font(family='Segoe UI', size=16, weight='bold'),
43
+ 'title': tkfont.Font(family='Segoe UI', size=12, weight='bold'),
44
+ 'normal': tkfont.Font(family='Segoe UI', size=10),
45
+ 'mono': tkfont.Font(family='Courier New', size=10),
46
+ 'small': tkfont.Font(family='Segoe UI', size=9)
47
+ }
48
+
49
+ # Variables
50
+ self.lat_var = tk.DoubleVar(value=40.7128)
51
+ self.lon_var = tk.DoubleVar(value=-74.0060)
52
+ self.alt_var = tk.DoubleVar(value=10)
53
+ self.duration_var = tk.IntVar(value=300)
54
+ self.sample_rate_var = tk.StringVar(value='2.6')
55
+ self.power_var = tk.IntVar(value=-80)
56
+ self.mode_var = tk.StringVar(value='static')
57
+ self.speed_var = tk.DoubleVar(value=5)
58
+ self.direction_var = tk.DoubleVar(value=0)
59
+ self.radius_var = tk.DoubleVar(value=100)
60
+ self.prn_var = tk.StringVar(value='1,2,3,4,5,6,7,8')
61
+ self.iono_var = tk.BooleanVar(value=True)
62
+ self.tropo_var = tk.BooleanVar(value=True)
63
+ self.noise_var = tk.BooleanVar(value=True)
64
+
65
+ # Satellite animation
66
+ self.satellites = []
67
+ self.animating = False
68
+
69
+ self.setup_styles()
70
+ self.create_ui()
71
+ self.start_satellite_animation()
72
+
73
+ # Initial log
74
+ self.after_init()
75
 
76
+ def setup_styles(self):
77
+ """Configure ttk styles for modern look"""
78
+ style = ttk.Style()
79
+ style.theme_use('clam')
80
+
81
+ # Configure styles
82
+ style.configure('Custom.TFrame', background=self.colors['bg_dark'])
83
+ style.configure('Card.TFrame', background=self.colors['bg_card'])
84
+
85
+ style.configure('Custom.TLabel',
86
+ background=self.colors['bg_card'],
87
+ foreground=self.colors['text'],
88
+ font=self.fonts['normal'])
89
+
90
+ style.configure('Title.TLabel',
91
+ background=self.colors['bg_card'],
92
+ foreground=self.colors['primary'],
93
+ font=self.fonts['title'])
94
+
95
+ style.configure('Muted.TLabel',
96
+ background=self.colors['bg_card'],
97
+ foreground=self.colors['text_muted'],
98
+ font=self.fonts['small'])
99
+
100
+ style.configure('Custom.TButton',
101
+ background=self.colors['primary'],
102
+ foreground='#000000',
103
+ font=self.fonts['normal'],
104
+ padding=10)
105
+
106
+ style.configure('Secondary.TButton',
107
+ background=self.colors['bg_card'],
108
+ foreground=self.colors['secondary'],
109
+ font=self.fonts['normal'])
110
+
111
+ style.configure('Danger.TButton',
112
+ background=self.colors['bg_card'],
113
+ foreground=self.colors['danger'],
114
+ font=self.fonts['normal'])
115
+
116
+ style.configure('Custom.TEntry',
117
+ fieldbackground='#050810',
118
+ foreground=self.colors['text'],
119
+ insertcolor=self.colors['primary'])
120
+
121
+ style.configure('Custom.TCombobox',
122
+ fieldbackground='#050810',
123
+ foreground=self.colors['text'])
124
+
125
+ # Notebook (tabs)
126
+ style.configure('Custom.TNotebook',
127
+ background=self.colors['bg_card'],
128
+ tabmargins=[2, 5, 2, 0])
129
+
130
+ style.configure('Custom.TNotebook.Tab',
131
+ background=self.colors['bg_dark'],
132
+ foreground=self.colors['text_muted'],
133
+ padding=[15, 8],
134
+ font=self.fonts['normal'])
135
+
136
+ style.map('Custom.TNotebook.Tab',
137
+ background=[('selected', self.colors['bg_card'])],
138
+ foreground=[('selected', self.colors['primary'])],
139
+ expand=[('selected', [1, 1, 1, 0])])
140
+
141
+ # Scale
142
+ style.configure('Custom.Horizontal.TScale',
143
+ background=self.colors['bg_card'],
144
+ troughcolor='#050810')
145
 
146
+ def create_ui(self):
147
+ """Create the main user interface"""
148
+ # Main container with padding
149
+ main_container = tk.Frame(self.root, bg=self.colors['bg_dark'])
150
+ main_container.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)
151
+
152
+ # Header
153
+ self.create_header(main_container)
154
+
155
+ # Warning banner
156
+ self.create_warning_banner(main_container)
157
+
158
+ # Content area
159
+ content = tk.Frame(main_container, bg=self.colors['bg_dark'])
160
+ content.pack(fill=tk.BOTH, expand=True, pady=15)
161
+
162
+ # Two-column layout
163
+ content.grid_columnconfigure(0, weight=1)
164
+ content.grid_columnconfigure(1, weight=1)
165
+ content.grid_rowconfigure(0, weight=1)
166
+
167
+ # Left column - Map and Coordinates
168
+ left_frame = self.create_left_column(content)
169
+ left_frame.grid(row=0, column=0, sticky='nsew', padx=(0, 10))
170
+
171
+ # Right column - Configuration
172
+ right_frame = self.create_right_column(content)
173
+ right_frame.grid(row=0, column=1, sticky='nsew', padx=(10, 0))
174
+
175
+ # Bottom section - Satellite visualization and Command output
176
+ bottom_frame = self.create_bottom_section(main_container)
177
+ bottom_frame.pack(fill=tk.BOTH, expand=True, pady=(15, 0))
178
 
179
+ def create_header(self, parent):
180
+ """Create the header with logo and built-with link"""
181
+ header = tk.Frame(parent, bg='#0d1117', height=60)
182
+ header.pack(fill=tk.X, pady=(0, 10))
183
+ header.pack_propagate(False)
184
+
185
+ # Left side - Logo
186
+ logo_frame = tk.Frame(header, bg='#0d1117')
187
+ logo_frame.pack(side=tk.LEFT, padx=20, pady=10)
188
+
189
+ # Satellite icon (using text as icon)
190
+ icon_label = tk.Label(logo_frame, text='🛰️',
191
+ bg='#0d1117', fg=self.colors['primary'],
192
+ font=('Segoe UI', 20))
193
+ icon_label.pack(side=tk.LEFT, padx=(0, 10))
194
+
195
+ title_label = tk.Label(logo_frame, text='GPS SDR Generator',
196
+ bg='#0d1117', fg=self.colors['primary'],
197
+ font=self.fonts['header'])
198
+ title_label.pack(side=tk.LEFT)
199
+
200
+ # Right side - Built with link
201
+ built_btn = tk.Label(header, text='🔧 Built with anycoder',
202
+ bg='#0d1117', fg=self.colors['text_muted'],
203
+ font=self.fonts['small'],
204
+ cursor='hand2',
205
+ padx=15, pady=5)
206
+ built_btn.pack(side=tk.RIGHT, padx=20)
207
+ built_btn.bind('<Enter>', lambda e: built_btn.config(fg=self.colors['primary']))
208
+ built_btn.bind('<Leave>', lambda e: built_btn.config(fg=self.colors['text_muted']))
209
+ built_btn.bind('<Button-1>', lambda e: webbrowser.open('https://huggingface.co/spaces/akhaliq/anycoder'))
210
+
211
+ def create_warning_banner(self, parent):
212
+ """Create the legal warning banner"""
213
+ banner = tk.Frame(parent, bg='#ff444415', highlightbackground=self.colors['danger'],
214
+ highlightthickness=2, highlightcolor=self.colors['danger'])
215
+ banner.pack(fill=tk.X, pady=(0, 10))
216
+
217
+ # Left border accent
218
+ left_accent = tk.Frame(banner, bg=self.colors['danger'], width=4)
219
+ left_accent.pack(side=tk.LEFT, fill=tk.Y)
220
+
221
+ content = tk.Frame(banner, bg='#ff444415', padx=15, pady=12)
222
+ content.pack(fill=tk.BOTH, expand=True)
223
+
224
+ # Warning icon
225
+ icon_label = tk.Label(content, text='⚠️', bg='#ff444415',
226
+ fg=self.colors['danger'], font=('Segoe UI', 24))
227
+ icon_label.pack(side=tk.LEFT, padx=(0, 15))
228
+
229
+ # Text content
230
+ text_frame = tk.Frame(content, bg='#ff444415')
231
+ text_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
232
+
233
+ title = tk.Label(text_frame, text='LEGAL NOTICE & SAFETY WARNING',
234
+ bg='#ff444415', fg=self.colors['danger'],
235
+ font=self.fonts['title'])
236
+ title.pack(anchor='w')
237
+
238
+ desc = tk.Label(text_frame,
239
+ text='GPS signal transmission is regulated by law. This tool generates configuration for gps-sdr-sim for educational and authorized testing purposes only. Unauthorized GPS spoofing is illegal and dangerous. Always ensure you have proper authorization and are in a shielded environment before transmitting.',
240
+ bg='#ff444415', fg=self.colors['text_muted'],
241
+ font=self.fonts['small'], wraplength=800, justify=tk.LEFT)
242
+ desc.pack(anchor='w', pady=(5, 0))
243
+
244
+ def create_left_column(self, parent):
245
+ """Create left column with map and coordinates"""
246
+ frame = tk.Frame(parent, bg=self.colors['bg_card'],
247
+ highlightbackground=self.colors['border'],
248
+ highlightthickness=1, padx=20, pady=20)
249
+ frame.grid_propagate(False)
250
+
251
+ # Header
252
+ header = tk.Frame(frame, bg=self.colors['bg_card'])
253
+ header.pack(fill=tk.X, pady=(0, 15))
254
+
255
+ tk.Label(header, text='🗺️', bg=self.colors['bg_card'],
256
+ fg=self.colors['secondary'], font=('Segoe UI', 16)).pack(side=tk.LEFT)
257
+
258
+ tk.Label(header, text='Target Coordinates', bg=self.colors['bg_card'],
259
+ fg=self.colors['primary'], font=self.fonts['title']).pack(side=tk.LEFT, padx=10)
260
+
261
+ # Map canvas (simulated map)
262
+ self.map_canvas = tk.Canvas(frame, bg='#050810', height=300,
263
+ highlightthickness=1, highlightbackground=self.colors['border'])
264
+ self.map_canvas.pack(fill=tk.BOTH, expand=True, pady=(0, 15))
265
+
266
+ # Draw grid pattern on map
267
+ self.draw_map_grid()
268
+
269
+ # Map marker (draggable)
270
+ self.map_marker = self.map_canvas.create_oval(0, 0, 20, 20,
271
+ fill=self.colors['primary'],
272
+ outline='white', width=2)
273
+ self.update_map_marker()
274
+
275
+ # Bind map interactions
276
+ self.map_canvas.bind('<Button-1>', self.on_map_click)
277
+ self.map_canvas.bind('<B1-Motion>', self.on_map_drag)
278
+
279
+ # Status grid
280
+ status_frame = tk.Frame(frame, bg=self.colors['bg_card'])
281
+ status_frame.pack(fill=tk.X, pady=(0, 15))
282
+
283
+ status_frame.grid_columnconfigure(0, weight=1)
284
+ status_frame.grid_columnconfigure(1, weight=1)
285
+
286
+ # Status items
287
+ self.status_items = {}
288
+ status_data = [
289
+ ('Latitude', 'lat-display', '40.7128° N'),
290
+ ('Longitude', 'lon-display', '74.0060° W'),
291
+ ('Altitude', 'alt-display', '10 m'),
292
+ ('Satellites', 'sat-count', '8 Visible')
293
+ ]
294
+
295
+ for i, (label, key, value) in enumerate(status_data):
296
+ row = i // 2
297
+ col = i % 2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
 
299
+ item = tk.Frame(status_frame, bg='#050810', padx=10, pady=8,
300
+ highlightbackground=self.colors['border'],
301
+ highlightthickness=1)
302
+ item.grid(row=row, column=col, sticky='nsew', padx=5, pady=5)
303
 
304
+ tk.Label(item, text=label, bg='#050810',
305
+ fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
 
307
+ value_label = tk.Label(item, text=value, bg='#050810',
308
+ fg=self.colors['primary'], font=self.fonts['mono'])
309
+ value_label.pack(anchor='w')
310
+ self.status_items[key] = value_label
311
+
312
+ # Manual input
313
+ input_frame = tk.Frame(frame, bg=self.colors['bg_card'])
314
+ input_frame.pack(fill=tk.X)
315
+
316
+ tk.Label(input_frame, text='Manual Coordinates Input', bg=self.colors['bg_card'],
317
+ fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(0, 8))
318
+
319
+ input_row = tk.Frame(input_frame, bg=self.colors['bg_card'])
320
+ input_row.pack(fill=tk.X)
321
+
322
+ self.lat_entry = tk.Entry(input_row, textvariable=self.lat_var,
323
+ bg='#050810', fg=self.colors['text'],
324
+ insertbackground=self.colors['primary'],
325
+ font=self.fonts['mono'], width=15)
326
+ self.lat_entry.pack(side=tk.LEFT, padx=(0, 5))
327
+
328
+ self.lon_entry = tk.Entry(input_row, textvariable=self.lon_var,
329
+ bg='#050810', fg=self.colors['text'],
330
+ insertbackground=self.colors['primary'],
331
+ font=self.fonts['mono'], width=15)
332
+ self.lon_entry.pack(side=tk.LEFT, padx=5)
333
+
334
+ update_btn = tk.Button(input_row, text='📍 Update',
335
+ bg=self.colors['secondary'], fg='#000',
336
+ font=self.fonts['small'], padx=10, pady=5,
337
+ command=self.update_from_input,
338
+ cursor='hand2', relief=tk.FLAT)
339
+ update_btn.pack(side=tk.LEFT, padx=5)
340
+
341
+ return frame
342
 
343
+ def draw_map_grid(self):
344
+ """Draw grid pattern on map canvas"""
345
+ width = 400
346
+ height = 300
347
+
348
+ # Grid lines
349
+ for i in range(0, width, 40):
350
+ self.map_canvas.create_line(i, 0, i, height, fill='#00ff8810', width=1)
351
+ for i in range(0, height, 40):
352
+ self.map_canvas.create_line(0, i, width, i, fill='#00ff8810', width=1)
353
+
354
+ # Some "streets" for visual interest
355
+ self.map_canvas.create_line(50, 100, 350, 80, fill='#1a2332', width=3)
356
+ self.map_canvas.create_line(100, 50, 120, 250, fill='#1a2332', width=3)
357
+ self.map_canvas.create_line(200, 30, 280, 270, fill='#1a2332', width=2)
358
+ self.map_canvas.create_line(30, 180, 370, 200, fill='#1a2332', width=2)
359
+
360
+ def update_map_marker(self):
361
+ """Update marker position based on coordinates"""
362
+ # Convert lat/lon to canvas coordinates (simplified)
363
+ # Center of canvas represents the current lat/lon
364
+ cx = self.map_canvas.winfo_width() // 2 or 200
365
+ cy = self.map_canvas.winfo_height() // 2 or 150
366
+
367
+ self.map_canvas.coords(self.map_marker, cx-10, cy-10, cx+10, cy+10)
368
+ self.update_status_display()
369
+
370
+ def on_map_click(self, event):
371
+ """Handle map click"""
372
+ self.move_marker(event.x, event.y)
373
+
374
+ def on_map_drag(self, event):
375
+ """Handle marker drag"""
376
+ self.move_marker(event.x, event.y)
377
+
378
+ def move_marker(self, x, y):
379
+ """Move marker to position and update coordinates"""
380
+ cx = max(10, min(x, self.map_canvas.winfo_width() - 10))
381
+ cy = max(10, min(y, self.map_canvas.winfo_height() - 10))
382
+
383
+ self.map_canvas.coords(self.map_marker, cx-10, cy-10, cx+10, cy+10)
384
+
385
+ # Small random offset to simulate map movement
386
+ offset_x = (cx - self.map_canvas.winfo_width()//2) * 0.0001
387
+ offset_y = (cy - self.map_canvas.winfo_height()//2) * 0.0001
388
+
389
+ new_lat = 40.7128 + offset_y
390
+ new_lon = -74.0060 + offset_x
391
+
392
+ self.lat_var.set(round(new_lat, 4))
393
+ self.lon_var.set(round(new_lon, 4))
394
+ self.update_status_display()
395
+
396
+ def update_from_input(self):
397
+ """Update from manual input"""
398
+ self.update_status_display()
399
+ self.update_map_marker()
400
+
401
+ def update_status_display(self):
402
+ """Update status labels"""
403
+ lat = self.lat_var.get()
404
+ lon = self.lon_var.get()
405
+
406
+ self.status_items['lat-display'].config(
407
+ text=f'{abs(lat):.4f}° {"N" if lat >= 0 else "S"}')
408
+ self.status_items['lon-display'].config(
409
+ text=f'{abs(lon):.4f}° {"E" if lon >= 0 else "W"}')
410
+ self.status_items['alt-display'].config(text=f'{self.alt_var.get():.0f} m')
411
+
412
+ def create_right_column(self, parent):
413
+ """Create right column with configuration tabs"""
414
+ frame = tk.Frame(parent, bg=self.colors['bg_card'],
415
+ highlightbackground=self.colors['border'],
416
+ highlightthickness=1, padx=20, pady=20)
417
+
418
+ # Header
419
+ header = tk.Frame(frame, bg=self.colors['bg_card'])
420
+ header.pack(fill=tk.X, pady=(0, 15))
421
+
422
+ tk.Label(header, text='⚙️', bg=self.colors['bg_card'],
423
+ fg=self.colors['secondary'], font=('Segoe UI', 16)).pack(side=tk.LEFT)
424
+
425
+ tk.Label(header, text='Signal Configuration', bg=self.colors['bg_card'],
426
+ fg=self.colors['primary'], font=self.fonts['title']).pack(side=tk.LEFT, padx=10)
427
+
428
+ # Notebook for tabs
429
+ self.notebook = ttk.Notebook(frame, style='Custom.TNotebook')
430
+ self.notebook.pack(fill=tk.BOTH, expand=True, pady=(0, 15))
431
+
432
+ # Basic tab
433
+ basic_frame = tk.Frame(self.notebook, bg=self.colors['bg_card'], padx=15, pady=15)
434
+ self.create_basic_tab(basic_frame)
435
+ self.notebook.add(basic_frame, text=' Basic ')
436
+
437
+ # Advanced tab
438
+ advanced_frame = tk.Frame(self.notebook, bg=self.colors['bg_card'], padx=15, pady=15)
439
+ self.create_advanced_tab(advanced_frame)
440
+ self.notebook.add(advanced_frame, text=' Advanced ')
441
+
442
+ # Trajectory tab
443
+ trajectory_frame = tk.Frame(self.notebook, bg=self.colors['bg_card'], padx=15, pady=15)
444
+ self.create_trajectory_tab(trajectory_frame)
445
+ self.notebook.add(trajectory_frame, text=' Trajectory ')
446
+
447
+ # Generate button
448
+ gen_btn = tk.Button(frame, text='🖥️ Generate Windows Command',
449
+ bg=self.colors['primary'], fg='#000',
450
+ font=self.fonts['title'], pady=12,
451
+ command=self.generate_command,
452
+ cursor='hand2', relief=tk.FLAT,
453
+ activebackground='#00cc6a')
454
+ gen_btn.pack(fill=tk.X)
455
+
456
+ return frame
457
+
458
+ def create_basic_tab(self, parent):
459
+ """Create basic configuration tab"""
460
+ # Simulation Mode
461
+ tk.Label(parent, text='Simulation Mode', bg=self.colors['bg_card'],
462
+ fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(0, 5))
463
+
464
+ mode_combo = ttk.Combobox(parent, textvariable=self.mode_var,
465
+ values=['static', 'dynamic', 'circle', 'line'],
466
+ state='readonly', width=30)
467
+ mode_combo.pack(fill=tk.X, pady=(0, 15))
468
+
469
+ # Duration
470
+ tk.Label(parent, text='Signal Duration (seconds)', bg=self.colors['bg_card'],
471
+ fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(0, 5))
472
+
473
+ tk.Spinbox(parent, from_=1, to=3600, textvariable=self.duration_var,
474
+ bg='#050810', fg=self.colors['text'], font=self.fonts['normal'],
475
+ insertbackground=self.colors['primary']).pack(fill=tk.X, pady=(0, 15))
476
+
477
+ # Sample Rate
478
+ tk.Label(parent, text='Sample Rate (MHz)', bg=self.colors['bg_card'],
479
+ fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(0, 5))
480
+
481
+ rate_combo = ttk.Combobox(parent, textvariable=self.sample_rate_var,
482
+ values=['2.6', '5.0', '10.0', '20.0'],
483
+ state='readonly', width=30)
484
+ rate_combo.pack(fill=tk.X, pady=(0, 15))
485
+
486
+ # GPS Time
487
+ tk.Label(parent, text='GPS Date & Time', bg=self.colors['bg_card'],
488
+ fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(0, 5))
489
+
490
+ # Use current time as default
491
+ now = datetime.now()
492
+ self.gps_time_var = tk.StringVar(value=now.strftime('%Y-%m-%d %H:%M'))
493
+
494
+ tk.Entry(parent, textvariable=self.gps_time_var,
495
+ bg='#050810', fg=self.colors['text'],
496
+ insertbackground=self.colors['primary'],
497
+ font=self.fonts['mono']).pack(fill=tk.X)
498
+
499
+ def create_advanced_tab(self, parent):
500
+ """Create advanced configuration tab"""
501
+ # Power slider
502
+ tk.Label(parent, text='Signal Power (dB)', bg=self.colors['bg_card'],
503
+ fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(0, 5))
504
+
505
+ power_frame = tk.Frame(parent, bg=self.colors['bg_card'])
506
+ power_frame.pack(fill=tk.X, pady=(0, 5))
507
+
508
+ power_scale = tk.Scale(power_frame, from_=-100, to=-50,
509
+ orient=tk.HORIZONTAL, variable=self.power_var,
510
+ bg=self.colors['bg_card'], fg=self.colors['primary'],
511
+ highlightthickness=0, troughcolor='#050810',
512
+ activebackground=self.colors['primary'])
513
+ power_scale.pack(fill=tk.X, side=tk.LEFT, expand=True)
514
+
515
+ self.power_label = tk.Label(power_frame, text='-80 dBm', bg=self.colors['bg_card'],
516
+ fg=self.colors['primary'], font=self.fonts['mono'], width=10)
517
+ self.power_label.pack(side=tk.RIGHT)
518
+
519
+ self.power_var.trace('w', lambda *args: self.power_label.config(
520
+ text=f'{self.power_var.get()} dBm'))
521
+
522
+ # Checkboxes
523
+ tk.Checkbutton(parent, text='Enable Ionospheric Model', variable=self.iono_var,
524
+ bg=self.colors['bg_card'], fg=self.colors['text'],
525
+ selectcolor='#050810', activebackground=self.colors['bg_card'],
526
+ activeforeground=self.colors['primary']).pack(anchor='w', pady=8)
527
+
528
+ tk.Checkbutton(parent, text='Enable Tropospheric Model', variable=self.tropo_var,
529
+ bg=self.colors['bg_card'], fg=self.colors['text'],
530
+ selectcolor='#050810', activebackground=self.colors['bg_card'],
531
+ activeforeground=self.colors['primary']).pack(anchor='w', pady=8)
532
+
533
+ tk.Checkbutton(parent, text='Add AWGN Noise', variable=self.noise_var,
534
+ bg=self.colors['bg_card'], fg=self.colors['text'],
535
+ selectcolor='#050810', activebackground=self.colors['bg_card'],
536
+ activeforeground=self.colors['primary']).pack(anchor='w', pady=8)
537
+
538
+ # PRN list
539
+ tk.Label(parent, text='PRN Satellites (1-32, comma separated)', bg=self.colors['bg_card'],
540
+ fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(15, 5))
541
+
542
+ tk.Entry(parent, textvariable=self.prn_var,
543
+ bg='#050810', fg=self.colors['text'],
544
+ insertbackground=self.colors['primary'],
545
+ font=self.fonts['mono']).pack(fill=tk.X)
546
+
547
+ def create_trajectory_tab(self, parent):
548
+ """Create trajectory configuration tab"""
549
+ # Speed
550
+ tk.Label(parent, text='Movement Speed (m/s)', bg=self.colors['bg_card'],
551
+ fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(0, 5))
552
+
553
+ tk.Spinbox(parent, from_=0, to=100, textvariable=self.speed_var,
554
+ bg='#050810', fg=self.colors['text'], font=self.fonts['normal'],
555
+ insertbackground=self.colors['primary']).pack(fill=tk.X, pady=(0, 15))
556
+
557
+ # Direction
558
+ tk.Label(parent, text='Path Direction (degrees)', bg=self.colors['bg_card'],
559
+ fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(0, 5))
560
+
561
+ tk.Spinbox(parent, from_=0, to=360, textvariable=self.direction_var,
562
+ bg='#050810', fg=self.colors['text'], font=self.fonts['normal'],
563
+ insertbackground=self.colors['primary']).pack(fill=tk.X, pady=(0, 15))
564
+
565
+ # Radius
566
+ tk.Label(parent, text='Circle Radius (m) - For circular mode', bg=self.colors['bg_card'],
567
+ fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(0, 5))
568
+
569
+ tk.Spinbox(parent, from_=10, to=10000, textvariable=self.radius_var,
570
+ bg='#050810', fg=self.colors['text'], font=self.fonts['normal'],
571
+ insertbackground=self.colors['primary']).pack(fill=tk.X)
572
+
573
+ def create_bottom_section(self, parent):
574
+ """Create bottom section with satellite viz and command output"""
575
+ frame = tk.Frame(parent, bg=self.colors['bg_dark'])
576
+
577
+ # Satellite visualization
578
+ sat_frame = tk.Frame(frame, bg=self.colors['bg_card'],
579
+ highlightbackground=self.colors['border'],
580
+ highlightthickness=1, padx=20, pady=20)
581
+ sat_frame.pack(fill=tk.X, pady=(0, 15))
582
+
583
+ # Header
584
+ sat_header = tk.Frame(sat_frame, bg=self.colors['bg_card'])
585
+ sat_header.pack(fill=tk.X, pady=(0, 15))
586
+
587
+ tk.Label(sat_header, text='🛰️', bg=self.colors['bg_card'],
588
+ fg=self.colors['secondary'], font=('Segoe UI', 16)).pack(side=tk.LEFT)
589
+
590
+ tk.Label(sat_header, text='GPS Constellation Visualization', bg=self.colors['bg_card'],
591
+ fg=self.colors['primary'], font=self.fonts['title']).pack(side=tk.LEFT, padx=10)
592
+
593
+ # Satellite canvas
594
+ self.sat_canvas = tk.Canvas(sat_frame, bg='#050810', height=250,
595
+ highlightthickness=1, highlightbackground=self.colors['border'])
596
+ self.sat_canvas.pack(fill=tk.X)
597
+
598
+ # Draw orbit rings
599
+ cx, cy = 400, 125
600
+ for r in [50, 75, 100, 125]:
601
+ self.sat_canvas.create_oval(cx-r, cy-r, cx+r, cy+r,
602
+ outline='#00ff8820', width=1)
603
+
604
+ # Earth center
605
+ self.sat_canvas.create_oval(cx-15, cy-15, cx+15, cy+15,
606
+ fill=self.colors['secondary'],
607
+ outline='', tags='earth')
608
+ self.sat_canvas.create_oval(cx-25, cy-25, cx+25, cy+25,
609
+ outline=self.colors['secondary'], width=2)
610
+
611
+ # Create satellites
612
+ self.create_satellites()
613
+
614
+ # Info text
615
+ tk.Label(sat_frame,
616
+ text='ℹ️ Visual representation of visible satellites based on selected location and time. Blue dot represents receiver position.',
617
+ bg=self.colors['bg_card'], fg=self.colors['text_muted'],
618
+ font=self.fonts['small'], wraplength=700).pack(anchor='w', pady=(10, 0))
619
+
620
+ # Command output
621
+ cmd_frame = tk.Frame(frame, bg=self.colors['bg_card'],
622
+ highlightbackground=self.colors['border'],
623
+ highlightthickness=1, padx=20, pady=20)
624
+ cmd_frame.pack(fill=tk.BOTH, expand=True)
625
+
626
+ # Header
627
+ cmd_header = tk.Frame(cmd_frame, bg=self.colors['bg_card'])
628
+ cmd_header.pack(fill=tk.X, pady=(0, 15))
629
+
630
+ tk.Label(cmd_header, text='💻', bg=self.colors['bg_card'],
631
+ fg=self.colors['secondary'], font=('Segoe UI', 16)).pack(side=tk.LEFT)
632
+
633
+ tk.Label(cmd_header, text='Generated Command', bg=self.colors['bg_card'],
634
+ fg=self.colors['primary'], font=self.fonts['title']).pack(side=tk.LEFT, padx=10)
635
+
636
+ # Command display
637
+ cmd_display_frame = tk.Frame(cmd_frame, bg='#050810',
638
+ highlightbackground=self.colors['border'],
639
+ highlightthickness=1)
640
+ cmd_display_frame.pack(fill=tk.X, pady=(0, 10))
641
+
642
+ # Copy button
643
+ copy_btn = tk.Button(cmd_display_frame, text='📋 Copy',
644
+ bg='#1a2332', fg=self.colors['text'],
645
+ font=self.fonts['small'], padx=10, pady=2,
646
+ command=self.copy_command, cursor='hand2',
647
+ relief=tk.FLAT)
648
+ copy_btn.pack(side=tk.RIGHT, padx=5, pady=5)
649
+
650
+ self.cmd_text = tk.Text(cmd_display_frame, bg='#050810', fg=self.colors['primary'],
651
+ font=self.fonts['mono'], height=4, wrap=tk.WORD,
652
+ padx=10, pady=10, insertbackground=self.colors['primary'])
653
+ self.cmd_text.pack(fill=tk.X, side=tk.LEFT, expand=True)
654
+ self.cmd_text.insert('1.0', '# Click "Generate Windows Command" to create gps-sdr-sim command...')
655
+ self.cmd_text.config(state=tk.DISABLED)
656
+
657
+ # Console
658
+ tk.Label(cmd_frame, text='Console Output', bg=self.colors['bg_card'],
659
+ fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(0, 5))
660
+
661
+ self.console = scrolledtext.ScrolledText(cmd_frame, bg='#050810', fg=self.colors['primary'],
662
+ font=self.fonts['mono'], height=8,
663
+ padx=10, pady=10, wrap=tk.WORD,
664
+ insertbackground=self.colors['primary'])
665
+ self.console.pack(fill=tk.BOTH, expand=True)
666
+ self.console.config(state=tk.DISABLED)
667
+
668
+ # Info box
669
+ info_box = tk.Frame(cmd_frame, bg='#050810', padx=15, pady=15,
670
+ highlightbackground=self.colors['secondary'],
671
+ highlightthickness=2)
672
+ info_box.pack(fill=tk.X, pady=(15, 0))
673
+
674
+ tk.Label(info_box, text='ℹ️ Windows Setup Instructions', bg='#050810',
675
+ fg=self.colors['secondary'], font=self.fonts['title']).pack(anchor='w')
676
+
677
+ instructions = """1. Install gps-sdr-sim for Windows (requires Visual Studio Build Tools)
678
+ 2. Install HackRF Tools for Windows
679
+ 3. Ensure HackRF One is connected and drivers are installed (Zadig)
680
+ 4. Run the generated command in Command Prompt or PowerShell
681
+ 5. ⚠️ Verify you are in a shielded room or have authorization before transmitting!"""
682
+
683
+ tk.Label(info_box, text=instructions, bg='#050810',
684
+ fg=self.colors['text_muted'], font=self.fonts['small'],
685
+ justify=tk.LEFT, wraplength=700).pack(anchor='w', pady=(10, 0))
686
+
687
+ return frame
688
+
689
+ def create_satellites(self):
690
+ """Create satellite objects on canvas"""
691
+ cx, cy = 400, 125
692
+ sat_count = 8
693
+
694
+ for i in range(sat_count):
695
+ angle = (i / sat_count) * 2 * math.pi
696
+ radius = 60 + (i % 3) * 35
697
 
698
+ # Calculate position
699
+ x = cx + radius * math.cos(angle)
700
+ y = cy + radius * math.sin(angle)
 
 
 
 
 
 
 
 
 
 
 
701
 
702
+ # Create satellite
703
+ sat = self.sat_canvas.create_oval(x-6, y-6, x+6, y+6,
704
+ fill=self.colors['primary'],
705
+ outline='', tags=f'sat_{i}')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
706
 
707
+ # Label
708
+ label = self.sat_canvas.create_text(x, y-15, text=f'G{i+1}',
709
+ fill=self.colors['text_muted'],
710
+ font=self.fonts['small'], tags=f'label_{i}')
711
 
712
+ self.satellites.append({
713
+ 'id': sat,
714
+ 'label': label,
715
+ 'base_angle': angle,
716
+ 'radius': radius,
717
+ 'speed': 0.02 + random.random() * 0.02
718
+ })
719
+
720
+ def start_satellite_animation(self):
721
+ """Start animating satellites"""
722
+ self.animating = True
723
+ self.animate_satellites()
724
+
725
+ def animate_satellites(self):
726
+ """Animate satellite orbits"""
727
+ if not self.animating:
728
+ return
729
+
730
+ cx, cy = 400, 125
731
+
732
+ for sat in self.satellites:
733
+ # Update angle
734
+ sat['base_angle'] += sat['speed']
735
 
736
+ # Calculate new position
737
+ x = cx + sat['radius'] * math.cos(sat['base_angle'])
738
+ y = cy + sat['radius'] * math.sin(sat['base_angle'])
 
 
 
 
 
 
 
 
 
 
739
 
740
+ # Update satellite position
741
+ self.sat_canvas.coords(sat['id'], x-6, y-6, x+6, y+6)
742
+ self.sat_canvas.coords(sat['label'], x, y-15)
743
+
744
+ self.root.after(50, self.animate_satellites)
745
+
746
+ def log(self, message, msg_type='info'):
747
+ """Log message to console"""
748
+ self.console.config(state=tk.NORMAL)
749
+
750
+ timestamp = datetime.now().strftime('%H:%M:%S')
751
+
752
+ # Color coding
753
+ color = self.colors['primary']
754
+ if msg_type == 'error':
755
+ color = self.colors['danger']
756
+ elif msg_type == 'success':
757
+ color = '#00ff00'
758
+
759
+ # Insert with tag
760
+ tag_name = f'color_{msg_type}'
761
+ self.console.tag_config(tag_name, foreground=color)
762
+
763
+ self.console.insert(tk.END, f'[{timestamp}] ', 'muted')
764
+ self.console.insert(tk.END, f'{message}\n', tag_name)
765
+
766
+ self.console.tag_config('muted', foreground=self.colors['secondary'])
767
+
768
+ self.console.see(tk.END)
769
+ self.console.config(state=tk.DISABLED)
770
+
771
+ def after_init(self):
772
+ """Initialize after UI is ready"""
773
+ self.root.after(500, lambda: [
774
+ self.log('GPS SDR Interface initialized'),
775
+ self.log('HackRF One detected on USB (simulated)'),
776
+ self.log('Waiting for user input...')
777
+ ])
778
+
779
+ def generate_command(self):
780
+ """Generate the gps-sdr-sim command"""
781
+ lat = self.lat_var.get()
782
+ lon = self.lon_var.get()
783
+ mode = self.mode_var.get()
784
+ duration = self.duration_var.get()
785
+ sample_rate = self.sample_rate_var.get()
786
+ power = self.power_var.get()
787
+ time_str = self.gps_time_var.get()
788
+
789
+ # Parse time
790
+ try:
791
+ dt = datetime.strptime(time_str, '%Y-%m-%d %H:%M')
792
+ date_str = dt.strftime('%Y%m%d')
793
+ time_formatted = dt.strftime('%H%M%S')
794
+ except ValueError:
795
+ self.log('Error: Invalid date/time format', 'error')
796
+ return
797
+
798
+ # Build command
799
+ command = 'gps-sdr-sim.exe'
800
+ command += f' -e brdc3540.14n'
801
+ command += f' -l {lat:.4f},{lon:.4f},{self.alt_var.get():.1f}'
802
+ command += f' -d {duration}'
803
+ command += f' -s {sample_rate}'
804
+
805
+ if mode == 'dynamic':
806
+ command += f' -t {date_str},{time_formatted}'
807
+ command += f' -v {self.speed_var.get():.1f},{self.direction_var.get():.1f}'
808
+ elif mode == 'circle':
809
+ command += f' -c {self.radius_var.get():.1f}'
810
+
811
+ # Gain adjustment
812
+ command += f' -g {power + 100}'
813
+
814
+ # Output file
815
+ command += ' -o gpssim.bin'
816
+
817
+ # HackRF command
818
+ hackrf_cmd = f'\n\n:: Then transmit with HackRF:\nhackrf_transfer.exe -t gpssim.bin -f 1575420000 -s {int(float(sample_rate) * 1000000)} -a 1 -x 40'
819
+
820
+ full_command = command + hackrf_cmd
821
+
822
+ # Update display
823
+ self.cmd_text.config(state=tk.NORMAL)
824
+ self.cmd_text.delete('1.0', tk.END)
825
+ self.cmd_text.insert('1.0', full_command)
826
+ self.cmd_text.config(state=tk.DISABLED)
827
+
828
+ # Log
829
+ self.log('Command generated successfully', 'success')
830
+ self.log(f'Coordinates: {lat:.4f}, {lon:.4f}')
831
+ self.log(f'Mode: {mode}, Duration: {duration}s')
832
+ self.log('Ready for execution on Windows', 'success')
833
+
834
+ def copy_command(self):
835
+ """Copy command to clipboard"""
836
+ self.cmd_text.config(state=tk.NORMAL)
837
+ text = self.cmd_text.get('1.0', tk.END).strip()
838
+ self.cmd_text.config(state=tk.DISABLED)
839
+
840
+ self.root.clipboard_clear()
841
+ self.root.clipboard_append(text)
842
+ self.log('Command copied to clipboard', 'success')
843
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
844
 
845
+ def main():
846
+ root = tk.Tk()
847
+ app = GPSSDRApp(root)
848
+ root.mainloop()
 
 
 
849
 
 
 
 
 
 
 
 
 
850
 
851
+ if __name__ == '__main__':
852
+ main()