Skip to content

Commit

Permalink
Lunar orbiter high camera (#553)
Browse files Browse the repository at this point in the history
* skeleton class for LO

* lo center time and ikid

* detector center line/sample dummy values

* LO driver (processes cube w/o error)

* lo test data

* lunar orbiter tests

* removed old comment

* removed geotransform and projection from ISD
  • Loading branch information
jrcain-usgs authored Sep 20, 2023
1 parent 57e0d2a commit a9939ac
Show file tree
Hide file tree
Showing 19 changed files with 372,482 additions and 117 deletions.
26 changes: 26 additions & 0 deletions ale/base/type_distortion.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import numpy as np
import spiceypy as spice

class LegendreDistortion():
"""
Expand Down Expand Up @@ -166,4 +167,29 @@ def usgscsm_distortion_model(self):
"x_coefficients" : transx,
"y_coefficients" : transy
}
}

class LoDistortion():
@property
def usgscsm_distortion_model(self):

# From ISIS LoHighDistortionMap::SetDistortion()
# Get the perspective correction factors for x and y and the distortion
# center (point of symmetry of distortion)
perspective_key = 'INS{}_PERSPECTIVE_FACTORS'.format(self.ikid)
center_key = 'INS{}_POINT_OF_SYMMETRY'.format(self.ikid)
perspective_x = float(spice.gdpool(perspective_key, 0, 1)[0])
perspective_y = float(spice.gdpool(perspective_key, 0, 2)[1])
center_point_x = float(spice.gdpool(center_key, 0, 1)[0])
center_point_y = float(spice.gdpool(center_key, 0, 2)[1])

# Get the distortion coefficients
# CameraDistortionMap::SetDistortion(naifIkCode);
return {
"lunarorbiter":{
"perspective_x" : perspective_x,
"perspective_y" : perspective_y,
"center_point_x" : center_point_x,
"center_point_y" : center_point_y
}
}
198 changes: 198 additions & 0 deletions ale/drivers/lo_drivers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import spiceypy as spice
import numpy as np
import affine6p
from ale.base.data_naif import NaifSpice
from ale.base.label_isis import IsisLabel
from ale.base.type_sensor import Framer
from ale.base.type_distortion import LoDistortion
from ale.base.base import Driver

class LoHighCameraIsisLabelNaifSpiceDriver(Framer, IsisLabel, NaifSpice, LoDistortion, Driver):

@property
def instrument_id(self):
"""
Returns the ID of the instrument.
Returns
-------
: str
Name of the instrument
"""

lo_table = {'Lunar Orbiter 1': 'LO1_HIGH_RESOLUTION_CAMERA',
'Lunar Orbiter 2': 'LO2_HIGH_RESOLUTION_CAMERA',
'Lunar Orbiter 3': 'LO3_HIGH_RESOLUTION_CAMERA',
'Lunar Orbiter 4': 'LO4_HIGH_RESOLUTION_CAMERA',
'Lunar Orbiter 5': 'LO5_HIGH_RESOLUTION_CAMERA'}

lookup_table = {'High Resolution Camera': lo_table[self.spacecraft_name]}

return lookup_table[super().instrument_id]

@property
def sensor_model_version(self):
"""
The ISIS Sensor model number. This is likely just 1
Returns
-------
: int
ISIS sensor model version
"""
return 1

@property
def sensor_name(self):
"""
Returns the name of the instrument
Returns
-------
: str
Name of the sensor
"""
return self.instrument_id

@property
def ephemeris_start_time(self):
"""
Returns the ephemeris time of the image.
Expects spacecraft_id to be defined. This should be the integer
Naif ID code for the spacecraft.
Returns
-------
: float
ephemeris time of the image
"""

return spice.utc2et(self.utc_start_time.strftime("%Y-%m-%d %H:%M:%S.%f"))

@property
def ephemeris_stop_time(self):
"""
Returns the ephemeris time of the image.
Expects spacecraft_id to be defined. This should be the integer
Naif ID code for the spacecraft.
Returns
-------
: float
ephemeris time of the image
"""

return self.ephemeris_start_time



@property
def ikid(self):
"""
Overridden to grab the ikid from the Isis Cube since there is no way to
obtain this value with a spice bods2c call.
Returns
-------
: int
Naif ID used to for identifying the instrument in Spice kernels
"""
return spice.namfrm(self.instrument_id)

@property
def detector_center_line(self):
"""
Returns the center detector line. This is a placeholder for use within
Isis. Since Isis does not use much of the ISD this value allows the as
accurate ISD for use in Isis but will be inaccurate for USGSCSM.
Returns
-------
: float
Detector sample of the principal point
"""
return 0

@property
def detector_center_sample(self):
"""
Returns the center detector sample. This is a placeholder for use within
Isis. Since Isis does not use much of the ISD this value allows the as
accurate ISD for use in Isis but will be inaccurate for USGSCSM.
Returns
-------
: float
Detector line of the principal point
"""
return 0

@property
def focal2pixel_samples(self):
return self.naif_keywords[f"INS{self.ikid}_ITRANSS"]

@property
def focal2pixel_lines(self):
return self.naif_keywords[f"INS{self.ikid}_ITRANSL"]


@property
def naif_keywords(self):
"""
Adds base LO instrument distortion.
Returns
-------
: dict
Dictionary of keywords and values that ISIS creates and attaches to the label
"""

if (not hasattr(self, "_naif_keywords")):
# From ISIS LoCameraFiducialMap

# Read Fiducials
p_fidSamples = self.label['IsisCube']['Instrument']['FiducialSamples'].value
p_fidLines = self.label['IsisCube']['Instrument']['FiducialLines'].value
p_fidXCoords = self.label['IsisCube']['Instrument']['FiducialXCoordinates'].value
p_fidYCoords = self.label['IsisCube']['Instrument']['FiducialYCoordinates'].value

# Create Affine Transformation

p_src = [p_fidSamples, p_fidLines]
p_dst = [p_fidXCoords, p_fidYCoords]

# format the fiducial coordinatens as [ [x, y], [x, y]...]
p_src = np.rot90(np.array([p_fidSamples, p_fidLines]))
p_dst = np.rot90(np.array([p_fidXCoords, p_fidYCoords]))

# find a best match for the transformation based on source and destination coordinates
tr_mat = affine6p.estimate(p_src, p_dst).get_matrix()

tr_mat_inv = np.linalg.inv(tr_mat)

# X and Y, Inverse S and L components of transformation
transx = tr_mat[0]
transy = tr_mat[1]
itranss = tr_mat_inv[0]
itransl = tr_mat_inv[1]

# move the last item to the front to get the ordering standard in ISIS
transx.insert(0, transx.pop())
transy.insert(0, transy.pop())
itranss = np.roll(itranss, 1).tolist()
itransl = np.roll(itransl, 1).tolist()

# Set the x-axis direction. The medium camera is reversed.
# High Cam is -53X001, Medium Cam is -53X002
if (self.ikid % 2 == 0):
x_dir = -1
transx = [i * x_dir for i in transx]
itranss[1] *= x_dir
itransl[1] *= x_dir

self._naif_keywords = {**super().naif_keywords,
f"INS{self.ikid}_TRANSX": transx,
f"INS{self.ikid}_TRANSY": transy,
f"INS{self.ikid}_ITRANSS": itranss,
f"INS{self.ikid}_ITRANSL": itransl}

return self._naif_keywords
3 changes: 3 additions & 0 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,6 @@ dependencies:
- pytest-cov
- networkx
- breathe
- pip
- pip:
- affine6p
3 changes: 2 additions & 1 deletion include/ale/Distortion.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ namespace ale {
KAGUYALISM,
DAWNFC,
LROLROCNAC,
CAHVOR
CAHVOR,
LUNARORBITER
};
}

Expand Down
Loading

0 comments on commit a9939ac

Please sign in to comment.