You hardware-trigger a camera, measure about 120 ms to the screen with a blinking LED or a photodiode, and can account for only 40 ms of it between the trigger and your render call. The next question is always the same: is the missing 80 ms in the camera, on the link, or in the host? The chunk data timestamp answers the camera half of that question, because it is the camera's own clock reading taken at the moment exposure ended. This explainer pins down which pipeline stage that value marks, why a PC clock must never be subtracted from it, and how the camera's TimestampLatch command turns it into a measured exposure-end-to-host delay. For the end-to-end measurement itself, start with the glass-to-glass latency method.
What the chunk timestamp marks: camera time at exposure end
Chunk data is metadata the camera appends to the pixels before they leave. Turn it on the standard GenICam way: set ChunkModeActive to true, pick Timestamp in the ChunkSelector enumeration, set ChunkTimestampEnable to true. Every delivered buffer then carries a ChunkTimestamp value, usually beside the frame ID and the exposure time the camera actually used. On a GigE camera that hands you an empty chunk block, the enable path has its own failure modes, covered in empty chunk data troubleshooting.
The value itself is the reading of the camera's free-running timestamp counter at the instant the exposure for that frame ended. Not the start of readout, not the moment packets left the sensor, not arrival in host memory. Sensor readout, packetization, link transfer and driver copies all happen after it, so none of that time is inside the number. Treat it as exposure end: that is the convention you will meet on most current machine-vision cameras, and a two-minute test settles any doubt (in the checks below).
Cameras can also push FrameStart and ExposureEnd events stamped with the same internal clock; routing exposure-end events into your code covers that route. Events tell you a stage happened while it happens. The chunk timestamp rides inside the frame itself and survives every buffer queue unchanged, which is why it is the one you use for per-frame latency accounting.
Two clocks: never subtract host time from camera ticks
ChunkTimestamp counts ticks of an oscillator inside the camera. Convert with the TimestampTickFrequency feature, reported in hertz; many current cameras run the counter at 1 GHz, so the raw reading is nanoseconds, but read the register instead of hardcoding that. The value shares nothing with the host clock — different zero point, different rate, different wrap. To see what the timestamps your SDK stamps on each host buffer do mean, read buffer timestamps and frame timing.
Latch and subtract: measuring exposure end to host delivery
GenICam cameras give you the camera's "now" on demand. The TimestampLatch command copies the current counter value into the TimestampLatchValue register, which you then read like any other feature. The measurement is therefore three steps: when your blocking grab returns a frame, execute TimestampLatch, read TimestampLatchValue, and subtract the ChunkTimestamp of that same frame.
Both operands are camera ticks, so the host clock never enters the arithmetic, and the reading covers end of exposure through sensor readout, link transfer and driver delivery into your user-level buffer. Latch the instant the grab call returns: every line of your own code between the return and the latch adds itself to the measurement. One latch per frame. Robot controllers expose the same latch-then-read pattern for their clocks, as covered in retrieving a robot controller's time data.
Worked example: splitting a 120 ms glass-to-glass delay on USB3
The case: a 2 MP-class USB3 mono camera, hardware triggered, displayed through an X11 and OpenGL path. A blinking LED shows 120 ms trigger-to-screen; host instrumentation shows 40 ms trigger-to-render-call. Inputs:
- Total trigger-to-screen: 120 ms, from an LED or photodiode viewed against the screen.
- Trigger to render call: 40 ms, from host-side instrumentation.
- Frame: 1600 x 1200 pixels, 8-bit mono, so 1600 x 1200 x 1 byte = 1,920,000 bytes, about 1.92 MB.
- Link: USB3 at a 5 Gbps line rate, roughly 350 MB/s of practical payload after encoding and protocol overhead.
- ChunkTimestamp on the delivered frame: 21,480,115,000 ns at a 1 GHz tick rate.
- TimestampLatchValue, latched the moment the grab returned: 21,480,126,800 ns.
Arithmetic:
- Transfer floor: 1.92 MB / 350 MB/s = 5.5 ms.
- Measured exposure end to host: 21,480,126,800 - 21,480,115,000 = 11,800,000 ns = 11.8 ms, covering readout, transfer and driver copies, and sitting sensibly above the 5.5 ms floor.
- Residual after the render call: 120 ms - 40 ms = 80 ms.
Decision: 11.8 ms is well inside the health mark — call a USB3 link healthy below about 15 ms at this frame size, and scale the mark with your byte count. Camera and link are fine; about 12 ms of the 40 ms host leg is delivery, and all 80 ms of the residual lives between the render call and photons leaving the glass.
The missing 80 ms sits on the display path
A render call is a request, not photons. The frame still goes through pixel format conversion, an upload to a GPU surface, zero or more queued swaps, the compositor, and scan-out at the next vertical blank. Each queued swap costs up to one refresh period — 16.7 ms at 60 Hz — and 80 ms is close to five of them. That is the signature of a swap queue or a compositor in the path, not of a slow camera. The screen tearing diagnosis guide walks through how these stages show up visually.
Format conversion is the quiet tax. Showing 8-bit mono on screen means expanding each pixel to 32 bits: 1600 x 1200 x 4 = 7,680,000 bytes, 7.68 MB per frame, about 461 MB/s of copies at 60 fps for frames that were 1.92 MB on the wire. Convert once into a persistent GPU-side buffer and update that buffer instead of rebuilding surfaces every frame; controlling the display window so the video surface stays unchanged helps the same way.
This is also why newest-only stream buffer handling changed nothing in this case. Newest-only decides which already-captured frame the API hands over when your loop runs behind acquisition; it cannot touch anything after the grab call returns, and the delay was entirely after it.
Hardware that keeps the camera-to-host leg short
When the split does point at the camera-to-host leg, the levers are bytes per frame, link speed and a clean host port. Ship a smaller ROI when the inspection allows it, because transfer time scales directly with pixel count. Cameras such as the MV-CH120-11UC, a 12 MP USB3 area scan camera with a global-shutter sensor, show the link class this measurement assumes.
- Sensor format
- 1.1"
- Lens mount
- C-mount
- Interface
- USB3
- Resolution
- 12 MP
On the host side, a dedicated adaptor with a consistent driver beats a shared onboard port when arrival-time stability matters:
| Specification | MV-GU2204 | MV-GE2002P | MV-GT2104 |
|---|---|---|---|
| Interconnect | PCI-E gen2 × 4 | PCI-E gen2 × 4 | PCI-E gen3 x 8 |
| Delivery bandwidth | 1720 MB/s (max. transmission bandwidth); 1600 MB/s (sustained transmission bandwidth) | 860 MB/s (max. transmission bandwidth); 800 MB/s (sustained transmission bandwidth) | 7800 MB/s (max. transmission bandwidth); 6700 MB/s (sustained transmission bandwidth) |
| Standard | USB3.0 | IEEE 802.3 | IEEE802.3 |
| Interface | USB3.0 type-A, female | 2 × RJ45 | RJ45 × 4 |
| Camera type supported | Industrial cameras with USB interfaces | GigE industrial camera | 10 GigE industrial camera |
| Camera quantity supported | 4 at most | 2 at most | 4 at most |
| Camera connection speed | Max. 5 Gbps (the actual speed is 3.08 Gbps) | 10/100/1000 Mbps | 1000/2500/5000/10000 Mbps |
| Stream | 4 | 2 | 4 |
| Power consumption | 5.5 W (without USB power supply); 23.5 W (with USB power supply) | 7.5 W (excl. PoE) | 12.5 W (excluding PoE) |
| Dimension | 165.5 mm × 120 mm × 22 mm (6.5" × 4.7" × 0.9") | 181 mm × 126.3 mm × 21.6 mm | 181 mm × 126.3 mm × 22.4 mm |
| Weight | Approx. 143.6 g (0.3 lb.) | 190 g | 205.14 g |
| Temperature | Working temperature: 0 °C to 60 °C (32 °F to 140 °F); Storage temperature: –20 °C to 70 °C (–4 °F to 158 °F) | Working temperature: 0 °C to 60 °C (32 °F to 140 °F); Storage temperature: –20 °C to 70 °C (–4 °F to 158 °F) | Working temperature: 0 °C to 55 °C (32 °F to 131 °F); Storage temperature: –30 °C to 80 °C (–22 °F to 176 °F) |
| Price | $469.00 | $249.00 | $1,429.00 |
All 3 share: Humidity 10% RH to 95% RH (no condensation). Values as printed in each product's data sheet; open a part for its drawing and full specifications.
Area scan cameras and frame grabbers cover both ends of the link, and vision controllers bundle camera ports, light outputs and processing in one box when the display leg should be a local HMI rather than a desktop stack.
Checks before you trust the delta
- Keep the loop ahead. Pause processing during the measurement; if the delta drops, queue time was inside it.
- Latch the instant the grab returns. Code between the return and the latch measures your software, not the camera.
- Read TimestampTickFrequency from the camera and convert with it. A hardcoded nanosecond assumption breaks the day a camera reports a different tick rate.
- Pin the latch point. Measure at 1 ms exposure, then at 20 ms: a delta that grows by 19 ms means the camera latches at exposure start, so relabel the measurement exposure-start-to-host and keep going.
- Handle counter wrap before subtracting. A 32-bit tick counter rolls over mid-session; add the counter range to the smaller operand when the subtraction goes negative.
- Read the shape of the delta, not just its size. Small and stable points at the display stack; large and stable points at host processing or a queue; large and erratic points at the link or driver, the case the frame arrival jitter isolation guide works through.
Run the latch split before changing any hardware: it costs an afternoon and it names the guilty leg. If the fix turns out to be a faster camera, a cleaner host port or a controller with the display built in, configure the system or contact the applications team with your delta numbers in hand.
Questions
Is the chunk data timestamp the time the frame arrived in host memory?
No. It is the camera's internal counter read at the end of exposure, before sensor readout and link transfer even start. Host arrival has to be measured on the host: latch the camera clock with the TimestampLatch command the moment the grab call returns, read TimestampLatchValue, subtract the frame's chunk value and divide by TimestampTickFrequency. That delta, not the chunk value alone, is your exposure-end-to-host delay.
How do you convert chunk timestamp ticks into seconds?
Divide by the camera's TimestampTickFrequency register, in hertz. Many current cameras run the counter at 1 GHz, so the raw reading is effectively nanoseconds, but do not hardcode that: read the frequency and convert. If the counter is 32 bits it also rolls over mid-session, so handle the wrap before subtracting two readings, or a delta taken across a wrap comes out negative.
Why didn't newest-only stream buffer handling reduce the delay?
Newest-only decides which already-captured frame the API hands over when your loop runs behind acquisition, and with a blocking grab the frame reaches you as soon as it completes in memory. It does nothing after that point. Pixel format conversion, GPU uploads, queued swaps and scan-out all sit downstream of the buffer queue, which is exactly where an 80 ms residual was hiding.
What other camera-side timing information can a frame carry?
Chunk data commonly carries the frame ID and the actual exposure time beside the timestamp, which lets you audit whether the camera really used the exposure you requested. The camera can also report stages as they happen through its event interface. Events notify you in real time, while the chunk timestamp rides inside the frame and survives buffer queues without extra plumbing — which is why it is the one used for per-frame latency accounting.
How much exposure-end-to-host delay is normal?
It is set by frame size and link speed. A 1.92 MB mono frame over USB3 at a practical 350 MB/s has a 5.5 ms transfer floor; with readout and driver copies, expect roughly 8 to 15 ms and call the link healthy below 15 ms. The same frame on 1 GigE at about 100 MB/s effective has a 19.2 ms transfer floor, so budget 20 to 30 ms there.
