Stellar ControlMission control · by Stellar Systems v0.1.0

Core Concepts

Measures and Current Values

From raw frames to calibrated samples, derived measures and the current value table, and how to read them.

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]
  1. Raw frame. The gateway publishes each frame it receives, as is, on stellar.tm.raw.<gateway>.<target> (stream TM_RAW), with the ground reception time in the Stellar-Ground-Time header (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.
  2. 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>.
  3. Raw values. The driver decodes each frame or unit into raw values, published on stellar.tm.decoded.<target> (stream TM_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).
  4. 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> (stream PARAMS).
  5. Current values. The current value table keeps the latest sample of each measure.

Raw values decoded by a driver:

JSON
[
  {"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:

JSON
{
  "value": 100.0,
  "raw": 20000,
  "time": "2026-10-02T10:15:03.500Z",
  "ground_time": "2026-10-02T10:15:03.912Z",
  "link": "nominal",
  "delivery": "realtime"
}
FieldMeaning
valuePhysical 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
rawRaw value of a measure calibrated by the MCS; absent otherwise
timeTime of the sample: on-board time when the driver provides it, else ground reception time
ground_timeGround reception time: the reference of freshness
linkLink of the frame the sample came from
deliveryrealtime, 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.

YAML
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 raw nor calibration. 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 (when stable becomes 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_age of the measure by check and requires, is measured from its ground_time.
  • Unknown values. A temporal derived measure whose window is empty (during a loss of signal, for instance) is published once with value: null; check and requires refuse it like a stale value.
  • Read as it will be. The table follows PARAMS a moment later, while wait until, expect and the verification of a telecommand follow the stream itself. check, requires and the start of a wait until therefore read the table together with the samples of the measure published after the last one it took, and keep the latest by time as 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 until a moment before its stream stores it. A check, a log or the requires of 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#

ReaderRule
checkReads the current value; fails if absent or older than the max_age of the measure
requiresSame, before encoding: the telecommand is REJECTED
expect … withinIgnores the table: waits for a sample received after the last SENT of the step
wait until … withinStarts from the current values, then follows new samples
verifyOnly samples received after SENT

See Time and Freshness and the Statement Reference.

Reading current values#

CLI#

Shell
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 30

See Direct Telecommands and Values.

Selectors#

A measure is selected in three ways, in the CLI, the API and the web console:

SelectorSelects
tcu[TCU1].respondingOne instance of a component
tcu.respondingEvery instance of the component (or its single instance)
respondingThat measure in any component

API#

GET /v1/targets/{target}/values returns every current value of a target:

JSON
{
  "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.

Stellar Control · v0.1.0

↑↓ to moveEnter to open