EGSE — the ground segment
The ground side of the link: the relay that fans the packet stream out, and the two reference operator tools that decode telemetry and build telecommands.
Source: egse/egse.c (the relay), tools/egse_tm.c (TM
viewer), tools/egse_tc.c (TC sender), tools/egse_link.h
(shared connect helper). None of these is flight software — plain POSIX C, no FreeRTOS, no
can_drv, ordinary blocking and select() calls, and none of the port hazards.
1. The EGSE is a dumb relay
It does not decode telemetry. It receives the PUS stream from TTC on TCP :21000 and fans
whole packets out, byte-for-byte, to every tool connected on the ground-stream port :21001. Bytes
a tool sends back are forwarded up to TTC unchanged.
All the intelligence — decoding TM, building TC — lives in the client tools, so many tools (a
decoder, an archiver, nc, a full mission control system) can share one live stream. The relay is
a single select() loop over the TTC socket and the client sockets; pusFrame is used for packet
boundaries only.
The spacecraft dials out to :21000 — the EGSE listens, TTC connects. That’s a
Posix-port
decision: a flight node must never block in accept().
2. Retrieving the packet stream
Any tool can connect to :21001:
nc 127.0.0.1 21001 > telemetry.pus # archive the raw packet stream
build/bin/egse_tm # decode the TM stream
build/bin/egse_tc DHS CAPTURE # send a commandFor a real mission control system, the same stream is what Yamcs attaches to, decoding
against the XTCE mission database rather than against egse_tm’s built-in knowledge.
3. egse_tm — the TM viewer
Decodes the TM stream against its own telemetry database. Each line shows the APID (source application), the PUS service/subtype, and the packet sequence count:
TM 2026-08-09T18:19:10.172Z ADCS HK (3,25) #4 rates=[+2.83 -2.89 +1.88] deg/s | q_est=[+0.6122
+0.5709 -0.4670 +0.2849] | B=[+20.0 +14.0 -2.9] uT |B|=24.6 | wheels=[+0 +0 +0] rpm |
mode=2 ctrl=DETUMBLE lock=0 err=104.50deg | fdir=0x05 NO_ATT RATE_HI | mag ECLIPSE
TM 2026-08-09T18:19:10.172Z EPS HK (3,25) #4 SoC=81.1% bus=28.78V array=1.71A load=2.19A | rails
instr=1.59 radio=0.40 bus=0.22 A | batt=+23.40C rad=-11.70C | sunlit illum=54.73% beta=-5.60deg |
payload cam=ON gnss=ON mem=ON | OBC-A(A+ B-) ACS-A(A+ B-) TTC-A(A+ B-)
TM 2026-08-09T18:19:11.192Z PAYLOAD HK (3,25) #17 GNSS lat=+36.21 lon=-131.89 alt=512.0km
TM +6s (unsynced) PAYLOAD HK (3,25) #5 flash 8/256 sectors, 1 file(s)
TM 2026-08-09T18:19:00.502Z DHS VERIF(1,1) #18 TC accepted (req 0x1801 0xE158)The ADCS’s and the EPS’s lines are wrapped here and printed on one line each by the tool: the DHS
builds each subsystem’s whole picture as a single packet, so there’s one line per cycle rather than
the five and eight there would be if their CAN frames were relayed individually
(dhs.md §6).
The leading field is the packet’s on-board time: UTC once the clock has been set, or
+Ns (unsynced) while it’s still free-running from boot (see
Time in packets). The VERIF line is the DHS
acknowledging a telecommand.
Parameter reports and events are rendered with mnemonics rather than raw identifiers:
datapool.h and events.h are FreeRTOS-free and
shared between the flight code and the tools — one catalog each, both ends.
4. egse_tc — the TC sender
Turns one typed command into a PUS TC packet and sends it. One command per invocation; run with no arguments for the list:
egse_tc <node> PING ping a subsystem
egse_tc <node> HK request housekeeping
egse_tc <node> MODE <SAFE|NOMINAL|SCIENCE>
egse_tc DHS TIME <NOW|NOW+secs|YYYY-MM-DDThh:mm:ss|secs> set the on-board clock (UTC)
egse_tc DHS CAPTURE trigger a payload image capture
egse_tc DHS FLASH request mass memory status
egse_tc DHS EVENTS replay the on-board event log
egse_tc DHS PARAMS report the whole on-board parameter pool
egse_tc DHS CONFIG [<node>|ALL] re-distribute the on-board database
egse_tc DHS PARAM GET <pid>... report parameter values [20,1]
egse_tc DHS PARAM SET <pid> <type> <value> [...] set values [20,3]
egse_tc DHS PARAM DEFINE <pid|AUTO> <type> <op> [<src>...] <name> [20,133]
egse_tc DHS PARAM DELETE <pid>... delete dynamic parameters [20,134]
pid a mnemonic (THERM_LIMIT_HI) or a number (0x0115)
type U8 I8 U16 I16 U32 I32 F32 ENUM8
op NONE (free-standing) | COPY (1 source) |
ADD SUB MIN MAX AVG DELTA PACK16 (2 sources)
egse_tc DHS SCHED ENABLE|DISABLE arm / hold the command schedule [11,1 / 11,2]
egse_tc DHS SCHED RESET discard every queued activity [11,3]
egse_tc DHS SCHED REPORT report the queued activities [11,16]
egse_tc DHS SCHED INSERT <time> <node> <command> [args] lodge one [11,4]
egse_tc DHS SCHED DELETE <request_id> remove one by request id [11,5]
time NOW+<secs> | YYYY-MM-DDThh:mm:ss | ccsds_seconds
command any verb above except PARAM, SCHED and FILE
note the schedule needs a synchronised clock: send DHS TIME first
egse_tc EPS OBC <A|B> switch the active OBC (high priority)
egse_tc EPS ACS <A|B> switch the active ADCS CPU board (high priority)
egse_tc EPS TTC <A|B> switch the active TTC CPU board (high priority)
egse_tc EPS PLPWR <CAM|GNSS|MEM> <ON|OFF> switch one payload instrument channel
note a mode change re-latches all three to that mode's defaults
egse_tc DHS FILE LIST <PAYLOAD|SSMM> summary-report a repository [23,12]
egse_tc DHS FILE INFO <PAYLOAD|SSMM> <name> report a file's attributes [23,3]
egse_tc DHS FILE CREATE SSMM <name> <bytes> reserve mass memory [23,1]
egse_tc DHS FILE DELETE SSMM <name> free a stored file [23,2]
egse_tc DHS FILE COPY <from> <name> <to> copy a file [23,14]
name a fixed 12-character file name, e.g. PLD00001
routes PAYLOAD SSMM pull the file over the SpaceWire link
SSMM GROUND downlink it as service-13 parts
note a PAYLOAD->SSMM copy needs the link in Run; DHS HK reports it
egse_tc PAYLOAD DOWNLINK <file_id> stream a stored file over the CAN bus
egse_tc <node> RAW <hex...> raw command bytes
nodes: DHS TTC ADCS EPS PAYLOAD ALLEach command becomes a PUS TC space packet, forwarded verbatim by the EGSE to TTC, which frames it and hands it to the DHS; the DHS routes the command to the addressed subsystem (the TC’s APID) and answers with a service 1 verification report.
FILE is a sub-dispatcher of its own, like PARAM and SCHED, because a file name alone is
twelve octets and the shared encode buffer is sixteen. See spacewire.md for the
two copy routes and why only those two exist.
SCHED INSERT is the one verb that builds two packets: it encodes the command typed after the
node as a complete telecommand of its own, then carries those octets as the body of the TC[11,4].
It prints back the embedded command’s request id, which SCHED DELETE takes and which every
1,x report about the activity quotes. egse_tc is one process per command, so it seeds its
sequence count from the wall clock rather than from zero — otherwise every activity ever lodged
would carry the same identifier.
Routing follows the APID. A command addressed to a specific subsystem (egse_tc ADCS MODE SAFE)
is routed straight there; a platform command addressed to the DHS (egse_tc DHS MODE SCIENCE) is
fanned out to the whole bus. The three EPS OBC|ACS|TTC switches are the exception — they’re
high-priority and TTC hands them to the EPS without the DHS
in the loop. EPS PLPWR deliberately is not: redundancy has to be commandable when the DHS is
the thing that has failed, and an imager isn’t, so instrument power takes the ordinary route and is
verified end to end.
5. A first session
./run_sim.sh # terminal 1
build/bin/egse_tm # terminal 2
build/bin/egse_tc DHS TIME NOW # terminal 3 — discipline the clock first
build/bin/egse_tc DHS MODE SCIENCE # SCIENCE is the mode whose default mask powers the camera
build/bin/egse_tc DHS CAPTURE # capture, store, pull down as service-13 packetsMODE NOMINAL isn’t enough: the camera channel is clear in both pl_mask_safe and
pl_mask_nominal — only pl_mask_science sets all three — so a capture in NOMINAL is refused for
want of power. egse_tc EPS PLPWR CAM ON is the other way to get there, and it survives until the
next mode change re-latches the defaults (payload.md).
Setting the clock first matters beyond timestamps: the ADCS gates fine attitude determination on
obtIsSynced(), so TRIAD stays disabled until the ground sets time (adcs.md).
quickstart.md shows what the nodes print while this runs.