diff --git a/ale/drivers/base.py b/ale/drivers/base.py
index 205d7b6db5a01307a25f17aa38675ac1b9ba7791..4cacadee74839bd5d88f185c20e854814ba605d1 100644
--- a/ale/drivers/base.py
+++ b/ale/drivers/base.py
@@ -5,8 +5,6 @@ import numpy as np
 import pvl
 import spiceypy as spice
 
-from ale.drivers import distortion
-
 class Base(ABC):
     """
     Abstract base class for all PDS label parsing. Implementations should override
@@ -19,7 +17,12 @@ class Base(ABC):
 
     """
     def __init__(self, label, *args, **kwargs):
-        self._label = pvl.loads(label, strict=False)
+        if isinstance(label, str):
+            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):
         """
@@ -95,6 +98,60 @@ class Base(ABC):
         data['name_sensor'] = data['instrument_id']
         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):
         """
         Helper function to pull position and velocity in one pass
@@ -121,12 +178,13 @@ class Base(ABC):
 
 
     @property
-    @abstractmethod
-    def metakernel(self):
-        pass
+    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
 
@@ -166,8 +224,19 @@ class Base(ABC):
         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 number_of_ephemerides(self):
+        return 909
+
+    @property
+    def number_of_quaternions(self):
+        return 909
 
     @property
     def spacecraft_clock_stop_count(self):
@@ -179,7 +248,7 @@ class Base(ABC):
     @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
+            self._ending_ephemeris_time = (self.image_lines * self.line_exposure_duration) + self.starting_ephemeris_time
         return self._ending_ephemeris_time
 
     @property
@@ -196,13 +265,51 @@ class Base(ABC):
     def spacecraft_name(self):
         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
     def ikid(self):
         return spice.bods2c(self.instrument_id)
 
     @property
     def fikid(self):
-        fn = self._label.get('FILTER_NUMBER', 0)
+        fn = self.filter_number
         if fn == 'N/A':
             fn = 0
         return self.ikid - int(fn)
@@ -223,35 +330,19 @@ class Base(ABC):
     def focal_length(self):
         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
     def semimajor(self):
-        rad = spice.bodvrd(self._label['TARGET_NAME'], 'RADII', 3)
+        rad = spice.bodvrd(self.target_name, 'RADII', 3)
         return rad[1][1]
 
     @property
     def semiminor(self):
-        rad = spice.bodvrd(self._label['TARGET_NAME'], 'RADII', 3)
+        rad = spice.bodvrd(self.target_name, 'RADII', 3)
         return rad[1][0]
 
     @property
     def reference_frame(self):
-        return 'IAU_{}'.format(self._label['TARGET_NAME'])
+        return 'IAU_{}'.format(self.target_name)
 
     @property
     def sun_position(self):
@@ -259,7 +350,7 @@ class Base(ABC):
                                      self.center_ephemeris_time,
                                      self.reference_frame,
                                      'NONE',
-                                     self._label['TARGET_NAME'])
+                                     self.target_name)
 
         return [sun_state[:4].tolist()]
 
@@ -269,7 +360,7 @@ class Base(ABC):
                                      self.center_ephemeris_time,
                                      self.reference_frame,
                                      'NONE',
-                                     self._label['TARGET_NAME'])
+                                     self.target_name)
 
         return [sun_state[3:6].tolist()]
 
@@ -307,23 +398,13 @@ 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]]
+        return [[float(self.starting_detector_line), self.t0_ephemeris, self.line_exposure_duration]]
 
     @property
     def detector_center(self):
@@ -340,50 +421,39 @@ 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
-
-    @property
-    def t0_quaternion(self):
-        return self.starting_ephemeris_time - self.center_ephemeris_time
 
+class Isis3():
     @property
-    def dt_ephemeris(self):
-        return (self.ending_ephemeris_time - self.starting_ephemeris_time) / self.number_of_ephemerides
+    def start_time(self):
+        return self._label['IsisCube']['Instrument']['StartTime']
 
     @property
-    def dt_quaternion(self):
-        return (self.ending_ephemeris_time - self.starting_ephemeris_time) / self.number_of_ephemerides
+    def spacecraft_name(self):
+        return self._label['IsisCube']['Instrument']['SpacecraftName']
 
     @property
-    def number_of_ephemerides(self):
-        return 909
+    def image_lines(self):
+        return self._label['IsisCube']['Core']['Dimensions']['Lines']
 
     @property
-    def number_of_quaternions(self):
-        return 909
-
-
+    def image_samples(self):
+        return self._label['IsisCube']['Core']['Dimensions']['Samples']
 
     @property
     def _exposure_duration(self):
-        return self._label['LINE_EXPOSURE_DURATION'].value * 0.001  # Scale to seconds
-
-class Framer(Base):
-
-    @property
-    def name_model(self):
-        return "USGS_ASTRO_FRAME_SENSOR_MODEL"
+        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..ba1926022f38e2d8ea51d70dbcfe387088c5d663 100644
--- a/ale/drivers/cassini_driver.py
+++ b/ale/drivers/cassini_driver.py
@@ -6,8 +6,7 @@ import spiceypy as spice
 import numpy as np
 
 from ale import config
-from ale.drivers.base import Framer
-from ale.drivers.distortion import RadialDistortion
+from ale.drivers.base import Framer, RadialDistortion
 
 
 class CassiniISS(Framer, RadialDistortion):
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/lro_driver.py b/ale/drivers/lro_driver.py
index c912497e3f7842047a762dea344d945d4f57e4ed..39ab7beee491e3f01456368671c8be6050072f79 100644
--- a/ale/drivers/lro_driver.py
+++ b/ale/drivers/lro_driver.py
@@ -6,10 +6,9 @@ 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, RadialDistortion, Spice, PDS3, Isis3
 
-class LRO_LROC(LineScanner, RadialDistortion):
+class LrocSpice(Spice, LineScanner, RadialDistortion):
 
     @property
     def metakernel(self):
@@ -17,6 +16,12 @@ 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):
         """
@@ -32,7 +37,3 @@ class LRO_LROC(LineScanner, RadialDistortion):
             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 00c1dcf895409af960d0dfdfef4ef7e741c4c625..d8bbb96c86d4bfe8a7c528218178c4732e7444d0 100644
--- a/ale/drivers/mdis_driver.py
+++ b/ale/drivers/mdis_driver.py
@@ -6,11 +6,9 @@ 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, TransverseDistortion, Spice, PDS3, Isis3
 
-
-class Messenger(Framer, TransverseDistortion):
+class MdisSpice(Spice, Framer, TransverseDistortion):
     id_lookup = {
         'MDIS-WAC': 'MSGR_MDIS_WAC',
         'MDIS-NAC':'MSGR_MDIS_NAC',
@@ -28,10 +26,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,7 +37,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)
+        return f_t(self.focal_plane_tempature)
 
     @property
     def focal_epsilon(self):
@@ -56,3 +50,33 @@ class Messenger(Framer, TransverseDistortion):
     @property
     def starting_detector_line(self):
         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
diff --git a/ale/drivers/mro_driver.py b/ale/drivers/mro_driver.py
index 20b28ab7c619b7b42f540f43e00f3144eeae40d8..0c5e843b5782dd7ce4fef093ba29e62bd7689c4c 100644
--- a/ale/drivers/mro_driver.py
+++ b/ale/drivers/mro_driver.py
@@ -6,10 +6,9 @@ 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, RadialDistortion, Spice, PDS3, Isis3
 
-class MRO_CTX(LineScanner, RadialDistortion):
+class CtxSpice(Spice, LineScanner, RadialDistortion):
     id_lookup = {
             'CONTEXT CAMERA':'MRO_CTX'
     }
@@ -25,6 +24,9 @@ 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']]
@@ -35,3 +37,7 @@ class MRO_CTX(LineScanner, RadialDistortion):
             'MARS_RECONNAISSANCE_ORBITER': 'MRO'
         }
         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..39fb43dc33d557a1868545a7b6a4d3d20e1a4a82 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,6 @@ 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)
diff --git a/tests/pytests/test_mdis_driver.py b/tests/pytests/test_mdis_driver.py
index 1084934187cca9d42fe3e69bd1d02bac3c1541fb..efcbd00a3425eeec72f5280a70e34fe900d14ad9 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,6 @@ END
     """
 
 def test_mdis_creation(mdislabel):
-    with Messenger(mdislabel) as m:
+    with MdisPDS3Driver(mdislabel) as m:
         d = m.to_dict()
         assert isinstance(d, dict)
diff --git a/tests/pytests/test_mro_drivers.py b/tests/pytests/test_mro_drivers.py
index 00b48f0d8a5616a372ce6c44b324cef6833f77a0..6f73701267efb77b56c7d93e3d26c61f69c744e3 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():
@@ -64,6 +63,6 @@ def 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()
     assert isinstance(d, dict)