# PX4 ULog

Nominal’s first-party extractor reads [PX4](https://px4.io/) [ULog](https://docs.px4.io/main/en/dev_log/ulog_file_format.html) flight logs (`.ulg` and `.ulog`). Each topic field becomes a channel, such as `vehicle_local_position.vx`. A topic logged by more than one instance keeps the instance in its name, such as `sensor_accel_0.x`.

## Before you start

Enable the extractor in your workspace:

1. In the Nominal app’s sidebar, open **Data** > **File extractors**.
2. Click **NEW EXTRACTOR** > **Native extractor**.
3. Choose **PX4 ULog** and click **ENABLE EXTRACTOR**.

## Upload a file

**Nominal app**

1. In the sidebar, click **+** (**New**), then **Upload data**.
2. On the **File extraction** card, click **SELECT** and choose **PX4 ULog**.
3. Click **SELECT A DESTINATION** and choose **Standalone dataset**. Pick a dataset, or click **NEW DATASET**, enter a name and click **CREATE DATASET**.
4. Next to **INPUT_FILE**, click **Select file** and choose the `.ulg` file.
5. Click **CONFIRM & INGEST**.

**Python**

Select the extractor’s row on the **File extractors** page to see its **RID**.

```python
from nominal.core import NominalClient

client = NominalClient.from_profile("default")
extractor = client.get_containerized_extractor("<EXTRACTOR_RID>")
dataset = client.create_dataset("Flight 42")
job = dataset.add_containerized(extractor, sources={"INPUT_FILE": "flight.ulg"})
for file in job.as_files_ingested():  # waits for ingestion
    print(file.name, file.ingest_status.name)
```

## Options

| Setting | Nominal app | Python | Extractor variable |
| --- | --- | --- | --- |
| Convert only these topics, such as `vehicle_attitude,sensor_combined`, in upper or lower case | **Topic filter** | `TOPIC_FILTER` in `arguments` | `TOPIC_FILTER` |
| A tag on every point | **Tags** | `tags` | — |

## Logs without a GPS fix

The extractor moves timestamps from time since boot to UTC using the log’s GPS fix, from `vehicle_gps_position` or `sensor_gps`. A log with no GPS fix keeps boot-relative times. From Python, give the time the log started, continuing the Python example above:

```python
from datetime import datetime, timezone

from nominal import ts

started = datetime(2026, 1, 1, 12, 0, tzinfo=timezone.utc)
job = dataset.add_containerized(
    extractor,
    sources={"INPUT_FILE": "bench.ulg"},
    timestamp_column="timestamps",
    timestamp_type=ts.Relative("nanoseconds", start=started),
)
```

## Convert a log yourself

To convert a log to CSV files with the Python client and upload those instead, see [PX4 ULog](https://dev.nominal.io/python/guides/datasets/px-4.md) in the Python client’s guides.
