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