mirror of
https://github.com/hanzoskill/flight-tracker.git
synced 2026-10-30 21:03:09 +00:00
160 lines
4.8 KiB
Python
160 lines
4.8 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Simple flight tracker using OpenSky Network API
|
|
Usage:
|
|
python3 track.py --region switzerland
|
|
python3 track.py --callsign SWR123
|
|
python3 track.py --icao <aircraft-code>
|
|
"""
|
|
import sys
|
|
import json
|
|
import urllib.request
|
|
import urllib.parse
|
|
from datetime import datetime
|
|
|
|
# Pre-defined regions (lat_min, lon_min, lat_max, lon_max)
|
|
REGIONS = {
|
|
'switzerland': (45.8, 5.9, 47.8, 10.5),
|
|
'zurich': (47.3, 8.4, 47.5, 8.7),
|
|
'geneva': (46.1, 6.0, 46.3, 6.3),
|
|
'europe': (35.0, -10.0, 70.0, 40.0),
|
|
}
|
|
|
|
def format_altitude(alt_meters):
|
|
"""Convert meters to feet and format"""
|
|
if alt_meters is None:
|
|
return "N/A"
|
|
feet = int(alt_meters * 3.28084)
|
|
return f"{feet:,} ft ({int(alt_meters)}m)"
|
|
|
|
def format_speed(speed_ms):
|
|
"""Convert m/s to km/h and knots"""
|
|
if speed_ms is None:
|
|
return "N/A"
|
|
kmh = int(speed_ms * 3.6)
|
|
knots = int(speed_ms * 1.94384)
|
|
return f"{kmh} km/h ({knots} kts)"
|
|
|
|
def format_heading(heading):
|
|
"""Format heading with cardinal direction"""
|
|
if heading is None:
|
|
return "N/A"
|
|
|
|
directions = ['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']
|
|
idx = int((heading + 22.5) / 45) % 8
|
|
return f"{int(heading)}° {directions[idx]}"
|
|
|
|
def get_flights(region=None, callsign=None, icao24=None):
|
|
"""Query OpenSky Network API"""
|
|
base_url = "https://opensky-network.org/api/states/all"
|
|
params = {}
|
|
|
|
if region and region in REGIONS:
|
|
lamin, lomin, lamax, lomax = REGIONS[region]
|
|
params = {
|
|
'lamin': lamin,
|
|
'lomin': lomin,
|
|
'lamax': lamax,
|
|
'lomax': lomax
|
|
}
|
|
elif icao24:
|
|
params['icao24'] = icao24
|
|
|
|
url = f"{base_url}?{urllib.parse.urlencode(params)}" if params else base_url
|
|
|
|
try:
|
|
with urllib.request.urlopen(url, timeout=10) as response:
|
|
data = json.loads(response.read())
|
|
return data
|
|
except urllib.error.HTTPError as e:
|
|
print(f"HTTP Error {e.code}: {e.reason}", file=sys.stderr)
|
|
sys.exit(1)
|
|
except Exception as e:
|
|
print(f"Error: {e}", file=sys.stderr)
|
|
sys.exit(1)
|
|
|
|
def print_flights(data, callsign_filter=None):
|
|
"""Print flight data in readable format"""
|
|
states = data.get('states', [])
|
|
|
|
if not states:
|
|
print("No flights found.")
|
|
return
|
|
|
|
# Filter by callsign if specified
|
|
if callsign_filter:
|
|
states = [s for s in states if s[1] and callsign_filter.upper() in s[1].strip().upper()]
|
|
|
|
if not states:
|
|
print(f"No flights matching callsign '{callsign_filter}' found.")
|
|
return
|
|
|
|
timestamp = data.get('time', 0)
|
|
dt = datetime.fromtimestamp(timestamp)
|
|
|
|
print(f"\n✈️ Flight Tracker - {dt.strftime('%Y-%m-%d %H:%M:%S')} UTC")
|
|
print(f"📊 Found {len(states)} flight(s)\n")
|
|
print("=" * 100)
|
|
|
|
for state in states:
|
|
icao24 = state[0]
|
|
callsign = (state[1] or 'N/A').strip()
|
|
country = state[2] or 'Unknown'
|
|
altitude = format_altitude(state[7])
|
|
velocity = format_speed(state[9])
|
|
heading = format_heading(state[10])
|
|
vertical_rate = state[11]
|
|
on_ground = state[8]
|
|
|
|
# Climb/descent indicator
|
|
climb = ""
|
|
if vertical_rate:
|
|
if vertical_rate > 0.5:
|
|
climb = "↗️ Climbing"
|
|
elif vertical_rate < -0.5:
|
|
climb = "↘️ Descending"
|
|
else:
|
|
climb = "→ Level"
|
|
|
|
status = "🛬 On Ground" if on_ground else "✈️ In Flight"
|
|
|
|
print(f"\n{callsign:12} | {country:20} | {status}")
|
|
print(f" ICAO24: {icao24}")
|
|
print(f" Altitude: {altitude}")
|
|
print(f" Speed: {velocity}")
|
|
print(f" Heading: {heading}")
|
|
if climb:
|
|
print(f" Status: {climb}")
|
|
|
|
print("\n" + "=" * 100)
|
|
|
|
def main():
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(description='Track flights using OpenSky Network')
|
|
parser.add_argument('--region', choices=list(REGIONS.keys()),
|
|
help='Track flights in a region')
|
|
parser.add_argument('--callsign', help='Filter by callsign (e.g., SWR123)')
|
|
parser.add_argument('--icao', help='Track specific aircraft by ICAO24 code')
|
|
|
|
args = parser.parse_args()
|
|
|
|
if not any([args.region, args.callsign, args.icao]):
|
|
print("Usage: python3 track.py --region switzerland")
|
|
print(" python3 track.py --callsign SWR123")
|
|
print(" python3 track.py --icao <icao24-code>")
|
|
print(f"\nAvailable regions: {', '.join(REGIONS.keys())}")
|
|
sys.exit(1)
|
|
|
|
# Get flight data
|
|
data = get_flights(
|
|
region=args.region,
|
|
icao24=args.icao
|
|
)
|
|
|
|
# Print results
|
|
print_flights(data, callsign_filter=args.callsign)
|
|
|
|
if __name__ == '__main__':
|
|
main()
|