Skip to content
Snippets Groups Projects
Commit 405de643 authored by Kelvin Rodriguez's avatar Kelvin Rodriguez Committed by GitHub
Browse files

Merge pull request #70 from Kelvinrr/cubes

Refactoring how Drivers work
parents 80385f76 1cb73043
No related branches found
No related tags found
No related merge requests found
from abc import ABC, abstractmethod
from dateutil import parser from dateutil import parser
import numpy as np import numpy as np
import pvl import pvl
import spiceypy as spice import spiceypy as spice
from ale.drivers import distortion class Driver():
class Base(ABC):
""" """
Abstract base class for all PDS label parsing. Implementations should override Base class for all Drivers.
properties where a kernel provider deviates from the most broadly adopted
approach.
Methods that must be provided:
- instrument_id
- metakernel
""" """
def __init__(self, label, *args, **kwargs): def __init__(self, file):
self._label = pvl.loads(label, strict=False) self._file = file
def __enter__(self):
"""
Called when the context is created. This is used
to get the kernels furnished.
"""
if self.metakernel:
spice.furnsh(self.metakernel)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""
Called when the context goes out of scope. Once
this is done, the object is out of scope and the
kernels can be unloaded.
"""
spice.unload(self.metakernel)
def __str__(self): def __str__(self):
return str(self.to_dict()) return str(self.to_dict())
...@@ -49,51 +22,71 @@ class Base(ABC): ...@@ -49,51 +22,71 @@ class Base(ABC):
return False return False
def to_dict(self): def to_dict(self):
return {p:getattr(self, p) for p in dir(self) if not p.startswith('_') and not callable(getattr(self,p))} keys = set(dir(self)) & self.required_keys
return {p:getattr(self, p) for p in keys}
class LineScanner(Driver):
@property
def name_model(self):
return "USGS_ASTRO_LINE_SCANNER_SENSOR_MODEL"
def to_pfeffer_response(self): @property
def t0_ephemeris(self):
return self.starting_ephemeris_time - self.center_ephemeris_time
@property
def t0_quaternion(self):
return self.starting_ephemeris_time - self.center_ephemeris_time
@property
def dt_ephemeris(self):
return (self.ending_ephemeris_time - self.starting_ephemeris_time) / self.number_of_ephemerides
@property
def dt_quaternion(self):
return (self.ending_ephemeris_time - self.starting_ephemeris_time) / self.number_of_ephemerides
@property
def line_scan_rate(self):
""" """
Parse the data into a valid ale response In the form: [start_line, line_time, exposure_duration]
The form below is for a fixed rate line scanner.
""" """
data = self.to_dict() return [[float(self.starting_detector_line), self.t0_ephemeris, self.line_exposure_duration]]
# Take the flat reponse and create the ale obj dicts
data['detector_center'] = {'line': data['detector_center'][0],
'sample': data['detector_center'][1]}
# Parse the distortion object out of the
if isinstance(self, distortion.RadialDistortion): @property
data['optical_distortion'] = {'radial':{'coefficients':data['odtk']}} def number_of_ephemerides(self):
elif isinstance(self, distortion.TransverseDistortion): #TODO: Not make this hardcoded
data['optical_distortion'] = {'transverse':{'x':data['odtx'], return 909
'y':data['odty']}}
data['focal_length_model'] = {'focal_length': data['focal_length']} @property
if hasattr(self, 'focal_epsilon'): def number_of_quaternions(self):
data['focal_length_model']['focal_epsilon'] = data['focal_epsilon'] #TODO: Not make this hardcoded
return 909
data['reference_height'] = {'minheight': data['reference_height'][0],
'maxheight': data['reference_height'][1],
'unit': 'm'}
data['sensor_position'] = {'unit':'m', class Framer(Driver):
'velocities': data['sensor_velocity'], @property
'positions': data['sensor_position']} def name_model(self):
return "USGS_ASTRO_FRAME_SENSOR_MODEL"
data['sun_position'] = {'unit': 'm', @property
'positions': data['sun_position'], def filter_number(self):
'velocities': data['sun_velocity']} return self.label.get('FILTER_NUMBER', 0)
data['sensor_orientation'] = {'quaternions':data['sensor_orientation']} @property
def number_of_ephemerides(self):
# always one for framers
return 1
data['radii'] = {'semimajor':data['semimajor'], @property
'semiminor':data['semiminor'], def number_of_quaternions(self):
'unit': 'km'} # always one for framers
return 1
data['name_platform'] = data['spacecraft_name']
data['name_sensor'] = data['instrument_id']
return data
class PDS3():
def _compute_ephemerides(self): def _compute_ephemerides(self):
""" """
Helper function to pull position and velocity in one pass Helper function to pull position and velocity in one pass
...@@ -118,74 +111,91 @@ class Base(ABC): ...@@ -118,74 +111,91 @@ class Base(ABC):
self._sensor_velocity = eph_rates self._sensor_velocity = eph_rates
self._sensor_position = eph self._sensor_position = eph
@property
def focal_plane_tempature(self):
return self.label['FOCAL_PLANE_TEMPERATURE'].value
@property @property
@abstractmethod def label(self):
def metakernel(self): if not hasattr(self, "_label"):
pass if isinstance(self._file, pvl.PVLModule):
self._label = label
try:
self._label = pvl.loads(self._file, strict=False)
except AttributeError:
self._label = pvl.load(self._file, strict=False)
except:
raise Exception("{} is not a valid label".format(label))
return self._label
@property
def line_exposure_duration(self):
try:
return self.label['LINE_EXPOSURE_DURATION'].value * 0.001 # Scale to seconds
except:
return np.nan
@property @property
@abstractmethod
def instrument_id(self): def instrument_id(self):
pass pass
@property @property
def start_time(self): def start_time(self):
return self._label['START_TIME'] return self.label['START_TIME']
@property @property
def image_lines(self): def image_lines(self):
return self._label['IMAGE']['LINES'] return self.label['IMAGE']['LINES']
@property @property
def image_samples(self): def image_samples(self):
return self._label['IMAGE']['LINE_SAMPLES'] return self.label['IMAGE']['LINE_SAMPLES']
@property @property
def interpolation_method(self): def interpolation_method(self):
return 'lagrange' return 'lagrange'
@property
def number_of_ephemerides(self):
return 1
@property @property
def target_name(self): def target_name(self):
return self._label['TARGET_NAME'] return self.label['TARGET_NAME']
@property @property
def _target_id(self): def _target_id(self):
return spice.bodn2c(self._label['TARGET_NAME']) return spice.bodn2c(self.label['TARGET_NAME'])
@property
def focal_epsilon(self):
return float(spice.gdpool('INS{}_FL_UNCERTAINTY'.format(self.ikid), 0, 1)[0])
@property @property
def starting_ephemeris_time(self): def starting_ephemeris_time(self):
if not hasattr(self, '_starting_ephemeris_time'): if not hasattr(self, '_starting_ephemeris_time'):
sclock = self._label['SPACECRAFT_CLOCK_START_COUNT'] sclock = self.label['SPACECRAFT_CLOCK_START_COUNT']
self._starting_ephemeris_time = spice.scs2e(self.spacecraft_id, sclock) self._starting_ephemeris_time = spice.scs2e(self.spacecraft_id, sclock)
return self._starting_ephemeris_time return self._starting_ephemeris_time
@property @property
def _exposure_duration(self): def exposure_duration(self):
return self._label['EXPOSURE_DURATION'].value * 0.001 # Scale to seconds try:
return self.label['EXPOSURE_DURATION'].value * 0.001 # Scale to seconds
except:
return np.nan
@property @property
def spacecraft_clock_stop_count(self): def spacecraft_clock_stop_count(self):
sc = self._label.get('SPACECRAFT_CLOCK_STOP_COUNT', None) sc = self.label.get('SPACECRAFT_CLOCK_STOP_COUNT', None)
if sc == 'N/A': if sc == 'N/A':
sc = None sc = None
return sc return sc
@property @property
def ending_ephemeris_time(self): def ending_ephemeris_time(self):
if not hasattr(self, '_ending_ephemeris_time'): return (self.image_lines * self.line_exposure_duration) + self.starting_ephemeris_time
self._ending_ephemeris_time = (self.image_lines * self._exposure_duration) + self.starting_ephemeris_time
return self._ending_ephemeris_time
@property @property
def center_ephemeris_time(self): def center_ephemeris_time(self):
if not hasattr(self, '_center_ephemeris_time'): return (self.starting_ephemeris_time + self.ending_ephemeris_time)/2
self._center_ephemeris_time = (self.starting_ephemeris_time + self.ending_ephemeris_time)/2
return self._center_ephemeris_time
@property @property
def detector_center(self): def detector_center(self):
...@@ -193,50 +203,78 @@ class Base(ABC): ...@@ -193,50 +203,78 @@ class Base(ABC):
@property @property
def spacecraft_name(self): def spacecraft_name(self):
return self._label['MISSION_NAME'] return self.label['MISSION_NAME']
@property @property
def ikid(self): def starting_detector_line(self):
return spice.bods2c(self.instrument_id) return 1
@property @property
def fikid(self): def starting_detector_sample(self):
fn = self._label.get('FILTER_NUMBER', 0) return 1
if fn == 'N/A':
fn = 0
return self.ikid - int(fn)
@property @property
def spacecraft_id(self): def detector_sample_summing(self):
return spice.bods2c(self.spacecraft_name) return 1
@property @property
def focal2pixel_lines(self): def detector_line_summing(self):
return spice.gdpool('INS{}_ITRANSL'.format(self.fikid), 0, 3) return self.label.get('SAMPLING_FACTOR', 1)
class Spice():
@property @property
def focal2pixel_samples(self): def metakernel(self):
return spice.gdpool('INS{}_ITRANSS'.format(self.fikid), 0, 3) pass
def __enter__(self):
"""
Called when the context is created. This is used
to get the kernels furnished.
"""
if self.metakernel:
spice.furnsh(self.metakernel)
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""
Called when the context goes out of scope. Once
this is done, the object is out of scope and the
kernels can be unloaded.
"""
spice.unload(self.metakernel)
@property @property
def focal_length(self): def odtx(self):
return float(spice.gdpool('INS{}_FOCAL_LENGTH'.format(self.ikid), 0, 1)[0]) return spice.gdpool('INS{}_OD_T_X'.format(self.ikid),0, 10)
@property @property
def starting_detector_line(self): def odty(self):
return 1 return spice.gdpool('INS{}_OD_T_Y'.format(self.ikid), 0, 10)
@property @property
def starting_detector_sample(self): def odtk(self):
return 1 return spice.gdpool('INS{}_OD_K'.format(self.ikid),0, 3)
@property @property
def detector_sample_summing(self): def ikid(self):
return 1 return spice.bods2c(self.instrument_id)
@property @property
def detector_line_summing(self): def spacecraft_id(self):
return self._label.get('SAMPLING_FACTOR', 1) return spice.bods2c(self.spacecraft_name)
@property
def focal2pixel_lines(self):
return spice.gdpool('INS{}_ITRANSL'.format(self.fikid), 0, 3)
@property
def focal2pixel_samples(self):
return spice.gdpool('INS{}_ITRANSS'.format(self.fikid), 0, 3)
@property
def focal_length(self):
return float(spice.gdpool('INS{}_FOCAL_LENGTH'.format(self.ikid), 0, 1)[0])
@property @property
def semimajor(self): def semimajor(self):
...@@ -306,24 +344,6 @@ class Base(ABC): ...@@ -306,24 +344,6 @@ class Base(ABC):
# TODO: This should be a reasonable # # TODO: This should be a reasonable #
return 0, 100 return 0, 100
class LineScanner(Base):
@property
def name_model(self):
return "USGS_ASTRO_LINE_SCANNER_SENSOR_MODEL"
@property
def _exposure_duration(self):
return self._label['LINE_EXPOSURE_DURATION'].value * 0.001 # Scale to seconds
@property
def line_scan_rate(self):
"""
In the form: [start_line, line_time, exposure_duration]
The form below is for a fixed rate line scanner.
"""
return [[float(self.starting_detector_line), self.t0_ephemeris, self._exposure_duration]]
@property @property
def detector_center(self): def detector_center(self):
if not hasattr(self, '_detector_center'): if not hasattr(self, '_detector_center'):
...@@ -339,50 +359,63 @@ class LineScanner(Base): ...@@ -339,50 +359,63 @@ class LineScanner(Base):
""" """
if not hasattr(self, '_center_ephemeris_time'): if not hasattr(self, '_center_ephemeris_time'):
halflines = self.image_lines / 2 halflines = self.image_lines / 2
center_sclock = self.starting_ephemeris_time + halflines * self._exposure_duration center_sclock = self.starting_ephemeris_time + halflines * self.line_exposure_duration
self._center_ephemeris_time = center_sclock self._center_ephemeris_time = center_sclock
return self._center_ephemeris_time return self._center_ephemeris_time
@property @property
def t0_ephemeris(self): def fikid(self):
return self.starting_ephemeris_time - self.center_ephemeris_time if isinstance(self, Framer):
fn = self.filter_number
if fn == 'N/A':
fn = 0
else:
fn = 0
@property return self.ikid - int(fn)
def t0_quaternion(self):
return self.starting_ephemeris_time - self.center_ephemeris_time
@property
def dt_ephemeris(self):
return (self.ending_ephemeris_time - self.starting_ephemeris_time) / self.number_of_ephemerides
class Isis3():
@property @property
def dt_quaternion(self): def label(self):
return (self.ending_ephemeris_time - self.starting_ephemeris_time) / self.number_of_ephemerides if not hasattr(self, "_label"):
if isinstance(self._file, pvl.PVLModule):
self._label = label
try:
self._label = pvl.loads(self._file, strict=False)
except AttributeError:
self._label = pvl.load(self._file, strict=False)
except:
raise Exception("{} is not a valid label".format(label))
return self._label
@property @property
def number_of_ephemerides(self): def start_time(self):
return 909 return self.label['IsisCube']['Instrument']['StartTime']
@property @property
def number_of_quaternions(self): def spacecraft_name(self):
return 909 return self.label['IsisCube']['Instrument']['SpacecraftName']
@property @property
def _exposure_duration(self): def image_lines(self):
return self._label['LINE_EXPOSURE_DURATION'].value * 0.001 # Scale to seconds return self.label['IsisCube']['Core']['Dimensions']['Lines']
class Framer(Base): @property
def image_samples(self):
return self.label['IsisCube']['Core']['Dimensions']['Samples']
@property @property
def name_model(self): def _exposure_duration(self):
return "USGS_ASTRO_FRAME_SENSOR_MODEL" return self.label['IsisCube']['Instrument']['ExposureDuration'].value * 0.001 # Scale to seconds
@property @property
def number_of_ephemerides(self): def target_name(self):
return 1 return self.label['IsisCube']['Instrument']['TargetName']
@property @property
def number_of_quaternions(self): def starting_ephemeris_time(self):
return 1 if not hasattr(self, '_starting_ephemeris_time'):
sclock = self.label['IsisCube']['Archive']['SpacecraftClockStartCount']
self._starting_ephemeris_time = spice.scs2e(self.spacecraft_id, sclock)
return self._starting_ephemeris_time
...@@ -7,15 +7,17 @@ import numpy as np ...@@ -7,15 +7,17 @@ import numpy as np
from ale import config from ale import config
from ale.drivers.base import Framer from ale.drivers.base import Framer
from ale.drivers.distortion import RadialDistortion from ale.drivers import keys
class CassiniISS(Framer, RadialDistortion): class CassiniISS(Framer):
id_lookup = { id_lookup = {
"ISSNA" : "CASSINI_ISS_NAC", "ISSNA" : "CASSINI_ISS_NAC",
"ISSWA" : "CASSINI_ISS_WAC" "ISSWA" : "CASSINI_ISS_WAC"
} }
required_keys = keys.base | keys.framer | keys.radial_distortion
@property @property
def metakernel(self): def metakernel(self):
metakernel_dir = config.cassini metakernel_dir = config.cassini
...@@ -28,7 +30,7 @@ class CassiniISS(Framer, RadialDistortion): ...@@ -28,7 +30,7 @@ class CassiniISS(Framer, RadialDistortion):
@property @property
def instrument_id(self): def instrument_id(self):
return self.id_lookup[self._label['INSTRUMENT_ID']] return self.id_lookup[self.label['INSTRUMENT_ID']]
@property @property
def focal_epsilon(self): def focal_epsilon(self):
...@@ -52,7 +54,7 @@ class CassiniISS(Framer, RadialDistortion): ...@@ -52,7 +54,7 @@ class CassiniISS(Framer, RadialDistortion):
@property @property
def _exposure_duration(self): def _exposure_duration(self):
# labels do not specify a unit explicitly # labels do not specify a unit explicitly
return self._label['EXPOSURE_DURATION'] * 0.001 # Scale to seconds return self.label['EXPOSURE_DURATION'] * 0.001 # Scale to seconds
@property @property
def odtk(self): def odtk(self):
......
from abc import ABC
import spiceypy as spice
class TransverseDistortion(ABC):
"""
Exposes the properties that are used to describe a transverse distortion model.
"""
@property
def odtx(self):
return spice.gdpool('INS{}_OD_T_X'.format(self.ikid),0, 10)
@property
def odty(self):
return spice.gdpool('INS{}_OD_T_Y'.format(self.ikid), 0, 10)
class RadialDistortion(ABC):
"""
Exposes the properties that are used to describe a radial distortion model.
"""
@property
def odtk(self):
return spice.gdpool('INS{}_OD_K'.format(self.ikid),0, 3)
base = {
'name_model',
'center_ephemeris_time',
'detector_center',
'detector_line_summing',
'detector_sample_summing',
'ending_ephemeris_time',
'exposure_duration',
'focal2pixel_lines',
'focal2pixel_samples',
'focal_epsilon',
'focal_length',
'ikid',
'image_lines',
'image_samples',
'instrument_id',
'interpolation_method',
'reference_frame',
'reference_height',
'semimajor',
'semiminor',
'sensor_orientation',
'sensor_position',
'sensor_velocity',
'spacecraft_clock_stop_count',
'spacecraft_id',
'spacecraft_name',
'start_time',
'starting_detector_line',
'starting_detector_sample',
'starting_ephemeris_time',
'sun_position',
'sun_velocity',
'target_name',
'number_of_ephemerides',
'number_of_ephemerides'
}
filter = {
'fikid'
}
transverse_distortion = {
'odtk',
'odtx'
}
radial_distortion = {
'odty'
}
framer = {
'filter_number',
}
temp_dep_focal_legth = {
'focal_plane_tempature'
}
linescanner = {
'line_exposure_duration',
'line_scan_rate',
't0_ephemeris',
't0_quaternion',
'dt_ephemeris',
'dt_quaternion',
}
...@@ -6,10 +6,12 @@ import pvl ...@@ -6,10 +6,12 @@ import pvl
import spiceypy as spice import spiceypy as spice
from ale.util import get_metakernels from ale.util import get_metakernels
from ale.drivers.base import LineScanner from ale.drivers.base import LineScanner, Spice, PDS3, Isis3
from ale.drivers.distortion import RadialDistortion from ale.drivers import keys
class LRO_LROC(LineScanner, RadialDistortion):
class LrocSpice(Spice, LineScanner):
required_keys = keys.base | keys.linescanner
@property @property
def metakernel(self): def metakernel(self):
...@@ -17,22 +19,24 @@ class LRO_LROC(LineScanner, RadialDistortion): ...@@ -17,22 +19,24 @@ class LRO_LROC(LineScanner, RadialDistortion):
self._metakernel = metakernels['data'][0]['path'] self._metakernel = metakernels['data'][0]['path']
return self._metakernel return self._metakernel
@property
def spacecraft_name(self):
return "LRO"
class LroPds3Driver(PDS3, LrocSpice):
@property @property
def instrument_id(self): def instrument_id(self):
""" """
Ignores Wide Angle for now Ignores Wide Angle for now
""" """
instrument = self._label.get("INSTRUMENT_ID") instrument = self.label.get("INSTRUMENT_ID")
# should be left or right # should be left or right
frame_id = self._label.get("FRAME_ID") frame_id = self.label.get("FRAME_ID")
if instrument == "LROC" and frame_id == "LEFT": if instrument == "LROC" and frame_id == "LEFT":
return "LRO_LROCNACL" return "LRO_LROCNACL"
elif instrument == "LROC" and frame_id == "RIGHT": elif instrument == "LROC" and frame_id == "RIGHT":
return "LRO_LROCNACR" return "LRO_LROCNACR"
@property
def spacecraft_name(self):
return "LRO"
...@@ -6,11 +6,11 @@ import spiceypy as spice ...@@ -6,11 +6,11 @@ import spiceypy as spice
import numpy as np import numpy as np
from ale import config from ale import config
from ale.drivers.base import Framer from ale.drivers.base import Framer, Spice, PDS3, Isis3
from ale.drivers.distortion import TransverseDistortion from ale.drivers import keys
class Messenger(Framer, TransverseDistortion): class MdisSpice(Spice, Framer):
id_lookup = { id_lookup = {
'MDIS-WAC': 'MSGR_MDIS_WAC', 'MDIS-WAC': 'MSGR_MDIS_WAC',
'MDIS-NAC':'MSGR_MDIS_NAC', 'MDIS-NAC':'MSGR_MDIS_NAC',
...@@ -18,6 +18,8 @@ class Messenger(Framer, TransverseDistortion): ...@@ -18,6 +18,8 @@ class Messenger(Framer, TransverseDistortion):
'MERCURY DUAL IMAGING SYSTEM WIDE ANGLE CAMERA':'MSGR_MDIS_WAC' 'MERCURY DUAL IMAGING SYSTEM WIDE ANGLE CAMERA':'MSGR_MDIS_WAC'
} }
required_keys = keys.base | keys.framer | keys.filter | keys.transverse_distortion | keys.temp_dep_focal_legth
@property @property
def metakernel(self): def metakernel(self):
metakernel_dir = config.mdis metakernel_dir = config.mdis
...@@ -28,10 +30,6 @@ class Messenger(Framer, TransverseDistortion): ...@@ -28,10 +30,6 @@ class Messenger(Framer, TransverseDistortion):
self._metakernel = mk self._metakernel = mk
return self._metakernel return self._metakernel
@property
def instrument_id(self):
return self.id_lookup[self._label['INSTRUMENT_ID']]
@property @property
def focal_length(self): def focal_length(self):
""" """
...@@ -43,11 +41,7 @@ class Messenger(Framer, TransverseDistortion): ...@@ -43,11 +41,7 @@ class Messenger(Framer, TransverseDistortion):
f_t = np.poly1d(coeffs[::-1]) f_t = np.poly1d(coeffs[::-1])
# eval at the focal_plane_tempature # eval at the focal_plane_tempature
return f_t(self._label['FOCAL_PLANE_TEMPERATURE'].value) return f_t(self.focal_plane_tempature)
@property
def focal_epsilon(self):
return float(spice.gdpool('INS{}_FL_UNCERTAINTY'.format(self.ikid), 0, 1)[0])
@property @property
def starting_detector_sample(self): def starting_detector_sample(self):
...@@ -57,14 +51,14 @@ class Messenger(Framer, TransverseDistortion): ...@@ -57,14 +51,14 @@ class Messenger(Framer, TransverseDistortion):
def starting_detector_line(self): def starting_detector_line(self):
return int(spice.gdpool('INS{}_FPUBIN_START_LINE'.format(self.ikid), 0, 1)[0]) return int(spice.gdpool('INS{}_FPUBIN_START_LINE'.format(self.ikid), 0, 1)[0])
class isisMessenger(Framer, TransverseDistortion):
id_lookup = {
'MDIS-WAC': 'MSGR_MDIS_WAC',
'MDIS-NAC':'MSGR_MDIS_NAC',
'MERCURY DUAL IMAGING SYSTEM NARROW ANGLE CAMERA':'MSGR_MDIS_NAC',
'MERCURY DUAL IMAGING SYSTEM WIDE ANGLE CAMERA':'MSGR_MDIS_WAC'
}
class MdisPDS3Driver(PDS3, MdisSpice):
@property
def instrument_id(self):
return self.id_lookup[self.label['INSTRUMENT_ID']]
class MdisIsis3Driver(Isis3, MdisSpice):
@property @property
def metakernel(self): def metakernel(self):
metakernel_dir = config.mdis metakernel_dir = config.mdis
...@@ -77,60 +71,8 @@ class isisMessenger(Framer, TransverseDistortion): ...@@ -77,60 +71,8 @@ class isisMessenger(Framer, TransverseDistortion):
@property @property
def instrument_id(self): def instrument_id(self):
return self.id_lookup[self._label['IsisCube']['Instrument']['InstrumentId']] return self.id_lookup[self.label['IsisCube']['Instrument']['InstrumentId']]
@property
def focal_length(self):
"""
"""
coeffs = spice.gdpool('INS{}_FL_TEMP_COEFFS '.format(self.fikid), 0, 5)
# reverse coeffs, mdis coeffs are listed a_0, a_1, a_2 ... a_n where
# numpy wants them a_n, a_n-1, a_n-2 ... a_0
f_t = np.poly1d(coeffs[::-1])
# eval at the focal_plane_tempature
return f_t(self._label['IsisCube']['Instrument']['FocalPlaneTemperature'].value)
@property
def focal_epsilon(self):
return float(spice.gdpool('INS{}_FL_UNCERTAINTY'.format(self.ikid), 0, 1)[0])
@property
def starting_detector_sample(self):
return int(spice.gdpool('INS{}_FPUBIN_START_SAMPLE'.format(self.ikid), 0, 1)[0])
@property
def starting_detector_line(self):
return int(spice.gdpool('INS{}_FPUBIN_START_LINE'.format(self.ikid), 0, 1)[0])
@property
def start_time(self):
return self._label['IsisCube']['Instrument']['StartTime']
@property
def spacecraft_name(self):
return self._label['IsisCube']['Instrument']['SpacecraftName']
@property
def image_lines(self):
return self._label['IsisCube']['Core']['Dimensions']['Lines']
@property
def image_samples(self):
return self._label['IsisCube']['Core']['Dimensions']['Samples']
@property
def _exposure_duration(self):
return self._label['IsisCube']['Instrument']['ExposureDuration'].value * 0.001 # Scale to seconds
@property
def target_name(self):
return self._label['IsisCube']['Instrument']['TargetName']
@property @property
def starting_ephemeris_time(self): def focal_plane_tempature(self):
if not hasattr(self, '_starting_ephemeris_time'): return self.label['IsisCube']['Instrument']['FocalPlaneTemperature'].value
sclock = self._label['IsisCube']['Archive']['SpacecraftClockStartCount']
self._starting_ephemeris_time = spice.scs2e(self.spacecraft_id, sclock)
return self._starting_ephemeris_time
...@@ -6,14 +6,17 @@ import pvl ...@@ -6,14 +6,17 @@ import pvl
import spiceypy as spice import spiceypy as spice
from ale import config from ale import config
from ale.drivers.base import LineScanner from ale.drivers.base import LineScanner, Spice, PDS3, Isis3
from ale.drivers.distortion import RadialDistortion from ale.drivers import keys
class MRO_CTX(LineScanner, RadialDistortion):
class CtxSpice(Spice, LineScanner):
id_lookup = { id_lookup = {
'CONTEXT CAMERA':'MRO_CTX' 'CONTEXT CAMERA':'MRO_CTX'
} }
required_keys = keys.base | keys.linescanner | keys.radial_distortion
@property @property
def metakernel(self): def metakernel(self):
metakernel_dir = config.mro metakernel_dir = config.mro
...@@ -25,13 +28,19 @@ class MRO_CTX(LineScanner, RadialDistortion): ...@@ -25,13 +28,19 @@ class MRO_CTX(LineScanner, RadialDistortion):
self._metakernel = mk self._metakernel = mk
return self._metakernel return self._metakernel
class CtxPds3Driver(PDS3, CtxSpice):
@property @property
def instrument_id(self): def instrument_id(self):
return self.id_lookup[self._label['INSTRUMENT_NAME']] return self.id_lookup[self.label['INSTRUMENT_NAME']]
@property @property
def spacecraft_name(self): def spacecraft_name(self):
name_lookup = { name_lookup = {
'MARS_RECONNAISSANCE_ORBITER': 'MRO' 'MARS_RECONNAISSANCE_ORBITER': 'MRO'
} }
return name_lookup[self._label['SPACECRAFT_NAME']] return name_lookup[self.label['SPACECRAFT_NAME']]
@property
def instrument_id(self):
return self.id_lookup[self.label['INSTRUMENT_NAME']]
...@@ -4,8 +4,8 @@ from unittest import mock ...@@ -4,8 +4,8 @@ from unittest import mock
import pytest import pytest
import ale import ale
from ale.drivers import lro_driver, base, distortion from ale.drivers import lro_driver, base
from ale.drivers.lro_driver import LRO_LROC from ale.drivers.lro_driver import LroPds3Driver
from ale import util from ale import util
# 'Mock' the spice module where it is imported # 'Mock' the spice module where it is imported
...@@ -14,9 +14,8 @@ from conftest import SimpleSpice, get_mockkernels ...@@ -14,9 +14,8 @@ from conftest import SimpleSpice, get_mockkernels
simplespice = SimpleSpice() simplespice = SimpleSpice()
base.spice = simplespice base.spice = simplespice
lro_driver.spice = simplespice lro_driver.spice = simplespice
distortion.spice = simplespice
LRO_LROC.metakernel = get_mockkernels LroPds3Driver.metakernel = get_mockkernels
@pytest.fixture @pytest.fixture
def lro_lroclabel(): def lro_lroclabel():
...@@ -113,6 +112,7 @@ def lro_lroclabel(): ...@@ -113,6 +112,7 @@ def lro_lroclabel():
""" """
def test_lro_creation(lro_lroclabel): def test_lro_creation(lro_lroclabel):
with LRO_LROC(lro_lroclabel) as m: with LroPds3Driver(lro_lroclabel) as m:
d = m.to_dict() d = m.to_dict()
assert isinstance(d, dict) assert isinstance(d, dict)
assert(set(d.keys()) == m.required_keys)
...@@ -4,8 +4,8 @@ from unittest import mock ...@@ -4,8 +4,8 @@ from unittest import mock
import pytest import pytest
import ale import ale
from ale.drivers import mdis_driver, base, distortion from ale.drivers import mdis_driver, base
from ale.drivers.mdis_driver import Messenger from ale.drivers.mdis_driver import MdisPDS3Driver
# 'Mock' the spice module where it is imported # 'Mock' the spice module where it is imported
...@@ -14,9 +14,8 @@ from conftest import SimpleSpice, get_mockkernels ...@@ -14,9 +14,8 @@ from conftest import SimpleSpice, get_mockkernels
simplespice = SimpleSpice() simplespice = SimpleSpice()
base.spice = simplespice base.spice = simplespice
mdis_driver.spice = simplespice mdis_driver.spice = simplespice
distortion.spice = simplespice
Messenger.metakernel = get_mockkernels MdisPDS3Driver.metakernel = get_mockkernels
@pytest.fixture @pytest.fixture
def mdislabel(): def mdislabel():
...@@ -245,6 +244,7 @@ END ...@@ -245,6 +244,7 @@ END
""" """
def test_mdis_creation(mdislabel): def test_mdis_creation(mdislabel):
with Messenger(mdislabel) as m: with MdisPDS3Driver(mdislabel) as m:
d = m.to_dict() d = m.to_dict()
assert isinstance(d, dict) assert isinstance(d, dict)
assert(set(d.keys()) == m.required_keys)
import pytest import pytest
import ale import ale
from ale.drivers.mro_driver import MRO_CTX from ale.drivers.mro_driver import CtxPds3Driver
from ale.drivers import mro_driver, base, distortion from ale.drivers import mro_driver, base
# 'Mock' the spice module where it is imported # 'Mock' the spice module where it is imported
from conftest import SimpleSpice, get_mockkernels from conftest import SimpleSpice, get_mockkernels
...@@ -10,9 +10,8 @@ from conftest import SimpleSpice, get_mockkernels ...@@ -10,9 +10,8 @@ from conftest import SimpleSpice, get_mockkernels
simplespice = SimpleSpice() simplespice = SimpleSpice()
base.spice = simplespice base.spice = simplespice
mro_driver.spice = simplespice mro_driver.spice = simplespice
distortion.spice = simplespice
MRO_CTX.metakernel = get_mockkernels CtxPds3Driver.metakernel = get_mockkernels
@pytest.fixture @pytest.fixture
def mroctx_label(): def mroctx_label():
...@@ -63,7 +62,8 @@ def mroctx_label(): ...@@ -63,7 +62,8 @@ def mroctx_label():
END""" END"""
def test_get_dict(mroctx_label): def test_ctx_creation(mroctx_label):
with MRO_CTX(mroctx_label) as m: with CtxPds3Driver(mroctx_label) as m:
d = m.to_dict() d = m.to_dict()
assert isinstance(d, dict) assert isinstance(d, dict)
assert(set(d.keys()) == m.required_keys)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment