From 6d3f2b73de44ccde31d931dd67b5cd72f37586e6 Mon Sep 17 00:00:00 2001 From: Jesse Mapel <jmapel@usgs.gov> Date: Tue, 17 Sep 2019 12:34:57 -0700 Subject: [PATCH] Added ISIS special NAIF keywords (#271) * Added ISIS special NAIF keywords * Better variable name * Moved obs and targ to temp vars * Added proper observer and target swapping code * Updated from upstream --- ale/base/data_naif.py | 80 ++++++++++++++++++++++------ tests/pytests/test_data_naif.py | 32 +++++++++++ tests/pytests/test_kaguya_drivers.py | 12 ++--- tests/pytests/test_mdis_drivers.py | 4 +- 4 files changed, 104 insertions(+), 24 deletions(-) diff --git a/ale/base/data_naif.py b/ale/base/data_naif.py index 422c324..b6a025c 100644 --- a/ale/base/data_naif.py +++ b/ale/base/data_naif.py @@ -46,11 +46,12 @@ class NaifSpice(): def light_time_correction(self): """ Returns the type of light time correciton and abberation correction to - use in NAIF calls. + use in NAIF calls. Expects ikid to be defined. This must be the integer + Naif id code of the instrument. - This defaults to light time correction and abberation correction (LT+S), - concrete drivers should override this if they need to either not use - light time correction or use a different type of light time correction. + This searches for the value of the NAIF keyword INS<ikid>_LIGHTTIME_CORRECTION. + If the keyword is not defined, then this defaults to light time + correction and abberation correction (LT+S). Returns ------- @@ -59,7 +60,10 @@ class NaifSpice(): See https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/C/req/abcorr.html for the different options available. """ - return 'LT+S' + try: + return spice.gcpool('INS{}_LIGHTTIME_CORRECTION'.format(self.ikid), 0, 1)[0] + except: + return 'LT+S' @property def odtx(self): @@ -318,21 +322,32 @@ class NaifSpice(): pos = [] vel = [] + target = self.target_name + observer = self.spacecraft_name + # Check for ISIS flag to fix target and observer swapping + if self.swap_observer_target: + target = self.spacecraft_name + observer = self.target_name + for time in ephem: # spkezr returns a vector from the observer's location to the aberration-corrected # location of the target. For more information, see: # https://naif.jpl.nasa.gov/pub/naif/toolkit_docs/FORTRAN/spicelib/spkezr.html - state, _ = spice.spkezr(self.target_name, - time, - self.reference_frame, - self.light_time_correction, - self.spacecraft_name,) - pos.append(state[:3]) - vel.append(state[3:]) - # By default, spice works in km, and the vector returned by spkezr points the opposite - # direction to what ALE needs, so it must be multiplied by (-1) - self._position = np.asarray([p * -1000 for p in pos]) - self._velocity = np.asarray([v * -1000 for v in vel]) + state, _ = spice.spkezr(target, + time, + self.reference_frame, + self.light_time_correction, + observer) + if self.swap_observer_target: + pos.append(state[:3]) + vel.append(state[3:]) + else: + pos.append(-state[:3]) + vel.append(-state[3:]) + + # By default, SPICE works in km, so convert to m + self._position = [p * 1000 for p in pos] + self._velocity = [v * 1000 for v in vel] return self._position, self._velocity, self.ephemeris_time @property @@ -427,6 +442,39 @@ class NaifSpice(): return float(spice.gdpool('INS{}_BORESIGHT_LINE'.format(self.ikid), 0, 1)[0]) + @property + def swap_observer_target(self): + """ + Returns if the observer and target should be swapped when determining the + sensor state relative to the target. This is defined by a keyword in + ISIS IAKs. If the keyword is not defined in any loaded kernels then False + is returned. + + Expects ikid to be defined. This should be an integer containing the + Naif Id code of the instrument. + """ + try: + swap = spice.gcpool('INS{}_SWAP_OBSERVER_TARGET'.format(self.ikid), 0, 1)[0] + return swap.upper() == "TRUE" + except: + return False + + @property + def correct_lt_to_surface(self): + """ + Returns if light time correction should be made to the surface instead of + to the center of the body. This is defined by a keyword in ISIS IAKs. + If the keyword is not defined in any loaded kernels then False is returned. + + Expects ikid to be defined. This should be an integer containing the + Naif Id code of the instrument. + """ + try: + surface_correct = spice.gcpool('INS{}_LT_SURFACE_CORRECT'.format(self.ikid), 0, 1)[0] + return surface_correct.upper() == "TRUE" + except: + return False + @property def isis_naif_keywords(self): """ diff --git a/tests/pytests/test_data_naif.py b/tests/pytests/test_data_naif.py index 457472b..5bf7fab 100644 --- a/tests/pytests/test_data_naif.py +++ b/tests/pytests/test_data_naif.py @@ -83,6 +83,15 @@ class test_data_naif(unittest.TestCase): def test_detector_center_line(self): assert self.driver.detector_center_line == 0.430442527 + def test_swap_observer_target(self): + assert not self.driver.swap_observer_target + + def test_light_time_correction(self): + assert self.driver.light_time_correction == "LT+S" + + def test_correct_lt_to_surface(self): + assert not self.driver.correct_lt_to_surface + def test_sun_position(self): sun_positions, sun_velocities, times = self.driver.sun_position assert len(sun_positions) == 1 @@ -91,3 +100,26 @@ class test_data_naif(unittest.TestCase): np.testing.assert_allclose(sun_velocities[0], [9883868.06162645, 8989183.29614645, 881.9339912834714]) assert len(times) == 1 np.testing.assert_allclose(times[0], 297088762.61698407) + +def test_light_time_correction_keyword(): + with patch('ale.base.data_naif.spice.gcpool', return_value=['NONE']) as gcpool, \ + patch('ale.base.data_naif.NaifSpice.ikid', new_callable=PropertyMock) as ikid: + ikid.return_value = -12345 + assert NaifSpice().light_time_correction == 'NONE' + gcpool.assert_called_with('INS-12345_LIGHTTIME_CORRECTION', 0, 1) + +@pytest.mark.parametrize(("key_val, return_val"), [(['TRUE'], True), (['FALSE'], False)]) +def test_swap_observer_target_keyword(key_val, return_val): + with patch('ale.base.data_naif.spice.gcpool', return_value=key_val) as gcpool, \ + patch('ale.base.data_naif.NaifSpice.ikid', new_callable=PropertyMock) as ikid: + ikid.return_value = -12345 + assert NaifSpice().swap_observer_target == return_val + gcpool.assert_called_with('INS-12345_SWAP_OBSERVER_TARGET', 0, 1) + +@pytest.mark.parametrize(("key_val, return_val"), [(['TRUE'], True), (['FALSE'], False)]) +def test_correct_lt_to_surface_keyword(key_val, return_val): + with patch('ale.base.data_naif.spice.gcpool', return_value=key_val) as gcpool, \ + patch('ale.base.data_naif.NaifSpice.ikid', new_callable=PropertyMock) as ikid: + ikid.return_value = -12345 + assert NaifSpice().correct_lt_to_surface == return_val + gcpool.assert_called_with('INS-12345_LT_SURFACE_CORRECT', 0, 1) diff --git a/tests/pytests/test_kaguya_drivers.py b/tests/pytests/test_kaguya_drivers.py index 7853a0d..d8d85cc 100644 --- a/tests/pytests/test_kaguya_drivers.py +++ b/tests/pytests/test_kaguya_drivers.py @@ -108,12 +108,12 @@ def test_load(test_kernels): [ 193824.32093681, 210061.95495794, -1767367.56104995], [ 193267.90484827, 209421.16305764, -1767500.49120063], [ 192711.44397634, 208780.32318594, -1767633.01318365]]), - 'velocities': np.array([[-1069.71225785, -1231.97438089, -258.37880523], - [-1069.80205939, -1232.06556741, -257.5939851 ], - [-1069.89161639, -1232.15647191, -256.80910187], - [-1069.98092881, -1232.24709434, -256.02415587], - [-1070.06999666, -1232.33743471, -255.2391471 ], - [-1070.15881991, -1232.42749298, -254.4540759 ]]), + 'velocities': np.array([[-1069.70946002, -1231.97108635, -258.37361381], + [-1069.79925825, -1232.06226912, -257.58879787], + [-1069.88881194, -1232.15316986, -256.80391882], + [-1069.97812106, -1232.24378854, -256.01897702], + [-1070.06718562, -1232.33412517, -255.23397236], + [-1070.15600557, -1232.42417969, -254.44890536]]), 'unit': 'm'}, 'sun_position': { 'positions': np.array([[9.50465237e+10, 1.15903815e+11, 3.78729685e+09]]), diff --git a/tests/pytests/test_mdis_drivers.py b/tests/pytests/test_mdis_drivers.py index c322893..ddb0099 100644 --- a/tests/pytests/test_mdis_drivers.py +++ b/tests/pytests/test_mdis_drivers.py @@ -28,8 +28,8 @@ def usgscsm_compare_dict(): 'semiminor': 2439.4, 'unit': 'km'}, 'sensor_position': { - 'positions': np.array([[ -629496.48621395, -1582946.00913133, 1783952.64031042]]), - 'velocities': np.array([[1732.18666313, 2502.76825324, 2412.65232223]]), + 'positions': np.array([[-629657.4559945846, -1583350.7374122413, 1784408.773440049]]), + 'velocities': np.array([[1732.8734290653545, 2504.0213215928925, 2412.578186708735]]), 'unit': 'm'}, 'sun_position': { 'positions': np.array([[-4.68946673e+10, -5.36158427e+08, 2.71167863e+07]]), -- GitLab