From 3a1b59e18f8e02938b6d99beb791ccd732640b9c Mon Sep 17 00:00:00 2001
From: acpaquette <acpaquette@usgs.gov>
Date: Mon, 5 Jun 2023 11:58:02 -0700
Subject: [PATCH] Mgs time bias (#538)

* Updated disclaimer for release

* Handle MGS time bias for NAC and WAC

* Re-enabled MGS drivers

* Updated changelog
---
 CHANGELOG.md                              |   1 +
 DISCLAIMER.md                             |  24 ++-
 ale/base/base.py                          |   1 -
 ale/base/data_naif.py                     |  17 ++-
 ale/drivers/__init__.py                   |   3 +-
 ale/drivers/mgs_drivers.py                |  28 ++++
 ale/transformation.py                     |  13 +-
 tests/pytests/data/isds/mgsmocwa_isd.json | 176 ++++++++++++----------
 tests/pytests/test_cassini_drivers.py     |   4 +-
 tests/pytests/test_mgs_drivers.py         |   3 -
 10 files changed, 158 insertions(+), 112 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3f4566f..29acad1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -37,6 +37,7 @@ release.
 
 ### Fixed
 - MexHrscIsisLabelNaifSpice and MexHrscPds3NaifSpice have had there ephemeris times changed and sampling factor updated. MexHrscIsisLabelNaifSpice has also had it's focal length, and focal plane translation updated to reflect those found in the MexHrscPds3NaifSpice driver [#541](https://github.com/DOI-USGS/ale/pull/541)
+- MGS drivers now account for a time bias in the ephemeris data [#538](https://github.com/DOI-USGS/ale/pull/538)
 
 ## [0.9.0] - 2023-04-19
 
diff --git a/DISCLAIMER.md b/DISCLAIMER.md
index 2b91b39..8515c12 100644
--- a/DISCLAIMER.md
+++ b/DISCLAIMER.md
@@ -1,15 +1,9 @@
-This software is preliminary or provisional and is subject to revision. It is
-being provided to meet the need for timely best science. The software has not
-received final approval by the U.S. Geological Survey (USGS). No warranty,
-expressed or implied, is made by the USGS or the U.S. Government as to the
-functionality of the software and related material nor shall the fact of release
-constitute any such warranty. The software is provided on the condition that
-neither the USGS nor the U.S. Government shall be held liable for any damages
-resulting from the authorized or unauthorized use of the software.This software is preliminary or provisional and is subject to revision. It is
-being provided to meet the need for timely best science. The software has not
-received final approval by the U.S. Geological Survey (USGS). No warranty,
-expressed or implied, is made by the USGS or the U.S. Government as to the
-functionality of the software and related material nor shall the fact of release
-constitute any such warranty. The software is provided on the condition that
-neither the USGS nor the U.S. Government shall be held liable for any damages
-resulting from the authorized or unauthorized use of the software.
\ No newline at end of file
+This software has been approved for release by the U.S. Geological Survey
+(USGS). Although the software has been subjected to rigorous review, the USGS
+reserves the right to update the software as needed pursuant to further analysis
+and review. No warranty, expressed or implied, is made by the USGS or the U.S.
+Government as to the functionality of the software and related material nor
+shall the fact of release constitute any such warranty. Furthermore, the
+software is released on condition that neither the USGS nor the U.S. Government
+shall be held liable for any damages resulting from its authorized or
+unauthorized use.
\ No newline at end of file
diff --git a/ale/base/base.py b/ale/base/base.py
index 6fba266..7bddcea 100644
--- a/ale/base/base.py
+++ b/ale/base/base.py
@@ -359,7 +359,6 @@ class Driver():
               self._projection = "" 
         return self._projection
     
-
     @property 
     def geotransform(self):
         if not hasattr(self, "_geotransform"): 
diff --git a/ale/base/data_naif.py b/ale/base/data_naif.py
index dad57ad..985abae 100644
--- a/ale/base/data_naif.py
+++ b/ale/base/data_naif.py
@@ -428,7 +428,8 @@ class NaifSpice():
                                                       target_frame=self.target_frame_id,
                                                       center_ephemeris_time=self.center_ephemeris_time,
                                                       ephemeris_times=self.ephemeris_time,
-                                                      nadir=nadir, exact_ck_times=exact_ck_times)
+                                                      nadir=nadir, exact_ck_times=exact_ck_times,
+                                                      inst_time_bias=self.instrument_time_bias)
 
             if nadir:
                 # Logic for nadir calculation was taken from ISIS3
@@ -589,3 +590,17 @@ class NaifSpice():
                 pass
 
         return self._naif_keywords
+
+    @property
+    def instrument_time_bias(self):
+        """
+        Time bias used for generating sensor orientations
+
+        The default is 0 for not time bias
+
+        Returns
+        -------
+        : int
+          Time bias in ephemeris time
+        """
+        return 0
\ No newline at end of file
diff --git a/ale/drivers/__init__.py b/ale/drivers/__init__.py
index 06343e1..dbb2609 100644
--- a/ale/drivers/__init__.py
+++ b/ale/drivers/__init__.py
@@ -27,8 +27,7 @@ __disabled_drivers__ = ["ody_drivers",
                         "hayabusa2_drivers",
                         "juno_drivers",
                         "tgo_drivers",
-                        "msi_drivers",
-                        "mgs_drivers"]
+                        "msi_drivers"]
 
 # dynamically load drivers
 __all__ = [os.path.splitext(os.path.basename(d))[0] for d in glob(os.path.join(os.path.dirname(__file__), '*_drivers.py'))]
diff --git a/ale/drivers/mgs_drivers.py b/ale/drivers/mgs_drivers.py
index 8469bf9..681acd0 100644
--- a/ale/drivers/mgs_drivers.py
+++ b/ale/drivers/mgs_drivers.py
@@ -130,6 +130,20 @@ class MgsMocNarrowAngleCameraIsisLabelNaifSpiceDriver(LineScanner, IsisLabel, Na
         """
         return [0, 0.0131240578522949, 0.0131240578522949]
 
+    @property
+    def instrument_time_bias(self):
+      """
+      Defines the time bias for Mars Global Survayor instrument rotation information.
+
+      This shifts the sensor orientation window back by 1.15 seconds in ephemeris time.
+
+      Returns
+      -------
+      : int
+        Time bias adjustment
+      """
+      return -1.15
+
 class MgsMocWideAngleCameraIsisLabelNaifSpiceDriver(LineScanner, IsisLabel, NaifSpice, RadialDistortion, Driver):
     """
     Driver for reading MGS MOC WA ISIS labels.
@@ -268,3 +282,17 @@ class MgsMocWideAngleCameraIsisLabelNaifSpiceDriver(LineScanner, IsisLabel, Naif
             return [0, -.007, .007]
         else:
             return [0, .007, .007]
+
+    @property
+    def instrument_time_bias(self):
+      """
+      Defines the time bias for Mars Global Survayor instrument rotation information.
+
+      This shifts the sensor orientation window back by 1.15 seconds in ephemeris time.
+
+      Returns
+      -------
+      : int
+        Time bias adjustment
+      """
+      return -1.15
diff --git a/ale/transformation.py b/ale/transformation.py
index 84ef0c4..ab3ff5e 100644
--- a/ale/transformation.py
+++ b/ale/transformation.py
@@ -96,7 +96,7 @@ class FrameChain(nx.DiGraph):
                      of frame rotations in the frame chain
     """
     @classmethod
-    def from_spice(cls, sensor_frame, target_frame, center_ephemeris_time, ephemeris_times=[], nadir=False, exact_ck_times=False):
+    def from_spice(cls, sensor_frame, target_frame, center_ephemeris_time, ephemeris_times=[], nadir=False, exact_ck_times=False, inst_time_bias=0):
         frame_chain = cls()
         sensor_times = []
         # Default assume one time
@@ -106,7 +106,7 @@ class FrameChain(nx.DiGraph):
 
         if exact_ck_times and len(ephemeris_times) > 1 and not nadir:
             try:
-                sensor_times = cls.extract_exact_ck_times(ephemeris_times[0], ephemeris_times[-1], sensor_frame)
+                sensor_times = cls.extract_exact_ck_times(ephemeris_times[0] + inst_time_bias, ephemeris_times[-1] + inst_time_bias, sensor_frame)
             except Exception as e:
                 pass
 
@@ -123,8 +123,8 @@ class FrameChain(nx.DiGraph):
 
         constant_frames.extend(target_constant_frames)
 
-        frame_chain.compute_time_dependent_rotiations(sensor_time_dependent_frames, sensor_times)
-        frame_chain.compute_time_dependent_rotiations(target_time_dependent_frames, target_times)
+        frame_chain.compute_time_dependent_rotiations(sensor_time_dependent_frames, sensor_times, inst_time_bias)
+        frame_chain.compute_time_dependent_rotiations(target_time_dependent_frames, target_times, 0)
 
         for s, d in constant_frames:
             quats = np.zeros(4)
@@ -380,7 +380,7 @@ class FrameChain(nx.DiGraph):
 
         return times
 
-    def compute_time_dependent_rotiations(self, frames, times):
+    def compute_time_dependent_rotiations(self, frames, times, time_bias):
         """
         Computes the time dependent rotations based on a list of tuples that define the
         relationships between frames as (source, destination) and a list of times to
@@ -409,5 +409,6 @@ class FrameChain(nx.DiGraph):
 
             if not avs:
                 avs = None
-            rotation = TimeDependentRotation(quats, times, s, d, av=avs)
+            biased_times = [time - time_bias for time in times]
+            rotation = TimeDependentRotation(quats, biased_times, s, d, av=avs)
             self.add_edge(rotation=rotation)
\ No newline at end of file
diff --git a/tests/pytests/data/isds/mgsmocwa_isd.json b/tests/pytests/data/isds/mgsmocwa_isd.json
index 864eb6d..f2f07e2 100644
--- a/tests/pytests/data/isds/mgsmocwa_isd.json
+++ b/tests/pytests/data/isds/mgsmocwa_isd.json
@@ -1,4 +1,4 @@
- {
+{
   "isis_camera_version": 1,
   "image_lines": 768,
   "image_samples": 640,
@@ -70,90 +70,97 @@
       -94000,
       1
     ],
-    "ck_table_start_time": -69382819.71598774,
-    "ck_table_end_time": -69382511.7160111,
-    "ck_table_original_size": 78,
+    "ck_table_start_time": -69382822.56598744,
+    "ck_table_end_time": -69382510.5660111,
+    "ck_table_original_size": 79,
     "ephemeris_times": [
-      -69382819.71598774,
-      -69382815.71598805,
-      -69382811.71598835,
-      -69382807.71598867,
-      -69382803.71598896,
-      -69382799.71598926,
-      -69382795.71598957,
-      -69382791.71598987,
-      -69382787.71599017,
-      -69382783.71599048,
-      -69382779.71599078,
-      -69382775.7159911,
-      -69382771.7159914,
-      -69382767.71599169,
-      -69382763.715992,
-      -69382759.7159923,
-      -69382755.7159926,
-      -69382751.71599291,
-      -69382747.71599321,
-      -69382743.71599352,
-      -69382739.71599382,
-      -69382735.71599412,
-      -69382731.71599443,
-      -69382727.71599473,
-      -69382723.71599503,
-      -69382719.71599534,
-      -69382715.71599564,
-      -69382711.71599595,
-      -69382707.71599625,
-      -69382703.71599655,
-      -69382699.71599686,
-      -69382695.71599716,
-      -69382691.71599746,
-      -69382687.71599777,
-      -69382683.71599805,
-      -69382679.71599837,
-      -69382675.71599866,
-      -69382671.71599896,
-      -69382667.71599928,
-      -69382663.71599957,
-      -69382659.71599987,
-      -69382655.71600018,
-      -69382651.71600048,
-      -69382647.71600078,
-      -69382643.7160011,
-      -69382639.71600139,
-      -69382635.7160017,
-      -69382631.716002,
-      -69382627.7160023,
-      -69382623.71600261,
-      -69382619.71600291,
-      -69382615.71600321,
-      -69382611.71600352,
-      -69382607.71600382,
-      -69382603.71600413,
-      -69382599.71600443,
-      -69382595.71600473,
-      -69382591.71600504,
-      -69382587.71600534,
-      -69382583.71600564,
-      -69382579.71600595,
-      -69382575.71600625,
-      -69382571.71600656,
-      -69382567.71600686,
-      -69382563.71600716,
-      -69382559.71600747,
-      -69382555.71600777,
-      -69382551.71600807,
-      -69382547.71600838,
-      -69382543.71600868,
-      -69382539.71600899,
-      -69382535.71600929,
-      -69382531.71600959,
-      -69382527.7160099,
-      -69382523.7160102,
-      -69382519.7160105,
-      -69382515.71601081,
-      -69382511.7160111
+      -69382822.56598744,
+      -69382818.56598774,
+      -69382814.56598805,
+      -69382810.56598835,
+      -69382806.56598866,
+      -69382802.56598896,
+      -69382798.56598926,
+      -69382794.56598957,
+      -69382790.56598987,
+      -69382786.56599016,
+      -69382782.56599048,
+      -69382778.56599078,
+      -69382774.56599109,
+      -69382770.56599139,
+      -69382766.56599168,
+      -69382762.565992,
+      -69382758.5659923,
+      -69382754.5659926,
+      -69382750.5659929,
+      -69382746.5659932,
+      -69382742.56599352,
+      -69382738.56599382,
+      -69382734.56599411,
+      -69382730.56599443,
+      -69382726.56599472,
+      -69382722.56599502,
+      -69382718.56599534,
+      -69382714.56599563,
+      -69382710.56599595,
+      -69382706.56599624,
+      -69382702.56599654,
+      -69382698.56599686,
+      -69382694.56599715,
+      -69382690.56599745,
+      -69382686.56599776,
+      -69382682.56599805,
+      -69382678.56599836,
+      -69382674.56599866,
+      -69382670.56599896,
+      -69382666.56599927,
+      -69382662.56599957,
+      -69382658.56599987,
+      -69382654.56600018,
+      -69382650.56600048,
+      -69382646.56600077,
+      -69382642.56600109,
+      -69382638.56600139,
+      -69382634.5660017,
+      -69382630.566002,
+      -69382626.5660023,
+      -69382622.5660026,
+      -69382618.5660029,
+      -69382614.5660032,
+      -69382610.56600352,
+      -69382606.56600381,
+      -69382602.56600413,
+      -69382598.56600443,
+      -69382594.56600472,
+      -69382590.56600504,
+      -69382586.56600533,
+      -69382582.56600563,
+      -69382578.56600595,
+      -69382574.56600624,
+      -69382570.56600656,
+      -69382566.56600685,
+      -69382562.56600715,
+      -69382558.56600747,
+      -69382554.56600776,
+      -69382550.56600806,
+      -69382546.56600837,
+      -69382542.56600867,
+      -69382538.56600899,
+      -69382534.56600928,
+      -69382530.56600958,
+      -69382526.5660099,
+      -69382522.56601019,
+      -69382518.56601049,
+      -69382514.5660108,
+      -69382510.5660111
     ],
     "quaternions": [
+      [
+        -0.23708037152577427,
+        0.06913949189951372,
+        0.9353535261221577,
+        0.2532319278209684
+      ],
       [
         -0.23957495682735272,
         0.06980832265124022,
@@ -624,6 +631,11 @@
       ]
     ],
     "angular_velocities": [
+      [
+        -1.3243650720946794e-05,
+        -0.001147942524828248,
+        -0.0006765182794334033
+      ],
       [
         -1.4192050960089805e-05,
         -0.001149540028905371,
diff --git a/tests/pytests/test_cassini_drivers.py b/tests/pytests/test_cassini_drivers.py
index c2627cc..79041a7 100644
--- a/tests/pytests/test_cassini_drivers.py
+++ b/tests/pytests/test_cassini_drivers.py
@@ -140,7 +140,7 @@ class test_cassini_pds3_naif(unittest.TestCase):
             target_frame_id.return_value = -800
             frame_chain = self.driver.frame_chain
             assert len(frame_chain.nodes()) == 0
-            from_spice.assert_called_with(center_ephemeris_time=2.4, ephemeris_times=[2.4], nadir=False, sensor_frame=14082360, target_frame=-800, exact_ck_times=True)
+            from_spice.assert_called_with(center_ephemeris_time=2.4, ephemeris_times=[2.4], nadir=False, sensor_frame=14082360, target_frame=-800, exact_ck_times=True,  inst_time_bias=0)
 
 # ========= Test cassini isislabel and naifspice driver =========
 class test_cassini_isis_naif(unittest.TestCase):
@@ -212,4 +212,4 @@ class test_cassini_isis_naif(unittest.TestCase):
             target_frame_id.return_value = -800
             frame_chain = self.driver.frame_chain
             assert len(frame_chain.nodes()) == 0
-            from_spice.assert_called_with(center_ephemeris_time=2.4000000000000004, ephemeris_times=[2.4000000000000004], nadir=False, sensor_frame=14082360, target_frame=-800, exact_ck_times=True)
+            from_spice.assert_called_with(center_ephemeris_time=2.4000000000000004, ephemeris_times=[2.4000000000000004], nadir=False, sensor_frame=14082360, target_frame=-800, exact_ck_times=True, inst_time_bias=0)
diff --git a/tests/pytests/test_mgs_drivers.py b/tests/pytests/test_mgs_drivers.py
index 0c5d184..5dbe682 100644
--- a/tests/pytests/test_mgs_drivers.py
+++ b/tests/pytests/test_mgs_drivers.py
@@ -20,7 +20,6 @@ def test_nac_kernels():
     for kern in binary_kernels:
         os.remove(kern)
 
-@pytest.mark.xfail
 def test_nac_load(test_nac_kernels):
     label_file = get_image_label('m0402852', 'isis')
     compare_dict = get_isd("mgsmocna")
@@ -39,14 +38,12 @@ def test_wac_kernels():
     for kern in binary_kernels:
         os.remove(kern)
 
-@pytest.mark.xfail
 def test_wac_load(test_wac_kernels):
     label_file = get_image_label('ab102401', 'isis3')
     compare_dict = get_isd("mgsmocwa")
 
     isd_str = ale.loads(label_file, props={'kernels': test_wac_kernels})
     isd_obj = json.loads(isd_str)
-    print(json.dumps(isd_obj, indent=2))
     assert compare_dicts(isd_obj, compare_dict) == []
 
 
-- 
GitLab