diff --git a/oops/gold_master/__init__.py b/oops/gold_master/__init__.py index cc31845c..85576263 100644 --- a/oops/gold_master/__init__.py +++ b/oops/gold_master/__init__.py @@ -1147,31 +1147,19 @@ def run_tests(self): log_path = (BACKPLANE_OUTPUT_PREFIX / self.output_dir / f'{self.task}.log') - localpath = None - try: - localpath = log_path.retrieve() - except FileNotFoundError: - pass - - if localpath: + localpath = log_path.get_local_path() + if os.path.exists(localpath): + # Append the latest modification date to the pre-existing file dt = datetime.datetime.fromtimestamp(os.path.getmtime(localpath)) suffix = dt.strftime('-%Y-%m-%dT%H-%M-%S') - dated_localpath = localpath[:-4] + suffix + '.log' dated_logpath = log_path[:-4] + suffix + '.log' - # Note that for a cloud destination, this doesn't actually delete - # the original summary.py file in the cloud, since you can't rename - # files in the cloud. Instead we upload a copy with the new dated - # name and then the write below will overwrite the old version. - # Also notice that this attempt to use the modification date of the - # original summary.py file won't work in the cloud, because - # creation/modification times are not preserved when a file is - # retrieved. Instead, it will use the time the file was downloaded. - os.rename(localpath, dated_localpath) - (BACKPLANE_OUTPUT_PREFIX / dated_logpath).upload() - - abs_log_path = log_path.get_local_path() - handler = logging.FileHandler(abs_log_path) + + # Move or copy the old log to its timestamped filename using + # filecache-aware replacement semantics. + log_path.replace(dated_logpath) + + handler = logging.FileHandler(localpath) LOGGING.logger.addHandler(handler) # Run the tests @@ -2325,28 +2313,17 @@ def write_summary(self, outdir): filepath = outdir / 'summary.py' - localpath = None - try: - localpath = str(filepath.retrieve(filepath)) - except FileNotFoundError: - pass - - if localpath: + localpath = filepath.get_local_path() + if os.path.exists(localpath): + # Append the latest modification date to any pre-existing file dt = datetime.datetime.fromtimestamp(os.path.getmtime(localpath)) suffix = dt.strftime('-%Y-%m-%dT%H-%M-%S') - dated_localpath = localpath[:-3] + suffix + '.py' dated_filepath = filepath[:-3] + suffix + '.py' - # Note that for a cloud destination, this doesn't actually delete - # the original summary.py file in the cloud, since you can't rename - # files in the cloud. Instead we upload a copy with the new dated - # name and then the write below will overwrite the old version. - # Also notice that this attempt to use the modification date of the - # original summary.py file won't work in the cloud, because - # creation/modification times are not preserved when a file is - # retrieved. Instead, it will use the time the file was downloaded. - os.rename(localpath, dated_localpath) - dated_filepath.upload() + + # Move or copy the old summary to its timestamped filename using + # filecache-aware replacement semantics. + filepath.replace(dated_filepath) LOGGING.info('Previous summary moved to:', dated_filepath.name) titles = list(self.summary.keys()) diff --git a/oops/hosts/cassini/iss.py b/oops/hosts/cassini/iss.py index adf31f3d..5fbc310c 100755 --- a/oops/hosts/cassini/iss.py +++ b/oops/hosts/cassini/iss.py @@ -38,8 +38,7 @@ def from_file(filespec, fast_distortion=True, # Load the VICAR file filespec = FCPath(filespec) - local_path = filespec.retrieve() - vic = vicar.VicarImage.from_file(local_path, strict=False) + vic = vicar.VicarImage.from_file(filespec, strict=False) vicar_dict = vic.as_dict() # Get key information from the header @@ -108,8 +107,7 @@ def from_index(filespec, **parameters): # Read the index file COLUMNS = [] # Return all columns TIMES = ['START_TIME'] - local_path = filespec.retrieve() - table = pdstable.PdsTable(local_path, columns=COLUMNS, times=TIMES) + table = pdstable.PdsTable(filespec, columns=COLUMNS, times=TIMES) row_dicts = table.dicts_by_row() # Create a list of Snapshot objects diff --git a/oops/hosts/cassini/uvis.py b/oops/hosts/cassini/uvis.py index a26c5c0e..56290211 100755 --- a/oops/hosts/cassini/uvis.py +++ b/oops/hosts/cassini/uvis.py @@ -21,7 +21,7 @@ # Standard class methods ########################################################################################## -def from_file(filespec, data=True, enclose=False, **parameters): +def from_file(filespec, data=True, enclose=False, method='strict', **parameters): """A general, static method to return one or more Observation subclass objects based on a label for a given Cassini UVIS file. @@ -34,13 +34,14 @@ def from_file(filespec, data=True, enclose=False, **parameters): enclosing limits in line and band, and the binning is assumed to be 1. If False and multiple windows are used, the function returns a tuple of observations rather than a single observation. + method: Label reading method to be passed to Pds3Label. """ UVIS.initialize() # Define everything the first time through; use defaults unless # initialize() is called explicitly. # Get the label dictionary and data array dimensions - label = pdsparser.Pds3Label(filespec, method='fast').as_dict() + label = pdsparser.Pds3Label(filespec, method=method).as_dict() # Load any needed SPICE kernels tstart = julian.tdb_from_tai(julian.tai_from_iso(label['START_TIME'])) diff --git a/oops/hosts/cassini/vims.py b/oops/hosts/cassini/vims.py index 0ee64460..8767043d 100755 --- a/oops/hosts/cassini/vims.py +++ b/oops/hosts/cassini/vims.py @@ -189,7 +189,7 @@ # Standard class methods ########################################################################################## -def from_file(filespec, data=True): +def from_file(filespec, data=True, method='strict'): """A general, static method to return a pair of Observation objects based on a given Cassini VIMS data file or label file. @@ -198,6 +198,7 @@ def from_file(filespec, data=True): data if True, data arrays are included in the returned observation objects. Use a tuple of two booleans to specify whether to include the VIS and IR data independently. + method Label reading method to be passed to Pds3Label. Return: (vis, ir) vis the VIS observation, or None if the VIS channel was inactive. @@ -212,7 +213,7 @@ def from_file(filespec, data=True): filespec = FCPath(filespec) - label = pdsparser.Pds3Label(filespec, method='fast').as_dict() + label = pdsparser.Pds3Label(filespec, method=method).as_dict() # Insert "data_file" and "header_recs" # Convert ISIS .qub info to a standard PDS3 label dictionary diff --git a/oops/hosts/galileo/ssi/__init__.py b/oops/hosts/galileo/ssi/__init__.py index c96676fc..f5eb5a00 100755 --- a/oops/hosts/galileo/ssi/__init__.py +++ b/oops/hosts/galileo/ssi/__init__.py @@ -20,7 +20,7 @@ # Standard class methods ################################################################################ def from_file(filespec, - return_all_planets=False, full_fov=False, **parameters): + return_all_planets=False, full_fov=False, method='strict', **parameters): """A general, static method to return a Snapshot object based on a given Galileo SSI image file. By default, only the valid image region is returned. @@ -33,6 +33,7 @@ def from_file(filespec, full_fov: If True, the full image is returned with a mask describing the regions with no data. + method: Label reading method to be passed to Pds3Label. """ SSI.initialize() # Define everything the first time through; use defaults @@ -41,7 +42,7 @@ def from_file(filespec, filespec = FCPath(filespec) # Load the PDS label - label = pdsparser.Pds3Label(filespec).as_dict() + label = pdsparser.Pds3Label(filespec, method=method).as_dict() # Load the data array vic = vicar.VicarImage.from_file(filespec) @@ -87,15 +88,13 @@ def from_index(filespec, supplemental_filespec=None, full_fov=False, **parameter # Read the index file COLUMNS = [] # Return all columns - local_path = filespec.retrieve(filespec) - table = pdstable.PdsTable(local_path, columns=COLUMNS) + table = pdstable.PdsTable(filespec, columns=COLUMNS) row_dicts = table.dicts_by_row() # Read the supplemental index file if supplemental_filespec is not None: supplemental_filespec = FCPath(supplemental_filespec) - supplemental_local_path = supplemental_filespec.retrieve() - table = pdstable.PdsTable(supplemental_local_path) + table = pdstable.PdsTable(supplemental_filespec) supplemental_row_dicts = table.dicts_by_row() # # Sort supplemental rows to match index file diff --git a/oops/hosts/juno/jiram/__init__.py b/oops/hosts/juno/jiram/__init__.py index 01e8fad0..76e1faa3 100644 --- a/oops/hosts/juno/jiram/__init__.py +++ b/oops/hosts/juno/jiram/__init__.py @@ -17,24 +17,21 @@ ################################################################################ # Standard class methods ################################################################################ -def from_file(filespec, return_all_planets=False, **parameters): +def from_file(filespec, return_all_planets=False, method='strict', **parameters): """A general, static method to return a Snapshot object based on a given JIRAM image or spectrum file. Inputs: return_all_planets Include kernels for all planets not just Jupiter or Saturn. + method Label reading method to be passed to Pds3Label. """ JIRAM.initialize() # Define everything the first time through; use # defaults unless initialize() is called explicitly. # Load the PDS label filespec = FCPath(filespec) - lbl_filespec = filespec.with_suffix('.LBL') - local_filespec = filespec.retrieve() - local_lbl_filespec = lbl_filespec.retrieve() - recs = pdsparser.PdsLabel.load_file(local_lbl_filespec) - label = pdsparser.PdsLabel.from_string(recs).as_dict() + label = pdsparser.Pds3Label(filespec, method=method).as_dict() # Get common metadata meta = Metadata(label) @@ -49,14 +46,14 @@ def from_file(filespec, return_all_planets=False, **parameters): # Image if ext.upper() == '.IMG': from . import img - return img.from_file(local_filespec, label, - return_all_planets=False, **parameters) + return img.from_file(filespec, label, + return_all_planets=return_all_planets, **parameters) # Spectrum if ext.upper() == '.DAT': from . import spe - return spe.from_file(local_filespec, label, - return_all_planets=False, **parameters) + return spe.from_file(filespec, label, + return_all_planets=return_all_planets, **parameters) return None diff --git a/oops/hosts/juno/junocam/__init__.py b/oops/hosts/juno/junocam/__init__.py index 8522d10a..b6d7ee3d 100644 --- a/oops/hosts/juno/junocam/__init__.py +++ b/oops/hosts/juno/junocam/__init__.py @@ -1,5 +1,5 @@ ################################################################################ -# oops/inst/juno/junocam.py +# oops/inst/juno/junocam/__init__.py ################################################################################ import re @@ -25,7 +25,7 @@ #=============================================================================== ### Avoid two horizontal separators in a row. One should suffice. def from_file(filespec, fast_distortion=True, - return_all_planets=False, snap=False, **parameters): + return_all_planets=False, snap=False, method='strict', **parameters): """A general, static method to return a Pushframe object based on a given JUNOCAM image file. @@ -41,20 +41,16 @@ def from_file(filespec, fast_distortion=True, snap True to model the image as a Snapshot rather than as a TimedImage. + method Label reading method to be passed to Pds3Label. """ JUNOCAM.initialize() # Define everything the first time through; use # defaults unless initialize() is called explicitly. ### I think we recommend a blank line after a multi-line docstring. filespec = FCPath(filespec) + # Load the PDS label - lbl_filespec = filespec.with_suffix('.LBL') -### This failed for me because the files come off the PDS archive volumes in -### upper case, so they end in '.IMG', not 'img'. You need to find a way to make -### this work regardless of the case of either file extension. - local_lbl_filespec = lbl_filespec.retrieve() - recs = pdsparser.PdsLabel.load_file(local_lbl_filespec) - label = pdsparser.PdsLabel.from_string(recs).as_dict() + label = pdsparser.Pds3Label(filespec, method=method).as_dict() # Get composite image metadata meta = Metadata(label) @@ -118,7 +114,7 @@ def from_file(filespec, fast_distortion=True, # item.insert_subfield('spice_kernels', \ # Juno.used_kernels(item.time, 'junocam', return_all_planets)) item.insert_subfield('filespec', filespec) - item.insert_subfield('basename', os.path.basename(filespec)) + item.insert_subfield('basename', filespec.name) obs.append(item) return obs diff --git a/oops/hosts/newhorizons/lorri.py b/oops/hosts/newhorizons/lorri.py index 55adbc24..bb045385 100755 --- a/oops/hosts/newhorizons/lorri.py +++ b/oops/hosts/newhorizons/lorri.py @@ -357,8 +357,7 @@ def from_index(filespec, fov_type='fast', asof=None, meta=None, **parameters): # Read the index file COLUMNS = [] # Return all columns TIMES = ['START_TIME'] # Convert this one to TAI - local_path = filespec.retrieve() - table = pdstable.PdsTable(local_path, columns=COLUMNS, times=TIMES) + table = pdstable.PdsTable(filespec, columns=COLUMNS, times=TIMES) row_dicts = table.dicts_by_row() # Create a list of Snapshot objects diff --git a/oops/hosts/voyager/iss.py b/oops/hosts/voyager/iss.py index ae17a7e9..5fbf3faa 100755 --- a/oops/hosts/voyager/iss.py +++ b/oops/hosts/voyager/iss.py @@ -18,8 +18,7 @@ ################################################################################ # Standard class methods ################################################################################ - -def from_file(filespec, astrometry=False, action='error', parameters={}): +def from_file(filespec, astrometry=False, action='error', method='strict', parameters=None): """A general, static method to return a Snapshot object based on a given Voyager ISS image file or its label. @@ -29,15 +28,19 @@ def from_file(filespec, astrometry=False, action='error', parameters={}): action What to do for a missing C kernel entry, via the Python warnings interface: 'error', 'ignore', 'always', 'default', 'module', 'once'. + parameters Dictionary of VGR-ISS-specific parameters. + method Label reading method to be passed to Pds3Label. """ + if parameters is None: + parameters={} + ISS.initialize() # Define everything the first time through filespec = FCPath(filespec) # Load the PDS label if available if filespec.name.upper().endswith('.LBL'): - local_path = filespec.retrieve() - label_dict = pdsparser.PdsLabel.from_file(local_path).as_dict() + label_dict = pdsparser.Pds3Label(filespec, method=method).as_dict() imagefile = label_dict['^IMAGE'][0] imagespec = filespec.with_name(imagefile) else: @@ -48,19 +51,16 @@ def from_file(filespec, astrometry=False, action='error', parameters={}): labelspec = filespec.with_suffix('.lbl') try: - local_labelspec = labelspec.retrieve() + label_dict = pdsparser.Pds3Label(labelspec, method=method).as_dict() except FileNotFoundError: label_dict = None - else: - label_dict = pdsparser.PdsLabel.from_file(local_labelspec).as_dict() imagespec = filespec # Load the VICAR file vicar_dict = label_dict if not astrometry: - local_imagespec = imagespec.retrieve() - vic = vicar.VicarImage.from_file(local_imagespec) + vic = vicar.VicarImage.from_file(imagespec) vicar_dict = vic.as_dict() # Get key information, preferably from the PDS label @@ -83,7 +83,7 @@ def from_file(filespec, astrometry=False, action='error', parameters={}): target = label_dict['TARGET_NAME'] filter = label_dict['FILTER_NAME'] factor = label_dict['IMAGE']['REFLECTANCE_SCALING_FACTOR'] - else: + elif vicar_dict is not None: lab02 = vicar_dict['LAB02'] lab03 = vicar_dict['LAB03'] stop_time = '19%s-%sT%s' % (lab02[47:49],lab02[50:53],lab02[54:62]) @@ -107,9 +107,11 @@ def from_file(filespec, astrometry=False, action='error', parameters={}): filter = lab03[37:43].rstrip() factor = None + else: + return None # Interpret the GEOMED parameter - if 'GEOMA' in vic['TASK+']: + if (not astrometry) and ('GEOMA' in vic['TASK+']): assert vic.data_2d.shape == (1000,1000) fovs = { 'NAC': ISS.fovs['NAC_GEOMED'], @@ -173,8 +175,8 @@ def from_file(filespec, astrometry=False, action='error', parameters={}): result = oops.obs.Snapshot(('v','u'), tstart, texp, fovs[camera], path = spacecraft, frame = image_frame, - dict = vicar_dict, # Add the VICAR dict - data = vic.data_2d, # Add the data array + dict = vicar_dict, # Add the VICAR dict + data = (None if astrometry else vic.data_2d), # Add the data array instrument = 'ISS', detector = camera, filter = filter, @@ -183,7 +185,7 @@ def from_file(filespec, astrometry=False, action='error', parameters={}): filespec = filespec, basename = filespec.name) - # TODO if factor is not None: + # TODO if factor: # result.insert_subfield('extended_calib', # oops.calib.ExtendedSource('I/F', factor)) @@ -211,8 +213,7 @@ def from_index(filespec, geomed=False, action='ignore', omit=True, # Read the index file COLUMNS = [] # Return all columns - local_path = filespec.retrieve() - table = pdstable.PdsTable(local_path, columns=COLUMNS) + table = pdstable.PdsTable(filespec, columns=COLUMNS) row_dicts = table.dicts_by_row() # Interpret GEOMED parameter diff --git a/spicedb/__init__.py b/spicedb/__init__.py index f02ddfa4..41c0bfe1 100755 --- a/spicedb/__init__.py +++ b/spicedb/__init__.py @@ -3530,4 +3530,4 @@ def translator2(filepath): ################################################################################ if __name__ == '__main__': unittest.main(verbosity=2) -################################################################################ +################################################################################ \ No newline at end of file diff --git a/tests/hosts/juno/__init__.py b/tests/hosts/juno/__init__.py index 17376c12..9d6a7ff3 100644 --- a/tests/hosts/juno/__init__.py +++ b/tests/hosts/juno/__init__.py @@ -1,471 +1,3 @@ ################################################################################ -# oops/inst/juno/__init__.py -# -# Utility functions for managing SPICE kernels while working with juno data -# sets. +# tests/hosts/juno/__init__.py ################################################################################ - -import numpy as np - -import julian -import spicedb -import cspyce -import oops - -from oops.body import Body - -################################################################################ -# Routines for managing the loading of C and SP kernels -################################################################################ - -# Make sure the leap seconds have been loaded -oops.spice.load_leap_seconds() - -# We load CK and SPK files on a very rough month-by-month basis. This is simpler -# than a more granular approach involving detailed calendar calculations. We -# divide the period October 1, 1997 to October 1, 2017 up into 240 "months" of -# equal length. Given any TDB, we quickly determine the month within which it -# falls. Each month is associated with a list of kernels that should be loaded -# whenever information is needed about any time within that month +/- 12 hours. -# The kernels needed for a given month only get loaded when they are needed, and -# are only loaded once. For any geometry calculation involving Juno, a quick -# call to load_ck(time) or load_spk(time) will ensure that the information is -# available. - -################################################################################ - -#******************************************************************************* -class Juno(object): - """An instance-free class to hold Juno-specific parameters.""" - - START_TIME = '2011-08-01' - STOP_TIME = '2025-08-01' - MONTHS = 168 # 14 years * 12 months/year - - TDB0 = julian.tdb_from_tai(julian.tai_from_iso(START_TIME)) - TDB1 = julian.tdb_from_tai(julian.tai_from_iso(STOP_TIME)) - DTDB = (TDB1 - TDB0) / MONTHS - SLOP = 43200. - - CK_LOADED = np.zeros(MONTHS, dtype='bool') # True if month was loaded - CK_LIST = np.empty(MONTHS, dtype='object') # Kernels needed by month - CK_DICT = {} # Dictionary keyed by filespec returns kernel info - # object, but only if loaded. - - SPK_LOADED = np.zeros(MONTHS, dtype='bool') - SPK_LIST = np.empty(MONTHS, dtype='object') - SPK_DICT = {} - - loaded_instruments = [] - - initialized = False - - #=========================================================================== - @staticmethod - def initialize(ck='reconstructed', planets=None, asof=None, - spk='reconstructed', gapfill=True, - mst_pck=True, irregulars=True): - """Intialize the Juno mission internals. - - After the first call, later calls to this function are ignored. - - Input: - ck,spk Used to specify which C and SPK kernels are used.: - 'reconstructed' for the reconstructed kernels (default); - 'predicted' for the predicted kernels; - 'none' to allow manual control of the C kernels. - planets A list of planets to pass to define_solar_system. None - or 0 means all. - asof Only use SPICE kernels that existed before this date; - None to ignore. - gapfill True to include gapfill CKs. False otherwise. - mst_pck True to include MST PCKs, which update the rotation - models for some of the small moons. - irregulars True to include the irregular satellites; - False otherwise. - """ - if Juno.initialized: return - - (ck, spk) = ('NONE', 'NONE') - - # Define some important paths and frames - Body.define_solar_system(Juno.START_TIME, Juno.STOP_TIME, - asof=asof, - planets=planets, - mst_pck=mst_pck, - irregulars=irregulars) - - ignore = oops.path.SpicePath('JUNO', 'JUPITER') - - spicedb.open_db() - - spk = spk.upper() - if spk == 'NONE': - - # This means no SPK will ever be loaded; handling is manual - Juno.initialize_kernels([], Juno.SPK_LIST) - Juno.SPK_LOADED = np.ones(Juno.MONTHS, dtype='bool') - else: - kernels = spicedb.select_spk(-61, name='JUNO_-SPK-' + spk, - time=(Juno.START_TIME, - Juno.STOP_TIME), - asof=asof) - Juno.initialize_kernels(kernels, Juno.SPK_LIST) - - ck = ck.upper() - if ck == 'NONE': - - # This means no CK will ever be loaded; handling is manual - Juno.initialize_kernels([], Juno.CK_LIST) - Juno.CK_LOADED = np.ones(Juno.MONTHS, dtype='bool') - else: - kernels = spicedb.select_ck(-61, name='JUNO_-CK-' + ck, - time=(Juno.START_TIME, - Juno.STOP_TIME), - asof=asof) - Juno.initialize_kernels(kernels, Juno.CK_LIST) - - # Load extra kernels if necessary - if gapfill and ck not in ('PREDICTED', 'NONE'): - _ = spicedb.furnish_ck(-61, name='JUNO_-CK-GAPFILL') - - spicedb.close_db() - - Juno.initialized = True - - #=========================================================================== - @staticmethod - def reset(): - """Reset the internal parameters. - - Can be useful for debugging. - """ - Juno.loaded_instruments = [] - - Juno.CK_LOADED = np.zeros(Juno.MONTHS, dtype='bool') - Juno.CK_LIST = np.empty(Juno.MONTHS, dtype='object') - Juno.CK_DICT = {} - - Juno.SPK_LOADED = np.zeros(Juno.MONTHS, dtype='bool') - Juno.SPK_LIST = np.empty(Juno.MONTHS, dtype='object') - Juno.SPK_DICT = {} - - Juno.initialized = False - - #=========================================================================== - @staticmethod - def load_ck(t): - """Ensure that the C kernels applicable at or near the given time have - been furnished. - - The time can be tai or tdb. - """ - Juno.load_kernels(t, t, Juno.CK_LOADED, Juno.CK_LIST, - Juno.CK_DICT) - - #=========================================================================== - @staticmethod - def load_cks(t0, t1): - """Ensure that all the C kernels applicable near or within the time - interval tdb0 to tdb1 have been furnished. - - The time can be tai or tdb. - """ - Juno.load_kernels(t0, t1, Juno.CK_LOADED, Juno.CK_LIST, - Juno.CK_DICT) - - #=========================================================================== - @staticmethod - def load_spk(t): - """Ensure that the SPK kernels applicable at or near the given time have - been furnished. - - The time can be tai or tdb. - """ - Juno.load_kernels(t, t, Juno.SPK_LOADED, Juno.SPK_LIST, - Juno.SPK_DICT) - - #=========================================================================== - @staticmethod - def load_spks(t0, t1): - """Ensure that all the SPK kernels applicable near or within the time - interval tdb0 to tdb1 have been furnished. - - The time can be tai or tdb. - """ - Juno.load_kernels(t0, t1, Juno.SPK_LOADED, Juno.SPK_LIST, - Juno.SPK_DICT) - - #=========================================================================== - @staticmethod - def load_kernels(t0, t1, loaded, lists, kernel_dict): - """Load kernal pool.""" - - from spicedb import get_spice_filecache_prefix - - SPICE_FILECACHE_PFX = get_spice_filecache_prefix() - - paths = SPICE_FILECACHE_PFX.retrieve([ - 'Juno/CK/juno_sc_rec_131006_131012_v01.bc', - 'Juno/SPK/spk_rec_131005_131014_131101.bsp', - 'Juno/CK/juno_sc_rec_161211_161217_v01.bc', - 'Juno/SPK/juno_rec_161115_170106_170113.bsp', - 'Juno/CK/juno_sc_rec_170702_170708_v01.bc', - 'Juno/SPK/juno_rec_170608_170728_170803.bsp', - 'Juno/CK/juno_sc_rec_171023_171025_v01.bc', - 'Juno/SPK/juno_rec_170918_171121_171127.bsp', - 'Juno/CK/juno_sc_rec_171215_171217_v01.bc', - 'Juno/SPK/juno_rec_171121_180113_180117.bsp', - 'Juno/CK/juno_sc_rec_180523_180524_v01.bc', - 'Juno/SPK/juno_rec_180429_180621_180626.bsp', - 'Juno/CK/juno_sc_rec_180906_180907_v01.bc', - 'Juno/SPK/juno_rec_180812_181004_181011.bsp', - 'Juno/CK/juno_sc_rec_190405_190406_v01.bc', - 'Juno/SPK/juno_rec_190312_190504_190509.bsp', - 'Juno/CK/juno_sc_rec_190911_190912_v01.bc', - 'Juno/SPK/juno_rec_190817_191010_191022.bsp', - 'Juno/CK/juno_sc_rec_200405_200411_v01.bc', - 'Juno/SPK/juno_rec_200316_200508_200512.bsp', - 'Juno/CK/juno_sc_rec_200719_200725_v01.bc', - 'Juno/SPK/juno_rec_200629_200822_200826.bsp', - 'Juno/CK/juno_sc_rec_201108_201114_v01.bc', - 'Juno/SPK/juno_rec_201014_201205_201208.bsp', - 'Juno/CK/juno_sc_rec_201227_210102_v01.bc', - 'Juno/SPK/juno_rec_201205_210127_210210.bsp', - 'Juno/CK/juno_sc_rec_210221_210227_v01.bc', - 'Juno/SPK/juno_rec_210127_210321_210329.bsp', - 'Juno/CK/juno_sc_rec_210221_210227_v01.bc', - 'Juno/SPK/juno_rec_210127_210321_210329.bsp', - 'Juno/CK/juno_sc_rec_190528_190529_v01.bc', - 'Juno/SPK/juno_rec_190504_190626_190627.bsp', - - 'Juno/CK/juno_sc_rec_160710_160716_v01.bc', - 'Juno/CK/juno_sc_rec_160717_160723_v01.bc', - 'Juno/SPK/spk_rec_160522_160729_160909.bsp', - - 'Juno/CK/juno_sc_rec_170827_170902_v01.bc', - 'Juno/CK/juno_sc_rec_170903_170909_v01.bc', - 'Juno/SPK/spk_rec_170728_170918_170922.bsp', - - 'Juno/CK/juno_sc_rec_180715_180716_v01.bc', - 'Juno/SPK/spk_rec_180620_180812_180821.bsp', - - 'General/LSK/naif0012.tls', - 'Juno/SCLK/jno_sclkscet_00128.tsc', - 'Juno/FK/juno_v12.tf', - 'Juno/IK/juno_junocam_v03.ti', - 'Juno/IK/juno_jiram_v02.ti', - 'Juno/SPK/de421.bsp', - 'Juno/SPK/de432s.bsp', - ]) - for path in paths: - cspyce.furnsh(path) - -### This would be best handled by creating a text file that contains a list of the -### file names to be loaded (starting with "ck/, "spk/" etc.). Maintain this file -### separately from the Python code. The Python code reads the list and furnishes -### inside a big loop. When a new SPK or CK is released, we just add it to the list -### and update it in the repo. See hosts/solar for how to allow a Python module to -### locate a file within its own directory path. The kernels can live inside the -### OOPS-Resources/SPICE tree, so you can determine the value of "kdir" from the -### value of (unittester_support.OOPS_RESOURCES_ + 'SPICE/Juno/'). -### -### Even better, adopt the Cassini module's method of just loading the CKs and SPKs -### as they are requested, because there are so many of them. It'll just furnish the -### CKs and SPKs when it needs them, in the background, silently. -### -### Other notes... -### -### There are a lot of duplicated files on Dropbox between -### OOPS-Resources/SPICE/Juno and test_data/juno/kernels. Better to keep a single -### set in the former. If kernels start to change out from underneath us in a way -### that breaks unit tests, that would be the time to start keeping a specific -### subset inside test_data/juno. -### -### The set of CKs and SPKs on Dropbox is currently incomplete. We need to start -### maintaining it, and continue to do so once we are generating metadata tables. -### -### I see on the NAIF ftp server that there are multiple versions of the CKs and -### SPKs, as there were for Cassini. We probably never need to worry about anything -### but the reconstructed. However, in order to allow for multiple future versions, -### I suggest you rename SPICE/Juno/CK to SPICE/Juno/CK-reconstructed and -### SPICE/Juno/SPK to SPICE/Juno/SPK-reconstructed. This will make for simpler file -### management going forward. -### -### The other day, you asked me to add a few kernels to SPICE.db. It's not clear to -### me why that was necessary. The kernel management method I described above should -### be sufficient for all Juno kernel management, and is the reason I've concluded -### we should get rid of the sqlite database. - - return - -## TODO: - # Find the range of months needed - m1 = int((t0 - Juno.TDB0) // Juno.DTDB) - m2 = int((t1 - Juno.TDB0) // Juno.DTDB) + 1 - - m1 = max(m1, 0) # ignore time limits outside mission duration - m2 = min(m2, Juno.MONTHS - 1) - - # Load any months not already loaded - for m in range(m1, m2+1): - if not loaded[m]: - for kernel in lists[m]: - filespec = kernel.filespec - if filespec not in kernel_dict: - spicedb.furnish_kernels([kernel]) - kernel_dict[filespec] = kernel - loaded[m] = True - - ######################################## - # Initialize the kernel lists - ######################################## - - #=========================================================================== - @staticmethod - def initialize_kernels(kernels, lists): - """After initialization, lists[m] is a the KernelInfo objects needed - within the specified month. - """ - for i in range(Juno.MONTHS): - lists[i] = [] - - for kernel in kernels: - - # Find the range of months applicable, extended by 12 hours - t0 = cspyce.str2et(kernel.start_time) - Juno.SLOP - t1 = cspyce.str2et(kernel.stop_time) + Juno.SLOP - - m1 = int((t0 - Juno.TDB0) // Juno.DTDB) - m2 = int((t1 - Juno.TDB0) // Juno.DTDB) + 1 - - m1 = max(m1, 0) # ignore time limits outside mission duration - m2 = min(m2, Juno.MONTHS - 1) - - # Add this kernel to each month's list - for m in range(m1, m2+1): - lists[m] += [kernel] - - ############################################################################ - # Routines for managing the loading other kernels - ############################################################################ - - #=========================================================================== - @staticmethod - def load_instruments(instruments=[], asof=None): - """Load the SPICE kernels and defines the basic paths and frames for - the Juno mission. - - It is generally only be called once. - - Input: - instruments an optional list of instrument names for which to load - frames kernels. The frames for JUNOCAM are always loaded. - - asof if this specifies a date or date-time in ISO format, - then only kernels that existed before the specified date - are used. Otherwise, the most recent versions are always - loaded. - """ - - # Load the default instruments on the first pass - if Juno.loaded_instruments == []: - instruments += ['JUNOCAM'] - - # On later calls, return quickly if there's nothing to do - if instruments == []: return - - # Check the formatting of the "as of" date - if asof is not None: - (day, sec) = julian.day_sec_from_iso(asof) - asof = julian.ymdhms_format_from_day_sec(day, sec) - - # Furnish instruments and frames - spicedb.open_db() - _ = spicedb.furnish_inst(-61, inst=instruments, asof=asof) - spicedb.close_db() - - ############################################################################ - # Routines for managing text kernel information - ############################################################################ - -### TODO: finish these routines... - - #=========================================================================== - @staticmethod - def spice_instrument_kernel(inst, asof=None): - """Return a dictionary containing the Instrument Kernel information. - - Also furnishes it for use by the SPICE tools. - - Input: - inst one of "JUNOCAM", etc. - asof an optional date in the past, in ISO date or date-time - format. If provided, then the information provided will - be applicable as of that date. Otherwise, the most - recent information is always provided. - - Return: a tuple containing: - the dictionary generated by textkernel.from_file() - the name of the kernel. - """ - if asof is not None: - (day,sec) = julian.day_sec_from_iso(stop_time) - asof = julian.ymdhms_format_from_day_sec(day, sec) - - spicedb.open_db() - kernel_info = spicedb.select_inst(-61, types='IK', inst='JUNOCAM', asof=asof) - spicedb.furnish_kernels(kernel_info, fast=True) - spicedb.close_db() - - return (spicedb.as_dict(kernel_info), spicedb.as_names(kernel_info)[0]) - - #=========================================================================== - @staticmethod - def spice_frames_kernel(asof=None): - """Return a dictionary containing the Juno Frames Kernel information. - - Also furnishes the kernels for use by the SPICE tools. - - Input: - asof an optional date in the past, in ISO date or date-time - format. If provided, then the information provided will - be applicable as of that date. Otherwise, the most - recent information is always provided. - - Return: a tuple containing: - the dictionary generated by textkernel.from_file() - an ordered list of the names of the kernels - """ - if asof is not None: - (day,sec) = julian.day_sec_from_iso(stop_time) - asof = julian.ymdhms_format_from_day_sec(day, sec) - - spicedb.open_db() - kernel_list = spicedb.select_inst(-61, types='FK', asof=asof) - spicedb.furnish_kernels(kernel_info, fast=True) - spicedb.close_db() - - return (spicedb.as_dict(kernel_list), spicedb.as_names(kernel_list)) - - #=========================================================================== - @staticmethod - def used_kernels(time, inst, return_all_planets=False): - """Return the list of kernels associated with a Juno observation at - a selected range of times. - """ - if return_all_planets: - bodies = [1, 199, 2, 299, 3, 399, 4, 499, 5, 599, 6, 699, - 7, 799, 8, 899] - if time[0] >= TOUR: - bodies += body.SATURN_MOONS_LOADED - else: - bodies += body.JUPITER_MOONS_LOADED - else: - if time[0] >= TOUR: - bodies = [6, 699] + body.SATURN_MOONS_LOADED - else: - bodies = [5, 599] + body.JUPITER_MOONS_LOADED - - return spicedb.used_basenames(time=time, inst=inst, sc=-61, - bodies=bodies) diff --git a/tests/hosts/juno/junocam/__init__.py b/tests/hosts/juno/junocam/__init__.py index 36af9346..b0420084 100644 --- a/tests/hosts/juno/junocam/__init__.py +++ b/tests/hosts/juno/junocam/__init__.py @@ -1,8 +1,6 @@ ################################################################################ -# oops/inst/juno/junocam.py +# oops/inst/juno/junocam/__init__.py ################################################################################ - -import os import unittest import oops.gold_master as gm @@ -13,7 +11,7 @@ class Test_Juno_Junocam_GoldMaster(unittest.TestCase): #=========================================================================== def setUp(self): - from oops.hosts.juno.junocam import standard_obs + from tests.hosts.juno.junocam import standard_obs #=========================================================================== def test_JNCR_2016347_03C00192_V01(self): diff --git a/tests/hosts/juno/junocam/standard_obs.py b/tests/hosts/juno/junocam/standard_obs.py index f050c5dd..4194baa1 100644 --- a/tests/hosts/juno/junocam/standard_obs.py +++ b/tests/hosts/juno/junocam/standard_obs.py @@ -3,9 +3,7 @@ ################################################################################ import os import unittest -import oops.backplane.gold_master as gm - -from oops.unittester_support import TEST_DATA_PREFIX +import oops.gold_master as gm # Because JunoCam has such a large, distorted FOV, we need to assign the # backplanes an especially large inventory border: border=10 seems to work.