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)