Commit 216269c4 authored by AustinSanders's avatar AustinSanders Committed by Kelvin Rodriguez
Browse files

Initial kaguya fixes + notebook (#205)

* Fixed kaguya driver and added example notebook

* updated documentation

* Swapped clock start/stop for corrected start/stop, fixed test failures.

* Swapped utc start/stop time for corrected utc start/stop time
parent 3e360d5f
Loading
Loading
Loading
Loading
+93 −17
Original line number Diff line number Diff line
import os
from glob import glob

import numpy as np

import pvl
import spiceypy as spice

from ale import config
from ale.base import Driver
from ale.base.data_naif import NaifSpice
@@ -13,7 +9,7 @@ from ale.base.label_pds3 import Pds3Label
from ale.base.type_sensor import LineScanner


class KaguyaTcPds3NaifSpiceDriver(LineScanner, Pds3Label, NaifSpice, Driver):
class KaguyaTcPds3NaifSpiceDriver(Pds3Label,NaifSpice, LineScanner, Driver):
    """
    Driver for a PDS3 Kaguya Terrain Camera (TC) images. Specifically level2b0 mono and stereo images.

@@ -40,11 +36,45 @@ class KaguyaTcPds3NaifSpiceDriver(LineScanner, Pds3Label, NaifSpice, Driver):
        metakernel_dir = config.kaguya
        mks = sorted(glob(os.path.join(metakernel_dir,'*.tm')))
        if not hasattr(self, '_metakernel'):
            self._metakernel = None
            for mk in mks:
                if str(self.start_time.year) in os.path.basename(mk):
                if str(self.utc_start_time.year) in os.path.basename(mk):
                    self._metakernel = mk
        return self._metakernel


    @property
    def utc_start_time(self):
        """
        Returns corrected utc start time.

        If no corrected form is found, defaults to the form specified in parent class.
        
        Returns
        -------
        : str
          Start time of the image in UTC YYYY-MM-DDThh:mm:ss[.fff]
        """
        return self.label.get('CORRECTED_START_TIME', super().utc_start_time)


    @property
    def utc_stop_time(self):
        """
        Returns corrected utc start time.

        If no corrected form is found, defaults to the form specified in parent class.

        Returns
        -------
        : str
          Stop time of the image in UTC YYYY-MM-DDThh:mm:ss[.fff]

        """

        return self.label.get('CORRECTED_STOP_TIME', super().utc_stop_time)


    @property
    def instrument_id(self):
        """
@@ -68,8 +98,37 @@ class KaguyaTcPds3NaifSpiceDriver(LineScanner, Pds3Label, NaifSpice, Driver):
        id = "LISM_{}_{}T{}".format(instrument, sd, swath)
        return id


    @property
    def _tc_id(self):
    def sensor_frame_id(self):
        """
        Returns the sensor frame id.  Depends on the instrument that was used to
        capture the image.

        Returns
        -------
        : int
          Sensor frame id
        """
        return spice.bods2c("LISM_{}_HEAD".format(super().instrument_id))


    @property
    def instrument_host_name(self):
        """
        Returns the name of the instrument host.  Kaguya/SELENE labels do not have an
        explicit instrument host name in the pvl, so we use the spacecraft name.

        Returns
        -------
        : str
          Spacecraft name as a proxy for instrument host name.
        """
        return self.label.get("SPACECRAFT_NAME", None)


    @property
    def ikid(self):
        """
        Returns ikid of LISM_TC1 or LISM_TC2, depending which camera was used
        for capturing the image.
@@ -89,7 +148,24 @@ class KaguyaTcPds3NaifSpiceDriver(LineScanner, Pds3Label, NaifSpice, Driver):
        return spice.bods2c("LISM_{}".format(super().instrument_id))

    @property
    def clock_stop_count(self):
    def spacecraft_name(self):
        """
        Returns "MISSION_NAME" as a proxy for spacecraft_name.

        No NAIF code exists for the spacecraft name 'SELENE-M.'  The NAIF code
        exists only for 'SELENE' or 'KAGUYA' -- 'SELENE' is captured as
        'MISSION_NAME'
        
        Returns
        -------
        : str
          mission name
        """
        return self.label.get('MISSION_NAME')


    @property
    def spacecraft_clock_stop_count(self):
        """
        The original SC_CLOCK_STOP_COUNT key is often incorrect and cannot be trusted.
        Therefore we get this information from CORRECTED_SC_CLOCK_STOP_COUNT
@@ -112,10 +188,10 @@ class KaguyaTcPds3NaifSpiceDriver(LineScanner, Pds3Label, NaifSpice, Driver):
        : float
          ephemeris stop time of the image
        """
        return spice.sct2e(self.spacecraft_id, super().ephemeris_stop_time)
        return spice.sct2e(self.spacecraft_id, self.spacecraft_clock_stop_count)

    @property
    def clock_start_count(self):
    def spacecraft_clock_start_count(self):
        """
        The original SC_CLOCK_START_COUNT key is often incorrect and cannot be trusted.
        Therefore we get this information from CORRECTED_SC_CLOCK_START_COUNT
@@ -138,7 +214,7 @@ class KaguyaTcPds3NaifSpiceDriver(LineScanner, Pds3Label, NaifSpice, Driver):
        : float
          ephemeris start time of the image
        """
        return spice.sct2e(self.spacecraft_id, super().ephemeris_start_time)
        return spice.sct2e(self.spacecraft_id, self.spacecraft_clock_start_count)

    @property
    def detector_center_line(self):
@@ -162,7 +238,7 @@ class KaguyaTcPds3NaifSpiceDriver(LineScanner, Pds3Label, NaifSpice, Driver):
          The detector sample of the principle point
        """
        # Pixels are 0 based, not one based, so subtract 1
        return spice.gdpool('INS{}_CENTER'.format(self._tc_id), 0, 2)[0]-1
        return spice.gdpool('INS{}_CENTER'.format(self.ikid), 0, 2)[0]-1

    @property
    def _sensor_orientation(self):
@@ -229,7 +305,7 @@ class KaguyaTcPds3NaifSpiceDriver(LineScanner, Pds3Label, NaifSpice, Driver):
        : list
          focal plane to detector samples
        """
        pixel_size = spice.gdpool('INS{}_PIXEL_SIZE'.format(self._tc_id), 0, 1)[0]
        pixel_size = spice.gdpool('INS{}_PIXEL_SIZE'.format(self.ikid), 0, 1)[0]
        return [0, 0, -1/pixel_size]


@@ -245,7 +321,7 @@ class KaguyaTcPds3NaifSpiceDriver(LineScanner, Pds3Label, NaifSpice, Driver):
        : list
          focal plane to detector lines
        """
        pixel_size = spice.gdpool('INS{}_PIXEL_SIZE'.format(self._tc_id), 0, 1)[0]
        pixel_size = spice.gdpool('INS{}_PIXEL_SIZE'.format(self.ikid), 0, 1)[0]
        return [0, -1/pixel_size, 0]


@@ -261,7 +337,7 @@ class KaguyaTcPds3NaifSpiceDriver(LineScanner, Pds3Label, NaifSpice, Driver):
        : list
          Optical distortion x coefficients
        """
        return spice.gdpool('INS{}_DISTORTION_COEF_X'.format(self._tc_id),0, 4).tolist()
        return spice.gdpool('INS{}_DISTORTION_COEF_X'.format(self.ikid),0, 4).tolist()


    @property
@@ -276,7 +352,7 @@ class KaguyaTcPds3NaifSpiceDriver(LineScanner, Pds3Label, NaifSpice, Driver):
        : list
          Optical distortion y coefficients
        """
        return spice.gdpool('INS{}_DISTORTION_COEF_Y'.format(self._tc_id), 0, 4).tolist()
        return spice.gdpool('INS{}_DISTORTION_COEF_Y'.format(self.ikid), 0, 4).tolist()

    @property
    def line_exposure_duration(self):
@@ -314,7 +390,7 @@ class KaguyaTcPds3NaifSpiceDriver(LineScanner, Pds3Label, NaifSpice, Driver):
        : float
          Camera focal length
        """
        return float(spice.gdpool('INS{}_FOCAL_LENGTH'.format(self._tc_id), 0, 1)[0])
        return float(spice.gdpool('INS{}_FOCAL_LENGTH'.format(self.ikid), 0, 1)[0])

    @property
    def usgscsm_distortion_model(self):
+99 −0

File added.

Preview size limit exceeded, changes collapsed.

+12 −12
Original line number Diff line number Diff line
@@ -32,27 +32,27 @@ def test_instrument_id(driver):
        assert driver.instrument_id == 'LISM_123_WTN'

@patch('ale.base.label_pds3.Pds3Label.instrument_id', 123)
def test_tc_id(driver):
    assert driver._tc_id == -12345
def test_ikid(driver):
    assert driver.ikid == -12345

def test_clock_stop_count(driver):
def test_spacecraft_clock_stop_count(driver):
    with patch.dict(driver.label, {'CORRECTED_SC_CLOCK_STOP_COUNT':
        pvl._collections.Units(value=105, units='<sec>')}) as f:
        assert driver.clock_stop_count == 105
        assert driver.spacecraft_clock_stop_count == 105

def test_clock_start_count(driver):
def test_spacecraft_clock_start_count(driver):
    with patch.dict(driver.label, {'CORRECTED_SC_CLOCK_START_COUNT':
        pvl._collections.Units(value=501, units='<sec>')}) as f:
        assert driver.clock_start_count == 501
        assert driver.spacecraft_clock_start_count == 501

@patch('ale.base.data_naif.NaifSpice.ephemeris_stop_time', 800)
@patch('ale.base.data_naif.NaifSpice.spacecraft_id', 123)
def test_ephemeris_stop_time(driver):
    with patch.dict(driver.label, {'CORRECTED_SC_CLOCK_STOP_COUNT':
                                   pvl._collections.Units(value=501, units='<sec>')}) as f:
        assert driver.ephemeris_stop_time == 0.1

@patch('ale.base.data_naif.NaifSpice.ephemeris_start_time', 800)
@patch('ale.base.data_naif.NaifSpice.spacecraft_id', 123)
def test_ephemeris_start_time(driver):
    with patch.dict(driver.label, {'CORRECTED_SC_CLOCK_START_COUNT':
                                   pvl._collections.Units(value=501, units='<sec>')}) as f:
        assert driver.ephemeris_start_time == 0.1

def test_detector_center_line(driver):