# Files & recordings

# Files & recordings

Play back a recording as if it were live, with the same channels, charts and
tests.

Three types are built in:

| Type | What it is |
|---|---|
| `csv` | rows under a header |
| `jsonl` | one JSON object per line |
| `jsonl_watch` | a folder you drop `.jsonl` files into |

Add one on **Inputs** ([how](https://phloem.ai/docs/inputs/)). For MCAP, ROS 2 bags, CAN logs, NI TDMS
or PX4 flight logs, install a connector from **Connectors**, then add it on
**Inputs** the same way. Those connectors are listed at the end of this page.


### csv

| Field | Required | Example |
|---|---|---|
| **File path** | yes | `/path/to/telemetry.csv` |
| **Loop (true/false)** | no | `true` |

```csv
t,output_v,current_a,temp_c
1721037600,0.0,25.1,31.4
1721037601,0.8,25.1,31.6
```

Each column except the time becomes a channel, with a `/` in front:
`/output_v`, `/current_a`, `/temp_c`.

| Time column | |
|---|---|
| Name | `t`, `ts`, `time`, `timestamp`, `time_ns`, `t_ns` or `event_time` — found by name, not position |
| A plain number | seconds, milliseconds, microseconds or nanoseconds since 1970, judged by its size |
| A written-out time | needs sub-second digits: `2024-07-15T10:00:00.000000Z` |

Without sub-second digits it is not read as a time, and rows are stamped by
row number instead.

### jsonl

| Field | Required | Example |
|---|---|---|
| **File path** | yes | `/path/to/stream.jsonl` |
| **Tail (true/false)** | no | `true` |

```jsonl
{"t_us":1721037600000000,"/imu/accel_z":9.81,"mode":"drive"}
{"t_us":1721037600100000,"/imu/accel_z":9.79,"mode":"drive"}
```

Each key except the time becomes a channel: a number, true/false, or text. A
key without a leading `/` gets one, so `mode` becomes `/mode`.

- Time comes from `t_us` (microseconds), `t_ns` (nanoseconds) or `t` (judged by
  its size). A line with none is stamped when it is read.
- Nested objects and arrays are skipped. Bad lines are dropped.
- After a restart, reading continues where it stopped.

### jsonl_watch

A drop folder: each `.jsonl` file you put in it is read once.

| Field | Required | Example |
|---|---|---|
| **Directory** | yes | `/path/to/drops` |

Lines are read the same way as `jsonl`. A file is picked up once it stops
growing. Finished files are remembered across restarts.

## Loop and follow

| Field | Types | Effect |
|---|---|---|
| **Loop (true/false)** | `csv` | starts over at the end, timed as if arriving now |
| **Tail (true/false)** | `jsonl` | keeps reading lines as they are added |

Both are text boxes: type `true`. A connector's recordings have their own
**Loop** checkbox.

Nothing appears until the whole file has loaded. A big recording takes a while,
then plays at its original speed.

> **Note:** Without **Loop**, a recording keeps the times it was recorded at.
> Find it on [Replay](https://phloem.ai/docs/review/replay/), not **Live**: set the time control to **All
> history**, or pick the day it was recorded. It is not listed by file name;
> that list only holds runs you recorded.
