#!/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()