Skip to content

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 command

For 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 ALL

Each 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 packets

MODE 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.

Last updated on