# Mavlink Wall-Clock Sync

This guide walks through configuring the NanoPi to set its system clock from a MAVLink peer's `SYSTEM_TIME` messages. This is useful in systems where there is one device that all of the other device logs should be synced with.

The wall-clock source can be any MAVLink device that sends `SYSTEM_TIME` with a valid epoch timestamp — a flight controller with GPS, a companion computer with NTP, a dedicated time module, etc. The timesync module does not care where the peer gets its time from.

## How It Works

```
Time source (GPS, NTP, etc.) → MAVLink peer → SYSTEM_TIME → SHM → chrony → Pi clock
```

1. Some MAVLink device on the bus has an accurate wall clock (however it obtained it).
2. That device sends `SYSTEM_TIME` messages containing `time_unix_usec`.
3. The timesync module receives those messages and writes valid timestamps into an NTP shared memory segment.
4. Chrony reads that segment as a reference clock and adjusts the Pi's system clock.
5. All other Theseus sensors sync from the Pi via NTP automatically.

## Prerequisites

* The configured MAVLink peer must be sending `SYSTEM_TIME` with a valid epoch `time_unix_usec` (i.e., a real wall-clock time, not zero or boot-relative). Without valid timestamps, chrony falls back to whatever time source it had before.
* If the peer is a flight controller using GPS as its time source, set `BRD_RTC_TYPES=1` on the FCU (reboot required). Do **not** use `2` — it creates a circular time loop where the FCU trusts the Pi's clock, which is trying to trust the FCU's clock.
* `chrony.conf` must include the SHM refclock line. This is included by default on mvps devices, but verify it is present:

```
refclock SHM 0 refid MAV precision 1e-3 delay 0.01 poll 1
```

## Parameters

All parameters live in `mav_timesync_params.yaml` (file ID: `components/timesync/mav_timesync_params`). Four parameters control wall-clock sync:

| Parameter                    | Type   | Default         | Description                                                                                                                                                                                        |
| ---------------------------- | ------ | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `mav_clock_input`            | bool   | `false`         | Master switch. When `true`, valid `SYSTEM_TIME` messages from the configured source are written into chrony's SHM segment. When `false`, no clock adjustment happens regardless of other settings. |
| `system_clock_source_mode`   | string | `timesync_peer` | Determines which MAVLink peer provides the wall-clock time for chrony. `timesync_peer` reuses the normal TIMESYNC target. `dedicated_peer` uses the explicit sysid/compid below instead.           |
| `system_clock_source_sysid`  | int    | `42`            | MAVLink system ID of the dedicated wall-clock peer. Only used when `system_clock_source_mode` is `dedicated_peer`.                                                                                 |
| `system_clock_source_compid` | int    | `1`             | MAVLink component ID of the dedicated wall-clock peer. Only used when `system_clock_source_mode` is `dedicated_peer`.                                                                              |

### When to use each mode

**`timesync_peer` (default):** The same MAVLink peer used for TIMESYNC round-trip offset estimation also provides `SYSTEM_TIME` for chrony. This is the simpler configuration and works whenever that peer has a valid wall clock.

**`dedicated_peer`:** A different MAVLink device provides the wall-clock time for chrony. Use this when:

* The TIMESYNC peer does not have a reliable wall clock.
* You want to source wall-clock time from a specific device on the MAVLink bus that is not the TIMESYNC target.

In dedicated peer mode, the TIMESYNC peer still handles boot-offset initialization and round-trip time measurement. Only the chrony wall-clock feed is redirected to the dedicated peer.

## Configuration

{% tabs %}
{% tab title="Option A: Helper Script" %}
Start by downloading the below python helper script:

{% file src="/files/AV6mN9MhnjE5bPCXTrT9" %}

The `timesync_clock_source.py` script wraps the Params API so you don't need to remember individual curl commands. It lives at `scripts/device/timesync_clock_source.py` in the repository.

**Check current status:**

```bash
python3 scripts/device/timesync_clock_source.py status
```

Example output:

```
TIMESYNC peer:              1:1
System clock source:        fallback to TIMESYNC peer (1:1)
Configured dedicated peer:  42:1 (ignored in current mode)
clock_source_mode:          timesync_peer
mav_clock_input:            False
Note: restart the consuming service after changing params.
```

**Enable wall-clock sync with the default TIMESYNC peer:**

```bash
python3 scripts/device/timesync_clock_source.py enable-clock-input
```

This sets `mav_clock_input: true` while leaving `system_clock_source_mode: timesync_peer`, so chrony gets its time from the same peer used for TIMESYNC.

**Switch to a dedicated peer:**

```bash
python3 scripts/device/timesync_clock_source.py set --sysid 42 --compid 1
```

This sets `system_clock_source_mode: dedicated_peer` and configures the peer IDs. If `mav_clock_input` is already enabled, the dedicated peer will start feeding chrony on the next service restart.

**Clear the dedicated peer (revert to TIMESYNC peer):**

```bash
python3 scripts/device/timesync_clock_source.py clear
```

**Disable wall-clock sync entirely:**

```bash
python3 scripts/device/timesync_clock_source.py disable-clock-input
```

**Custom API URL** (if the edge API is not at the default `http://192.168.218.100:5050`):

```bash
python3 scripts/device/timesync_clock_source.py --base-url http://192.168.218.100:5050/api/v2/params status
```

{% endtab %}

{% tab title="Option B: curl Commands" %}
All parameters use the Params API at `http://<device>:5050/api/v2/params`. The file ID for timesync is `components/timesync/mav_timesync_params`.

**Read all timesync params:**

```bash
curl http://192.168.218.100:5050/api/v2/params/components/timesync/mav_timesync_params
```

**Enable wall-clock sync:**

```bash
curl -X PUT http://192.168.218.100:5050/api/v2/params/components/timesync/mav_timesync_params/mav_clock_input \
  -H 'Content-Type: application/json' \
  -d '{"value": true}'
```

**Switch to dedicated peer mode with sysid 42, compid 1:**

```bash
# Set the peer IDs first
curl -X PUT http://192.168.218.100:5050/api/v2/params/components/timesync/mav_timesync_params/system_clock_source_sysid \
  -H 'Content-Type: application/json' \
  -d '{"value": 42}'

curl -X PUT http://192.168.218.100:5050/api/v2/params/components/timesync/mav_timesync_params/system_clock_source_compid \
  -H 'Content-Type: application/json' \
  -d '{"value": 1}'

# Then switch the mode
curl -X PUT http://192.168.218.100:5050/api/v2/params/components/timesync/mav_timesync_params/system_clock_source_mode \
  -H 'Content-Type: application/json' \
  -d '{"value": "dedicated_peer"}'
```

**Revert to TIMESYNC peer mode:**

```bash
curl -X PUT http://192.168.218.100:5050/api/v2/params/components/timesync/mav_timesync_params/system_clock_source_mode \
  -H 'Content-Type: application/json' \
  -d '{"value": "timesync_peer"}'
```

**Reset peer IDs to defaults:**

```bash
curl -X DELETE http://192.168.218.100:5050/api/v2/params/components/timesync/mav_timesync_params/system_clock_source_sysid
curl -X DELETE http://192.168.218.100:5050/api/v2/params/components/timesync/mav_timesync_params/system_clock_source_compid
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
Parameter changes require restarting the consuming service (Cyclops or MVPS) for the new values to take effect. The process reads its YAML config once at startup.
{% endhint %}

## Verification

After restarting the service, verify that chrony is receiving time from the MAVLink source:

```bash
chronyc sources
```

Look for a line with `#* MAV` — the `*` means chrony has selected it as the active source:

```
MS Name/IP address         Stratum Poll Reach LastRx Last sample
===============================================================================
#* MAV                           0   1   377     1   +125us[ +130us] +/-  500us
```

Verify the system clock is correct:

```bash
date
```

If chrony shows `MAV` but without the `*`, it means chrony sees the source but has not selected it yet (it may still be evaluating samples). Give it 30-60 seconds after the peer starts sending valid timestamps.

## Troubleshooting

**No `MAV` source in `chronyc sources`:**

* Check that `mav_clock_input: true` is set and the service was restarted.
* Verify `refclock SHM 0 refid MAV ...` exists in `/etc/chrony/chrony.conf`.

**`MAV` source present but not selected (`#?` instead of `#*`):**

* The peer may not have a valid wall clock yet. Check `time_unix_usec` in the `SYSTEM_TIME` messages — values near zero mean the peer does not have a time source.
* Chrony may be preferring another source. Check `chronyc sources -v` for competing sources with better stratum.

**`system_time_invalid` events in logs:**

* The configured source is sending `SYSTEM_TIME` with a `time_unix_usec` that is not a valid epoch timestamp (before Feb 2009). This means the peer does not yet have a valid wall clock. The events will stop once the peer starts sending valid time, and a `system_time_recovered` event will be emitted.

**Dedicated peer configured but no clock feed:**

* Verify `system_clock_source_mode` is `dedicated_peer`, not `timesync_peer`. In `timesync_peer` mode the explicit sysid/compid values are ignored.
* Verify the dedicated peer is on the MAVLink bus and sending `SYSTEM_TIME` at the expected sysid:compid.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.theseus.us/micro-vps/autopilot-integration/developer-api/mavlink-wall-clock-sync.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
