Stellar ControlMission control · by Stellar Systems v0.1.0

Catalogues

Derived Measures

Pure and temporal derived measures computed by the compute stage.

A derived measure is computed by the MCS from other measures of the same component: a temperature difference, a flag, an average over a window, the time since a condition holds. The compute stage evaluates it at each update of its inputs and publishes it like any measure, so a derived measure is used exactly like a measure in expect, check, wait until, requires, verify and alarms.

YAML
components:
  tcu:
    instances: tcu_id
    derived:
      ppu_temperature_delta:
        type: f32
        unit: degC
        expr: ppu_output_temperature - ppu_input_temperature
      active:
        type: bool
        expr: mode is not STANDBY
      ping_answered:
        type: bool
        expr: updated_at(responding) > sent_at(ping)
      cathode_ready:
        type: bool
        expr: stable(cathode_temperature > 800 degC, 30 s)
      anode_voltage_rate:
        type: f32
        unit: V/s
        expr: rate(anode_voltage, 10 s)

Keys#

KeyRequiredMeaning
typeyesResult type: numeric, bool, bytes, a named enum, or enum with values.
unitnumeric resultsUnit of the result; checked against the unit of expr.
expryesPure or temporal expression over the measures and derived measures of the component.
limitsnoSoft and hard limits, as for a measure: they raise the alarm named after the derived measure.
descriptionnoFree text for operators.

The expression is type-checked against the declared type (catalogue::derived-type-mismatch): an integer result fits an integer or float type, a decimal result needs a float type, and units must be compatible (a result in mV fits unit: V).

Scope#

An expression refers by name, without index, to the measures and derived measures of its component. On a multi-instance component, a derived measure exists per instance and reads the measures of that instance: tcu[TCU2].active reads tcu[TCU2].mode. Calculations across components or across targets are not supported.

A derived measure may depend on other derived measures; the dependency graph must be acyclic (catalogue::derived-cycle), and the compute stage evaluates them in dependency order.

Pure expressions#

ElementSyntax
Numbers42, 0.5, 1e-3, with an optional unit: 800 degC, 5 V, 30 s
Booleanstrue, false
Enum valuesSTANDBY, compared with is
Arithmetic+, -, *, /, unary -
Comparisons<, <=, >, >=
Equalitya is b, a is not b, and a is b +/- tolerance for numbers
Logicand, or, not
Grouping( … )

Precedence, from lowest to highest: or, and, not, comparisons and is, + and -, * and /, unary -.

Units follow dimensional analysis: +, - and comparisons need compatible units (V and mV), * and / combine them (V / s). A number without unit cannot be added to or compared with a value that has one (expr::missing-unit): write 5 V, not 5.

Functions (pure):

FunctionResult
abs(x), floor(x), ceil(x), round(x)Same type and unit as x
sqrt(x), exp(x), ln(x), log10(x), asin(x), acos(x), atan(x)Number without unit; x without unit
sin(x), cos(x), tan(x)Number without unit; x an angle (rad, deg) or a plain number
atan2(a, b)Angle in rad; a and b in compatible units
hypot(a, b)Unit of a; a and b in compatible units
clamp(x, low, high)Unit of x; the three in compatible units
YAML
bus_power:
  type: f32
  unit: W
  expr: bus_voltage * bus_current
overcurrent:
  type: bool
  expr: bus_current > 2.5 A or bus_voltage is 0 V +/- 0.1 V

Temporal functions#

Temporal functions form a closed list, built into the language. They take literal windows, bounded by derived.max_window of the global configuration (1 h by default; expr::window-too-long above it).

FunctionResultMeaning
age(x)DurationTime since the last real-time sample of x
updated_at(x)InstantTime of the last real-time sample of x
sent_at(tc)InstantLast time the telecommand tc of the same instance reached SENT
since(cond)DurationHow long cond has been true without interruption (0 when false)
stable(cond, d)boolWhether cond has been true for at least d
avg(x, d)Unit of xMean of the samples in the window
min(x, d), max(x, d)Unit of xExtremes of the samples in the window
delta(x, d)Unit of xLast sample minus first sample of the window
rate(x, d)Unit of x per seconddelta divided by the time between the first and last samples
changes(x, d)u64Number of value changes in the window
  • The first argument of age, updated_at and the window functions is a measure or derived measure name, not an expression (expr::wrong-arguments): declare a derived measure for an expression and pass its name.
  • avg, min, max, delta and rate need a numeric measure; changes accepts any.
  • Instants compare with instants (updated_at(responding) > sent_at(ping)); instant minus instant is a duration; instant plus or minus a duration is an instant. Durations are numbers in a time unit: age(responding) > 30 s, or a derived measure of type: f32 and unit: s.
  • Temporal functions are allowed in derived measures and alarm conditions, but not in requires and verify of a telecommand, nor in procedures: declare a derived measure and use it there.
YAML
responding_late:
  type: bool
  expr: age(responding) > 45 s
heater_cycles:
  type: u64
  expr: changes(heater_on, 10 min)
battery_trend:
  type: f32
  unit: V
  expr: avg(battery_voltage, 5 min)

How they are evaluated#

  • Sample time. Windows and durations use the time of each sample: on-board time when the driver provides it, else ground reception time.
  • Real time only. Temporal functions only see real-time samples: deferred samples (telemetry stored on board and dumped later) never enter their windows nor trigger their evaluation.
  • Deadlines. Besides evaluating at each input sample, the compute stage schedules the next moment the result can change: the end of the duration of stable, the oldest sample leaving a window, and every second for age and since. cathode_ready becomes true 30 s after the threshold is crossed, without a new sample.
  • Unknown value. When a window holds no sample (a loss of signal, for instance), the result is unknown: it is published once with value: null, and check and requires refuse it like a stale value. rate is unknown with fewer than two samples at different times.
  • Publication. A temporal derived measure is published only when its value changes, dated at the evaluation time.
  • Restart. The compute stage keeps no state of its own: when it meets a target (at start, or on a new configuration revision) it replays PARAMS over the longest window of the catalogue, and the telecommand events of that window for sent_at, without republishing anything. A sent_at older than the window is lost at a restart: derived measures reading it are unknown until the telecommand is sent again.
  • Determinism. The same samples give the same results: the history of a temporal derived measure contains only its published values.

When a derived measure is computed#

  • It is computed again at each update of one of its inputs.
  • Its time is that of its most recent input; its freshness (ground_time) is that of its oldest input: a derived measure is never fresher than its oldest source.
  • It is not published while one of its inputs has no value.
  • It is deferred when one of its inputs is deferred. A deferred frame computes its pure derived measures from its own values, completed by the last known ones; it never replaces newer values in the compute stage.

The web console shows derived measures with the other measures of a target, marked as derived (GET /v1/targets/{target}/catalogue, field derived).

Stellar Control · v0.1.0

↑↓ to moveEnter to open