diff --git a/edosl0util/cli/crgen.py b/edosl0util/cli/crgen.py new file mode 100644 index 0000000000000000000000000000000000000000..ac1cc318e1b9751d63f6c7091f13d008972c8c7d --- /dev/null +++ b/edosl0util/cli/crgen.py @@ -0,0 +1,33 @@ + +"""Generate a PDS construction record from a PDS data file""" + +import logging +from edosl0util import crio +from edosl0util.cli import util +from edosl0util.crgen import build_cr + + +def main(): + parser = util.default_parser(description=__doc__) + parser.add_argument('input_file') + parser.add_argument('-o', '--output-file', help='generated from input file name by default') + parser.add_argument('-p', '--prev_pds_file', + help='previous PDS data file, used for detecting cross-file packet gaps') + args = parser.parse_args() + util.configure_logging(args) + crgen(args.input_file, args.output_file, args.prev_pds_file) + + +def crgen(input_file, output_file=None, prev_pds_file=None): + cr = build_cr(input_file, prev_pds_file) + if output_file is None: + output_file = cr['pds_id'] + '.PDS' + logger.info('writing {}'.format(output_file)) + crio.write(cr, output_file) + + +logger = logging.getLogger(__name__) + + +if __name__ == '__main__': + main() diff --git a/edosl0util/cli/util.py b/edosl0util/cli/util.py index ed6cc3496296396e0573ef52b091a5f4a0445114..352e7521fa49e235b67982731c15b14bdf671b2d 100644 --- a/edosl0util/cli/util.py +++ b/edosl0util/cli/util.py @@ -7,8 +7,8 @@ def timestamp(v): return datetime.strptime(v, '%Y-%m-%d %H:%M:%S') -def default_parser(): - parser = argparse.ArgumentParser() +def default_parser(**kwargs): + parser = argparse.ArgumentParser(**kwargs) parser.add_argument('-v', '--verbose', action='store_true') return parser diff --git a/edosl0util/crgen.py b/edosl0util/crgen.py index 048e61045154e64da4c62183e6de739103480f0f..56c40ef545f9b8ab52f8f73935e9720e342e9d3c 100644 --- a/edosl0util/crgen.py +++ b/edosl0util/crgen.py @@ -3,7 +3,9 @@ from datetime import datetime import itertools +import logging import os +import edosl0util.crio as crio from edosl0util.headers import DaySegmentedTimecode from edosl0util.stream import jpss_packet_stream @@ -11,7 +13,7 @@ from edosl0util.stream import jpss_packet_stream def test_build_apid_info(): # FIXME: build CR comparison into the CLI calculated = build_cr('P1571289CRISSCIENCEAAT15320210920101.PDS') - ingested = read_pds_cr('P1571289CRISSCIENCEAAT15320210920100.PDS') + ingested = crio.read('P1571289CRISSCIENCEAAT15320210920100.PDS') insert_fake_cr_info(ingested) del calculated['completion_time'] # it seems CR completion time does not match PDS del ingested['completion_time'] # creation time from the file name @@ -60,14 +62,28 @@ def build_cr(pds_file, prev_pds_file=None): def insert_fake_cr_info(cr): """Populate a CR with phony values for fields that can't be discovered via a packet scan""" cr.update({'edos_sw_ver_major': 0, 'edos_sw_ver_minor': 0, 'cr_type': 1, 'test_flag': 0, - 'scs_count': 1, 'scs_info': {'start': missing_time_value, - 'stop': missing_time_value}, + 'scs_count': 1, 'scs_info': [{'start': missing_time_value, + 'stop': missing_time_value}], 'first_packet_esh_time': missing_time_value, 'last_packet_esh_time': missing_time_value, 'fill_bytes': 0, 'mismatched_length_packets': 0, 'rs_corrected_packets': 0}) insert_fake_apid_info(cr['apid_info']) +def insert_fake_apid_info(apid_info): + """Fill CR apid_info with phony values for fields that can't be found via a packet scan""" + for entry in apid_info: + entry.update({ + 'fill_packets': 0, 'fill_packet_info': [], 'fill_bytes': 0, + 'mismatched_length_packets': 0, 'mismatched_length_packet_ssc_list': [], + 'first_packet_esh_time': missing_time_value, + 'last_packet_esh_time': missing_time_value, + 'rs_corrected_packets': 0}) + for gap in entry['gap_info']: + gap.update({'pre_gap_packet_esh_time': missing_time_value, + 'post_gap_packet_esh_time': missing_time_value}) + + def pds_id_from_path(pds_file): """Pull 36-char PDS ID from a file name; that's the CR file name minus the .PDS""" file_name = os.path.basename(pds_file) @@ -80,12 +96,14 @@ def pds_id_from_path(pds_file): def get_pds_creation_time(pds_file_or_id): """Parse 11-char creation time out of a PDS ID or file name; return a DaySegmentedTimecode""" + pds_file_or_id = os.path.basename(pds_file_or_id) return datetime_to_ccsds(datetime.strptime(pds_file_or_id[22:33], '%y%j%H%M%S')) def build_apid_info(scan_apid_info): """Build up apid_info resulting from scan_packets into a full apid_info for a CR""" - for entry in scan_apid_info: + apid_info = deepcopy(scan_apid_info) + for entry in apid_info: entry['scid'] = npp_scid entry['vcid_count'] = 1 entry['vcid_info'] = [{'scid': npp_scid, 'vcid': npp_apid_to_vcid_map[entry['apid']]}] @@ -94,24 +112,9 @@ def build_apid_info(scan_apid_info): insert_fake_apid_info(apid_info) return apid_info - -def insert_fake_apid_info(apid_info): - """Fill CR apid_info with phony values for fields that can't be found via a packet scan""" - for entry in apid_info: - entry.update({ - 'fill_packets': 0, 'fill_packet_info': [], 'fill_bytes': 0, - 'mismatched_length_packets': 0, 'mismatched_length_packet_ssc_list': [], - 'first_packet_esh_time': missing_time_value, - 'last_packet_esh_time': missing_time_value, - 'rs_corrected_packets': 0}) - for gap in entry['gap_info']: - gap.update({'pre_gap_packet_esh_time': missing_time_value, - 'post_gap_packet_esh_time': missing_time_value}) - - missing_time_value = DaySegmentedTimecode(0, 0, 0) -npp_scid = 157 # FIXME: i guess this should come from the file name +npp_scid = 157 # ripped from MDFCB, 2014-06-05 revision # modified to place CrIS FOV 6 in VCID 7 after testing against some real data @@ -154,6 +157,7 @@ def scan_packets(pds_file, prev_pds_file=None): def main(): prev_apid_map = build_prev_apid_map(prev_pds_file) apid_map = {} + logger.info('scanning {}'.format(pds_file)) stream = jpss_packet_stream(open(pds_file, 'rb')) first_pkt = stream.next() for pkt in itertools.chain([first_pkt], stream): @@ -219,3 +223,6 @@ def datetime_to_ccsds(dt): return DaySegmentedTimecode(days, micros // 1000, micros % 1000) else: return DaySegmentedTimecode() + + +logger = logging.getLogger(__name__) diff --git a/edosl0util/crio.py b/edosl0util/crio.py index ba81f8b3a41dd56616b21b1e7468c2f6366dcd2c..e308b87f811bdbf5263baa43418f9771d905cfd3 100644 --- a/edosl0util/crio.py +++ b/edosl0util/crio.py @@ -2,7 +2,6 @@ """PDS construction record input and output""" import ctypes as c -import warnings from edosl0util.headers import BaseStruct, DaySegmentedTimecode @@ -13,18 +12,18 @@ def read(cr_file): rv = {} with open(cr_file, 'rb') as f: read_into_dict(f, Main1Struct, rv) - rv['scs_info'] = [read_struct(f, ScsStruct) for i in range(rv['scs_count'])] + rv['scs_info'] = [read_struct(f, ScsStruct) for _ in range(rv['scs_count'])] read_into_dict(f, Main2Struct, rv) rv['apid_info'] = [] - for i in range(rv['apid_count']): + for _ in range(rv['apid_count']): d = {} read_into_dict(f, Apid1Struct, d) - d['vcid_info'] = [read_struct(f, ApidVcidStruct) for j in range(d['vcid_count'])] + d['vcid_info'] = [read_struct(f, ApidVcidStruct) for _ in range(d['vcid_count'])] read_into_dict(f, Apid2Struct, d) - d['gap_info'] = [read_struct(f, ApidGapStruct) for j in range(d['gap_count'])] + d['gap_info'] = [read_struct(f, ApidGapStruct) for _ in range(d['gap_count'])] read_into_dict(f, Apid3Struct, d) d['fill_packet_info'] = [ - read_struct(f, ApidFillStruct) for j in range(d['fill_packets'])] + read_struct(f, ApidFillStruct) for _ in range(d['fill_packets'])] read_into_dict(f, Apid4Struct, d) d['mismatched_length_packet_ssc_list'] = [ read_struct(f, ApidMismatchedLengthStruct)['packet_ssc'] @@ -33,15 +32,16 @@ def read(cr_file): rv['apid_info'].append(d) read_into_dict(f, Main3Struct, rv) rv['file_info'] = [] - for i in range(rv['file_count']): + for _ in range(rv['file_count']): d = {} read_into_dict(f, FileStruct, d) - d['apid_info'] = [read_struct(f, FileApidStruct) for j in range(d['apid_count'])] + d['apid_info'] = [read_struct(f, FileApidStruct) for _ in range(d['apid_count'])] if d['apid_count'] == 0: # bogus all-zero apid struct is present for the CR file read_struct(f, FileApidStruct) rv['file_info'].append(d) - if f.read(): - warnings.warn('{} bytes remain after reading CR'.format(len(extra))) + extra = f.read() + if extra: + raise ValueError('{} bytes remain after reading CR'.format(len(extra))) return rv def read_into_dict(f, struct, data):