# PX4 ULog

URL: /collect/formats/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.



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 [#file-structure]

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

### Header [#header]

| Bytes   | Field      | Content                                                                                 |
| ------- | ---------- | --------------------------------------------------------------------------------------- |
| 0 to 6  | File magic | `55 4C 6F 67 01 12 35`: "ULog", then `0x01 0x12 0x35`                                   |
| 7       | Version    | The file format version. PX4 v1.16.0 writes `1`.                                        |
| 8 to 15 | Timestamp  | A `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 [#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.

| Type | Name                 | Section                             | What it carries                                                                                          |
| ---- | -------------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------- |
| `B`  | Flag bits            | Definitions, first after the header | Compatible and incompatible feature flags, and the offsets of any appended data                          |
| `F`  | Format               | Definitions                         | One message layout, written as `name:type field;type field;`                                             |
| `I`  | Information          | Both                                | One key and its value, such as `sys_name`, `ver_hw`, `ver_sw` or `time_ref_utc`                          |
| `M`  | Multi information    | Both                                | Long or repeated information values, split across messages                                               |
| `P`  | Parameter            | Both                                | One parameter value, `int32` or `float`: at log start in the definitions, and on each change in the data |
| `Q`  | Default parameter    | Both                                | A parameter's default, system-wide or for the current configuration such as the airframe                 |
| `A`  | Subscription         | Data                                | A topic name, its `multi_id` instance and the `msg_id` its samples carry                                 |
| `R`  | Unsubscription       | Data                                | Marks a topic that stops being logged. Not used currently.                                               |
| `D`  | Logged data          | Data                                | One sample of a subscribed topic, tagged with its `msg_id`                                               |
| `L`  | Logged string        | Data                                | A console message: a log level, a `uint64` timestamp and the text                                        |
| `C`  | Tagged logged string | Data                                | A logged string with a `uint16` tag naming its source                                                    |
| `S`  | Synchronization      | Data                                | Eight fixed bytes, `2F 73 13 20 25 0C BB 12`, so a reader can recover after a corrupt message            |
| `O`  | Dropout              | Data                                | A `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 [#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:

```text title="Original record" caption="Example dataset, INC-0142: the last obstacle_distance sample before the stall"
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 [#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.

| Topic                     | Q4 recon quad | T4 tracked UGV | What it gives the investigation                                                                                                         |
| ------------------------- | ------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `vehicle_status`          | Logged        | Logged         | Arming state, navigation state and the failsafe flag, for the state band on the timeline and the `failsafe_entered` rule                |
| `vehicle_land_detected`   | Logged        | Not expected   | Landing, such as **Landed** in INC-0142                                                                                                 |
| `sensor_gps`              | Logged        | Logged         | The GPS time anchor, `eph` and `satellites_used` for `gps_quality_drop`, and `jamming_state` and `jamming_indicator` for `gnss_jamming` |
| `vehicle_global_position` | Logged        | Logged         | The track on the map and the ground speed lane                                                                                          |
| `battery_status`          | Logged        | Logged         | The battery lane and the `battery_sag` rule                                                                                             |
| `failsafe_flags`          | Logged        | Logged         | The cause of a failsafe, such as `gcs_connection_lost`                                                                                  |
| `telemetry_status`        | Logged        | Logged         | PX4's own view of each link, from `heartbeat_type_gcs` and `heartbeat_type_onboard_controller`                                          |
| `obstacle_distance`       | Logged        | Not expected   | Obstacle data from the companion computer. Its stalls are findings in INC-0142 and INC-0139.                                            |
| `estimator_status`        | Logged        | Logged         | The state estimator's status, checked under **Flight log topics**                                                                       |
| `cpuload`                 | Logged        | Logged         | Processor load and RAM use on the flight controller, each from 0 to 1                                                                   |
| logged messages           | Logged        | Logged         | PX4's console messages, such as **Failsafe activated** and **GCS connection regained**                                                  |
| `distance_sensor`         | Not logged    | Not logged     | Expected on both platforms and logged on neither in the example dataset                                                                 |
| `vehicle_imu_status`      | Not logged    | Logged         | `accel_vibration_metric` for the `vibration_high` rule                                                                                  |
| `vehicle_command`         | Not expected  | Logged         | Commands the vehicle received, such as `DO_SET_MODE` from system 255 in INC-0143                                                        |
| `offboard_control_mode`   | Not expected  | Not logged     | Which 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 [#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 [#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.

| Parameter          | Description on the vehicle page                                                                 | Q4 example  | T4 example  |
| ------------------ | ----------------------------------------------------------------------------------------------- | ----------- | ----------- |
| `COM_DL_LOSS_T`    | Seconds without a ground heartbeat before the data link failsafe                                | 10          | 10          |
| `NAV_DLL_ACT`      | Data link loss action                                                                           | 2 (Return)  | 1 (Hold)    |
| `COM_FAIL_ACT_T`   | None. PX4 describes it as the delay between failsafe condition triggered and failsafe reaction. | 5           | 5           |
| `COM_OF_LOSS_T`    | Seconds without offboard setpoints before the offboard failsafe                                 | 1.0         | 1.0         |
| `COM_OBL_RC_ACT`   | Offboard loss action                                                                            | 5 (Hold)    | 5 (Hold)    |
| `COM_POS_FS_EPH`   | Horizontal accuracy that invalidates position, metres                                           | 5           | Not read    |
| `SYS_HAS_NUM_GNSS` | GNSS receivers expected                                                                         | 1           | 1           |
| `COM_GNSSLOSS_ACT` | GNSS loss action                                                                                | 0 (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](/reference/px4-parameters) gives each parameter's range and default.

## The GPS time anchor [#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:

| Field                     | Type     | Meaning, from SensorGps.msg in PX4 v1.16.0                                           |
| ------------------------- | -------- | ------------------------------------------------------------------------------------ |
| `timestamp`               | `uint64` | Time since system start, in microseconds                                             |
| `timestamp_time_relative` | `int32`  | Added to `timestamp`, the time of the UTC reading on the boot clock, in microseconds |
| `time_utc_usec`           | `uint64` | UTC 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](/concepts/clocks) covers the anchor in full.

## Dropouts and the log\_dropout rule [#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](/rules/log-dropout).

## Truncated logs are quarantined [#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 [#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](/collect/troubleshoot-imports) walks through both cases.

## Related [#related]

* [Clocks, anchors and error bounds](/concepts/clocks) explains the boot clock and its GPS anchor.
* [PX4 parameters Foxborne reads](/reference/px4-parameters) lists each parameter's range and default.
* [log\_dropout](/rules/log-dropout) and [topic\_stall](/rules/topic-stall) describe the rules that read the log's structure.
* [Supported file formats](/collect/formats) compares the ULog with the other formats.
