Skip to content
Docs
foxborne.comRequest a pilot

PX4 ULog

What Foxborne reads from a PX4 flight log: the file's structure, the topics and parameters it uses, the GPS time anchor, dropouts and why a truncated log is quarantined and an encrypted one rejected.

ReferenceMarkdown
On this page12

ULog is the format PX4's logger writes to the flight controller's SD card. It is self-describing: the file defines each message format before the data that uses it. Foxborne reads the completed .ulg with parser ulog 0.9.2.

With a valid clock, PX4 names each log for its date and start time. Run R-0931's flight log in the example dataset is log/2026-09-24/14_13_19.ulg. Without a valid clock, the logger numbers its folders and files instead.

File structure#

A ULog has three sections: a fixed 16-byte header, then definitions, then data. All binary values are little endian.

BytesFieldContent
0 to 6File magic55 4C 6F 67 01 12 35: "ULog", then 0x01 0x12 0x35
7VersionThe file format version. PX4 v1.16.0 writes 1.
8 to 15TimestampA uint64: when logging started, in microseconds on the flight controller's boot clock

Foxborne checks the magic bytes before anything else. The Import page then shows PX4 ULog, version 1 and Magic bytes verified.

Messages#

After the header, the file is a sequence of messages. Each starts with three bytes: msg_size, a uint16 giving the message's size without those three bytes, and msg_type, one character. The definitions section ends where the first subscription or logged string begins.

TypeNameSectionWhat it carries
BFlag bitsDefinitions, first after the headerCompatible and incompatible feature flags, and the offsets of any appended data
FFormatDefinitionsOne message layout, written as name:type field;type field;
IInformationBothOne key and its value, such as sys_name, ver_hw, ver_sw or time_ref_utc
MMulti informationBothLong or repeated information values, split across messages
PParameterBothOne parameter value, int32 or float: at log start in the definitions, and on each change in the data
QDefault parameterBothA parameter's default, system-wide or for the current configuration such as the airframe
ASubscriptionDataA topic name, its multi_id instance and the msg_id its samples carry
RUnsubscriptionDataMarks a topic that stops being logged. Not used currently.
DLogged dataDataOne sample of a subscribed topic, tagged with its msg_id
LLogged stringDataA console message: a log level, a uint64 timestamp and the text
CTagged logged stringDataA logged string with a uint16 tag naming its source
SSynchronizationDataEight fixed bytes, 2F 73 13 20 25 0C BB 12, so a reader can recover after a corrupt message
ODropoutDataA uint16 duration in milliseconds: how long the logger lost messages

"Both" means the definitions and the data sections.

Each format with a subscription carries a timestamp field: a uint64 in microseconds that only increases within that subscription. In PX4 it counts from boot, so a sample at 1,132,793,000 µs sits 1,132.793 s after the flight controller started.

PX4 v1.16.0 writes its console messages as L messages. It stores the level as a character, from '0' (EMERG) to '7' (DEBUG), as in the Linux kernel. It writes a synchronization message whenever more than 500 ms have passed since the last one.

How a record is cited#

A flight log event cites its topic and instance, its timestamp, its message number and its byte offset in the file. The evidence inspector shows the original record like this:

Original recordExample dataset, INC-0142: the last obstacle_distance sample before the stallText
topic      obstacle_distance (multi_id 0)
timestamp  1,132,793,000 µs   boot + 1132.793 s
message    1,146,099 of 1,388,540
offset     byte 53,503,302

frame                       12 (MAV_FRAME_BODY_FRD)
sensor_type                 0 (laser)
min_distance                20 cm
max_distance                2000 cm
increment                   5.0 deg
distances[0..7]             412 406 399 401 388 1999 1999 1999

Anyone holding the original file can find message 1,146,099 at byte 53,503,302 and check the values against it.

Topics Foxborne reads#

Foxborne expects a set of topics on each platform. In the example dataset each platform expects 13, and each run logged 11 of them.

TopicQ4 recon quadT4 tracked UGVWhat it gives the investigation
vehicle_statusLoggedLoggedArming state, navigation state and the failsafe flag, for the state band on the timeline and the failsafe_entered rule
vehicle_land_detectedLoggedNot expectedLanding, such as Landed in INC-0142
sensor_gpsLoggedLoggedThe GPS time anchor, eph and satellites_used for gps_quality_drop, and jamming_state and jamming_indicator for gnss_jamming
vehicle_global_positionLoggedLoggedThe track on the map and the ground speed lane
battery_statusLoggedLoggedThe battery lane and the battery_sag rule
failsafe_flagsLoggedLoggedThe cause of a failsafe, such as gcs_connection_lost
telemetry_statusLoggedLoggedPX4's own view of each link, from heartbeat_type_gcs and heartbeat_type_onboard_controller
obstacle_distanceLoggedNot expectedObstacle data from the companion computer. Its stalls are findings in INC-0142 and INC-0139.
estimator_statusLoggedLoggedThe state estimator's status, checked under Flight log topics
cpuloadLoggedLoggedProcessor load and RAM use on the flight controller, each from 0 to 1
logged messagesLoggedLoggedPX4's console messages, such as Failsafe activated and GCS connection regained
distance_sensorNot loggedNot loggedExpected on both platforms and logged on neither in the example dataset
vehicle_imu_statusNot loggedLoggedaccel_vibration_metric for the vibration_high rule
vehicle_commandNot expectedLoggedCommands the vehicle received, such as DO_SET_MODE from system 255 in INC-0143
offboard_control_modeNot expectedNot loggedWhich setpoints offboard control uses. Expected on T4 vehicles and not logged in the example dataset.

The topic_stall rule fires when a logged topic stops updating: no sample for 5 times its median period.

Topics shown as Not logged#

A topic missing from the file shows Not logged under Flight log topics on the run page. The panel's subtitle counts what arrived, for example 11 of 13 expected topics were logged.

A topic can be missing because the logging profile left it out or because nothing on the vehicle published it. A rule that reads a missing topic has nothing to read on that run, so it produces no finding there.

Parameters read from the header#

Foxborne reads parameters from the P messages in the definitions section, which hold the values in force when logging started. It compares them from one run to the next on each vehicle.

ParameterDescription on the vehicle pageQ4 exampleT4 example
COM_DL_LOSS_TSeconds without a ground heartbeat before the data link failsafe1010
NAV_DLL_ACTData link loss action2 (Return)1 (Hold)
COM_FAIL_ACT_TNone. PX4 describes it as the delay between failsafe condition triggered and failsafe reaction.55
COM_OF_LOSS_TSeconds without offboard setpoints before the offboard failsafe1.01.0
COM_OBL_RC_ACTOffboard loss action5 (Hold)5 (Hold)
COM_POS_FS_EPHHorizontal accuracy that invalidates position, metres5Not read
SYS_HAS_NUM_GNSSGNSS receivers expected11
COM_GNSSLOSS_ACTGNSS loss action0 (Warning)0 (Warning)

The vehicle page lists these values under Failsafe parameters, and any differences in the Changes between runs drawer, opened from that panel's header. On UGV-05 in the example dataset it reads NAV_DLL_ACT 0 (Disabled) to 1 (Hold) in R-0924, 22 Sep. PX4 parameters Foxborne reads gives each parameter's range and default.

The GPS time anchor#

The flight log counts from boot, and sensor_gps ties that count to UTC. Three of its fields do the work:

FieldTypeMeaning, from SensorGps.msg in PX4 v1.16.0
timestampuint64Time since system start, in microseconds
timestamp_time_relativeint32Added to timestamp, the time of the UTC reading on the boot clock, in microseconds
time_utc_usecuint64UTC from the GPS module, in microseconds. It reads 0 right after a cold start.

A fix with a nonzero time_utc_usec reads one instant on both clocks and gives the offset from boot to UTC. Foxborne leaves out the zeros and reports how many fixes it used. For run R-0931 the run page reads GPS time from sensor_gps, 1,081 fixes, which puts the flight controller's boot at 14:13:11.286 UTC.

The information key time_ref_utc is no substitute. PX4 writes it from the SDLOG_UTC_OFFSET parameter, so it holds a configured offset in seconds, not a reading of any clock.

GPS time that reaches the flight controller over a serial link carries latency and jitter. That is why the example dataset carries ±40 ms on flight logs.

A run with no fix has no anchor, and the run page shows Elapsed time only. Clocks, anchors and error bounds covers the anchor in full.

Dropouts and the log_dropout rule#

When the logger cannot keep up, it loses messages and records the gap as an O message with its length in milliseconds. The samples in that gap were never written. The ULog specification names a device that is not fast enough as one cause.

The log_dropout rule turns each dropout of 50 ms or longer into a finding worded Observed: flight log has a dropout. In the example dataset's INC-0133, UAS-05 logged three dropouts of 60 to 140 ms once its logging profile included high-rate IMU topics.

The SD card's write rate had saturated, and moving back to the default logging profile fixed it. See log_dropout.

Truncated logs are quarantined#

A log cut off by a power loss ends partway through a message. Foxborne quarantines the file and shows nothing from it as complete, and the run page offers Replace the file. Run R-0933 in the example dataset gives the reason in full:

The flight log ends inside a data message at 41.2 MB. The file was truncated, most likely when power was cut during logging. Nothing from it is shown as complete.

Encrypted logs are rejected#

An encrypted flight log stops at the first step of the import job, Validate type, size and schema. Run R-0932 in the example dataset reads:

The flight log is encrypted. Foxborne does not decrypt logs. Import the decrypted .ulg exported from your key holder.

Ask whoever holds the log's key for the decrypted .ulg, then replace the file from the run page. Import statuses and fixes walks through both cases.