HALO’s flight segmentation#

To aid in the analysis of flight data, all HALO flights are segmented via timestamps into a system of hierarchical identifiers. Non-exclusive segments are defined by two (YYYY-MM-DD hh:mm:ss) timestamps, the first one defining the start of the segment and the second denoting the first time step after the end of the segment. Timestamps have a temporal resolution of 1 s, and times are given in UTC. Every segment belongs to a kind – a categorical type for segments defined below. It helps to think of segments as an interval of flight time and the corresponding kinds as describing how the aircraft was being operated during this time interval.

Flight segmentation data are provided in a YAML (YAML Ain’t Markup Language) file can be accessed at https://orcestra-campaign.github.io/flight_segmentation/all_flights.yaml or in python directly via the pyorcestra package. This section provides a description of the YAML file and some examples of how to use it with python.

Loading the flight segmentation data#

from orcestra import get_flight_segments

meta = get_flight_segments()

Which flights are included in the flight segmentation?#

flight_ids = [flight_id
              for flights in meta.values()
              for flight_id in flights]
print(f"Totel number of flights: {len(flight_ids)}")
flight_ids
Totel number of flights: 23
['HALO-20240809b',
 'HALO-20240811a',
 'HALO-20240813a',
 'HALO-20240816a',
 'HALO-20240818a',
 'HALO-20240821a',
 'HALO-20240822a',
 'HALO-20240825a',
 'HALO-20240829a',
 'HALO-20240831a',
 'HALO-20240903a',
 'HALO-20240906a',
 'HALO-20240909a',
 'HALO-20240912a',
 'HALO-20240914a',
 'HALO-20240916a',
 'HALO-20240919a',
 'HALO-20240921a',
 'HALO-20240923a',
 'HALO-20240924a',
 'HALO-20240926a',
 'HALO-20240928a',
 'HALO-20240929a']

Segments#

Sometimes it may be useful to have a flat list of segments, irrespective of which flight or platformt they belong to. So we build it here:

segments = [{**s,
             "platform_id": platform_id,
             "flight_id": flight_id
            }
            for platform_id, flights in meta.items()
            for flight_id, flight in flights.items()
            for s in flight["segments"]
           ]

List available segment kinds#

kinds = set(k for s in segments for k in s["kinds"])
kinds
{'ATR_coordination',
 'ascent',
 'atr_coordination',
 'c_pirouette',
 'calibration',
 'circle',
 'decent',
 'descent',
 'ec_track',
 'ec_underpass',
 'meteor_coordination',
 'pace_track',
 'radar_calibration',
 'straight-leg',
 'straight_leg'}

Plotting all segments of a specific flight#

A supplement function is added here for loading HALO position and attitude data to show the application of flight segmentation information.

import xarray as xr
import matplotlib.pyplot as plt
from orcestra.flightplan import sal, tbpb

def get_halo_position_attitude(flight_id):
    root = "ipns://latest.orcestra-campaign.org/products/HALO/position_attitude"
    return (xr.open_dataset(f"{root}/{flight_id}.zarr", engine="zarr")
            .reset_coords().resample(time="1s").mean().load())

def kinds2color(kinds):
    if "circle" and "atr_coordination" in kinds:
        return "C2"
    if "circle" in kinds:
        return "C1"
    if "ec_track" in kinds:
        return "C3"
    return "C0"
flight = "HALO-20240811a"
ds = get_halo_position_attitude(flight)

fig, ax = plt.subplots()
for s in meta["HALO"][flight]["segments"]:
    t = slice(s["start"], s["end"])
    ax.plot(ds.lon.sel(time=t), ds.lat.sel(time=t), c=kinds2color(s["kinds"]))#, label=s["name"])

for k in ["circle", "straight_leg", "ec_track", "atr_coordination"]:
    ax.plot([], [], color=kinds2color(k), label=k)

plt.scatter(sal.lon, sal.lat, marker="o", color="k")
plt.annotate("SAL", (sal.lon, sal.lat))

ax.set_xlabel("longitude / °")
ax.set_ylabel("latitude / °")
ax.spines[['right', 'top']].set_visible(False)
ax.legend()
plt.title(flight);
_images/1a1a4cc96055a3ee7a3cc576d85d1bab9bf645542c68c21f3494b71d19a11099.png

Plotting all segments of a specific kind#

fig, ax = plt.subplots()
kind = "circle"

for f in flight_ids:
    ds = get_halo_position_attitude(f)
    for s in segments:
        if kind in s["kinds"] and f==s["flight_id"]:
            t = slice(s["start"], s["end"])
            ax.plot(ds.lon.sel(time=t), ds.lat.sel(time=t), c=kinds2color(kind), label=s["name"])
ax.set_xlabel("longitude / °")
ax.set_ylabel("latitude / °")

ax.scatter(sal.lon, sal.lat, marker="o", color="k")
ax.annotate("SAL", (sal.lon, sal.lat))
ax.scatter(tbpb.lon, tbpb.lat, marker="o", color="k")
ax.annotate("BARBADOS", (tbpb.lon, tbpb.lat))
ax.spines[['right', 'top']].set_visible(False)
_images/ffc5108d180916addd15c14cb0841d3c4c1558cbaf0c466e30771b6bc8caa02e.png

Some statistics#

segment_ids_by_kind = {kind: [segment["segment_id"]
                              for segment in segments
                              if kind in segment["kinds"]]
                       for kind in kinds}

Print the total number of circles.
Note that those are not necessarily with dropsondes and the circles can have different radii.

len(segment_ids_by_kind["circle"])
87

List the segments that include the tag atr_coordination

segment_ids_by_kind["atr_coordination"]
['HALO-20240811a_9f65',
 'HALO-20240813a_63f7',
 'HALO-20240816a_7241',
 'HALO-20240825a_64c5',
 'HALO-20240831a_e17f',
 'HALO-20240903a_bda6',
 'HALO-20240903a_71a0']

Total time spent on EarthCARE tracks#

import datetime

ec_time = sum([s["end"] - s["start"]
               for s in segments
               if "ec_track" in s["kinds"]
              ], datetime.timedelta())
print(ec_time)
1 day, 4:30:16

Events#

Events are different from segments in having only one timestamp. Examples are the usual “EC meeting points” or station / ship overpasses.

events = [{**e,
             "platform_id": platform_id,
             "flight_id": flight_id
            }
            for platform_id, flights in meta.items()
            for flight_id, flight in flights.items()
            for e in flight["events"]
           ]

EarthCARE underpass events#

How well did HALO manage to meet with EathCARE?

ec_dist = [e["distance"] for e in events if "ec_underpass" in e["kinds"]]
ec_dist
[1123,
 706,
 101,
 111,
 138,
 200,
 71,
 59,
 291,
 4,
 30,
 15,
 18,
 2,
 3,
 10,
 16,
 15,
 20]

Which flights do not have an ec_event?

flight_ids_events = [e["flight_id"] for e in events]
set(flight_ids) - set(flight_ids_events)
{'HALO-20240809b', 'HALO-20240929a'}

Histogram of distance HALO - EarthCARE during meeting point#

fig, ax = plt.subplots()

ax.hist(ec_dist)
ax.set_xlabel("Distance / m")
ax.set_ylabel("Frequency")
ax.spines[['right', 'top']].set_visible(False)
_images/ce545ab7e577bf6b51f152906650f0a08811fe6665e97242f993a292370451a4.png