Interface control document, version 1.0.0, 2026-09-21 · Nolle Engineering GmbH
The normative document is
qes-record-icd.json. This page is a reading companion: it says the same
things with the colours and the worked examples that a JSON file cannot carry. Where the two
disagree, the JSON is the specification. qes check validates a record against the
JSON and fails on any field the document does not describe, so a disagreement between the
document and the data is found rather than argued about.
A record is the byte stream an instrument emitted, kept as it came off the link. The copy on the card and the copy over the link are byte-identical: the firmware encodes once and writes the same bytes to both.
COBS frame → [ CBOR map ][ CRC-16 ] → 0x00
| Framing | COBS. 0x00 is the delimiter and appears nowhere else in the
encoding, so splitting the file on 0x00 recovers the frames. A run of zero bytes
inside a record is impossible in the encoding — though not in the medium, after a power cut. |
|---|---|
| Payload | CBOR, a flat map with short text keys. Major types used: 0, 1, 2, 3, 4, 5. A decoder that rejects arrays counts every science packet as corrupt. |
| Checksum | CRC-16/CCITT-FALSE — polynomial 0x1021, initial value 0xFFFF, not
reflected, little byte first, over the CBOR bytes only and inside the COBS frame. |
| Console text | A reply to a console command is framed on its own, so it never merges into a packet. A frame whose bytes are all printable ASCII is such a reply — count it separately, do not treat it as corruption. |
The first byte of a frame is not a delimiter. COBS replaces every zero in the payload with a pointer to where the next one was, so a frame begins with a pointer and the only 0x00 in it is the one that ends it. A pointer of 05 says: the next four bytes contain no zero, and then there was one.
Row 1 and row 2 share a colour per COBS group. pointer a zero the pointer stood for frame delimiter
Row 3: CBOR map header key value CRC-16
Hover any byte to see what it is.
Real bytes, from the published null record at offset 81.
An event packet for a second in which that channel saw nothing. It is the smallest complete packet in the record, which makes it the clearest one to walk through: a present channel with an empty array says “nothing this second”, which is not the same as the channel being absent.
| key | value |
|---|---|
t | 1789976938158 |
i | 0 |
s | 0 |
e | array of 0, first: [] |
Real bytes, from the published null record at offset 312404.
h is SHA-256 of the previous anchor's hash concatenated with every link byte since that anchor's frame. o is this frame's offset in the stream, so a verifier can compare the difference between two anchors' o against the bytes it actually counted and measure anything inserted or dropped.
| key | value |
|---|---|
i | 205 |
k | 1 |
n | 75 |
q | 1 |
o | 15778902 |
t | 1789976997684 |
b | 3965654266 |
c | 1177 |
f | 127 |
pk | 083ad19f9de95d8b |
h | 63278715545820a65b27c118825d2004029c7d58487ebe14151beb7e053f8c7f |
Real bytes, from the published null record at offset 4411.
Thirty-two bytes from the secure element's own conditioned generator, recorded in the same enclosure on the same supply through the same pipeline as the decay channels, so that any analysis can be run against something that should show nothing. Never an entropy source, and never in the product path.
| key | value |
|---|---|
i | 206 |
t | 1789976939687 |
r | f822bf19b099a0c70db794bf8f8b96136798599bfa771f64670775cec96e9ba4 |
Every packet carries i. Dispatch on it before reading anything else: keys are
scoped to a channel and are reused with different meanings on others.
| i | name | clock | notes |
|---|---|---|---|
0 | decay_ch0 | capture | |
1 | decay_ch1 | capture | |
2 | decay_ch2 | capture | |
8 | pps_ref_on_reference_clock | reference | the second-marker input as counted on the external reference timebase. Not wired on the bench instrument: the channel appears with an empty event array. |
9 | pps_ref_on_core_clock | core | the same input on the core cycle counter. Not wired on the bench instrument: empty. |
10 | gnss_pps_on_reference_clock | reference | the GNSS second marker on the external reference timebase, hardware-latched. |
11 | gnss_pps_on_core_clock | core | the GNSS second marker read on the core cycle counter after the latch, so it carries interrupt latency. Superseded for anchoring by channel 13. |
12 | reference_beat | reference | a low-frequency beat between the two frequency references, hardware-latched on the reference timebase. Used to compare the two clocks against each other; not a measurement channel. |
13 | gnss_pps | capture | the GNSS one-pulse-per-second edge captured on the same counter as the decays, so it shares their timebase |
200 | health | capture | diagnostic, not measurement; a decoder may ignore it |
205 | chain | capture | |
206 | control | capture | |
250 | session_header | capture |
Three clocks, and mixing them is the easy mistake. Only channels 0–2 and 13 share a timebase. Their ticks are not comparable with the others' without conversion.
| clock | rate, Hz | tick | wraps after |
|---|---|---|---|
| capture | 150 000 000 | 6.6667 ns | 28.6331 s |
| reference | 8 391 841 | 119.16 ns | 511.8 s |
| core | 600 000 000 | 1.6667 ns | 7.158 s |
The value on the wire is the low 32 bits of the capture counter. The modular difference between consecutive events is the interval, and it is correct whenever the gap is shorter than one wrap — which it is for every decay interval at any usable rate.
interval_ticks = (t[k+1] - t[k]) & 0xFFFFFFFF
The first three events of a packet in the published null record.
e[0] = 630541745 e[1] = 631601455 (e[1] - e[0]) & 0xFFFFFFFF = 1 059 710 ticks = 7.065 ms e[2] = 633426669 (e[2] - e[1]) & 0xFFFFFFFF = 1 825 214 ticks = 12.168 ms
For a gap longer than one wrap the packet's own t fixes the wrap count: the
expected tick delta is Δt_ms × 1e-3 × 150e6, and the number of wraps is that minus
the modular difference, divided by 232, rounded and clamped at zero.
6.667 ns is a quantisation step, not a timing accuracy. The detector front end contributes several microseconds of its own, which is three orders of magnitude larger. Nothing about absolute timing should be built on the step size.
events — channels 0, 1, 2, 8, 9, 10, 11, 12, 13Channels 0, 1, 2 and 13 are counted on the capture clock; 8 to 12 are not. Each channel's entry in `channels` names the clock its ticks belong to. A channel that is present with an empty `e` array is reporting that it saw nothing that second, which is different from the channel being absent.
| key | type | unit | meaning |
|---|---|---|---|
t | uint | ms | the instrument's unix time when the packet was built. Annotation only, never the measurement; it also fixes the wrap count of a long gap. |
i | uint | — | channel id |
s | uint | — | status bits |
e | array of uint | capture ticks | THE MEASUREMENT: raw counter values, one per event, in order, low 32 bits. Nothing is derived on the instrument. |
chain_anchor — channel 205
| key | type | unit | meaning |
|---|---|---|---|
i | uint | — | 205 |
n | uint | — | anchor sequence: 0 at boot, +1 each anchor, strictly consecutive within a boot |
k | uint | — | kind |
h | bytes[32] | — | h_n = SHA-256(h_{n-1} || every link byte since anchor frame n-1 inclusive, excluding this frame). h_{-1} is 32 zero bytes. |
o | uint | bytes | link bytes chained since boot before this anchor frame, i.e. its offset in the stream. The difference between two anchors' o, compared with the bytes actually counted, measures inserted or dropped bytes. |
c | uint | — | the secure element's monotonic counter after the increment made for this anchor; valid only when flag bit 3 is set. Never resettable. |
f | uint | — | flags |
pk | bytes[8] | — | first 8 bytes of SHA-256(0x04 || X || Y) of the signing public key; empty when there is none |
q | uint | — | SD file sequence at emission |
t | uint | ms | instrument unix time |
b | uint | — | boot id: distinguishes two boots that report the same boot count |
fw | text | — | build identifier (boot anchors only) |
ih | bytes[32] | — | SHA-256 of the firmware image read back from flash (boot anchors only) |
il | uint | bytes | length of that image (boot anchors only) |
rst | text | — | reset cause (boot anchors only) |
bc | uint | — | battery-backed boot count (boot anchors only) |
sn | bytes[9] | — | secure element serial number (boot anchors only) |
rtc | uint | s | RTC epoch seconds (boot anchors only) |
chain_signature — channel 205Emitted only when the secure element is present and provisioned, i.e. anchor flag bit 4 is set. It follows its anchor by about 0.1 s, so it is usually several packets later in the stream. A missing signature for any anchor but the last is a verification failure; for the last it is the signature of a power cut.
| key | type | unit | meaning |
|---|---|---|---|
i | uint | — | 205 |
n | uint | — | the anchor sequence number this signature covers |
g | bytes[64] | — | ECDSA-P256 signature, r || s, over SHA-256 of that anchor's CBOR payload exactly as framed: the map bytes only, without the CRC trailer |
control — channel 206A co-located control channel: same enclosure, temperature, supply and pipeline as the decay channels, but a conditioned opaque source, recorded so that any analysis can be run against something that should show nothing. Never an entropy source and never in the product path. Its absence from a record means the chip is unprovisioned or missing, never that it was quiet.
| key | type | unit | meaning |
|---|---|---|---|
i | uint | — | 206 |
t | uint | ms | instrument unix time |
r | bytes[32] | — | output of the secure element's conditioned random generator |
health — channel 200Documented field by field because an analyst building anything from the derived context table needs these, and 'documentation available on request' is the thing an interface control document exists to abolish. `scale` is the factor to MULTIPLY the wire value by to get the stated unit. Where it is absent the wire value is already in that unit. open_map stays true because the map is the instrument's diagnostic channel and grows: adding a counter must not break a decoder. `qes check` reports how many keys it skipped for that reason, so a growing gap is visible rather than silent.
| key | type | unit | meaning |
|---|---|---|---|
t | uint | ms | the instrument's own unix time when the packet was built. An annotation, never a measurement |
i | uint | — | 200 |
s | uint | — | status bits, as on the event channels |
fw | text | — | firmware build identifier, sent every second so any slice of a record names its build |
qe | int | ps | quantisation error of the NEXT pulse: the sawtooth correction |
wk | uint | week | GPS week number of the next pulse |
tow | uint | ms | GPS time of week of the NEXT pulse |
fx | uint | — | receiver fix type; 3 is a 3D fix |
fok | uint | — | receiver reports the fix usable |
sv | uint | — | satellites tracked |
nus | uint | — | satellites used in the solution |
td | uint | — | time dilution of precision, x100 (multiply the wire value by 0.01) |
pd | uint | — | position dilution of precision, x100 (multiply the wire value by 0.01) |
hd | uint | — | horizontal dilution of precision, x100 (multiply the wire value by 0.01) |
gd | uint | — | geometric dilution of precision, x100 (multiply the wire value by 0.01) |
agc | uint | — | receiver automatic gain control count, 0 to 8191 |
jam | uint | — | receiver jamming indicator, 0 to 255 |
noi | uint | per ms | receiver noise level |
cx | uint | dBHz | highest carrier-to-noise over the satellites used |
cm | uint | dBHz | mean carrier-to-noise over the satellites used |
ta | uint | ns | the receiver's own estimate of its time accuracy; 0xFFFFFFFF means it reports none |
tpc | uint | — | pulse-timing messages seen: pulse liveness |
lat | int | deg | latitude, degrees x 1e7. For a stationary instrument, wander here is a multipath indicator that shows before the accuracy estimate does (multiply the wire value by 1e-07) |
lon | int | deg | longitude, degrees x 1e7 (multiply the wire value by 1e-07) |
alt | int | mm | height above mean sea level; altitude sets the cosmic-ray background |
hel | int | mm | height above the ellipsoid |
hac | uint | mm | horizontal accuracy estimate |
vac | uint | mm | vertical accuracy estimate |
pr | uint | Pa | ambient pressure. Present only when the environmental sensor is fitted |
tc | int | degC | AMBIENT temperature, centi-degrees. Distinct from tdie (multiply the wire value by 0.01) |
rh | uint | % | relative humidity, hundredths of a percent (multiply the wire value by 0.01) |
tdie | int | degC | on-die temperature, centi-degrees. Self-heated to about 48 C; tracks ambient, is not ambient (multiply the wire value by 0.01) |
pps | uint | reference ticks | the GNSS pulse latched on the reference timebase |
pdl | uint | reference ticks | ticks since the previous pulse |
pn | uint | — | pulses captured since boot. THE CONTINUITY KEY: use it as the pulse index, never the position of an edge in an array |
tm | uint | capture ticks | raw 16-bit capture count read at the pulse; a coherence check only, no domain transfer |
bt | uint | reference ticks | clock-comparison beat edge |
bdl | uint | reference ticks | ticks since the previous accepted beat edge |
bn | uint | — | accepted beat edges since boot; 0 means the comparison is not running |
bch | uint | — | captures inside the holdoff, cumulative: chatter, not new edges |
bf | uint | reference ticks | first edge of the last completed beat burst |
bl | uint | reference ticks | last edge of that burst |
bnn | uint | — | edge count of that burst |
bs | uint | reference ticks | sum of (edge - bf) over that burst, so the centroid is bf + bs/bnn |
bp | uint | — | parity of that burst: which beat edge it was |
lps | uint | 1/s | main-loop iterations in the last second |
lmx | uint | us | worst single loop iteration in the last second |
stk | uint | bytes | lowest free stack ever seen, by paint and scan |
hp | uint | bytes | free heap |
up | uint | s | uptime |
qun | uint | — | events anchored while the service gap exceeded half a capture span, summed over channels. Must stay 0 |
qgs | uint | — | service periods within an eighth of the capture span: the real-time margin. Must stay 0 |
qrs | uint | — | capture-ring flushes made to recover from persistent desync |
qov | uint | — | events the capture path discarded, summed over channels |
qol | uint | — | packet-FIFO overflows, summed over channels |
lov | uint | — | frames that exceeded the encode buffer and reached NEITHER sink. Must stay 0: non-zero means the record has holes |
lsw | uint | — | USB writes that returned short because the host stopped consuming. Non-zero means the USB copy and the card copy are no longer byte-identical. Must stay 0 |
ltd | uint | bytes | bytes discarded with those short writes |
crs | uint | bytes | length of an unread crash report from the previous boot, held in RAM. Non-zero means the instrument restarted on a fault and still holds the reason |
ssc | uint | — | times the stack scan reached the top of RAM instead of finding its mark. Must stay 0 |
tee | uint | — | encodes the instrument refused: a wrong map entry count, or a payload that would not fit. Must stay 0 |
qn0 | uint | — | events the capture path handed to channel 0's packet FIFO since boot |
qn1 | uint | — | the same for channel 1 |
qn2 | uint | — | the same for channel 2 |
qls0 | uint | — | fine/coarse lockstep faults on channel 0. A desynced channel still counts correctly and only timestamps wrongly, so this is the only place a marginal input shows |
qls1 | uint | — | the same for channel 1 |
qls2 | uint | — | the same for channel 2 |
qcs0 | uint | — | events on channel 0 whose coarse capture disagreed with the fine-only reconstruction by a whole fine span or more |
qcs1 | uint | — | the same for channel 1 |
qcs2 | uint | — | the same for channel 2 |
qcs3 | uint | — | the same for the pulse channel |
sbs | uint | — | card writes deferred because the card was busy |
sbf | uint | — | writes forced after 2000 ms of deferral |
lsd | uint | us | worst card-service visit in the last second |
lgp | uint | us | worst receiver-service visit in the last second |
ltx | uint | us | worst transmit visit in the last second |
lqc | uint | us | worst capture-service visit in the last second |
sdd | uint | bytes | card bytes dropped by ring overflow; 0xFFFFFFFF means no card |
sdf | uint | bytes | free space, measured at boot and rotation and decremented by writes |
sdw | uint | bytes | bytes written this boot |
sdq | uint | — | file sequence number |
chn | uint | — | chain anchors emitted since boot |
sig | uint | — | signatures that failed. Must stay 0 on a provisioned instrument; stays 0 on one without a chip because nothing is attempted, and the anchors' flag byte says which |
gpr | uint | — | battery-backed general-purpose register read back after write: 0 means the register is not retaining |
session_header — channel 250What the instrument was when it started: build, reset cause, RTC time, SD file sequence.
| key | type | unit | meaning |
|---|---|---|---|
i | uint | — | 250 |
A recording starts and stops between anchors, so the bytes before
the first verified anchor and after the last are covered by no hash and can be altered without
any check failing. qes verify --covered-range prints the range that is covered;
qes recompute --signed-only restricts itself to it. Treat everything outside it as
unattested.