Stellar ControlMission control · by Stellar Systems v0.1.0

Topology

Drivers, Transports and Gateways

The three layers of a link and what each one does.

A link stacks up to three components between the MCS and the target. Each is a process of its own, outside the core, written with the Rust or Python SDK or in any language that speaks the NATS contract. They all register with the reconciler, send heartbeats, and receive their bindings and credentials; the topology decides which ones serve which target.

flowchart LR
    MCS["MCS<br/>semantic telecommand"] -->|stellar.tc.encode| D[Driver]
    D -->|"unit (packet)"| T[Transport]
    T -->|frames| G[Gateway]
    G --> M[(Medium:<br/>RF, CAN, serial…)]
    M --> G
    G -->|stellar.tm.raw| T
    T -->|stellar.tm.unit| D
    D -->|"stellar.tm.decoded<br/>raw values"| C["MCS<br/>compute stage"]
LayerRoleDefined byExample: obc.ping to obc-flatsat-1 over the RF bench
DriverTelecommand ↔ application unit (PUS packet, CSP packet); bytes → raw valuesThe ICD of the targetobc-csp builds a whole CSP 1 packet: destination and port fixed by the telecommand, source address read from the link (csp_address)
TransportApplication unit ↔ frames of the medium, both ways; optionalThe linkcsp1-kiss appends the CRC32 and wraps the packet in a KISS frame (FEND, data command, escaped bytes)
GatewayFrames ↔ mediumThe gatewaysatlink-gateway sends the frame through the radio of the bench

Without a transport, the driver produces the frames of the medium itself, like tcu-can or lab-psu. With one, the same driver serves different media: only the transport of the link changes, and the driver knows nothing of it.

Driver#

The driver is a black box that translates between the semantic catalogue and binary data the MCS never sees decoded:

  • it encodes each telecommand and gives ACK 1 (ENCODED or ENCODE_FAILED);
  • it decodes the frames (or units) received into raw values, published on stellar.tm.decoded.<target>; the MCS applies the calibration;
  • it checks echoes: it compares each frame received with the telecommands it encoded in the last 60 s and publishes an ECHO event, conforming or not;
  • for file transfers, it extracts the chunks of on-board files, runs the CFDP entity of a platform that uses CFDP, and decodes verified files (LTTM) into deferred samples.

A driver registers with:

FieldMeaning
codecName of its encoding, part of the encode subject
catalogueThe catalogue it implements and the versions it accepts: platform-v3@^1.4 (1.4.0 and later 1.x), platform-v3@1.4 (1.4.x), platform-v3@1.4.0 (that one)
telecommands, measuresWhat it covers, as <component>.<name>
outputOptional: what its telecommands become (space-packet, csp1, csp2, can…)
params_schemaOptional: JSON Schema of the link parameters it reads

A driver knows a catalogue by its versions, never by a hash: a released catalogue version never changes in place.

Transport#

A transport sits between a driver and a gateway when the link declares one (transport: ccsds-tc@1). It frames the units of the driver for the medium of the gateway, and turns the frames received back into units:

  • up, it reads units on stellar.tc.wrap.<transport>.<target> and publishes frames on the uplink subject of the gateway; a unit split into several frames carries Stellar-Fragment: <i>/<n> on each, and the gateway gives ACK 2 after the last one;
  • down, it reads the raw telemetry of the gateway and publishes each unit a frame completes on stellar.tm.unit.<transport>.<target>, which the driver decodes. Raw frames stay archived as received;
  • it keeps a state per link (reassembly across frames); a state that must survive a restart, such as COP-1, lives in JetStream.

A unit the transport cannot frame, or received after its deadline, fails its telecommand with SEND_FAILED and the reason (the driver already gave ACK 1).

A transport registers with the kind transport, its software and version, its input (what it frames: the output of the drivers), its output (what it produces: the uplink link type of the gateways) and an optional params_schema.

Gateway#

The gateway carries frames to and from the medium: a ground station, an RF bench, a CAN bus, a serial line, an EGSE.

  • Uplink: it sends the frames of its uplink subject and gives ACK 2 (SENT or SEND_FAILED); a frame received after its deadline is refused.
  • Downlink: it publishes every frame received, as is, on stellar.tm.raw.<gateway>.<target>, stamped with the ground reception time (Stellar-Ground-Time), and marks data replayed from on-board storage as deferred. It publishes the segments of continuous streams.
  • Link availability: its heartbeat says whether the link is available (satellite in view, station in service). A gateway without that notion, on a bench, always reports it available.
  • Throughput: every metrics_period (5 s by default), a report per active target on stellar.metrics.<gateway>.<target>, also exposed to Prometheus.

A gateway registers with its tags (sband, a region, a station name, bench) and, for each direction, uplink and downlink: {link_type, rate_bps}, the physical link type (rf, can, rs422, ethernet, serial, sim…) and its theoretical rate. See the gateway contract.

Chain compatibility#

The reconciler binds a link only when its chain holds:

LinkCondition
Without transportoutput of the driver = uplink link_type of the gateway
With a transportoutput of the driver = input of the transport, and output of the transport = uplink link_type of the gateway
ParametersIn every environment, the parameters satisfy the schemas of the driver and the transport

What a component does not declare is not checked: a driver without output fits any gateway. A driver producing space-packet behind a CAN gateway gives the reason … the link needs a transport.

The example components of the repository:

ComponentKindDeclares
platform-v3-ccsdsdriverplatform-v3@^1.4, output space-packet
obc-cspdriverobc-v1@^1, output csp1 (or csp2), schema csp_address
tcu-candriverplatform-v3@^1.4, covers tcu.* only, no output
scpi-psudriverlab-psu@^1.0
satlink-benchdriversatlink-bench@^1.0
ccsds-tctransportinput space-packet, output rf, COP-1
csp1-kiss, csp2-kisstransportsinput csp1 / csp2, output rf (or serial with --output serial), schema crc32
csp1-can, csp2-cantransportsinput csp1 / csp2, output can, schema crc32, via, can_address, reassembly_timeout_ms
bench-cangatewaylink type can
satlink-gatewaygatewaylink type rf, tags bench, rf
bench-gatewaygatewaylink type ethernet
stellar-simulatordriver and gatewaylink type sim, tag sim

Registration and health#

Every instance registers on stellar.ctl.register with its kind, instance name, software and version, heartbeat period and the public key of an nkey it generates at start. The reconciler refuses, with the reason, a kind or name that is not a token, a version that is not semantic, a heartbeat period outside 100 ms to 60 s, a missing or superfluous driver, transport or gateway section, a catalogue that is not name@requirement, or coverage entries that are not <component>.<name>. A new registration of an instance replaces the previous one (a restart).

The registration and the last heartbeat live in the stellar_instances bucket with a TTL of three heartbeat periods. A heartbeat is healthy or degraded with a reason; a degraded instance is not bound. After three periods without heartbeat, the instance is gone: the reconciler unbinds it and its targets are no longer ready.

Every instance answers control verbs on stellar.ctl.rpc.<kind>.<instance>.<verb>: status, bindings, credentials and reregister, plus its own (faults for a simulated gateway, cfdp for a driver running CFDP). GET /v1/instances and GET /v1/instances/{kind}/{instance} show them; see Writing a Driver for the full life of an instance.

Stellar Control · v0.1.0

↑↓ to moveEnter to open