Defining a Custom PUS Service #
In addition to the functionality provided by standard PUS services, it is expected that you will want to define your own services to accomplish mission-specific tasks.
This consists of:
- Choosing a service ID
- Defining commands
- Defining telemetry
- Implementing a service handler
For both commands and telemetry, the task is to specify the data types exchanged between the ground segment and space segment.
In order to support many ground station software packages, we use the
XML Telemetric and Command Exchange™ (XTCE) standard.
We use
pymdb
to generate XTCE descriptions easily.
In the future, we want to provide a less verbose version using YAML.
The mdb/service.py
file
#
To start a new service, create a file mdb/service.py
in your application directory.
The name should reflect the name of your service.
Add the following contents:
from yamcs.pymdb import *
from rustgen import *
service = System("Raccoon")
service_type_id = 130
base_cmd = Command(
system=service,
name="base",
abstract=True,
base="/PUS/pus-tc",
assignments={"type": service_type_id},
)
Choosing a service ID #
IDs below 127 are reserved for standard PUS services. You can use service IDs above 127. The service ID must be unique within an APID.
Defining commands #
For each command that you want to define, add the following to mdb/service.py
:
my_new_command = Command(
system=service,
base=base_cmd,
assignments={"subtype": 1},
name="MyNewCommand",
arguments=[
# ...
],
)
Pay attention to the assignments={"subtype": 1}
line.
This specifies the PUS subservice type, which is used to identify the command.
You should increment it for each command that you define.
Command arguments #
The arguments
array specifies the available arguments for the command.
They can be of the following types:
- Supported:
- Not yet supported:
Take a look at the argument list for each of the linked class in the pymdb
source code to see the available options.
Example #
arguments=[
IntegerArgument(
name="BatteryNum",
minimum=1,
maximum=3,
encoding=uint8_t,
signed=False,
),
IntegerArgument(
name="CustomLength",
minimum=0,
maximum=244,
encoding=IntegerEncoding(bits=5),
signed=False,
),
EnumeratedArgument(
name="EnumArg",
choices=[[0, "OFF"], [1, "ON"], [2, "EXPLODE"]],
encoding=uint8_t,
),
EnumeratedArgument(
name="EnumeratedArgCustomType",
choices=[[0, "AUS"], [1, "EIN"], [2, "JA"]],
encoding=IntegerEncoding(bits=4),
),
],
Defining telemetry #
The process of defining telemetry is very similar to commands, with some differences.
A new telemetry container is defined by adding the following to mdb/service.py
:
my_telemetry = Container(
system=service,
base="/PUS/pus-tm",
name="MyTelemetry",
condition=AndExpression(
EqExpression("/PUS/pus-tm/type", service_type_id),
EqExpression("/PUS/pus-tm/subtype", 1)
),
entries=[
...
]
)
The two key differences are:
- The subservice type (i.e. telemetry packet identifier) is specified in the
condition
field, and it has to be ANDed together with the service type. Remember to increment the subtype for each telemetry container you define. - Instead of an
arguments
field, we useentries
.
Container entries #
…
Example #
…