Skip to content

The XTCE mission database

mdb/minscs.xtce is the minscs packet catalog in a portable, standard format, so a real mission control system can decode telemetry and build commands without reimplementing tools/egse_tm.c and tools/egse_tc.c. See yamcs.md for pointing a Yamcs install at it and pus.md for the wire format it describes.

The standard: XTCE

XTCEXML Telemetric and Command Exchange, CCSDS 660.0-B — is the standard for exchanging telemetry and telecommand database definitions between ground systems, native to Yamcs and understood by most commercial MCS. It fits minscs directly: the ground link already speaks CCSDS Space Packets with PUS-C secondary headers (pus.md), and XTCE was designed for exactly that — a header container all packets inherit, plus parameter extraction by bit offset with calibration and enumerations.

minscs.xtce targets XTCE 1.2 (http://www.omg.org/spec/XTCE/20180204).

Which simulator this database describes

Because the file is synchronised by hand, nothing stops an MDB from one release being pointed at a spacecraft running another — and the failure is quiet: a container whose Packet_Length no longer matches simply stops matching, and the packet lands in the ground system undecoded rather than wrong. So the database states which build it was written for, and the spacecraft states which build it’s running.

In the file: the <Header version="…"> attribute names the minscs release, the number in include/version.h. (The XTCE standard version is not stated there — the 20180204 namespace on the SpaceSystem element already fixes it.)

On the wire: TM_DHS_Platform_HK ends with dhs_sw_major / dhs_sw_minor / dhs_sw_patch, three octets the DHS fills from include/version.h — three plain numbers rather than one packed word, precisely so the comparison is done by eye. The same number is also a data-pool parameter, SYS_SW_VERSION (0x010A), readable on demand instead of waiting for a housekeeping cycle; there it’s packed, major<<16 | minor<<8 | patch, so 0.0.1 reads as 1.

build/bin/egse_tm | grep 'DHS .* HK'        # ... | ssmm files=0 | sw=0.0.1
grep '<Header version' mdb/minscs.xtce      # <Header version="0.0.1" ...
build/bin/egse_tc DHS PARAM GET SYS_SW_VERSION

It’s read-only, and a modelled OBC switch doesn’t change it: the reboot models a different computer running the same build. Every binary also prints the version in its startup banner, so a mismatched build tree shows up before anything is commanded.

When they disagree, trust neither decode. Bump <Header version> in the same change that bumps include/version.h.

What’s inside

Shared framing — one abstract CCSDS_TM_Packet container carries the 6-octet CCSDS primary header and the 13-octet PUS-C TM secondary header (service, subtype, message counter, destination, CUC time). Every telemetry packet inherits it and restricts on APID + Service + Subtype. The on-board time is an AbsoluteTimeParameterType declaring the UNIX epoch with offset="-378691200" — deliberately not the 1958-01-01 a CCSDS CUC field suggests, which is a silent 37-second error rather than a shortcut. See The CUC epoch is UTC, not TAI before touching those types.

Telemetry (ContainerSet) — one container per distinct layout in prvDecodeTm:

ContainerAPIDsvc/sub
TM_DHS_Platform_HKDHS (1)3/25
TM_ADCS_HKADCS (3)3/25
TM_EPS_HKEPS (4)3/25
TM_Payload_GNSS_HK / TM_Payload_Flash_StatusPAYLOAD (5)3/25
TM_TTC_Link_HKTTC (2)3/25
TM_Event_Reportany5/1..4
TM_Verif_Accept_Success / _Accept_Failureany1/1, 1/2
TM_Verif_Start_Success / _Start_Failureany1/3, 1/4
TM_Verif_Complete_Success / _Complete_Failureany1/7, 1/8
TM_Large_Data_Firstany13/1
TM_Sched_Detail_ReportDHS (1)11/17
TM_Param_Value_ReportDHS (1)20/2
TM_File_Attr_ReportDHS (1)23/4
TM_Repo_SummaryDHS (1)23/13

Physical values are scaled integers on the wire (×100, or ×10 for altitude); the XTCE calibrators recover SI units (%, V, A, deg/s, deg, km, degC). Enumerations cover platform mode, OBC id, on/off, event severity, parameter value type and derivation operator, and the time-reference status nibble.

TM_Param_Value_Report’s length isn’t fixed: a chunk-control octet, a record count, and that many eight-octet records. Fixed-width records are what make it modellable — XTCE 1.2 repeats an entry a dynamically counted number of times (<RepeatEntry><Count><DynamicValue parameterRef="pv_count"/>) but can’t repeat one whose members vary in width. pv_value stays raw 32 bits because its interpretation follows the pv_type octet beside it, which no calibration can express.

Telecommands (MetaCommandSet) — an abstract PUS_TC_Base builds the CCSDS primary + PUS-C TC secondary header; each concrete command assigns its service/subtype and appends the app-data opcode and arguments. All twenty-nine:

svc/subMetaCommands
3/27, 9/128, 17/1TC_Request_HK, TC_Set_Time, TC_PING
8/1 (routed to CAN)TC_Set_Mode, TC_Capture, TC_Flash_Status, TC_Dump_Events, TC_Downlink_File, TC_Payload_Power (opcode CMD_PL_PWR), TC_Param_Dump (CMD_PARAM_DUMP), TC_Config_Push (CMD_CFG_PUSH)
8/2 (high-priority, to EPS)TC_OBC_Switch, TC_ACS_Switch, TC_TTC_Switch
11/1..5, 11/16TC_Sched_Enable, _Disable, _Reset, _Insert, _Delete, _Detail_Req
20/1, 20/3, 20/133, 20/134TC_Param_Report_Req, TC_Param_Set, TC_Param_Define, TC_Param_Delete
23/1, 23/2, 23/3, 23/12, 23/14TC_File_Create, _Delete, _Attr, _List, _Copy

The on-board engine accepts up to thirty parameters per request; the MetaCommands model the single-parameter form, what an operator sends by hand, with the leading count pinned by a FixedValueEntry. egse_tc builds the multi-parameter form directly. Subtypes 133/134 are mission-specific because ECSS-E-ST-70-41C standardises only 20,1 / 20,2 / 20,3 and reserves subtypes 1..127 — the same convention the mission already uses for the set-clock command at 9,128.

Discrimination notes

  • APID carries provenance — several subsystems share service 3/25, so each container’s restriction criteria include APID, not just service/subtype.

  • Same-APID collisions use Packet_Length. PAYLOAD (GNSS / flash) is the only case left: the two layouts share APID+service+subtype and are disambiguated by the CCSDS packet-length field, which mirrors the app_len guards in prvDecodeTm (22/20); the 0x12 tag byte is still modelled as the first parameter of the tagged layout, so it doesn’t rest on length alone.

    This is a constraint on the flight software, not just on this file. The ADCS and EPS used to relay one container per CAN frame, told apart by a ladder of lengths, which made every report’s octet count a ground-visible interface. The DHS now builds one report per subsystem from its parameter pool, so TM_ADCS_HK (47) and TM_EPS_HK (44) are one container each at a length nothing else reaches. A layout identifiable only by length is one wire change from being wrong; where that’s unavoidable, compare the tag as well.

  • TM_Param_Value_Report restricts on APID + service + subtype alone, deliberately: its length varies with the record count, so there’s no Packet_Length to compare against — and no other layout shares 1 / 20 / 2.

  • Bit order in the configuration-readiness flags. TM_DHS_Platform_HK carries a one-octet mask whose bit 0 is TTC and bit 3 the payload; a container reads MSB first, so the EntryList runs the other way — four unused high bits, then payload, EPS, ADCS, TTC, as four one-bit booleans so a ground system renders four indicators. The command-schedule block follows the mask.

What is deliberately not modelled

  • The on-board database distribution (CMD_CFG_BEGIN / _ITEM / _END) never crosses the ground link, so it gets no containers: a mission database describes the space-to-ground wire, not the internal bus. What the ground sees is the effect — the epoch and readiness flags in TM_DHS_Platform_HK, the CFG_* events, and the parameters through service 20.
  • The on-board execution time option is a Yamcs-level concept registered from Java, not a mission-database construct, which is what keeps this file standard XTCE (yamcs.md). A wrapped command is a TC[11,4] on the wire, so TC_Sched_Insert describes it, not the MetaCommand the operator picked.
  • The telecommand embedded in a TC_Sched_Insert is opaque octets (ArgTcBlock) of length tc_length, because XTCE can’t say “any other member of this MetaCommandSet appears here”; a ground system encodes the inner command itself, as egse_tc’s prvDispatchSched does. Likewise TM_Sched_Detail_Report reports each activity’s identity (APID, service, subtype, length) but not its bytes, since the flight software doesn’t echo them.
  • No ADCS or EPS report tag. ADCS_RPT_* and EPS_RPT_* still sort those subsystems’ CAN frames, but none reaches the ground now that the DHS builds their reports rather than relaying them. Only a relayed layout’s tag belongs in a mission database, which here leaves FlashTag alone.
  • Big-endian — every multi-byte field is MSB-first, matching pusPutPrimary and the (d[0]<<8)|d[1] decoders.

Uplink caveats (Yamcs fills these)

Three fields in PUS_TC_Base_Container are zero placeholders because they depend on the final encoded packet, and the ground system computes them at build time: the 14-bit sequence count (per-APID rolling counter), the 16-bit packet length, and the PEC trailer — a 2-octet CRC-16-CCITT (poly 0x1021, seed 0xFFFF) that isn’t in the command container and must be added by a Yamcs command post-processor or in the link. The same CRC is on every downlink packet, modelled as the trailing PEC_CRC parameter, and Yamcs can verify it on receipt.

Ack flags are fixed to 0x9 (ACCEPT | COMPLETE), matching prvSendTc; the TC source id is the ground APID 0x00A.

The CUC epoch is UTC, not TAI

OBT and ArgOBT declare the UNIX epoch with an offset="-378691200", not the 1958-01-01 a CCSDS CUC field suggests. A 1958 epoch means TAI to Yamcs, and this clock isn’t TAI: cucFromUnix() is plain Unix seconds plus 378691200, and obt.c knows nothing of leap seconds. With the 1958 declaration a TC_Set_Time for 18:00:00Z encoded as on-board 18:00:37Z, and every scheduled activity was misplaced by the same amount, silently, growing by a second at each leap.

Two details that are easy to get wrong when touching this:

  • It must be the named token UNIX, not the literal date 1970-01-01T00:00:00. Yamcs’s own instant is already “millis since 1970 TAI”, so a literal 1970 epoch is used raw and reproduces the identical 37-second error; only the token selects the UTC conversion. Both were tried.
  • The offset attribute on <Encoding> is honoured, and is what carries the 378691200 s from the Unix epoch to the CCSDS one.

Check it after any change to those types, with the simulator not even running:

curl -s -X POST localhost:8090/api/processors/minscs/realtime/commands/minscs/TC_Set_Time \
  -H 'Content-Type: application/json' \
  -d '{"dryRun":true,"args":{"obt":"2026-08-03T18:00:00.000Z"}}'

Base64-decode binary; the six app-data octets must be 810335A00000 (coarse 2164471200). If they read 810335C50000 the TAI regression is back.

Command verification (command-history status)

Each DHS-handled MetaCommand carries a <VerifierSet> so Yamcs drives command-history status from the DHS’s service-1 reports: an AcceptedVerifier on TM_Verif_Accept_Success (1,1) and, where a completion follows, a CompleteVerifier on TM_Verif_Complete_Success (1,7). These are container verifiers — a stage passes when a matching report arrives inside its check window (acceptance 3 s, completion 8 s, the latter chosen to outlast the DHS verif_timeout_ms so a genuine 1,8 timeout also fails the stage). A rejected or timed-out command produces no success report, so the stage’s window expires and the command is marked failed; the _Failure containers (1,2 / 1,8) still decode as telemetry with their ver_failure_code reason.

Three deliberate gaps. There’s no StartedVerifier anywhere — 1,3/1,4 are emitted only for telecommands the schedule releases, which aren’t the MetaCommand instance Yamcs is tracking; the two TM_Verif_Start_* containers exist so those reports decode, not so a stage waits on them. TC_OBC_Switch, TC_ACS_Switch and TC_TTC_Switch (8/2) bypass the DHS and generate no report, so they carry no verifiers. TC_Downlink_File gets acceptance only, since the payload’s file-stored ack is consumed by the DHS downlink path rather than producing a 1,7.

Why a container verifier cannot correlate a report to a specific command instance — and what the decomposed request id (ver_req_pid / ver_req_seqflags / ver_req_seqcount) is there for — is in yamcs.md.

The mission data link

Service 23 and the SpaceWire link are modelled like anything else: TM_File_Attr_Report (23,4) and TM_Repo_Summary (23,13), the five TC_File_* metacommands, and SpwLinkState / XferState / FileRepo / FileName in the type sets. Two things to know when editing:

  • The TC_File_* metacommands carry no FixedValueEntry opcode octet. Unlike the service-8 telecommands, service 23 executes wholly on the DHS and never becomes a CAN command, so the body is the arguments and nothing else. Services 20 and 11 are built the same way.
  • TC_File_Copy’s completion window is 30 s, not the usual 8. A PAYLOAD -> SSMM copy completes only on the payload’s CAN ack, which can be queued behind a readout already in progress.

TM_DHS_Platform_HK grew from Packet_Length 65 to 77 when the link’s six parameters joined the DHS’s own housekeeping, and to 80 with the three software-version octets — why every container in the file states its length in a comment beside it.

Validate and load

# Well-formedness + schema validation against the XTCE 1.2 XSD
curl -fsSLO https://raw.githubusercontent.com/yamcs/yamcs/master/yamcs-xtce/SpaceSystemV1.2.xsd
xmllint --noout --schema SpaceSystemV1.2.xsd mdb/minscs.xtce   # -> "mdb/minscs.xtce validates"

The XTCE loader stanza, the TM/TC data links and the end-to-end check are in yamcs.md.

Keeping it in sync

This file is hand-authored and must track the C sources. When a packet changes, update:

ChangeSource of truthXTCE section
APID / service / subtype constantinclude/pus.hcontainer RestrictionCriteria
Command opcodeinclude/shared_can.h (CMD_*)FixedValueEntry in the TC container
TM byte layouttools/egse_tm.c (prvDecodeTm)TM EntryList + ParameterSet
TC byte layouttools/egse_tc.c (prvEncodeVerb, prvDispatchSched, prvDispatchFile)TC CommandContainer EntryList
Verification failure codeinclude/pus.h (PUS_VERIF_ERR_*)VerifFailCode enumeration
Parameter value type / derivation operatorinclude/datapool.h (PUS_PARAM_TYPE_*, DP_OP_*)ParamType / ParamOp enumerations
A distributable subsystem parameterinclude/datapool.h (DP_CFG_CATALOG)nothing — see below
The simulator releaseinclude/version.h<Header version="…">

Deliberately not here: the on-board parameter catalog (DP_PID_CATALOG). Its identifiers are data carried inside TM_Param_Value_Report, not packet layouts, and the pool is editable in flight — a ground-defined parameter has no XTCE definition at all. A mission control system keeps its own list, or reads one back with TC_Param_Dump.

Last updated on