diff --git a/edosl0util/rdrgen.py b/edosl0util/rdrgen.py new file mode 100644 index 0000000000000000000000000000000000000000..73ac856be35ec8018f59ee9ac574b85535337a17 --- /dev/null +++ b/edosl0util/rdrgen.py @@ -0,0 +1,153 @@ +import ctypes +from collections import OrderedDict + +import attr +from astropy.time import Time, TimeDelta +import numpy as np + +from edosl0util.jpssrdr import StaticHeader, Apid as ApidListItem, PacketTracker + + +def prototype(): + pds_file = 'P1571289CRISSCIENCEA6T17230135400001.PDS' + satellite = 'snpp' + rdr_type = 'CRIS-SCIENCE-RDR' + granule_iet = 1881755831079000 + + +class RdrBlobBuilder(object): + def __init__(self, rdr_type, sat, granule_iet): + self.sat = sat + + +def build_rdr_blob(pkt_stream, rdr_type, sat, granule_iet): + granule_iet_end = granule_iet + rdr_type.granularity + + total_pkt_size = 0 + apid_info = OrderedDict() + total_trackers = 0 + all_pkts = [] + for apid in rdr_type.apids: + apid_info[apid.num] = { + 'name': apid.name, + 'pkts_reserved': apid.max_expected, 'pkts_received': 0, + 'first_tracker_index': total_trackers, + 'pkt_info': [{} for _ in range(apid.max_expected)]} + total_trackers += apid.max_expected + + for pkt in pkt_stream: + if pkt.apid not in apid_info: + continue + pkt_iet = timecode_to_iet(pkt.secondary_header.timecode) + if not granule_iet <= pkt_iet < granule_iet_end: + continue + info = apid_info[pkt.apid] + pkt_info = info['pkt_info'][info['pkts_received']] + pkt_info['obs_time'] = pkt_iet + pkt_info['seq_num'] = pkt.seqid + pkt_info['size'] = pkt.size + pkt_info['offset'] = total_pkt_size + info['pkts_received'] += 1 + total_pkt_size += pkt.size + all_pkts.append(pkt) + + apid_list_offset = ctypes.sizeof(StaticHeader) + pkt_tracker_offset = apid_list_offset + len(apid_info) * ctypes.sizeof(ApidListItem) + ap_storage_offset = pkt_tracker_offset + total_trackers * ctypes.sizeof(PacketTracker) + buf_size = ap_storage_offset + total_pkt_size + buf = np.zeros([buf_size], np.uint8) # zeros needed to null-pad strings + + header = StaticHeader.from_buffer(buf) + header.satellite = sat # XXX: mapping? + header.sensor = rdr_type.sensor + header.type_id = rdr_type.type_id + header.num_apids = len(apid_info) + header.apid_list_offset = apid_list_offset + header.pkt_tracker_offset = pkt_tracker_offset + header.ap_storage_offset = ap_storage_offset + header.next_pkt_pos = total_pkt_size + header.start_boundary = granule_iet + header.end_boundary = granule_iet_end + + for i, (apid, info) in enumerate(apid_info.items()): + offset = header.apid_list_offset + i * ctypes.sizeof(ApidListItem) + item = ApidListItem.from_buffer(buf, offset) + item.name = info['name'] + item.value = apid + item.pkt_tracker_start_idx = info['first_tracker_index'] + item.pkts_reserved = info['pkts_reserved'] + item.pkts_received = info['pkts_received'] + + for j, pkt_info in enumerate(info['pkt_info']): + offset = (header.pkt_tracker_offset + + (info['first_tracker_index'] + j) * ctypes.sizeof(PacketTracker)) + tracker = PacketTracker.from_buffer(buf, offset) + if pkt_info: + tracker.obs_time = pkt_info['obs_time'] + tracker.sequence_number = pkt_info['seq_num'] + tracker.size = pkt_info['size'] + tracker.offset = pkt_info['offset'] + tracker.fill_percent = 0 + else: + tracker.offset = -1 + + buf[ap_storage_offset:] = bytearray().join(pkt.bytes() for pkt in all_pkts) + + return buf + + +def get_cris_science_apids(): + return ([ApidSpec(1289, 'EIGHT_S_SCI', 5), ApidSpec(1290, 'ENG', 1)] + + get_cris_obs_apids()) + + +def get_cris_obs_apids(): + view_types = ['N', 'S', 'C'] # "normal", "space", "calibration" + bands = ['LW', 'MW', 'SW'] + num_fovs = 9 + base_apid = 1315 + apids = [] + for i in range(len(view_types) * len(bands) * num_fovs): + apid = base_apid + i + view_type = view_types[i // (num_fovs * len(bands))] + band = bands[i // num_fovs % len(bands)] + fov = str(i % num_fovs + 1) + apid_name = view_type + band + fov + max_expected = get_max_expected_cris_packets(apid_name) + apids.append(ApidSpec(apid, apid_name, max_expected)) + return apids + + +def get_max_expected_cris_packets(apid_name): + if apid_name == 'EIGHT_S_SCI': + return 5 + elif apid_name == 'ENG': + return 1 + else: + view_type = apid_name[0] + if view_type == 'N': + return 121 + else: + return 9 + + +@attr.s +class ApidSpec(object): + num = attr.ib() + name = attr.ib() + max_expected = attr.ib() + + +class CrisScienceRdrType(object): + short_name = 'CRIS-SCIENCE-RDR' + granularity = 31997000 + apids = get_cris_science_apids() + sensor = 'CrIS' + type_id = 'SCIENCE' + + +def timecode_to_iet(timecode): + # FIXME: move to timecode.py + ccsds_epoch = iet_epoch = Time('1958-01-01', scale='tai') + day = Time(ccsds_epoch.jd + timecode.days, scale='utc', format='jd') - iet_epoch + return int(day.sec * 1e6 + timecode.milliseconds * 1e3 + timecode.microseconds)