Previous reading: 20240320 - Ground segment - Workshop 1 - Send simulated telemetry to the cloud
In this guide we are going to create a CCSDS telemetry on openC3 COSMOS, and then we are going to simulate how to send it to the ground segment software.
CCSDS standard
The Consultative Committee for Space Data Systems (CCSDS) was founded in 1982 to discuss and develop standards for space data and information systems. It is composed of the world’s leading space agencies, including ESA, NASA, and JAXA. One of the most important standards developed by this organization is the Space Packet Protocol, often referred to as the CCSDS protocol. This standard specifies the communications protocol to be used by space missions. Its main purpose is to transfer space application data between a sending and a receiving entity, such as a ground station and a satellite, relying on services provided by underlying layers.
The CCSDS protocol defines a specific packet format for telemetry data. This format ensures that data is correctly structured for transmission and reception, facilitating communication between ground stations and satellites. The CCSDS standard is widely adopted in the space industry and provides a robust and reliable framework for space data communication.
he Space Packet shall be a variable-length, delimited, octet-aligned data unit defined in section 4 of this Recommendation. It shall consist of at least 7 and at most 65542 octets, but individual project organizations may establish the maximum length used for their projects, taking into account the maximum service data unit size in all subnetworks traversed by the LDP.
They are variable in length (or may be fixed at the discretion of the user) and are transmitted at variable intervals. Aside from a header that identifies the Packet, the internal data content of Space Packets is completely under the control of the user application. Each user application can define the organization and content of Packets independently of other user applications and with a minimum of constraints imposed by the transmission mechanisms of the underlying subnetworks.
Packet Version Number: Binary encodede Version Number
Packet type: Telemetry ‘0’, for telecommand ‘1’
Secondary Header Flag: ‘1’ fi Packet Secondary Header is present, ‘o’ if Packet Secondaty Header is not present
Application process identifier: APID may uniquely identify the individual sending or receiving application process within a particular space vehicle
Sequence flags:
- ‘00’ if is a continuation segment of USER Data
- ‘01’ if is the first segment of User Data
- ‘10’ if is the last segment of User Data
- ‘11’ if constains unsegment User Data
Paquet Sequence Count or Packet Name: Is a counter for sending or receiving more than one packet. Max value : 16383
Packet Data Length: (Total Number of Octets in the Packet Data Field) - 1
You can read more about the standard in the official document SPACE PACKET PROTOCOL.
Create our telemetry
We can define our simples CCSDS telemetry modifying the tlm.txt
. We are going to define our CCSDS telemetry as:
TELEMETRY PYTHON_CCSDS_EXAMPLE STATUS BIG_ENDIAN "Telemetry description"
# Keyword Name BitSize Type ID Description
APPEND_ITEM CCSDSVER 3 UINT "CCSDS PACKET VERSION NUMBER (SEE CCSDS 133.0-B-1)"
APPEND_ITEM CCSDSTYPE 1 UINT "CCSDS PACKET TYPE (COMMAND OR TELEMETRY)"
STATE TLM 0
STATE CMD 1
APPEND_ITEM CCSDSSHF 1 UINT "CCSDS SECONDARY HEADER FLAG"
STATE FALSE 0
STATE TRUE 1
APPEND_ID_ITEM CCSDSAPID 11 UINT 1 "CCSDS APPLICATION PROCESS ID"
APPEND_ITEM CCSDSSEQFLAGS 2 UINT "CCSDS SEQUENCE FLAGS"
STATE FIRST 0
STATE CONT 1
STATE LAST 2
STATE NOGROUP 3
APPEND_ITEM CCSDSSEQCNT 14 UINT "CCSDS PACKET SEQUENCE COUNT"
APPEND_ITEM CCSDSLENGTH 16 UINT "CCSDS PACKET DATA LENGTH"
APPEND_ITEM TEMP 32 FLOAT "Temperature of the SS" LITTLE_ENDIAN
APPEND_ITEM PREASSURE 32 FLOAT "Preassure" LITTLE_ENDIAN
APPEND_ITEM ALTITUDE 32 FLOAT "Alttitude" LITTLE_ENDIAN
APPEND_ITEM HUMIDITY 32 FLOAT "Humidity" LITTLE_ENDIAN
APPEND_ITEM X 32 FLOAT "X" LITTLE_ENDIAN
APPEND_ITEM Y 32 FLOAT "Y" LITTLE_ENDIAN
APPEND_ITEM Z 32 FLOAT "Z" LITTLE_ENDIAN
Our plugin.txt
needs to be rewriten as:
# Set VARIABLEs here to allow variation in your plugin
VARIABLE bob_target_name BOB
TARGET BOB <%= bob_target_name %>
INTERFACE <%= bob_target_name %>_INT tcpip_server_interface.rb 8002 8002 10.0 nil LENGTH 32 16 1 1 "BIG_ENDIAN" 0 nil 100
MAP_TARGET <%= bob_target_name %>
Defining the protocol
Official documentation about OpenC3 COSMOS Protocols
Lets take a look to the protocol specification on our plugin.txt
:
INTERFACE <%= bob_target_name %>_INT tcpip_server_interface.rb 8002 8002 10.0 nil LENGTH 32 16 1 1 "BIG_ENDIAN" 0 nil 100
The length protocol is specified in the next way:
- PROTOCOL: LENGTH
- Length Bit Offset: 32
- Length Bit Size: 16
- Length Value Offset: 7
- Bytes per Count: 1
- Length Endianness: “BIG_ENDIAN”
- Discard Leading Bytes: 0
- Sync Pattern: nil
- Max Length: 100
I am using the packet as an example: 20 01 c0 00 00 1b 3d 0a 87 40 a4 70 9d 3f 52 b8 9e 3f 00 00 a0 3f ae 47 a1 3f 5c 8f a2 3f 0a d7 a3 3f
- Lenght of the packet: 34 Bytes
- Length field content: 27 Bytes
- Length of the Header (without the 16 bits of the length): 4
- Length of data: 28
- Length of the length = 16 bits = 2 bytes
$$ L_{PACKET} = L_{HEADER} + L_{LENGTH} + L_{DATA} \Rightarrow 34 = 4 + 2 + 28 $$ In that example the packet said the length is 27, that means. C Is content of the variable:
$$ C_{LENGTH} + {Offset} = L_{Packet} \Rightarrow 27 + 7 = 34 $$
Simulate telemetry with python
from construct import Float32l, BitStruct, Struct, BitsInteger, Int16ub
import math
# Generate a OBC_Packet telemetry packet
CCSDS_TM_Packet_Header = BitStruct(
"CCSDS_VERSION" / BitsInteger(3),
"CCSDS_TYPE" / BitsInteger(1),
"CCSDS_SECONDARY_HEADER_FLAG" / BitsInteger(1),
"CCSDS_APID" / BitsInteger(11),
"CCSDS_SEQUENCE_FLAG" / BitsInteger(2),
"CCSDS_SEQUENCE_COUNT" / BitsInteger(14)
)
TM_DATA = Struct(
"TEMP" / Float32l,
"PREASSURE" / Float32l,
"ALTITUDE" / Float32l,
"HUMIDITY" / Float32l,
"X" / Float32l,
"Y" / Float32l,
"Z" / Float32l
)
TLM = Struct(
"CCSDS_TM_Packet_Header" / CCSDS_TM_Packet_Header,
"CCSDS_PACKET_DATA_LENGTH" / Int16ub,
"TM_DATA" / TM_DATA
)
tm_data_dict = dict(
TEMP = 1.22,
PREASSURE = 1.23,
ALTITUDE = 1.24,
HUMIDITY = 1.25,
X = 1.26,
Y = 1.27,
Z = 1.28
)
tm_data = TM_DATA.build(tm_data_dict)
print(tm_data.hex())
ccsds_header_dict = dict(
CCSDS_VERSION = 0b0,
CCSDS_TYPE = 0b0,
CCSDS_SECONDARY_HEADER_FLAG = 0b0,
CCSDS_APID = 1,
CCSDS_SEQUENCE_FLAG = 0b11,
CCSDS_SEQUENCE_COUNT = 0b0
)
packet= TLM.build(dict(
CCSDS_TM_Packet_Header = ccsds_header_dict,
CCSDS_PACKET_DATA_LENGTH = len(tm_data) + 6 - 1,
TM_DATA = tm_data_dict
))
ccsds_header = CCSDS_TM_Packet_Header.build(ccsds_header_dict)
print(' '.join('{:02x}'.format(x) for x in packet))
print("The lenght of data is {}, so is going to be {}. First part is {}, and packet {}".format(len(tm_data), len(tm_data) + 4 - 1, len(ccsds_header),len(packet)))
import socket
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# Connect the socket to the server and port
server_address = ('cosmos.insightsat.com', 8002)
sock.connect(server_address)
try:
# Prepare the binary data
message = bytes([3, 1, 250])
# Send data
print('sending {!r}'.format(packet))
sock.sendall(packet)
finally:
# Close the socket
print('closing socket')
sock.close()