Unverified Commit 1b5a5969 authored by Amy Stamile's avatar Amy Stamile Committed by GitHub
Browse files

Initial MSL Mastcam Driver (#491)

* Initial MSL Mastcam Driver

* added docs

* added cahvor params and sensor frame id to driver.

* added unit tests

* added test data

* small updates to cahvor camera model function

* added focal2pixel functions

* fixes docs
parent beafc19a
Loading
Loading
Loading
Loading
+15 −11
Original line number Original line Diff line number Diff line
@@ -288,19 +288,23 @@ class Pds3Label():
         """
         """
        # The EXPOSURE_DURATION may either be stored as a (value, unit) or just a value
        # The EXPOSURE_DURATION may either be stored as a (value, unit) or just a value
        if 'EXPOSURE_DURATION' in self.label:
        if 'EXPOSURE_DURATION' in self.label:
            key = self.label['EXPOSURE_DURATION']
        elif 'INSTRUMENT_STATE_PARMS' in self.label:
            key = self.label['INSTRUMENT_STATE_PARMS']['EXPOSURE_DURATION']
        else:
            return self.line_exposure_duration
        try:
        try:
                unit = self.label['EXPOSURE_DURATION'].units
            unit = key.units
            unit = unit.lower()
            unit = unit.lower()
            if unit == "ms" or unit == "msec" or unit == "millisecond":
            if unit == "ms" or unit == "msec" or unit == "millisecond":
                  return self.label['EXPOSURE_DURATION'].value * 0.001
                return key.value * 0.001
            else:
            else:
                  return self.label['EXPOSURE_DURATION'].value
                return key.value


        # With no units, assume milliseconds
        # With no units, assume milliseconds
        except:
        except:
                return self.label['EXPOSURE_DURATION'] * 0.001
            return key * 0.001
        else:
        
            return self.line_exposure_duration




    # Consider expanding this to handle units
    # Consider expanding this to handle units
+117 −0
Original line number Original line Diff line number Diff line
import numpy as np
import spiceypy as spice

from ale.base.data_naif import NaifSpice
from ale.base.label_pds3 import Pds3Label
from ale.base.type_sensor import Framer
from ale.base.type_distortion import NoDistortion
from ale.base.base import Driver

class MslMastcamPds3NaifSpiceDriver(Framer, Pds3Label, NaifSpice, NoDistortion, Driver):
    @property
    def spacecraft_name(self):
        """
        Spacecraft name used in various SPICE calls to acquire
        ephemeris data. MSL Mastcam img PDS3 labels do not the have a SPACECRAFT_NAME keyword,
        so we override it here to find INSTRUMENT_HOST_NAME in the label.

        Returns
        -------
        : str
          Spacecraft name
        """
        return self.instrument_host_name
    
    @property
    def instrument_id(self):
        """
        Returns an instrument id for uniquely identifying the instrument, but often
        also used to be piped into Spice Kernels to acquire IKIDs. Therefore they
        the same ID the Spice expects in bods2c calls.

        Expects instrument_id to be defined in the Pds3Label mixin. This should
        be a string of the form MAST_RIGHT or MAST_LEFT.

        Returns
        -------
        : str
          instrument id
        """
        lookup = {
          "MAST_RIGHT": 'MASTCAM_RIGHT',
          "MAST_LEFT": 'MASTCAM_LEFT'
        }
        return self.instrument_host_id + "_" + lookup[super().instrument_id]

    @property
    def cahvor_camera_dict(self):
        """
        Gets the PVL group that represents the CAHVOR camera model
        for the site
        Returns
        -------
        : dict
          A dict of CAHVOR keys to use in other methods
        """
        if not hasattr(self, '_cahvor_camera_params'):
            camera_model_group = self.label.get('GEOMETRIC_CAMERA_MODEL_PARMS', None)

            self._cahvor_camera_params = {}
            self._cahvor_camera_params['C'] = np.array(camera_model_group["MODEL_COMPONENT_1"])
            self._cahvor_camera_params['A'] = np.array(camera_model_group["MODEL_COMPONENT_2"])
            self._cahvor_camera_params['H'] = np.array(camera_model_group["MODEL_COMPONENT_3"])
            self._cahvor_camera_params['V'] = np.array(camera_model_group["MODEL_COMPONENT_4"])
            if len(camera_model_group.get('MODEL_COMPONENT_ID', ['C', 'A', 'H', 'V'])) == 6:
                self._cahvor_camera_params['O'] = np.array(camera_model_group["MODEL_COMPONENT_5"])
                self._cahvor_camera_params['R'] = np.array(camera_model_group["MODEL_COMPONENT_6"])
        return self._cahvor_camera_params

    @property
    def sensor_frame_id(self):
        """
        Returns the Naif ID code for the site reference frame
        Expects REFERENCE_COORD_SYSTEM_INDEX to be defined in the camera
        PVL group. 
        Returns
        -------
        : int
          Naif ID code for the sensor frame
        """
        if not hasattr(self, "_site_frame_id"):
          site_frame = "MSL_SITE_" + str(self.label["GEOMETRIC_CAMERA_MODEL_PARMS"]["REFERENCE_COORD_SYSTEM_INDEX"][0])
          self._site_frame_id= spice.bods2c(site_frame)
        return self._site_frame_id

    @property
    def focal2pixel_lines(self):
        """
        Expects pixel_size to be defined.

        Returns
        -------
        : list<double>
          focal plane to detector lines
        """
        return [0, 1/self.pixel_size, 0]
    
    @property
    def focal2pixel_samples(self):
        """
        Expects pixel_size to be defined. 

        Returns
        -------
        : list<double>
          focal plane to detector samples
        """
        return [1/self.pixel_size, 0, 0]
    
    @property
    def sensor_model_version(self):
        """
        Returns
        -------
        : int
          ISIS sensor model version
        """
        return 1
+493 −0

File added.

Preview size limit exceeded, changes collapsed.

+42 −0
Original line number Original line Diff line number Diff line
import numpy as np
import unittest

import ale
from ale.drivers.msl_drivers import MslMastcamPds3NaifSpiceDriver

from conftest import get_image_label
from unittest.mock import PropertyMock, patch

class test_mastcam_pds_naif(unittest.TestCase):
    def setUp(self):
        label = get_image_label("1664MR0086340000802438C00_DRCL", "pds3")
        self.driver = MslMastcamPds3NaifSpiceDriver(label)

    def test_instrument_id(self):
        assert self.driver.instrument_id == "MSL_MASTCAM_RIGHT"

    def test_spacecraft_name(self):
        assert self.driver.spacecraft_name == "MARS SCIENCE LABORATORY"

    def test_exposure_duration(self):
        np.testing.assert_almost_equal(self.driver.exposure_duration, 0.0102)

    def test_cahvor_camera_dict(self):
        cahvor_camera_dict = self.driver.cahvor_camera_dict
        assert len(cahvor_camera_dict) == 4
        np.testing.assert_allclose(cahvor_camera_dict['C'], [6.831825e-01, 5.243722e-01, -1.955875e+00])
        np.testing.assert_allclose(cahvor_camera_dict['A'], [-3.655151e-01, 5.396012e-01, 7.584387e-01])
        np.testing.assert_allclose(cahvor_camera_dict['H'], [-1.156881e+04, -7.518712e+03, 6.618359e+02])
        np.testing.assert_allclose(cahvor_camera_dict['V'], [5.843885e+03, -8.213856e+03, 9.438374e+03])

    def test_sensor_frame_id(self):
        with patch('ale.drivers.msl_drivers.spice.bods2c', return_value=-76562) as bods2c:
            assert self.driver.sensor_frame_id == -76562
            bods2c.assert_called_with("MSL_SITE_62")

    # uncomment once cahvor mixin is merged
    # def test_focal2pixel_lines(self):
    #     np.testing.assert_allclose(self.driver.focal2pixel_lines, [0, 137968.44341513602, 0])

    # def test_focal2pixel_samples(self):
    #     np.testing.assert_allclose(self.driver.focal2pixel_samples, [137968.44341513602, 0, 0])