From afec464ac6776a12c927485f4b83e5c3ce1ad7bd Mon Sep 17 00:00:00 2001 From: Marko Mecina <marko.mecina@univie.ac.at> Date: Wed, 20 Dec 2023 13:34:09 +0100 Subject: [PATCH] add preliminary ARIEL packet header definitions --- Ccs/packet_config_ARIEL.py | 310 +++++++++++++++++++++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 Ccs/packet_config_ARIEL.py diff --git a/Ccs/packet_config_ARIEL.py b/Ccs/packet_config_ARIEL.py new file mode 100644 index 0000000..d68168d --- /dev/null +++ b/Ccs/packet_config_ARIEL.py @@ -0,0 +1,310 @@ +""" +PUS & SpW packet header structure definitions and utility functions + +PUS-C for ARIEL + +Author: Marko Mecina (MM) +""" +### TBD ### +import ctypes +import datetime +import struct +import numpy as np + +import crcmod +from s2k_partypes import ptt + +# ID of the parameter format type defining parameter +FMT_TYPE_PARAM = 'DPPXXXXX' + +# pre/suffixes for TM/TC packets from/to PLMSIM +# PLM_PKT_PREFIX = b'tc PUS_TC ' +PLM_PKT_PREFIX_TC_SEND = b'tc PUS_TC ' +PLM_PKT_PREFIX_TC = b'TM PUS_TC ' +PLM_PKT_PREFIX_TM = b'TM PUS_TM ' +PLM_PKT_SUFFIX = b'\r\n' + +# CRC methods +puscrc = crcmod.predefined.mkPredefinedCrcFun('crc-ccitt-false') +rmapcrc = crcmod.mkCrcFun(0x107, rev=True, initCrc=0, xorOut=0) +PEC_LEN = 2 # in bytes +RMAP_PEC_LEN = 1 + +# PUS packet structure definition + +PUS_PKT_VERS_NUM = 0 # 0 for space packets +PUS_VERSION = 2 +MAX_PKT_LEN = 1024 # 886 for TMs [EID-1298], 504 for TCs [EID-1361] + +TMTC = {0: 'TM', 1: 'TC'} +TSYNC_FLAG = {0: 'U', 1: 'S'} + +PRIMARY_HEADER = [ + ("PKT_VERS_NUM", ctypes.c_uint16, 3), + ("PKT_TYPE", ctypes.c_uint16, 1), + ("SEC_HEAD_FLAG", ctypes.c_uint16, 1), + ("APID", ctypes.c_uint16, 11), + ("SEQ_FLAGS", ctypes.c_uint16, 2), + ("PKT_SEQ_CNT", ctypes.c_uint16, 14), + ("PKT_LEN", ctypes.c_uint16, 16) +] + +TM_SECONDARY_HEADER = [ + ("PUS_VERSION", ctypes.c_uint8, 4), + ("SCTRS", ctypes.c_uint8, 4), + ("SERV_TYPE", ctypes.c_uint8, 8), + ("SERV_SUB_TYPE", ctypes.c_uint8, 8), + ("MTC", ctypes.c_uint16, 16), + ("DEST_ID", ctypes.c_uint16, 16), + ("CTIME", ctypes.c_uint32, 32), + ("FTIME", ctypes.c_uint16, 16) +] + +TC_SECONDARY_HEADER = [ + ("PUS_VERSION", ctypes.c_uint8, 4), + ("ACK", ctypes.c_uint8, 4), + ("SERV_TYPE", ctypes.c_uint8, 8), + ("SERV_SUB_TYPE", ctypes.c_uint8, 8), + ("SOURCE_ID", ctypes.c_uint16, 16) +] + +# [format of time stamp, amount of bytes of time stamp including sync byte(s), fine time resolution, length of extra sync flag in bytes] +timepack = [ptt(9, 17), 6, 2**16, 0] +CUC_EPOCH = datetime.datetime(2020, 1, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc) + + +def timecal(data, string=False, checkft=False): + if not isinstance(data, bytes): + try: + return data[0] + except (IndexError, TypeError): + return data + + if len(data) == timepack[1]: + sync_byte = True + elif len(data) == timepack[1] - timepack[3]: + sync_byte = False + else: + raise ValueError('Wrong length of time stamp data ({} bytes)'.format(len(data))) + + data = int.from_bytes(data, 'big') + + if sync_byte: + coarse = data >> 24 + fine = ((data >> 8) & 0xffff) / timepack[2] + else: + coarse = data >> 16 + fine = (data & 0xffff) / timepack[2] + + # check for fine time overflow + if checkft and (fine > timepack[2]): + raise ValueError('Fine time is greater than resolution {} > {}!'.format(fine, timepack[2])) + + if string: + if sync_byte: + sync = 'S' if (data & 0xff) == 0b101 else 'U' + else: + sync = '' + return '{:.6f}{}'.format(coarse + fine, sync) + + else: + return coarse + fine + + +def calc_timestamp(time, sync=None, return_bytes=False): + if isinstance(time, (float, int)): + ctime = int(time) + ftime = round(time % 1 * timepack[2]) + if ftime == timepack[2]: + ctime += 1 + ftime = 0 + + elif isinstance(time, str): + if time[-1].upper() in ['U', 'S']: + t = float(time[:-1]) + else: + t = float(time) + ctime = int(t) + ftime = round(t % 1 * timepack[2]) + if ftime == timepack[2]: + ctime += 1 + ftime = 0 + sync = 0b101 if time[-1].upper() == 'S' else 0 + + elif isinstance(time, bytes): + if len(time) not in [timepack[1], timepack[1] - timepack[3]]: + raise ValueError( + 'Bytestring size ({}) does not match length specified in format ({})'.format(len(time), timepack[1])) + ctime = int.from_bytes(time[:4], 'big') + ftime = int.from_bytes(time[4:6], 'big') + if len(time) == timepack[1]: + sync = None + # sync = time[-1] + else: + sync = None + + else: + raise TypeError('Unsupported input for time ({})'.format(type(time))) + + if return_bytes: + if sync is None or sync is False: + return ctime.to_bytes(4, 'big') + ftime.to_bytes(2, 'big') + else: + pass + # no sync byte in ARIEL? + # return ctime.to_bytes(4, 'big') + ftime.to_bytes(2, 'big') + sync.to_bytes(1, 'big') + else: + return ctime, ftime, sync + + +# P_HEADER_LEN = sum([x[2] for x in PRIMARY_HEADER]) // 8 +# TM_HEADER_LEN = sum([x[2] for x in PRIMARY_HEADER + TM_SECONDARY_HEADER]) // 8 +# TC_HEADER_LEN = sum([x[2] for x in PRIMARY_HEADER + TC_SECONDARY_HEADER]) // 8 + +class RawGetterSetter: + + @property + def raw(self): + return bytes(self.bin) + + @raw.setter + def raw(self, rawdata): + self.bin[:] = rawdata + + +class PHeaderBits(ctypes.BigEndianStructure): + _pack_ = 1 + _fields_ = [(label, ctype, bits) for label, ctype, bits in PRIMARY_HEADER] + + +P_HEADER_LEN = ctypes.sizeof(PHeaderBits) + + +class PHeader(ctypes.Union, RawGetterSetter): + _pack_ = 1 + _fields_ = [ + ('bits', PHeaderBits), + ('bin', ctypes.c_ubyte * P_HEADER_LEN) + ] + + +class TMHeaderBits(ctypes.BigEndianStructure): + _pack_ = 1 + _fields_ = [(label, ctype, bits) for label, ctype, bits in PRIMARY_HEADER + TM_SECONDARY_HEADER] + + +TM_HEADER_LEN = ctypes.sizeof(TMHeaderBits) + + +class TMHeader(ctypes.Union, RawGetterSetter): + _pack_ = 1 + _fields_ = [ + ('bits', TMHeaderBits), + ('bin', ctypes.c_ubyte * TM_HEADER_LEN) + ] + + def __init__(self): + super(TMHeader, self).__init__() + self.bits.PKT_VERS_NUM = PUS_PKT_VERS_NUM + self.bits.PKT_TYPE = 0 + self.bits.PUS_VERSION = PUS_VERSION + + +class TCHeaderBits(ctypes.BigEndianStructure): + _pack_ = 1 + _fields_ = [(label, ctype, bits) for label, ctype, bits in PRIMARY_HEADER + TC_SECONDARY_HEADER] + + +TC_HEADER_LEN = ctypes.sizeof(TCHeaderBits) + + +class TCHeader(ctypes.Union, RawGetterSetter): + _pack_ = 1 + _fields_ = [ + ('bits', TCHeaderBits), + ('bin', ctypes.c_ubyte * TC_HEADER_LEN) + ] + + def __init__(self, *args, **kw): + super(TCHeader, self).__init__(*args, **kw) + self.bits.PKT_VERS_NUM = PUS_PKT_VERS_NUM + self.bits.PKT_TYPE = 1 + self.bits.PUS_VERSION = PUS_VERSION + + +CUC_OFFSET = TMHeaderBits.CTIME.offset + +SPW_PROTOCOL_IDS = { + "RMAP": 0x01, + "FEEDATA": 0xF0, + "CCSDS": 0x02 +} + +# RMAP packet structure definitions + +RMAP_MAX_PKT_LEN = 2 ** 15 +SPW_DPU_LOGICAL_ADDRESS = 0x50 +SPW_FEE_LOGICAL_ADDRESS = 0x51 +SPW_FEE_KEY = 0xD1 # application authorisation key + +RMAP_COMMAND_HEADER = [ + ("TARGET_LOGICAL_ADDR", ctypes.c_uint32, 8), + ("PROTOCOL_ID", ctypes.c_uint32, 8), + ("PKT_TYPE", ctypes.c_uint32, 2), + ("WRITE", ctypes.c_uint32, 1), + ("VERIFY", ctypes.c_uint32, 1), + ("REPLY", ctypes.c_uint32, 1), + ("INCREMENT", ctypes.c_uint32, 1), + ("REPLY_ADDR_LEN", ctypes.c_uint32, 2), + ("KEY", ctypes.c_uint32, 8), + ("INIT_LOGICAL_ADDR", ctypes.c_uint32, 8), + ("TRANSACTION_ID", ctypes.c_uint32, 16), + ("EXT_ADDR", ctypes.c_uint32, 8), + ("ADDR", ctypes.c_uint32, 32), + ("DATA_LEN", ctypes.c_uint32, 24), + ("HEADER_CRC", ctypes.c_uint32, 8) +] + +RMAP_REPLY_WRITE_HEADER = [ + ("INIT_LOGICAL_ADDR", ctypes.c_uint32, 8), + ("PROTOCOL_ID", ctypes.c_uint32, 8), + ("PKT_TYPE", ctypes.c_uint32, 2), + ("WRITE", ctypes.c_uint32, 1), + ("VERIFY", ctypes.c_uint32, 1), + ("REPLY", ctypes.c_uint32, 1), + ("INCREMENT", ctypes.c_uint32, 1), + ("REPLY_ADDR_LEN", ctypes.c_uint32, 2), + ("STATUS", ctypes.c_uint32, 8), + ("TARGET_LOGICAL_ADDR", ctypes.c_uint32, 8), + ("TRANSACTION_ID", ctypes.c_uint32, 16), + ("HEADER_CRC", ctypes.c_uint32, 8) +] + +RMAP_REPLY_READ_HEADER = [ + ("INIT_LOGICAL_ADDR", ctypes.c_uint32, 8), + ("PROTOCOL_ID", ctypes.c_uint32, 8), + ("PKT_TYPE", ctypes.c_uint32, 2), + ("WRITE", ctypes.c_uint32, 1), + ("VERIFY", ctypes.c_uint32, 1), + ("REPLY", ctypes.c_uint32, 1), + ("INCREMENT", ctypes.c_uint32, 1), + ("REPLY_ADDR_LEN", ctypes.c_uint32, 2), + ("STATUS", ctypes.c_uint32, 8), + ("TARGET_LOGICAL_ADDR", ctypes.c_uint32, 8), + ("TRANSACTION_ID", ctypes.c_uint32, 16), + ("RESERVED", ctypes.c_uint32, 8), + ("DATA_LEN", ctypes.c_uint32, 24), + ("HEADER_CRC", ctypes.c_uint32, 8) +] + + +# S13 data header format, using python struct conventions +S13_FMT_OBSID = 'I' +S13_FMT_TIME = 'I' +S13_FMT_FTIME = 'H' +S13_FMT_COUNTER = 'H' +_S13_HEADER_FMT = S13_FMT_OBSID + S13_FMT_TIME + S13_FMT_FTIME + S13_FMT_COUNTER + + +def s13_unpack_data_header(buf): + return struct.unpack('>' + _S13_HEADER_FMT, buf[:struct.calcsize(_S13_HEADER_FMT)]) -- GitLab