From 246ae894592f56f719260b86ce608dbcf26bd0d2 Mon Sep 17 00:00:00 2001 From: AustinSanders <austinsanders1993@gmail.com> Date: Wed, 4 May 2022 14:31:38 -0700 Subject: [PATCH] Add filter-specific calculations and tests (#450) * Added filter-specific calculations and tests * Addressed PR feedback --- ale/drivers/lro_drivers.py | 159 +++++++++++++++++++++++++++++- tests/pytests/test_lro_drivers.py | 62 ++++++------ 2 files changed, 190 insertions(+), 31 deletions(-) diff --git a/ale/drivers/lro_drivers.py b/ale/drivers/lro_drivers.py index 94b3e41..5eb3f72 100644 --- a/ale/drivers/lro_drivers.py +++ b/ale/drivers/lro_drivers.py @@ -892,6 +892,44 @@ class LroLrocWacIsisLabelIsisSpiceDriver(PushFrame, IsisLabel, IsisSpice, Radial return 2 + @property + def filter_number(self): + """ + Return the filter number from the cub label + + Returns + ------- + : int + The filter number + """ + try: + return self.label['IsisCube']['BandBin']['FilterNumber'][0] + except: + return self.label['IsisCube']['BandBin']['FilterNumber'] + + + @property + def fikid(self): + """ + Naif ID code of the filter dependent instrument codes. + + Expects ikid to be defined. This should be the integer Naif ID code for + the instrument. + + Returns + ------- + : int + Naif ID code used in calculating focal length + """ + if self.instrument_id == "LRO_LROCWAC_UV": + base = -85640 + elif self.instrument_id == "LRO_LROCWAC_VIS": + base = -85630 + + fikid = base - self.filter_number + return fikid + + @property def odtk(self): """ @@ -902,7 +940,9 @@ class LroLrocWacIsisLabelIsisSpiceDriver(PushFrame, IsisLabel, IsisSpice, Radial : list Radial distortion coefficients. """ - return [self.naif_keywords.get('INS{}_OD_K'.format(self.ikid), None)] + coeffs = self.naif_keywords.get('INS{}_OD_K'.format(self.fikid), None) + coeffs = [x * -1 for x in coeffs] + return coeffs @property @@ -989,6 +1029,12 @@ class LroLrocWacIsisLabelNaifSpiceDriver(PushFrame, IsisLabel, NaifSpice, Radial @property def sensor_name(self): + """ + Returns + ------- + : String + The name of the spacecraft + """ return self.label['IsisCube']['Instrument']['SpacecraftName'] @@ -1002,7 +1048,9 @@ class LroLrocWacIsisLabelNaifSpiceDriver(PushFrame, IsisLabel, NaifSpice, Radial : list Radial distortion coefficients. """ - return spice.gdpool('INS{}_OD_K'.format(self.ikid), 0, 3).tolist() + coeffs = spice.gdpool('INS{}_OD_K'.format(self.fikid), 0, 3).tolist() + coeffs = [x * -1 for x in coeffs] + return coeffs @property @@ -1028,7 +1076,13 @@ class LroLrocWacIsisLabelNaifSpiceDriver(PushFrame, IsisLabel, NaifSpice, Radial @property def framelets_flipped(self): - return self.label['IsisCube']['Instrument']['SpacecraftName'] == "Yes" + """ + Returns + ------- + : boolean + True if framelets are flipped, else false + """ + return self.label['IsisCube']['Instrument']['DataFlipped'] == "Yes" @property @@ -1041,12 +1095,111 @@ class LroLrocWacIsisLabelNaifSpiceDriver(PushFrame, IsisLabel, NaifSpice, Radial @property def num_frames(self): + """ + Number of frames in the image + + Returns + ------- + : int + Number of frames in the image + """ return self.image_lines // (self.framelet_height // self.sampling_factor) @property def framelet_height(self): + """ + Return the number of lines in a framelet. + + Returns + ------- + : int + The number of lines in a framelet + """ if self.instrument_id == "LRO_LROCWAC_UV": return 16 elif self.instrument_id == "LRO_LROCWAC_VIS": return 14 + + + @property + def filter_number(self): + """ + Return the filter number from the cub label + + Returns + ------- + : int + The filter number + """ + try: + return self.label['IsisCube']['BandBin']['FilterNumber'][0] + except: + return self.label['IsisCube']['BandBin']['FilterNumber'] + + + @property + def fikid(self): + """ + Naif ID code of the filter dependent instrument codes. + + Expects ikid to be defined. This should be the integer Naif ID code for + the instrument. + + Returns + ------- + : int + Naif ID code used in calculating focal length + """ + if self.instrument_id == "LRO_LROCWAC_UV": + base = -85640 + elif self.instrument_id == "LRO_LROCWAC_VIS": + base = -85630 + + fikid = base - self.filter_number + return fikid + + + @property + def pixel2focal_x(self): + """ + Expects fikid to be defined. This must be the integer Naif id code of the filter + + Returns + ------- + : list<double> + detector to focal plane x + """ + return list(spice.gdpool('INS{}_TRANSX'.format(self.fikid), 0, 3)) + + + @property + def pixel2focal_y(self): + """ + Expects fikid to be defined. This must be the integer Naif id code of the filter + + Returns + ------- + : list<double> + detector to focal plane y + """ + return list(spice.gdpool('INS{}_TRANSY'.format(self.fikid), 0, 3)) + + + @property + def detector_start_line(self): + """ + Filter-specific starting line + + Returns + ------- + : int + Zero based Detector line corresponding to the first image line + """ + offset = list(spice.gdpool('INS{}_FILTER_OFFSET'.format(self.fikid), 0, 3)) + try: + # If multiple items are present, use the first one + offset = offset[0] + except (IndexError, TypeError): + pass + return super().detector_start_line + offset diff --git a/tests/pytests/test_lro_drivers.py b/tests/pytests/test_lro_drivers.py index 0bac2f7..e16d1d6 100644 --- a/tests/pytests/test_lro_drivers.py +++ b/tests/pytests/test_lro_drivers.py @@ -276,21 +276,17 @@ class test_wac_isis_naif(unittest.TestCase): label = get_image_label('wac0000a1c4.uv.even', 'isis3') self.driver = LroLrocWacIsisLabelNaifSpiceDriver(label) - def test_short_mission_name(self): assert self.driver.short_mission_name == 'lro' - def test_intrument_id(self): assert self.driver.instrument_id == 'LRO_LROCWAC_UV' - def test_ephemeris_start_time(self): with patch('ale.drivers.lro_drivers.spice.scs2e', return_value=321) as scs2e: np.testing.assert_almost_equal(self.driver.ephemeris_start_time, 321) scs2e.assert_called_with(-85, '1/274692469:15073') - def test_detector_center_sample(self): with patch('ale.drivers.lro_drivers.spice.gdpool', return_value=np.array([1.0])) as gdpool, \ patch('ale.drivers.lro_drivers.spice.bods2c', return_value=-12345) as bods2c: @@ -298,7 +294,6 @@ class test_wac_isis_naif(unittest.TestCase): gdpool.assert_called_with('INS-12345_BORESIGHT_SAMPLE', 0, 1) bods2c.assert_called_with('LRO_LROCWAC_UV') - def test_detector_center_line(self): with patch('ale.drivers.lro_drivers.spice.gdpool', return_value=np.array([1.0])) as gdpool, \ patch('ale.drivers.lro_drivers.spice.bods2c', return_value=-12345) as bods2c: @@ -306,51 +301,53 @@ class test_wac_isis_naif(unittest.TestCase): gdpool.assert_called_with('INS-12345_BORESIGHT_LINE', 0, 1) bods2c.assert_called_with('LRO_LROCWAC_UV') - - def test_usgscsm_distortion_model(self): - with patch('ale.drivers.lro_drivers.spice.gdpool', return_value=np.array([1.0])) as gdpool, \ - patch('ale.drivers.lro_drivers.spice.bods2c', return_value=-12345) as bods2c: - distortion_model = self.driver.usgscsm_distortion_model - assert distortion_model['radial']['coefficients'] == [1.0] - gdpool.assert_called_with('INS-12345_OD_K', 0, 3) - bods2c.assert_called_with('LRO_LROCWAC_UV') - - def test_odtk(self): - with patch('ale.drivers.lro_drivers.spice.gdpool', return_value=np.array([1.0])) as gdpool, \ - patch('ale.drivers.lro_drivers.spice.bods2c', return_value=-12345) as bods2c: - assert self.driver.odtk == [1.0] - gdpool.assert_called_with('INS-12345_OD_K', 0, 3) - bods2c.assert_called_with('LRO_LROCWAC_UV') - + with patch('ale.drivers.lro_drivers.spice.gdpool', return_value=np.array([1.0])) as gdpool: + assert self.driver.odtk == [-1.0] + gdpool.assert_called_with('INS-85641_OD_K', 0, 3) def test_light_time_correction(self): assert self.driver.light_time_correction == 'LT+S' - def test_exposure_duration(self): np.testing.assert_almost_equal(self.driver.exposure_duration, 0.04) - def test_sensor_name(self): assert self.driver.sensor_name == "LUNAR RECONNAISSANCE ORBITER" - def test_framelets_flipped(self): assert self.driver.framelets_flipped == False - def test_sampling_factor(self): assert self.driver.sampling_factor == 4 - def test_num_frames(self): assert self.driver.num_frames == 260 - def test_framelet_height(self): assert self.driver.framelet_height == 16 + def test_filter_number(self): + assert self.driver.filter_number == 1 + + def test_fikid(self): + assert self.driver.fikid == -85641 + + def test_pixel2focal_x(self): + with patch('ale.drivers.lro_drivers.spice.gdpool', return_value=np.array([0, 0, -0.009])) as gdpool: + assert self.driver.pixel2focal_x == [0, 0, -0.009] + gdpool.assert_called_with('INS-85641_TRANSX', 0, 3) + + def test_pixel2focal_y(self): + with patch('ale.drivers.lro_drivers.spice.gdpool', return_value=np.array([0, 0.009, 0])) as gdpool: + assert self.driver.pixel2focal_y == [0, 0.009, 0] + gdpool.assert_called_with('INS-85641_TRANSY', 0, 3) + + def test_detector_start_line(self): + with patch('ale.drivers.lro_drivers.spice.gdpool', return_value=np.array([244])) as gdpool: + assert self.driver.detector_start_line == 244 + gdpool.assert_called_with('INS-85641_FILTER_OFFSET', 0, 3) + # ========= Test WAC isislabel and isis spice driver ========= class test_wac_isis_isis(unittest.TestCase): @@ -368,4 +365,13 @@ class test_wac_isis_isis(unittest.TestCase): np.testing.assert_almost_equal(self.driver.exposure_duration, 0.04) def test_usgscsm_distortion_model(self): - assert self.driver.usgscsm_distortion_model == {'radial': {'coefficients': [[-0.024, -0.0007]]}} + assert self.driver.usgscsm_distortion_model == {'radial': {'coefficients': [-0.0258246, -4.66139e-05, -0.000144651]}} + + def test_filter_number(self): + assert self.driver.filter_number == 1 + + def test_fikid(self): + assert self.driver.fikid == -85641 + + def test_odtk(self): + assert self.driver.odtk == [-0.0258246, -4.66139e-05, -0.000144651] -- GitLab