Yamcs setup
Everything needed to point a stock Yamcs install at the running minscs
simulator. Yamcs connects as a TCP client to the EGSE ground stream on :21001 — it reads the
raw downlink PUS packets and writes uplink telecommands back over the same socket, exactly as
egse_tm/egse_tc do.
Files in yamcs/:
| File | Goes to | Purpose |
|---|---|---|
yamcs.yaml | <yamcs>/etc/ | Top-level server config; registers the minscs instance, HTTP UI on :8090. |
yamcs.minscs.yaml | <yamcs>/etc/ | Instance config; TM/TC data links onto the EGSE stream, loads the XTCE. |
MinscsCommandPostprocessor.java | Yamcs classpath | Fills the CCSDS packet-length field and appends the CRC-16-CCITT PEC on uplink; wraps a command in a TC[11,4] when an on-board execution time is set. |
MinscsPlugin.java | Yamcs classpath | Registers the On-board execution time command option. Optional — without it the postprocessor simply never wraps. |
The mission database is not in that folder — it’s a portable, standard artifact shared beyond
Yamcs and lives at mdb/minscs.xtce (see mdb.md). Copy it in
during setup (step 2).
For the port map and protocol details, see quickstart.md and pus.md.
Prerequisites
- A Yamcs server install (
<yamcs>) — see Getting Yamcs below. - The simulator running from the repo root:
./run_sim.sh(brings up the hub, EGSE, and the five nodes).
Getting Yamcs
Nothing here ships Yamcs; it’s a separate product with its own installation instructions —
yamcs.org, the Server Manual
and the quickstart project (the Maven route, which fetches
and runs the server in place). Either an unpacked server distribution or a quickstart project works
here. The one minscs-specific note: a fresh quickstart project ships a sample simulator.py
telemetry setup, which the configs in step 1 replace — delete it.
Where the files go in each route
The steps below name the layout of an unpacked server distribution. In a Maven quickstart project the paths differ, and putting a file in the wrong one is a silent no-op:
| Distribution | Maven project | |
|---|---|---|
| Configs | <yamcs>/etc/ | src/main/yamcs/etc/ |
| Mission database | <yamcs>/mdb/ | src/main/yamcs/mdb/ |
| Java sources | compiled into <yamcs>/lib/ext | src/main/java/minscs/ — compiled for you |
Setup
From the repo root:
cp yamcs/yamcs.yaml yamcs/yamcs.minscs.yaml <yamcs>/etc/
cp mdb/minscs.xtce <yamcs>/mdb/
javac -cp "<yamcs>/lib/*" -d <yamcs>/lib/ext yamcs/MinscsCommandPostprocessor.javaThe instance file loads mdb/minscs.xtce relative to the Yamcs working directory, so launching
Yamcs from this repo’s root resolves it without the copy.
The postprocessor is not optional: without the PEC trailer the on-board TTC fails the CRC
check and silently drops every telecommand, and Yamcs’s stock GenericCommandPostprocessor never
sets the CCSDS length field either, so it isn’t a substitute.
Optionally compile MinscsPlugin.java alongside it for the on-board execution time. It’s
discovered through META-INF/services/org.yamcs.Plugin; the yamcs-maven-plugin’s detect goal
generates that file, otherwise echo minscs.MinscsPlugin > META-INF/services/org.yamcs.Plugin. Note
mvn compile alone doesn’t run detect — its execution binds to a later phase, while
mvn yamcs:run and mvn package do. If the option never appears in the send dialog, that missing
service file is the first thing to check.
Run and verify
./run_sim.sh # from the repo root, if not already running
<yamcs>/bin/yamcsd # start Yamcs — distribution layout
./mvnw yamcs:run # ...or from a Maven quickstart projectOpen http://localhost:8090: both the tm and tc data links should read connected, and
telemetry should decode against the XTCE. Send TC_Request_HK or TC_Set_Mode from the UI and
confirm a service-1 verification report lands in command history; cross-check decoded values
(SoC, body rates) against build/bin/egse_tm watching the same stream.
On-board execution time
Set On-board execution time on any command and it isn’t executed now: it’s uplinked
immediately and added to the spacecraft’s time-tagged queue, to be carried out at the instant you
gave. The postprocessor wraps the finished packet in a TC[11,4] and sends it to the DHS,
which stores it and executes it later — with no ground station in contact. See
Time-based scheduling.
This isn’t Yamcs’s “Send later…”. That option is ground-side — Yamcs holds the command and transmits it when due, so it needs a live link at that moment, and a command scheduled past the end of a pass never reaches the spacecraft. This one transmits now and defers only the execution, on board.
egse_tc DHS TIME NOW # the queue needs a synchronised clock
egse_tc DHS SCHED ENABLE # and an armed scheduleThen in the send dialog, fill On-board execution time and send. What follows:
- Immediately —
TM[1,1]/TM[1,7]acknowledging that the activity was queued, and anEVT_SCHED_INSERTevent. Note these verify the insertion, not the command itself. - At the execution instant —
TM[1,3](started),EVT_SCHED_RELEASE, thenTM[1,7]when the target subsystem acknowledges the command it was actually asked to run.
Command history gains MinscsOnboardRequestId, the PUS request identifier the DHS will quote
in all of those reports and the id TC_Sched_Delete takes. It’s published because Yamcs can’t
correlate the reports itself — see the limitation below.
Refusals. The postprocessor fails the command in command history, without transmitting, when:
| Case | Why |
|---|---|
service 11 (any TC_Sched_*) | the DHS rejects a schedule inside a schedule |
service 8 subtype 2 (TC_OBC/ACS/TTC_Switch) | high-priority commands exist to reach their subsystem with the DHS out of the loop; the schedule lives in the DHS |
| encoded command over 96 octets | larger than the on-board slot (SCHED_TC_BYTES) |
| execution time already past | the DHS would reject it SCHED_PAST |
Leave the field empty and nothing changes — the command is transmitted immediately and the byte stream is identical to before the plugin existed.
Limitation: verifiers do not follow a wrapped command
A container verifier passes when a matching packet arrives inside its check window. That’s a
match on shape and timing, not on identity: nothing in XTCE lets a verifier compare a field of the
incoming report against the request id of the command instance that’s waiting, so the verifier
takes the next service-1 report of the right subtype and can’t tell one command’s from another’s.
With one command outstanding at a time this is invisible; it’s what the decomposed request id in
the reports (ver_req_pid / ver_req_seqflags / ver_req_seqcount) exists for — an operator, or a
ground system with its own correlation logic, reads which command a report is actually about.
A wrapped command is where the gap shows. Two request ids are in play: the outer TC[11,4] insert
and the inner activity. Acceptance (TM[1,1]/TM[1,7]) arrives immediately and belongs to the
outer insert, so the verifiers pass on it; the TM[1,3]/TM[1,7] that belong to the activity
arrive at the execution instant carrying the inner id, which Yamcs never saw as a command
instance and so attributes to nothing. Command-history status therefore reads “the DHS accepted the
insert”, not “the activity ran” — arguably the honest answer, since the insert is what was sent.
Send wrapped commands with verifiers disabled to avoid a spurious completion timeout. The real audit
trail is the telemetry stream, which decodes correctly throughout because
TM_Verif_Start_Success/_Failure are modelled; correlate on the MinscsOnboardRequestId the
postprocessor publishes into command history, the inner id the DHS quotes in every report about the
activity.
A note on the API
When driving the API directly rather than the UI, minscsOnboardExecutionTime takes a Yamcs
instant — milliseconds since 1970 TAI, i.e. Unix millis + 37000 today. The web UI produces
this correctly from a picked date; a hand-written Unix-millis value lands 37 s early.
dryRun: true returns the XTCE encoding only — the postprocessor doesn’t run, so there’s no filled
length field, no CRC and no wrapper. Use it to check argument encoding, never the wrapper.
Execution happens on on-board time, which drifts here
A queued activity runs when the spacecraft’s clock reaches the instant, not when your workstation’s does, and the two aren’t the same thing in this simulator: the FreeRTOS Posix port is explicitly not real-time, so OBT runs slow against wall clock — observed at roughly two and a half minutes behind after ten minutes of running. An activity scheduled for “wall clock + 30 s” can therefore sit in the queue for minutes before firing, which looks like a stuck scheduler and isn’t one.
egse_tc DHS SCHED REPORT shows what’s queued and when it’s due in on-board terms; re-sending
egse_tc DHS TIME NOW re-disciplines the clock and closes the gap.