diff --git a/Ccs/packet_config_WS0922.py b/Ccs/packet_config_WS0922.py
new file mode 100644
index 0000000000000000000000000000000000000000..b8f556d0436d7b353155f845a200d21e2e177cb0
--- /dev/null
+++ b/Ccs/packet_config_WS0922.py
@@ -0,0 +1,762 @@
+"""
+PUS & SpW packet header structure definitions and utility functions
+
+PUS-A
+
+Author: Marko Mecina (MM)
+"""
+
+import ctypes
+import datetime
+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 = 1
+MAX_PKT_LEN = 1024  # bytes
+
+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 = [
+    ("SPARE1", ctypes.c_uint8, 1),
+    ("PUS_VERSION", ctypes.c_uint8, 3),
+    ("SPARE2", ctypes.c_uint8, 4),
+    ("SERV_TYPE", ctypes.c_uint8, 8),
+    ("SERV_SUB_TYPE", ctypes.c_uint8, 8),
+    ("DEST_ID", ctypes.c_uint8, 8),
+    ("CTIME", ctypes.c_uint32, 32),
+    ("FTIME", ctypes.c_uint32, 24),
+    ("TIMESYNC", ctypes.c_uint32, 8)
+]
+
+TC_SECONDARY_HEADER = [
+    ("CCSDS_SEC_HEAD_FLAG", ctypes.c_uint8, 1),
+    ("PUS_VERSION", ctypes.c_uint8, 3),
+    ("ACK", ctypes.c_uint8, 4),
+    ("SERV_TYPE", ctypes.c_uint8, 8),
+    ("SERV_SUB_TYPE", ctypes.c_uint8, 8),
+    ("SOURCE_ID", ctypes.c_uint8, 8)
+]
+
+# [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, 18), 8, 1e6, 1]
+CUC_EPOCH = datetime.datetime(2018, 1, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc)
+
+
+def timecal(data, string=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 >> 32
+        fine = ((data >> 8) & 0xffffff) / timepack[2]
+    else:
+        coarse = data >> 24
+        fine = (data & 0xffffff) / 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:7], 'big')
+        if len(time) == timepack[1]:
+            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(3, 'big')
+        else:
+            return ctime.to_bytes(4, 'big') + ftime.to_bytes(3, '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_uint8, 8),
+    ("PROTOCOL_ID", ctypes.c_uint8, 8),
+    ("PKT_TYPE", ctypes.c_uint8, 2),
+    ("WRITE", ctypes.c_uint8, 1),
+    ("VERIFY", ctypes.c_uint8, 1),
+    ("REPLY", ctypes.c_uint8, 1),
+    ("INCREMENT", ctypes.c_uint8, 1),
+    ("REPLY_ADDR_LEN", ctypes.c_uint8, 2),
+    ("KEY", ctypes.c_uint8, 8),
+    ("INIT_LOGICAL_ADDR", ctypes.c_uint8, 8),
+    ("TRANSACTION_ID", ctypes.c_uint16, 16),
+    ("EXT_ADDR", ctypes.c_uint8, 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_uint8, 8),
+    ("PROTOCOL_ID", ctypes.c_uint8, 8),
+    ("PKT_TYPE", ctypes.c_uint8, 2),
+    ("WRITE", ctypes.c_uint8, 1),
+    ("VERIFY", ctypes.c_uint8, 1),
+    ("REPLY", ctypes.c_uint8, 1),
+    ("INCREMENT", ctypes.c_uint8, 1),
+    ("REPLY_ADDR_LEN", ctypes.c_uint8, 2),
+    ("STATUS", ctypes.c_uint8, 8),
+    ("TARGET_LOGICAL_ADDR", ctypes.c_uint8, 8),
+    ("TRANSACTION_ID", ctypes.c_uint16, 16),
+    ("HEADER_CRC", ctypes.c_uint8, 8)
+]
+
+RMAP_REPLY_READ_HEADER = [
+    ("INIT_LOGICAL_ADDR", ctypes.c_uint8, 8),
+    ("PROTOCOL_ID", ctypes.c_uint8, 8),
+    ("PKT_TYPE", ctypes.c_uint8, 2),
+    ("WRITE", ctypes.c_uint8, 1),
+    ("VERIFY", ctypes.c_uint8, 1),
+    ("REPLY", ctypes.c_uint8, 1),
+    ("INCREMENT", ctypes.c_uint8, 1),
+    ("REPLY_ADDR_LEN", ctypes.c_uint8, 2),
+    ("STATUS", ctypes.c_uint8, 8),
+    ("TARGET_LOGICAL_ADDR", ctypes.c_uint8, 8),
+    ("TRANSACTION_ID", ctypes.c_uint16, 16),
+    ("RESERVED", ctypes.c_uint8, 8),
+    ("DATA_LEN", ctypes.c_uint32, 24),
+    ("HEADER_CRC", ctypes.c_uint32, 8)
+]
+
+# FEEDATA packet structure definitions
+
+FEEDATA_TRANSFER_HEADER = [
+    ("INIT_LOGICAL_ADDR", ctypes.c_uint8, 8),
+    ("PROTOCOL_ID", ctypes.c_uint8, 8),
+    ("DATA_LEN", ctypes.c_uint16, 16),
+    ("RESERVED1", ctypes.c_uint8, 4),
+    ("MODE", ctypes.c_uint8, 4),
+    ("LAST_PKT", ctypes.c_uint8, 1),
+    ("CCDSIDE", ctypes.c_uint8, 2),
+    ("CCD", ctypes.c_uint8, 1),
+    ("RESERVED2", ctypes.c_uint8, 2),
+    ("PKT_TYPE", ctypes.c_uint8, 2),
+    ("FRAME_CNT", ctypes.c_uint16, 16),
+    ("SEQ_CNT", ctypes.c_uint16, 16)
+]
+
+
+class RMapCommandHeaderBits(ctypes.BigEndianStructure):
+    _pack_ = 1
+    _fields_ = [(label, ctype, bits) for label, ctype, bits in RMAP_COMMAND_HEADER]
+
+
+RMAP_COMMAND_HEADER_LEN = ctypes.sizeof(RMapCommandHeaderBits)  # sum([x[2] for x in RMAP_COMMAND_HEADER]) // 8
+
+
+class RMapCommandHeader(ctypes.Union, RawGetterSetter):
+    _pack_ = 1
+    _fields_ = [
+        ('bits', RMapCommandHeaderBits),
+        ('bin', ctypes.c_ubyte * RMAP_COMMAND_HEADER_LEN)
+    ]
+
+    def __init__(self, *args, **kw):
+        super(RMapCommandHeader, self).__init__(*args, **kw)
+        self.bits.PROTOCOL_ID = SPW_PROTOCOL_IDS["RMAP"]
+        self.bits.PKT_TYPE = 1
+
+
+class RMapReplyWriteHeaderBits(ctypes.BigEndianStructure):
+    _pack_ = 1
+    _fields_ = [(label, ctype, bits) for label, ctype, bits in RMAP_REPLY_WRITE_HEADER]
+
+
+RMAP_REPLY_WRITE_HEADER_LEN = ctypes.sizeof(
+    RMapReplyWriteHeaderBits)  # sum([x[2] for x in RMAP_REPLY_WRITE_HEADER]) // 8
+
+
+class RMapReplyWriteHeader(ctypes.Union, RawGetterSetter):
+    _pack_ = 1
+    _fields_ = [
+        ('bits', RMapReplyWriteHeaderBits),
+        ('bin', ctypes.c_ubyte * RMAP_REPLY_WRITE_HEADER_LEN)
+    ]
+
+    def __init__(self, *args, **kw):
+        super(RMapReplyWriteHeader, self).__init__(*args, **kw)
+        self.bits.PROTOCOL_ID = SPW_PROTOCOL_IDS["RMAP"]
+        self.bits.PKT_TYPE = 0
+        self.bits.WRITE = 1
+        self.bits.REPLY = 1
+
+
+class RMapReplyReadHeaderBits(ctypes.BigEndianStructure):
+    _pack_ = 1
+    _fields_ = [(label, ctype, bits) for label, ctype, bits in RMAP_REPLY_READ_HEADER]
+
+
+RMAP_REPLY_READ_HEADER_LEN = ctypes.sizeof(RMapReplyReadHeaderBits)  # sum([x[2] for x in RMAP_REPLY_READ_HEADER]) // 8
+
+
+class RMapReplyReadHeader(ctypes.Union, RawGetterSetter):
+    _pack_ = 1
+    _fields_ = [
+        ('bits', RMapReplyReadHeaderBits),
+        ('bin', ctypes.c_ubyte * RMAP_REPLY_READ_HEADER_LEN)
+    ]
+
+    def __init__(self, *args, **kw):
+        super(RMapReplyReadHeader, self).__init__(*args, **kw)
+        self.bits.PROTOCOL_ID = SPW_PROTOCOL_IDS["RMAP"]
+        self.bits.PKT_TYPE = 0
+        self.bits.WRITE = 0
+        self.bits.VERIFY = 0
+        self.bits.REPLY = 1
+
+
+class FeeDataTransferHeaderBits(ctypes.BigEndianStructure):
+    _pack_ = 1
+    _fields_ = [(label, ctype, bits) for label, ctype, bits in FEEDATA_TRANSFER_HEADER]
+
+
+FEE_DATA_TRANSFER_HEADER_LEN = ctypes.sizeof(
+    FeeDataTransferHeaderBits)  # sum([x[2] for x in FEEDATA_TRANSFER_HEADER]) // 8
+
+
+class FeeDataTransferHeader(ctypes.Union):
+    _pack_ = 1
+    _fields_ = [
+        ('bits', FeeDataTransferHeaderBits),
+        ('bin', ctypes.c_ubyte * FEE_DATA_TRANSFER_HEADER_LEN)
+    ]
+
+    def __init__(self, *args, **kw):
+        super(FeeDataTransferHeader, self).__init__()
+        self.bits.INIT_LOGICAL_ADDR = SPW_DPU_LOGICAL_ADDRESS
+        self.bits.PROTOCOL_ID = SPW_PROTOCOL_IDS["FEEDATA"]
+
+    @property
+    def raw(self):
+        return bytes(self.bin)
+
+    @property
+    def comptype(self):
+        """Composite packet type used in DB storage, consists of sub-parameters"""
+        return int.from_bytes(self.bin[4:6], 'big')
+
+
+#########################
+# FEE utility functions #
+#########################
+
+# look-up table for FEE parameters (DP IDs & internal IDs) (20220908)
+fee_id = {'vstart': {'pid': 345544373, 'idx': 1},
+          'vend': {'pid': 345544372, 'idx': 2},
+          'charge_injection_width': {'pid': 345544348, 'idx': 3},
+          'charge_injection_gap': {'pid': 345544347, 'idx': 4},
+          'parallel_toi_period': {'pid': 345544366, 'idx': 5},
+          'parallel_clk_overlap': {'pid': 345544365, 'idx': 6},
+          'n_final_dump': {'pid': 345544363, 'idx': 7},
+          'h_end': {'pid': 345544359, 'idx': 8},
+          'packet_size': {'pid': 345544364, 'idx': 9},
+          'int_period': {'pid': 345544362, 'idx': 10},
+          'readout_node_sel': {'pid': 345544368, 'idx': 11},
+          'h_start': {'pid': 345544360, 'idx': 12},
+          'ccd2_e_pix_threshold': {'pid': 345544327, 'idx': 13},
+          'ccd2_f_pix_threshold': {'pid': 345544328, 'idx': 14},
+          'ccd4_e_pix_threshold': {'pid': 345544334, 'idx': 15},
+          'ccd4_f_pix_threshold': {'pid': 345544335, 'idx': 16},
+          'full_sun_pix_threshold': {'pid': 345544358, 'idx': 17},
+          'ccd1_readout': {'pid': 345544325, 'idx': 18},
+          'ccd2_readout': {'pid': 345544329, 'idx': 19},
+          'charge_injection': {'pid': 345544346, 'idx': 20},
+          'tri_level_clk': {'pid': 345544371, 'idx': 21},
+          'img_clk_dir': {'pid': 345544361, 'idx': 22},
+          'reg_clk_dir': {'pid': 345544369, 'idx': 23},
+          'sync_sel': {'pid': 345544370, 'idx': 24},
+          'digitise_en': {'pid': 345544353, 'idx': 25},
+          'correction_bypass': {'pid': 345544351, 'idx': 26},
+          'dg_en': {'pid': 345544352, 'idx': 27},
+          'clear_full_sun_counters': {'pid': 345544350, 'idx': 28},
+          'edu_wandering_mask_en': {'pid': 345544354, 'idx': 29},
+          'ccd2_vod_config': {'pid': 345544331, 'idx': 30},
+          'ccd4_vod_config': {'pid': 345544337, 'idx': 31},
+          'ccd2_vrd_config': {'pid': 345544332, 'idx': 32},
+          'ccd4_vrd_config': {'pid': 345544338, 'idx': 33},
+          'ccd2_vgd_config': {'pid': 345544330, 'idx': 34},
+          'ccd4_vgd_config': {'pid': 345544336, 'idx': 35},
+          'ccd_vog_config': {'pid': 345544345, 'idx': 36},
+          'event_detection': {'pid': 345544355, 'idx': 37},
+          'clear_error_flag': {'pid': 345544349, 'idx': 38},
+          'event_pkt_limit': {'pid': 345544356, 'idx': 39},
+          'execute_op': {'pid': 345544357, 'idx': 40},
+          'ccd_mode_config': {'pid': 345544342, 'idx': 41},
+          'ccd_mode2_config': {'pid': 345544341, 'idx': 42},
+          'pix_offset': {'pid': 345544367, 'idx': 43}}
+
+# FEE RW registers (SMILE-MSSL-PL-Register_map_v0.20)
+
+FEE_CFG_REG_0 = 0x00000000
+FEE_CFG_REG_1 = 0x00000004
+FEE_CFG_REG_2 = 0x00000008
+FEE_CFG_REG_3 = 0x0000000C
+FEE_CFG_REG_4 = 0x00000010
+FEE_CFG_REG_5 = 0x00000014
+FEE_CFG_REG_6 = 0x00000018  # unused
+FEE_CFG_REG_7 = 0x0000001C  # unused
+FEE_CFG_REG_8 = 0x00000020  # unused
+FEE_CFG_REG_9 = 0x00000024  # unused
+FEE_CFG_REG_10 = 0x00000028  # unused
+FEE_CFG_REG_11 = 0x0000002C  # unused
+FEE_CFG_REG_12 = 0x00000030  # unused
+FEE_CFG_REG_13 = 0x00000034  # unused
+FEE_CFG_REG_14 = 0x00000038
+FEE_CFG_REG_15 = 0x0000003C
+FEE_CFG_REG_16 = 0x00000040
+FEE_CFG_REG_17 = 0x00000044
+FEE_CFG_REG_18 = 0x00000048
+FEE_CFG_REG_19 = 0x0000004C
+FEE_CFG_REG_20 = 0x00000050
+FEE_CFG_REG_21 = 0x00000054
+FEE_CFG_REG_22 = 0x00000058
+FEE_CFG_REG_23 = 0x0000005C
+FEE_CFG_REG_24 = 0x00000060
+FEE_CFG_REG_25 = 0x00000064
+FEE_CFG_REG_26 = 0x00000068
+
+# FEE  RO registers (SMILE-MSSL-PL-Register_map_v0.20)
+
+FEE_HK_REG_0 = 0x00000700  # reserved
+FEE_HK_REG_1 = 0x00000704  # reserved
+FEE_HK_REG_2 = 0x00000708  # reserved
+FEE_HK_REG_3 = 0x0000070C  # reserved
+FEE_HK_REG_4 = 0x00000710
+FEE_HK_REG_5 = 0x00000714
+FEE_HK_REG_6 = 0x00000718
+FEE_HK_REG_7 = 0x0000071C
+FEE_HK_REG_8 = 0x00000720
+FEE_HK_REG_9 = 0x00000724
+FEE_HK_REG_10 = 0x00000728
+FEE_HK_REG_11 = 0x0000072C
+FEE_HK_REG_12 = 0x00000730
+FEE_HK_REG_13 = 0x00000734
+FEE_HK_REG_14 = 0x00000738
+FEE_HK_REG_15 = 0x0000073C
+FEE_HK_REG_16 = 0x00000740
+FEE_HK_REG_17 = 0x00000744
+FEE_HK_REG_18 = 0x00000748
+FEE_HK_REG_19 = 0x0000074C
+FEE_HK_REG_20 = 0x00000750
+FEE_HK_REG_21 = 0x00000754
+FEE_HK_REG_22 = 0x00000758
+FEE_HK_REG_23 = 0x0000075C
+FEE_HK_REG_24 = 0x00000760  # reserved
+FEE_HK_REG_25 = 0x00000764  # reserved
+FEE_HK_REG_26 = 0x00000768  # reserved
+FEE_HK_REG_27 = 0x0000076C  # reserved
+FEE_HK_REG_28 = 0x00000770  # reserved
+FEE_HK_REG_29 = 0x00000774  # reserved
+FEE_HK_REG_30 = 0x00000778  # reserved
+FEE_HK_REG_31 = 0x0000077C  # reserved
+FEE_HK_REG_32 = 0x00000780
+FEE_HK_REG_33 = 0x00000784
+FEE_HK_REG_34 = 0x00000788
+FEE_HK_REG_35 = 0x0000078C
+FEE_HK_REG_36 = 0x00000790
+FEE_HK_REG_37 = 0x00000794
+
+# FEE modes
+# see MSSL-SMILE-SXI-IRD-0001  Draft A.14, req. MSSL-IF-17
+# also SMILE-MSSL-PL-Register_map_v0.22, as the IRD does not list all modes
+
+FEE_MODE_ID_ON = 0x0  # the thing is switched on
+FEE_MODE_ID_FTP = 0x1  # frame transfer pattern
+FEE_MODE_ID_STBY = 0x2  # stand-by-mode
+FEE_MODE_ID_FT = 0x3  # frame transfer
+FEE_MODE_ID_FF = 0x4  # full frame
+FEE_CMD__ID_IMM_ON = 0x8  # immediate on-mode, this is a command, not a mode
+FEE_MODE_ID_FFSIM = 0x9  # full frame simulation simulation
+FEE_MODE_ID_EVSIM = 0xA  # event detection simulation
+FEE_MODE_ID_PTP1 = 0xB  # parallel trap pump mode 1
+FEE_MODE_ID_PTP2 = 0xC  # parallel trap pump mode 2
+FEE_MODE_ID_STP1 = 0xD  # serial trap pump mode 1
+FEE_MODE_ID_STP2 = 0xE  # serial trap pump mode 2
+
+FEE_MODE2_NOBIN = 0x1  # no binning mode
+FEE_MODE2_BIN6 = 0x2  # 6x6 binning mode
+FEE_MODE2_BIN24 = 0x3  # 24x4 binning mode
+
+# these identifiy the bits in the readout node selection register
+FEE_READOUT_NODE_E2 = 0b0010
+FEE_READOUT_NODE_F2 = 0b0001
+FEE_READOUT_NODE_E4 = 0b1000
+FEE_READOUT_NODE_F4 = 0b0100
+
+# see MSSL-SMILE-SXI-IRD-0001 Draft A.14, req. MSSL-IF-108
+FEE_CCD_SIDE_F = 0x0  # left side
+FEE_CCD_SIDE_E = 0x1  # right side
+FEE_CCD_INTERLEAVED = 0x2  # F and E inverleaved
+
+FEE_CCD_ID_2 = 0x0
+FEE_CCD_ID_4 = 0x1
+
+FEE_PKT_TYPE_DATA = 0x0  # any data
+FEE_PKT_TYPE_EV_DET = 0x1  # event detection
+FEE_PKT_TYPE_HK = 0x2  # housekeeping
+FEE_PKT_TYPE_WMASK = 0x3  # wandering mask packet
+
+
+class RMapCommandWrite(RMapCommandHeader):
+    """This is intended for building an RMap Write Command"""
+
+    def __init__(self, addr, data, verify=True, reply=True, incr=True, key=SPW_FEE_KEY,
+                 initiator=SPW_DPU_LOGICAL_ADDRESS, tid=1, *args, **kwargs):
+        super(RMapCommandWrite, self).__init__(*args, **kwargs)
+
+        self.header = self.bits
+        self.data = data
+        self.data_crc = rmapcrc(self.data).to_bytes(RMAP_PEC_LEN, 'big')
+
+        self.bits.TARGET_LOGICAL_ADDR = SPW_FEE_LOGICAL_ADDRESS
+        self.bits.PROTOCOL_ID = SPW_PROTOCOL_IDS['RMAP']
+
+        self.bits.PKT_TYPE = 1
+        self.bits.WRITE = 1
+        self.bits.VERIFY = verify
+        self.bits.REPLY = reply
+        self.bits.INCREMENT = incr
+        self.bits.REPLY_ADDR_LEN = 0
+        self.bits.KEY = key
+
+        self.bits.INIT_LOGICAL_ADDR = initiator
+        self.bits.TRANSACTION_ID = tid
+        self.bits.EXT_ADDR = addr >> 32
+        self.bits.ADDR = addr & 0xFFFFFFFF
+        self.bits.DATA_LEN = len(self.data)
+        self.bits.HEADER_CRC = rmapcrc(bytes(self.bin[:-1]))
+
+    @property
+    def raw(self):
+        """Return raw packet with updated CRCs"""
+        self.bits.HEADER_CRC = rmapcrc(bytes(self.bin[:-1]))
+        self.data_crc = rmapcrc(self.data).to_bytes(RMAP_PEC_LEN, 'big')
+        return bytes(self.bin) + self.data + self.data_crc
+
+    @raw.setter
+    def raw(self, rawdata):
+        self.bin[:] = rawdata[:RMAP_COMMAND_HEADER_LEN]
+        self.data = rawdata[RMAP_COMMAND_HEADER_LEN:-RMAP_PEC_LEN]
+        self.data_crc = rawdata[-RMAP_PEC_LEN:]
+
+
+class RMapCommandRead(RMapCommandHeader):
+    """This is intended for building an RMap Read Command"""
+
+    def __init__(self, addr, datalen, incr=True, key=SPW_FEE_KEY, initiator=SPW_DPU_LOGICAL_ADDRESS, tid=1,
+                 *args, **kwargs):
+        super(RMapCommandRead, self).__init__(*args, **kwargs)
+
+        self.header = self.bits
+
+        self.bits.TARGET_LOGICAL_ADDR = SPW_FEE_LOGICAL_ADDRESS
+        self.bits.PROTOCOL_ID = SPW_PROTOCOL_IDS['RMAP']
+
+        self.bits.PKT_TYPE = 1
+        self.bits.WRITE = 0
+        self.bits.VERIFY = 0
+        self.bits.REPLY = 1
+        self.bits.INCREMENT = incr
+        self.bits.REPLY_ADDR_LEN = 0
+        self.bits.KEY = key
+
+        self.bits.INIT_LOGICAL_ADDR = initiator
+        self.bits.TRANSACTION_ID = tid
+        self.bits.EXT_ADDR = addr >> 32
+        self.bits.ADDR = addr
+        self.bits.DATA_LEN = datalen
+        self.bits.HEADER_CRC = rmapcrc(bytes(self.bin[:-1]))
+
+    @property
+    def raw(self):
+        """Return raw packet with updated CRCs"""
+        self.bits.HEADER_CRC = rmapcrc(bytes(self.bin[:-1]))
+        return bytes(self.bin)
+
+    @raw.setter
+    def raw(self, rawdata):
+        self.bin[:] = rawdata[:RMAP_COMMAND_HEADER_LEN]
+
+
+class FeeDataTransfer(FeeDataTransferHeader):
+    """
+    Bytes 4 and 5 of the data-packet-header contains additional information about the packet-content. The type-field is defined in the following way:
+    - bits 15:12 = reserved for future usage
+    - bits 11:8 = See MSSL-IF-17
+    - bit 7 = last packet: 1 = last packet of this type in the current read-out-cycle
+    - bit 6:5 = CCD side: 0 = left side (side F), 1 = right side (side E), 2 = F&E   interleaved
+    - bit 4 = CCD: 0 = CCD2, 1= CCD4
+    - bit 3:2 = reserved
+    - bits 1:0 = packet type: 0 = data packet, 1 = Event detection packet, 2 = housekeeping packet
+    """
+
+    _modes = {FEE_MODE_ID_ON: "On Mode",
+              FEE_MODE_ID_FTP: "Frame Transfer Pattern",
+              FEE_MODE_ID_STBY: "Stand-By-Mode",
+              FEE_MODE_ID_FT: "Frame Transfer",
+              FEE_MODE_ID_FF: "Full Frame",
+              FEE_MODE_ID_FFSIM: "Full frame simulation",
+              FEE_MODE_ID_EVSIM: "Event detection simulation",
+              FEE_MODE_ID_PTP1: "Parallel trap pumping mode 1",
+              FEE_MODE_ID_PTP2: "Parallel trap pumping mode 2",
+              FEE_MODE_ID_STP1: "Serial trap pumping mode 1",
+              FEE_MODE_ID_STP2: "Serial trap pumping mode 2"}
+    _ccd_sides = {FEE_CCD_SIDE_F: "left side (F)",
+                  FEE_CCD_SIDE_E: "right side (E)",
+                  FEE_CCD_INTERLEAVED: "F&E interleaved"}
+    _ccds = {FEE_CCD_ID_2: "CCD2",
+             FEE_CCD_ID_4: "CCD4"}
+    _pkt_types = {FEE_PKT_TYPE_DATA: "Data",
+                  FEE_PKT_TYPE_EV_DET: "Event detection",
+                  FEE_PKT_TYPE_HK: "Housekeeping",
+                  FEE_PKT_TYPE_WMASK: "Wandering mask"}
+
+    _DATA_HK_STRUCT = []
+
+    def __init__(self, pkt=None):
+        super(FeeDataTransfer, self).__init__()
+
+        if pkt is not None:
+            self._raw = pkt
+            self.bin[:] = self._raw[:FEE_DATA_TRANSFER_HEADER_LEN]
+            self.data = self._raw[FEE_DATA_TRANSFER_HEADER_LEN:]
+
+            self.set_evt_data()
+
+        else:
+            self._raw = b''
+            self.set_evt_data()
+
+        self.set_type_details()
+
+    @property
+    def raw(self):
+        return self._raw
+
+    @raw.setter
+    def raw(self, rawdata):
+        self.bin[:] = rawdata[:FEE_DATA_TRANSFER_HEADER_LEN]
+        self.data = rawdata[FEE_DATA_TRANSFER_HEADER_LEN:]
+        self._raw = rawdata
+        self.set_type_details()
+        self.set_evt_data()
+
+    # @property
+    def info(self):
+        head = 'HEADER\n' + '\n'.join(['{}:\t{}'.format(key, self.type_details[key]) for key in self.type_details])
+        if self.evt_data is not None:
+            data = 'DATA\ncolumn: {}, row: {}\n\n{}'.format(self.evt_data['COLUMN'], self.evt_data['ROW'],
+                                                            str(self.evt_data['IMAGE']).replace('[', ' ').replace(']',
+                                                                                                                  ' '))
+        else:
+            data = 'DATA\n' + self.data.hex().upper()
+
+        # return head + '\n' + data
+        print(head + '\n' + data)
+
+    def set_type_details(self):
+        self.type_details = {"MODE": self._modes[self.bits.MODE] if self.bits.MODE in self._modes else self.bits.MODE,
+                             "LAST_PKT": bool(self.bits.LAST_PKT),
+                             "CCDSIDE": self._ccd_sides[
+                                 self.bits.CCDSIDE] if self.bits.CCDSIDE in self._ccd_sides else self.bits.CCDSIDE,
+                             "CCD": self._ccds[self.bits.CCD] if self.bits.CCD in self._ccds else self.bits.CCD,
+                             "PKT_TYPE": self._pkt_types[
+                                 self.bits.PKT_TYPE] if self.bits.PKT_TYPE in self._pkt_types else self.bits.PKT_TYPE}
+
+    def set_evt_data(self):
+        if self.bits.PKT_TYPE == FEE_PKT_TYPE_EV_DET:
+            evtdata = EventDetectionData()
+            evtdata.bin[:] = self.data
+            self.evt_data = {"COLUMN": evtdata.bits.column,
+                             "ROW": evtdata.bits.row,
+                             "IMAGE": np.array(evtdata.bits.array)[
+                                      ::-1]}  # structure according to MSSL-SMILE-SXI-IRD-0001
+        else:
+            self.evt_data = None
+
+
+class EventDetectionFields(ctypes.BigEndianStructure):
+    _pack_ = 1
+    _fields_ = [
+        ("column", ctypes.c_uint16),
+        ("row", ctypes.c_uint16),
+        ("array", (ctypes.c_uint16 * 5) * 5)
+    ]
+
+
+class EventDetectionData(ctypes.Union):
+    _pack_ = 1
+    _fields_ = [
+        ("bits", EventDetectionFields),
+        ("bin", ctypes.c_ubyte * ctypes.sizeof(EventDetectionFields))
+    ]