Skip to content
Snippets Groups Projects
Commit a3e760f5 authored by Kelvin Rodriguez's avatar Kelvin Rodriguez
Browse files

First draft of new driver layout

parent 5645ee2a
No related branches found
No related tags found
No related merge requests found
...@@ -5,8 +5,6 @@ import numpy as np ...@@ -5,8 +5,6 @@ import numpy as np
import pvl import pvl
import spiceypy as spice import spiceypy as spice
from ale.drivers import distortion
class Base(ABC): class Base(ABC):
""" """
Abstract base class for all PDS label parsing. Implementations should override Abstract base class for all PDS label parsing. Implementations should override
...@@ -19,7 +17,12 @@ class Base(ABC): ...@@ -19,7 +17,12 @@ class Base(ABC):
""" """
def __init__(self, label, *args, **kwargs): def __init__(self, label, *args, **kwargs):
if isinstance(label, str):
self._label = pvl.loads(label, strict=False) self._label = pvl.loads(label, strict=False)
elif isinstance(label, pvl.PVLModule):
self._label = label
else:
self._label = pvl.load(label, strict=False)
def __enter__(self): def __enter__(self):
""" """
...@@ -95,6 +98,60 @@ class Base(ABC): ...@@ -95,6 +98,60 @@ class Base(ABC):
data['name_sensor'] = data['instrument_id'] data['name_sensor'] = data['instrument_id']
return data return data
@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
class TransverseDistortion(ABC):
"""
Exposes the properties that are used to describe a transverse distortion model.
"""
@property
@abstractmethod
def odtx(self):
pass
@property
@abstractmethod
def odty(self):
pass
class RadialDistortion(ABC):
"""
Exposes the properties that are used to describe a radial distortion model.
"""
@property
@abstractmethod
def odtk(self):
pass
class LineScanner(Base):
@property
def name_model(self):
return "USGS_ASTRO_LINE_SCANNER_SENSOR_MODEL"
class Framer(Base):
@property
def name_model(self):
return "USGS_ASTRO_FRAME_SENSOR_MODEL"
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
...@@ -121,12 +178,13 @@ class Base(ABC): ...@@ -121,12 +178,13 @@ class Base(ABC):
@property @property
@abstractmethod def line_exposure_duration(self):
def metakernel(self): try:
pass 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
...@@ -166,8 +224,19 @@ class Base(ABC): ...@@ -166,8 +224,19 @@ class Base(ABC):
return self._starting_ephemeris_time return self._starting_ephemeris_time
@property @property
def _exposure_duration(self): def exposure_duration(self):
try:
return self._label['EXPOSURE_DURATION'].value * 0.001 # Scale to seconds return self._label['EXPOSURE_DURATION'].value * 0.001 # Scale to seconds
except:
return np.nan
@property
def number_of_ephemerides(self):
return 909
@property
def number_of_quaternions(self):
return 909
@property @property
def spacecraft_clock_stop_count(self): def spacecraft_clock_stop_count(self):
...@@ -179,7 +248,7 @@ class Base(ABC): ...@@ -179,7 +248,7 @@ class Base(ABC):
@property @property
def ending_ephemeris_time(self): def ending_ephemeris_time(self):
if not hasattr(self, '_ending_ephemeris_time'): if not hasattr(self, '_ending_ephemeris_time'):
self._ending_ephemeris_time = (self.image_lines * self._exposure_duration) + self.starting_ephemeris_time self._ending_ephemeris_time = (self.image_lines * self.line_exposure_duration) + self.starting_ephemeris_time
return self._ending_ephemeris_time return self._ending_ephemeris_time
@property @property
...@@ -196,13 +265,51 @@ class Base(ABC): ...@@ -196,13 +265,51 @@ class Base(ABC):
def spacecraft_name(self): def spacecraft_name(self):
return self._label['MISSION_NAME'] return self._label['MISSION_NAME']
@property
def starting_detector_line(self):
return 1
@property
def starting_detector_sample(self):
return 1
@property
def detector_sample_summing(self):
return 1
@property
def detector_line_summing(self):
return self._label.get('SAMPLING_FACTOR', 1)
@property
def filter_number(self):
return self._label.get('FILTER_NUMBER', 0)
class Spice():
@property
def metakernel(self):
pass
@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)
@property
def odtk(self):
return spice.gdpool('INS{}_OD_K'.format(self.ikid),0, 3)
@property @property
def ikid(self): def ikid(self):
return spice.bods2c(self.instrument_id) return spice.bods2c(self.instrument_id)
@property @property
def fikid(self): def fikid(self):
fn = self._label.get('FILTER_NUMBER', 0) fn = self.filter_number
if fn == 'N/A': if fn == 'N/A':
fn = 0 fn = 0
return self.ikid - int(fn) return self.ikid - int(fn)
...@@ -223,35 +330,19 @@ class Base(ABC): ...@@ -223,35 +330,19 @@ class Base(ABC):
def focal_length(self): def focal_length(self):
return float(spice.gdpool('INS{}_FOCAL_LENGTH'.format(self.ikid), 0, 1)[0]) return float(spice.gdpool('INS{}_FOCAL_LENGTH'.format(self.ikid), 0, 1)[0])
@property
def starting_detector_line(self):
return 1
@property
def starting_detector_sample(self):
return 1
@property
def detector_sample_summing(self):
return 1
@property
def detector_line_summing(self):
return self._label.get('SAMPLING_FACTOR', 1)
@property @property
def semimajor(self): def semimajor(self):
rad = spice.bodvrd(self._label['TARGET_NAME'], 'RADII', 3) rad = spice.bodvrd(self.target_name, 'RADII', 3)
return rad[1][1] return rad[1][1]
@property @property
def semiminor(self): def semiminor(self):
rad = spice.bodvrd(self._label['TARGET_NAME'], 'RADII', 3) rad = spice.bodvrd(self.target_name, 'RADII', 3)
return rad[1][0] return rad[1][0]
@property @property
def reference_frame(self): def reference_frame(self):
return 'IAU_{}'.format(self._label['TARGET_NAME']) return 'IAU_{}'.format(self.target_name)
@property @property
def sun_position(self): def sun_position(self):
...@@ -259,7 +350,7 @@ class Base(ABC): ...@@ -259,7 +350,7 @@ class Base(ABC):
self.center_ephemeris_time, self.center_ephemeris_time,
self.reference_frame, self.reference_frame,
'NONE', 'NONE',
self._label['TARGET_NAME']) self.target_name)
return [sun_state[:4].tolist()] return [sun_state[:4].tolist()]
...@@ -269,7 +360,7 @@ class Base(ABC): ...@@ -269,7 +360,7 @@ class Base(ABC):
self.center_ephemeris_time, self.center_ephemeris_time,
self.reference_frame, self.reference_frame,
'NONE', 'NONE',
self._label['TARGET_NAME']) self.target_name)
return [sun_state[3:6].tolist()] return [sun_state[3:6].tolist()]
...@@ -307,23 +398,13 @@ class Base(ABC): ...@@ -307,23 +398,13 @@ 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 @property
def line_scan_rate(self): def line_scan_rate(self):
""" """
In the form: [start_line, line_time, exposure_duration] In the form: [start_line, line_time, exposure_duration]
The form below is for a fixed rate line scanner. The form below is for a fixed rate line scanner.
""" """
return [[float(self.starting_detector_line), self.t0_ephemeris, self._exposure_duration]] return [[float(self.starting_detector_line), self.t0_ephemeris, self.line_exposure_duration]]
@property @property
def detector_center(self): def detector_center(self):
...@@ -340,50 +421,39 @@ class LineScanner(Base): ...@@ -340,50 +421,39 @@ 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
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
class Isis3():
@property @property
def dt_ephemeris(self): def start_time(self):
return (self.ending_ephemeris_time - self.starting_ephemeris_time) / self.number_of_ephemerides return self._label['IsisCube']['Instrument']['StartTime']
@property @property
def dt_quaternion(self): def spacecraft_name(self):
return (self.ending_ephemeris_time - self.starting_ephemeris_time) / self.number_of_ephemerides return self._label['IsisCube']['Instrument']['SpacecraftName']
@property @property
def number_of_ephemerides(self): def image_lines(self):
return 909 return self._label['IsisCube']['Core']['Dimensions']['Lines']
@property @property
def number_of_quaternions(self): def image_samples(self):
return 909 return self._label['IsisCube']['Core']['Dimensions']['Samples']
@property @property
def _exposure_duration(self): def _exposure_duration(self):
return self._label['LINE_EXPOSURE_DURATION'].value * 0.001 # Scale to seconds return self._label['IsisCube']['Instrument']['ExposureDuration'].value * 0.001 # Scale to seconds
class Framer(Base):
@property
def name_model(self):
return "USGS_ASTRO_FRAME_SENSOR_MODEL"
@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
...@@ -6,8 +6,7 @@ import spiceypy as spice ...@@ -6,8 +6,7 @@ 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, RadialDistortion
from ale.drivers.distortion import RadialDistortion
class CassiniISS(Framer, RadialDistortion): class CassiniISS(Framer, RadialDistortion):
......
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)
...@@ -6,10 +6,9 @@ import pvl ...@@ -6,10 +6,9 @@ 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, RadialDistortion, Spice, PDS3, Isis3
from ale.drivers.distortion import RadialDistortion
class LRO_LROC(LineScanner, RadialDistortion): class LrocSpice(Spice, LineScanner, RadialDistortion):
@property @property
def metakernel(self): def metakernel(self):
...@@ -17,6 +16,12 @@ class LRO_LROC(LineScanner, RadialDistortion): ...@@ -17,6 +16,12 @@ 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):
""" """
...@@ -32,7 +37,3 @@ class LRO_LROC(LineScanner, RadialDistortion): ...@@ -32,7 +37,3 @@ class LRO_LROC(LineScanner, RadialDistortion):
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,9 @@ import spiceypy as spice ...@@ -6,11 +6,9 @@ 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, TransverseDistortion, Spice, PDS3, Isis3
from ale.drivers.distortion import TransverseDistortion
class MdisSpice(Spice, Framer, TransverseDistortion):
class Messenger(Framer, TransverseDistortion):
id_lookup = { id_lookup = {
'MDIS-WAC': 'MSGR_MDIS_WAC', 'MDIS-WAC': 'MSGR_MDIS_WAC',
'MDIS-NAC':'MSGR_MDIS_NAC', 'MDIS-NAC':'MSGR_MDIS_NAC',
...@@ -28,10 +26,6 @@ class Messenger(Framer, TransverseDistortion): ...@@ -28,10 +26,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,7 +37,7 @@ class Messenger(Framer, TransverseDistortion): ...@@ -43,7 +37,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 @property
def focal_epsilon(self): def focal_epsilon(self):
...@@ -56,3 +50,33 @@ class Messenger(Framer, TransverseDistortion): ...@@ -56,3 +50,33 @@ class Messenger(Framer, TransverseDistortion):
@property @property
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 MdisPDS3Driver(PDS3, MdisSpice):
@property
def instrument_id(self):
return self.id_lookup[self._label['INSTRUMENT_ID']]
@property
def focal_plane_tempature(self):
return self._label['FOCAL_PLANE_TEMPERATURE'].value
class MdisIsis3Driver(Isis3, MdisSpice):
@property
def metakernel(self):
metakernel_dir = config.mdis
mks = sorted(glob(os.path.join(metakernel_dir,'*.tm')))
if not hasattr(self, '_metakernel'):
for mk in mks:
if str(self.start_time.year) in os.path.basename(mk):
self._metakernel = mk
return self._metakernel
@property
def instrument_id(self):
return self.id_lookup[self._label['IsisCube']['Instrument']['InstrumentId']]
@property
def focal_plane_tempature(self):
return self._label['IsisCube']['Instrument']['FocalPlaneTemperature'].value
...@@ -6,10 +6,9 @@ import pvl ...@@ -6,10 +6,9 @@ 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, RadialDistortion, Spice, PDS3, Isis3
from ale.drivers.distortion import RadialDistortion
class MRO_CTX(LineScanner, RadialDistortion): class CtxSpice(Spice, LineScanner, RadialDistortion):
id_lookup = { id_lookup = {
'CONTEXT CAMERA':'MRO_CTX' 'CONTEXT CAMERA':'MRO_CTX'
} }
...@@ -25,6 +24,9 @@ class MRO_CTX(LineScanner, RadialDistortion): ...@@ -25,6 +24,9 @@ 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']]
...@@ -35,3 +37,7 @@ class MRO_CTX(LineScanner, RadialDistortion): ...@@ -35,3 +37,7 @@ class MRO_CTX(LineScanner, RadialDistortion):
'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,6 @@ def lro_lroclabel(): ...@@ -113,6 +112,6 @@ 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)
...@@ -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,6 @@ END ...@@ -245,6 +244,6 @@ 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)
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():
...@@ -64,6 +63,6 @@ def mroctx_label(): ...@@ -64,6 +63,6 @@ def mroctx_label():
def test_get_dict(mroctx_label): def test_get_dict(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)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment