diff --git a/edosl0util/rdrgen.py b/edosl0util/rdrgen.py index cf527b41dc69e949094867b9c7761d2fe2f951ee..674976969856ba22f4d72315b16c3fc80d2d5c3c 100644 --- a/edosl0util/rdrgen.py +++ b/edosl0util/rdrgen.py @@ -16,83 +16,6 @@ from edosl0util.jpssrdr import ( from edosl0util.stream import jpss_packet_stream -class GetJpssPacketTime(object): - def __init__(self): - self._viirs_tracker = ViirsGroupedPacketTimeTracker() - - def __call__(self, pkt): - if self._viirs_tracker.handles(pkt.apid): - return self._viirs_tracker.get_iet(pkt) - else: - return get_packet_iet(pkt) - - -class ViirsGroupedPacketTimeTracker(object): - grouped_apids = list(range(800, 824)) + [825] - - @classmethod - def handles(cls, apid): - return apid in cls.grouped_apids - - def __init__(self): - self._db = {} - - def get_iet(self, pkt): - if not self.handles(pkt.apid): - raise ValueError('APID {} not a VIIRS grouped packet type'.format(pkt.apid)) - if pkt.is_first(): - obs_iet = get_packet_iet(pkt) - self._db[pkt.apid] = (obs_iet, pkt.seqid) - return obs_iet - else: - last_obs_iet, last_seq = self._db[pkt.apid] - group_size = ViirsScienceApidInfo.get_packets_per_scan(pkt.apid) - if not self.check_sequence_number(pkt.seqid, last_seq, group_size): - raise OrphanedViirsPacket(pkt) - if not self.check_packet_iet(self.get_viirs_iet(pkt), last_obs_iet): - raise OrphanedViirsPacket(pkt) - return last_obs_iet - - @staticmethod - def get_viirs_iet(pkt): - if pkt.is_standalone(): - idx = 18 - elif pkt.is_first(): - idx = 20 - else: - idx = 10 - arr = np.frombuffer(pkt.bytes()[idx:idx+8], 'B') - days = arr[0:2].view('>u2')[0] - ms = arr[2:6].view('>u4')[0] - us = arr[6:8].view('>u2')[0] - return timecode_parts_to_iet(days, ms, us) - - @staticmethod - def check_sequence_number(nonfirst_seq_num, first_seq_num, group_size): - seq_limit = 2**14 - group_end = first_seq_num + group_size - # the 2nd check below is needed to handle wrap-around - return (first_seq_num < nonfirst_seq_num < group_end - or first_seq_num < nonfirst_seq_num + seq_limit < group_end) - - @staticmethod - def check_packet_iet(pkt_iet, obs_iet): - # this can be a pretty loose check since it is only needed to prevent - # the very rare case where the sequence number check yields a false - # positive - permitted_lag = 5 # seconds - lag = (pkt_iet - obs_iet) * 1e-6 - return 0 <= lag <= permitted_lag - - -class OrphanedViirsPacket(Exception): - def __init__(self, pkt): - self.packet = pkt - - def __str__(self): - return repr(self.packet) - - def packets_to_rdrs(sat, pkt_files): # FIXME: refactor!!! rdr_pkt_files = {} @@ -292,6 +215,17 @@ def build_rdr_blob(sat, pkt_stream): return buf +class GetJpssPacketTime(object): + def __init__(self): + self._viirs_tracker = ViirsGroupedPacketTimeTracker() + + def __call__(self, pkt): + if self._viirs_tracker.tracks_apid(pkt.apid): + return self._viirs_tracker.get_iet(pkt) + else: + return get_packet_iet(pkt) + + class ViirsScienceApidInfo(object): apids = list(x for x in range(800, 827) if x != 824) names = ['M04', 'M05', 'M03', 'M02', 'M01', 'M06', 'M07', 'M09', 'M10', @@ -326,39 +260,43 @@ class ViirsScienceApidInfo(object): return 33 -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 +class CrisScienceApidInfo(object): + apids = [1289, 1290] + list(range(1315, 1396)) + + @classmethod + def get_specs(cls): + return [ApidSpec(apid, cls.get_name(apid), cls.get_max_expected(apid)) + for apid in cls.apids] + + @classmethod + def get_name(cls, apid): + if apid == 1289: + return 'EIGHT_S_SCI' + elif apid == 1290: + return 'ENG' else: - return 9 + offset = apid - 1315 + view_types = ['N', 'S', 'C'] + bands = ['LW', 'MW', 'SW'] + num_fovs = 9 + view_type = view_types[offset // (num_fovs * len(bands))] + band = bands[offset // num_fovs % len(bands)] + fov = str(offset % num_fovs + 1) + return view_type + band + fov + + @classmethod + def get_max_expected(cls, apid): + name = cls.get_name(apid) + if name == 'EIGHT_S_SCI': + return 5 + elif name == 'ENG': + return 1 + else: + view_type = name[0] + if view_type == 'N': + return 121 + else: + return 9 @attr.s @@ -407,7 +345,7 @@ class CrisScienceRdrType(object): sensor = 'cris' type_id = 'SCIENCE' document = '474-00448-02-03_JPSS-DD-Vol-II-Part-3_0200B.pdf' - apids = get_cris_science_apids() + apids = CrisScienceApidInfo.get_specs() @rdr_type_spec @@ -423,6 +361,72 @@ class SpacecraftDiaryRdrType(object): ApidSpec(11, 'DIARY', max_expected=21)] +class ViirsGroupedPacketTimeTracker(object): + grouped_apids = list(range(800, 824)) + [825] + + @classmethod + def tracks_apid(cls, apid): + return apid in cls.grouped_apids + + def __init__(self): + self._db = {} + + def get_iet(self, pkt): + if not self.tracks_apid(pkt.apid): + raise ValueError('APID {} not a VIIRS grouped packet type'.format(pkt.apid)) + if pkt.is_first(): + obs_iet = get_packet_iet(pkt) + self._db[pkt.apid] = (obs_iet, pkt.seqid) + return obs_iet + else: + last_obs_iet, last_seq = self._db[pkt.apid] + group_size = ViirsScienceApidInfo.get_packets_per_scan(pkt.apid) + if not self.check_sequence_number(pkt.seqid, last_seq, group_size): + raise OrphanedViirsPacket(pkt) + if not self.check_packet_iet(self.get_viirs_iet(pkt), last_obs_iet): + raise OrphanedViirsPacket(pkt) + return last_obs_iet + + @staticmethod + def get_viirs_iet(pkt): + if pkt.is_standalone(): + idx = 18 + elif pkt.is_first(): + idx = 20 + else: + idx = 10 + arr = np.frombuffer(pkt.bytes()[idx:idx+8], 'B') + days = arr[0:2].view('>u2')[0] + ms = arr[2:6].view('>u4')[0] + us = arr[6:8].view('>u2')[0] + return timecode_parts_to_iet(days, ms, us) + + @staticmethod + def check_sequence_number(nonfirst_seq_num, first_seq_num, group_size): + seq_limit = 2**14 + group_end = first_seq_num + group_size + # the 2nd check below is needed to handle wrap-around + return (first_seq_num < nonfirst_seq_num < group_end + or first_seq_num < nonfirst_seq_num + seq_limit < group_end) + + @staticmethod + def check_packet_iet(pkt_iet, obs_iet): + # this can be a pretty loose check since it is only needed to prevent + # the very rare case where the sequence number check yields a false + # positive + permitted_lag = 5 # seconds + lag = (pkt_iet - obs_iet) * 1e-6 + return 0 <= lag <= permitted_lag + + +class OrphanedViirsPacket(Exception): + def __init__(self, pkt): + self.packet = pkt + + def __str__(self): + return repr(self.packet) + + def make_rdr_filename(prod_id, sat, gran_time, gran_end_time, orbit_num, creation_time, origin, domain, compressed): sat_strs = {'snpp': 'npp'}