(Not Mod Related) --- Aircraft Tracker
-
I started exploring different things and dont really do the whole forum thing often but figured I'll start sharing my codes for programs, i feel dont get enough open source code for people to learn.This is a fairly simple python aircraft tracker using ads-b and other thangs(im drunk and dont wanna explain it right now, will maybe update with "//" explanations if i dont start another random project)import requests import tkinter as tk from tkinter import ttk import threading import time import math from datetime import datetime, timedelta, timezone import numpy as np from skyfield.api import load, Topos, wgs84 # ============================== # LOCATION (MULTI-SOURCE) # ============================== def get_best_location(): sources = [] try: r = requests.get("http://ip-api.com/json/", timeout=2).json() if r.get("status") == "success": sources.append((r["lat"], r["lon"])) except: pass try: r = requests.get("https://ipinfo.io/json", timeout=2).json() if "loc" in r: lat, lon = map(float, r["loc"].split(",")) sources.append((lat, lon)) except: pass try: r = requests.get("https://ipwho.is/", timeout=2).json() if r.get("success"): sources.append((r["latitude"], r["longitude"])) except: pass try: r = requests.get("https://freegeoip.app/json/", timeout=2).json() sources.append((r["latitude"], r["longitude"])) except: pass if not sources: return 40.7128, -74.0060, 1 lats, lons = zip(*sources) return float(np.mean(lats)), float(np.mean(lons)), len(sources) LAT, LON, CONF = get_best_location() print(f"Location: {LAT:.4f}, {LON:.4f} (Confidence: {CONF} sources)") # ============================== # SATELLITES # ============================== sat_data = {} try: print("Fetching satellite data...") TLE_URL = "https://celestrak.com/NORAD/elements/active.txt" tle = requests.get(TLE_URL, timeout=5).text.splitlines() i = 0 count = 0 while i + 2 < len(tle): name = tle[i].strip() line1 = tle[i+1].strip() line2 = tle[i+2].strip() if name and line1.startswith("1") and line2.startswith("2"): sat_data[name] = (line1, line2) count += 1 i += 3 print(f"Loaded {count} satellites") except Exception as e: print(f"Warning: Could not fetch satellite data - {e}") ts = load.timescale() observer = Topos(latitude_degrees=LAT, longitude_degrees=LON) # ============================== # ADS-B EXCHANGE # ============================== def get_aircraft(): """Fetch aircraft from OpenSky Network API""" try: url = f"https://opensky-network.org/api/states/all?lamin={LAT-2}&lomin={LON-2}&lamax={LAT+2}&lomax={LON+2}" r = requests.get(url, timeout=5) if r.status_code == 200: data = r.json() aircraft = [] for ac in data.get("states", []): if ac[2] is not None and ac[5] is not None and ac[6] is not None: aircraft.append({ "icao": ac[0] if ac[0] else "????", "callsign": (ac[1] if ac[1] else "N/A").strip(), "lat": ac[6], "lon": ac[5], "alt": ac[7] if ac[7] else 0, "speed": ac[9] if ac[9] else 0, "track": ac[10] if ac[10] else 0, }) return aircraft except Exception as e: print(f"Aircraft fetch error: {e}") return [] # ============================== # AIRCRAFT DATABASE (ICAO LOOKUP) # ============================== AIRCRAFT_TYPES = { "A380": "Airbus A380", "A350": "Airbus A350", "A330": "Airbus A330", "A320": "Airbus A320", "A319": "Airbus A319", "B787": "Boeing 787", "B777": "Boeing 777", "B747": "Boeing 747", "B737": "Boeing 737", "B757": "Boeing 757", "B767": "Boeing 767", "CRJ": "Bombardier CRJ", "E190": "Embraer E190", } AIRLINES = { "UAL": "United Airlines", "AAL": "American Airlines", "DAL": "Delta Air Lines", "SWA": "Southwest Airlines", "JBU": "JetBlue", "SKW": "SkyWest", "FFT": "Frontier", "ACA": "Air Canada", "BAW": "British Airways", "DLH": "Lufthansa", "AFR": "Air France", "KLM": "KLM", } def get_aircraft_type(callsign): """Estimate aircraft type from callsign""" for code, aircraft_type in AIRCRAFT_TYPES.items(): if code in callsign: return aircraft_type return "Unknown Aircraft" def get_airline_name(callsign): """Get airline name from callsign""" if len(callsign) >= 3: airline_code = callsign[:3].upper() return AIRLINES.get(airline_code, "Unknown Airline") return "Unknown Airline" def bearing_to_direction(bearing): """Convert bearing (0-360) to cardinal direction""" directions = ["N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"] idx = int((bearing + 11.25) / 22.5) % 16 return directions[idx] # ============================== # DISTANCE & BEARING CALCULATIONS # ============================== def haversine(lat1, lon1, lat2, lon2): """Calculate distance (km) and bearing between two points""" R = 6371 dlat = math.radians(lat2 - lat1) dlon = math.radians(lon2 - lon1) a = math.sin(dlat/2)**2 + math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) * math.sin(dlon/2)**2 c = 2 * math.asin(math.sqrt(a)) dist = R * c y = math.sin(dlon) * math.cos(math.radians(lat2)) x = math.cos(math.radians(lat1)) * math.sin(math.radians(lat2)) - math.sin(math.radians(lat1)) * math.cos(math.radians(lat2)) * math.cos(dlon) bearing = math.degrees(math.atan2(y, x)) % 360 return dist, bearing def feet_to_km(feet): return feet * 0.0003048 # ============================== # GUI SETUP - MAIN MENU # ============================== root = tk.Tk() root.title("Radar Tracker - Select Mode") root.geometry("600x400") root.configure(bg="black") FONT = ("Consolas", 12) FONT_LARGE = ("Consolas", 16, "bold") GREEN = "#00ff66" RED = "#ff3333" YELLOW = "#ffff00" CYAN = "#00ffff" def open_aircraft_tracker(): root.withdraw() create_aircraft_window() def open_satellite_tracker(): root.withdraw() create_satellite_window() # Main menu frame menu_frame = tk.Frame(root, bg="black") menu_frame.pack(expand=True, fill="both", padx=20, pady=20) title = tk.Label(menu_frame, text="RADAR HUD TRACKER", fg=GREEN, bg="black", font=("Consolas", 20, "bold")) title.pack(pady=30) subtitle = tk.Label(menu_frame, text=f"Location: {LAT:.4f}, {LON:.4f}", fg=CYAN, bg="black", font=FONT) subtitle.pack(pady=10) divider = tk.Frame(menu_frame, bg=GREEN, height=2) divider.pack(fill="x", pady=20) button_frame = tk.Frame(menu_frame, bg="black") button_frame.pack(expand=True, fill="both") # Aircraft button aircraft_btn = tk.Button(button_frame, text="✈ AIRCRAFT TRACKER", command=open_aircraft_tracker, bg=RED, fg="black", font=FONT_LARGE, relief="raised", bd=3, padx=40, pady=20, cursor="hand2", activebackground=YELLOW) aircraft_btn.pack(pady=15, fill="x") aircraft_desc = tk.Label(button_frame, text="Track aircraft in real-time using ADS-B data", fg=GREEN, bg="black", font=("Consolas", 10)) aircraft_desc.pack(pady=5) divider2 = tk.Frame(button_frame, bg=YELLOW, height=1) divider2.pack(fill="x", pady=15) # Satellite button satellite_btn = tk.Button(button_frame, text="🛰 SATELLITE TRACKER", command=open_satellite_tracker, bg=GREEN, fg="black", font=FONT_LARGE, relief="raised", bd=3, padx=40, pady=20, cursor="hand2", activebackground=YELLOW) satellite_btn.pack(pady=15, fill="x") satellite_desc = tk.Label(button_frame, text="Track satellites and space objects in orbit", fg=GREEN, bg="black", font=("Consolas", 10)) satellite_desc.pack(pady=5) # ============================== # AIRCRAFT TRACKER WINDOW # ============================== def create_aircraft_window(): aircraft_window = tk.Tk() aircraft_window.title("Aircraft Tracker") aircraft_window.geometry("1300x750") aircraft_window.configure(bg="black") current_objects = [] def on_closing(): aircraft_window.destroy() root.deiconify() aircraft_window.protocol("WM_DELETE_WINDOW", on_closing) # Top info panel top_frame = tk.Frame(aircraft_window, bg="black") top_frame.pack(fill="x") info = tk.Label(top_frame, fg=GREEN, bg="black", font=FONT, text=f"Lat {LAT:.4f} | Lon {LON:.4f}") info.pack(side="left", padx=5) clock = tk.Label(top_frame, fg=GREEN, bg="black", font=FONT) clock.pack(side="left", padx=5) refresh_btn = tk.Button(top_frame, text="REFRESH", bg=GREEN, fg="black", font=("Consolas", 10, "bold"), relief="raised", bd=2, width=15) refresh_btn.pack(side="left", padx=10) status = tk.Label(top_frame, fg=YELLOW, bg="black", font=("Consolas", 8)) status.pack(side="left", padx=5) # Main content frame content_frame = tk.Frame(aircraft_window, bg="black") content_frame.pack(fill="both", expand=True) # Left side - Radar left_frame = tk.Frame(content_frame, bg="black") left_frame.pack(side="left", fill="both", expand=True) debug = tk.Label(left_frame, fg=CYAN, bg="black", font=("Consolas", 8)) debug.pack() SIZE = 500 CENTER = SIZE // 2 canvas = tk.Canvas(left_frame, width=SIZE, height=SIZE, bg="black", highlightthickness=0) canvas.pack(pady=10) def draw_radar(): canvas.delete("radar") canvas.create_oval(10, 10, SIZE-10, SIZE-10, outline=GREEN, tags="radar", width=2) canvas.create_line(CENTER, 10, CENTER, SIZE-10, fill=GREEN, tags="radar") canvas.create_line(10, CENTER, SIZE-10, CENTER, fill=GREEN, tags="radar") for km, ratio in [(50, 0.2), (100, 0.4), (150, 0.6), (200, 0.8), (250, 1.0)]: ring_px = ratio * (CENTER - 20) canvas.create_oval(CENTER-ring_px, CENTER-ring_px, CENTER+ring_px, CENTER+ring_px, outline=GREEN, dash=(2, 2), tags="radar", width=1) canvas.create_text(CENTER + ring_px, CENTER, text=f"{km}km", fill=GREEN, font=("Consolas", 8), tags="radar") draw_radar() map_canvas = tk.Canvas(left_frame, width=360, height=180, bg="black", highlightthickness=1, relief="solid") map_canvas.pack() map_canvas.create_rectangle(0, 0, 360, 180, outline=GREEN, width=2) map_canvas.create_text(180, 10, text="World Map", fill=GREEN, font=("Consolas", 8)) # Right side - Sidebar sidebar_frame = tk.Frame(content_frame, bg="black", width=350) sidebar_frame.pack(side="right", fill="both", padx=10, pady=10) sidebar_frame.pack_propagate(False) sidebar_title = tk.Label(sidebar_frame, text="AIRCRAFT DATA", fg=GREEN, bg="black", font=("Consolas", 11, "bold")) sidebar_title.pack(fill="x", pady=5) sidebar_divider = tk.Frame(sidebar_frame, bg=GREEN, height=2) sidebar_divider.pack(fill="x", pady=5) sidebar_content = tk.Frame(sidebar_frame, bg="black") sidebar_content.pack(fill="both", expand=True) scrollbar = ttk.Scrollbar(sidebar_content) scrollbar.pack(side="right", fill="y") aircraft_listbox = tk.Listbox(sidebar_content, bg="#001100", fg=GREEN, font=("Consolas", 8), yscrollcommand=scrollbar.set, relief="flat", bd=0) aircraft_listbox.pack(side="left", fill="both", expand=True) scrollbar.config(command=aircraft_listbox.yview) def perform_update(): nonlocal current_objects try: now = datetime.now(timezone.utc) clock.config(text=f"UTC {now.strftime('%H:%M:%S')}") canvas.delete("all") draw_radar() map_canvas.delete("all") map_canvas.create_rectangle(0, 0, 360, 180, outline=GREEN, width=2) map_canvas.create_text(180, 10, text="World Map", fill=GREEN, font=("Consolas", 8)) objects = [] aircraft = get_aircraft() for ac in aircraft: dist, bearing = haversine(LAT, LON, ac["lat"], ac["lon"]) alt_km = feet_to_km(ac["alt"]) if dist > 0: elev = math.degrees(math.atan2(alt_km, dist)) else: elev = 90 if dist < 250 and elev > -10: objects.append({ "type": "aircraft", "name": ac["callsign"] if ac["callsign"] != "N/A" else ac["icao"][:8], "distance": dist, "altitude": elev, "azimuth": bearing, "speed": ac["speed"], "lat": ac["lat"], "lon": ac["lon"], "visible": elev > 0, "track": ac["track"], "icao": ac["icao"], }) objects.sort(key=lambda x: x["distance"]) objects = objects[:20] current_objects = objects for obj in objects: az = obj["azimuth"] alt = obj["altitude"] dist = obj["distance"] name = obj["name"] if alt > 90: alt = 90 if alt < -90: alt = -90 r = max(0, (90 - alt) / 90.0 * (CENTER - 30)) a = math.radians(az) x = CENTER + r * math.sin(a) y = CENTER - r * math.cos(a) x = max(20, min(SIZE - 20, x)) y = max(20, min(SIZE - 20, y)) canvas.create_oval(x-6, y-6, x+6, y+6, fill=RED, outline="white", width=2) label_text = f"{name[:12]} {dist:.0f}km {alt:.0f}°" canvas.create_text(x+12, y-10, text=label_text, fill=RED, anchor="w", font=("Consolas", 8)) mx = int((obj["lon"] + 180) / 360 * 360) my = int((90 - obj["lat"]) / 180 * 180) if 0 <= mx < 360 and 0 <= my < 180: map_canvas.create_oval(mx-3, my-3, mx+3, my+3, fill=RED, outline="white") aircraft_listbox.delete(0, tk.END) for obj in objects: aircraft_type = get_aircraft_type(obj["name"]) airline = get_airline_name(obj["name"]) direction = bearing_to_direction(obj["azimuth"]) speed = f"{obj['speed']:.0f} kts" if obj["speed"] else "N/A" header = f"✈ {obj['name']} ({obj['icao']})" aircraft_listbox.insert(tk.END, header) aircraft_listbox.insert(tk.END, f" Aircraft: {aircraft_type}") aircraft_listbox.insert(tk.END, f" Airline: {airline}") aircraft_listbox.insert(tk.END, f" Direction: {direction} ({obj['azimuth']:.0f}°)") aircraft_listbox.insert(tk.END, f" Speed: {speed}") aircraft_listbox.insert(tk.END, f" Distance: {obj['distance']:.1f} km") aircraft_listbox.insert(tk.END, f" Altitude: {int(obj['altitude']*1000):.0f} ft") aircraft_listbox.insert(tk.END, "") visible_aircraft = len([o for o in objects if o.get("visible")]) status.config(text=f"Tracking: {visible_aircraft} aircraft visible") debug.config(text=f"Total aircraft: {len(objects)}") except Exception as e: debug.config(text=f"Error: {str(e)[:60]}") def on_refresh(): refresh_btn.config(state="disabled", bg=YELLOW) refresh_btn.update() perform_update() refresh_btn.config(state="normal", bg=RED) refresh_btn.config(command=on_refresh) # ============================== # SATELLITE TRACKER WINDOW # ============================== def create_satellite_window(): satellite_window = tk.Tk() satellite_window.title("Satellite Tracker") satellite_window.geometry("1300x750") satellite_window.configure(bg="black") def on_closing(): satellite_window.destroy() root.deiconify() satellite_window.protocol("WM_DELETE_WINDOW", on_closing) # Top info panel top_frame = tk.Frame(satellite_window, bg="black") top_frame.pack(fill="x") info = tk.Label(top_frame, fg=GREEN, bg="black", font=FONT, text=f"Lat {LAT:.4f} | Lon {LON:.4f}") info.pack(side="left", padx=5) clock = tk.Label(top_frame, fg=GREEN, bg="black", font=FONT) clock.pack(side="left", padx=5) refresh_btn = tk.Button(top_frame, text="REFRESH", bg=GREEN, fg="black", font=("Consolas", 10, "bold"), relief="raised", bd=2, width=15) refresh_btn.pack(side="left", padx=10) status = tk.Label(top_frame, fg=YELLOW, bg="black", font=("Consolas", 8)) status.pack(side="left", padx=5) # Main content frame content_frame = tk.Frame(satellite_window, bg="black") content_frame.pack(fill="both", expand=True) # Left side - Radar left_frame = tk.Frame(content_frame, bg="black") left_frame.pack(side="left", fill="both", expand=True) debug = tk.Label(left_frame, fg=CYAN, bg="black", font=("Consolas", 8)) debug.pack() SIZE = 500 CENTER = SIZE // 2 canvas = tk.Canvas(left_frame, width=SIZE, height=SIZE, bg="black", highlightthickness=0) canvas.pack(pady=10) def draw_radar(): canvas.delete("radar") canvas.create_oval(10, 10, SIZE-10, SIZE-10, outline=GREEN, tags="radar", width=2) canvas.create_line(CENTER, 10, CENTER, SIZE-10, fill=GREEN, tags="radar") canvas.create_line(10, CENTER, SIZE-10, CENTER, fill=GREEN, tags="radar") for km, ratio in [(500, 0.2), (1000, 0.4), (1500, 0.6), (2000, 0.8), (2500, 1.0)]: ring_px = ratio / 2500.0 * (CENTER - 20) canvas.create_oval(CENTER-ring_px, CENTER-ring_px, CENTER+ring_px, CENTER+ring_px, outline=GREEN, dash=(2, 2), tags="radar", width=1) canvas.create_text(CENTER + ring_px, CENTER, text=f"{km}km", fill=GREEN, font=("Consolas", 8), tags="radar") draw_radar() map_canvas = tk.Canvas(left_frame, width=360, height=180, bg="black", highlightthickness=1, relief="solid") map_canvas.pack() map_canvas.create_rectangle(0, 0, 360, 180, outline=GREEN, width=2) map_canvas.create_text(180, 10, text="World Map", fill=GREEN, font=("Consolas", 8)) # Right side - Sidebar sidebar_frame = tk.Frame(content_frame, bg="black", width=350) sidebar_frame.pack(side="right", fill="both", padx=10, pady=10) sidebar_frame.pack_propagate(False) sidebar_title = tk.Label(sidebar_frame, text="SATELLITE DATA", fg=GREEN, bg="black", font=("Consolas", 11, "bold")) sidebar_title.pack(fill="x", pady=5) sidebar_divider = tk.Frame(sidebar_frame, bg=GREEN, height=2) sidebar_divider.pack(fill="x", pady=5) sidebar_content = tk.Frame(sidebar_frame, bg="black") sidebar_content.pack(fill="both", expand=True) scrollbar = ttk.Scrollbar(sidebar_content) scrollbar.pack(side="right", fill="y") sat_listbox = tk.Listbox(sidebar_content, bg="#001100", fg=GREEN, font=("Consolas", 8), yscrollcommand=scrollbar.set, relief="flat", bd=0) sat_listbox.pack(side="left", fill="both", expand=True) scrollbar.config(command=sat_listbox.yview) sat_objs = {} def perform_update(): try: now = datetime.now(timezone.utc) clock.config(text=f"UTC {now.strftime('%H:%M:%S')}") canvas.delete("all") draw_radar() map_canvas.delete("all") map_canvas.create_rectangle(0, 0, 360, 180, outline=GREEN, width=2) map_canvas.create_text(180, 10, text="World Map", fill=GREEN, font=("Consolas", 8)) t = ts.now() objects = [] sat_count = 0 for name, (l1, l2) in sat_data.items(): try: sat = sat_objs.setdefault(name, load.tle(l1, l2, name)) astrometric = sat.at(t) alt, az, dist = astrometric.apparent().relative_to(observer).altaz() if alt.degrees > -5: objects.append({ "type": "satellite", "name": name[:20], "distance": dist.km, "altitude": alt.degrees, "azimuth": az.degrees, "speed": 7.8, "visible": alt.degrees > 0, }) sat_count += 1 if sat_count >= 100: break except: pass objects.sort(key=lambda x: x["distance"]) objects = objects[:20] for obj in objects: az = obj["azimuth"] alt = obj["altitude"] dist = obj["distance"] name = obj["name"] if alt > 90: alt = 90 if alt < -90: alt = -90 r = max(0, (90 - alt) / 90.0 * (CENTER - 30)) a = math.radians(az) x = CENTER + r * math.sin(a) y = CENTER - r * math.cos(a) x = max(20, min(SIZE - 20, x)) y = max(20, min(SIZE - 20, y)) canvas.create_oval(x-6, y-6, x+6, y+6, fill=GREEN, outline="white", width=2) label_text = f"{name[:12]} {dist:.0f}km {alt:.0f}°" canvas.create_text(x+12, y-10, text=label_text, fill=GREEN, anchor="w", font=("Consolas", 8)) try: sat = sat_objs.get(name) if sat: sub = sat.at(t).subpoint() mx = int((sub.longitude.degrees + 180) / 360 * 360) my = int((90 - sub.latitude.degrees) / 180 * 180) if 0 <= mx < 360 and 0 <= my < 180: map_canvas.create_oval(mx-3, my-3, mx+3, my+3, fill=GREEN, outline="white") except: pass sat_listbox.delete(0, tk.END) for obj in objects: direction = bearing_to_direction(obj["azimuth"]) header = f"🛰 {obj['name']}" sat_listbox.insert(tk.END, header) sat_listbox.insert(tk.END, f" Distance: {obj['distance']:.1f} km") sat_listbox.insert(tk.END, f" Altitude: {obj['altitude']:.2f}°") sat_listbox.insert(tk.END, f" Direction: {direction} ({obj['azimuth']:.0f}°)") sat_listbox.insert(tk.END, f" Speed: {obj['speed']:.1f} km/s") sat_listbox.insert(tk.END, f" Status: {'VISIBLE' if obj['visible'] else 'Below Horizon'}") sat_listbox.insert(tk.END, "") visible_sats = len([o for o in objects if o.get("visible")]) status.config(text=f"Tracking: {visible_sats} satellites visible") debug.config(text=f"Total satellites: {len(objects)} | Catalog: {len(sat_data)}") except Exception as e: debug.config(text=f"Error: {str(e)[:60]}") def on_refresh(): refresh_btn.config(state="disabled", bg=YELLOW) refresh_btn.update() perform_update() refresh_btn.config(state="normal", bg=GREEN) refresh_btn.config(command=on_refresh) root.mainloop() -
How would a Call of Duty mod community benefit from an IRL aircraft tracker?
-
undefined Xerxes moved this topic from BO2 Modding Support & Discussion
-
i didnt make this for a "call of duty mod community" i made this for myself and realized its not a widely open source thing so forgive me for posting it here but i dont care how you benefit from it. at bare minimum i posted it because i can and wanted to for those of similar ambition.