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 sIndividual 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 telecommandsrun_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
| Port | Link |
|---|---|
UDP :20000 | can_hub — the CAN backplane every node registers with |
UDP :20100 | The SpaceWire mission data link — the payload dials the DHS |
TCP :21000 | TTC → EGSE. The spacecraft dials out; the EGSE listens. |
TCP :21001 | EGSE 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 downThe 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_hubcan 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 ofsocatbridges inside the namespace forward:21010/:21011to the EGSE’s real listeners. Without them a host client connects and is dropped immediately. - Restarting
can_hubrecreates the namespace and breaks the six containers joined to it, so every service isrestart: "no". The platform is one failure domain, which is howrun_sim.shtreats 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 partsThe 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.