Live Flight Data Step by Step - Part 15 - Holdings, go-arounds and echoes
In part 10 I found 37 minutes on Tuesday afternoon, 14:14 to 14:51, in which no airliner took off at Zurich. The landings went on. This part asks what the arriving aircraft did in that time. Did they have to wait in a holding pattern? Did any of them go around?
The data is the same as in parts 4 to 14: the Swiss feed from 20 October, 14:00 to 21 October, 11:59 Swiss time, one snapshot about every 35 seconds. All times are Swiss time.
Two ways to wait in the air
- Holding. The aircraft flies a racetrack over a fixed point: a 180° turn, a straight leg of one minute, another 180° turn, one minute back. Standard holdings turn right (ICAO Doc 8168, Vol. I). The official Swiss charts aren’t openly available. According to a chart for flight simulation, Zurich has holdings at three points, used from 7,000 ft upwards. Aircraft wait in them stacked above each other until approach control calls them in.
- Go-around. The crew breaks off the approach or the landing and climbs away (SKYbrary), for example because the runway isn’t clear or the approach isn’t stable. Then it flies a new approach. A study with ADS-B data of 20 large European airports found 1.5 to 6 go-arounds per 1,000 landings (Figuet et al. 2023).
Both should be visible in the positions alone. A holding is a heading that turns through 360° or more at roughly the same altitude. A go-around is an aircraft that comes down close to the runway and climbs again without touching the ground.
During the 37 minutes
First the direct question. The course notebook has two simple detectors:
- Holding: within 20 minutes at least eight positions, turned through at least 360° in total, altitude within 2,000 ft, all positions within 15 km of the centre, at least 250 km/h. It looks only between 6,500 and 30,000 ft and within 80 km of Zurich.
- Go-around: the lowest airborne position within 8 km of the airport and less than 2,500 ft above it. In the five minutes before, the aircraft was at least 300 ft higher and in the air. In the five minutes after, it climbs more than 500 ft before it touches the ground.
For the afternoon of the outage, both find nothing. No holding, no go-around.
No holding doesn’t have to mean no delay. Controllers can also make an aircraft lose time by sending it on a detour or slowing it down. So I measured one more thing: the minutes from the moment an arriving aircraft enters a circle of 50 km around the airport until it lands.
# first position within 4 km of the airport and below 1,800 ft = landing;
# a gap of more than 30 minutes between two such positions = a new landing of the same aircraft
cand = near[(near.dist_km < 4) & (near.altitude_ft < 1800)
& near.flight_phase.isin(["descending", "on ground"])].sort_values(["icao24", "snapshot_time"])
cand["seg"] = (cand.groupby("icao24").snapshot_time.diff() > pd.Timedelta(minutes=30)).groupby(cand.icao24).cumsum()
landings = cand.groupby(["icao24", "seg"]).snapshot_time.min()
For each landing, I go back through the aircraft’s airborne positions within 50 km, as long as there’s no gap of more than five minutes. The notebook counts only the first landing per aircraft. An aircraft that lands twice in my 22 hours – in the evening and again the next morning – would then be missing once. My version finds 220 landings. 210 of them I can follow from at least 45 km out.
That’s fewer than the real number: EUROCONTROL counted 795 and 793 flights on these days (part 14), about half of them arrivals. Close to the ground, the receivers lose many aircraft before they are below 1,800 ft.

During the 37 minutes, 8 aircraft landed that I can follow this way. They needed 8 to 12 minutes from 50 km to the runway, 11.2 minutes in the median. Over all 210 landings, the median is 11.4 minutes.

So during the outage, the arriving aircraft landed as on any other afternoon. Part 10 already showed that the landings didn’t stop. Now I can add that they weren’t delayed in the air either – at least not in the last 50 km.
The longest approaches of the whole window were somewhere else: on Wednesday between 11:30 and 12:00, 11 landings needed 19 minutes in the median, up to 26 minutes. At that time it rained (part 13), and since about 10:00 the aircraft had been landing on runway 28 instead of 14. Whether one of the two, or something else, made the approaches longer, I can’t tell from my data.
Six go-arounds that weren’t
Then I ran both detectors over the whole 22 hours. The holding detector found one aircraft, the go-around detector six.
Six go-arounds in about 350 arrivals would be more than the study above would lead me to expect, but not impossible. So I looked at each of them. Here’s one, SWR9G on Wednesday morning:

The descent is clean. At 10:04 the aircraft is 1,425 ft high, almost on the runway. Then the feed reports it on the ground – and in between, three times, at 3,700, 2,950 and 2,725 ft, several kilometres east of the airport. My detector sees a climb of 2,275 ft and calls it a go-around.
On the map it’s clear what these positions are. They lie on the approach path, exactly where the aircraft was a few minutes earlier. The position at 10:05:13 is 630 m from the one at 10:01:08, and 125 ft lower. These positions are echoes of the approach.
All six “go-arounds” look like this. For every airborne position in the six minutes after the low point, I searched for the nearest position of the same aircraft in the ten minutes before. They are 0.1 to 1.7 km apart and differ by less than 350 ft – while the aircraft was really on the ground or close to it. Some of the echoes come with a speed of 5 to 45 km/h, the speed of an aircraft taxiing, at an altitude of several thousand feet.
The echoes aren’t rare. Of the 206 airline landings I can follow, 45 have at least one, and in 29 of them it’s 500 ft or more above the landing point – enough for my go-around detector. Where they come from, I don’t know. OpenSky delivers them with a current timestamp and under the aircraft’s own ICAO address. For three of them I checked whether another aircraft in the feed was at that position at the time: none was.
A filter against echoes
A real go-around climbs away over the runway and beyond. An echo jumps back to a place the aircraft has already been. So I drop every position after the low point that lies within 2 km and 500 ft of a position the same aircraft had at least one minute earlier:
def echoes(track, t, look_min=10, after_min=6, km=2.0, ft=500):
before = track[track.snapshot_time.between(t - pd.Timedelta(minutes=look_min), t)]
after = track[(track.snapshot_time > t) & (track.snapshot_time <= t + pd.Timedelta(minutes=after_min))
& ~track.on_ground.fillna(False)]
out = []
for i, p in after.iterrows():
b = before[before.snapshot_time <= p.snapshot_time - pd.Timedelta(minutes=1)]
d = haversine_km(b.latitude, b.longitude, p.latitude, p.longitude)
if ((d < km) & ((b.altitude_ft - p.altitude_ft).abs() < ft)).any():
out.append(i)
return out
With this filter, the detector finds no go-around in the 22 hours. That fits the study: with 1.5 to 6 per 1,000 landings, I would expect zero to two among my roughly 350 arrivals.
My first filters were simpler and didn’t work. “Airborne, but slower than 100 km/h” catches the echoes with taxiing speed, but not those that come with the approach speed, like SWR9G at 10:05:13 with 294 km/h. And it would also remove helicopters. “A jump faster than 1,000 km/h” misses echoes that are close enough to the previous position. Only the comparison with the aircraft’s own track caught all six.
The course module enrichment/go_arounds.py has stricter thresholds – less than 1,500 ft above the airport, more than 800 ft of climb. And it counts a climb only while the aircraft is still nearest to the same airport. On the notebook’s run, it found no go-around at Zurich. But I don’t think it’s immune: run on my 22 hours without a METAR, it reports a go-around at Zurich at 11:54 on Wednesday, and others at the Dübendorf airfield, 10 km south-east – among them SWR9G and ELY347, which were landing at Zurich. Their echoes simply sat nearer to Dübendorf. I haven’t checked these one by one.
The one holding
The holding detector found ELY347, an El Al flight, on Wednesday at 10:02. At 7,050 ft, about 25 km north-east of the airport, it flew one loop in about four minutes, then continued its descent and landed on runway 28 at about 10:12.
That’s not a holding in the sense above: one loop, not a racetrack over a holding point, and in the middle of the descent. The course module doesn’t report it. It looks at all positions from 1,000 ft upwards, and within 20 minutes the aircraft descended by much more than 2,000 ft. The notebook’s detector only sees positions above 6,500 ft – there, the loop is almost all that’s left. What’s interesting is the timing. In my data, the last landing on runway 14 was at 09:52, the first on 28 at 09:59. ELY347 flew its loop a few minutes after the change. Whether that’s the reason, the positions don’t say.
What I take from this
The question about the 37 minutes has a short answer: the arrivals didn’t notice. No holding, no go-around, normal approach times. The Skyguide fault stopped the departures and nothing else that my data can see.
The longer lesson is about the data. A detector that finds something rare is the one that needs the closest look. My go-around detector worked correctly on its test profiles and still reported six events that weren’t there – because about one landing in five has positions that repeat the approach after touchdown. Without plotting each case, I would have written about six go-arounds in 22 hours.
What’s next
The next part is the last one of the series: a small dashboard from the pipeline, and a look back at what worked, what didn’t, and what 22 hours of a crowdsourced sample can and can’t tell.
Data source
Flight data: The OpenSky Network – Matthias Schäfer, Martin Strohmeier, Vincent Lenders, Ivan Martinovic and Matthias Wilhelm. “Bringing Up OpenSky: A Large-scale ADS-B Sensor Network for Research”. In Proceedings of the 13th IEEE/ACM International Symposium on Information Processing in Sensor Networks (IPSN), pages 83–94, April 2014. doi:10.1109/IPSN.2014.6846743
Municipality boundaries on the cover: Federal Statistical Office (BFS), generalised boundaries, as in part 12. Airport reference point: OurAirports, public domain.
Useful Links
Holdings and go-arounds
- ICAO Doc 8168, Vol. I – PANS-OPS, including the holding procedures
- SKYbrary: go-around – what it is and why crews do it
- Figuet et al. 2023: go-arounds at large European airports – rates from ADS-B data
Related posts
- Part 10 – analysis – the 37 minutes without take-offs
- Part 13 – the weather at Kloten – rain and the runway change on Wednesday
- Part 14 – the official numbers – the previous part
Related Articles
Live Flight Data Step by Step - Part 14 - The official numbers
My flight data is a sample: what the OpenSky receivers picked up. EUROCONTROL counts every IFR flight. For Zurich on 20 and 21 October, my estimate lands within a few percent of the official count – but the check is rougher than that number suggests.
Live Flight Data Step by Step - Part 1 - OpenSky Network and the first API call
Where live flight data comes from, how ADS-B and the OpenSky Network work, and how to fetch every aircraft over Switzerland with ten lines of Python.
Live Flight Data Step by Step - Part 13 - The weather at Kloten
I put the 10-minute measurements of the MeteoSwiss station at Zurich airport next to the take-offs and landings. In 22 hours the weather barely moved the traffic – the one big dip was the Skyguide fault. The wind fits the runway change that part 12 couldn't explain – but it doesn't prove the cause.
Comments
Comments are powered by giscus. You need a GitHub account to comment.