Read a flight back
Three tools read dataflash logs, from coarse to fine: log_summary for the flight by phase, log_fields for a time series, log_messages for whole records. They read the file afresh each time through pymavlink’s per-type index, so a 45 MB log summarizes in about a third of a second and a log SITL is still writing reads up to its current end.
Name the log
Section titled “Name the log”Every log tool takes log as one of:
- a session id, for that open session’s live log;
- a kept log’s name, as
close_sitlreported it orlist_logslists it; - a path to any
.BIN(over the HTTP transport, only under the server’s data directory).
A session id stops working once the session is closed, because the session is gone. Use the kept name from close_sitl then.
list_logs(limit=3)[ { "name": "20261003T202311Z-gusty-b.BIN", "live": false, "size_mb": 10.6, "modified": "2026-10-03T20:23:21+00:00", "pinned": false }, { "name": "20261003T202301Z-calm-a.BIN", "live": false, "size_mb": 10.4, "modified": "2026-10-03T20:23:11+00:00", "pinned": false }]Open sessions are listed first with live: true, then kept logs newest first.
The flight by phase
Section titled “The flight by phase”log_summary(log="20261003T202311Z-gusty-b.BIN"){ "firmware": "ArduPlane V4.8.0-dev (4c98c922)", "duration_s": 82.792, "energy_wh": 5.4932, "lift_motor_channels": [5, 6, 7, 8], "phases": [ { "t0_s": 10.801, "t1_s": 49.514, "mode": "QLOITER", "armed": false, "volts": 12.6, "amps": 0.0, "energy_wh": 0.0 }, { "t0_s": 49.514, "t1_s": 72.917, "mode": "QLOITER", "armed": true, "lift_pct": 73.1, "amps": 42.5, "amps_peak": 68.0, "volts": 12.18, "roll_abs_max_deg": 3.29, "alt_max_m": 7.57, "energy_wh": 3.3607 }, { "t0_s": 72.917, "t1_s": 93.513, "mode": "QLAND", "armed": true, "lift_pct": 58.4, "amps": 30.6, "amps_peak": 46.8, "volts": 12.29, "roll_abs_max_deg": 2.83, "alt_max_m": 7.45, "energy_wh": 2.1324 }, { "t0_s": 93.513, "t1_s": 93.593, "mode": "QLAND", "armed": false, "energy_wh": 0.0001 } ], "events": [ { "t_s": 10.801, "text": "ArduPlane V4.8.0-dev (4c98c922)" } /* ... */ ], "message_counts": { "ATT": 828, "BAT": 828, "RCOU": 2071 /* and the rest */ }}How the summary is built:
- Phases split at every flight-mode change, arm and disarm, and, in AUTO, at every mission item, with
mission_itemandmission_cmdset. A CMD record outside AUTO (a mission being loaded) is not a phase boundary. Spans shorter than 0.05 s are dropped. - Averages per phase: airspeed (ARSP, or CTUN without an airspeed sensor), groundspeed, throttle, lift motors, pitch, angle of attack, current, voltage, climb rate. Peaks: absolute roll, current, height above home.
- Lift motors are the channels whose
SERVOn_FUNCTIONis a motor (33 to 40) in the log’s own parameters, so nothing about the airframe is assumed.lift_pctis their mean output above 1000 µs, in percent. - Energy is BAT volts times amps, integrated by the trapezoid rule with samples interpolated to the phase bounds, so the phases add up to the flight’s total. It is
nullwhen the log has no battery records. - Events are every autopilot text, ERR and EV record, with EV ids in words (
EV armed,EV EKF yaw reset).
Any series, with units
Section titled “Any series, with units”log_fields(log="20261003T202311Z-gusty-b.BIN", msg="BAT", fields=["Volt", "Curr"], step=10){ "msg": "BAT", "columns": ["t_s", "Volt", "Curr"], "units": ["s", "V", "A"], "rows": [ [51.256156, 12.018, 57.29], [61.355448, 12.162, 43.80] /* ... */ ], "matched": 828, "step_s": 10.0, "thinned": true}step gives one row per that many seconds; past max_rows (2000 by default, at most 5000) the step widens to fit and thinned says so. t0 and t1 cut a window in seconds since boot, the same clock as snapshot.t_sim_s and the step results. instance picks one sensor or EKF core for message types that have an instance column.
An unknown message or column is answered with what the log does have, so a guess costs one call:
BAT has no Current; its columns: TimeUS, Inst, Volt, VoltR, Curr, CurrTot, EnrgTot, Temp, Res, RemPct, H, SHWhole messages
Section titled “Whole messages”log_messages(log=..., types=["MODE", "EV"], limit=20){ "messages": [ { "t_s": 10.800678, "type": "MODE", "Mode": 19, "ModeNum": 19, "Rsn": 2 }, { "t_s": 49.576828, "type": "EV", "Id": 10 }, { "t_s": 67.99779, "type": "MODE", "Mode": 20, "ModeNum": 20, "Rsn": 2 }, { "t_s": 88.866106, "type": "EV", "Id": 11 } ], "more": false}Messages come in time order with every field. more: true means limit (at most 1000) cut the list short; narrow t0 and t1 and ask again.
Compare two
Section titled “Compare two”log_compare(a, b) lines up two flights’ phases and diffs everything: see Run a gusty A/B test.
From Python
Section titled “From Python”The same functions are in the library, returning the same dicts:
from mcardupilot import logread, logcompare
s = logread.summary("flight.BIN")roll = logread.fields("flight.BIN", "ATT", ["Roll"], t0=50, t1=70, step=0.5)diff = logcompare.compare("a.BIN", "b.BIN")