diff --git a/ale/drivers/lro_drivers.py b/ale/drivers/lro_drivers.py index 225732fd69db999606b4f73b61a1b22446518c6e..6b2d2dbbb4f90b7ccf567b85f13b8b82719e9424 100644 --- a/ale/drivers/lro_drivers.py +++ b/ale/drivers/lro_drivers.py @@ -12,92 +12,133 @@ from ale.base.label_pds3 import Pds3Label from ale.base.type_sensor import LineScanner -class LroLrocPds3LabelNaifSpiceDriver(Driver, NaifSpice, Pds3Label, LineScanner): - """ - Driver for reading Lroc NACL, NACR, WAC labels. Requires a Spice mixin to - acquire addtional ephemeris and instrument data located exclusively in - spice kernels. - """ - - @property - def instrument_id(self): +class LroLrocPds3LabelNaifSpiceDriver(NaifSpice, Pds3Label, LineScanner, Driver): """ - Returns an instrument id for uniquely identifying the instrument, but often - also used to be piped into Spice Kernels to acquire IKIDs. Therefore they - are the same ID that spice lib expects as input to bods2c calls. - - Ignores Wide Angle for now - - Returns - ------- - : str - instrument id + Driver for reading LROC NACL, NACR (not WAC, it is a push frame) labels. Requires a Spice mixin to + acquire addtional ephemeris and instrument data located exclusively in SPICE kernels, A PDS3 label, + and the LineScanner and Driver bases. """ - instrument = self.label.get("INSTRUMENT_ID") - - # NACL, NACR, or WAC - frame_id = self.label.get("FRAME_ID") - - if instrument == "LROC" and frame_id == "LEFT": - return "LRO_LROCNACL" - elif instrument == "LROC" and frame_id == "RIGHT": - return "LRO_LROCNACR" - # WAC not implemented yet - - @property - def metakernel(self): - """ - Returns latest instrument metakernels + @property + def instrument_id(self): + """ + The short text name for the instrument + + Returns an instrument id uniquely identifying the instrument. Used to acquire + instrument codes from Spice Lib bods2c routine. + + Returns + ------- + str + The short text name for the instrument + """ + + instrument = super().instrument_id + + frame_id = self.label.get("FRAME_ID") + + if instrument == "LROC" and frame_id == "LEFT": + return "LRO_LROCNACL" + elif instrument == "LROC" and frame_id == "RIGHT": + return "LRO_LROCNACR" + + + @property + def metakernel(self): + """ + Returns latest instrument metakernels + + Returns + ------- + : str + Path to latest metakernel file + """ + metakernel_dir = config.lro + + mks = sorted(glob(os.path.join(metakernel_dir, '*.tm'))) + if not hasattr(self, '_metakernel'): + for mk in mks: + if str(self.start_time.year) in os.path.basename(mk): + self._metakernel = mk + return self._metakernel + + + @property + def spacecraft_name(self): + """ + Spacecraft name used in various SPICE calls to acquire + ephemeris data. LROC NAC img PDS3 labels do not the have SPACECRAFT_NAME keyword, so we + override it here to use the label_pds3 property for instrument_host_id + + Returns + ------- + : str + Spacecraft name + """ + return self.instrument_host_id + + @property + def sensor_model_version(self): + """ + Returns ISIS instrument sensor model version number + + Returns + ------- + : int + sensor model version + """ + return 2 + + @property + def detector_start_sample(self): + """ + Returns the starting sample contained in the image + + Returns + ------- + : int + Returns the starting sample + """ + return 1 + + @property + def detector_start_line(self): + """ + Returns the starting line contained in the image + + Returns + ------- + : int + Returns the starting line + """ + return 1 + + @property + def usgscsm_distortion_model(self): + """ + The distortion model name with its coefficients + + LRO LROC NAC does not use the default distortion model so we need to overwrite the + method packing the distortion model into the ISD. + + Returns + ------- + : dict + Returns a dict with the model name : dict of the coefficients + """ + + return {"lrolrocnac": + {"coefficients": self.odtk}} + + @property + def odtk(self): + """ + The coefficients for the distortion model + + Returns + ------- + : list + Radial distortion coefficients. There is only one coefficient for LROC NAC l/r + """ + return spice.gdpool('INS{}_OD_K'.format(self.ikid), 0, 1).tolist() - Returns - ------- - : string - Path to latest metakernel file - """ - metakernel_dir = config.lro - - mks = sorted(glob(os.path.join(metakernel_dir, '*.tm'))) - if not hasattr(self, '_metakernel'): - for mk in mks: - if str(self.start_time.year) in os.path.basename(mk): - self._metakernel = mk - return self._metakernel - - - @property - def spacecraft_name(self): - """ - Spacecraft name used in various SPICE calls to acquire - ephemeris data. - - Returns - ------- - : str - Spacecraft name - """ - return "LRO" - - - @property - def odtk(self): - """ - Returns - ------- - : list - Radial distortion coefficients - """ - return spice.gdpool('INS{}_OD_K'.format(self.ikid), 0, 1).tolist() - - @property - def optical_distortion(self): - """ - LRO LROC NAC does not use the default distortion model so we need to overwrite the - method packing the distortion model into the ISD. - - """ - return { - "lrolrocnac": { - "coefficients": self.odtk, - } - } diff --git a/tests/pytests/test_lro_drivers.py b/tests/pytests/test_lro_drivers.py index 95421db735c0a64cf52393f59277c58273ba0f0d..cfabb358342fe699097eeb3fa56577ce790df0df 100644 --- a/tests/pytests/test_lro_drivers.py +++ b/tests/pytests/test_lro_drivers.py @@ -1,122 +1,64 @@ -from collections import namedtuple -from unittest import mock - import pytest +# 'Mock' the spice module where it is imported +from conftest import SimpleSpice, get_mockkernels + +from collections import namedtuple + import ale from ale.drivers import lro_drivers from ale.base import data_naif +from ale.base import label_pds3 + +from unittest.mock import PropertyMock, patch -from ale.drivers.lro_drivers import LroLrocPds3LabelNaifSpiceDriver from ale import util -# 'Mock' the spice module where it is imported -from conftest import SimpleSpice, get_mockkernels +from ale.drivers.lro_drivers import LroLrocPds3LabelNaifSpiceDriver + simplespice = SimpleSpice() data_naif.spice = simplespice lro_drivers.spice = simplespice +label_pds3.spice = simplespice LroLrocPds3LabelNaifSpiceDriver.metakernel = get_mockkernels @pytest.fixture -def lro_lroclabel(): - return """ - PDS_VERSION_ID = PDS3 - - /*FILE CHARACTERISTICS*/ - RECORD_TYPE = FIXED_LENGTH - RECORD_BYTES = 5064 - FILE_RECORDS = 13313 - LABEL_RECORDS = 1 - ^IMAGE = 2 - - /*DATA IDENTIFICATION*/ - DATA_SET_ID = "LRO-L-LROC-2-EDR-V1.0" - ORIGINAL_PRODUCT_ID = nacl0002fc60 - PRODUCT_ID = M128963531LE - MISSION_NAME = "LUNAR RECONNAISSANCE ORBITER" - MISSION_PHASE_NAME = "NOMINAL MISSION" - INSTRUMENT_HOST_NAME = "LUNAR RECONNAISSANCE ORBITER" - INSTRUMENT_HOST_ID = LRO - INSTRUMENT_NAME = "LUNAR RECONNAISSANCE ORBITER CAMERA" - INSTRUMENT_ID = LROC - LRO:PREROLL_TIME = 2010-05-20T02:57:44.373 - START_TIME = 2010-05-20T02:57:44.720 - STOP_TIME = 2010-05-20T02:57:49.235 - LRO:SPACECRAFT_CLOCK_PREROLL_COUNT = "1/296017064:22937" - SPACECRAFT_CLOCK_START_COUNT = "1/296017064:45694" - SPACECRAFT_CLOCK_STOP_COUNT = "1/296017069:13866" - ORBIT_NUMBER = 4138 - PRODUCER_ID = LRO_LROC_TEAM - PRODUCT_CREATION_TIME = 2013-09-16T19:57:12 - PRODUCER_INSTITUTION_NAME = "ARIZONA STATE UNIVERSITY" - PRODUCT_TYPE = EDR - PRODUCT_VERSION_ID = "v1.8" - UPLOAD_ID = "SC_2010140_0000_A_V01.txt" - - /*DATA DESCRIPTION*/ - TARGET_NAME = "MOON" - RATIONALE_DESC = "TARGET OF OPPORTUNITY" - FRAME_ID = LEFT - DATA_QUALITY_ID = "0" - DATA_QUALITY_DESC = "The DATA_QUALITY_ID is set to an 8-bit - value that encodes the following data quality information for the - observation. For each bit a value of 0 means FALSE and a value of 1 means - TRUE. More information about the data quality ID can be found in the LROC - EDR/CDR SIS, section 3.3 'Label and Header Descriptions'. - Bit 1: Temperature of focal plane array is out of bounds. - Bit 2: Threshold for saturated pixels is reached. - Bit 3: Threshold for under-saturated pixels is reached. - Bit 4: Observation is missing telemetry packets. - Bit 5: SPICE information is bad or missing. - Bit 6: Observation or housekeeping information is bad or missing. - Bit 7: Spare. - Bit 8: Spare." - - /*ENVIRONMENT*/ - LRO:TEMPERATURE_SCS = 4.51 <degC> - LRO:TEMPERATURE_FPA = 17.88 <degC> - LRO:TEMPERATURE_FPGA = -12.33 <degC> - LRO:TEMPERATURE_TELESCOPE = 5.91 <degC> - LRO:TEMPERATURE_SCS_RAW = 2740 - LRO:TEMPERATURE_FPA_RAW = 2107 - LRO:TEMPERATURE_FPGA_RAW = 3418 - LRO:TEMPERATURE_TELESCOPE_RAW = 2675 - - /*IMAGING PARAMETERS*/ - CROSSTRACK_SUMMING = 1 - BANDWIDTH = 300 <nm> - CENTER_FILTER_WAVELENGTH = 600 <nm> - LINE_EXPOSURE_DURATION = 0.337600 <ms> - LRO:LINE_EXPOSURE_CODE = 0 - LRO:DAC_RESET_LEVEL = 198 - LRO:CHANNEL_A_OFFSET = 60 - LRO:CHANNEL_B_OFFSET = 123 - LRO:COMPAND_CODE = 3 - LRO:LINE_CODE = 13 - LRO:BTERM = (0,16,69,103,128) - LRO:MTERM = (0.5,0.25,0.125,0.0625,0.03125) - LRO:XTERM = (0,64,424,536,800) - LRO:COMPRESSION_FLAG = 1 - LRO:MODE = 7 - - /*DATA OBJECT*/ - OBJECT = IMAGE - LINES = 13312 - LINE_SAMPLES = 5064 - SAMPLE_BITS = 8 - SAMPLE_TYPE = LSB_INTEGER - UNIT = "RAW_INSTRUMENT_COUNT" - MD5_CHECKSUM = "0fe91f4b2e93083ee0093e7c8d05f3bc" - END_OBJECT = IMAGE - END - """ - -def test_lro_creation(lro_lroclabel): - #with LroLrocPds3LabelNaifSpiceDriver(lro_lroclabel) as m: - # d = m.to_dict() - # assert isinstance(d, dict) - - # Need to insert new tests here, one for each property unique to this driver - assert True +def driver(): + return LroLrocPds3LabelNaifSpiceDriver("") + + +@patch('ale.base.label_pds3.Pds3Label.instrument_id', 'LROC') +def test_instrument_id_left(driver): + with patch.dict(driver.label, {'FRAME_ID':'LEFT'}) as f: + assert driver.instrument_id == 'LRO_LROCNACL' + +@patch('ale.base.label_pds3.Pds3Label.instrument_id', 'LROC') +def test_instrument_id_right(driver): + with patch.dict(driver.label, {'FRAME_ID':'RIGHT'}) as f: + assert driver.instrument_id == 'LRO_LROCNACR' + +@patch('ale.base.label_pds3.Pds3Label.instrument_host_id', 'LRO') +def test_spacecraft_name(driver): + assert driver.spacecraft_name == 'LRO' + +def test_sensor_model_version(driver): + assert driver.sensor_model_version == 2 + +def test_detector_start_sample(driver): + assert driver.detector_start_sample == 1 + +def test_detector_start_line(driver): + assert driver.detector_start_sample == 1 + +@patch('ale.base.data_naif.NaifSpice.ikid', 123) +def test_odtk(driver): + assert driver.odtk == [1.0] + +@patch('ale.base.data_naif.NaifSpice.ikid', 123) +def test_usgscsm_distortion_model(driver): + distortion_model = driver.usgscsm_distortion_model + assert distortion_model['lrolrocnac']['coefficients'] == [1.0] + +