# Live-Tracking

Live-ADS-B-Tracking integrieren — geografische Filter, Flottenansichten und Muster für Kartenanimationen.



Lauffähige Beispiele zur Abfrage von Live-Positionen von Luftfahrzeugen und Feed-Statistiken. Filterparameter und Antwortstruktur finden Sie in der vollständigen Referenz [ADS-B-Flugzeugtracking](/docs/v31/adsb).

| # | Frage                                                                                                 | Verwendete Daten            |
| - | ----------------------------------------------------------------------------------------------------- | --------------------------- |
| 1 | Wie viele Flugzeuge sind gerade in der Luft, und wie hoch ist die durchschnittliche Flughöhe?         | `/adsb/aircraft/statistics` |
| 2 | Welche Flugzeuge fliegen gerade im Luftraum um KJFK?                                                  | `/adsb/aircraft`            |
| 3 | Wie verteilen sich Flugzeuge in einem europäischen Luftraum-Begrenzungsrahmen (Bbox) auf Höhenbänder? | `/adsb/aircraft`            |

<DocRequirements>
  <DocRequirementAuth />
</DocRequirements>

### Installation [#install]

Die folgenden Skripte verwenden `requests` für API-Aufrufe und `rich` für die Terminalausgabe. Einmalig installieren:

```bash
pip install requests rich
```

***

### 1. Live-Luftraumstatistiken [#1-live-airspace-statistics]

**Frage:** Wie viele Flugzeuge werden gerade getrackt, wie viele davon sind in der Luft, und wie hoch ist die durchschnittliche Flughöhe?

Der Statistik-Endpoint aggregiert die Zahlen über den gesamten Feed — Parameter sind nicht erforderlich.

```python
import os

import requests
from rich.console import Console
from rich.panel import Panel

console = Console()

HEADERS = {
    "X-RapidAPI-Key": os.getenv("RAPIDAPI_KEY", "YOUR_RAPIDAPI_KEY"),
    "X-RapidAPI-Host": "skylink-api.p.rapidapi.com",
}
BASE = "https://skylink-api.p.rapidapi.com"


def fetch_statistics() -> dict:
    r = requests.get(
        f"{BASE}/adsb/aircraft/statistics",
        headers=HEADERS,
        timeout=(10, 15),
    )
    r.raise_for_status()
    return r.json()


def main() -> None:
    stats = fetch_statistics()
    alt = stats.get("altitude_stats") or {}

    avg_ft = alt.get("avg_altitude")
    max_ft = alt.get("max_altitude")
    avg_fl = round(avg_ft / 100) if avg_ft is not None else None
    max_fl = round(max_ft / 100) if max_ft is not None else None

    lines = [
        f"[bold]Total tracked:[/bold]    {stats.get('total_aircraft', 0):,}",
        f"[bold]Airborne:[/bold]         {stats.get('airborne', 0):,}",
        f"[bold]On ground:[/bold]        {stats.get('on_ground', 0):,}",
    ]
    if avg_ft is not None:
        lines.append(f"[bold]Avg altitude:[/bold]     {avg_ft:,.0f} ft  (FL{avg_fl:03d})")
    if max_ft is not None:
        lines.append(f"[bold]Max altitude:[/bold]     {max_ft:,.0f} ft  (FL{max_fl:03d})")

    console.print(Panel("\n".join(lines), title="Live ADS-B Statistics", border_style="cyan"))
    console.print(
        "[dim]ADS-B data refreshes every few seconds — do not cache these results.[/dim]"
    )


if __name__ == "__main__":
    main()
```

**Ausgabe:** Ein Panel mit der Gesamtzahl getrackter Flugzeuge, der Anzahl in der Luft, der Anzahl am Boden, der durchschnittlichen Flughöhe in ft und als FL sowie der maximalen Flughöhe.

> **Hinweis:** [ADS-B](https://www.faa.gov/air_traffic/technology/adsb)-Daten sind kurzlebig — der Feed wird alle paar Sekunden aktualisiert. Speichern Sie Antworten nicht im Cache; rufen Sie für Echtzeit-Anwendungsfälle immer aktuelle Daten ab.

***

### 2. Flugzeuge im Luftraum um KJFK [#2-aircraft-in-the-airspace-around-kjfk]

**Frage:** Welche Flugzeuge befinden sich derzeit im Umkreis von 80 km um den John F. Kennedy International Airport in der Luft?

Filtern Sie mit `lat`, `lon` und `radius` und schließen Sie anschließend Einträge am Boden aus, um nur Flugzeuge in der Luft anzuzeigen. Die Ergebnisse werden absteigend nach Flughöhe sortiert.

```python
import os

import requests
from rich.console import Console
from rich.table import Table

console = Console()

HEADERS = {
    "X-RapidAPI-Key": os.getenv("RAPIDAPI_KEY", "YOUR_RAPIDAPI_KEY"),
    "X-RapidAPI-Host": "skylink-api.p.rapidapi.com",
}
BASE = "https://skylink-api.p.rapidapi.com"

# KJFK coordinates
LAT = 40.6413
LON = -73.7781
RADIUS_KM = 80
LIMIT = 20


def fetch_aircraft_near(lat: float, lon: float, radius: int, limit: int) -> list[dict]:
    r = requests.get(
        f"{BASE}/adsb/aircraft",
        headers=HEADERS,
        params={"lat": lat, "lon": lon, "radius": radius, "limit": limit},
        timeout=(10, 15),
    )
    r.raise_for_status()
    return r.json().get("aircraft", [])


def main() -> None:
    aircraft = fetch_aircraft_near(LAT, LON, RADIUS_KM, LIMIT)

    airborne = [
        a for a in aircraft
        if not a.get("is_on_ground")
    ]
    airborne.sort(key=lambda a: a.get("altitude") or 0, reverse=True)

    table = Table(
        title=f"Airborne Aircraft within {RADIUS_KM} km of KJFK  ({len(airborne)} shown)",
        border_style="cyan",
    )
    table.add_column("Callsign")
    table.add_column("ICAO24")
    table.add_column("Altitude (ft)", justify="right")
    table.add_column("Speed (kt)", justify="right")
    table.add_column("Track (°)", justify="right")
    table.add_column("Type")

    for a in airborne:
        alt = f"{int(a['altitude']):,}" if a.get("altitude") is not None else "—"
        spd = str(int(a["ground_speed"])) if a.get("ground_speed") is not None else "—"
        trk = f"{a['track']:.0f}" if a.get("track") is not None else "—"
        table.add_row(
            a.get("callsign") or "—",
            a.get("icao24") or "—",
            alt,
            spd,
            trk,
            a.get("aircraft_type") or "—",
        )

    console.print(table)
    console.print("[dim]On-ground aircraft excluded. ADS-B data refreshes every few seconds.[/dim]")


if __name__ == "__main__":
    main()
```

**Ausgabe:** Eine Tabelle der Flugzeuge in der Luft, absteigend nach Flughöhe sortiert: Callsign | [ICAO24](https://en.wikipedia.org/wiki/ICAO_24-bit_address) | Altitude (ft) | Speed (kt) | Track (°) | Type.

***

### 3. Verteilung auf Höhenbänder in einer europäischen Bbox [#3-altitude-band-distribution-over-a-european-bbox]

**Frage:** Wie verteilen sich die Flugzeuge derzeit auf Höhenbänder im Korridor Paris–Frankfurt?

Fragen Sie einen Begrenzungsrahmen mit `bbox=lat1,lon1,lat2,lon2` ab und ordnen Sie anschließend jedes Flugzeug einer der Kategorien Ground / Low / Medium / High zu. `None`-Flughöhen werden anhand des Flags `is_on_ground` behandelt.

```python
import os
from collections import defaultdict

import requests
from rich.console import Console
from rich.table import Table

console = Console()

HEADERS = {
    "X-RapidAPI-Key": os.getenv("RAPIDAPI_KEY", "YOUR_RAPIDAPI_KEY"),
    "X-RapidAPI-Host": "skylink-api.p.rapidapi.com",
}
BASE = "https://skylink-api.p.rapidapi.com"

# Roughly Paris–Frankfurt bounding box
BBOX = "48,2,52,8"


def fetch_bbox(bbox: str) -> list[dict]:
    r = requests.get(
        f"{BASE}/adsb/aircraft",
        headers=HEADERS,
        params={"bbox": bbox, "limit": 200},
        timeout=(10, 15),
    )
    r.raise_for_status()
    return r.json().get("aircraft", [])


def classify(aircraft: dict) -> str | None:
    alt = aircraft.get("altitude")
    if alt is None:
        return "Ground" if aircraft.get("is_on_ground") else None
    if alt < 100:
        return "Ground"
    if alt < 10_000:
        return "Low  (<10,000 ft)"
    if alt <= 25_000:
        return "Medium  (10,000–25,000 ft)"
    return "High  (>25,000 ft)"


def main() -> None:
    aircraft = fetch_bbox(BBOX)

    bands: dict[str, list[str]] = defaultdict(list)
    for a in aircraft:
        band = classify(a)
        if band is None:
            continue
        callsign = a.get("callsign") or a.get("icao24") or "?"
        bands[band].append(callsign)

    order = ["Ground", "Low  (<10,000 ft)", "Medium  (10,000–25,000 ft)", "High  (>25,000 ft)"]

    table = Table(title=f"Altitude Distribution — bbox {BBOX}", border_style="cyan")
    table.add_column("Band", style="bold")
    table.add_column("Count", justify="right")
    table.add_column("Example callsigns")

    for band in order:
        entries = bands.get(band, [])
        examples = ", ".join(entries[:3]) if entries else "—"
        table.add_row(band, str(len(entries)), examples)

    console.print(table)
    console.print(
        f"[dim]Total aircraft returned: {len(aircraft)}. "
        "Aircraft with unknown altitude and not on_ground are excluded.[/dim]"
    )


if __name__ == "__main__":
    main()
```

**Ausgabe:** Eine Tabelle mit vier Höhenbändern, der jeweiligen Anzahl und bis zu drei Beispiel-Rufzeichen pro Band.
