Skip to content

Commit

Permalink
Merge remote-tracking branch 'refs/remotes/upstream/main' into dawnfc…
Browse files Browse the repository at this point in the history
…-driver
  • Loading branch information
antonhibl committed Oct 12, 2023
2 parents 76cefa8 + 695d312 commit 208bbeb
Show file tree
Hide file tree
Showing 11 changed files with 8,556 additions and 8,166 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ release.
## [Unreleased]

### Added
- Mariner10 IsisLabelNaifSpice driver, tests, and test data [#547](https://github.com/DOI-USGS/ale/pull/547)
- Mariner10 IsisLabelNaifSpice driver, tests, and test data [#547](https://github.com/DOI-USGS/ale/pull/547)

### Fixed
- Fixed LRO MiniRF drivers naif keywords focal to pixel and pixel to focal translations to be correct. [#563](https://github.com/DOI-USGS/ale/pull/563)

## [0.9.1] - 2023-06-05

Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# Specify the required version of CMake.
# cmake 3.15 required for findPython virtualenv configuration
cmake_minimum_required(VERSION 3.15)
project(ale VERSION 0.9.0 DESCRIPTION "Abstraction Library for Ephemerides ")
project(ale VERSION 0.9.1 DESCRIPTION "Abstraction Library for Ephemerides ")

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")

Expand Down
34 changes: 31 additions & 3 deletions ale/base/type_sensor.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import math

import numpy as np
import spiceypy as spice
from scipy.spatial.transform import Rotation

from ale.transformation import FrameChain
from ale.transformation import ConstantRotation
from ale.transformation import ConstantRotation, TimeDependentRotation

class LineScanner():
"""
Expand Down Expand Up @@ -468,7 +469,7 @@ def cahvor_rotation_matrix(self):
H_prime = (self.cahvor_camera_dict['H'] - h_c * self.cahvor_camera_dict['A'])/h_s
V_prime = (self.cahvor_camera_dict['V'] - v_c * self.cahvor_camera_dict['A'])/v_s
if self._props.get("landed", False):
self._cahvor_rotation_matrix = np.array([H_prime, -V_prime, -self.cahvor_camera_dict['A']])
self._cahvor_rotation_matrix = np.array([-H_prime, -V_prime, self.cahvor_camera_dict['A']])
else:
self._cahvor_rotation_matrix = np.array([H_prime, V_prime, self.cahvor_camera_dict['A']])
return self._cahvor_rotation_matrix
Expand All @@ -485,12 +486,39 @@ def frame_chain(self):
A networkx frame chain object
"""
if not hasattr(self, '_frame_chain'):
nadir = self._props.get("nadir", False)
self._frame_chain = FrameChain.from_spice(sensor_frame=self.final_inst_frame,
target_frame=self.target_frame_id,
center_ephemeris_time=self.center_ephemeris_time,
ephemeris_times=self.ephemeris_time,
nadir=False, exact_ck_times=False)
nadir=nadir, exact_ck_times=False)
cahvor_quats = Rotation.from_matrix(self.cahvor_rotation_matrix).as_quat()

if nadir:
# Logic for nadir calculation was taken from ISIS3
# SpiceRotation::setEphemerisTimeNadir
rotation = self._frame_chain.compute_rotation(self.target_frame_id, 1)
p_vec, v_vec, times = self.sensor_position
rotated_positions = rotation.apply_at(p_vec, times)
rotated_velocities = rotation.rotate_velocity_at(p_vec, v_vec, times)

p_vec = rotated_positions
v_vec = rotated_velocities

velocity_axis = 2
# Get the default line translation with no potential flipping
# from the driver
trans_x = np.array(self.focal2pixel_lines)

if (trans_x[0] < trans_x[1]):
velocity_axis = 1

quats = [spice.m2q(spice.twovec(-p_vec[i], 3, v_vec[i], velocity_axis)) for i, time in enumerate(times)]
quats = np.array(quats)[:,[1,2,3,0]]

rotation = TimeDependentRotation(quats, times, 1, self.final_inst_frame)
self._frame_chain.add_edge(rotation)

# If we are landed we only care about the final cahvor frame relative to the target
if self._props.get("landed", False):
cahvor_rotation = ConstantRotation(cahvor_quats, self.target_frame_id, self.sensor_frame_id)
Expand Down
30 changes: 26 additions & 4 deletions ale/drivers/lro_drivers.py
Original file line number Diff line number Diff line change
Expand Up @@ -814,19 +814,21 @@ def ephemeris_start_time(self):
: float
start time
"""
return spice.str2et(self.utc_start_time.strftime("%Y-%m-%d %H:%M:%S.%f"))
return spice.str2et(self.utc_start_time.strftime("%Y-%m-%d %H:%M:%S.%f")) - self.line_exposure_duration

@property
def ephemeris_stop_time(self):
"""
Returns the stop ephemeris time for the image.
Returns the stop ephemeris time for the image. This is computed from
the start time plus the line exposure per line, plus the line exposure
removed from the start time, plus the line exposure for the final line.
Returns
-------
: float
stop time
"""
return spice.str2et(self.utc_stop_time.strftime("%Y-%m-%d %H:%M:%S.%f"))
return self.ephemeris_start_time + (self.image_lines * self.line_exposure_duration) + (self.line_exposure_duration * 2)

@property
def look_direction(self):
Expand All @@ -846,15 +848,35 @@ def sensor_frame_id(self):
Returns the Naif ID code for the sensor reference frame
We replace this with the target frame ID because the sensor operates
entirely in the target reference frame
Returns
-------
: int
Naif ID code for the sensor frame
"""
return self.target_frame_id

@property
def naif_keywords(self):
"""
Adds the correct TRANSX/Y and ITRANS/L values for use in ISIS. By default
these values are placeholders in the ISIS iaks and need to be computed manully
from the ground range resolution. See RadarGroundRangeMap.cpp in ISIS for
the calculations.
Returns
-------
: dict
An updated dictionary of NAIF keywords with the correct TRANSX/Y and ITRANSS/L
values computed
"""
naif_keywords = super().naif_keywords
ground_range_resolution = self.label['IsisCube']['Instrument']["ScaledPixelHeight"]
icode = "INS" + str(self.ikid)
naif_keywords[icode + "_TRANSX"] = [-1.0 * ground_range_resolution, ground_range_resolution, 0.0]
naif_keywords[icode + "_TRANSY"] = [0.0, 0.0, 0.0]
naif_keywords[icode + "_ITRANSS"] = [1.0, 1.0 / ground_range_resolution, 0.0]
naif_keywords[icode + "_ITRANSL"] = [0.0, 0.0, 0.0]
return naif_keywords

class LroLrocWacIsisLabelIsisSpiceDriver(PushFrame, IsisLabel, IsisSpice, RadialDistortion, Driver):
@property
Expand Down
10 changes: 8 additions & 2 deletions ale/drivers/msl_drivers.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,10 @@ def focal2pixel_lines(self):
: list<double>
focal plane to detector lines
"""
return [0, 0, 1/self.pixel_size]
if self._props.get("landed", False):
return [0, 0, -1/self.pixel_size]
else:
return [0, 0, 1/self.pixel_size]

@property
def focal2pixel_samples(self):
Expand All @@ -120,7 +123,10 @@ def focal2pixel_samples(self):
: list<double>
focal plane to detector samples
"""
return [0, -1/self.pixel_size, 0]
if (self._props.get("nadir", False)):
return [0, 1/self.pixel_size, 0]
else:
return [0, -1/self.pixel_size, 0]

@property
def sensor_model_version(self):
Expand Down
17 changes: 14 additions & 3 deletions ale/isd_generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,15 @@ def main():
parser.add_argument(
"-l", "--local",
action="store_true",
help="Generate local spice data, or image that is unaware of itself relative to "
help="Generate local spice data, an isd that is unaware of itself relative to "
"target body. This is largely used for landed/rover data."
)
parser.add_argument(
"-N", "--nadir",
action="store_true",
help="Generate nadir spice pointing, an isd that has pointing directly towards "
"the center of the target body."
)
parser.add_argument(
'--version',
action='version',
Expand Down Expand Up @@ -117,7 +123,8 @@ def main():
"log_level": log_level,
"only_isis_spice": args.only_isis_spice,
"only_naif_spice": args.only_naif_spice,
"local": args.local}
"local": args.local,
"nadir": args.nadir}
): f for f in args.input
}
for f in concurrent.futures.as_completed(futures):
Expand All @@ -138,7 +145,8 @@ def file_to_isd(
log_level=logging.WARNING,
only_isis_spice=False,
only_naif_spice=False,
local=False
local=False,
nadir=False
):
"""
Returns nothing, but acts as a thin wrapper to take the *file* and generate
Expand Down Expand Up @@ -167,6 +175,9 @@ def file_to_isd(
if local:
props['landed'] = local

if nadir:
props['nadir'] = nadir

if kernels is not None:
kernels = [str(PurePath(p)) for p in kernels]
props["kernels"] = kernels
Expand Down
33 changes: 21 additions & 12 deletions ale/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,15 @@ def find_latest_metakernel(path, year):


def dict_merge(dct, merge_dct):
new_dct = dct.copy()
for k, v in merge_dct.items():
if (k in dct and isinstance(dct[k], dict)
and isinstance(merge_dct[k], Mapping)):
dict_merge(dct[k], merge_dct[k])
new_dct[k] = dict_merge(dct[k], merge_dct[k])
else:
dct[k] = merge_dct[k]
new_dct[k] = merge_dct[k]

return dct
return new_dct


def get_isis_preferences(isis_preferences=None):
Expand Down Expand Up @@ -178,15 +179,23 @@ def dict_to_lower(d):


def expandvars(path, env_dict=os.environ, default=None, case_sensitive=True):
user_dict = env_dict if case_sensitive else dict_to_lower(env_dict)
if env_dict != os.environ:
env_dict = dict_merge(env_dict, os.environ)

def replace_var(m):
group0 = m.group(0) if case_sensitive else m.group(0).lower()
group1 = m.group(1) if case_sensitive else m.group(1).lower()
while "$" in path:
user_dict = env_dict if case_sensitive else dict_to_lower(env_dict)

return user_dict.get(m.group(2) or group1, group0 if default is None else default)
reVar = r'\$(\w+|\{([^}]*)\})'
return re.sub(reVar, replace_var, path)
def replace_var(m):
group1 = m.group(1) if case_sensitive else m.group(1).lower()
val = user_dict.get(m.group(2) or group1 if default is None else default)
if not val:
raise KeyError(f"Failed to evaluate {m.group(0)} from env_dict. " +
f"Should {m.group(0)} be an environment variable?")

return val
reVar = r'\$(\w+|\{([^}]*)\})'
path = re.sub(reVar, replace_var, path)
return path


def generate_kernels_from_cube(cube, expand=False, format_as='list'):
Expand Down Expand Up @@ -281,7 +290,7 @@ def load_table_data(key):
if not "DataDirectory" in isisprefs:
warnings.warn("No IsisPreferences file found, is your ISISROOT env var set?")

kernels = [expandvars(expandvars(k, isisprefs['DataDirectory'], case_sensitive=False)) for k in kernels]
kernels = [expandvars(k, isisprefs['DataDirectory'], case_sensitive=False) for k in kernels]
# Ensure that the ISIS Addendum kernel is last in case it overrides
# some values from the default Instrument kernel
# Sorts planetary constants kernel first so it can be overridden by more specific kernels
Expand All @@ -295,7 +304,7 @@ def load_table_data(key):
for kern_list in mk_paths:
for index, kern in enumerate(mk_paths[kern_list]):
if kern is not None:
mk_paths[kern_list][index] = expandvars(expandvars(kern, isisprefs['DataDirectory'], case_sensitive=False))
mk_paths[kern_list][index] = expandvars(kern, isisprefs['DataDirectory'], case_sensitive=False)
return mk_paths
else:
raise Exception(f'{format_as} is not a valid return format')
Expand Down
Loading

0 comments on commit 208bbeb

Please sign in to comment.