$GNSS-Zeilen), GPX oder einfache CSV mit lat/lon/ele bzw. E/N/H. Die Datei wird in Meter umgerechnet und in Phasen zerlegt. Spuren aus der Schweiz lassen sich auf echtem Gelände an ihrer gemessenen Position auswerten.Aus einer geloggten Flugspur soll an jeder Stelle ablesbar sein, wie hoch über dem Gelände geflogen wurde – nicht über Meer, sondern über dem, was tatsächlich darunter liegt: Fels, Baumwipfel, Gebäude. Dazu drei Ausbaustufen:
| Datensatz | Inhalt | Auflösung | Rolle |
|---|---|---|---|
| swissSURFACE3D Raster | Digitales Oberflächenmodell (DOM) aus LiDAR – mit Vegetation und Bauten | 0,5 m | Referenzfläche für den Bodenabstand |
| swissSURFACE3D | Klassifizierte Punktwolke (LAS) | 15–20 Pkt/m² | Quelle für eigene Raster, z. B. nur Klasse Vegetation |
| swissALTI3D | Digitales Terrainmodell (DTM), ohne Bewuchs | 0,5 / 2 m | Vergleich, Baumhöhen als Differenz DOM − DTM |
| SWISSIMAGE | Orthofoto | 0,1–0,25 m | Kartenhintergrund zur Orientierung |
| GPS-Log | FlySight, Uhr, Vario, Handy – GPX, CSV oder FIT | 1–10 Hz | Flugspur |
Ein GPS-Log liegt in WGS84, die swisstopo-Raster in LV95 (EPSG 2056) mit Höhen im Gebrauchshorizont LN02. Beides muss angeglichen werden, sonst rechnet man einen Bodenabstand aus zwei verschiedenen Welten:
hMSL, also bereits auf das Geoidmodell des Empfängers bezogen; zu LN02 bleiben nur noch wenige Meter Unterschied.
Sauber wird beides über den swisstopo-Dienst REFRAME, pragmatisch über einen festen Versatz je Sprunggebiet.Der Prototyp liest beide Gerätegenerationen. Version 1 schreibt eine CSV mit zwei Kopfzeilen – Spaltennamen, dann Einheiten. Version 2 stellt jeder Zeile ein Schlüsselwort voran: $VAR für Geräteangaben, $COL und $UNIT für Spalten und Einheiten je Sensor, $DATA als Trenner, danach die Messzeilen mit $GNSS.
| Feld | Bedeutung | Einheit | Verwendung hier |
|---|---|---|---|
| time | Zeitstempel UTC | ISO 8601 | Flugzeit, Abtastrate |
| lat, lon | Position | Grad | Umrechnung nach LV95 |
| hMSL | Höhe über Meeresspiegel | m | Flughöhe für den Bodenabstand |
| velN, velE, velD | Geschwindigkeit Nord, Ost, nach unten | m/s | Sinkrate, Horizontalfahrt, Gleitzahl |
| hAcc, vAcc, sAcc | geschätzte Genauigkeit | m bzw. m/s | Bewertung schwacher Abschnitte |
| numSV | Zahl der Satelliten | – | Qualitätsmerkmal unter der Wand |
Ein Log mit 5 Hz genügt für die Auswertung im Tal; für die ersten Sekunden an der Wand ist die höhere Rate des FlySight 2 deutlich besser, weil bei 40 m/s zwischen zwei Sekundenpunkten bereits 40 m Strecke liegen.
Für jeden Spurpunkt P(E, N, H) wird die Geländehöhe aus dem Raster bilinear interpoliert – also aus den vier umliegenden Zellen gewichtet, statt aus der nächstgelegenen. Das vermeidet Treppenstufen im Profil:
AGL(P) = H(P) − DOM(E, N)
Korridor: für jeden Punkt i die Normale n zur Flugrichtung,
Abtastung s = −b/2 … +b/2 in Schritten von 5 m
AGL_korridor(i) = H(i) − max( DOM(E + s·nₑ , N + s·n_n) )
Klassen: kritisch < 25 m
knapp 25 – 60 m
Reserve 60 – 150 m
frei > 150 m
Die Schwellen sind im Prototyp frei einstellbar – jede Linie, jeder Anzug und jede Erfahrungsstufe hat andere. Der Korridorwert ist bewusst pessimistisch: er nimmt die höchste Geländehöhe im Querschnitt, nicht den Mittelwert.
Die Spur wird als starrer Körper behandelt. Relativ zum Exit gespeicherte Offsets (Δe, Δn, Δh) werden um den Winkel φ gedreht und am neuen Exit angesetzt; die Höhe hängt sich an das dortige Gelände:
E' = E_exit + Δe·cos φ + Δn·sin φ (φ = Abflugkurs, im Uhrzeigersinn) N' = N_exit − Δe·sin φ + Δn·cos φ H' = DOM(E_exit, N_exit) + Exitabstand + Δh
Was dabei bewusst nicht mitgerechnet wird: Luftdichte auf anderer Höhe, Wind, Thermik, Talwind am neuen Ort. Eine versetzte Spur zeigt die Geometrie der Linie, nicht deren Flugbarkeit.
Ein Logfile enthält selten nur den Sprung: Anmarsch, Warten am Exit, Packen und Rückweg stehen mit darin. Die Auswertung braucht deshalb zuerst eine Zerlegung in Phasen, wie sie auch die verbreiteten Trackwerkzeuge vornehmen – BASEline und Skyderby arbeiten mit denselben Merkmalen. Der Prototyp erkennt sie aus Sinkrate und Horizontalfahrt, über eine Sekunde geglättet:
| Phase | Erkennungsmerkmal | Im Beispiel |
|---|---|---|
| Boden, Anmarsch | Sinkrate < 1,5 m/s, Fahrt < 2,5 m/s | 19 min vor dem Exit |
| Exit | Sinkrate hält 2 s über 6 m/s, Höhenverlust > 25 m | 09:41:58, 1795 m |
| Freifall und Flug | Sinkrate bis 40 m/s, Gleitzahl steigt | 39 s, bis Gleitzahl 2,9 |
| Schirmöffnung | Sinkrate bricht unter 12 m/s und bleibt dort | 767 m, 1028 m unter dem Exit |
| Schirmflug | Sinkrate 2–6 m/s, Gleitzahl um 2 | 30 s bis zur Landung |
| Landung | 3 s ohne Bewegung | 638 m |
Die Phasen bestimmen, was überhaupt ausgewertet wird: Der Bodenabstand zählt zwischen Exit und Öffnung, denn am Absprung ist er naturgemäss null und unter dem Schirm gelten andere Massstäbe. Enthält eine Datei mehrere Sprünge, wird jeder einzeln erkannt.
In der BASE-Praxis wird ein Exit mit dem Entfernungsmesser vermessen: Distanz und Winkel zu markanten Punkten der Wand ergeben ein Profil, über das die geflogene Linie gelegt wird. Beide Achsen tragen denselben Massstab, deshalb ist die 45°-Diagonale die 1:1-Linie – der Punkt, an dem die zurückgelegte Strecke dem Höhenverlust entspricht. Wie tief unter dem Exit dieser Punkt liegt, ist die gebräuchlichste Kennzahl für die Bewertung eines Abgangs.
Genau dieses Profil lässt sich aus swissSURFACE3D erzeugen, ohne eine Wand zu vermessen: Der Geländeschnitt vom Exit in die Flugrichtung tritt an die Stelle der Lasermessung, und statt einzelner Zielpunkte steht eine durchgehende Kontur zur Verfügung. Der Prototyp zeigt in dieser Ansicht zusätzlich das Gelände unmittelbar unter der Spur – bei einer gekrümmten Linie weicht es vom geraden Schnitt ab, und diese Differenz ist es, die den Bodenabstand am Ende ausmacht.
gdalbuildvrt gelaende.vrt *.tif zu einem virtuellen Raster zusammenfassen. Das VRT lädt nur, was gerade gebraucht wird – Kachelmosaike bleiben so handlich.track_points), dann Reprojizieren nach 2056 und als GeoPackage speichern. Höhe aus dem Feld ele in ein Feld h_ln02 überführen, Geoid-Korrektur abziehen.dom an jedem Spurpunkt.agl: "h_ln02" - "dom". Symbolisierung: abgestufte Farben auf agl mit den eigenen Klassengrenzen, Linienstärke 1,2 mm.max, oder feiner: die Linie in Abschnitte teilen (Linien teilen nach Maximallänge), je Abschnitt puffern und einzeln auswerten.In der Python-Konsole von QGIS ausführbar; ersetzt die Schritte 4 bis 6 für eine beliebige Zahl von Flügen.
from qgis.core import QgsRasterLayer, QgsVectorLayer, QgsPointXY
import math
dom = QgsRasterLayer('/daten/gelaende.vrt', 'DOM')
trk = QgsVectorLayer('/daten/flug.gpkg|layername=spur', 'Spur', 'ogr')
prov = dom.dataProvider()
GEOID, KORRIDOR, SCHRITT = 49.3, 80.0, 5.0 # m
def dom_h(e, n):
v, ok = prov.sample(QgsPointXY(e, n), 1)
return v if ok else None
trk.startEditing()
for i, f in enumerate(trk.getFeatures()):
p = f.geometry().asPoint()
h = f['ele'] - GEOID # ellipsoidisch -> LN02
g = dom_h(p.x(), p.y())
f['agl'] = None if g is None else h - g
# Querprofil: Normale aus der lokalen Flugrichtung
nb = trk.getFeature(f.id() + 1 if f.id() + 1 < trk.featureCount() else f.id() - 1)
q = nb.geometry().asPoint()
dx, dy = q.x() - p.x(), q.y() - p.y()
L = math.hypot(dx, dy) or 1.0
nx, ny = -dy / L, dx / L # Normale
hoch = g or -9999
s = -KORRIDOR / 2
while s <= KORRIDOR / 2:
v = dom_h(p.x() + s * nx, p.y() + s * ny)
if v is not None:
hoch = max(hoch, v)
s += SCHRITT
f['agl_korridor'] = h - hoch
trk.updateFeature(f)
trk.commitChanges()
Die Felder agl und agl_korridor müssen vorher als double angelegt sein. Für grosse Logs lohnt es sich, das Raster einmal in ein NumPy-Array zu lesen statt pro Punkt zu samplen – bei 10 Hz und mehreren Minuten Flug ist das der Unterschied zwischen Sekunden und Minuten.