The ground link — CCSDS / PUS
Inside the spacecraft everything is CAN. The ground link carries CCSDS Space Packets (6-octet primary header) with ECSS PUS-C secondary headers and a CRC-16-CCITT Packet Error Control trailer. Space packets are self-delimiting via the primary-header length field, so the link needs no sync word.
Source: include/pus.h — header-only and FreeRTOS-free, so the DHS, the framing
side of TTC, the plain-POSIX EGSE relay and the ground tools all share exactly one definition of
the wire format: pusTmEncode/pusDecode in the DHS, pusFrame for TTC and the relay (boundaries
only), pusTcEncode/pusDecode for the tools.
1. Who does what
The DHS is the CAN↔PUS boundary; TTC is the link layer that moves whole packets.
- Downlink — the DHS builds each PUS TM packet, stamps it with on-board time, stores it in the
mass memory unit and hands it to TTC over
CAN_FUNC_PKT. The producing subsystem’s CAN address becomes the APID (provenance), and the CAN function selects the service (HK/science → 3, ACK → verification 1, file readout → large-data 13); recorded occurrences go out as service-5 events. - Uplink — TTC frames each PUS TC off the TCP stream (
pusFrame, boundaries only, no decode) and forwards the whole packet to the DHS, which decodes it, routes a CAN command to the subsystem named by the TC’s APID (time-set is always the DHS, which owns the clock), and answers with service 1 request verification.
2. Services
| Downlink | PUS service, subtype |
|---|---|
| HK / science | 3, 25 (housekeeping report) |
| On-board occurrence | 5, 1..4 (event report, by severity) |
| TC accepted / rejected | 1, 1 / 1, 2 (acceptance report, +code) |
| TC started / failed to start | 1, 3 / 1, 4 (start-of-execution report, +code) |
| TC completed / failed | 1, 7 / 1, 8 (completion report, +code) |
| Reassembled payload file | 13, 1 / 13, 2 (large data) |
| Scheduled activities | 11, 17 (schedule detail report) |
| Parameter values | 20, 2 (parameter values report) |
| File attributes | 23, 4 (file attribute report) |
| Repository contents | 23, 13 (repository content summary report) |
| Uplink | PUS service, subtype |
|---|---|
| Ping | 17, 1 (connection test) |
| HK request | 3, 27 (one-shot report) |
| Mission command | 8, 1 (perform function) |
| High-priority command (bypasses the DHS) | 8, 2 (perform function) |
| Set clock | 9, 128 (time management) |
| Arm / hold the command schedule | 11, 1 / 11, 2 (time-based scheduling) |
| Reset the schedule | 11, 3 |
| Insert / delete an activity | 11, 4 / 11, 5 |
| Report the schedule | 11, 16 |
| Report / set parameters | 20, 1 / 20, 3 (parameter management) |
| Define / delete parameter | 20, 133 / 20, 134 (mission-specific) |
| Create / delete a file | 23, 1 / 23, 2 (file management) |
| Report file attributes | 23, 3 |
| Summary-report a repository | 23, 12 |
| Copy a file | 23, 14 — the mission data link’s trigger |
The full telemetry inventory is in Telemetry generated.
3. Request verification (service 1)
A telecommand the ground uplinks is verified in two stages: the DHS first judges acceptance — is the service/subtype/parameters something it can carry out? — answering TM[1,1] (accepted) or TM[1,2] (rejected); then, on acceptance, it carries the command out and answers completion with TM[1,7] (done) or TM[1,8] (failed).
A telecommand the on-board schedule releases is verified in three, with a start-of-execution report between them: TM[1,3] when the activity begins, TM[1,4] + reason when it cannot. That stage exists because a scheduled command splits acceptance from execution across arbitrary time, so the moment execution began is a fact the ground can learn no other way; for an immediate command the two instants coincide, so the direct-uplink path never emits them. See Time-based scheduling.
Every report opens with a 4-octet request identifier echoing the TC’s primary header, so the
ground pairs report to command; the failure reports append a 2-octet error code (see
pusVerifErrName in pus.h). For a scheduled activity the identifier is that of the embedded
telecommand, and it stays the same from the acceptance of the TC[11,4] that lodged it through to
the completion of the command it eventually runs.
The acceptance rules, the three completion paths and the pending-request table are in Telecommand handling.
4. The high-priority bypass
A telecommand marked high-priority (service 8 / subtype PUS_ST_FUNC_PERFORM_HP) is recognised by
TTC and handed straight to the addressed subsystem instead of being forwarded to the DHS.
That is what makes an OBC switch commandable with the DHS down, and it is why redundancy state is a
board strap rather than DHS-distributed database content. See Redundancy.
5. Time in packets — CCSDS CUC
The DHS is the clock master. On boot no absolute time exists, so every node’s clock free-runs
from zero on its own FreeRTOS tick and telemetry is flagged unsynchronized — the CUC “spacecraft
time reference status” nibble in each PUS TM header, surfaced on the ground as +Ns (unsynced).
The ground sets the clock with one telecommand:
TC DHS TIME NOW # the operator host's current UTC
TC DHS TIME 2026-07-16T12:00:00 # an explicit UTC instant
TC DHS TIME 2163017200 # raw CCSDS seconds, for scriptingThat TC travels as PUS service 9 / subtype 128 to the DHS, which latches its clock and then
broadcasts the time on the CAN bus (CMD_SET_TIME, re-sent every few seconds). ADCS, EPS,
PAYLOAD and TTC each discipline their own on-board-time module
(common/obt.c) to that broadcast, so all five nodes agree on one clock.
Time is carried everywhere in CCSDS CUC form — 32-bit coarse seconds from the CCSDS epoch
(1958-01-01) plus 16-bit fine sub-seconds — representing UTC, and that last word is
load-bearing. A 1958 epoch conventionally implies TAI, 37 s ahead of UTC today and gaining a
second at each leap; this clock is not TAI (cucFromUnix() is plain Unix seconds plus
CCSDS_EPOCH_UNIX_OFFSET, and obt.c just counts). The epoch bridge lives only at the ground edge,
and a ground system that assumes TAI sits 37 s away from the spacecraft — see
mdb.md for how the mission database declares it.
Timestamps are captured where the packet is built, not where the datum was sampled
(On-board time). Two consequences: TRIAD attitude determination is
gated on obtIsSynced(), so fine determination needs egse_tc DHS TIME NOW first
(adcs.md); and a node reboot resets its clock, so telemetry from a freshly switched
board reads (free) again.
6. Mass memory and events
Every generated packet passes through the DHS’s mass memory unit before downlink, and each on-board
occurrence is downlinked as a PUS service-5 report at its severity subtype (TC DHS EVENTS
replays the log). Both stores, the event catalogue and the overwrite policy are in
dhs.md §7.
TODO — no octet-level layout here yet: the primary header fields, the PUS-C TM/TC secondary header layouts and the PEC computation are only in
pus.hand in the XTCE (mdb.md).