Skip to content

Quickstart

How to build the platform and bring it up. See index.md for the architecture and egse.md for the operator tools.


1. Build

$ cmake -B build && cmake --build build -j     # first run fetches FreeRTOS-Kernel V11.3.0
$ cmake -B build -DFREERTOS_KERNEL_PATH=/path/to/FreeRTOS-Kernel   # ...or use a local checkout
$ ctest --test-dir build --output-on-failure -LE integration       # the unit suite, ~2 s

Individual binaries land in build/bin/ — see testing.md for how to run the test suite.

Version

One version covers all seven processes, written down once in include/version.h and parsed back out of it by CMake, so bumping the header is the whole release procedure. Every binary prints it at startup (-- minscs: version 0.0.1) and the spacecraft downlinks it — which is what makes the mission database checkable against a running simulator rather than assumed to match. See Which simulator this database describes.


2. Run

$ ./run_sim.sh                                 # run hub + egse + all 5 nodes; Ctrl+C stops
$ build/bin/egse_tm                            # watch decoded telemetry
$ build/bin/egse_tc DHS PING                   # send telecommands

run_sim.sh exports MINSCS_CONFIG as an absolute path, so every node reads the same parameter file regardless of where it was started from. See config.md.

Nodes can also be started individually out of build/bin/can_hub first, then any subset of nodes; the hub learns the routing table at runtime from whoever announces an address.

Ports

PortLink
UDP :20000can_hub — the CAN backplane every node registers with
UDP :20100The SpaceWire mission data link — the payload dials the DHS
TCP :21000TTC → EGSE. The spacecraft dials out; the EGSE listens.
TCP :21001EGSE ground stream — where egse_tm, egse_tc, Yamcs or nc connect

TTC dialling out rather than accepting is a port hazard decision, not a protocol one.


3. Run with Docker

The entire prerequisite list is Docker. docker-compose.yml brings the same seven processes up, one container each, from a single image built by containers/Dockerfile:

$ docker compose up -d --build                 # the whole platform, no host toolchain
$ docker compose logs -f adcs                  # one subsystem
$ docker compose down

The image is built in two stages: CMake fetches FreeRTOS-Kernel V11.3.0 and compiles all nine binaries, and the runtime stage keeps build/bin and config/minscs.conf only. run_sim.sh is unaffected and remains the way to run a host build.

The operator tools ship in the image even though no service runs them (docker compose exec can_hub egse_tm). 21000 and 21001 are published, so a host-built egse_tm/egse_tc — or Yamcs — attaches to the containerised platform exactly as it would to a host run.

Why the containers share one network namespace

Every address in the platform is a compile-time constant that bind()s INADDR_LOOPBACK, settable by neither config key nor environment variable. So can_hub owns the network namespace and every other service joins it with network_mode: "service:can_hub" — all seven processes then see the same loopback while keeping a container each. Two consequences are worth knowing before editing the compose file:

  • Only can_hub can publish ports, which is why the EGSE’s belong to the hub service — and because Docker forwards a published port to the container address rather than to loopback, a pair of socat bridges inside the namespace forward :21010/:21011 to the EGSE’s real listeners. Without them a host client connects and is dropped immediately.
  • Restarting can_hub recreates the namespace and breaks the six containers joined to it, so every service is restart: "no". The platform is one failure domain, which is how run_sim.sh treats it too.

4. What a run looks like

The platform boots in SAFE mode and is idle until commanded; A first session in egse.md is the command sequence to drive it. Node output on the console looks like this — the science chain, from the ground’s capture command to the file arriving in mass memory:

[hub    ] INFO  registered PAYLOAD (0x5) at 127.0.0.1:64258
[TTC    ] INFO  EGSE link up to 127.0.0.1:21000 (SCID 0x00C5, 0 packet(s) buffered)
[DHS    ] INFO  uplink: TC (8,1) -> DHS: CAN cmd 0x10
[DHS    ] INFO  ground requests capture -> PAYLOAD
[DHS    ] INFO  payload stored file 1 -- requesting downlink
[DHS    ] INFO  xfer: receiving file 1, 8192 bytes announced
[DHS    ] INFO  xfer: file 1 complete, 8192/8192 bytes, 0 gap(s)
[DHS    ] INFO  xfer: file 1 downlinked in 200-byte parts

The default 8 KiB capture fills 8 of the payload’s 256 flash sectors and crosses the CAN bus as about 1170 segments; over the SpaceWire link the same file takes roughly a millisecond. The chain end to end is in The capture-to-ground chain.

The hub logs every frame it routes — the place to look first when a subsystem goes quiet — but at debug, with bulk readouts collapsed to one route ... N consecutive frame(s) line so they do not bury everything else. Raise [hub] log_level to see it.

With the EGSE stopped, TTC reports the link down and keeps buffering:

[TTC    ] INFO  link=DOWN SCID=0x00C5 tm_sent=0 tc_rcvd=0 buffered=15 dropped=0
[TTC    ] INFO  EGSE link up to 127.0.0.1:21000 (SCID 0x00C5, 21 packet(s) buffered)

Per-node verbosity is a board strap, not database content, so it is one of the few things still read straight from config/minscs.conf. The shipped file sets [adcs] to error and [payload] to off; every other section is commented out and takes the compiled-in info. Turn the payload up to see its exposure and storage lines.

Last updated on