Skip to content

Commit 442ef9e

Browse files
committed
feat: Add PUS 2 simulator code
1 parent 1f6cb24 commit 442ef9e

9 files changed

Lines changed: 665 additions & 146 deletions

File tree

examples/pus/src/main/yamcs/etc/yamcs.pus.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ mdb:
9898
spec: "mdb/dt.xml"
9999
- type: "xtce"
100100
spec: "mdb/pus.xml"
101+
- type: "xtce"
102+
spec: "mdb/pus2.xml"
101103
- type: "xtce"
102104
spec: "mdb/pus5.xml"
103105
- type: "xtce"

examples/pus/src/main/yamcs/mdb/pus.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@
1111
<Enumeration label="INVALID_PUS_SUBTYPE" value="1" />
1212
<Enumeration label="NOT_IMPLEMENTED" value="2" />
1313
<Enumeration label="INVALID_EVENT_ID" value="3" />
14+
<Enumeration label="UNKNOWN_PHYSICAL_DEVICE" value="10" />
15+
<Enumeration label="UNKNOWN_LOGICAL_DEVICE" value="11" />
16+
<Enumeration label="UNKNOWN_DEVICE_COMMAND" value="12" />
17+
<Enumeration label="UNKNOWN_DEVICE_PARAMETER" value="13" />
1418
<Enumeration label="INVALID_VOLTAGE_NUM" value="100" />
1519
</EnumerationList>
1620
</EnumeratedParameterType>
Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Sample implementation of PUS Service 2 (ST[02]) - Device Access.
4+
5+
ST[02] distributes commands to / acquires data from on-board devices. Most of its
6+
fields are "deduced" by the PUS standard (their type/size is declared per device in the
7+
mission database, not fixed by the standard). XTCE cannot express per-device type
8+
variation, so this sample fixes a set of mission conventions to make the service concrete
9+
and demonstrable end-to-end. See pus_analysis/pus02.md for the full analysis.
10+
11+
Sample mission conventions used here:
12+
- protocol_specific_data is a fixed 4-byte aggregate {direction, sub_address, word_count}
13+
for every physical device (e.g. a MIL-STD-1553B remote terminal).
14+
- command_data (TC[2,7]) is a fixed 8-byte block for every physical device.
15+
- command_args (TC[2,10]) is a fixed uint32 for every logical device command.
16+
- parameter_value (TM[2,12]) is a fixed uint32 for every logical device parameter
17+
(same "deduced value -> uint32" convention used by ST[20]).
18+
- TM[2,9] data_block is a fixed 4 x uint16 block.
19+
- auxiliary_data (deduced-presence) is never present in this sample.
20+
- All TM[2,*] reports share MAIN_APID; containers are discriminated by subtype only.
21+
22+
Because every field has a mission-uniform fixed size, all four telecommands use the
23+
generic N-instruction array pattern (AggregateArgumentType + ArrayArgumentType with a
24+
top-level count argument), confirmed working in Yamcs (see pus11.xml RequestArrayType).
25+
-->
26+
<SpaceSystem name="PUS2" xmlns="http://www.omg.org/spec/XTCE/20180204" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
27+
xsi:schemaLocation="http://www.omg.org/spec/XTCE/20180204 https://www.omg.org/spec/XTCE/20180204/SpaceSystem.xsd">
28+
<Header validationStatus="Unknown" version="1.0" date="2026-06-30T00:00:00Z">
29+
<NoteSet>
30+
<Note>Sample implementation of PUS2 Device Access (physical and logical device access).</Note>
31+
</NoteSet>
32+
</Header>
33+
<TelemetryMetaData>
34+
<ParameterTypeSet>
35+
<IntegerParameterType name="TransactionIdType" baseType="/dt/uint16"/>
36+
<EnumeratedParameterType name="DataAcqReturnCodeType">
37+
<IntegerDataEncoding sizeInBits="8"/>
38+
<EnumerationList>
39+
<Enumeration value="0" label="SUCCESS"/>
40+
<Enumeration value="1" label="TIMEOUT"/>
41+
<Enumeration value="2" label="BUS_ERROR"/>
42+
<Enumeration value="3" label="INVALID_DATA"/>
43+
</EnumerationList>
44+
</EnumeratedParameterType>
45+
<!-- TM[2,9] data_block: fixed 4 x uint16 acquired data words (mission convention) -->
46+
<ArrayParameterType name="DataBlockType" arrayTypeRef="/dt/uint16">
47+
<DimensionList>
48+
<Dimension>
49+
<StartingIndex><FixedValue>0</FixedValue></StartingIndex>
50+
<EndingIndex><FixedValue>3</FixedValue></EndingIndex>
51+
</Dimension>
52+
</DimensionList>
53+
</ArrayParameterType>
54+
</ParameterTypeSet>
55+
<ParameterSet>
56+
<Parameter parameterTypeRef="TransactionIdType" name="transaction_id"/>
57+
<Parameter parameterTypeRef="DataAcqReturnCodeType" name="data_acq_return_code"/>
58+
<Parameter parameterTypeRef="DataBlockType" name="data_block"
59+
shortDescription="TM[2,9] acquired data words"/>
60+
<Parameter parameterTypeRef="/dt/uint32" name="parameter_value"
61+
shortDescription="TM[2,12] acquired logical device parameter value"/>
62+
</ParameterSet>
63+
<ContainerSet>
64+
<SequenceContainer name="pus2-tm">
65+
<EntryList/>
66+
<BaseContainer containerRef="/PUS/pus-tm">
67+
<RestrictionCriteria>
68+
<Comparison parameterRef="/PUS/type" comparisonOperator="==" value="2"/>
69+
</RestrictionCriteria>
70+
</BaseContainer>
71+
</SequenceContainer>
72+
<SequenceContainer name="PHYSICAL_DEVICE_DATA_REPORT" shortDescription="TM[2,9] physical device data report">
73+
<EntryList>
74+
<ParameterRefEntry parameterRef="transaction_id"/>
75+
<ParameterRefEntry parameterRef="data_acq_return_code"/>
76+
<ArrayParameterRefEntry parameterRef="data_block"/>
77+
</EntryList>
78+
<BaseContainer containerRef="pus2-tm">
79+
<RestrictionCriteria>
80+
<Comparison parameterRef="/PUS/subtype" comparisonOperator="==" value="9"/>
81+
</RestrictionCriteria>
82+
</BaseContainer>
83+
</SequenceContainer>
84+
<SequenceContainer name="LOGICAL_DEVICE_DATA_REPORT" shortDescription="TM[2,12] logical device data report">
85+
<EntryList>
86+
<ParameterRefEntry parameterRef="transaction_id"/>
87+
<ParameterRefEntry parameterRef="data_acq_return_code"/>
88+
<ParameterRefEntry parameterRef="parameter_value"/>
89+
</EntryList>
90+
<BaseContainer containerRef="pus2-tm">
91+
<RestrictionCriteria>
92+
<Comparison parameterRef="/PUS/subtype" comparisonOperator="==" value="12"/>
93+
</RestrictionCriteria>
94+
</BaseContainer>
95+
</SequenceContainer>
96+
</ContainerSet>
97+
</TelemetryMetaData>
98+
<CommandMetaData>
99+
<ArgumentTypeSet>
100+
<IntegerArgumentType name="NumInstructionsType" baseType="/dt/uint8"/>
101+
<IntegerArgumentType name="DeviceIdType" baseType="/dt/uint8"/>
102+
103+
<!-- protocol_specific_data: fixed 4-byte mission layout, shared by physical TCs -->
104+
<AggregateArgumentType name="ProtocolDataType">
105+
<MemberList>
106+
<Member typeRef="/dt/uint8" name="direction"/>
107+
<Member typeRef="/dt/uint8" name="sub_address"/>
108+
<Member typeRef="/dt/uint16" name="word_count"/>
109+
</MemberList>
110+
</AggregateArgumentType>
111+
<!-- command_data: fixed 8-byte mission block (TC[2,7]) -->
112+
<BinaryArgumentType name="CommandDataType">
113+
<BinaryDataEncoding>
114+
<SizeInBits><FixedValue>64</FixedValue></SizeInBits>
115+
</BinaryDataEncoding>
116+
</BinaryArgumentType>
117+
118+
<!-- TC[2,7] instruction: physical_device_id + protocol_data + command_data -->
119+
<AggregateArgumentType name="PhysicalCmdEntryType">
120+
<MemberList>
121+
<Member typeRef="DeviceIdType" name="physical_device_id"/>
122+
<Member typeRef="ProtocolDataType" name="protocol_data"/>
123+
<Member typeRef="CommandDataType" name="command_data"/>
124+
</MemberList>
125+
</AggregateArgumentType>
126+
<ArrayArgumentType arrayTypeRef="PhysicalCmdEntryType" name="PhysicalCmdArrayType">
127+
<DimensionList>
128+
<Dimension>
129+
<StartingIndex><FixedValue>0</FixedValue></StartingIndex>
130+
<EndingIndex>
131+
<DynamicValue>
132+
<ArgumentInstanceRef argumentRef="n"/>
133+
<LinearAdjustment intercept="-1"/>
134+
</DynamicValue>
135+
</EndingIndex>
136+
</Dimension>
137+
</DimensionList>
138+
</ArrayArgumentType>
139+
140+
<!-- TC[2,8] instruction: transaction_id + physical_device_id + protocol_data -->
141+
<AggregateArgumentType name="PhysicalAcqEntryType">
142+
<MemberList>
143+
<Member typeRef="/dt/uint16" name="transaction_id"/>
144+
<Member typeRef="DeviceIdType" name="physical_device_id"/>
145+
<Member typeRef="ProtocolDataType" name="protocol_data"/>
146+
</MemberList>
147+
</AggregateArgumentType>
148+
<ArrayArgumentType arrayTypeRef="PhysicalAcqEntryType" name="PhysicalAcqArrayType">
149+
<DimensionList>
150+
<Dimension>
151+
<StartingIndex><FixedValue>0</FixedValue></StartingIndex>
152+
<EndingIndex>
153+
<DynamicValue>
154+
<ArgumentInstanceRef argumentRef="n"/>
155+
<LinearAdjustment intercept="-1"/>
156+
</DynamicValue>
157+
</EndingIndex>
158+
</Dimension>
159+
</DimensionList>
160+
</ArrayArgumentType>
161+
162+
<!-- TC[2,10] instruction: logical_device_id + command_id + command_args -->
163+
<AggregateArgumentType name="LogicalCmdEntryType">
164+
<MemberList>
165+
<Member typeRef="DeviceIdType" name="logical_device_id"/>
166+
<Member typeRef="/dt/uint8" name="command_id"/>
167+
<Member typeRef="/dt/uint32" name="command_args"/>
168+
</MemberList>
169+
</AggregateArgumentType>
170+
<ArrayArgumentType arrayTypeRef="LogicalCmdEntryType" name="LogicalCmdArrayType">
171+
<DimensionList>
172+
<Dimension>
173+
<StartingIndex><FixedValue>0</FixedValue></StartingIndex>
174+
<EndingIndex>
175+
<DynamicValue>
176+
<ArgumentInstanceRef argumentRef="n"/>
177+
<LinearAdjustment intercept="-1"/>
178+
</DynamicValue>
179+
</EndingIndex>
180+
</Dimension>
181+
</DimensionList>
182+
</ArrayArgumentType>
183+
184+
<!-- TC[2,11] instruction: transaction_id + logical_device_id + parameter_id -->
185+
<AggregateArgumentType name="LogicalAcqEntryType">
186+
<MemberList>
187+
<Member typeRef="/dt/uint16" name="transaction_id"/>
188+
<Member typeRef="DeviceIdType" name="logical_device_id"/>
189+
<Member typeRef="/dt/uint8" name="parameter_id"/>
190+
</MemberList>
191+
</AggregateArgumentType>
192+
<ArrayArgumentType arrayTypeRef="LogicalAcqEntryType" name="LogicalAcqArrayType">
193+
<DimensionList>
194+
<Dimension>
195+
<StartingIndex><FixedValue>0</FixedValue></StartingIndex>
196+
<EndingIndex>
197+
<DynamicValue>
198+
<ArgumentInstanceRef argumentRef="n"/>
199+
<LinearAdjustment intercept="-1"/>
200+
</DynamicValue>
201+
</EndingIndex>
202+
</Dimension>
203+
</DimensionList>
204+
</ArrayArgumentType>
205+
</ArgumentTypeSet>
206+
<MetaCommandSet>
207+
<MetaCommand name="pus2-tc" abstract="true">
208+
<BaseMetaCommand metaCommandRef="/PUS/pus-tc">
209+
<ArgumentAssignmentList>
210+
<ArgumentAssignment argumentName="apid" argumentValue="1"/>
211+
<ArgumentAssignment argumentName="type" argumentValue="2"/>
212+
</ArgumentAssignmentList>
213+
</BaseMetaCommand>
214+
<CommandContainer name="pus2-tc">
215+
<EntryList/>
216+
</CommandContainer>
217+
</MetaCommand>
218+
219+
<MetaCommand name="DISTRIBUTE_PHYSICAL_CMDS" shortDescription="TC[2,7] distribute physical device commands">
220+
<BaseMetaCommand metaCommandRef="pus2-tc">
221+
<ArgumentAssignmentList>
222+
<ArgumentAssignment argumentName="subtype" argumentValue="7"/>
223+
</ArgumentAssignmentList>
224+
</BaseMetaCommand>
225+
<ArgumentList>
226+
<Argument argumentTypeRef="NumInstructionsType" name="n"/>
227+
<Argument argumentTypeRef="PhysicalCmdArrayType" name="instructions"/>
228+
</ArgumentList>
229+
<CommandContainer name="DISTRIBUTE_PHYSICAL_CMDS">
230+
<EntryList>
231+
<ArgumentRefEntry argumentRef="n"/>
232+
<ArgumentRefEntry argumentRef="instructions"/>
233+
</EntryList>
234+
<BaseContainer containerRef="pus2-tc"/>
235+
</CommandContainer>
236+
</MetaCommand>
237+
238+
<MetaCommand name="ACQUIRE_PHYSICAL_DATA" shortDescription="TC[2,8] acquire data from physical devices">
239+
<BaseMetaCommand metaCommandRef="pus2-tc">
240+
<ArgumentAssignmentList>
241+
<ArgumentAssignment argumentName="subtype" argumentValue="8"/>
242+
</ArgumentAssignmentList>
243+
</BaseMetaCommand>
244+
<ArgumentList>
245+
<Argument argumentTypeRef="NumInstructionsType" name="n"/>
246+
<Argument argumentTypeRef="PhysicalAcqArrayType" name="instructions"/>
247+
</ArgumentList>
248+
<CommandContainer name="ACQUIRE_PHYSICAL_DATA">
249+
<EntryList>
250+
<ArgumentRefEntry argumentRef="n"/>
251+
<ArgumentRefEntry argumentRef="instructions"/>
252+
</EntryList>
253+
<BaseContainer containerRef="pus2-tc"/>
254+
</CommandContainer>
255+
</MetaCommand>
256+
257+
<MetaCommand name="DISTRIBUTE_LOGICAL_CMDS" shortDescription="TC[2,10] distribute logical device commands">
258+
<BaseMetaCommand metaCommandRef="pus2-tc">
259+
<ArgumentAssignmentList>
260+
<ArgumentAssignment argumentName="subtype" argumentValue="10"/>
261+
</ArgumentAssignmentList>
262+
</BaseMetaCommand>
263+
<ArgumentList>
264+
<Argument argumentTypeRef="NumInstructionsType" name="n"/>
265+
<Argument argumentTypeRef="LogicalCmdArrayType" name="instructions"/>
266+
</ArgumentList>
267+
<CommandContainer name="DISTRIBUTE_LOGICAL_CMDS">
268+
<EntryList>
269+
<ArgumentRefEntry argumentRef="n"/>
270+
<ArgumentRefEntry argumentRef="instructions"/>
271+
</EntryList>
272+
<BaseContainer containerRef="pus2-tc"/>
273+
</CommandContainer>
274+
</MetaCommand>
275+
276+
<MetaCommand name="ACQUIRE_LOGICAL_DATA" shortDescription="TC[2,11] acquire data from logical devices">
277+
<BaseMetaCommand metaCommandRef="pus2-tc">
278+
<ArgumentAssignmentList>
279+
<ArgumentAssignment argumentName="subtype" argumentValue="11"/>
280+
</ArgumentAssignmentList>
281+
</BaseMetaCommand>
282+
<ArgumentList>
283+
<Argument argumentTypeRef="NumInstructionsType" name="n"/>
284+
<Argument argumentTypeRef="LogicalAcqArrayType" name="instructions"/>
285+
</ArgumentList>
286+
<CommandContainer name="ACQUIRE_LOGICAL_DATA">
287+
<EntryList>
288+
<ArgumentRefEntry argumentRef="n"/>
289+
<ArgumentRefEntry argumentRef="instructions"/>
290+
</EntryList>
291+
<BaseContainer containerRef="pus2-tc"/>
292+
</CommandContainer>
293+
</MetaCommand>
294+
</MetaCommandSet>
295+
</CommandMetaData>
296+
</SpaceSystem>

pus_analysis/pus02.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,3 +612,61 @@ ST[02] is implementable **entirely with XTCE MDB definitions and zero Java code
612612
3. **TC[2,7/8]** require per-device MetaCommands with N=1 for heterogeneous device types; a generic N-instruction MetaCommand is possible when the mission standardizes `protocol_specific_data` and `command_data` sizes.
613613
4. **TM[2,9/12]** are the hardest: "deduced presence" auxiliary data and variable-type payload fields are not expressible generically. Per-device `SequenceContainer`s with APID-based discrimination are the correct approach. Discrimination without separate APIDs requires the spacecraft to echo a device discriminator field in the TM (a mission extension beyond the minimum PUS spec).
614614
5. The PUS spec's "deduced" fields are a design-time declaration — they become concrete fixed types per mission device declaration. XTCE handles this correctly by requiring per-device definitions.
615+
616+
## D. Sending Commands from the Web UI — Valid Inputs
617+
618+
After starting Yamcs + the simulator (see `pus_analysis/pus_simulator_architecture.md` for
619+
build/run), go to **Commanding → Send a command → `/PUS2/...`** in the web UI and fill in the
620+
arguments.
621+
622+
The simulator validates every request against this sample device registry (`Pus2Service.java`):
623+
624+
- **Physical devices:** `1`, `2`
625+
- **Logical devices & commands:** device `1` → command `1` or `2`; device `2` → command `1`
626+
- **Logical parameters (device, parameter → value):** `(1,1)→100`, `(1,2)→25`, `(2,1)→4200`
627+
628+
Set `n` to the number of entries in the `instructions` array (they must match). The examples
629+
below use `n = 1`. Valid inputs get ACK start + ACK completion, and the acquire commands also
630+
emit a TM report. Any device/command/parameter outside the registry gets a **NACK start**
631+
(unknown physical device / logical device / device command / device parameter).
632+
633+
**`DISTRIBUTE_PHYSICAL_CMDS` — TC[2,7]**
634+
```
635+
n = 1
636+
instructions = [
637+
{ physical_device_id: 1,
638+
protocol_data: { direction: 0, sub_address: 1, word_count: 4 },
639+
command_data: 0x1122334455667788 } # 8-byte binary
640+
]
641+
```
642+
643+
**`ACQUIRE_PHYSICAL_DATA` — TC[2,8]** → replies TM[2,9]
644+
```
645+
n = 1
646+
instructions = [
647+
{ transaction_id: 777, physical_device_id: 1,
648+
protocol_data: { direction: 1, sub_address: 1, word_count: 4 } }
649+
]
650+
```
651+
652+
**`DISTRIBUTE_LOGICAL_CMDS` — TC[2,10]**
653+
```
654+
n = 1
655+
instructions = [
656+
{ logical_device_id: 1, command_id: 2, command_args: 42 }
657+
]
658+
```
659+
660+
**`ACQUIRE_LOGICAL_DATA` — TC[2,11]** → replies TM[2,12]
661+
```
662+
n = 1
663+
instructions = [
664+
{ transaction_id: 777, logical_device_id: 1, parameter_id: 2 }
665+
]
666+
```
667+
668+
To send multiple instructions in one command, set `n` accordingly and add that many entries,
669+
e.g. `n = 2` with two objects in `instructions`.
670+
671+
The TM reports (TM[2,9] / TM[2,12]) and the command acknowledgements appear in the web UI
672+
under the `/PUS2` space system and in the command history.

0 commit comments

Comments
 (0)