Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

TMTC modelling using Rust

Before we talk about how to model telecommand and telemetry data using Rust, we are going to present some basic concepts and useful libraries first.

Serialization

Serialization and deserialization is the process of converting (Rust) data structures into some format which can be stored or transmitted. We can use this system for generating the payload of our telecommand and telemetry packets. This allows us to model our payloads with Rust data structures, fits perfectly into the data-driven approach that Rust programs tend to favor and allows us to use the excellent type system.

The Rust ecosystem provides the serde library for this task. The library makes it trivial to add serialization support to custom datastructures by providing a derive macro. In almost all cases, you can just add this derive macro to a data structure to make it serializable with any serde compatible serializer.

There are various serializers available which are well suited to the requirements of space systems.

  • Generally, we try to minimize the payload size to save data bandwidth.
  • The data does not necessarily have to be human-readable

We recommend the postcard serializer, which fulfills these requirements and also works well for embedded systems.

Modelling telecommands and telemetry

Using a serializer library like serde allows us to do some interesting things. For example, let’s assume you have a Camera object in software that you want to send some commands to. This object should have the following capability:

  • Process a ping command
  • Capture an image
  • Send back configuration data

You can now model a request to your Camera object using the following data structure

#![allow(unused)]
fn main() {
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub enum CameraRequest {
    Ping,
    CaptureImage,
    RequestConfig,
}
}

This data structure models all the requests that the Camera provides. On the telemetry side, you would have a similar object

#![allow(unused)]
fn main() {
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub enum CameraResponse {
    Ok,
    Config(ConfigStructure)
}
}

where ConfigStructure would be some other wrapped configuration structure, and the Ok response would be the reply for successful execution for all other commands which do not have additional telemetry information.

Rust makes it trivial to move components into a new shared library. You can now put these data structures in a shared types or data library which can be re-used by both a ground system library and by the on-board software.

On the ground system, you could use a function like postcard::to_allocvec to generate the byte representation of a CameraRequest, which is then sent as the payload inside a CCSDS space packet. On the on-board software side, you can use postcard::from_bytes to deserialize the CameraRequest from the raw payload bytes. In both cases, you do not need to hand-write the serialization and de-serialization code anymore. The only trade-off is that you need a Rust conversion layer if you want to create your telecommands in another language like Python.

Using Rust structures like this also has other advantages. Once you have the CameraRequest structure, you can match on it to cover all commands that the device handler needs to cover. If you add a new field, you have to handle the new field variant as well and you can not forget to handle a variant.

One trade-off to keep in mind is that a Rust enum will always have the size of its largest variant in memory. If you need to send large payload to and from the on-board software, you can also add this data as a secondary data blob behind the primary serde payload, and still send something like small metadata as part of the payload. postcard can tell you the size of the deserialized payload which helps with determining the size of any additional payload data.

We recommend this approach for all TMTC definitions where you control all sides of the communication.