Skip to content

Time and synchronization

deckerjulian edited this page Oct 6, 2026 · 1 revision

Time and synchronization

Every sample openSciLab shows has a time on one clock: the clock of the computer it runs on. A logic analyzer behind USB, a DAQ on another computer and a simulator each have clocks of their own. This page explains how their samples are placed on that clock, how well, and how to do better: with a measured latency, a sync signal, a shared sample clock or a shared time scale (NTP, PTP, GPS).

It matters when several instruments record the same event, when a flow sets an output and measures the reaction, or when values of a remote device have to line up with a capture. For a single capture on a single device, nothing on this page is needed.

Methods, best first

Method How Accuracy (guide values until measured)
simulated a simulator knows when it took its samples exact
sync signal the same irregular signal recorded by both instruments, edges matched a sample period of the slower recording
shared clock both sample with one clock; a sync signal gives the offset only as above, without drift
time scale the device stamps in UTC/TAI (PTP, GPS) and this computer's clock follows it too what PTP/NTP achieve (PTP with hardware time stamps: < 1 µs; in software: 10–100 µs)
latency the arrival of the data, less a latency measured with a loopback a fraction of a millisecond
bounds between the command that started the capture and the arrival of its data half the distance (about a millisecond over USB)
arrival only the arrival of the data an upper bound (late by the shortest latency)
network (remote devices) the device's clock measured with pings ± half the shortest round trip (wired: 0.1–0.5 ms, WiFi: ms)

Without anything to do, openSciLab uses bounds: a sample cannot be taken before the command that started the acquisition, and a block of samples cannot arrive before its last sample was taken. The arrivals jitter (USB frames, buffers, the operating system); openSciLab draws a line under the fastest ones, which removes the jitter, and once a stream has run for about 10 s its slope also gives the drift of the device's sample clock. A capture that waited for a trigger has no start bound, so only its arrival (less a measured latency) places it.

The Timing tab of the device card

Every instrument that captures has a Timing tab on its device card (Devices):

  • Time of the samples: how the last capture or stream was placed (Placed by, e.g. between start command and arrival or sync signal), its Accuracy, the Sample clock, whether the device stamps its samples in a time scale (Device time stamps) and This computer's clock. This works for captures of a data view and of a flow alike. This computer's clock… opens Settings → Time; Align with a sync signal (example)… opens the example flow.
  • Sample clock: Own sample clock (offset and drift are fitted) or Shared or external sample clock (only the offset is fitted). timing.align follows this choice (its drift: auto).
  • Latency: the latencies measured for the device, and a loopback measurement without a flow: choose the Output, the channel it is wired to, the Rate and the Mode (Stream or Capture), then Measure. On a simulator, Wire them (simulator) adds the wire. Forget the latencies deletes them; In a flow (example)… opens the flow with timing.calibrate.
  • Sync output: a sync signal on a Pin (with a Seed) from Start until Stop (or until the card is closed), e.g. for an input of a DAQ that records it along with its data.

The Details tab lists the same Time of the samples among everything else about the device.

Measuring the latency (loopback)

An output of the instrument is wired to one of its own inputs and switched a few times. An edge cannot be recorded before the command that made it, and its sample cannot be later than its arrival; half of the shortest loop is the latency of the input. It is kept per instrument, mode and rate (and length of a capture) and used by every later capture or stream with the same settings – the wire is only needed once.

  1. Wire an output to a channel of the same device with a jumper (for example GP16 to GP17 on a Pico).
  2. On the device card, Timing → Latency: choose output, channel, the rate you capture with and the mode, then Measure. The result shows in a message and in the list.
  3. Or in a flow: node timing.calibrate with pin, channel, rate and mode (real time only). Example: Templates → Time → Calibrate latency.

The measured latencies are listed (and can be forgotten) in Settings → Time as well.

Aligning instruments with a sync signal

The most precise way to put two instruments on one time axis: one output makes edges at irregular intervals (20–60 ms, the same for the same seed), and every instrument records it on a spare channel. Because the pattern of intervals is unique, the edges can be matched without ambiguity, even when the clocks are seconds apart.

  1. Choose a free output pin on one instrument and wire it to a channel of every instrument to align (and to the reference).
  2. In the flow, timing.sync drives the pin (or Sync output on the Timing tab does it).
  3. Stream (or capture) the sync channel on each instrument.
  4. timing.align gets the signal as the instrument to align recorded it at signal, and as the reference recorded it at reference (or the commanded edges of timing.sync, as precise as that output). Its device input is the instrument to align.
  5. Everything wired to in leaves out on the reference's time; offset and uncertainty say how far it was moved and how well. Later samples of that instrument are placed the same way.

drift of timing.align: auto (the default) follows the Sample clock setting of the device card, fit fits offset and drift, none fits only the offset (a shared sample clock). The edges are as precise as the recording: a DAQ at 10 kS/s aligns to about 30 µs, a logic analyzer at 100 MHz to nanoseconds.

Example: Templates → Time → Align instruments – a simulated Uno drives the sync signal, a simulator that knows its time is the reference, a Pico behind (emulated) USB is aligned to a few microseconds. Templates → Start a project → Synchronized instruments does the same with a logic analyzer and a DAQ whose clock drifts.

A shared sample clock

When the sample clock of a device comes from another instrument (a DAQ clocked by the Pico, a 10 MHz reference), the two never drift apart: only the offset has to be found, from one edge of a sync signal or a shared start trigger. Set Shared or external sample clock on the Timing tab (or drift: none on timing.align). Making such a clock in hardware is not part of openSciLab yet.

Time scales: NTP, PTP, GPS

A device whose clock follows UTC or TAI (Linux with ptp4l and phc2sys, a GPS receiver, NTP) can stamp its samples in that time scale. When this computer's clock follows it too, openSciLab converts the device's times without measuring anything. Tell openSciLab in Settings → Time:

  • This computer's clock follows: Nothing (no time scale), NTP (UTC, about milliseconds) or PTP (UTC/TAI, microseconds);
  • Accuracy: how well it follows, in milliseconds (usual for it unless you know better).

PTP aligns the clocks of computers, not samples: an instrument behind USB still needs its latency or a sync signal to place its samples on that clock. macOS has no PTP client of its own; Windows has one with software time stamps.

Remote devices

A device on another computer stamps every value with its own clock when it measures it; the network only delays it. openSciLab measures the offset and drift of that clock all the time with pings (a fraction of a millisecond on a wired network, a few milliseconds over WiFi). The Remote tab of its device card shows the Clock of the device: Method, Accuracy, Round trip, Jitter, Drift. For microseconds, the device drives a sync output (or records a sync signal on one of its inputs) and the flow node remote.sync matches it with a recording. See Remote devices.

Trying it with simulators

A simulator knows the exact time of its samples, unless it emulates a USB link:

  • The simulated Pico emulates USB (frames, latency, jitter), so all the methods above work as with the real board. On any simulator, the Signals tab has USB link and clock (Emulate a USB link: Latency, Jitter, Frame; Clock drift) and Wires (Add wire… connects a pin to a channel, e.g. a loopback). See Simulators.
  • sim:daq is a measuring box on USB whose sample clock runs 30 ppm fast, with a loopback (P0.0 → P0.1) and P0.7 free for a sync signal.
  • The device list has a simulated remote DAQ with a drifting clock, with a clock that follows UTC (as with PTP) and with a shared sample clock. The Simulation: timing box on its Remote tab changes network latency, jitter and clock drift while it runs.
  • The category Time of the templates has four examples: Align instruments, Calibrate latency, Remote DAQ and DAQ latency.

Staying responsive

The threads that receive samples must never wait long: their arrival times place the samples, and the device's buffer fills meanwhile (the Pico firmware's stream buffer lasts about 22 ms at 6 MB/s). Therefore a Pico, an Arduino or a DSLogic is read in a process of its own (Settings → Devices → Read devices on USB in a process of their own, on by default), and the protocol decoders run in a process of their own as well. On a busy computer, Read devices with a higher priority (off by default) raises the priority of openSciLab and its device processes. Windows needs no rights for it; on macOS and Linux openSciLab asks when it starts, with the password dialog of the system, and on Linux Allow it for good (from the next login) saves the question. The Details tab of a device read in a device process shows its priority under Process.

See also

  • docs/timing.md – the details, including how device scripts measure their own latency
  • Remote devices – devices on other computers and their clocks
  • Simulators – USB link emulation, wires, clock drift

Clone this wiki locally