# ArduPilot DataFlash

Nominal reads [ArduPilot](https://ardupilot.org/) DataFlash logs (`.bin`). Each field of each log message becomes a channel named `<message>.<field>`, such as `IMU.GyrX`. The vehicle’s parameters become channels named `PARM.<parameter>`, such as `PARM.ARMING_CHECK`.

## Upload a file

**Nominal app**

1. In the sidebar, click **+** (**New**), then **Upload data**. Drop the `.bin` file onto **Upload files**, or click **SELECT FILES**.
2. Click **SELECT A DESTINATION** and choose **Standalone dataset**. Pick a dataset, or click **NEW DATASET**, enter a name and click **CREATE DATASET**.
3. Click **CONFIRM & INGEST**.

**Python**

```python
from nominal.core import NominalClient

client = NominalClient.from_profile("default")
dataset = client.create_dataset("Flight 42")
file = dataset.add_ardupilot_dataflash("flight.bin")
file.poll_until_ingestion_completed()  # raises if ingestion fails
```

**nomctl**

```bash
nomctl ingest ardupilot-dataflash --name "Flight 42" flight.bin
```

## Options

| Setting | Nominal app | Python | nomctl |
| --- | --- | --- | --- |
| A tag on every point | **Tags** | `tags` | `--file-tag` |

## Logs with invalid timestamps

If a log fails with `INVALID_TIMESTAMPS`, because a timestamp can’t be represented, run Nominal’s first-party ArduPilot DataFlash extractor on it with **Ignore invalid timestamps** (`IGNORE_INVALID_TIMESTAMPS`) set to `true`. The extractor drops the records with those timestamps and keeps the rest. Its input, `INPUT_FILE_1`, also takes logs compressed with zstd, gzip, bzip2 or xz.

If **ArduPilot DataFlash** isn’t in the **File extraction** menu, enable it first: in the sidebar, open **Data** > **File extractors**, click **NEW EXTRACTOR** > **Native extractor**, choose **ArduPilot DataFlash** and click **ENABLE EXTRACTOR**.

**Nominal app**

1. In the sidebar, click **+** (**New**), then **Upload data**.
2. On the **File extraction** card, click **SELECT** and choose **ArduPilot DataFlash**.
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_1**, click **Select file** and choose the `.bin` file. Enter `true` in **Ignore invalid timestamps**.
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_1": "flight.bin"},
    arguments={"IGNORE_INVALID_TIMESTAMPS": "true"},
)
for file in job.as_files_ingested():  # waits for ingestion
    print(file.name, file.ingest_status.name)
```
