fluxcode timestamps: the time axis

← codec comparison (index.html) · SPEC §3: time axis

fluxcode can store each sample's timestamp exactly next to its values (by default; the last section lets them move by a share of an interval). This page covers only that time axis: what the timestamps add to a one-minute block group (60 blocks of 1,000 samples, nominally 1 kHz, int64 ns ticks). Every number here is the block group with times minus the same block group without them. For scale, that block group's values alone take 17,976 bytes (a 4.12 Hz sine at the default settings), 311 µs to encode and 100 µs to decode. Regenerate: cd experimental && uv run python make_time_report.py.

How timestamps are stored

The common shapes

A perfect grid, and a perfect grid with a few gaps, are by far the most common timestamps in practice. A noisy clock (host timestamps on arrival) is the third common shape, and the only one of the three where the timestamps cost more than the values (here about two and a half times as much).

Sample interval over one minute for each clock shape
shapewhat it isbytes (example)bits/sample irregular blocksvs valuesbytes over 64 groups: medianmax encode time addeddecode time added
Perfect gridEvery interval exactly 1 ms: hardware-clocked acquisition, resampled or synthesized series.660.0090/600.4%66133+13 µs (+4%)+14 µs (+14%)
Grid with a few gapsA perfect grid that sometimes skips: dropped packets, reconnects, a paused logger (Poisson, mean 2 gaps per minute, each 5 ms to 3 s).1670.0222/600.9%166834+15 µs (+5%)+15 µs (+15%)
Noisy clockHost timestamps on arrival: 1 ms nominal, σ = 20 µs jitter, µs resolution.53,4137.12260/60297.1%53,41153,560+336 µs (+108%)+119 µs (+119%)

The example is the median-size block group of 64 seeds per shape. Times are single thread, best of 60, Apple M3 on AC power; each added time's percentage is of the same block group's values-only encode (311 µs) or decode (100 µs). vs values is the timestamps' bytes as a share of the values' 17,976.

Cost per gap

A grid with k gaps per minute, each 5 ms to 3 s; median of 16 block groups per row.

gaps per minuteirregular blocksbytesbits/samplebytes per gap
00/60630.008—
11/601250.01762
22/601640.02251
55/607470.100137
109/609140.12285
2017/601,1400.15254
6038/601,3740.18322

Harder shapes

For comparison, shapes with many more irregular blocks (from bench/time_axis.py). Only the cases with random intervals cost more than the values. Percentages are of the values-only encode and decode, as above.

timestampsbytesbits/sampleirregular blocks encode time addeddecode time added
1 kHz, 20 gaps1,0940.14619/60+97 µs (+31%)+60 µs (+60%)
1 kHz, 1% dropped1,4610.19560/60+261 µs (+84%)+123 µs (+122%)
drifting clock (0.99998 ms)3390.04560/60+216 µs (+69%)+85 µs (+84%)
jitter σ=10 µs, ns resolution120,71216.09560/60+416 µs (+134%)+178 µs (+178%)
jitter σ=10 µs, µs resolution45,9036.12060/60+336 µs (+108%)+126 µs (+125%)
Poisson events, mean 1 ms, µs87,88911.71960/60+341 µs (+110%)+160 µs (+160%)
Poisson events, mean 1 ms, ns164,66121.95560/60+379 µs (+122%)+189 µs (+188%)
deadband logging, ms grid55,4987.40060/60+513 µs (+165%)+219 µs (+218%)
bursts 10 kHz / idle1020.01460/60+121 µs (+39%)+91 µs (+91%)

1% dropped and a drifting clock stay cheap: their intervals take two or three values. Jitter, Poisson events and deadband logging have random intervals, and ns resolution costs more than µs when the jitter really is at ns resolution.

Rounding timestamps: Params.time_error

When the jitter is measurement noise, Params(time_error=e) lets fluxcode round the timestamps (0 to 0.5; the default 0 keeps them exact). Each block gets a coarse consistent step at which 95% of its times keep within e of their own interval, and none moves more than e of the block's mean interval. The values are kept as measured, and the format doesn't change: the decoder sees ordinary times.

Bytes the timestamps add per block group against the time error
timestampsexacte = 0.02e = 0.05e = 0.1
Perfect grid66 B · 0/60
+12 µs (+4%)
66 B · 0/60
+30 µs (+10%) · max 0.000
66 B · 0/60
+28 µs (+9%) · max 0.000
66 B · 0/60
+29 µs (+9%) · max 0.000
Grid with a few gaps167 B · 2/60
+41 µs (+13%)
167 B · 2/60
+53 µs (+17%) · max 0.000
167 B · 2/60
+42 µs (+14%) · max 0.000
167 B · 2/60
+42 µs (+14%) · max 0.000
Noisy clock53,413 B · 60/60
+358 µs (+115%)
21,168 B · 60/60
+433 µs (+139%) · max 0.010
2,990 B · 60/60
+435 µs (+140%) · max 0.050
65 B · 0/60
+86 µs (+28%) · max 0.092
Jitter σ = 10 µs (1%), ns, on round times120,712 B · 60/60
+419 µs (+135%)
14,726 B · 60/60
+418 µs (+134%) · max 0.010
63 B · 0/60
+86 µs (+28%) · max 0.046
63 B · 0/60
+84 µs (+27%) · max 0.046
The same, free-running: 50 µs off the epoch's grid120,653 B · 60/60
+421 µs (+136%)
14,667 B · 60/60
+409 µs (+131%) · max 0.010
66 B · 0/60
+113 µs (+36%) · max 0.044
66 B · 0/60
+113 µs (+36%) · max 0.054
Drifting clock (0.99998 ms)339 B · 60/60
+214 µs (+69%)
87 B · 0/60
+94 µs (+30%) · max 0.020
93 B · 0/60
+91 µs (+29%) · max 0.049
93 B · 0/60
+88 µs (+28%) · max 0.098
Drift and jitter σ = 10 µs, 37 µs off120,648 B · 60/60
+409 µs (+132%)
14,661 B · 60/60
+409 µs (+132%) · max 0.010
210 B · 1/60
+111 µs (+36%) · max 0.050
111 B · 0/60
+94 µs (+30%) · max 0.100
Poisson events, mean 1 ms, ns164,661 B · 60/60
+384 µs (+124%)
83,895 B · 60/60
+504 µs (+162%) · max 0.001
73,951 B · 60/60
+495 µs (+159%) · max 0.002
66,463 B · 60/60
+500 µs (+161%) · max 0.005
Deadband logging, ms grid55,498 B · 60/60
+552 µs (+178%)
55,498 B · 60/60
+651 µs (+209%) · max 0.000
55,498 B · 60/60
+648 µs (+209%) · max 0.000
55,498 B · 60/60
+642 µs (+207%) · max 0.000
1024 Hz clock, jitter 0.5% (no 1-2-5 step divides the period)112,735 B · 60/60
+402 µs (+129%)
9,221 B · 49/60
+396 µs (+127%) · max 0.020
131 B · 0/60
+136 µs (+44%) · max 0.050
100 B · 0/60
+113 µs (+36%) · max 0.101
60 Hz clock, jitter 0.5% (no 1-2-5 step divides the period)144,005 B · 60/60
+414 µs (+133%)
5,995 B · 48/60
+366 µs (+118%) · max 0.020
216 B · 0/60
+120 µs (+39%) · max 0.051
125 B · 0/60
+115 µs (+37%) · max 0.102

Each cell: bytes the timestamps add · irregular blocks of 60, then the encode time they add (percentage of the values-only encode, 311 µs) and the largest move in mean intervals. Single thread, best of 60, Apple M3 on AC power. The chart samples e every 0.0025.

Measurements behind the choices (1-2-5 steps over powers of two, no interpolation of values, no resampling, per-block phases): docs/TUNING.md, Time error.