A component exposes parameters: on-board settings such as the rate of a transmitter or the
anode voltage of a TCU. The topology declares the modes of operation (Nominal,
Survival…) and gives each target the value of its parameters in each mode. Every target has
a current mode; the procedure statement configure applies a mode on board, and the ground
chain (driver, transport, gateway) receives the parameters of the current mode, so that a
ground modem can follow the rate of the spacecraft.
The pieces live in four places:
| Where | What |
|---|---|
| Catalogue | parameters of a component: type, unit, range, and how each is set (set) and read back (readback, tolerance) |
| Topology | modes, and per target parameters (value per mode) and overrides (by environment) |
| Procedures | configure <role> for <mode>, and param <path> [in <Mode>] in expressions |
| Operations | The current mode of each target in the stellar_modes bucket; stellar mode, stellar parameters and the API |
flowchart LR
cat["Catalogue<br/>parameters: set, readback"] --> conf
topo["Topology<br/>modes, values per target"] --> conf
conf["configure sat for Survival<br/>(send setters, expect readbacks)"] --> kv[("stellar_modes<br/>current mode")]
op["Operator<br/>stellar mode / PUT"] --> kv
kv --> rec["Reconciler<br/>bindings: mode + parameters"]
rec --> chain["Driver, transport, gateway<br/>LinkContext.mode, .parameters"]
kv --> exe["Executor<br/>param in the current mode"]Parameters in the catalogue#
A component declares its parameters under parameters, like the arguments of a telecommand.
From the example catalogue (packages/platform-v3/catalogue.yaml):
components:
tcu:
instances: tcu_id
measures:
mode: {type: tcu_operating_mode}
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]}
# … other measures
telecommands:
# … ping, standby, safe_mode
set_mode:
args:
mode: {type: tcu_operating_mode}
verify: [echo, {mode: args.mode, within: 10s}]
set_anode_voltage:
args:
voltage: {type: f32, unit: V, range: [0 V, 300 V]}
ramp: {type: f32, unit: V/s, range: [1 V/s, 50 V/s], default: 10 V/s}
verify: [echo]
parameters:
operating_mode:
description: Operating mode of the TCU.
type: tcu_operating_mode
set: {telecommand: set_mode, arg: mode}
readback: mode
anode_voltage:
description: Anode voltage, read back within the noise of the measure.
type: f32
unit: V
range: [0 V, 300 V]
tolerance: 2 V
set: {telecommand: set_anode_voltage, arg: voltage}
readback: anode_voltage
anode_ramp:
description: Rate at which the anode voltage is reached.
type: f32
unit: V/s
range: [1 V/s, 50 V/s]
set: {telecommand: set_anode_voltage, arg: ramp}setnames the telecommand of the component that sets the parameter and the argument that carries its value. Several parameters may share one setter, each through its own argument (anode_voltageandanode_rampboth go throughset_anode_voltage).readbacknames the measure (or derived measure) of the component that reads it back;tolerancebounds the gap accepted on a numeric readback.- A multi-instance component has one parameter per instance:
tcu[TCU1].anode_voltage.
The full reference of the keys and their checks is in Telecommands and Verification.
Modes and values in the topology#
topology.yaml declares the modes, in their order, and exactly one default: the mode of a
target whose mode was never set.
modes:
Nominal: {description: TCUs powered and ready to fire, default: true}
Survival: {description: TCUs safe and anodes off}Each target gives the value of its parameters: one value for every mode, or a table by mode,
possibly partial. A key names the parameter as procedures do (tcu[TCU1].anode_voltage; the
component is left out on a platform of one component). On a multi-instance component, a key
without instance (tcu.anode_voltage) holds for every instance; the key of an instance
(tcu[TCU2].anode_voltage) wins for that instance.
targets:
sim-1:
platform: platform-v3@1.4.0
environments: [SIM, AIT]
parameters:
tcu.operating_mode: {Nominal: STANDBY, Survival: SAFE_MODE}
tcu.anode_voltage: {Nominal: 200 V, Survival: 0 V}
tcu[TCU2].anode_voltage: {Nominal: 180 V}
tcu.anode_ramp: 50 V/s
flatsat-1:
platform: platform-v3@1.4.0
environments: [AIT, IVV]
parameters:
tcu.operating_mode: {Nominal: STANDBY, Survival: SAFE_MODE}
tcu.anode_voltage: {Nominal: 200 V, Survival: 0 V}
tcu.anode_ramp: 20 V/s
# IVV qualifies the flatsat at a lower voltage.
overrides:
IVV:
parameters:
tcu.anode_voltage: {Nominal: 150 V}On sim-1, tcu[TCU2].anode_voltage is 180 V in Nominal and, since its own table has no
Survival, 0 V in Survival from the key without instance. On flatsat-1,
tcu.anode_voltage in Nominal is 150 V in IVV and 200 V in AIT. The topology rules are on
Targets.
Which value applies#
The value of a parameter of a target, in a mode and an environment, is looked up in this order:
- the key as written (with its instance): the override of the environment, then the value of the target;
- for a parameter of an instance without a value of its own: the key without instance, again override first, then the value of the target.
So an override of tcu.anode_voltage does not change an instance that has its own value in the
same mode, such as tcu[TCU2].anode_voltage on sim-1: override the key of the instance for
that.
The current mode#
The current mode of each target lives in the KV bucket stellar_modes, under the name of the
target: the mode, when it was set, who set it, and the run that set it. A target whose mode
was never set (or whose stored mode is no longer declared by the topology) is in the default
mode.
It changes in two ways:
- A run, at the end of a successful
configure: the eventmode_changedis logged, and the record names the run. - An operator, who declares the mode after something that happened outside the MCS, such as an autonomous switch of the spacecraft to survival. Declaring a mode sends nothing to the target.
stellar mode sim-1 # sim-1: Nominal (default)
# modes: Nominal, Survival
stellar mode sim-1 Survival # declare the mode, as the operator of --asThe web console shows it in the tab Parameters of a target, where an operator can declare another mode (see Web Console). The API offers the same (tag modes):
| Request | Result |
|---|---|
GET /v1/targets/{target}/mode | ModeView: mode, default, modes, since, by, run |
PUT /v1/targets/{target}/mode with {"mode": "Survival"} | Sets the mode, answers the new ModeView |
PUT needs the identity of the operator (X-Stellar-User or a token, else 400 api::anonymous), refuses a mode the topology does not declare (422 mode::unknown) and a
target held by a run (409 mode::target-held: its runs set its mode). An unknown target answers
404 api::unknown-target.
Parameters of a target#
stellar parameters <target> lists the parameters of a target: their value in the current
mode, their value in each mode, and their readback with its last sample.
$ stellar parameters sim-1
sim-1: mode Nominal, environment SIM
tcu[TCU1].operating_mode "STANDBY" [Nominal="STANDBY", Survival="SAFE_MODE"] read "STANDBY" ok
tcu[TCU1].anode_voltage 200.0 V [Nominal=200.0, Survival=0.0] read 199.4 ok
tcu[TCU1].anode_ramp 50.0 V/s [Nominal=50.0, Survival=50.0]
…The tab Parameters of a target in the web console shows the same table, read again every
5 s, the readbacks marked ✓ or ✗. --environment picks the environment of the overrides; by default, a target engaged in a single
environment is read in it, and a target engaged in several without overrides. The API is
GET /v1/targets/{target}/parameters?environment=…, answering a ParametersView: for each
parameter its name, unit, value in the current mode, values by mode, set (the setter
telecommand) and readback (measure, current sample, and matches: whether the sample has
the value of the mode within the tolerance; null without a sample or a value). Codes:
404 api::unknown-target, 422 api::unknown-environment (the target is not engaged in it),
503 api::no-catalogue.
Applying a mode: configure#
In a step, configure <role> for <mode> applies a mode to the target of the role. From
packages/platform-v3-steps/operations.proc:
procedure "Enter survival"
uses sat: platform-v3
step "Configure survival"
configure sat for Survival
procedure "Enter nominal"
uses sat: platform-v3
step "Configure nominal"
configure sat for Nominal
check sat.tcu[TCU1].anode_voltage is param sat.tcu[TCU1].anode_voltage +/- 2 VThe compiler expands configure from the catalogue of the platform of the role:
- for each telecommand setting parameters, in the order of the catalogue, and for each instance
of its component in the order of the enum of its instances, one
sendwhose parameter arguments takeparam … in <mode>(the other arguments keep their default); - right after each
send, for each of its parameters that has areadback, anexpectthat the readback measureisthe value of the mode (+/- tolerancewhen declared), within the longestwithinof the verifications of the setter, or 10 s when it has none; - once all have succeeded, the mode of the target becomes the mode (event
mode_changed).
For platform-v3, configure sat for Survival therefore sends, for TCU1 then TCU2 then TCU3,
set_mode with mode = SAFE_MODE and expects mode is SAFE_MODE within 10 s; then, for each
TCU, set_anode_voltage with voltage = 0 V, ramp = 50 V/s (on sim-1) and expects
anode_voltage is 0 V +/- 2 V within 10 s.
These sends follow the rules of every send: a hazardous setter needs its ask operator in the
same step, a step with a setter that changes the on-board state is not eligible for retries, and
the maximum duration counts every setter and every readback window (see
Safety Rules). If a step fails, the mode is not changed.
At the resolution of the run, every parameter a configure sets must have a value on the target
in that mode (run::parameter-undefined, 422).
Reading a parameter: param#
In any expression of a procedure, param <role>.<component>[<instance>].<parameter> is the
value of the parameter in the current mode of the target, read when the statement is
evaluated, in the environment of the run; param … in <Mode> is its value in that mode. It has
the type and unit of the parameter, and serves as an argument of a telecommand or in a
condition:
send set_anode_voltage to sat.tcu[tcu] with voltage = param sat.tcu[tcu].anode_voltage in Nominal
check sat.tcu[TCU1].anode_voltage is param sat.tcu[TCU1].anode_voltage +/- 2 VThe mode is checked at compile time (procedure::unknown-mode). A param … in <Mode> whose
instance is known is checked again at the resolution of the run (run::parameter-undefined);
otherwise a parameter without value for the target fails the statement at run time. Details are
in Expressions.
The ground chain#
The ground segment follows the mode of the target:
- Each binding the reconciler delivers (
stellar_bindings, verbbindings) carries the currentmodeof its target and itsparametersin that mode, plusparameter_environments: the same, overrides applied, for each environment that overrides some. The reconciler watchesstellar_modesand delivers the bindings again at each change of mode. - The context of a link (
LinkContext) gainsmodeandparameters, resolved for the environment likeparams. Drivers and transports receive it with each telecommand and each frame, as before. - A gateway receives it with each frame (
Gateway::send_within Rust, asendwith three arguments in Python) and each time its bindings change (Gateway::configure,@gateway.configure), for instance to set the rate of its modem. LinkContext::parameter("tcu[TCU1].anode_voltage")gives the value of an instance, falling back on the key without instance, as in procedures.
See Writing a Gateway and Writing a Driver.
Diagnostics#
| Code | Where | Meaning |
|---|---|---|
catalogue::parameter-type | Catalogue | A parameter is a number, bool or an enum value, not bytes nor a duration |
catalogue::unknown-setter, catalogue::unknown-setter-argument | Catalogue | set names no telecommand of the component, or no argument of it |
catalogue::setter-type-mismatch | Catalogue | The argument has another type or unit than the parameter |
catalogue::duplicate-setter | Catalogue | The argument already carries another parameter |
catalogue::setter-argument-without-default | Catalogue | A setter has an argument that carries no parameter and has no default |
catalogue::unknown-readback, catalogue::readback-type-mismatch | Catalogue | readback names no measure of the component, or one of another type or unit |
catalogue::tolerance-on-non-numeric, catalogue::invalid-tolerance | Catalogue | A tolerance on a non-numeric parameter, or negative or in the wrong unit |
topology::default-mode | Topology | No mode, or several, marked default: true |
topology::unknown-parameter | Topology | The key names no parameter of the catalogue of the target |
topology::unknown-mode | Topology | A value is given for an undeclared mode |
topology::invalid-parameter-value | Topology | A value does not fit the parameter: type, unit, range, enum value |
topology::unknown-override-environment | Topology | overrides names an environment the target is not engaged in |
procedure::unknown-mode | Procedure | configure … for or param … in names an undeclared mode |
procedure::unknown-parameter | Procedure | param names no parameter |
procedure::nothing-to-configure | Procedure | The platform of the role has no parameter with a set |
expr::parameter-not-here | Catalogue | param in a catalogue expression: parameters are read in procedures only |
run::parameter-undefined | Run request | The target has no value of a parameter in the mode used |
mode::unknown, mode::target-held | API | See The current mode |