#!/usr/bin/env python3 """ GPS SDR Signal Generator - Educational Interface Built with anycoder - https://huggingface.co/spaces/akhaliq/anycoder """ import tkinter as tk from tkinter import ttk, messagebox, scrolledtext from tkinter import font as tkfont import webbrowser import math import random from datetime import datetime, timedelta import threading import time class GPSSDRApp: def __init__(self, root): self.root = root self.root.title("GPS SDR Signal Generator | Educational Interface") self.root.geometry("1400x900") self.root.minsize(1200, 800) # Colors - Cyberpunk/GPS theme self.colors = { 'bg_dark': '#0a0e17', 'bg_card': '#141928', 'primary': '#00ff88', 'secondary': '#00ccff', 'danger': '#ff4444', 'text': '#e0e6ed', 'text_muted': '#8b95a8', 'border': '#00ff8820' } # Configure root self.root.configure(bg=self.colors['bg_dark']) # Custom fonts self.fonts = { 'header': tkfont.Font(family='Segoe UI', size=16, weight='bold'), 'title': tkfont.Font(family='Segoe UI', size=12, weight='bold'), 'normal': tkfont.Font(family='Segoe UI', size=10), 'mono': tkfont.Font(family='Courier New', size=10), 'small': tkfont.Font(family='Segoe UI', size=9) } # Variables self.lat_var = tk.DoubleVar(value=40.7128) self.lon_var = tk.DoubleVar(value=-74.0060) self.alt_var = tk.DoubleVar(value=10) self.duration_var = tk.IntVar(value=300) self.sample_rate_var = tk.StringVar(value='2.6') self.power_var = tk.IntVar(value=-80) self.mode_var = tk.StringVar(value='static') self.speed_var = tk.DoubleVar(value=5) self.direction_var = tk.DoubleVar(value=0) self.radius_var = tk.DoubleVar(value=100) self.prn_var = tk.StringVar(value='1,2,3,4,5,6,7,8') self.iono_var = tk.BooleanVar(value=True) self.tropo_var = tk.BooleanVar(value=True) self.noise_var = tk.BooleanVar(value=True) # Satellite animation self.satellites = [] self.animating = False self.setup_styles() self.create_ui() self.start_satellite_animation() # Initial log self.after_init() def setup_styles(self): """Configure ttk styles for modern look""" style = ttk.Style() style.theme_use('clam') # Configure styles style.configure('Custom.TFrame', background=self.colors['bg_dark']) style.configure('Card.TFrame', background=self.colors['bg_card']) style.configure('Custom.TLabel', background=self.colors['bg_card'], foreground=self.colors['text'], font=self.fonts['normal']) style.configure('Title.TLabel', background=self.colors['bg_card'], foreground=self.colors['primary'], font=self.fonts['title']) style.configure('Muted.TLabel', background=self.colors['bg_card'], foreground=self.colors['text_muted'], font=self.fonts['small']) style.configure('Custom.TButton', background=self.colors['primary'], foreground='#000000', font=self.fonts['normal'], padding=10) style.configure('Secondary.TButton', background=self.colors['bg_card'], foreground=self.colors['secondary'], font=self.fonts['normal']) style.configure('Danger.TButton', background=self.colors['bg_card'], foreground=self.colors['danger'], font=self.fonts['normal']) style.configure('Custom.TEntry', fieldbackground='#050810', foreground=self.colors['text'], insertcolor=self.colors['primary']) style.configure('Custom.TCombobox', fieldbackground='#050810', foreground=self.colors['text']) # Notebook (tabs) style.configure('Custom.TNotebook', background=self.colors['bg_card'], tabmargins=[2, 5, 2, 0]) style.configure('Custom.TNotebook.Tab', background=self.colors['bg_dark'], foreground=self.colors['text_muted'], padding=[15, 8], font=self.fonts['normal']) style.map('Custom.TNotebook.Tab', background=[('selected', self.colors['bg_card'])], foreground=[('selected', self.colors['primary'])], expand=[('selected', [1, 1, 1, 0])]) # Scale style.configure('Custom.Horizontal.TScale', background=self.colors['bg_card'], troughcolor='#050810') def create_ui(self): """Create the main user interface""" # Main container with padding main_container = tk.Frame(self.root, bg=self.colors['bg_dark']) main_container.pack(fill=tk.BOTH, expand=True, padx=20, pady=20) # Header self.create_header(main_container) # Warning banner self.create_warning_banner(main_container) # Content area content = tk.Frame(main_container, bg=self.colors['bg_dark']) content.pack(fill=tk.BOTH, expand=True, pady=15) # Two-column layout content.grid_columnconfigure(0, weight=1) content.grid_columnconfigure(1, weight=1) content.grid_rowconfigure(0, weight=1) # Left column - Map and Coordinates left_frame = self.create_left_column(content) left_frame.grid(row=0, column=0, sticky='nsew', padx=(0, 10)) # Right column - Configuration right_frame = self.create_right_column(content) right_frame.grid(row=0, column=1, sticky='nsew', padx=(10, 0)) # Bottom section - Satellite visualization and Command output bottom_frame = self.create_bottom_section(main_container) bottom_frame.pack(fill=tk.BOTH, expand=True, pady=(15, 0)) def create_header(self, parent): """Create the header with logo and built-with link""" header = tk.Frame(parent, bg='#0d1117', height=60) header.pack(fill=tk.X, pady=(0, 10)) header.pack_propagate(False) # Left side - Logo logo_frame = tk.Frame(header, bg='#0d1117') logo_frame.pack(side=tk.LEFT, padx=20, pady=10) # Satellite icon (using text as icon) icon_label = tk.Label(logo_frame, text='đŸ›°ī¸', bg='#0d1117', fg=self.colors['primary'], font=('Segoe UI', 20)) icon_label.pack(side=tk.LEFT, padx=(0, 10)) title_label = tk.Label(logo_frame, text='GPS SDR Generator', bg='#0d1117', fg=self.colors['primary'], font=self.fonts['header']) title_label.pack(side=tk.LEFT) # Right side - Built with link built_btn = tk.Label(header, text='🔧 Built with anycoder', bg='#0d1117', fg=self.colors['text_muted'], font=self.fonts['small'], cursor='hand2', padx=15, pady=5) built_btn.pack(side=tk.RIGHT, padx=20) built_btn.bind('', lambda e: built_btn.config(fg=self.colors['primary'])) built_btn.bind('', lambda e: built_btn.config(fg=self.colors['text_muted'])) built_btn.bind('', lambda e: webbrowser.open('https://huggingface.co/spaces/akhaliq/anycoder')) def create_warning_banner(self, parent): """Create the legal warning banner""" banner = tk.Frame(parent, bg='#ff444415', highlightbackground=self.colors['danger'], highlightthickness=2, highlightcolor=self.colors['danger']) banner.pack(fill=tk.X, pady=(0, 10)) # Left border accent left_accent = tk.Frame(banner, bg=self.colors['danger'], width=4) left_accent.pack(side=tk.LEFT, fill=tk.Y) content = tk.Frame(banner, bg='#ff444415', padx=15, pady=12) content.pack(fill=tk.BOTH, expand=True) # Warning icon icon_label = tk.Label(content, text='âš ī¸', bg='#ff444415', fg=self.colors['danger'], font=('Segoe UI', 24)) icon_label.pack(side=tk.LEFT, padx=(0, 15)) # Text content text_frame = tk.Frame(content, bg='#ff444415') text_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True) title = tk.Label(text_frame, text='LEGAL NOTICE & SAFETY WARNING', bg='#ff444415', fg=self.colors['danger'], font=self.fonts['title']) title.pack(anchor='w') desc = tk.Label(text_frame, 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.', bg='#ff444415', fg=self.colors['text_muted'], font=self.fonts['small'], wraplength=800, justify=tk.LEFT) desc.pack(anchor='w', pady=(5, 0)) def create_left_column(self, parent): """Create left column with map and coordinates""" frame = tk.Frame(parent, bg=self.colors['bg_card'], highlightbackground=self.colors['border'], highlightthickness=1, padx=20, pady=20) frame.grid_propagate(False) # Header header = tk.Frame(frame, bg=self.colors['bg_card']) header.pack(fill=tk.X, pady=(0, 15)) tk.Label(header, text='đŸ—ēī¸', bg=self.colors['bg_card'], fg=self.colors['secondary'], font=('Segoe UI', 16)).pack(side=tk.LEFT) tk.Label(header, text='Target Coordinates', bg=self.colors['bg_card'], fg=self.colors['primary'], font=self.fonts['title']).pack(side=tk.LEFT, padx=10) # Map canvas (simulated map) self.map_canvas = tk.Canvas(frame, bg='#050810', height=300, highlightthickness=1, highlightbackground=self.colors['border']) self.map_canvas.pack(fill=tk.BOTH, expand=True, pady=(0, 15)) # Draw grid pattern on map self.draw_map_grid() # Map marker (draggable) self.map_marker = self.map_canvas.create_oval(0, 0, 20, 20, fill=self.colors['primary'], outline='white', width=2) self.update_map_marker() # Bind map interactions self.map_canvas.bind('', self.on_map_click) self.map_canvas.bind('', self.on_map_drag) # Status grid status_frame = tk.Frame(frame, bg=self.colors['bg_card']) status_frame.pack(fill=tk.X, pady=(0, 15)) status_frame.grid_columnconfigure(0, weight=1) status_frame.grid_columnconfigure(1, weight=1) # Status items self.status_items = {} status_data = [ ('Latitude', 'lat-display', '40.7128° N'), ('Longitude', 'lon-display', '74.0060° W'), ('Altitude', 'alt-display', '10 m'), ('Satellites', 'sat-count', '8 Visible') ] for i, (label, key, value) in enumerate(status_data): row = i // 2 col = i % 2 item = tk.Frame(status_frame, bg='#050810', padx=10, pady=8, highlightbackground=self.colors['border'], highlightthickness=1) item.grid(row=row, column=col, sticky='nsew', padx=5, pady=5) tk.Label(item, text=label, bg='#050810', fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w') value_label = tk.Label(item, text=value, bg='#050810', fg=self.colors['primary'], font=self.fonts['mono']) value_label.pack(anchor='w') self.status_items[key] = value_label # Manual input input_frame = tk.Frame(frame, bg=self.colors['bg_card']) input_frame.pack(fill=tk.X) tk.Label(input_frame, text='Manual Coordinates Input', bg=self.colors['bg_card'], fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(0, 8)) input_row = tk.Frame(input_frame, bg=self.colors['bg_card']) input_row.pack(fill=tk.X) self.lat_entry = tk.Entry(input_row, textvariable=self.lat_var, bg='#050810', fg=self.colors['text'], insertbackground=self.colors['primary'], font=self.fonts['mono'], width=15) self.lat_entry.pack(side=tk.LEFT, padx=(0, 5)) self.lon_entry = tk.Entry(input_row, textvariable=self.lon_var, bg='#050810', fg=self.colors['text'], insertbackground=self.colors['primary'], font=self.fonts['mono'], width=15) self.lon_entry.pack(side=tk.LEFT, padx=5) update_btn = tk.Button(input_row, text='📍 Update', bg=self.colors['secondary'], fg='#000', font=self.fonts['small'], padx=10, pady=5, command=self.update_from_input, cursor='hand2', relief=tk.FLAT) update_btn.pack(side=tk.LEFT, padx=5) return frame def draw_map_grid(self): """Draw grid pattern on map canvas""" width = 400 height = 300 # Grid lines for i in range(0, width, 40): self.map_canvas.create_line(i, 0, i, height, fill='#00ff8810', width=1) for i in range(0, height, 40): self.map_canvas.create_line(0, i, width, i, fill='#00ff8810', width=1) # Some "streets" for visual interest self.map_canvas.create_line(50, 100, 350, 80, fill='#1a2332', width=3) self.map_canvas.create_line(100, 50, 120, 250, fill='#1a2332', width=3) self.map_canvas.create_line(200, 30, 280, 270, fill='#1a2332', width=2) self.map_canvas.create_line(30, 180, 370, 200, fill='#1a2332', width=2) def update_map_marker(self): """Update marker position based on coordinates""" # Convert lat/lon to canvas coordinates (simplified) # Center of canvas represents the current lat/lon cx = self.map_canvas.winfo_width() // 2 or 200 cy = self.map_canvas.winfo_height() // 2 or 150 self.map_canvas.coords(self.map_marker, cx-10, cy-10, cx+10, cy+10) self.update_status_display() def on_map_click(self, event): """Handle map click""" self.move_marker(event.x, event.y) def on_map_drag(self, event): """Handle marker drag""" self.move_marker(event.x, event.y) def move_marker(self, x, y): """Move marker to position and update coordinates""" cx = max(10, min(x, self.map_canvas.winfo_width() - 10)) cy = max(10, min(y, self.map_canvas.winfo_height() - 10)) self.map_canvas.coords(self.map_marker, cx-10, cy-10, cx+10, cy+10) # Small random offset to simulate map movement offset_x = (cx - self.map_canvas.winfo_width()//2) * 0.0001 offset_y = (cy - self.map_canvas.winfo_height()//2) * 0.0001 new_lat = 40.7128 + offset_y new_lon = -74.0060 + offset_x self.lat_var.set(round(new_lat, 4)) self.lon_var.set(round(new_lon, 4)) self.update_status_display() def update_from_input(self): """Update from manual input""" self.update_status_display() self.update_map_marker() def update_status_display(self): """Update status labels""" lat = self.lat_var.get() lon = self.lon_var.get() self.status_items['lat-display'].config( text=f'{abs(lat):.4f}° {"N" if lat >= 0 else "S"}') self.status_items['lon-display'].config( text=f'{abs(lon):.4f}° {"E" if lon >= 0 else "W"}') self.status_items['alt-display'].config(text=f'{self.alt_var.get():.0f} m') def create_right_column(self, parent): """Create right column with configuration tabs""" frame = tk.Frame(parent, bg=self.colors['bg_card'], highlightbackground=self.colors['border'], highlightthickness=1, padx=20, pady=20) # Header header = tk.Frame(frame, bg=self.colors['bg_card']) header.pack(fill=tk.X, pady=(0, 15)) tk.Label(header, text='âš™ī¸', bg=self.colors['bg_card'], fg=self.colors['secondary'], font=('Segoe UI', 16)).pack(side=tk.LEFT) tk.Label(header, text='Signal Configuration', bg=self.colors['bg_card'], fg=self.colors['primary'], font=self.fonts['title']).pack(side=tk.LEFT, padx=10) # Notebook for tabs self.notebook = ttk.Notebook(frame, style='Custom.TNotebook') self.notebook.pack(fill=tk.BOTH, expand=True, pady=(0, 15)) # Basic tab basic_frame = tk.Frame(self.notebook, bg=self.colors['bg_card'], padx=15, pady=15) self.create_basic_tab(basic_frame) self.notebook.add(basic_frame, text=' Basic ') # Advanced tab advanced_frame = tk.Frame(self.notebook, bg=self.colors['bg_card'], padx=15, pady=15) self.create_advanced_tab(advanced_frame) self.notebook.add(advanced_frame, text=' Advanced ') # Trajectory tab trajectory_frame = tk.Frame(self.notebook, bg=self.colors['bg_card'], padx=15, pady=15) self.create_trajectory_tab(trajectory_frame) self.notebook.add(trajectory_frame, text=' Trajectory ') # Generate button gen_btn = tk.Button(frame, text='đŸ–Ĩī¸ Generate Windows Command', bg=self.colors['primary'], fg='#000', font=self.fonts['title'], pady=12, command=self.generate_command, cursor='hand2', relief=tk.FLAT, activebackground='#00cc6a') gen_btn.pack(fill=tk.X) return frame def create_basic_tab(self, parent): """Create basic configuration tab""" # Simulation Mode tk.Label(parent, text='Simulation Mode', bg=self.colors['bg_card'], fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(0, 5)) mode_combo = ttk.Combobox(parent, textvariable=self.mode_var, values=['static', 'dynamic', 'circle', 'line'], state='readonly', width=30) mode_combo.pack(fill=tk.X, pady=(0, 15)) # Duration tk.Label(parent, text='Signal Duration (seconds)', bg=self.colors['bg_card'], fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(0, 5)) tk.Spinbox(parent, from_=1, to=3600, textvariable=self.duration_var, bg='#050810', fg=self.colors['text'], font=self.fonts['normal'], insertbackground=self.colors['primary']).pack(fill=tk.X, pady=(0, 15)) # Sample Rate tk.Label(parent, text='Sample Rate (MHz)', bg=self.colors['bg_card'], fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(0, 5)) rate_combo = ttk.Combobox(parent, textvariable=self.sample_rate_var, values=['2.6', '5.0', '10.0', '20.0'], state='readonly', width=30) rate_combo.pack(fill=tk.X, pady=(0, 15)) # GPS Time tk.Label(parent, text='GPS Date & Time', bg=self.colors['bg_card'], fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(0, 5)) # Use current time as default now = datetime.now() self.gps_time_var = tk.StringVar(value=now.strftime('%Y-%m-%d %H:%M')) tk.Entry(parent, textvariable=self.gps_time_var, bg='#050810', fg=self.colors['text'], insertbackground=self.colors['primary'], font=self.fonts['mono']).pack(fill=tk.X) def create_advanced_tab(self, parent): """Create advanced configuration tab""" # Power slider tk.Label(parent, text='Signal Power (dB)', bg=self.colors['bg_card'], fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(0, 5)) power_frame = tk.Frame(parent, bg=self.colors['bg_card']) power_frame.pack(fill=tk.X, pady=(0, 5)) power_scale = tk.Scale(power_frame, from_=-100, to=-50, orient=tk.HORIZONTAL, variable=self.power_var, bg=self.colors['bg_card'], fg=self.colors['primary'], highlightthickness=0, troughcolor='#050810', activebackground=self.colors['primary']) power_scale.pack(fill=tk.X, side=tk.LEFT, expand=True) self.power_label = tk.Label(power_frame, text='-80 dBm', bg=self.colors['bg_card'], fg=self.colors['primary'], font=self.fonts['mono'], width=10) self.power_label.pack(side=tk.RIGHT) self.power_var.trace('w', lambda *args: self.power_label.config( text=f'{self.power_var.get()} dBm')) # Checkboxes tk.Checkbutton(parent, text='Enable Ionospheric Model', variable=self.iono_var, bg=self.colors['bg_card'], fg=self.colors['text'], selectcolor='#050810', activebackground=self.colors['bg_card'], activeforeground=self.colors['primary']).pack(anchor='w', pady=8) tk.Checkbutton(parent, text='Enable Tropospheric Model', variable=self.tropo_var, bg=self.colors['bg_card'], fg=self.colors['text'], selectcolor='#050810', activebackground=self.colors['bg_card'], activeforeground=self.colors['primary']).pack(anchor='w', pady=8) tk.Checkbutton(parent, text='Add AWGN Noise', variable=self.noise_var, bg=self.colors['bg_card'], fg=self.colors['text'], selectcolor='#050810', activebackground=self.colors['bg_card'], activeforeground=self.colors['primary']).pack(anchor='w', pady=8) # PRN list tk.Label(parent, text='PRN Satellites (1-32, comma separated)', bg=self.colors['bg_card'], fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(15, 5)) tk.Entry(parent, textvariable=self.prn_var, bg='#050810', fg=self.colors['text'], insertbackground=self.colors['primary'], font=self.fonts['mono']).pack(fill=tk.X) def create_trajectory_tab(self, parent): """Create trajectory configuration tab""" # Speed tk.Label(parent, text='Movement Speed (m/s)', bg=self.colors['bg_card'], fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(0, 5)) tk.Spinbox(parent, from_=0, to=100, textvariable=self.speed_var, bg='#050810', fg=self.colors['text'], font=self.fonts['normal'], insertbackground=self.colors['primary']).pack(fill=tk.X, pady=(0, 15)) # Direction tk.Label(parent, text='Path Direction (degrees)', bg=self.colors['bg_card'], fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(0, 5)) tk.Spinbox(parent, from_=0, to=360, textvariable=self.direction_var, bg='#050810', fg=self.colors['text'], font=self.fonts['normal'], insertbackground=self.colors['primary']).pack(fill=tk.X, pady=(0, 15)) # Radius tk.Label(parent, text='Circle Radius (m) - For circular mode', bg=self.colors['bg_card'], fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(0, 5)) tk.Spinbox(parent, from_=10, to=10000, textvariable=self.radius_var, bg='#050810', fg=self.colors['text'], font=self.fonts['normal'], insertbackground=self.colors['primary']).pack(fill=tk.X) def create_bottom_section(self, parent): """Create bottom section with satellite viz and command output""" frame = tk.Frame(parent, bg=self.colors['bg_dark']) # Satellite visualization sat_frame = tk.Frame(frame, bg=self.colors['bg_card'], highlightbackground=self.colors['border'], highlightthickness=1, padx=20, pady=20) sat_frame.pack(fill=tk.X, pady=(0, 15)) # Header sat_header = tk.Frame(sat_frame, bg=self.colors['bg_card']) sat_header.pack(fill=tk.X, pady=(0, 15)) tk.Label(sat_header, text='đŸ›°ī¸', bg=self.colors['bg_card'], fg=self.colors['secondary'], font=('Segoe UI', 16)).pack(side=tk.LEFT) tk.Label(sat_header, text='GPS Constellation Visualization', bg=self.colors['bg_card'], fg=self.colors['primary'], font=self.fonts['title']).pack(side=tk.LEFT, padx=10) # Satellite canvas self.sat_canvas = tk.Canvas(sat_frame, bg='#050810', height=250, highlightthickness=1, highlightbackground=self.colors['border']) self.sat_canvas.pack(fill=tk.X) # Draw orbit rings cx, cy = 400, 125 for r in [50, 75, 100, 125]: self.sat_canvas.create_oval(cx-r, cy-r, cx+r, cy+r, outline='#00ff8820', width=1) # Earth center self.sat_canvas.create_oval(cx-15, cy-15, cx+15, cy+15, fill=self.colors['secondary'], outline='', tags='earth') self.sat_canvas.create_oval(cx-25, cy-25, cx+25, cy+25, outline=self.colors['secondary'], width=2) # Create satellites self.create_satellites() # Info text tk.Label(sat_frame, text='â„šī¸ Visual representation of visible satellites based on selected location and time. Blue dot represents receiver position.', bg=self.colors['bg_card'], fg=self.colors['text_muted'], font=self.fonts['small'], wraplength=700).pack(anchor='w', pady=(10, 0)) # Command output cmd_frame = tk.Frame(frame, bg=self.colors['bg_card'], highlightbackground=self.colors['border'], highlightthickness=1, padx=20, pady=20) cmd_frame.pack(fill=tk.BOTH, expand=True) # Header cmd_header = tk.Frame(cmd_frame, bg=self.colors['bg_card']) cmd_header.pack(fill=tk.X, pady=(0, 15)) tk.Label(cmd_header, text='đŸ’ģ', bg=self.colors['bg_card'], fg=self.colors['secondary'], font=('Segoe UI', 16)).pack(side=tk.LEFT) tk.Label(cmd_header, text='Generated Command', bg=self.colors['bg_card'], fg=self.colors['primary'], font=self.fonts['title']).pack(side=tk.LEFT, padx=10) # Command display cmd_display_frame = tk.Frame(cmd_frame, bg='#050810', highlightbackground=self.colors['border'], highlightthickness=1) cmd_display_frame.pack(fill=tk.X, pady=(0, 10)) # Copy button copy_btn = tk.Button(cmd_display_frame, text='📋 Copy', bg='#1a2332', fg=self.colors['text'], font=self.fonts['small'], padx=10, pady=2, command=self.copy_command, cursor='hand2', relief=tk.FLAT) copy_btn.pack(side=tk.RIGHT, padx=5, pady=5) self.cmd_text = tk.Text(cmd_display_frame, bg='#050810', fg=self.colors['primary'], font=self.fonts['mono'], height=4, wrap=tk.WORD, padx=10, pady=10, insertbackground=self.colors['primary']) self.cmd_text.pack(fill=tk.X, side=tk.LEFT, expand=True) self.cmd_text.insert('1.0', '# Click "Generate Windows Command" to create gps-sdr-sim command...') self.cmd_text.config(state=tk.DISABLED) # Console tk.Label(cmd_frame, text='Console Output', bg=self.colors['bg_card'], fg=self.colors['text_muted'], font=self.fonts['small']).pack(anchor='w', pady=(0, 5)) self.console = scrolledtext.ScrolledText(cmd_frame, bg='#050810', fg=self.colors['primary'], font=self.fonts['mono'], height=8, padx=10, pady=10, wrap=tk.WORD, insertbackground=self.colors['primary']) self.console.pack(fill=tk.BOTH, expand=True) self.console.config(state=tk.DISABLED) # Info box info_box = tk.Frame(cmd_frame, bg='#050810', padx=15, pady=15, highlightbackground=self.colors['secondary'], highlightthickness=2) info_box.pack(fill=tk.X, pady=(15, 0)) tk.Label(info_box, text='â„šī¸ Windows Setup Instructions', bg='#050810', fg=self.colors['secondary'], font=self.fonts['title']).pack(anchor='w') instructions = """1. Install gps-sdr-sim for Windows (requires Visual Studio Build Tools) 2. Install HackRF Tools for Windows 3. Ensure HackRF One is connected and drivers are installed (Zadig) 4. Run the generated command in Command Prompt or PowerShell 5. âš ī¸ Verify you are in a shielded room or have authorization before transmitting!""" tk.Label(info_box, text=instructions, bg='#050810', fg=self.colors['text_muted'], font=self.fonts['small'], justify=tk.LEFT, wraplength=700).pack(anchor='w', pady=(10, 0)) return frame def create_satellites(self): """Create satellite objects on canvas""" cx, cy = 400, 125 sat_count = 8 for i in range(sat_count): angle = (i / sat_count) * 2 * math.pi radius = 60 + (i % 3) * 35 # Calculate position x = cx + radius * math.cos(angle) y = cy + radius * math.sin(angle) # Create satellite sat = self.sat_canvas.create_oval(x-6, y-6, x+6, y+6, fill=self.colors['primary'], outline='', tags=f'sat_{i}') # Label label = self.sat_canvas.create_text(x, y-15, text=f'G{i+1}', fill=self.colors['text_muted'], font=self.fonts['small'], tags=f'label_{i}') self.satellites.append({ 'id': sat, 'label': label, 'base_angle': angle, 'radius': radius, 'speed': 0.02 + random.random() * 0.02 }) def start_satellite_animation(self): """Start animating satellites""" self.animating = True self.animate_satellites() def animate_satellites(self): """Animate satellite orbits""" if not self.animating: return cx, cy = 400, 125 for sat in self.satellites: # Update angle sat['base_angle'] += sat['speed'] # Calculate new position x = cx + sat['radius'] * math.cos(sat['base_angle']) y = cy + sat['radius'] * math.sin(sat['base_angle']) # Update satellite position self.sat_canvas.coords(sat['id'], x-6, y-6, x+6, y+6) self.sat_canvas.coords(sat['label'], x, y-15) self.root.after(50, self.animate_satellites) def log(self, message, msg_type='info'): """Log message to console""" self.console.config(state=tk.NORMAL) timestamp = datetime.now().strftime('%H:%M:%S') # Color coding color = self.colors['primary'] if msg_type == 'error': color = self.colors['danger'] elif msg_type == 'success': color = '#00ff00' # Insert with tag tag_name = f'color_{msg_type}' self.console.tag_config(tag_name, foreground=color) self.console.insert(tk.END, f'[{timestamp}] ', 'muted') self.console.insert(tk.END, f'{message}\n', tag_name) self.console.tag_config('muted', foreground=self.colors['secondary']) self.console.see(tk.END) self.console.config(state=tk.DISABLED) def after_init(self): """Initialize after UI is ready""" self.root.after(500, lambda: [ self.log('GPS SDR Interface initialized'), self.log('HackRF One detected on USB (simulated)'), self.log('Waiting for user input...') ]) def generate_command(self): """Generate the gps-sdr-sim command""" lat = self.lat_var.get() lon = self.lon_var.get() mode = self.mode_var.get() duration = self.duration_var.get() sample_rate = self.sample_rate_var.get() power = self.power_var.get() time_str = self.gps_time_var.get() # Parse time try: dt = datetime.strptime(time_str, '%Y-%m-%d %H:%M') date_str = dt.strftime('%Y%m%d') time_formatted = dt.strftime('%H%M%S') except ValueError: self.log('Error: Invalid date/time format', 'error') return # Build command command = 'gps-sdr-sim.exe' command += f' -e brdc3540.14n' command += f' -l {lat:.4f},{lon:.4f},{self.alt_var.get():.1f}' command += f' -d {duration}' command += f' -s {sample_rate}' if mode == 'dynamic': command += f' -t {date_str},{time_formatted}' command += f' -v {self.speed_var.get():.1f},{self.direction_var.get():.1f}' elif mode == 'circle': command += f' -c {self.radius_var.get():.1f}' # Gain adjustment command += f' -g {power + 100}' # Output file command += ' -o gpssim.bin' # HackRF command 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' full_command = command + hackrf_cmd # Update display self.cmd_text.config(state=tk.NORMAL) self.cmd_text.delete('1.0', tk.END) self.cmd_text.insert('1.0', full_command) self.cmd_text.config(state=tk.DISABLED) # Log self.log('Command generated successfully', 'success') self.log(f'Coordinates: {lat:.4f}, {lon:.4f}') self.log(f'Mode: {mode}, Duration: {duration}s') self.log('Ready for execution on Windows', 'success') def copy_command(self): """Copy command to clipboard""" self.cmd_text.config(state=tk.NORMAL) text = self.cmd_text.get('1.0', tk.END).strip() self.cmd_text.config(state=tk.DISABLED) self.root.clipboard_clear() self.root.clipboard_append(text) self.log('Command copied to clipboard', 'success') def main(): root = tk.Tk() app = GPSSDRApp(root) root.mainloop() if __name__ == '__main__': main()