Commit 214fd889 authored by paarongiroux's avatar paarongiroux Committed by Summer Stapleton
Browse files

Changed Kaguya Drivers (#175)

* updated kaguya_drivers and added tests

* minor changes for consistency
parent 17865937
Loading
Loading
Loading
Loading
+28 −13
Original line number Diff line number Diff line
@@ -13,7 +13,7 @@ from ale.base.label_pds3 import Pds3Label
from ale.base.type_sensor import LineScanner


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

@@ -48,7 +48,7 @@ class KaguyaTcPds3NaifSpiceDriver(Driver, LineScanner, Pds3Label, NaifSpice):
        COMPRESS = D/T short for DCT or through, we assume image has been decompressed already
        SWATCH = swatch mode, different swatch modes have different FOVs
        """
        instrument = self.label.get("INSTRUMENT_ID")
        instrument = super().instrument_id
        swath = self.label.get("SWATH_MODE_ID")[0]
        sd = self.label.get("PRODUCT_SET_ID").split("_")[1].upper()

@@ -65,23 +65,23 @@ class KaguyaTcPds3NaifSpiceDriver(Driver, LineScanner, Pds3Label, NaifSpice):
        presumably because they are not affected by the addtional parameters encoded in
        the ikid returned by self.ikid. This method exists for those gdpool calls.
        """
        return spice.bods2c("LISM_{}".format(self.label.get("INSTRUMENT_ID")))
        return spice.bods2c("LISM_{}".format(super().instrument_id))

    @property
    def clock_stop_count(self):
        return self.label.get('CORRECTED_SC_CLOCK_STOP_COUNT').value

    @property
    def ephemermis_stop_time(self):
        return spice.sct2e(self.spacecraft_id, self.ephemeris_stop_time)
    def ephemeris_stop_time(self):
        return spice.sct2e(self.spacecraft_id, super().ephemeris_stop_time)

    @property
    def clock_start_count(self):
        return self.label.get('CORRECTED_SC_CLOCK_START_COUNT').value

    @property
    def ephemermis_start_time(self):
        return spice.sct2e(self.spacecraft_id, self.ephemeris_start_time)
    def ephemeris_start_time(self):
        return spice.sct2e(self.spacecraft_id, super().ephemeris_start_time)

    @property
    def detector_center_line(self):
@@ -99,7 +99,7 @@ class KaguyaTcPds3NaifSpiceDriver(Driver, LineScanner, Pds3Label, NaifSpice):

            qua = np.empty((len(ephem), 4))
            for i, time in enumerate(ephem):
                instrument = self.label.get("INSTRUMENT_ID")
                instrument = super().instrument_id
                # Find the rotation matrix
                camera2bodyfixed = spice.pxform("LISM_{}_HEAD".format(instrument),
                                                self.reference_frame,
@@ -117,8 +117,8 @@ class KaguyaTcPds3NaifSpiceDriver(Driver, LineScanner, Pds3Label, NaifSpice):
        Kaguya uses a slightly more accurate "mean Earth" reference frame for
        moon obvervations. see https://darts.isas.jaxa.jp/pub/spice/SELENE/kernels/fk/moon_assoc_me.tf
        """
        if self.target_name.lower == "moon":
            "MOON_ME"
        if self.target_name.lower() == "moon":
            return "MOON_ME"
        else:
            # TODO: How do we handle no target?
            return "NO TARGET"
@@ -192,7 +192,7 @@ class KaguyaTcPds3NaifSpiceDriver(Driver, LineScanner, Pds3Label, NaifSpice):
        return float(spice.gdpool('INS{}_FOCAL_LENGTH'.format(self._tc_id), 0, 1)[0])

    @property
    def optical_distortion(self):
    def usgscsm_distortion_model(self):
        """
        Kaguya uses a unique radial distortion model so we need to overwrite the
        method packing the distortion model into the ISD.
@@ -222,7 +222,7 @@ class KaguyaTcPds3NaifSpiceDriver(Driver, LineScanner, Pds3Label, NaifSpice):
        }

    @property
    def starting_detector_sample(self):
    def detector_start_sample(self):
        """
        Returns starting detector sample

@@ -266,3 +266,18 @@ class KaguyaTcPds3NaifSpiceDriver(Driver, LineScanner, Pds3Label, NaifSpice):
        """

        return self.label["FIRST_PIXEL_NUMBER"]

    @property
    def detector_start_line(self):
        return 1


    @property
    def sensor_model_version(self):
        """
        Returns
        -------
        : int
          model version
        """
        return 1
+105 −153
Original line number Diff line number Diff line
import pytest
import numpy as np
import pvl

import ale
from ale.drivers import kaguya_drivers
from ale.drivers.kaguya_drivers import KaguyaTcPds3NaifSpiceDriver
from ale.base import data_naif
from ale.base import label_pds3

from unittest.mock import PropertyMock, patch

# 'Mock' the spice module where it is imported
from conftest import SimpleSpice, get_mockkernels

simplespice = SimpleSpice()

data_naif.spice = simplespice
kaguya_drivers.spice = simplespice
label_pds3.spice = simplespice

from ale.drivers.kaguya_drivers import KaguyaTcPds3NaifSpiceDriver

KaguyaTcPds3NaifSpiceDriver.metakernel = get_mockkernels

@pytest.fixture
def kaguya_tclabel():
    return  """
    PDS_VERSION_ID                       = "PDS3"
    /*** FILE FORMAT ***/
    RECORD_TYPE                          = "UNDEFINED"
    FILE_NAME                            = "TC1W2B0_01_00296N081E2387.img"
    PRODUCT_ID                           = "TC1W2B0_01_00296N081E2387"
    DATA_FORMAT                          = "PDS"

    /*** POINTERS TO START BYTE OFFSET OF OBJECTS IN FILE ***/
    ^IMAGE                               = 7609 <BYTES>

    /*** GENERAL DATA DESCRIPTION PARAMETERS ***/
    SOFTWARE_NAME                        = "RGC_TC_w_Level2B0 (based on RGC_TC_MI version 2.10.1)"
    SOFTWARE_VERSION                     = "1.0.0"
    PROCESS_VERSION_ID                   = "L2B"
    PRODUCT_CREATION_TIME                = 2013-07-16T20:12:53Z
    PROGRAM_START_TIME                   = 2013-07-16T20:08:01Z
    PRODUCER_ID                          = "LISM"
    PRODUCT_SET_ID                       = "TC_w_Level2B0"
    PRODUCT_VERSION_ID                   = "01"
    REGISTERED_PRODUCT                   = "Y"
    ILLUMINATION_CONDITION               = "MORNING"
    LEVEL2A_FILE_NAME                    = "TC1W2A0_02TSF00296_001_0022.img"
    SPICE_METAKERNEL_FILE_NAME           = "RGC_INF_TCv401IK_MIv200IK_SPv105IK_RISE100h_02_LongCK_D_V02_de421_110706.mk"

    /*** SCENE RELATED PARAMETERS ***/
    MISSION_NAME                         = "SELENE"
    SPACECRAFT_NAME                      = "SELENE-M"
    DATA_SET_ID                          = "TC1_Level2B"
    INSTRUMENT_NAME                      = "Terrain Camera 1"
    INSTRUMENT_ID                        = "TC1"
    MISSION_PHASE_NAME                   = "InitialCheckout"
    REVOLUTION_NUMBER                    = 296
    STRIP_SEQUENCE_NUMBER                = 1
    SCENE_SEQUENCE_NUMBER                = 22
    UPPER_LEFT_DAYTIME_FLAG              = "Day"
    UPPER_RIGHT_DAYTIME_FLAG             = "Day"
    LOWER_LEFT_DAYTIME_FLAG              = "Day"
    LOWER_RIGHT_DAYTIME_FLAG             = "Day"
    TARGET_NAME                          = "MOON"
    OBSERVATION_MODE_ID                  = "NORMAL"
    SENSOR_DESCRIPTION                   = "Imagery type:Pushbroom. ImageryMode:Mono,Stereo. ExposureTimeMode:Long,Middle,Short. CompressionMode:NonComp,DCT. Q-table:32 patterns. H-table:4 patterns. SwathMode:F(Full),N(Nominal),H(Half). First pixel number:1(F),297(N),1172(H)."
    SENSOR_DESCRIPTION2                  = "Pixel size:7x7[micron^2](TC1/TC2). Wavelength range:430-850[nm](TC1/TC2). A/D rate:10[bit](TC1/TC2). Slant angle:+/-15[degree] (from nadir to +x of S/C)(TC1/TC2). Focal length:72.45/72.63[mm](TC1/TC2). F number:3.97/3.98(TC1/TC2)."
    DETECTOR_STATUS                      = {"TC1:ON","TC2:ON","MV:OFF","MN:OFF","SP:ON"}
    EXPOSURE_MODE_ID                     = "SHORT"
    LINE_EXPOSURE_DURATION               =   1.625000 <msec>
    SPACECRAFT_CLOCK_START_COUNT         =  878074165.1875 <sec>
    SPACECRAFT_CLOCK_STOP_COUNT          =  878074195.4450 <sec>
    CORRECTED_SC_CLOCK_START_COUNT       =  878074165.186621 <sec>
    CORRECTED_SC_CLOCK_STOP_COUNT        =  878074195.443901 <sec>
    START_TIME                           = 2007-11-02T21:29:27.123714Z
    STOP_TIME                            = 2007-11-02T21:29:57.381214Z
    CORRECTED_START_TIME                 = 2007-11-02T21:29:27.122835Z
    CORRECTED_STOP_TIME                  = 2007-11-02T21:29:57.380115Z
    LINE_SAMPLING_INTERVAL               =   6.500000 <msec>
    CORRECTED_SAMPLING_INTERVAL          =   6.499953 <msec>
    UPPER_LEFT_LATITUDE                  =   7.290785 <deg>
    UPPER_LEFT_LONGITUDE                 = 238.410490 <deg>
    UPPER_RIGHT_LATITUDE                 =   7.288232 <deg>
    UPPER_RIGHT_LONGITUDE                = 238.991705 <deg>
    LOWER_LEFT_LATITUDE                  =   8.820028 <deg>
    LOWER_LEFT_LONGITUDE                 = 238.417311 <deg>
    LOWER_RIGHT_LATITUDE                 =   8.817605 <deg>
    LOWER_RIGHT_LONGITUDE                = 238.999370 <deg>
    LOCATION_FLAG                        = "A"
    ROLL_CANT                            = "NO"
    SCENE_CENTER_LATITUDE                =   8.053752 <deg>
    SCENE_CENTER_LONGITUDE               = 238.704621 <deg>
    INCIDENCE_ANGLE                      =  28.687 <deg>
    EMISSION_ANGLE                       =  17.950 <deg>
    PHASE_ANGLE                          =  31.600 <deg>
    SOLAR_AZIMUTH_ANGLE                  = 108.126 <deg>
    FOCAL_PLANE_TEMPERATURE              =  18.85 <degC>
    TELESCOPE_TEMPERATURE                =  18.59 <degC>
    SATELLITE_MOVING_DIRECTION           = "-1"
    FIRST_SAMPLED_LINE_POSITION          = "UPPERMOST"
    FIRST_DETECTOR_ELEMENT_POSITION      = "LEFT"
    A_AXIS_RADIUS                        = 1737.400 <km>
    B_AXIS_RADIUS                        = 1737.400 <km>
    C_AXIS_RADIUS                        = 1737.400 <km>
    DEFECT_PIXEL_POSITION                = "N/A"

    /*** CAMERA RELATED PARAMETERS ***/
    SWATH_MODE_ID                        = "FULL"
    FIRST_PIXEL_NUMBER                   = 1
    LAST_PIXEL_NUMBER                    = 1600
    SPACECRAFT_ALTITUDE                  =  108.719 <km>
    SPACECRAFT_GROUND_SPEED              =  1.530 <km/sec>
    TC1_TELESCOPE_TEMPERATURE            =  18.70 <degC>
    TC2_TELESCOPE_TEMPERATURE            =  18.70 <degC>
    DPU_TEMPERATURE                      =  14.60 <degC>
    TM_TEMPERATURE                       =  18.01 <degC>
    TM_RADIATOR_TEMPERATURE              =  17.67 <degC>
    Q_TABLE_ID                           = "N/A"
    HUFFMAN_TABLE_ID                     = "N/A"
    DATA_COMPRESSION_PERCENT_MEAN        = 100.0
    DATA_COMPRESSION_PERCENT_MAX         = 100.0
    DATA_COMPRESSION_PERCENT_MIN         = 100.0

    /*** DESCRIPTION OF OBJECTS CONTAINED IN THE FILE ***/

    OBJECT                               = IMAGE
        ENCODING_TYPE                    = "N/A"
        ENCODING_COMPRESSION_PERCENT     = 100.0
        NOMINAL_LINE_NUMBER              = 4088
        NOMINAL_OVERLAP_LINE_NUMBER      = 568
        OVERLAP_LINE_NUMBER              = 568
        LINES                            = 4656
        LINE_SAMPLES                     = 1600
        SAMPLE_TYPE                      = "MSB_INTEGER"
        SAMPLE_BITS                      = 16
        IMAGE_VALUE_TYPE                 = "RADIANCE"
        UNIT                             = "W/m**2/micron/sr"
        SCALING_FACTOR                   = 1.30000e-02
        OFFSET                           = 0.00000e+00
        MIN_FOR_STATISTICAL_EVALUATION   = 0
        MAX_FOR_STATISTICAL_EVALUATION   = 32767
        SCENE_MAXIMUM_DN                 = 7602
        SCENE_MINIMUM_DN                 = 1993
        SCENE_AVERAGE_DN                 = 2888.6
        SCENE_STDEV_DN                   = 370.2
        SCENE_MODE_DN                    = 2682
        SHADOWED_AREA_MINIMUM            = 0
        SHADOWED_AREA_MAXIMUM            = 0
        SHADOWED_AREA_PERCENTAGE         = 0
        INVALID_TYPE                     = ("SATURATION" , "MINUS" , "DUMMY_DEFECT" , "OTHER")
        INVALID_VALUE                    = (-20000 , -21000 , -22000 , -23000)
        INVALID_PIXELS                   = (0 , 0 , 0 , 0)
    END_OBJECT                           = IMAGE

    OBJECT                               = PROCESSING_PARAMETERS
        DARK_FILE_NAME                   = "TC1_DRK_00293_02951_S_N_b05.csv"
        FLAT_FILE_NAME                   = "TC1_FLT_00293_04739_N_N_b05.csv"
        EFFIC_FILE_NAME                  = "TC1_EFF_PRFLT_N_N_v01.csv"
        NONLIN_FILE_NAME                 = "TC1_NLT_PRFLT_N_N_v01.csv"
        RAD_CNV_COEF                     = 3.790009 <W/m**2/micron/sr>
        L2A_DEAD_PIXEL_THRESHOLD         = 30
        L2A_SATURATION_THRESHOLD         = 1023
        DARK_VALID_MINIMUM               = -5
        RADIANCE_SATURATION_THRESHOLD    = 425.971000 <W/m**2/micron/sr>
    END_OBJECT                           = PROCESSING_PARAMETERS
    END
    """

def test_kaguya_creation(kaguya_tclabel):
    #with KaguyaTcPds3NaifSpiceDriver(kaguya_tclabel) as m:
    #    d = m.to_dict()
    #    assert isinstance(d, dict)

    # Need to insert new tests here, one for each property unique to this driver
    assert True
def driver():
    return KaguyaTcPds3NaifSpiceDriver("")

@patch('ale.base.label_pds3.Pds3Label.instrument_id', 123)
def test_instrument_id(driver):
    with patch.dict(driver.label, {'SWATH_MODE_ID':'NOMINAL', 'PRODUCT_SET_ID':'TC_w_Level2B0' }) as f:
        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_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

def test_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

@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):
    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):
    assert driver.ephemeris_start_time == 0.1

def test_detector_center_line(driver):
    assert driver.detector_center_line == 0

@patch('ale.base.label_pds3.Pds3Label.instrument_id', 123)
def test_detector_center_sample(driver):
    assert driver.detector_center_sample == 0

@patch('ale.base.label_pds3.Pds3Label.instrument_id', 123)
@patch('ale.base.label_pds3.Pds3Label.target_name', 'MOON')
def test_sensor_orientation(driver):
    with patch('ale.base.type_sensor.LineScanner.ephemeris_time', new_callable=PropertyMock) as mock_time:
        mock_time.return_value = np.linspace(100,200,2)
        assert driver._sensor_orientation == [[2,3,4,1], [2,3,4,1]]

def test_reference_frame(driver):
    with patch('ale.base.label_pds3.Pds3Label.target_name', new_callable=PropertyMock) as mock_target_name:
        mock_target_name.return_value = 'MOOn'
        assert driver.reference_frame == 'MOON_ME'
        mock_target_name.return_value = 'sun'
        assert driver.reference_frame == 'NO TARGET'

@patch('ale.base.label_pds3.Pds3Label.instrument_id', 123)
def test_focal2pixel_samples(driver):
    assert driver.focal2pixel_samples == [0, 0, -1]

@patch('ale.base.label_pds3.Pds3Label.instrument_id', 123)
def test_focal2pixel_lines(driver):
    assert driver.focal2pixel_lines == [0, -1, 0]

@patch('ale.base.label_pds3.Pds3Label.instrument_id', 123)
def test_odkx(driver):
    assert driver._odkx == [1, 1, 1, 1]

@patch('ale.base.label_pds3.Pds3Label.instrument_id', 123)
def test_odky(driver):
    assert driver._odky == [1, 1, 1, 1]

def test_line_exposure_duration(driver):
    with patch.dict(driver.label, {'CORRECTED_SAMPLING_INTERVAL':
        pvl._collections.Units(value=1000, units='<msec>')}) as f:
        assert driver.line_exposure_duration == 1
    with patch.dict(driver.label, {'CORRECTED_SAMPLING_INTERVAL':
        [pvl._collections.Units(value=10000, units='<msec>'), 1]}) as f:
         assert driver.line_exposure_duration == 10

@patch('ale.base.label_pds3.Pds3Label.instrument_id', 123)
def test_focal_length(driver):
    assert driver.focal_length == 1

@patch('ale.base.label_pds3.Pds3Label.instrument_id', 123)
def test_usgscsm_distortion_model(driver):
    distortion_model = driver.usgscsm_distortion_model
    assert distortion_model['kaguyatc']['x'] == [1, 1, 1, 1]
    assert distortion_model['kaguyatc']['y'] == [1, 1, 1, 1]

def test_detector_start_sample(driver):
    with patch.dict(driver.label, {'FIRST_PIXEL_NUMBER': 123}) as f:
        assert driver.detector_start_sample == 123

def test_sensor_model_version(driver):
    assert driver.sensor_model_version == 1