diff --git a/edosl0util/rdrgen.py b/edosl0util/rdrgen.py index b16fdcfad47a212c22d5e11fabc3900c0e08985f..cf527b41dc69e949094867b9c7761d2fe2f951ee 100644 --- a/edosl0util/rdrgen.py +++ b/edosl0util/rdrgen.py @@ -16,6 +16,17 @@ 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] @@ -23,21 +34,20 @@ class ViirsGroupedPacketTimeTracker(object): def handles(cls, apid): return apid in cls.grouped_apids - def __init_(self): - self.db = {} + 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)) - viirs_iet = self.get_viirs_iet(pkt) if pkt.is_first(): obs_iet = get_packet_iet(pkt) - self.db[pkt.apid] = (obs_iet, pkt.seq_id) + self._db[pkt.apid] = (obs_iet, pkt.seqid) return obs_iet else: - last_obs_iet, last_seq = self.db[pkt.apid] - group_size = self.get_group_size(pkt.apid) - if not self.check_sequence_number(pkt.seq_id, last_seq, group_size): + 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) @@ -45,24 +55,18 @@ class ViirsGroupedPacketTimeTracker(object): @staticmethod def get_viirs_iet(pkt): - idx = 20 if pkt.is_first() else 10 + 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 get_group_size(apid): - i_band_apids = [813, 817, 818, 819, 820] - cal_apid = 825 - if apid in i_band_apids: - return 32 - elif apid == cal_apid: - return 24 - else: - return 16 - @staticmethod def check_sequence_number(nonfirst_seq_num, first_seq_num, group_size): seq_limit = 2**14 @@ -92,19 +96,23 @@ class OrphanedViirsPacket(Exception): def packets_to_rdrs(sat, pkt_files): # FIXME: refactor!!! rdr_pkt_files = {} + get_jpss_packet_time = GetJpssPacketTime() for pkt_file in pkt_files: with open(pkt_file) as file_obj: stream = jpss_packet_stream(file_obj) for pkt in stream: rdr_type = get_rdr_type(pkt.apid) - gran = calc_rdr_granule(sat, rdr_type, pkt) + pkt_iet = get_jpss_packet_time(pkt) + gran = calc_rdr_granule(sat, rdr_type, pkt_iet) if (rdr_type, gran) not in rdr_pkt_files: rdr_pkt_files[rdr_type, gran] = TemporaryFile() rdr_pkt_files[rdr_type, gran].write(pkt.bytes()) + get_jpss_packet_time = GetJpssPacketTime() for rdr_pkt_file in rdr_pkt_files.values(): rdr_pkt_file.seek(0) - pkts = jpss_packet_stream(rdr_pkt_file) - pkts = sorted(pkts, key=(lambda p: (get_packet_iet(p), p.apid))) + pkts = list(jpss_packet_stream(rdr_pkt_file)) + pkt_times = {p: get_jpss_packet_time(p) for p in pkts} + pkts.sort(key=(lambda p: (pkt_times[p], p.apid))) blob = build_rdr_blob(sat, pkts) write_rdr(sat, blob) @@ -204,9 +212,10 @@ def set_h5_attrs(h5_obj, attrs): def build_rdr_blob(sat, pkt_stream): pkt_stream = iter(pkt_stream) + get_jpss_packet_time = GetJpssPacketTime() first_pkt = next(pkt_stream) # FIXME: what if there are no packets? rdr_type = get_rdr_type(first_pkt.apid) - granule_iet = calc_rdr_granule(sat, rdr_type, first_pkt) + granule_iet = calc_rdr_granule(sat, rdr_type, get_jpss_packet_time(first_pkt)) granule_iet_end = granule_iet + rdr_type.gran_len total_pkt_size = 0 @@ -225,7 +234,7 @@ def build_rdr_blob(sat, pkt_stream): if pkt.apid not in apid_info: raise ValueError( 'APID {} not expected for {}'.format(pkt.apid, rdr_type.short_name)) - pkt_iet = get_packet_iet(pkt) + pkt_iet = get_jpss_packet_time(pkt) if not granule_iet <= pkt_iet < granule_iet_end: raise ValueError('packet stream crosses granule boundary') info = apid_info[pkt.apid] @@ -283,6 +292,40 @@ def build_rdr_blob(sat, pkt_stream): return buf +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', + 'M08', 'M11', 'M13', 'M12', 'I04', 'M16', 'M15', 'M14', 'I05', + 'I05', 'I01', 'I02', 'I03', 'DNB', 'DNB_MGS', 'DNB_LGS', + 'CAL', 'ENG'] + + @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): + return cls.names[cls.apids.index(apid)] + + @classmethod + def get_max_expected(cls, apid): + max_scans = 48 + return max_scans * cls.get_packets_per_scan(apid) + + @classmethod + def get_packets_per_scan(cls, apid): + name = cls.get_name(apid) + if name == 'ENG': + return 1 + elif name == 'CAL': + return 24 + elif name.startswith('M'): + return 17 + else: + return 33 + + def get_cris_science_apids(): return ([ApidSpec(1289, 'EIGHT_S_SCI', 5), ApidSpec(1290, 'ENG', 1)] + get_cris_obs_apids()) @@ -345,6 +388,17 @@ rdr_type_spec = rdr_type_mgr.register_type get_rdr_type = rdr_type_mgr.get_type_for_apid +@rdr_type_spec +class ViirsScienceRdrType(object): + product_id = 'RVIRS' + short_name = 'VIIRS-SCIENCE-RDR' + gran_len = 85350000 + sensor = 'viirs' + type_id = 'SCIENCE' + document = '474-00448-02-08_JPSS-DD-Vol-II-Part-8_0200B.pdf' + apids = ViirsScienceApidInfo.get_specs() + + @rdr_type_spec class CrisScienceRdrType(object): product_id = 'RCRIS' @@ -401,9 +455,8 @@ def iet_to_datetime(iet): return (iet_epoch + TimeDelta(iet * 1e-6, format='sec')).utc.datetime -def calc_rdr_granule(sat, rdr_type, pkt): - return calc_iet_granule(satellite_base_times[sat], rdr_type.gran_len, - get_packet_iet(pkt)) +def calc_rdr_granule(sat, rdr_type, pkt_iet): + return calc_iet_granule(satellite_base_times[sat], rdr_type.gran_len, pkt_iet) def calc_iet_granule(base_time, gran_len, iet): @@ -435,7 +488,7 @@ def calc_percent_missing(common_rdr): iet_epoch = Time('1958-01-01', scale='tai') satellite_base_times = {'snpp': 1698019234000000} platform_short_names = {'snpp': 'NPP'} -instrument_short_names = {'cris': 'CrIS', None: 'SPACECRAFT'} +instrument_short_names = {'viirs': 'VIIRS', 'cris': 'CrIS', None: 'SPACECRAFT'} default_origin = 'ssec' default_domain = 'dev' diff --git a/tests/test_rdrgen.py b/tests/test_rdrgen.py index beb6fef13647110a7df7a7af481f01d6f0a7e1ed..2b9023c406d3d0ff216b0f8f9205a5385cd632b9 100644 --- a/tests/test_rdrgen.py +++ b/tests/test_rdrgen.py @@ -11,7 +11,45 @@ from edosl0util.stream import jpss_packet_stream class TestViirsGroupedPacketTimeTracker(object): - pass + def test_check_sequence_number(self): + group_size = 24 + def run(nonfirst_seq_num, first_seq_num): + return m.ViirsGroupedPacketTimeTracker.check_sequence_number( + nonfirst_seq_num, first_seq_num, group_size) + + first_seq = 4096 + assert not run(4095, first_seq) + assert run(4097, first_seq) + assert run(4119, first_seq) + assert not run(4120, first_seq) + + max_seq = 2**14 + first_seq = max_seq - 16 + assert not run(max_seq - 17, first_seq) + assert run(max_seq - 15, first_seq) + assert run(max_seq - 1, first_seq) + assert run(0, first_seq) + assert run(7, first_seq) + assert not run(8, first_seq) + + def test_get_viirs_iet(self): + def run(pkt): + return m.iet_to_datetime(m.ViirsGroupedPacketTimeTracker.get_viirs_iet(pkt)) + + with open(self.l0_path) as l0_file: + stream = jpss_packet_stream(l0_file) + standalone_pkt = next(p for p in stream if p.is_standalone()) + first_pkt = next(p for p in stream if p.is_first()) + nonfirst_pkt = next(p for p in stream if p.is_continuing()) + + # expected values haven't been independently verified, just looked at + # to see that they at least make sense + assert run(standalone_pkt) == datetime(2017, 9, 27, 13, 54, 1, 727765) + assert run(first_pkt) == datetime(2017, 9, 27, 13, 54, 1, 746898) + assert run(nonfirst_pkt) == datetime(2017, 9, 27, 13, 54, 1, 748328) + + l0_file = 'P1570826VIIRSSCIENCE6T17270135400001.PDS' + l0_path = os.path.join(os.path.dirname(__file__), l0_file) def test_can_reproduce_rdr_from_class():