In this Sherlock, your task is to analyze the drone’s mission file and flight telemetry logs. By correlating waypoints, timestamps, GPS tracks, and system events, you must extract concrete facts about the route, behavior at key points, and the final impact.
The scenario portrayed in this challenge is entirely fictional and created solely for educational and entertainment purposes. Any resemblance to actual persons, living or dead, organizations, or real events is purely coincidental and unintentional. All characters, scenarios, and data presented are products of imagination.
A drone flew a pre-planned spline route around a hilltop landmark in Budapest, with a single 30 second hold, then crossed the Danube by a bridge toward the other half of the city. The flight log shows it disarmed in the air, lost control and hit the ground at about 13 m/s roughly ten and a half minutes after takeoff. Comparing the mission plan against the telemetry gives the planned route, the crash position and the launch point. The launch point is the useful intelligence: it is where the operator was standing.
evidence/AOTR_Winter_Blackout.plan → QGroundControl mission plan, plain JSON. Read it with Python jsonevidence/log.bin → ArduPilot DataFlash binary log (38 MB). Parse with pymavlink (DFReader) and filter by message type (GPS, CMD, MSG, EV)Timestamp note:
TimeUSis microseconds since boot, not wall-clock time. Durations are differences between twoTimeUSvalues. The flight-time answer needs thehh:mm:ss.sssform, and coordinate answers need exactly seven decimals, trailing zeros included.
The plan file lists 48 items under mission.items, and the home position is stored separately in plannedHomePosition. The flight controller counts Home as item 0, so the log’s CMD messages report CTot=49. That total, with Home included, is the number the question asks for.
import json;p=json.load(open('evidence/AOTR_Winter_Blackout.plan'));items=p['mission']['items'];print(len(items))
m=mavutil.mavlink_connection('evidence/log.bin');x=m.recv_match(type='CMD');print(x)
Gotcha: The plan JSON alone gives 48. The question says to include Home, which the controller counts as the first mission command.
Count plan items whose command is 82 (MAV_CMD_NAV_SPLINE_WAYPOINT). They are contiguous, mission items 2 through 47, after the takeoff and before the landing.
import json;p=json.load(open('evidence/AOTR_Winter_Blackout.plan'));items=p['mission']['items'];print(sum(i['command']==82 for i in items))
Plot the spline waypoints. The route is a loop tight around a hilltop on the Buda side of the Danube, centred near 47.4867, 19.0448. That position is Gellért Hill and the fortress on its summit. This answer was reasoned from the coordinates rather than read from a label in either artifact, since the plan carries no place names.
import json;p=json.load(open('evidence/AOTR_Winter_Blackout.plan'));items=p['mission']['items'];print([(i['params'][4],i['params'][5]) for i in items])
Hint: Drop the waypoint list into any map and look at what the loop encircles. Format mask is a single word.
Scan the plan for an item with a non-zero hold time in params[0]. Only one item has it, item 18, a waypoint with a 30 second dwell in the middle of the spline run.
import json;p=json.load(open('evidence/AOTR_Winter_Blackout.plan'));items=p['mission']['items'];print([(n,i['command'],i['params'][0]) for n,i in enumerate(items,1) if i['params'][0]])
Same item as Q4. Its first parameter, the hold time, is 30.
import json;p=json.load(open('evidence/AOTR_Winter_Blackout.plan'));items=p['mission']['items'];print(items[17]['params'])
Spline items 30 to 31 are the two waypoints that straddle the river, at about 47.491 N. At that latitude the crossing is the bridge between Buda and Pest just north of Gellért Hill, which is Elisabeth Bridge. Like Q3 this was reasoned from the coordinates, not read from an artifact.
import json;p=json.load(open('evidence/AOTR_Winter_Blackout.plan'));items=p['mission']['items'];print([(n,i['params'][4],i['params'][5]) for n,i in enumerate(items,1) if 29<=n<=32])
The log’s MSG text Mission: 1 Takeoff marks the start of the flight at TimeUS 48893768. The crash is the SIM Hit ground message from Q8 at 687813098. The difference is 638919330 µs, which is 10 minutes 38.919 seconds. This was derived from those two log events rather than read from a single field, and HTB accepted it.
m=mavutil.mavlink_connection('evidence/log.bin');[print(x.TimeUS,x.Message) for x in iter(lambda:m.recv_match(type='MSG'),None)]
print((687813098-48893768)/1e6)
Dump every MSG record. The last one is SIM Hit ground at 12.93254 m/s, and its TimeUS is the crash timestamp. The drone had been disarmed in flight about 676 s into the log and fell from there.
m=mavutil.mavlink_connection('evidence/log.bin');[print(x) for x in iter(lambda:m.recv_match(type='MSG'),None)]
Take the last GPS sample before the impact message. It sits at TimeUS 687783943, about 30 ms ahead of the crash event. Filter on Status>=3 so only real fixes count.
m=mavutil.mavlink_connection('evidence/log.bin');[print(x.TimeUS,x.Lat,x.Lng) for x in iter(lambda:m.recv_match(type='GPS'),None) if 687600000<x.TimeUS<688000000]
The speed is in the same SIM Hit ground message as Q8, so there is nothing to compute.
m=mavutil.mavlink_connection('evidence/log.bin');print([x.Message for x in iter(lambda:m.recv_match(type='MSG'),None) if 'Hit ground' in x.Message])
Take the maximum of the Alt field across GPS records with a 3D fix.
g=[x for x in iter(lambda:m.recv_match(type='GPS'),None) if x.Status>=3];print(max(x.Alt for x in g))
Same GPS set as Q11, maximum of the Spd field.
print(max(x.Spd for x in g))
The first GPS fix and the home entry (CMD 0) agree on the launch point. Print the values with seven decimals.
print('%.7f, %.7f'%(g[0].Lat,g[0].Lng))
Gotcha: The longitude is 19.01916 with its trailing zeros dropped. The mask needs seven decimals, so pad it to
19.0191600.
| Phase | Action |
|---|---|
| Planning | Mission of 48 items uploaded: takeoff, 46 spline waypoints looping a hilltop landmark, one 30 second hold at item 18, then land |
| Launch | Takeoff from a point on the Pest side, recorded as the home position |
| Flight | Route flown across the Danube past Elisabeth Bridge, reaching 377.07 m altitude and 10.24 m/s ground speed |
| Failure | Vehicle disarmed in flight at about 676 s into the log |
| Impact | Ground contact at 12.93254 m/s, 10 min 38.919 s after takeoff |
| Type | Value |
|---|---|
| Launch coordinates | 47.4819399, 19.0191600 |
| Crash coordinates | 47.4903055, 19.0460476 |
| Mission file | AOTR_Winter_Blackout.plan |
| Flight log | log.bin |
The takeoff point is the strongest lead in a recovered drone. A flight log records the home position and the first GPS fix, which place the operator at launch. Recovering the log from a downed drone should be a first step for any response aimed at the pilot.
Plan and log answer different questions. The plan shows intent (route, hold, landmark); the log shows what happened (disarm, crash, speed). Reading both and reconciling item counts, such as CTot including Home, avoids off-by-one errors.
Time in dataflash logs is relative. TimeUS is time since boot, so durations come from event differences, and pairing a GPS fix with an event needs the nearest sample, not an exact match.
The drone was disarmed in flight and fell about ten and a half minutes into its route, but its log kept both the crash site and the launch point. That launch position is where the operator can be found.