Telemetry reaches the MCS as opaque frames and leaves the compute stage as samples: typed,
calibrated values of named measures, each with its times, its link and its delivery. The latest
sample of every measure is kept in the current value table, which check, requires, the
web console, stellar watch and the API read.
This page follows a value from the antenna to the table. To declare measures, see Measures, Limits and Calibration and Derived Measures.
The pipeline#
flowchart LR
gw[Gateway] -- "tm.raw (TM_RAW)" --> tr[Transport]
tr -- "tm.unit" --> drv[Driver]
gw -- "tm.raw, without transport" --> drv
drv -- "tm.decoded (TM_DECODED)" --> comp[Compute stage]
comp -- "param (PARAMS)" --> table[(stellar_values)]
comp --> exe[Executor]
comp --> alm[Alarm service]
comp --> conn[Output connectors]- Raw frame. The gateway publishes each frame it receives, as is, on
stellar.tm.raw.<gateway>.<target>(streamTM_RAW), with the ground reception time in theStellar-Ground-Timeheader (RFC 3339) and, for data replayed from on-board storage,Stellar-Delivery: deferred. Raw frames stay archived exactly as received, so that they can be decoded again. - Unit. When the link has a transport, it
extracts the application units (packets) from the frames and publishes them on
stellar.tm.unit.<transport>.<target>. - Raw values. The driver decodes each frame or unit into raw values, published on
stellar.tm.decoded.<target>(streamTM_DECODED, one day of retention) as an array of{component, instance, measure, value, onboard_time}. The headers repeat the ground time and the delivery of the frame and add the link (Stellar-Link), the configuration revision of the target (Stellar-Config), the driver that decoded it (Stellar-Driver: <software>@<version>) and the environment of the link (Stellar-Environment). - Samples. The compute stage calibrates the raw values with the catalogue of that revision,
computes the derived measures, and publishes one sample per measure on
stellar.param.<target>.<component>.<instance>.<measure>(streamPARAMS). - Current values. The current value table keeps the latest sample of each measure.
Raw values decoded by a driver:
[
{"component": "tcu", "instance": "TCU1", "measure": "anode_voltage", "value": 20000,
"onboard_time": "2026-10-02T10:15:03.500Z"},
{"component": "tcu", "instance": "TCU1", "measure": "mode", "value": "STANDBY"}
]Samples#
A sample, as published on stellar.param.… and kept in the table:
{
"value": 100.0,
"raw": 20000,
"time": "2026-10-02T10:15:03.500Z",
"ground_time": "2026-10-02T10:15:03.912Z",
"link": "nominal",
"delivery": "realtime"
}| Field | Meaning |
|---|---|
value | Physical value: a number in the unit of the measure, a boolean, an enum value, or bytes in hexadecimal; null when a temporal derived measure becomes unknown |
raw | Raw value of a measure calibrated by the MCS; absent otherwise |
time | Time of the sample: on-board time when the driver provides it, else ground reception time |
ground_time | Ground reception time: the reference of freshness |
link | Link of the frame the sample came from |
delivery | realtime, or deferred for telemetry recorded on board and downloaded later |
For a derived measure, time and link are those of its most recent input, ground_time
that of its oldest input, and delivery is deferred when any input is: a derived measure is
never fresher than its oldest source.
A single-instance component uses _ as instance token in the subject
(stellar.param.psu-sim-1.psu._.voltage).
Calibration#
The driver publishes raw values; the MCS applies the calibration declared in the catalogue.
anode_voltage:
raw: u16
type: f32
unit: V
calibration: {polynomial: [0.0, 0.005]}
limits: {soft: [5 V, 280 V], hard: [0 V, 300 V]}- Supported forms: polynomial, interpolation table, and enumeration (raw code to enum value).
- Raw and physical values are both archived; a corrected calibration can be applied again to the history.
- Limits apply to the physical value.
- A raw value the calibration does not cover (outside the table, unknown code) produces no sample and is logged.
- Calibration kept by the driver. When a calibration is intellectual property, the driver
publishes the physical value itself and the measure is declared without
rawnorcalibration. Such measures have no archived raw value and cannot be recalibrated.
See Measures, Limits and Calibration.
Derived measures#
Derived measures are computed by the same stage, in the same flow:
- Pure derived measures (arithmetic, comparisons, booleans, math functions) are recomputed at each update of an input, and published only once every input has a value.
- Temporal derived measures (
age,stable,avg,rate, …) are computed over the real-time samples only, and also re-evaluated at their next deadline (whenstablebecomes true, when a sample leaves a window), without waiting for a new sample. They are published only when their value changes.
A derived measure is used like a measure everywhere: expect, check, requires, verify,
alarms, the table. See Derived Measures.
The current value table#
The table lives in the KV bucket stellar_values, one key per measure:
<target>.<component>.<instance>.<measure> (sim-1.tcu.TCU1.anode_voltage,
psu-sim-1.psu._.voltage). It is fed by the PARAMS stream, where deferred samples arrive too.
- Latest by
time. A sample replaces the known value only if it is not older: a sample older than the current value never replaces it, real-time or deferred. A dump of old on-board telemetry therefore never hides the live value. - Age. The age of a value, compared with the
max_ageof the measure bycheckandrequires, is measured from itsground_time. - Unknown values. A temporal derived measure whose window is empty (during a loss of signal,
for instance) is published once with
value: null;checkandrequiresrefuse it like a stale value. - Read as it will be. The table follows
PARAMSa moment later, whilewait until,expectand the verification of a telecommand follow the stream itself.check,requiresand the start of await untiltherefore read the table together with the samples of the measure published after the last one it took, and keep the latest bytimeas the table will: what a step just saw, the next one sees too. - Caught up with the run. The server hands a sample to a
wait untila moment before its stream stores it. Acheck, alogor therequiresof a telecommand of the run therefore wait, 2 s at most, until the value they read is no older than the newest sample of its component that the run already received, whichever step received it.
Freshness in procedures and telecommands#
| Reader | Rule |
|---|---|
check | Reads the current value; fails if absent or older than the max_age of the measure |
requires | Same, before encoding: the telecommand is REJECTED |
expect … within | Ignores the table: waits for a sample received after the last SENT of the step |
wait until … within | Starts from the current values, then follows new samples |
verify | Only samples received after SENT |
See Time and Freshness and the Statement Reference.
Reading current values#
CLI#
stellar watch sim-1 # every value of the target, then updates
stellar watch sim-1 'tcu[TCU1].anode_voltage' mode # a selection
stellar watch sim-1 'tcu.responding' --until true --timeout 30See Direct Telecommands and Values.
Selectors#
A measure is selected in three ways, in the CLI, the API and the web console:
| Selector | Selects |
|---|---|
tcu[TCU1].responding | One instance of a component |
tcu.responding | Every instance of the component (or its single instance) |
responding | That measure in any component |
API#
GET /v1/targets/{target}/values returns every current value of a target:
{
"values": [
{"component": "tcu", "instance": "TCU1", "measure": "anode_voltage",
"sample": {"value": 100.0, "raw": 20000, "time": "…", "ground_time": "…",
"link": "nominal", "delivery": "realtime"}}
]
}GET /v1/targets/{target}/values/watch?measures=tcu[TCU1].responding,psu.voltage opens a WebSocket
that sends the matching current values, then every update of the table. See the
values API.
Deferred telemetry#
Telemetry recorded on board and downloaded later — long-term telemetry files (LTTM) decoded by the
driver, or frames replayed with Stellar-Delivery: deferred — follows the same path, marked
deferred:
- it is archived in
PARAMS, and pure derived measures are computed for it, from its own values completed by the latest known ones, at the instant of its values; - it never replaces a more recent current value;
- temporal derived measures and alarms ignore it: it is analysed afterwards, not alarmed on.
See Time and Freshness and File Transfers.
Restart without persisted state#
The compute stage keeps no state on disk. When it meets a target (at start, or when the
configuration revision of the target changes), it rebuilds its state from the PARAMS stream:
the last sample of each measure, then the samples of the longest window declared, and the
telecommand events of that window for sent_at. Nothing is republished during this replay; the
temporal derived measures are then evaluated once and only those whose value changed are
published. A sent_at older than the window is lost at restart: derived measures reading it are
unknown until the next send of the telecommand.
Beyond the retention#
JetStream keeps PARAMS for 30 days by default. The long-term history of measures lives in the
database fed by an output connector (TimescaleDB with the reference
connector), queried with the tools of that database, such as Grafana. The API never reads it.