> This page is for Firmware, version 4.0 (default).
> For other versions, use one of these documentation indexes:
> - 4.0 (default): https://docs.ouster.com/sensor-docs/firmware/4.0/llms.txt
> - 3.2: https://docs.ouster.com/sensor-docs/firmware/3.2/llms.txt

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

# Time Synchronization

> Sensor timestamp modes, timing signal paths, and multipurpose IO configuration.

## Timing Overview Diagram

![Signal path with MULTIPURPOSE\_IO set as input](/sensor-docs/_fern-files/ouster.docs.buildwithfern.com/639b7a427688c09fd0d69eb54b821a8427f118cb48983c17df1f8c700bf8023a/assets/common_images/appendix/time_sync/timestamp_and_io_1.svg)![Signal path with MULTIPURPOSE\_IO set as output](/sensor-docs/_fern-files/ouster.docs.buildwithfern.com/e14a463097da8b2ab74f1a03d8133fd45b78d19b156500eb15097c9c002cdca5/assets/common_images/appendix/time_sync/timestamp_and_io_2.svg)

## Sensor Time Source

* All lidar and IMU data shares a common timer with 10 ns precision.

* The timer runs from one of three clock sources:

* An internal clock derived from a high accuracy, low drift oscillator.

* An opto-isolated digital input from the external connector for timing off an external hardware trigger such as a GPS. The polarity of this input signal is programmable. For instance, both a GPS PPS pulse and a 30 Hz frame sync from an industrial camera can supply a timing signal to the sensor.

* The IEEE 1588 Precision Time Protocol. PTP provides the convenience of configuring timing over a network that supports IEEE 1588 with no additional hardware signals.

#### Setting Ouster Sensor Time Source

The source for measurement timestamps can be configured using the `timestamp_mode`. The available modes are described below:

| Command                   | Response                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `TIME_FROM_INTERNAL_OSC`  | Use the internal clock. Free running counter based on the sensor's internal oscillator. Counts seconds and nanoseconds since sensor turn on, reported at ns resolution (both a second and nanosecond register in every UDP packet), but minimum increment is on the order of 10 ns.                                                                                                                                                                                                                                                                          |
| `TIME_FROM_SYNC_PULSE_IN` | A free running counter synced to the SYNC_PULSE_IN input counts seconds (# of pulses) and nanoseconds since sensor turn on. If `multipurpose_io_mode` is set to `INPUT_NMEA_UART` then the seconds register jumps to time extracted from a NMEA \$GPRMC message read on the `multipurpose_io` port. Reported at ns resolution (both a second and nanosecond register in every UDP packet), but minimum increment is on the order of 10 ns.                                                                                                                   |
| `TIME_FROM_PTP_1588`      | Synchronize with an external PTP master. A monotonically increasing counter that will begin counting seconds and nanoseconds since startup. As soon as a 1588 sync event happens, the time will be updated to seconds and nanoseconds since 1970. The counter must always count forward in time. If another 1588 sync event happens the counter will either jump forward to match the new time, or slow itself down. It is reported at ns resolution (there is both a second and nanosecond register in every UDP packet), but the minimum increment varies. |

When synchronizing from an external sync pulse, specify the pulse polarity as described in the Sensor-configuration section. The input frequency is assumed to be 1 Hz. The following `POST` commands configure an active-low pulse with the seconds timestamp tracking pulse count since startup:

* time-stamp-mode
* sync_pulse_in_polarity

To accept an external NMEA UART message, set `multipurpose_io_mode` to `INPUT_NMEA_UART` as described in the external-clock section. When the sensor receives a valid UART message, the seconds timestamp snaps to the received value. The following commands configure an active-high NMEA input at 115200 bps with 27 leap seconds, ignoring the valid-character flag:

* time-sync-multipurpose-io-mode
* nmea-in-polarity
* nmea-baud-rate
* nmea-leap-seconds
* nmea_ignore_valid_char

#### External Trigger Clock Source

The sensor can also output a `SYNC_PULSE_OUT` signal from various sources. See the http-api section for example commands. Output pulses are always evenly spaced.

Enable this via the `multipurpose_io_mode` configuration parameter.

| Configuration               | Response                                                                                           |
| --------------------------- | -------------------------------------------------------------------------------------------------- |
| `OFF`                       | Do not output a `SYNC_PULSE_OUT` signal.                                                           |
| `INPUT_NMEA_UART`           | Reconfigures the `MULTIPURPOSE_IO` port as an input. See setting-sensor-time for more information. |
| `OUTPUT_FROM_INTERNAL_OSC`  | Output a `SYNC_PULSE_OUT` signal synchronized with the internal clock.                             |
| `OUTPUT_FROM_SYNC_PULSE_IN` | Output a `SYNC_PULSE_OUT` signal synchronized with a `SYNC_PULSE_IN` provided to the unit.         |
| `OUTPUT_FROM_PTP_1588`      | Output a `SYNC_PULSE_OUT` signal synchronized with an external PTP IEEE 1588 master.               |
| `OUTPUT_FROM_ENCODER_ANGLE` | Output a `SYNC_PULSE_OUT` signal with a user defined rate in an integer number of degrees.         |

When `multipurpose_io_mode` is `OUTPUT_FROM_INTERNAL_OSC`, `OUTPUT_FROM_SYNC_PULSE_IN`, or `OUTPUT_FROM_PTP_1588`, set the output rate using `sync_pulse_out_frequency` (Hz). Defaults to 1 Hz. Must be greater than 0 Hz; maximum is limited by the criterion below.

When `multipurpose_io_mode` is `OUTPUT_FROM_ENCODER_ANGLE`, set `sync_pulse_out_angle` (degrees) to pulse at each multiple of that angle, indexed from the 0° crossing. Must be an integer between 0 and 360, inclusive; the minimum is also limited by the criterion below.

In all modes, `sync_pulse_out_pulse_width` (ms) sets the output pulse width.

> **Note**
>
> If `sync_pulse_out_pulse_width` x `sync_pulse_out_frequency` is close to 1 second, the output pulses will not function (will not return to 0). For example, at 10 Hz rotation and a 10 ms pulse width, the limitation on the number of pulses per rotation is 9.

### Examples

Examples below assume `lidar_mode` = `1024x10` and `sync_pulse_out_pulse_width` = 10 ms. For full `POST` command syntax, see the http-api section.

| Configuration (POST /api/v1/sensor/config)                                                                    | Response                                                                                                                                                                                         |
| ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `multipurpose_io_mode=OUTPUT_FROM_SYNC_PULSE_IN` `sync_pulse_out_pulse_width=10` `sync_pulse_out_frequency=1` | The output pulse frequency is 1 Hz. Each pulse is 10 ms wide. `sync_pulse_out_pulse_width` and `sync_pulse_out_frequency` commands are optional because they just re-command the default values. |
| `multipurpose_io_mode=OUTPUT_FROM_SYNC_PULSE_IN` `sync_pulse_out_frequency=50`                                | The output pulse frequency is 50 Hz. Each pulse is 10 ms wide.                                                                                                                                   |
| `multipurpose_io_mode=OUTPUT_FROM_ENCODER_ANGLE` `sync_pulse_out_angle=360`                                   | The output pulse frequency is 10 Hz, since the sensor is in 10 Hz mode (10 rotations/sec) and the angle is set to 360°, a full rotation. Each pulse is 10 ms wide.                               |
| `multipurpose_io_mode=OUTPUT_FROM_ENCODER_ANGLE` `sync_pulse_out_angle=45`                                    | The output pulse frequency is 80 Hz, since the sensor is in 10 Hz mode (10 rotations/sec) and the angle is set to 45°. Each full rotation will have 8 pulses. Each pulse is 10 ms wide.          |

## NMEA Message Format

The sensor expects a standard NMEA $GPRMC UART message. Each sentence is an ASCII string starting with `$`and ending with a return character. Fields are comma-separated; the last field (checksum) is delimited by`\*\`.

Standard messages are up to 80 characters. The sensor accepts non-standard messages up to 85 characters (see Example 2).

The sensor derives time from seconds since 1970-01-01 00:00:00 UTC. `nmea_leap_seconds` defaults to 0 (Unix Epoch time, no leap second correction). As of February 2019, UTC lags TAI by 37 s (10 s initial offset + 27 accumulated leap seconds). Setting `nmea_leap_seconds` to 37 aligns timestamps with TAI.

`nmea_in_polarity` defaults to `ACTIVE_HIGH` (start bit after a falling edge). For RS-232 (inverted polarity, start bit after a rising edge), set `nmea_in_polarity` to `ACTIVE_LOW`.

#### Example 1 Message:

`$GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A`

| Field     | Description                                                                     |
| --------- | ------------------------------------------------------------------------------- |
| \$GPRMC   | Recommended Minimum sentence C                                                  |
| 123519    | Fix taken at 12:35:19 UTC                                                       |
| A         | Status A=active or V=Void                                                       |
| 4807.038  | Latitude 48 deg 07.038'                                                         |
| N         | Latitude cardinal reference                                                     |
| 01131.000 | Longitude 11 deg 31.000'                                                        |
| E         | Longitude cardinal reference                                                    |
| 022.4     | Speed over the ground in knots                                                  |
| 084.4     | Track angle in degrees True                                                     |
| 230394    | Date - 23rd of March 1994                                                       |
| 003.1     | Magnetic Variation                                                              |
| W         | Magnetic cardinal reference                                                     |
| A         | \[Optional] A=autonomous, D=differential, E=Estimated, N=not valid, S=Simulator |
| \*6A      | The checksum data, always begins with \*                                        |

#### Example 2 Message:

`$GPRMC,042901.00,A,3745.871698,N,12224.825960,W,0.874,327.72,130219,13.39,E,A,*60`

| Field        | Description                                                                     |
| ------------ | ------------------------------------------------------------------------------- |
| \$GPRMC      | Recommended Minimum sentence C                                                  |
| 042901.00    | Fix taken at 4:29:01 UTC                                                        |
| A            | Status A=active or V=Void                                                       |
| 3745.871698  | Latitude 37 deg 45.871698'                                                      |
| N            | Latitude cardinal reference                                                     |
| 12224.825960 | Longitude 12 deg 24.825960'                                                     |
| W            | Longitude cardinal reference                                                    |
| 0.874        | Speed over the ground in knots                                                  |
| 327.72       | Track angle in degrees True                                                     |
| 130219       | Date - 13th of February 2019                                                    |
| 13.39        | Magnetic Variation                                                              |
| E            | Magnetic cardinal reference                                                     |
| A            | \[Optional] A=autonomous, D=differential, E=Estimated, N=not valid, S=Simulator |
| \*60         | The checksum data, always begins with \*                                        |