diff --git a/ale/drivers/base.py b/ale/drivers/base.py index 039f0ac9cb73b1446d00c67fdc36ffee7ece8509..050ed24852cb85894f4c9ae2538caa5decbf8cb3 100644 --- a/ale/drivers/base.py +++ b/ale/drivers/base.py @@ -1,42 +1,15 @@ -from abc import ABC, abstractmethod - from dateutil import parser import numpy as np import pvl import spiceypy as spice -from ale.drivers import distortion - -class Base(ABC): +class Driver(): """ - Abstract base class for all PDS label parsing. Implementations should override - properties where a kernel provider deviates from the most broadly adopted - approach. - - Methods that must be provided: - - instrument_id - - metakernel + Base class for all Drivers. """ - def __init__(self, label, *args, **kwargs): - self._label = pvl.loads(label, strict=False) - - 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 __init__(self, file): + self._file = file def __str__(self): return str(self.to_dict()) @@ -49,51 +22,71 @@ class Base(ABC): return False 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() - # 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 + return [[float(self.starting_detector_line), self.t0_ephemeris, self.line_exposure_duration]] - if isinstance(self, distortion.RadialDistortion): - data['optical_distortion'] = {'radial':{'coefficients':data['odtk']}} - elif isinstance(self, distortion.TransverseDistortion): - data['optical_distortion'] = {'transverse':{'x':data['odtx'], - 'y':data['odty']}} + @property + def number_of_ephemerides(self): + #TODO: Not make this hardcoded + return 909 - data['focal_length_model'] = {'focal_length': data['focal_length']} - if hasattr(self, 'focal_epsilon'): - data['focal_length_model']['focal_epsilon'] = data['focal_epsilon'] + @property + def number_of_quaternions(self): + #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', - 'velocities': data['sensor_velocity'], - 'positions': data['sensor_position']} +class Framer(Driver): + @property + def name_model(self): + return "USGS_ASTRO_FRAME_SENSOR_MODEL" - data['sun_position'] = {'unit': 'm', - 'positions': data['sun_position'], - 'velocities': data['sun_velocity']} + @property + def filter_number(self): + 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'], - 'semiminor':data['semiminor'], - 'unit': 'km'} + @property + def number_of_quaternions(self): + # 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): """ Helper function to pull position and velocity in one pass @@ -118,74 +111,91 @@ class Base(ABC): self._sensor_velocity = eph_rates self._sensor_position = eph + @property + def focal_plane_tempature(self): + return self.label['FOCAL_PLANE_TEMPERATURE'].value @property - @abstractmethod - def metakernel(self): - pass + def label(self): + 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 + def line_exposure_duration(self): + try: + return self.label['LINE_EXPOSURE_DURATION'].value * 0.001 # Scale to seconds + except: + return np.nan @property - @abstractmethod def instrument_id(self): pass @property def start_time(self): - return self._label['START_TIME'] + return self.label['START_TIME'] @property def image_lines(self): - return self._label['IMAGE']['LINES'] + return self.label['IMAGE']['LINES'] @property def image_samples(self): - return self._label['IMAGE']['LINE_SAMPLES'] + return self.label['IMAGE']['LINE_SAMPLES'] @property def interpolation_method(self): return 'lagrange' - @property - def number_of_ephemerides(self): - return 1 - @property def target_name(self): - return self._label['TARGET_NAME'] + return self.label['TARGET_NAME'] @property 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 def starting_ephemeris_time(self): 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) return self._starting_ephemeris_time @property - def _exposure_duration(self): - return self._label['EXPOSURE_DURATION'].value * 0.001 # Scale to seconds + def exposure_duration(self): + try: + return self.label['EXPOSURE_DURATION'].value * 0.001 # Scale to seconds + except: + return np.nan @property 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': sc = None return sc @property def ending_ephemeris_time(self): - if not hasattr(self, '_ending_ephemeris_time'): - self._ending_ephemeris_time = (self.image_lines * self._exposure_duration) + self.starting_ephemeris_time - return self._ending_ephemeris_time + return (self.image_lines * self.line_exposure_duration) + self.starting_ephemeris_time @property def center_ephemeris_time(self): - if not hasattr(self, '_center_ephemeris_time'): - self._center_ephemeris_time = (self.starting_ephemeris_time + self.ending_ephemeris_time)/2 - return self._center_ephemeris_time + return (self.starting_ephemeris_time + self.ending_ephemeris_time)/2 + @property def detector_center(self): @@ -193,50 +203,78 @@ class Base(ABC): @property def spacecraft_name(self): - return self._label['MISSION_NAME'] + return self.label['MISSION_NAME'] @property - def ikid(self): - return spice.bods2c(self.instrument_id) + def starting_detector_line(self): + return 1 @property - def fikid(self): - fn = self._label.get('FILTER_NUMBER', 0) - if fn == 'N/A': - fn = 0 - return self.ikid - int(fn) + def starting_detector_sample(self): + return 1 @property - def spacecraft_id(self): - return spice.bods2c(self.spacecraft_name) + def detector_sample_summing(self): + return 1 @property - def focal2pixel_lines(self): - return spice.gdpool('INS{}_ITRANSL'.format(self.fikid), 0, 3) + def detector_line_summing(self): + return self.label.get('SAMPLING_FACTOR', 1) + +class Spice(): @property - def focal2pixel_samples(self): - return spice.gdpool('INS{}_ITRANSS'.format(self.fikid), 0, 3) + def metakernel(self): + 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 - def focal_length(self): - return float(spice.gdpool('INS{}_FOCAL_LENGTH'.format(self.ikid), 0, 1)[0]) + def odtx(self): + return spice.gdpool('INS{}_OD_T_X'.format(self.ikid),0, 10) @property - def starting_detector_line(self): - return 1 + def odty(self): + return spice.gdpool('INS{}_OD_T_Y'.format(self.ikid), 0, 10) @property - def starting_detector_sample(self): - return 1 + def odtk(self): + return spice.gdpool('INS{}_OD_K'.format(self.ikid),0, 3) @property - def detector_sample_summing(self): - return 1 + def ikid(self): + return spice.bods2c(self.instrument_id) @property - def detector_line_summing(self): - return self._label.get('SAMPLING_FACTOR', 1) + def spacecraft_id(self): + 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 def semimajor(self): @@ -306,24 +344,6 @@ class Base(ABC): # TODO: This should be a reasonable # 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 def detector_center(self): if not hasattr(self, '_detector_center'): @@ -339,50 +359,63 @@ class LineScanner(Base): """ if not hasattr(self, '_center_ephemeris_time'): 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 return self._center_ephemeris_time @property - def t0_ephemeris(self): - return self.starting_ephemeris_time - self.center_ephemeris_time + def fikid(self): + if isinstance(self, Framer): + fn = self.filter_number + if fn == 'N/A': + fn = 0 + else: + fn = 0 - @property - def t0_quaternion(self): - return self.starting_ephemeris_time - self.center_ephemeris_time + return self.ikid - int(fn) - @property - def dt_ephemeris(self): - return (self.ending_ephemeris_time - self.starting_ephemeris_time) / self.number_of_ephemerides +class Isis3(): @property - def dt_quaternion(self): - return (self.ending_ephemeris_time - self.starting_ephemeris_time) / self.number_of_ephemerides + def label(self): + 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 - def number_of_ephemerides(self): - return 909 + def start_time(self): + return self.label['IsisCube']['Instrument']['StartTime'] @property - def number_of_quaternions(self): - return 909 - - + def spacecraft_name(self): + return self.label['IsisCube']['Instrument']['SpacecraftName'] @property - def _exposure_duration(self): - return self._label['LINE_EXPOSURE_DURATION'].value * 0.001 # Scale to seconds + def image_lines(self): + return self.label['IsisCube']['Core']['Dimensions']['Lines'] -class Framer(Base): + @property + def image_samples(self): + return self.label['IsisCube']['Core']['Dimensions']['Samples'] @property - def name_model(self): - return "USGS_ASTRO_FRAME_SENSOR_MODEL" + def _exposure_duration(self): + return self.label['IsisCube']['Instrument']['ExposureDuration'].value * 0.001 # Scale to seconds @property - def number_of_ephemerides(self): - return 1 + def target_name(self): + return self.label['IsisCube']['Instrument']['TargetName'] @property - def number_of_quaternions(self): - return 1 + def starting_ephemeris_time(self): + 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 diff --git a/ale/drivers/cassini_driver.py b/ale/drivers/cassini_driver.py index e28822c8642447e3e3965568bd56871c874ca6ec..ebbb670fb19a8ebd17cecbda0253b34789cfeb19 100644 --- a/ale/drivers/cassini_driver.py +++ b/ale/drivers/cassini_driver.py @@ -7,15 +7,17 @@ import numpy as np from ale import config 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 = { "ISSNA" : "CASSINI_ISS_NAC", "ISSWA" : "CASSINI_ISS_WAC" } + required_keys = keys.base | keys.framer | keys.radial_distortion + @property def metakernel(self): metakernel_dir = config.cassini @@ -28,7 +30,7 @@ class CassiniISS(Framer, RadialDistortion): @property def instrument_id(self): - return self.id_lookup[self._label['INSTRUMENT_ID']] + return self.id_lookup[self.label['INSTRUMENT_ID']] @property def focal_epsilon(self): @@ -52,7 +54,7 @@ class CassiniISS(Framer, RadialDistortion): @property def _exposure_duration(self): # 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 def odtk(self): diff --git a/ale/drivers/distortion.py b/ale/drivers/distortion.py deleted file mode 100644 index 3a5bac4d443af3750f03d3b8c7120d6e572d799f..0000000000000000000000000000000000000000 --- a/ale/drivers/distortion.py +++ /dev/null @@ -1,23 +0,0 @@ -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) diff --git a/ale/drivers/keys.py b/ale/drivers/keys.py new file mode 100644 index 0000000000000000000000000000000000000000..94cf987c7e744dcb3362fc59015155a94f8a0165 --- /dev/null +++ b/ale/drivers/keys.py @@ -0,0 +1,67 @@ +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', +} diff --git a/ale/drivers/lro_driver.py b/ale/drivers/lro_driver.py index c912497e3f7842047a762dea344d945d4f57e4ed..5654f672f993a8779a17326cf8af1713b90fc8a9 100644 --- a/ale/drivers/lro_driver.py +++ b/ale/drivers/lro_driver.py @@ -6,10 +6,12 @@ import pvl import spiceypy as spice from ale.util import get_metakernels -from ale.drivers.base import LineScanner -from ale.drivers.distortion import RadialDistortion +from ale.drivers.base import LineScanner, Spice, PDS3, Isis3 +from ale.drivers import keys -class LRO_LROC(LineScanner, RadialDistortion): + +class LrocSpice(Spice, LineScanner): + required_keys = keys.base | keys.linescanner @property def metakernel(self): @@ -17,22 +19,24 @@ class LRO_LROC(LineScanner, RadialDistortion): self._metakernel = metakernels['data'][0]['path'] return self._metakernel + @property + def spacecraft_name(self): + return "LRO" + + +class LroPds3Driver(PDS3, LrocSpice): @property def instrument_id(self): """ Ignores Wide Angle for now """ - instrument = self._label.get("INSTRUMENT_ID") + instrument = self.label.get("INSTRUMENT_ID") # 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": return "LRO_LROCNACL" elif instrument == "LROC" and frame_id == "RIGHT": return "LRO_LROCNACR" - - @property - def spacecraft_name(self): - return "LRO" diff --git a/ale/drivers/mdis_driver.py b/ale/drivers/mdis_driver.py index f5aebfe5e4e4417dc3e8b6c7e21aac5c2b5d3124..89770feb9eb99448e6acb01d409a3daa8e3f70d5 100644 --- a/ale/drivers/mdis_driver.py +++ b/ale/drivers/mdis_driver.py @@ -6,11 +6,11 @@ import spiceypy as spice import numpy as np from ale import config -from ale.drivers.base import Framer -from ale.drivers.distortion import TransverseDistortion +from ale.drivers.base import Framer, Spice, PDS3, Isis3 +from ale.drivers import keys -class Messenger(Framer, TransverseDistortion): +class MdisSpice(Spice, Framer): id_lookup = { 'MDIS-WAC': 'MSGR_MDIS_WAC', 'MDIS-NAC':'MSGR_MDIS_NAC', @@ -18,6 +18,8 @@ class Messenger(Framer, TransverseDistortion): '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 def metakernel(self): metakernel_dir = config.mdis @@ -28,10 +30,6 @@ class Messenger(Framer, TransverseDistortion): self._metakernel = mk return self._metakernel - @property - def instrument_id(self): - return self.id_lookup[self._label['INSTRUMENT_ID']] - @property def focal_length(self): """ @@ -43,11 +41,7 @@ class Messenger(Framer, TransverseDistortion): f_t = np.poly1d(coeffs[::-1]) # eval at the focal_plane_tempature - return f_t(self._label['FOCAL_PLANE_TEMPERATURE'].value) - - @property - def focal_epsilon(self): - return float(spice.gdpool('INS{}_FL_UNCERTAINTY'.format(self.ikid), 0, 1)[0]) + return f_t(self.focal_plane_tempature) @property def starting_detector_sample(self): @@ -57,14 +51,14 @@ class Messenger(Framer, TransverseDistortion): def starting_detector_line(self): 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 def metakernel(self): metakernel_dir = config.mdis @@ -77,60 +71,8 @@ class isisMessenger(Framer, TransverseDistortion): @property def instrument_id(self): - 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) + return self.id_lookup[self.label['IsisCube']['Instrument']['InstrumentId']] - # 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 - def starting_ephemeris_time(self): - 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 + def focal_plane_tempature(self): + return self.label['IsisCube']['Instrument']['FocalPlaneTemperature'].value diff --git a/ale/drivers/mro_driver.py b/ale/drivers/mro_driver.py index 20b28ab7c619b7b42f540f43e00f3144eeae40d8..870d3eca1d40398e2de673a972edbb7e78a2ade0 100644 --- a/ale/drivers/mro_driver.py +++ b/ale/drivers/mro_driver.py @@ -6,14 +6,17 @@ import pvl import spiceypy as spice from ale import config -from ale.drivers.base import LineScanner -from ale.drivers.distortion import RadialDistortion +from ale.drivers.base import LineScanner, Spice, PDS3, Isis3 +from ale.drivers import keys -class MRO_CTX(LineScanner, RadialDistortion): + +class CtxSpice(Spice, LineScanner): id_lookup = { 'CONTEXT CAMERA':'MRO_CTX' } + required_keys = keys.base | keys.linescanner | keys.radial_distortion + @property def metakernel(self): metakernel_dir = config.mro @@ -25,13 +28,19 @@ class MRO_CTX(LineScanner, RadialDistortion): self._metakernel = mk return self._metakernel + +class CtxPds3Driver(PDS3, CtxSpice): @property def instrument_id(self): - return self.id_lookup[self._label['INSTRUMENT_NAME']] + return self.id_lookup[self.label['INSTRUMENT_NAME']] @property def spacecraft_name(self): name_lookup = { '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']] diff --git a/tests/pytests/test_lro_drivers.py b/tests/pytests/test_lro_drivers.py index 2e0bd6dae3c09d148edc39a96b93679406378825..05234b7d06ab00892a2707a55f1ddd6fd5faa469 100644 --- a/tests/pytests/test_lro_drivers.py +++ b/tests/pytests/test_lro_drivers.py @@ -4,8 +4,8 @@ from unittest import mock import pytest import ale -from ale.drivers import lro_driver, base, distortion -from ale.drivers.lro_driver import LRO_LROC +from ale.drivers import lro_driver, base +from ale.drivers.lro_driver import LroPds3Driver from ale import util # 'Mock' the spice module where it is imported @@ -14,9 +14,8 @@ from conftest import SimpleSpice, get_mockkernels simplespice = SimpleSpice() base.spice = simplespice lro_driver.spice = simplespice -distortion.spice = simplespice -LRO_LROC.metakernel = get_mockkernels +LroPds3Driver.metakernel = get_mockkernels @pytest.fixture def lro_lroclabel(): @@ -113,6 +112,7 @@ def 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() assert isinstance(d, dict) + assert(set(d.keys()) == m.required_keys) diff --git a/tests/pytests/test_mdis_driver.py b/tests/pytests/test_mdis_driver.py index 1084934187cca9d42fe3e69bd1d02bac3c1541fb..a290173041896bc4e9902652d102f8c5bb14ecc0 100644 --- a/tests/pytests/test_mdis_driver.py +++ b/tests/pytests/test_mdis_driver.py @@ -4,8 +4,8 @@ from unittest import mock import pytest import ale -from ale.drivers import mdis_driver, base, distortion -from ale.drivers.mdis_driver import Messenger +from ale.drivers import mdis_driver, base +from ale.drivers.mdis_driver import MdisPDS3Driver # 'Mock' the spice module where it is imported @@ -14,9 +14,8 @@ from conftest import SimpleSpice, get_mockkernels simplespice = SimpleSpice() base.spice = simplespice mdis_driver.spice = simplespice -distortion.spice = simplespice -Messenger.metakernel = get_mockkernels +MdisPDS3Driver.metakernel = get_mockkernels @pytest.fixture def mdislabel(): @@ -245,6 +244,7 @@ END """ def test_mdis_creation(mdislabel): - with Messenger(mdislabel) as m: + with MdisPDS3Driver(mdislabel) as m: d = m.to_dict() assert isinstance(d, dict) + assert(set(d.keys()) == m.required_keys) diff --git a/tests/pytests/test_mro_drivers.py b/tests/pytests/test_mro_drivers.py index 00b48f0d8a5616a372ce6c44b324cef6833f77a0..a18b153593d260e6faa40471bea43b5c19892106 100644 --- a/tests/pytests/test_mro_drivers.py +++ b/tests/pytests/test_mro_drivers.py @@ -1,8 +1,8 @@ import pytest import ale -from ale.drivers.mro_driver import MRO_CTX -from ale.drivers import mro_driver, base, distortion +from ale.drivers.mro_driver import CtxPds3Driver +from ale.drivers import mro_driver, base # 'Mock' the spice module where it is imported from conftest import SimpleSpice, get_mockkernels @@ -10,9 +10,8 @@ from conftest import SimpleSpice, get_mockkernels simplespice = SimpleSpice() base.spice = simplespice mro_driver.spice = simplespice -distortion.spice = simplespice -MRO_CTX.metakernel = get_mockkernels +CtxPds3Driver.metakernel = get_mockkernels @pytest.fixture def mroctx_label(): @@ -63,7 +62,8 @@ def mroctx_label(): END""" -def test_get_dict(mroctx_label): - with MRO_CTX(mroctx_label) as m: +def test_ctx_creation(mroctx_label): + with CtxPds3Driver(mroctx_label) as m: d = m.to_dict() assert isinstance(d, dict) + assert(set(d.keys()) == m.required_keys)