File names and where they come from
DJI Fly, DJI GO 4 and DJI Pilot 2 write one flight record per flight to the app's FlightRecord folder:
The exact folder depends on the app and device. Our guide to where DJI flight logs are stored lists every path for Android, iOS and DJI controllers.
DJI Pilot 2 and FlightHub 2 can also produce FTR_Record_<flight-id>.txt files. Despite the similar extension, these are a different format: gzip-compressed JSON with device, flight and frameState sections, where each frame lists only the values that changed since the previous one. The rest of this page is about the binary DJIFlightRecord format.
File layout and header
Every flight record starts with a fixed-size header, called the prefix: 100 bytes on format version 6 and later, 12 bytes before that. All numbers are little-endian.
| Offset | Type | Field |
|---|---|---|
| 0 | u64 | Offset of the details (summary) block |
| 8 | u16 | Length of the details block |
| 10 | u8 | Format version |
| 11 – 99 | — | Encryption magic and reserved bytes |
After the header come two regions: the records (the flight itself) and the details block (a summary). Where each one sits depends on the version. Up to v11 the records start right after the header and the details block sits at the offset in the header. v12 is a transitional layout. From v13 the details move into an auxiliary "Info" block in front of the records, followed by a "Version" block that holds the keychain version and department needed for decryption.
The details block is where the DETAILS fields come from: take-off location (street, city, area), start time, home coordinates, total distance and time, maximum height and speeds, photo count and video time, take-off altitude, aircraft model and name, the aircraft, camera, remote controller and battery serial numbers, and the app platform and version.
Each record in the records region is framed the same way:
Older logs can also contain embedded JPEG thumbnails between records, which a parser has to skip.
Format versions and encryption
How a record's content is protected depends on the format version in the header:
| Version | Record protection | Decodable offline? |
|---|---|---|
| v0 – v6 | None. Record payloads are stored as they are. | Yes |
| v7 – v12 | Each record is XOR-scrambled with a key derived from the record type and its first byte. | Yes |
| v13 and later | XOR, then AES-256-CBC per record group. The AES keys are not in the file; they come from DJI's keychain API. | No, a DJI API call is needed per log |
How v13+ decryption works
- Every record type belongs to a feature point, a group with its own AES key, such as Base, Vision, Waypoint, AirLink, Gimbal, RC, Camera, Battery or FlySafe. DJI names them like
FR_Standardization_Feature_Base_1. - The log carries the encrypted key material in its own key-storage records (types 50 and 56), which are not encrypted themselves.
- The parser sends those descriptors, together with the keychain version and department from the Version block, to DJI's flight-record keychain API. The request is authenticated with a DJI developer API key.
- DJI returns an AES key and IV per feature point. Records are then XOR-decoded and AES-256-CBC decrypted.
- The IV chains from record to record within a feature point: each record's last ciphertext block is the IV for the next one. Records must therefore be decrypted in file order, and a record that can't be decrypted still has to advance the chain.
This is why older open-source converters fail on current DJI Fly and DJI Pilot 2 logs, and why any tool that reads them needs network access to DJI.
Record types
These are the record types a parser needs to turn a flight record into a table, and the field categories they feed. A log contains other record types too (waypoint, agriculture, FlySafe and more). Many are written only by certain aircraft or app versions.
| Type id | Record | Contents | Fields |
|---|---|---|---|
| 1 | OSD | Main telemetry: position, height, speeds, attitude, flight mode, GPS satellites. One CSV row per OSD record. | OSD |
| 2 | Home | Home point, return-to-home height, height limit. | HOME |
| 3 | Gimbal | Gimbal pitch, roll and yaw, gimbal mode. | GIMBAL |
| 4, 62 | RC, RC display field | Stick positions and buttons. v13+ logs drop type 4 and carry switch state in type 62. | RC |
| 49 | OFDM | Radio link quality, reported as uplink and downlink signal. | RC |
| 5 | Custom | Wall-clock timestamp and the aircraft odometer. | CUSTOM |
| 7, 8, 22, 63 | CenterBattery, SmartBattery, SmartBatteryGroup, remaining time | Pack voltage, current, cell voltages, temperature, charge level and charge cycles. Newer logs only write the SmartBatteryGroup record. | BATTERY |
| 25 | Camera | Photo and video state, shooting mode, SD card status. | CAMERA |
| 17 | Vision | Obstacle-avoidance and braking flags. | MC |
| 13 | Recover | Aircraft identity: product type, names and serials. | RECOVER |
| 40 | Component serial | Serial numbers of components such as the battery, camera and remote controller. | SERIAL |
| 14 | App GPS | The pilot's phone or controller location. | APPGPS |
| 9, 10, 24 | App tip, warning, serious warning | The messages the DJI app showed during the flight. | APP |
| 50, 56 | Key storage | v13+ only: the encrypted keychain descriptors that are sent to DJI to get the decryption keys. | — |
Inside the OSD record, latitude and longitude are 64-bit floats in radians. Height, speeds and attitude angles are scaled integers in tenths of a unit. Some columns, such as distance from home, are not in the file at all and are calculated from the telemetry.
Sample rate and timestamps
- OSD sets the clock. The OSD record is written about ten times per second. Parsers produce one row per OSD record, so a CSV export has a row every 0.1 s of flight time.
- Everything else is forward-filled. Battery, gimbal, camera and other records arrive at their own rate. Some, like the Vision record on v13+ logs, are written only when a value changes. A parser keeps the last value of each one and attaches it to the next OSD row.
- Flight time vs wall-clock time.
OSD.flyTimecounts flight time, while Custom records carry the wall-clock timestamp. The details block holds the start time of the flight. Together they give each row a UTC time (CUSTOM.updateTime [UTC]).
.txt vs .DAT
The .txt flight record described here is written by the DJI app on your phone or controller. The aircraft also writes its own .DAT log, with more detailed sensor data, which you download with DJI Assistant 2. The two formats are unrelated, and tools for one don't read the other. The Dronelog API and free DJI flight log viewer read the .txt record. More on .DAT logs and how to download them.
How to parse a DJI flight log
You have two practical options:
- Run an open-source parser. dji-log-parser is an MIT-licensed Rust library that implements the format above. Logs below v13 work offline. For v13+ logs you need your own DJI developer API key, because the parser has to call DJI's keychain API for every log. You also maintain the integration and keep up with new aircraft and format changes.
- Call an API. The Dronelog DJI flight log parser takes the file as it is (encrypted or not), handles the DJI keychain call, and returns CSV with up to 477 columns in one request.
Converting a flight record to CSV with Python and the Dronelog API:
import requests
with open("DJIFlightRecord_2026-05-16_[19-02-56].txt", "rb") as log:
response = requests.post(
"https://dronelogapi.com/api/v2/logs/upload/dji",
headers={"Authorization": "Bearer YOUR_KEY"},
files={"file": log},
data={"fields[]": ["OSD.flyTime [s]", "OSD.latitude", "OSD.longitude", "OSD.height [m]"]},
)
response.raise_for_status()
print(response.text[:500]) # CSV, one row per OSD record
Leave out fields[] to get every column. Field names are listed in the DJI flight log field reference. For a side-by-side look at the options, see DJI flight log parsers compared.