diff --git a/imap_processing/codice/CcsdsHeaderXtceGenerator.py b/imap_processing/codice/CcsdsHeaderXtceGenerator.py new file mode 100644 index 000000000..8e5abfc8a --- /dev/null +++ b/imap_processing/codice/CcsdsHeaderXtceGenerator.py @@ -0,0 +1,47 @@ +class CCSDSParameters: + def __init__(self): + self.parameters = [ + { + "name": "VERSION", + "parameterTypeRef": "uint3", + "description": "CCSDS Packet Version Number (always 0)", + }, + { + "name": "TYPE", + "parameterTypeRef": "uint1", + "description": "CCSDS Packet Type Indicator (0=telemetry)", + }, + { + "name": "SEC_HDR_FLG", + "parameterTypeRef": "uint1", + "description": "CCSDS Packet Secondary Header Flag (always 1)", + }, + { + "name": "PKT_APID", + "parameterTypeRef": "uint11", + "description": "CCSDS Packet Application Process ID", + }, + { + "name": "SEG_FLGS", + "parameterTypeRef": "uint2", + "description": "CCSDS Packet Grouping Flags (3=not part of group)", + }, + { + "name": "SRC_SEQ_CTR", + "parameterTypeRef": "uint14", + "description": "CCSDS Packet Sequence Count (increments with each new packet)", + }, + { + "name": "PKT_LEN", + "parameterTypeRef": "uint16", + "description": "CCSDS Packet Length (number of bytes after Packet length minus 1)", + }, + { + "name": "SHCOARSE", + "parameterTypeRef": "uint32", + "description": "CCSDS Packet Time Stamp (coarse time)", + }, + ] + + +# Other utility functions related to CCSDS parameters can also be added here diff --git a/imap_processing/codice/L0/ccsds-header.xml b/imap_processing/codice/L0/ccsds-header.xml new file mode 100644 index 000000000..6c9ae64f7 --- /dev/null +++ b/imap_processing/codice/L0/ccsds-header.xml @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CCSDS Packet Version Number (always 0) + + + CCSDS Packet Type Indicator (0=telemetry) + + + CCSDS Packet Secondary Header Flag (always 1) + + + CCSDS Packet Application Process ID + + + CCSDS Packet Grouping Flags (3=not part of group) + + + CCSDS Packet Sequence Count (increments with each new packet) + + + CCSDS Packet Length (number of bytes after Packet length minus 1) + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/imap_processing/codice/L0/decom_python_example.py b/imap_processing/codice/L0/decom_python_example.py new file mode 100644 index 000000000..cf6d2f88f --- /dev/null +++ b/imap_processing/codice/L0/decom_python_example.py @@ -0,0 +1,237 @@ +""" +Main script to extract and decommute parameters from a binary file containing packets. + +This script reads a binary file containing packet data, searches for a packet with a specific Application Process Identifier (APID), +and then decommutes the packet's parameters using a provided decommutation table. The extracted parameter values are printed. + +Usage: + 1. Set the 'bin_file_path' variable to the path of the binary file containing packet data. + 2. Replace 'target_apid' with the desired APID to search for. + 3. Define the 'decomm_table' with the decommutation information for different parameters. + 4. Run the script to extract and print decommuted parameter values. + +Example: + Assuming 'read_binary_file' and other functions are defined: + - Given a binary file at 'bin_file_path' and a desired 'target_apid': + - If a packet with the 'target_apid' is found, its parameters are extracted and printed. + - If no matching packet is found, a message indicating such is printed. +""" + +""" +Parameters +---------- +bin_file_path : str + Path to the binary file containing packet data. + target_apid : int + APID of the packet to search for. + decomm_table : list + List of dictionaries, each containing decommutation information for a parameter. + Each dictionary should contain: + - "mnemonic": A unique identifier for the parameter. + - "sequence": An optional parameter sequence number. + - "startByte": Starting byte index in the packet. + - "startBitInByte": Starting bit index within the starting byte. + - "startBit": Overall starting bit index in the packet. + - "lengthInBits": Number of bits to extract for this parameter. + - "dataType": Data type of the parameter, e.g., "unsigned_int", "float", etc. + """ + + +def read_binary_file(file_path): + with open(file_path, "rb") as file: + data = file.read() + return data + + +""" +Extracts a value from binary data by interpreting a specified range of bits. + +This function is used to extract a value from a sequence of binary data by specifying the starting bit position and the number of bits to consider. The bits are interpreted as an unsigned integer value. + +Parameters +---------- + data (bytes): The binary data from which the value will be extracted. + start_bit (int): The index of the starting bit for extraction. + length (int): The number of bits to extract. + +Returns +------- + int: The extracted value represented as an integer. + +""" + + +def extract_bits(data, start_bit, length): + byte_offset = start_bit // 8 + bit_shift = start_bit % 8 + mask = (1 << length) - 1 + + value = 0 + for i in range(length): + byte = data[byte_offset + i] + value |= (byte >> bit_shift) << (i * 8) + + return value & mask + + +""" +Finds the index of the first occurrence of a packet with a specific APID in binary data. + +This function searches through a sequence of binary data to find the index of the first packet that matches the specified Application Process Identifier (APID). The APID is a unique identifier used in packet-based data communication protocols. + +Parameters +---------- + bin_data (bytes): The binary data to search within. + target_apid (int): The target APID to search for. + +Returns +------- + int: The index of the first occurrence of the packet with the specified APID, or -1 if not found. + +Example: + binary_data = bytes([0x12, 0x34, 0x56, 0x12, 0x78, 0x90]) # Example binary data + target_apid = 0x1234 # Example target APID + packet_index = find_packet_with_apid(binary_data, target_apid) +""" + + +def find_packet_with_apid(bin_data, target_apid): + # Search for the target APID in the binary data + target_apid_bytes = target_apid.to_bytes(2, byteorder="big") + idx = bin_data.find(target_apid_bytes) + + return idx + + +""" +Decommutes packet data using a provided decommutation table. + +This function takes a packet's binary data and a decommutation table as input, and returns a dictionary of parameter values extracted from the packet according to the table. + +Parameters +---------- + packet_data (bytes): Binary data of the packet to decommute. + decomm_table (list): List of dictionaries, each containing decommutation information for a parameter. + Each dictionary should contain: + - "mnemonic": A unique identifier for the parameter. + - "sequence": An optional parameter sequence number. + - "startByte": Starting byte index in the packet. + - "startBitInByte": Starting bit index within the starting byte. + - "startBit": Overall starting bit index in the packet. + - "lengthInBits": Number of bits to extract for this parameter. + - "dataType": Data type of the parameter, e.g., "unsigned_int", "float", etc. + +""" + + +# Decommutation table variables may not all be the same +def decommute_packet(packet_data, decomm_table): + parameters = {} + + for entry in decomm_table: + mnemonic = entry["mnemonic"] + entry["sequence"] + entry["startByte"] + entry["startBitInByte"] + start_bit = entry["startBit"] + length_in_bits = entry["lengthInBits"] + entry["dataType"] + + value = extract_bits(packet_data, start_bit, length_in_bits) + parameters[mnemonic] = value + + return parameters + + +if __name__ == "__main__": + bin_file_path = "../RAW.bin" + target_apid = 0x460 # Replace with the APID of the desired packet + decomm_table = [ + { + "mnemonic": "SHCOARSE", + "sequence": 7, + "startByte": 6, + "startBitInByte": 0, + "startBit": 48, + "lengthInBits": 32, + "dataType": "UINT", + }, + { + "mnemonic": "Spare", + "sequence": 8, + "startByte": 10, + "startBitInByte": 0, + "startBit": 80, + "lengthInBits": 6, + "dataType": "UINT", + }, + { + "mnemonic": "Power_Cycle_Rq", + "sequence": 9, + "startByte": 10, + "startBitInByte": 6, + "startBit": 86, + "lengthInBits": 1, + "dataType": "UINT", + }, + { + "mnemonic": "Power_Off_Rq", + "sequence": 10, + "startByte": 10, + "startBitInByte": 7, + "startBit": 87, + "lengthInBits": 1, + "dataType": "UINT", + }, + { + "mnemonic": "Heater_Control_Enabled", + "sequence": 11, + "startByte": 11, + "startBitInByte": 0, + "startBit": 88, + "lengthInBits": 1, + "dataType": "UINT", + }, + { + "mnemonic": "Heater_1_State", + "sequence": 12, + "startByte": 11, + "startBitInByte": 1, + "startBit": 89, + "lengthInBits": 1, + "dataType": "UINT", + }, + { + "mnemonic": "Heater_2_State", + "sequence": 13, + "startByte": 11, + "startBitInByte": 2, + "startBit": 90, + "lengthInBits": 1, + "dataType": "UINT", + }, + { + "mnemonic": "Spare2", + "sequence": 14, + "startByte": 11, + "startBitInByte": 3, + "startBit": 91, + "lengthInBits": 5, + "dataType": "UINT", + }, + ] + + data = read_binary_file(bin_file_path) + idx = find_packet_with_apid(data, target_apid) + + if idx >= 0: + packet_data = data[idx:] + parameters = decommute_packet(packet_data, decomm_table) + print("Decommuted Parameters of Apid:") + for mnemonic, value in parameters.items(): + print(f"{mnemonic}: {value}") + else: + print("Packet with APID not found in the binary data.") + +# FileNotFoundError: If the specified file does not exist. +# IOError: If an error occurs while reading the file. diff --git a/imap_processing/codice/L0/p_cod_aut_test.xml b/imap_processing/codice/L0/p_cod_aut_test.xml new file mode 100644 index 000000000..41121aaa2 --- /dev/null +++ b/imap_processing/codice/L0/p_cod_aut_test.xml @@ -0,0 +1,217 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + CCSDS Packet Version Number (always 0) + + + CCSDS Packet Type Indicator (0=telemetry) + + + CCSDS Packet Secondary Header Flag (always 1) + + + CCSDS Packet Application Process ID + + + CCSDS Packet Grouping Flags (3=not part of group) + + + CCSDS Packet Sequence Count (increments with each new packet) + + + CCSDS Packet Length (number of bytes after Packet length minus 1) + + + CCSDS Packet Time Stamp (coarse time) + + + Spare - required per GI-ICD + + + Power Cycle Request + + + Power Off Request + + + Specifies whether the FSW bang-bang controller for the heaters is enabled. If the controller is not enabled, both heaters should be OFF. If control is enabled, heater outputs will vary as the control algorithm determines when to turn them ON or OFF. + + + Current ON/OFF state of the heater 1 output + + + Current ON/OFF state of the heater 2 output + + + Spare for alignment + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/imap_processing/codice/RAW.bin b/imap_processing/codice/RAW.bin new file mode 100644 index 000000000..7a02a6d94 Binary files /dev/null and b/imap_processing/codice/RAW.bin differ diff --git a/imap_processing/codice/__init__.py b/imap_processing/codice/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/imap_processing/codice/decom.py b/imap_processing/codice/decom.py new file mode 100644 index 000000000..043908740 --- /dev/null +++ b/imap_processing/codice/decom.py @@ -0,0 +1,29 @@ +""" This is an example of how to use the 'space_packet_parser' module to parse with a XTCE file specifically for the + CODICE L0 data. This is a modified version of the example found in the 'space_packet_parser' module documentation. + This is the start of CODICE L0 data processing. + +""" + +from pathlib import Path + +from space_packet_parser import parser, xtcedef + +# Define the APID. This is the APID for the CODICE L0 data that is in the 'RAW.bin' file. +# Data bins like 'RAW.bin' will encompass multiple APIDs. This is why we need to specify the APID. +# The APID should be in the packet definition file given by instrument team. +apid = 0x460 + +# Define paths +packet_file = Path("RAW.bin") +xtce_document = Path("L0/p_cod_aut_test.xml") + +packet_definition = xtcedef.XtcePacketDefinition(xtce_document) +my_parser = parser.PacketParser(packet_definition, apid) + +with packet_file.open("rb") as binary_data: + packet_generator = my_parser.generator(binary_data) + + for packet in packet_generator: + # Do something with the packet data + print(packet.header["PKT_APID"]) + print(packet.data) diff --git a/imap_processing/codice/generate_ccsds_header_xml.py b/imap_processing/codice/generate_ccsds_header_xml.py new file mode 100644 index 000000000..4b4388abd --- /dev/null +++ b/imap_processing/codice/generate_ccsds_header_xml.py @@ -0,0 +1,121 @@ +""" All L0 data is written with a CCSDS header. This script creates the CCSDS header + parameters and writes them to an XML file. The XML file is then used to create + the CCSDS header parameters in the XTCE file. + """ + +import xml.etree.ElementTree as ET + +ET.register_namespace("xtce", "http://www.omg.org/space") + +# Create the root element + +root = ET.Element("{http://www.omg.org/space}SpaceSystem") +root.attrib["name"] = "CCSDS_Headers" + +# Create the Header element and its attributes +header = ET.SubElement(root, "{http://www.omg.org/space}Header") +header.attrib["date"] = "2023" +header.attrib["version"] = "1.0" +header.attrib["author"] = "IMAP SDC" + +# Create the TelemetryMetaData element +telemetry_metadata = ET.SubElement(root, "{http://www.omg.org/space}TelemetryMetaData") + +# Create the ParameterTypeSet element +parameter_type_set = ET.SubElement( + telemetry_metadata, "{http://www.omg.org/space}ParameterTypeSet" +) + +# Create integer parameter types +integer_sizes = [1, 2, 3, 11, 14, 16, 32] +for size in integer_sizes: + parameter_type = ET.SubElement( + parameter_type_set, "{http://www.omg.org/space}IntegerParameterType" + ) + parameter_type.attrib["name"] = f"uint{size}" + parameter_type.attrib["signed"] = "false" + + encoding = ET.SubElement( + parameter_type, "{http://www.omg.org/space}IntegerDataEncoding" + ) + encoding.attrib["sizeInBits"] = str(size) + encoding.attrib["encoding"] = "unsigned" + + unit_set = ET.SubElement(parameter_type, "{http://www.omg.org/space}UnitSet") + +# Create the ParameterSet element +parameter_set = ET.SubElement( + telemetry_metadata, "{http://www.omg.org/space}ParameterSet" +) + +# Create CCSDS Header parameters +ccsds_parameters = [ + { + "name": "VERSION", + "parameterTypeRef": "uint3", + "description": "CCSDS Packet Version Number (always 0)", + }, + { + "name": "TYPE", + "parameterTypeRef": "uint1", + "description": "CCSDS Packet Type Indicator (0=telemetry)", + }, + { + "name": "SEC_HDR_FLG", + "parameterTypeRef": "uint1", + "description": "CCSDS Packet Secondary Header Flag (always 1)", + }, + { + "name": "PKT_APID", + "parameterTypeRef": "uint11", + "description": "CCSDS Packet Application Process ID", + }, + { + "name": "SEG_FLGS", + "parameterTypeRef": "uint2", + "description": "CCSDS Packet Grouping Flags (3=not part of group)", + }, + { + "name": "SRC_SEQ_CTR", + "parameterTypeRef": "uint14", + "description": "CCSDS Packet Sequence Count (increments with each new packet)", + }, + { + "name": "PKT_LEN", + "parameterTypeRef": "uint16", + "description": "CCSDS Packet Length (number of bytes after Packet length minus 1)", + }, +] + +for parameter_data in ccsds_parameters: + parameter = ET.SubElement(parameter_set, "{http://www.omg.org/space}Parameter") + parameter.attrib["name"] = parameter_data["name"] + parameter.attrib["parameterTypeRef"] = parameter_data["parameterTypeRef"] + + description = ET.SubElement(parameter, "{http://www.omg.org/space}LongDescription") + description.text = parameter_data["description"] + +# Create the ContainerSet element +container_set = ET.SubElement( + telemetry_metadata, "{http://www.omg.org/space}ContainerSet" +) + +# Create the SequenceContainer element +sequence_container = ET.SubElement( + container_set, "{http://www.omg.org/space}SequenceContainer" +) +sequence_container.attrib["name"] = "CCSDSPacket" + +# Create the EntryList element and add ParameterRefEntry elements +entry_list = ET.SubElement(sequence_container, "{http://www.omg.org/space}EntryList") +for parameter_data in ccsds_parameters: + parameter_ref_entry = ET.SubElement( + entry_list, "{http://www.omg.org/space}ParameterRefEntry" + ) + parameter_ref_entry.attrib["parameterRef"] = parameter_data["name"] + +# Create the XML tree +tree = ET.ElementTree(root) +ET.indent(tree, space="\t", level=0) +# Save the XML document to a file +tree.write("L0/ccsds-header.xml", encoding="utf-8", xml_declaration=True) diff --git a/imap_processing/codice/make_xtce.py b/imap_processing/codice/make_xtce.py new file mode 100644 index 000000000..44d737f4f --- /dev/null +++ b/imap_processing/codice/make_xtce.py @@ -0,0 +1,216 @@ +""" The module will take an Excel file and convert it into an XTCE formatted XML file. This is specific for CODICE L0, + but can be modified for other missions. This is the start of CODICE L0 data processing. + +""" +import xml.etree.ElementTree as ET + +import pandas as pd + +from imap_processing.codice.CcsdsHeaderXtceGenerator import CCSDSParameters + +# Make sure the "sheet" name is correct. In an Excel file, there might be several "packets", which are "sheets" +# within the file.This is case-sensitive. +packet_name = "P_COD_AUT" # This is the name of the packet (sheet) in the Excel file you want to convert to XML + +# This is the path to the Excel file you want to convert to XML +path_to_excel_file = "/Users/gamo6782/Desktop/IMAP/TLM_COD_20230629-110638(update).xlsx" + +ccsds_parameters = CCSDSParameters().parameters + +if __name__ == "__main__": + # ET.register_namespace is important! Make sure you use the correct namespace for the xtce file you are using. + # This is the namespace for the IMAP xtce files currently. + + ET.register_namespace( + "xtce", "http://www.omg.org/space" + ) # Update the namespace here + + # Load data from Excel file + xls = pd.ExcelFile(path_to_excel_file) + pkt = xls.parse(packet_name) + + # Fill missing values with '-*-' + pkt.fillna("", inplace=True) + + # Create the root element and add namespaces + root = ET.Element( + "{http://www.omg.org/space}SpaceSystem", + nsmap={"xtce": "http://www.omg.org/space/xtce"}, + ) + + root.attrib["name"] = "packet_name" + + # Create the Header element with attributes 'date', 'version', and 'author' + # Versioning is used to keep track of changes to the XML file. + header = ET.SubElement(root, "{http://www.omg.org/space}Header") + header.attrib["date"] = "2023-08-21" + header.attrib["version"] = "1.0" + header.attrib["author"] = "IMAP SDC" + + """ + These lines of code create a structured XML hierarchy to represent data according to a defined XML schema: + - The 'TelemetryMetaData' element is added under the 'root' element, providing a namespace. + - Within 'TelemetryMetaData', the 'ParameterTypeSet' element is created to define parameter types. + - Similarly, the 'ParameterSet' element is added to 'TelemetryMetaData' to define specific parameters. + These elements and their organization help organize and standardize data representation in XML format. + """ + + # Create the TelemetryMetaData element + telemetry_metadata = ET.SubElement( + root, "{http://www.omg.org/space}TelemetryMetaData" + ) + + # Create the ParameterTypeSet element + parameter_type_set = ET.SubElement(telemetry_metadata, "xtce:ParameterTypeSet") + + # Create the ParameterSet element + parameter_set = ET.SubElement(telemetry_metadata, "xtce:ParameterSet") + + """ + The following loop creates a series of XML elements to define integer parameter types: + - The loop iterates from 0 to 32, creating an 'IntegerParameterType' element for each number. + - Within each 'IntegerParameterType' element, attributes like 'name' and 'signed' are set. + - An 'IntegerDataEncoding' element is added with attributes 'sizeInBits' and 'encoding'. + - Lastly, a 'UnitSet' element is added within each 'IntegerParameterType'. + This loop efficiently defines integer parameter types for a range of values in the XML structure. + """ + + # Create integer parameter types for all numbers between 0-32 + for size in range(33): # Range goes up to 33 to include 0-32 + parameter_type = ET.SubElement(parameter_type_set, "xtce:IntegerParameterType") + parameter_type.attrib["name"] = f"uint{size}" + parameter_type.attrib["signed"] = "false" + + encoding = ET.SubElement(parameter_type, "xtce:IntegerDataEncoding") + encoding.attrib["sizeInBits"] = str(size) + encoding.attrib["encoding"] = "unsigned" + # unit_set is used to define the units for the parameter. This is not needed for CODICE L0. + # UnitSet will be used for CODICE L1. It can be used for other missions as well. + unit_set = ET.SubElement(parameter_type, "xtce:UnitSet") + + """ + This loop generates XML elements to define CCSDS packet parameters: + - It iterates over a list of 'ccsds_parameters', each containing parameter data. + - For each parameter, an 'xtce:Parameter' element is added to the 'parameter_set'. + - Attributes like 'name' and 'parameterTypeRef' are set using the parameter data. + - A 'LongDescription' element is created with the description text. + This loop effectively creates XML elements to define CCSDS packet parameters based on the given data. + """ + + for parameter_data in ccsds_parameters: + parameter = ET.SubElement(parameter_set, "xtce:Parameter") + parameter.attrib["name"] = parameter_data["name"] + parameter.attrib["parameterTypeRef"] = parameter_data["parameterTypeRef"] + + description = ET.SubElement(parameter, "xtce:LongDescription") + description.text = parameter_data["description"] + + # Create ContainerSet element + container_set = ET.SubElement(telemetry_metadata, "xtce:ContainerSet") + + # Create CCSDSPacket SequenceContainer + ccsds_packet_container = ET.SubElement(container_set, "xtce:SequenceContainer") + ccsds_packet_container.attrib["name"] = "CCSDSPacket" + ccsds_packet_entry_list = ET.SubElement(ccsds_packet_container, "xtce:EntryList") + + for parameter_data in ccsds_parameters: + parameter_ref_entry = ET.SubElement( + ccsds_packet_entry_list, "xtce:ParameterRefEntry" + ) + parameter_ref_entry.attrib["parameterRef"] = parameter_data["name"] + + # Create CoDICESciencePacket SequenceContainer + codice_science_container = ET.SubElement(container_set, "xtce:SequenceContainer") + codice_science_container.attrib["name"] = "CoDICESciencePacket" + + base_container = ET.SubElement(codice_science_container, "xtce:BaseContainer") + base_container.attrib["containerRef"] = "CCSDSPacket" + + restriction_criteria = ET.SubElement(base_container, "xtce:RestrictionCriteria") + comparison = ET.SubElement(restriction_criteria, "xtce:Comparison") + comparison.attrib["parameterRef"] = "PKT_APID" + comparison.attrib["value"] = "1120" + comparison.attrib["useCalibratedValue"] = "false" + + codice_science_entry_list = ET.SubElement( + codice_science_container, "xtce:EntryList" + ) + + # Add ParameterRefEntry elements for CoDICESciencePacket + # ******************************* NEED TO LOOK AT THIS: To pkt specific + # This will be the list of parameters that will be included in the CoDICE Science Packet after the CCSDS header''' + parameter_refs = [ + "Spare", + "Power_Cycle_Rq", + "Power_Off_Rq", + "Heater_Control_Enabled", + "Heater_1_State", + "Heater_2_State", + "Spare2", + ] + + for parameter_ref in parameter_refs: + parameter_ref_entry = ET.SubElement( + codice_science_entry_list, "xtce:ParameterRefEntry" + ) + parameter_ref_entry.attrib["parameterRef"] = parameter_ref + + """ + This loop processes rows from the DataFrame starting from the 9th row onwards: + - It iterates over the DataFrame rows using the 'iterrows()' function. + - For each row, it checks if the row index is less than 8 (rows before the 9th row). + - If the condition is met, the loop continues to the next iteration, skipping these rows. + - Otherwise, it creates an 'xtce:Parameter' element and adds it to the 'parameter_set'. + - Attributes like 'name' and 'parameterTypeRef' are set based on row data. + - A 'LongDescription' element is created with the description text from the row. + This loop effectively generates XML elements for parameters starting from the 9th row of the DataFrame. + """ + + # Process rows from 9 until the last available row in the DataFrame + for index, row in pkt.iterrows(): + if index < 8: + continue # Skip rows before row 9 + + parameter = ET.SubElement(parameter_set, "{http://www.omg.org/space}Parameter") + parameter.attrib["name"] = row["mnemonic"] + parameter_type_ref = f"uint{row['lengthInBits']}" + parameter.attrib["parameterTypeRef"] = parameter_type_ref + + description = ET.SubElement( + parameter, "{http://www.omg.org/space}LongDescription" + ) + description.text = row["longDescription"] + + """ + This section creates the final XML structure, indents it, and then saves it to a file: + - The 'ET.ElementTree(root)' creates an ElementTree with the root element 'root'. + - 'ET.indent(tree, space="\t", level=0)' adds indentation to the XML structure for readability. + - 'tree' is the ElementTree object. + - 'space="\t"' specifies the character used for indentation (a tab in this case). + - 'level=0' indicates the starting level of indentation. + - 'tree.write("p_cod_aut_test.xml", encoding="utf-8", xml_declaration=True)' writes the XML content to a file named "p_cod_aut_test.xml". + - 'encoding="utf-8"' specifies the character encoding for the file. + - 'xml_declaration=True' adds an XML declaration at the beginning of the file. + This section completes the XML generation process by creating a structured XML tree, formatting it with indentation, and saving it to a file. + """ + + # Create the XML tree + tree = ET.ElementTree(root) + ET.indent(tree, space="\t", level=0) + + # Save the XML document to a file + output_xml_path = "L0/p_cod_aut_test.xml" + tree.write(output_xml_path, encoding="utf-8", xml_declaration=True) + + # Read and modify the XML file contents + with open(output_xml_path) as file: + contents = file.read() + + modified_content = contents.replace( + 'xmlns:xtce="http://www.omg.org/space/"', + 'xmlns:xtce="http://www.omg.org/space/xtce"', + ) + + # Write the modified content back to the file + with open(output_xml_path, "w") as file: + file.write(modified_content) diff --git a/imap_processing/codice/tests/__init__.py b/imap_processing/codice/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/imap_processing/codice/tests/test_bin_data.py b/imap_processing/codice/tests/test_bin_data.py new file mode 100644 index 000000000..d3a12e4e2 --- /dev/null +++ b/imap_processing/codice/tests/test_bin_data.py @@ -0,0 +1,53 @@ +""" This code is used to test the size of a binary file. It is not used in the actual decom.py file. + It is used to test the size of the binary file that is used in the decom.py file. + """ +import pytest + + +@pytest.mark.skip(reason="This test is not used in the actual decom.py file.") +def test_bin_file_size(): + # Replace this path with the full path to your .bin file + bin_file_path = "../RAW.bin" + + with open(bin_file_path, "rb") as file: + data = file.read() + file_size = len(data) + print(f"The size of the .bin file is: {file_size} bytes") + + +def read_binary_file(file_path): + with open(file_path, "rb") as file: + data = file.read() + return data + + +def extract_header(packet_data): + # Extract the header from the packet_data + header = packet_data[:6] + return header + + +def extract_data_payload(packet_data): + # Extract the data payload from the packet_data + data_payload = packet_data[6:] + return data_payload + + +def binary_to_int(binary_data): + # Convert binary data to an integer + return int.from_bytes(binary_data, byteorder="big") + + +if __name__ == "__main__": + bin_file_path = "../RAW.bin" # Replace this with your .bin file path + + data = read_binary_file(bin_file_path) + packet_length_bytes = ( + binary_to_int(data[4:6]) + 1 + ) # Convert the 16-bit packet length to integer + header = extract_header(data) + data_payload = extract_data_payload(data) + + print(f"Header (in hex): {header.hex()}") + print(f"Data Payload (in hex): {data_payload.hex()}") + print(f"Total Packet Size (in bytes): {packet_length_bytes}")