diff --git a/comIF/obsw_dummy_com_if.py b/comIF/obsw_dummy_com_if.py index ddc36f8e89fe443f59371a75715e1b88436d8290..6730a5ba07bfc291e9b35860bbc60964fb7a24fe 100644 --- a/comIF/obsw_dummy_com_if.py +++ b/comIF/obsw_dummy_com_if.py @@ -39,8 +39,6 @@ class DummyComIF(CommunicationInterface): return [tm_packet] return [] - - def send_telecommand(self, tc_packet: PusTelecommand, tc_packet_info: PusTcInfoT = None) -> None: if isinstance(tc_packet_info, dict) and tc_packet_info.__len__() > 0: self.service_sent = tc_packet_info[TcDictionaryKeys.SERVICE] diff --git a/comIF/obsw_serial_com_if.py b/comIF/obsw_serial_com_if.py index c718b5da00ca893c7c6711552be554e0fb383f24..14617c58cfd4469c6d6d221d9811b6ed063cb219 100644 --- a/comIF/obsw_serial_com_if.py +++ b/comIF/obsw_serial_com_if.py @@ -50,7 +50,7 @@ class SerialComIF(CommunicationInterface): # self.tmtc_printer.print_telecommand(tc_packet, tc_packet_info) self.serial.write(tc_packet) - def receive_telemetry(self, parameters: any = 0) -> list: + def receive_telemetry(self, parameters: any = 0) -> PusTmListT: (packet_received, packet_list) = self.poll_interface() if packet_received: return packet_list diff --git a/sendreceive/obsw_command_sender_receiver.py b/sendreceive/obsw_command_sender_receiver.py index d800fd78212a20682317c07dd1103cc78ca47c2d..8824782260adefd7c421bc7cf8672f145b3421d2 100644 --- a/sendreceive/obsw_command_sender_receiver.py +++ b/sendreceive/obsw_command_sender_receiver.py @@ -11,8 +11,8 @@ if the first reply has not been received. @author: R. Mueller """ -import sys import time +import logging import config.obsw_config as g from comIF.obsw_com_interface import CommunicationInterface @@ -21,7 +21,7 @@ from sendreceive.obsw_tm_listener import TmListener from tc.obsw_pus_tc_base import TcQueueEntryT from tm.obsw_pus_tm_factory import PusTmQueueT from utility.obsw_logger import get_logger -from tm.obsw_pus_tm_factory import PusTelemetryFactory + logger = get_logger() @@ -150,7 +150,11 @@ class CommandSenderReceiver: time.sleep(0.5) def print_tm_queue(self, tm_queue: PusTmQueueT): - for tm_packet in tm_queue: - logger.debug(str(tm_packet)) - tm_info = tm_packet.pack_tm_information() - self._tmtc_printer.print_telemetry(tm_packet, tm_info) + for tm_packet_list in tm_queue: + try: + for tm_packet in tm_packet_list: + self._tmtc_printer.print_telemetry(tm_packet) + except AttributeError as e: + logger.exception( + "CommandSenderReceiver Exception: Invalid queue entry. Traceback:", e) + diff --git a/sendreceive/obsw_tm_listener.py b/sendreceive/obsw_tm_listener.py index f0177702d66f5c7dc9eaa6be13b8fd3ca5f273a0..2020545c1859eb20c85aa3fee9000c0ce0d16bca 100644 --- a/sendreceive/obsw_tm_listener.py +++ b/sendreceive/obsw_tm_listener.py @@ -80,7 +80,7 @@ class TmListener: while not self.mode_op_finished.is_set(): self.perform_mode_operation() self.mode_op_finished.clear() - logger.info("Transitioning to listener mode.") + logger.info("TmListener: Transitioning to listener mode.") self.mode_id = g.ModeList.ListenerMode def perform_mode_operation(self): @@ -137,15 +137,17 @@ class TmListener: logger.error("TmListener: Configuration error in communication interface!") sys.exit() - def retrieve_tm_packet_queue(self): + def retrieve_tm_packet_queue(self) -> PusTmQueueT: return self.__tm_packet_queue.copy() @staticmethod - def retrieve_info_queue_from_packet_queue(tm_queue: PusTmQueueT, pus_info_queue_to_fill: PusTmInfoQueueT): + def retrieve_info_queue_from_packet_queue( + tm_queue: PusTmQueueT, pus_info_queue_to_fill: PusTmInfoQueueT): tm_queue_copy = tm_queue.copy() while tm_queue_copy.__len__() != 0: - pus_packet = tm_queue_copy.pop() - pus_info_queue_to_fill.append(pus_packet.pack_tm_information()) + pus_packet_list = tm_queue_copy.pop() + for pus_packet in pus_packet_list: + pus_info_queue_to_fill.append(pus_packet.pack_tm_information()) def clear_tm_packet_queue(self): self.__tm_packet_queue.clear() diff --git a/tm/obsw_pus_tm_base.py b/tm/obsw_pus_tm_base.py index 691675804acb42af16024f2303328da409500672..609b447a0c9987dbb4c98bc1ac2223ded6c48237 100644 --- a/tm/obsw_pus_tm_base.py +++ b/tm/obsw_pus_tm_base.py @@ -41,17 +41,25 @@ class PusTelemetry: PUS Telemetry structure according to ECSS-E-70-41A p.46. Also see structure below (bottom). """ PUS_HEADER_SIZE = 6 - def __init__(self, byte_array: bytearray = bytearray()): - if byte_array == bytearray(): + CDS_SHORT_SIZE = 7 + PUS_TIMESTAMP_SIZE = CDS_SHORT_SIZE + def __init__(self, raw_telemetry: bytearray = bytearray()): + if raw_telemetry == bytearray(): return - self._packet_raw: Final = byte_array - self._pus_header: Final = PusPacketHeader(byte_array) - byte_array = byte_array[6:] - self._data_field_header: Final = PusPacketDataFieldHeader(byte_array) - byte_array = byte_array[12:] - self._tm_data: Final = byte_array[:len(byte_array) - 2] - self._crc: Final = byte_array[len(byte_array) - 2] << 8 | byte_array[len(byte_array) - 1] + self._packet_raw: Final = raw_telemetry + self._pus_header: Final = PusPacketHeader(raw_telemetry) + self._valid = False + if self._pus_header.length + PusTelemetry.PUS_HEADER_SIZE + 1 > len(raw_telemetry): + logger.error("PusTelemetry: Passed packet shorter than specified packet " + "length in PUS header") + return + self._data_field_header: Final = PusPacketDataFieldHeader(raw_telemetry[6:]) + self._tm_data: Final = raw_telemetry[PusPacketDataFieldHeader.DATA_HEADER_SIZE + + PusTelemetry.PUS_HEADER_SIZE:len(raw_telemetry) - 2] + self._crc: Final = raw_telemetry[len(raw_telemetry) - 2] << 8 | \ + raw_telemetry[len(raw_telemetry) - 1] self.print_info = "" + self.__perform_crc_check(raw_telemetry) def get_service(self): """ @@ -65,6 +73,9 @@ class PusTelemetry: """ return self._data_field_header.service_subtype + def is_valid(self): + return self._valid + def get_tm_data(self) -> bytearray: """ :return: TM application data (raw) @@ -82,10 +93,22 @@ class PusTelemetry: TmDictionaryKeys.SSC: self.get_ssc(), TmDictionaryKeys.DATA: self._tm_data, TmDictionaryKeys.CRC: self._crc, - TmDictionaryKeys.VALID: self._pus_header.valid + TmDictionaryKeys.VALID: self._valid } return tm_information + def __perform_crc_check(self, raw_telemetry: bytearray): + crc_func = crcmod.mkCrcFun(0x11021, rev=False, initCrc=0xFFFF, xorOut=0x0000) + if len(raw_telemetry) < self.get_packet_size(): + logger.warning("PusPacketHeader: Invalid packet length") + return + data_to_check = raw_telemetry[0:self.get_packet_size()] + crc = crc_func(data_to_check) + if crc == 0: + self._valid = True + else: + logger.warning("PusPacketHeader: Invalid CRC detected !") + def specify_packet_info(self, print_info: str): """ Caches a print information string for later printing @@ -112,6 +135,10 @@ class PusTelemetry: """ self._data_field_header.append_data_field_header(content_list) self._pus_header.append_pus_packet_header(content_list) + if self.is_valid(): + content_list.append("Yes") + else: + content_list.append("No") def append_telemetry_column_headers(self, header_list: list): """ @@ -124,6 +151,7 @@ class PusTelemetry: """ self._data_field_header.append_data_field_header_column_header(header_list) self._pus_header.append_pus_packet_header_column_headers(header_list) + header_list.append("Packet valid") def get_raw_packet(self) -> bytes: """ @@ -147,26 +175,37 @@ class PusTelemetry: """ return self._pus_header.source_sequence_count - def return_data_string(self) -> str: + def print_full_packet_string(self): + """ + Print the full TM packet in a clean format. + """ + + logger.info(self.return_data_string(self._packet_raw, len(self._packet_raw))) + + def print_source_data(self): + """ + Prints the TM source data in a clean format + :return: + """ + logger.info(self.return_data_string(self._tm_data, len(self._tm_data))) + + @staticmethod + def return_data_string(byte_array: bytearray, length: int) -> str: """ Returns the TM data in a clean printable string format + Prints payload data in default mode + and prints the whole packet if full_packet = True is passed. :return: """ str_to_print = "[" - for byte in self._tm_data: - str_to_print += str(hex(byte)) + " , " + for index in range(length): + str_to_print += str(hex(byte_array[index])) + " , " str_to_print = str_to_print.rstrip() str_to_print = str_to_print.rstrip(',') + str_to_print = str_to_print.rstrip() str_to_print += ']' return str_to_print - def print_data(self): - """ - Prints the TM data in a clean format - :return: - """ - print(self.return_data_string()) - # pylint: disable=too-many-instance-attributes class PusPacketHeader: @@ -175,8 +214,8 @@ class PusPacketHeader: """ def __init__(self, pus_packet_raw: bytes): - data_to_check = pus_packet_raw if len(pus_packet_raw) < PusTelemetry.PUS_HEADER_SIZE: + logger.warning("PusPacketHeader: Packet size smaller than PUS header size!") self.version = 0 self.type = 0 self.data_field_header_flag = 0 @@ -184,7 +223,6 @@ class PusPacketHeader: self.segmentation_flag = 0 self.source_sequence_count = 0 self.length = 0 - self.valid = False return self.version = pus_packet_raw[0] >> 5 self.type = pus_packet_raw[0] & 0x10 @@ -193,38 +231,23 @@ class PusPacketHeader: self.segmentation_flag = (pus_packet_raw[2] & 0xC0) >> 6 self.source_sequence_count = ((pus_packet_raw[2] & 0x3F) << 8) | pus_packet_raw[3] self.length = pus_packet_raw[4] << 8 | pus_packet_raw[5] - self.valid = False - crc_func = crcmod.mkCrcFun(0x11021, rev=False, initCrc=0xFFFF, xorOut=0x0000) - if len(data_to_check) < ((self.length + 1) + PusTelemetry.PUS_HEADER_SIZE): - logger.warning("PusPacketHeader: Invalid packet length") - return - data_to_check = data_to_check[0:(self.length + 1) + PusTelemetry.PUS_HEADER_SIZE] - crc = crc_func(data_to_check) - if crc == 0: - self.valid = True - else: - logger.warning("PusPacketHeader: Invalid CRC detected !") def append_pus_packet_header(self, array): array.append(str(chr(self.apid))) array.append(str(self.source_sequence_count)) - if self.valid: - array.append("Yes") - else: - array.append("No") @staticmethod def append_pus_packet_header_column_headers(array): array.append("APID") array.append("SSC") - array.append("Packet Valid") class PusPacketDataFieldHeader: """ Unpacks the PUS packet data field header. """ - def __init__(self, bytes_array): + DATA_HEADER_SIZE = PusTelemetry.PUS_TIMESTAMP_SIZE + 4 + def __init__(self, bytes_array: bytearray): self.pus_version_and_ack_byte = (bytes_array[0] & 0x70) >> 4 self.service_type = bytes_array[1] self.service_subtype = bytes_array[2] @@ -254,6 +277,7 @@ class PusPacketDataFieldHeader: self.time.print_time_headers(header_list) + class PusTelemetryTimestamp: """ Unpacks the time datafield of the TM packet. Right now, CDS Short timeformat is used, @@ -263,6 +287,7 @@ class PusTelemetryTimestamp: SECONDS_PER_DAY = 86400 EPOCH = datetime.datetime.utcfromtimestamp(0) DAYS_CCSDS_TO_UNIX = 4383 + TIMESTAMP_SIZE = PusTelemetry.PUS_TIMESTAMP_SIZE def __init__(self, byte_array: bytearray=bytearray([])): if len(byte_array) > 0: # pField = byte_array[0] @@ -276,7 +301,8 @@ class PusTelemetryTimestamp: self.datetime = str(datetime.datetime. utcfromtimestamp(self.time).strftime("%Y-%m-%d %H:%M:%S.%f")) - def pack_current_time(self) -> bytearray: + @staticmethod + def pack_current_time() -> bytearray: """ Returns a seven byte CDS short timestamp """ diff --git a/tm/obsw_pus_tm_creator.py b/tm/obsw_pus_tm_creator.py index fad430151e60b8ff05159e5d22a4900449b748fe..1f768b269cd14be176b5a1e159ac3e7f2736a8ea 100644 --- a/tm/obsw_pus_tm_creator.py +++ b/tm/obsw_pus_tm_creator.py @@ -47,38 +47,31 @@ class PusTelemetryCreator(PusTelemetry): source_length = self.get_source_data_length() tm_packet_raw.append((source_length & 0xFF00) >> 8) tm_packet_raw.append(source_length & 0xFF) - logger.debug(len(tm_packet_raw)) # PUS Source Data Field tm_packet_raw.append(self.data_field_version) tm_packet_raw.append(self.service) tm_packet_raw.append(self.subservice) tm_packet_raw.append(self.pack_subcounter) - logger.debug(len(tm_packet_raw)) - timestamper = PusTelemetryTimestamp() - tm_packet_raw.extend(timestamper.pack_current_time()) - logger.debug(len(tm_packet_raw)) - + tm_packet_raw.extend(PusTelemetryTimestamp.pack_current_time()) # Source Data tm_packet_raw.extend(self.source_data) - logger.debug(len(tm_packet_raw)) - # CRC16 checksum crc_func = crcmod.mkCrcFun(0x11021, rev=False, initCrc=0xFFFF, xorOut=0x0000) crc16 = crc_func(tm_packet_raw) tm_packet_raw.append((crc16 & 0xFF00) >> 8) tm_packet_raw.append(crc16 & 0xFF) - logger.debug(len(tm_packet_raw)) return tm_packet_raw def get_source_data_length(self) -> int: """ - Retrieve size of TC packet in bytes. - Formula according to PUS Standard: C = (Number of octets in packet data field) - 1. - The size of the TC packet is the size of the packet secondary header with - source ID + the length of the application data + length of the CRC16 checksum - 1 + Retrieve size of TM packet data header in bytes. + Formula according to PUS Standard: C = (Number of octets in packet source data field) - 1. + The size of the TM packet is the size of the packet secondary header with + the timestamp + the length of the application data + PUS timestamp size + + length of the CRC16 checksum - 1 """ try: - data_length = 4 + len(self.source_data) + 1 + data_length = 4 + PusTelemetry.PUS_TIMESTAMP_SIZE + len(self.source_data) + 1 return data_length except TypeError: logger.error("PusTelecommand: Invalid type of application data!") diff --git a/tm/obsw_pus_tm_factory.py b/tm/obsw_pus_tm_factory.py index 5e99f6a38ef35b86db0ed70d8ce8350e72d566a1..f6b9be1758556e93072c073790a71bca8ad6027a 100644 --- a/tm/obsw_pus_tm_factory.py +++ b/tm/obsw_pus_tm_factory.py @@ -10,9 +10,11 @@ import struct logger = get_logger() PusRawTmList = List[bytearray] PusRawTmQueue = Deque[bytearray] -PusTmQueueT = Deque[PusTelemetry] -PusTmListT = List[PusTelemetry] PusTmTupleT = Tuple[PusTmInfoT, PusTelemetry] + +PusTmListT = List[PusTelemetry] +PusTmQueueT = Deque[PusTmListT] + PusTmInfoQueueT = Deque[PusTmInfoT] PusTmTupleQueueT = Deque[PusTmTupleT] diff --git a/tm/obsw_tm_service_1.py b/tm/obsw_tm_service_1.py index c7908285e49d5b8ee01239304999b1a14cbc81e3..952e6313dd7bc0e01a0286e53ab43158f395bcd9 100644 --- a/tm/obsw_tm_service_1.py +++ b/tm/obsw_tm_service_1.py @@ -36,7 +36,7 @@ class Service1TM(PusTelemetry): self.errorParam1 = struct.unpack('>I', self._tm_data[7:11])[0] self.errorParam2 = struct.unpack('>I', self._tm_data[11:15])[0] else: - self.print_data() + self.print_source_data() self.err_code = struct.unpack('>H', self._tm_data[4:6])[0] self.errorParam1 = struct.unpack('>I', self._tm_data[6:10])[0] self.errorParam2 = struct.unpack('>I', self._tm_data[10:14])[0] diff --git a/utility/obsw_tmtc_printer.py b/utility/obsw_tmtc_printer.py index 6e8a972a1ff59380849ca343568df29f8ff17d68..7b0117e923087512e79184d8f6b8018a6268a757 100644 --- a/utility/obsw_tmtc_printer.py +++ b/utility/obsw_tmtc_printer.py @@ -11,7 +11,6 @@ import os import sys import enum -import logging from config import obsw_config as g from tm.obsw_pus_tm_base import PusTelemetry from tm.obsw_tm_service_3 import Service3TM @@ -78,7 +77,7 @@ class TmTcPrinter: def __handle_long_print(self, tm_packet: PusTelemetry): self.print_buffer = "Received Telemetry: " + tm_packet.print_info - print(self.print_buffer) + logger.info(self.print_buffer) self.add_print_buffer_to_file_buffer() self.__handle_column_header_print(tm_packet) self.__handle_tm_content_print(tm_packet) @@ -87,7 +86,7 @@ class TmTcPrinter: rec_pus = [] tm_packet.append_telemetry_column_headers(rec_pus) self.print_buffer = str(rec_pus) - print(self.print_buffer) + logger.info(self.print_buffer) self.add_print_buffer_to_file_buffer() def __handle_tm_content_print(self, tm_packet: PusTelemetry): @@ -98,7 +97,7 @@ class TmTcPrinter: rec_pus = [] tm_packet.append_telemetry_content(rec_pus) self.print_buffer = str(rec_pus) - print(self.print_buffer) + logger.info(self.print_buffer) self.add_print_buffer_to_file_buffer() def __handle_hk_print(self, tm_packet: Service3TM):