> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.stereolabs.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.stereolabs.com/_mcp/server.

# Time Synchronization with PPS

Besides the position, most GNSS receivers can output a **PPS** (*Pulse Per Second*) signal: a hardware pulse emitted at the beginning of every UTC second. Used together with the time contained in the NMEA sentences, it lets the operating system align its clock to UTC within about a microsecond.

## Why synchronize the clock?

The ZED SDK [Fusion](/docs/development/zed-sdk/modules/global-localization) module matches camera data and GNSS data by **timestamp**. Camera images are timestamped with the system clock of the host, while GNSS measurements are usually timestamped with the GNSS time given by the receiver. If the system clock drifts away from UTC, the two timestamps no longer refer to the same instant, which degrades the fusion result or causes the data to be dropped. See [Data Synchronization](/docs/development/zed-sdk/modules/global-localization/data-synchronization) for more details on how the matching is performed.

The time contained in the NMEA sentences is affected by the latency and the jitter of the serial link, which limits its accuracy to a few milliseconds. The PPS edge, instead, is aligned to UTC with an accuracy in the order of tens of nanoseconds.

Disciplining the system clock with PPS is recommended when:

* your device has no reliable network time source (NTP), for example on a robot operating outdoors without connectivity,
* you fuse camera and GNSS data with the **Global Localization** module and need the best possible timestamp alignment,
* you record datasets that must be replayed or compared with data coming from other machines,
* you run a **multi-camera** setup where several devices must share a common time base.

> **Note**
>
> The PPS signal is generated only when the GNSS receiver has acquired a valid fix. If no antenna is connected, or if the sky visibility is poor, no pulse is produced.

## Hardware requirements

On Linux, the PPS signal is handled by the kernel **PPS subsystem**, which exposes it as a `/dev/ppsN` character device. The most common setup is:

* a GNSS receiver with a **PPS output** pin,
* a connection from this pin to a **GPIO** of the host, with compatible voltage levels,
* a **device tree** node that binds this GPIO to the Linux `pps-gpio` driver, and a kernel built with `CONFIG_PPS` and `CONFIG_PPS_CLIENT_GPIO`.

The GPIO to use and the device tree configuration depend on your board. **Consult your carrier board vendor** to know which pins support this use and how to enable the `pps-gpio` driver.

> **Tip**
>
> On the **ZED Box Orin** with the GNSS option, the PPS signal of the GNSS module is already wired and enabled: `/dev/pps0` is available out of the box. See [Using GNSS with ZED Box Orin](/docs/products/embedded/zed-box-orin/using-gnss#pps-signal).

USB receivers usually do not transmit the PPS signal over USB. Some USB-to-serial adapters can carry it on the **DCD** line of the serial port, which `gpsd` can use, but with a lower accuracy than a GPIO because of the USB latency.

## Checking the PPS device

Once the PPS input is configured, the kernel exposes the signal as a PPS character device:

```bash
ls /dev/pps*
```

You should get `/dev/pps0`, or another index if several PPS sources are declared.

A quick check that does not require any additional package is to read the timestamp of the last pulse:

```bash
cat /sys/class/pps/pps0/assert
```

```text
1758012345.000000123#42
```

The value is the system time of the last rising edge, followed by the number of pulses received since boot. Reading it twice one second apart should show the counter increasing.

To monitor the pulses continuously, install the `pps-tools` package:

```bash
sudo apt install pps-tools
```

Then monitor the device:

```bash
sudo ppstest /dev/pps0
```

The output should look like this:

```text
trying PPS source "/dev/pps0"
found PPS source "/dev/pps0"
ok, found 1 source(s), now start fetching data...
source 0 - assert 1758012345.000000123, sequence: 42 - clear  0.000000000, sequence: 0
source 0 - assert 1758012346.000000118, sequence: 43 - clear  0.000000000, sequence: 0
```

The `assert` value is the system time at which the kernel detected the rising edge of the pulse, and `sequence` increments once per second. A value close to a whole second means that the system clock is already well aligned to UTC.

## Synchronizing the system clock

This section assumes that `gpsd` is installed and configured as described in [Setting up GNSS / RTK on Linux](/docs/development/zed-sdk/modules/global-localization/gnss/setting-up-gnss-rtk).

### Feeding the PPS signal to gpsd

`gpsd` can read the PPS device together with the serial port of the GNSS receiver. Add `/dev/pps0` to the list of devices passed to `gpsd`:

```bash
sudo pkill gpsd # Kill gpsd if it is already running
sudo gpsd -nG -s 115200 /dev/ttyACM0 /dev/pps0
```

If you are also using **RTK corrections** through NTRIP, the PPS device is simply appended to the NTRIP command:

```bash
sudo gpsd -nG ntrip://<username>:<password>@<url>:<port>/<mountpoint> -s 115200 /dev/ttyACM0 /dev/pps0
```

> **Note**
>
> Replace `ttyACM0` with the communication port of your GNSS receiver, and `pps0` with your PPS device.

You can check that `gpsd` is actually receiving the pulses with:

```bash
ntpshmmon
```

Samples labeled `NTP0` come from the NMEA time, while the ones labeled `NTP1` come from the PPS signal.

To make the configuration persistent, update the `gpsd` line of your cron job so that it also includes the PPS device (see the warning below).

### Configuring Chrony

`gpsd` publishes these two time sources in shared memory, where an NTP daemon such as [Chrony](https://chrony-project.org/) can read them to discipline the system clock. Install it with:

```bash
sudo apt install chrony
```

And add the two reference clocks to `/etc/chrony/chrony.conf`:

```bash
# Time from the NMEA sentences, coarse but always available
refclock SHM 0 refid NMEA offset 0.0 delay 0.2 poll 3
# Time from the PPS signal, high precision
refclock SHM 1 refid PPS precision 1e-7 poll 3 prefer
```

Restart the service and verify that the reference clocks are used:

```bash
sudo systemctl restart chrony
chronyc sources -v
```

The `PPS` source should be marked as the selected one (`*`) after a few tens of seconds.

> **Warning**
>
> Reading the PPS device and writing to the shared memory segments 0 and 1 require `root` privileges. That is why the commands above start `gpsd` with `sudo`. If you start it from a cron job, add the job to the `root` crontab with `sudo crontab -e`.
>
> These two segments are readable by the `root` user only. If `chronyd` cannot read them on your system, configure `gpsd` and Chrony to use segments 2 and 3 instead, which are world-readable.

Once the system clock is disciplined by PPS, every timestamp generated on the device, including the ZED camera timestamps, shares the same UTC-aligned time base.