Commit 6cefa2bb authored by acpaquette's avatar acpaquette Committed by Jesse Mapel
Browse files

Frame Chain Update (#216)

* Significant update to framechain computation

* Updated default config for dawn

* Initial update to isis formatter

* Got correct rotation

* change to compute time dependent frames

* Further isis formatter updates

* finished compling to the tests

* Added back NaifSpice import

* Added networkx to environment file

* Updated doc strings for the FrameChain class

* Updated FrameChain with class function for spice and overloaded add_edge method

* Updated isis formatter to use node ids and not the time dependent source

* Updated test fixtures to use the new add_edge method

* Rmoved networkx import from data_naif
parent 42ee087a
Loading
Loading
Loading
Loading
+5 −63
Original line number Diff line number Diff line
import spiceypy as spice
import numpy as np

from ale.base.type_sensor import Framer
from ale.transformation import FrameNode
from ale.transformation import FrameChain
from ale.rotation import TimeDependentRotation

class NaifSpice():
@@ -313,68 +314,9 @@ class NaifSpice():

    @property
    def frame_chain(self):
        """
        Return the root node of the rotation frame tree/chain.

        The root node is the J2000 reference frame. The other nodes in the
        tree can be accessed via the methods in the FrameNode class.

        This property expects the ephemeris_time property/attribute to be defined.
        It should be a list of the ephemeris seconds past the J2000 epoch for each
        exposure in the image.

        Returns
        -------
        FrameNode
            The root node of the frame tree. This will always be the J2000 reference frame.
        """
        if not hasattr(self, '_root_frame'):
            j2000_id = 1 #J2000 is our root reference frame
            self._root_frame = FrameNode(j2000_id)

            sensor_quats = np.zeros((len(self.ephemeris_time), 4))
            sensor_times = np.array(self.ephemeris_time)
            body_quats = np.zeros((len(self.ephemeris_time), 4))
            body_times = np.array(self.ephemeris_time)
            for i, time in enumerate(self.ephemeris_time):
                sensor2j2000 = spice.pxform(
                    spice.frmnam(self.sensor_frame_id),
                    spice.frmnam(j2000_id),
                    time)
                q_sensor = spice.m2q(sensor2j2000)
                sensor_quats[i,:3] = q_sensor[1:]
                sensor_quats[i,3] = q_sensor[0]

                body2j2000 = spice.pxform(
                    spice.frmnam(self.target_frame_id),
                    spice.frmnam(j2000_id),
                    time)
                q_body = spice.m2q(body2j2000)
                body_quats[i,:3] = q_body[1:]
                body_quats[i,3] = q_body[0]

            sensor2j2000_rot = TimeDependentRotation(
                sensor_quats,
                sensor_times,
                self.sensor_frame_id,
                j2000_id
            )
            sensor_node = FrameNode(
                self.sensor_frame_id,
                parent=self._root_frame,
                rotation=sensor2j2000_rot)

            body2j2000_rot = TimeDependentRotation(
                body_quats,
                body_times,
                self.target_frame_id,
                j2000_id
            )
            body_node = FrameNode(
                self.target_frame_id,
                parent=self._root_frame,
                rotation=body2j2000_rot)
        return self._root_frame
        if not hasattr(self, '_frame_chain'):
            self._frame_chain = FrameChain.from_spice(frame_changes = [(1, self.sensor_frame_id), (1, self.target_frame_id)], ephemeris_time=self.ephemeris_time)
        return self._frame_chain

    @property
    def sensor_orientation(self):
+2 −2
Original line number Diff line number Diff line
spice_root: "/data/spice/"
spice_root: '/data/spice/'
cassini: '/usgs/cpkgs/isis3/data/cassini/kernels/mk/' # Cassini ISS
mdis: '/scratch/jlaura/spice/mess-e_v_h-spice-6-v1.0/messsp_1000/extras/mk' # Messenger
mex: '/scratch/jlaura/spice/mex-e_m-spice-6-v1.0/mexsp_1000/EXTRAS/MK' # Mars Explorer
mro: '/data/spice/mro-m-spice-6-v1.0/mrosp_1000/extras/mk' # Mars Reconnaissance Orbiter
kaguya: '/data/spice/SELENE/kernels/mk/'
dawn: '/data/spice/dawn-m_a-spice-6-v1.0/dawnsp_1000/extras/mk'
dawn: '/scratch/spice/dawn-m_a-spice-6-v1.0/dawnsp_1000/extras/mk/'
lro: '/scratch/jlaura/spice/lro-l-spice-6-v1.0/lrosp_1000/extras/mk/' # LRO
+23 −22
Original line number Diff line number Diff line
import json

from ale.transformation import FrameNode
from ale.rotation import ConstantRotation, TimeDependentRotation
from ale.encoders import NumpyEncoder

from networkx.algorithms.shortest_paths.generic import shortest_path

def to_isis(driver):
    """
    Formatter to create ISIS sensor model meta data from a driver.
@@ -24,51 +25,51 @@ def to_isis(driver):

    meta_data['NaifKeywords'] = driver.isis_naif_keywords

    j2000 = driver.frame_chain
    frame_chain = driver.frame_chain
    sensor_frame = driver.sensor_frame_id

    instrument_pointing = {}
    sensor_frame = j2000.find_child_frame(driver.sensor_frame_id)
    time_dependent_sensor_frame = j2000.last_time_dependent_frame_between(sensor_frame)
    if time_dependent_sensor_frame != j2000:
        forward_path, reverse_path = j2000.path_to(time_dependent_sensor_frame)
    source_frame, destination_frame, time_dependent_sensor_frame = frame_chain.last_time_dependent_frame_between(sensor_frame, 1)

    if source_frame != 1:
        # Reverse the frame order because ISIS orders frames as
        # (destination, intermediate, ..., intermediate, source)
        instrument_pointing['TimeDependentFrames'] = [frame.id for frame in (forward_path + reverse_path)[::-1]]
        time_dependent_rotation = j2000.rotation_to(time_dependent_sensor_frame)
        instrument_pointing['TimeDependentFrames'] = shortest_path(frame_chain, source_frame, 1)
        time_dependent_rotation = frame_chain.compute_rotation(1, source_frame)
        instrument_pointing['CkTableStartTime'] = time_dependent_rotation.times[0]
        instrument_pointing['CkTableEndTime'] = time_dependent_rotation.times[-1]
        instrument_pointing['CkTableOriginalSize'] = len(time_dependent_rotation.times)
        instrument_pointing['EphemerisTimes'] = time_dependent_rotation.times
        instrument_pointing['Quaternions'] = time_dependent_rotation.quats
    if time_dependent_sensor_frame != sensor_frame:
        forward_path, reverse_path = time_dependent_sensor_frame.path_to(sensor_frame)

    if source_frame != sensor_frame:
        # Reverse the frame order because ISIS orders frames as
        # (destination, intermediate, ..., intermediate, source)
        instrument_pointing['ConstantFrames'] = [frame.id for frame in (forward_path + reverse_path)[::-1]]
        constant_rotation = time_dependent_sensor_frame.rotation_to(sensor_frame)
        instrument_pointing['ConstantFrames'] = shortest_path(frame_chain, sensor_frame, source_frame)
        constant_rotation = frame_chain.compute_rotation(source_frame, sensor_frame)
        instrument_pointing['ConstantRotation'] = constant_rotation.rotation_matrix()
    meta_data['InstrumentPointing'] = instrument_pointing

    body_rotation = {}
    target_frame = j2000.find_child_frame(driver.target_frame_id)
    time_dependent_target_frame = j2000.last_time_dependent_frame_between(target_frame)
    if time_dependent_target_frame != j2000:
        forward_path, reverse_path = j2000.path_to(time_dependent_target_frame)
    target_frame = driver.target_frame_id
    source_frame, destination_frame, time_dependent_target_frame = frame_chain.last_time_dependent_frame_between(target_frame, 1)

    if source_frame != 1:
        # Reverse the frame order because ISIS orders frames as
        # (destination, intermediate, ..., intermediate, source)
        body_rotation['TimeDependentFrames'] = [frame.id for frame in (forward_path + reverse_path)[::-1]]
        time_dependent_rotation = j2000.rotation_to(time_dependent_target_frame)
        body_rotation['TimeDependentFrames'] = shortest_path(frame_chain, source_frame, 1)
        time_dependent_rotation = frame_chain.compute_rotation(1, source_frame)
        body_rotation['CkTableStartTime'] = time_dependent_rotation.times[0]
        body_rotation['CkTableEndTime'] = time_dependent_rotation.times[-1]
        body_rotation['CkTableOriginalSize'] = len(time_dependent_rotation.times)
        body_rotation['EphemerisTimes'] = time_dependent_rotation.times
        body_rotation['Quaternions'] = time_dependent_rotation.quats
    if time_dependent_target_frame != target_frame:
        forward_path, reverse_path = time_dependent_target_frame.path_to(target_frame)
        
    if source_frame != target_frame:
        # Reverse the frame order because ISIS orders frames as
        # (destination, intermediate, ..., intermediate, source)
        body_rotation['ConstantFrames'] = [frame.id for frame in (forward_path + reverse_path)[::-1]]
        constant_rotation = time_dependent_target_frame.rotation_to(target_frame)
        body_rotation['ConstantFrames'] = shortest_path(frame_chain, target_frame, source_frame)
        constant_rotation = frame_chain.compute_rotation(source_frame, target_frame)
        body_rotation['ConstantRotation'] = constant_rotation.rotation_matrix()
    meta_data['BodyRotation'] = body_rotation

+5 −4
Original line number Diff line number Diff line
import json

from ale.transformation import FrameChain

from ale.base.type_sensor import LineScanner, Framer
from ale.encoders import NumpyEncoder
from ale.rotation import ConstantRotation, TimeDependentRotation
@@ -39,10 +42,8 @@ def to_usgscsm(driver):
        'unit' : 'm'
    }

    j2000 = driver.frame_chain
    sensor_frame = j2000.find_child_frame(driver.sensor_frame_id)
    target_frame = j2000.find_child_frame(driver.target_frame_id)
    sensor_to_target = sensor_frame.rotation_to(target_frame)
    frame_chain = driver.frame_chain
    sensor_to_target = frame_chain.compute_rotation(driver.sensor_frame_id, driver.target_frame_id)
    quaternions = sensor_to_target.quats
    rotation_times = sensor_to_target.times
    isd_data['sensor_orientation'] = {
+7 −1
Original line number Diff line number Diff line
@@ -54,6 +54,9 @@ class ConstantRotation:
        self.dest = dest
        self.quat = np.asarray(quat)

    def __repr__(self):
        return f'ConstantRotation Source: {self.source}, Destination: {self.dest}, Quat: {self.quat}'

    @property
    def quat(self):
        """
@@ -174,6 +177,9 @@ class TimeDependentRotation:
        self.quats = np.asarray(quats)
        self.times = np.asarray(times)

    def __repr__(self):
        return f'Time Dependent Rotation Source: {self.source}, Destination: {self.dest}, Quat: {self.quats}'

    @property
    def quats(self):
        """
Loading