diff --git a/isis/src/base/apps/spkwriter/SpkSpiceSegment.cpp b/isis/src/base/apps/spkwriter/SpkSpiceSegment.cpp
index bc18000380ef960aaa63de051ed13ad546edad88..bf354ae777b98a5801d39f68062e033f7978d4b4 100644
--- a/isis/src/base/apps/spkwriter/SpkSpiceSegment.cpp
+++ b/isis/src/base/apps/spkwriter/SpkSpiceSegment.cpp
@@ -220,6 +220,7 @@ bool SpkSpiceSegment::getImageTimes(Pvl &lab, double &start, double &end) const
   return (true);
 }
 
+
 /**
  * @brief Add elements to top and bottom of a matrix
  *
diff --git a/isis/src/odyssey/apps/thm2isis/main.cpp b/isis/src/odyssey/apps/thm2isis/main.cpp
index a38a4fa4176763c3f24dd2e7c6e3f187263f7e85..263b3d361ef798cc10463203a57ab89e80f0409d 100644
--- a/isis/src/odyssey/apps/thm2isis/main.cpp
+++ b/isis/src/odyssey/apps/thm2isis/main.cpp
@@ -8,266 +8,12 @@ find files of those names at the top level of this repository. **/
 
 #include "Isis.h"
 
-#include <cstdio>
-#include <string>
+#include "Application.h"
+#include "thm2isis.h"
 
-#include "ProcessImportPds.h"
-#include "LineManager.h"
-#include "UserInterface.h"
-#include "CubeAttribute.h"
-#include "FileName.h"
-#include "IException.h"
-#include "iTime.h"
-#include "ProcessByBrick.h"
-#include "Brick.h"
-#include "OriginalLabel.h"
-
-using namespace std;
 using namespace Isis;
 
-vector<Cube *> outputCubes;
-int frameletLines;
-void TranslateLabels(Pvl &labelFile, Pvl &isis3, int numBands);
-void separateFrames(Buffer &in);
-
 void IsisMain() {
-  // Grab the file to import
-  ProcessImportPds p;
-  outputCubes.clear();
-  frameletLines = 192;
   UserInterface &ui = Application::GetUserInterface();
-  FileName in = ui.GetFileName("FROM");
-
-  // Make sure it is a Themis EDR/RDR
-  bool projected;
-  try {
-    Pvl lab(in.expanded());
-    projected = lab.hasObject("IMAGE_MAP_PROJECTION");
-    QString id;
-    id = (QString)lab["DATA_SET_ID"];
-    id = id.simplified().trimmed();
-    if(!id.startsWith("ODY-M-THM")) {
-      QString msg = "Invalid DATA_SET_ID [" + id + "]";
-      throw IException(IException::Unknown, msg, _FILEINFO_);
-    }
-  }
-  catch(IException &e) {
-    QString msg = "Input file [" + in.expanded() +
-                  "] does not appear to be " +
-                  "in Themis EDR/RDR format";
-    throw IException(IException::Io, msg, _FILEINFO_);
-  }
-
-  //Checks if in file is rdr
-  if(projected) {
-    QString msg = "[" + in.name() + "] appears to be an rdr file.";
-    msg += " Use pds2isis.";
-    throw IException(IException::User, msg, _FILEINFO_);
-  }
-
-  // Ok looks good ... set it as the PDS file
-  Pvl pdsLab;
-  p.SetPdsFile(in.expanded(), "", pdsLab);
-
-  OriginalLabel origLabels(pdsLab);
-
-  Pvl isis3Lab;
-  TranslateLabels(pdsLab, isis3Lab, p.Bands());
-
-  // Set up the output cube
-  FileName outFile(ui.GetFileName("TO"));
-  PvlGroup &inst = isis3Lab.findGroup("Instrument", Pvl::Traverse);
-
-  if((QString)inst["InstrumentId"] == "THEMIS_VIS") {
-    Cube *even = new Cube();
-    Cube *odd = new Cube();
-
-    even->setDimensions(p.Samples(), p.Lines(), p.Bands());
-    even->setPixelType(Isis::Real);
-    odd->setDimensions(p.Samples(), p.Lines(), p.Bands());
-    odd->setPixelType(Isis::Real);
-
-    QString evenFile = outFile.path() + "/" + outFile.baseName() + ".even.cub";
-    QString oddFile = outFile.path() + "/" + outFile.baseName() + ".odd.cub";
-
-    even->create(evenFile);
-    odd->create(oddFile);
-
-    frameletLines = 192 / ((int)inst["SpatialSumming"]);
-
-    outputCubes.push_back(odd);
-    outputCubes.push_back(even);
-  }
-  else {
-    Cube *outCube = new Cube();
-    outCube->setDimensions(p.Samples(), p.Lines(), p.Bands());
-    outCube->setPixelType(Isis::Real);
-
-    outCube->create(outFile.expanded());
-    outputCubes.push_back(outCube);
-  }
-
-  // Import the file and then translate labels
-  p.StartProcess(separateFrames);
-  p.EndProcess();
-
-  for(int i = 0; i < (int)outputCubes.size(); i++) {
-    for(int grp = 0; grp < isis3Lab.groups(); grp++) {
-
-      // vis image?
-      if(outputCubes.size() != 1) {
-        int numFramelets = p.Lines() / frameletLines;
-        isis3Lab.findGroup("Instrument").addKeyword(
-          PvlKeyword("NumFramelets", toString(numFramelets)), Pvl::Replace
-        );
-
-        QString frameletType = ((i == 0) ? "Odd" : "Even");
-        isis3Lab.findGroup("Instrument").addKeyword(
-          PvlKeyword("Framelets", frameletType), Pvl::Replace
-        );
-      }
-
-      outputCubes[i]->putGroup(isis3Lab.group(grp));
-    }
-    p.WriteHistory(*outputCubes[i]);
-    outputCubes[i]->write(origLabels);
-    outputCubes[i]->close();
-    delete outputCubes[i];
-  }
-
-  outputCubes.clear();
-}
-
-//! Separates each of the individual VIS frames into their own file
-void separateFrames(Buffer &in) {
-  // (line-1)/frameletHeight % numOutImages
-  int outputCube = (in.Line() - 1) / frameletLines % outputCubes.size();
-  LineManager mgr(*outputCubes[outputCube]);
-  mgr.SetLine(in.Line(), in.Band());
-
-  // mgr.Copy(in); doesn't work because the raw buffers dont match
-  for(int i = 0; i < mgr.size(); i++)
-    mgr[i] = in[i];
-
-  outputCubes[outputCube]->write(mgr);
-
-  // Null out every other cube
-  for(int i = 0; i < (int)outputCubes.size(); i++) {
-    if(i == outputCube) continue;
-
-    LineManager mgr(*outputCubes[i]);
-    mgr.SetLine(in.Line(), in.Band());
-
-    for(int j = 0; j < mgr.size(); j++) {
-      mgr[j] = Isis::Null;
-    }
-
-    outputCubes[i]->write(mgr);
-  }
-}
-
-void TranslateLabels(Pvl &pdsLab, Pvl &isis3, int numBands) {
-  // Create the Instrument Group
-  PvlGroup inst("Instrument");
-  inst += PvlKeyword("SpacecraftName", "MARS_ODYSSEY");
-  QString instId = (QString) pdsLab["InstrumentId"] + "_" +
-                  (QString) pdsLab["DetectorId"];
-  inst += PvlKeyword("InstrumentId", instId);
-  inst += PvlKeyword("TargetName", (QString) pdsLab["TargetName"]);
-  inst += PvlKeyword("MissionPhaseName", (QString) pdsLab["MissionPhaseName"]);
-  inst += PvlKeyword("StartTime", (QString)pdsLab["StartTime"]);
-  inst += PvlKeyword("StopTime", (QString)pdsLab["StopTime"]);
-  inst += PvlKeyword("SpacecraftClockCount",
-                     (QString) pdsLab["SpacecraftClockStartCount"]);
-
-  PvlObject &sqube = pdsLab.findObject("SPECTRAL_QUBE");
-  if(instId == "THEMIS_IR") {
-    inst += PvlKeyword("GainNumber", (QString)sqube["GainNumber"]);
-    inst += PvlKeyword("OffsetNumber", (QString)sqube["OffsetNumber"]);
-    inst += PvlKeyword("MissingScanLines", (QString)sqube["MissingScanLines"]);
-    inst += PvlKeyword("TimeDelayIntegration",
-                       (QString)sqube["TimeDelayIntegrationFlag"]);
-    if(sqube.hasKeyword("SpatialSumming")) {
-      inst += PvlKeyword("SpatialSumming", (QString)sqube["SpatialSumming"]);
-    }
-  }
-  else {
-    inst += PvlKeyword("ExposureDuration", (QString)sqube["ExposureDuration"]);
-    inst += PvlKeyword("InterframeDelay", (QString)sqube["InterframeDelay"]);
-    inst += PvlKeyword("SpatialSumming", (QString)sqube["SpatialSumming"]);
-  }
-
-  // Add at time offset to the Instrument group
-  UserInterface &ui = Application::GetUserInterface();
-  double spacecraftClockOffset = ui.GetDouble("TIMEOFFSET");
-  inst += PvlKeyword("SpacecraftClockOffset", toString(spacecraftClockOffset), "seconds");
-
-  isis3.addGroup(inst);
-
-  // Create the Band bin Group
-  PvlGroup bandBin("BandBin");
-
-  // The original band is the original ISIS cube band number upon ingestion
-  PvlKeyword originalBand("OriginalBand");
-  for(int i = 1; i <= numBands; i++) {
-    originalBand.addValue(toString(i));
-  }
-  bandBin += originalBand;
-
-  bandBin += sqube.findGroup("BandBin")["BandBinCenter"];
-  bandBin["BandBinCenter"].setName("Center");
-
-  bandBin += sqube.findGroup("BandBin")["BandBinWidth"];
-  bandBin["BandBinWidth"].setName("Width");
-
-  // The FilterNumber keyword is a list indicating the time-ordered filter number of the
-  // corresponding ISIS band. These values also indicate the physical order of the bands in the
-  // detector array. They are numbered by ascending times (or starting detector lines).
-  bandBin += sqube.findGroup("BandBin")["BandBinFilterNumber"];
-  bandBin["BandBinFilterNumber"].setName("FilterNumber");
-
-  // The BandNumber keyword is a list of wavelength-ordered band
-  // numbers corresponding to filter numbers for each ISIS band.
-  //
-  // For IR, BandNumber always matches filter number since the filters are found
-  // on the ccd in ascending wavelength order.
-  //
-  // For VIS, we have the following one to one correspondence of these keywords:
-  // BandNumber   {1, 2, 3, 4, 5}
-  // FilterNumber {2, 5, 3, 4, 1}
-  //
-  // Note that the BandNumber will match the OriginalBand only if the image
-  // to be imported contains band number 1 and is not missing consecutive band numbers.
-  bandBin += sqube.findGroup("BandBin")["BandBinBandNumber"];
-  bandBin["BandBinBandNumber"].setName("BandNumber");
-
-  isis3.addGroup(bandBin);
-
-  // Create the archive Group
-  PvlGroup arch("Archive");
-  arch += PvlKeyword("DataSetId", (QString)pdsLab["DataSetId"]);
-  arch += PvlKeyword("ProducerId", (QString)pdsLab["ProducerId"]);
-  arch += PvlKeyword("ProductId", (QString)pdsLab["ProductId"]);
-  arch += PvlKeyword("ProductCreationTime",
-                     (QString)pdsLab["ProductCreationTime"]);
-  arch += PvlKeyword("ProductVersionId", (QString)pdsLab["ProductVersionId"]);
-//  arch += PvlKeyword("ReleaseId",(string)pdsLab["ReleaseId"]);
-  arch += PvlKeyword("OrbitNumber", (QString)pdsLab["OrbitNumber"]);
-
-  arch += PvlKeyword("FlightSoftwareVersionId",
-                     (QString)sqube["FlightSoftwareVersionId"]);
-  arch += PvlKeyword("CommandSequenceNumber",
-                     (QString)sqube["CommandSequenceNumber"]);
-  arch += PvlKeyword("Description", (QString)sqube["Description"]);
-  isis3.addGroup(arch);
-
-  // Create the Kernel Group
-  PvlGroup kerns("Kernels");
-  if(instId == "THEMIS_IR") {
-    kerns += PvlKeyword("NaifFrameCode", toString(-53031));
-  }
-  else {
-    kerns += PvlKeyword("NaifFrameCode", toString(-53032));
-  }
-  isis3.addGroup(kerns);
-}
+  thm2isis(ui);
+}
\ No newline at end of file
diff --git a/isis/src/odyssey/apps/thm2isis/thm2isis.cpp b/isis/src/odyssey/apps/thm2isis/thm2isis.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..02db971ce8763e29a2a071c580b6c0825c94c71f
--- /dev/null
+++ b/isis/src/odyssey/apps/thm2isis/thm2isis.cpp
@@ -0,0 +1,265 @@
+#include <cstdio>
+#include <string>
+
+#include "ProcessImportPds.h"
+#include "LineManager.h"
+#include "UserInterface.h"
+#include "CubeAttribute.h"
+#include "FileName.h"
+#include "IException.h"
+#include "iTime.h"
+#include "ProcessByBrick.h"
+#include "Brick.h"
+#include "OriginalLabel.h"
+
+#include "thm2isis.h"
+
+using namespace std;
+
+namespace Isis { 
+  static vector<Cube *> outputCubes;
+  static int frameletLines;
+  static void TranslateLabels(Pvl &labelFile, Pvl &isis3, int numBands, UserInterface &ui);
+  static void separateFrames(Buffer &in);
+
+
+  void thm2isis(UserInterface &ui) {
+    // Grab the file to import
+    ProcessImportPds p;
+    outputCubes.clear();
+    frameletLines = 192;
+
+    FileName in = ui.GetFileName("FROM");
+
+    // Make sure it is a Themis EDR/RDR
+    bool projected;
+    try {
+      Pvl lab(in.expanded());
+      projected = lab.hasObject("IMAGE_MAP_PROJECTION");
+      QString id;
+      id = (QString)lab["DATA_SET_ID"];
+      id = id.simplified().trimmed();
+      if(!id.startsWith("ODY-M-THM")) {
+        QString msg = "Invalid DATA_SET_ID [" + id + "]";
+        throw IException(IException::Unknown, msg, _FILEINFO_);
+      }
+    }
+    catch(IException &e) {
+      QString msg = "Input file [" + in.expanded() +
+                    "] does not appear to be " +
+                    "in Themis EDR/RDR format";
+      throw IException(IException::Io, msg, _FILEINFO_);
+    }
+
+    //Checks if in file is rdr
+    if(projected) {
+      QString msg = "[" + in.name() + "] appears to be an rdr file.";
+      msg += " Use pds2isis.";
+      throw IException(IException::User, msg, _FILEINFO_);
+    }
+
+    // Ok looks good ... set it as the PDS file
+    Pvl pdsLab;
+    p.SetPdsFile(in.expanded(), "", pdsLab);
+
+    OriginalLabel origLabels(pdsLab);
+
+    Pvl isis3Lab;
+    TranslateLabels(pdsLab, isis3Lab, p.Bands(), ui);
+
+    // Set up the output cube
+    FileName outFile(ui.GetFileName("TO"));
+    PvlGroup &inst = isis3Lab.findGroup("Instrument", Pvl::Traverse);
+    CubeAttributeOutput outAttr = ui.GetOutputAttribute("to");
+    
+    if((QString)inst["InstrumentId"] == "THEMIS_VIS") {
+      Cube *even = new Cube();
+      Cube *odd = new Cube();
+
+      even->setDimensions(p.Samples(), p.Lines(), p.Bands());
+      odd->setDimensions(p.Samples(), p.Lines(), p.Bands());
+
+      QString evenFile = outFile.path() + "/" + outFile.baseName() + ".even.cub";
+      QString oddFile = outFile.path() + "/" + outFile.baseName() + ".odd.cub";
+
+      even->create(evenFile, outAttr);
+      odd->create(oddFile, outAttr);
+
+      frameletLines = 192 / ((int)inst["SpatialSumming"]);
+
+      outputCubes.push_back(odd);
+      outputCubes.push_back(even);
+    }
+    else {
+      Cube *outCube = new Cube();
+      outCube->setDimensions(p.Samples(), p.Lines(), p.Bands());
+
+      outCube->create(outFile.expanded(), outAttr);
+      outputCubes.push_back(outCube);
+    }
+
+    // Import the file and then translate labels
+    p.StartProcess(separateFrames);
+    p.EndProcess();
+
+    for(int i = 0; i < (int)outputCubes.size(); i++) {
+      for(int grp = 0; grp < isis3Lab.groups(); grp++) {
+
+        // vis image?
+        if(outputCubes.size() != 1) {
+          int numFramelets = p.Lines() / frameletLines;
+          isis3Lab.findGroup("Instrument").addKeyword(
+            PvlKeyword("NumFramelets", toString(numFramelets)), Pvl::Replace
+          );
+
+          QString frameletType = ((i == 0) ? "Odd" : "Even");
+          isis3Lab.findGroup("Instrument").addKeyword(
+            PvlKeyword("Framelets", frameletType), Pvl::Replace
+          );
+        }
+
+        outputCubes[i]->putGroup(isis3Lab.group(grp));
+      }
+      p.WriteHistory(*outputCubes[i]);
+      outputCubes[i]->write(origLabels);
+      outputCubes[i]->close();
+      delete outputCubes[i];
+    }
+
+    outputCubes.clear();
+  }
+
+  //! Separates each of the individual VIS frames into their own file
+  void separateFrames(Buffer &in) {
+    // (line-1)/frameletHeight % numOutImages
+    int outputCube = (in.Line() - 1) / frameletLines % outputCubes.size();
+    LineManager mgr(*outputCubes[outputCube]);
+    mgr.SetLine(in.Line(), in.Band());
+
+    // mgr.Copy(in); doesn't work because the raw buffers dont match
+    for(int i = 0; i < mgr.size(); i++)
+      mgr[i] = in[i];
+
+    outputCubes[outputCube]->write(mgr);
+
+    // Null out every other cube
+    for(int i = 0; i < (int)outputCubes.size(); i++) {
+      if(i == outputCube) continue;
+
+      LineManager mgr(*outputCubes[i]);
+      mgr.SetLine(in.Line(), in.Band());
+
+      for(int j = 0; j < mgr.size(); j++) {
+        mgr[j] = Isis::Null;
+      }
+
+      outputCubes[i]->write(mgr);
+    }
+  }
+
+  void TranslateLabels(Pvl &pdsLab, Pvl &isis3, int numBands, UserInterface &ui) {
+    // Create the Instrument Group
+    PvlGroup inst("Instrument");
+    inst += PvlKeyword("SpacecraftName", "MARS_ODYSSEY");
+    QString instId = (QString) pdsLab["InstrumentId"] + "_" +
+                    (QString) pdsLab["DetectorId"];
+    inst += PvlKeyword("InstrumentId", instId);
+    inst += PvlKeyword("TargetName", (QString) pdsLab["TargetName"]);
+    inst += PvlKeyword("MissionPhaseName", (QString) pdsLab["MissionPhaseName"]);
+    inst += PvlKeyword("StartTime", (QString)pdsLab["StartTime"]);
+    inst += PvlKeyword("StopTime", (QString)pdsLab["StopTime"]);
+    inst += PvlKeyword("SpacecraftClockCount",
+                      (QString) pdsLab["SpacecraftClockStartCount"]);
+
+    PvlObject &sqube = pdsLab.findObject("SPECTRAL_QUBE");
+    if(instId == "THEMIS_IR") {
+      inst += PvlKeyword("GainNumber", (QString)sqube["GainNumber"]);
+      inst += PvlKeyword("OffsetNumber", (QString)sqube["OffsetNumber"]);
+      inst += PvlKeyword("MissingScanLines", (QString)sqube["MissingScanLines"]);
+      inst += PvlKeyword("TimeDelayIntegration",
+                        (QString)sqube["TimeDelayIntegrationFlag"]);
+      if(sqube.hasKeyword("SpatialSumming")) {
+        inst += PvlKeyword("SpatialSumming", (QString)sqube["SpatialSumming"]);
+      }
+    }
+    else {
+      inst += PvlKeyword("ExposureDuration", (QString)sqube["ExposureDuration"]);
+      inst += PvlKeyword("InterframeDelay", (QString)sqube["InterframeDelay"]);
+      inst += PvlKeyword("SpatialSumming", (QString)sqube["SpatialSumming"]);
+    }
+
+    // Add at time offset to the Instrument group
+    
+    double spacecraftClockOffset = ui.GetDouble("TIMEOFFSET");
+    inst += PvlKeyword("SpacecraftClockOffset", toString(spacecraftClockOffset), "seconds");
+
+    isis3.addGroup(inst);
+
+    // Create the Band bin Group
+    PvlGroup bandBin("BandBin");
+
+    // The original band is the original ISIS cube band number upon ingestion
+    PvlKeyword originalBand("OriginalBand");
+    for(int i = 1; i <= numBands; i++) {
+      originalBand.addValue(toString(i));
+    }
+    bandBin += originalBand;
+
+    bandBin += sqube.findGroup("BandBin")["BandBinCenter"];
+    bandBin["BandBinCenter"].setName("Center");
+
+    bandBin += sqube.findGroup("BandBin")["BandBinWidth"];
+    bandBin["BandBinWidth"].setName("Width");
+
+    // The FilterNumber keyword is a list indicating the time-ordered filter number of the
+    // corresponding ISIS band. These values also indicate the physical order of the bands in the
+    // detector array. They are numbered by ascending times (or starting detector lines).
+    bandBin += sqube.findGroup("BandBin")["BandBinFilterNumber"];
+    bandBin["BandBinFilterNumber"].setName("FilterNumber");
+
+    // The BandNumber keyword is a list of wavelength-ordered band
+    // numbers corresponding to filter numbers for each ISIS band.
+    //
+    // For IR, BandNumber always matches filter number since the filters are found
+    // on the ccd in ascending wavelength order.
+    //
+    // For VIS, we have the following one to one correspondence of these keywords:
+    // BandNumber   {1, 2, 3, 4, 5}
+    // FilterNumber {2, 5, 3, 4, 1}
+    //
+    // Note that the BandNumber will match the OriginalBand only if the image
+    // to be imported contains band number 1 and is not missing consecutive band numbers.
+    bandBin += sqube.findGroup("BandBin")["BandBinBandNumber"];
+    bandBin["BandBinBandNumber"].setName("BandNumber");
+
+    isis3.addGroup(bandBin);
+
+    // Create the archive Group
+    PvlGroup arch("Archive");
+    arch += PvlKeyword("DataSetId", (QString)pdsLab["DataSetId"]);
+    arch += PvlKeyword("ProducerId", (QString)pdsLab["ProducerId"]);
+    arch += PvlKeyword("ProductId", (QString)pdsLab["ProductId"]);
+    arch += PvlKeyword("ProductCreationTime",
+                      (QString)pdsLab["ProductCreationTime"]);
+    arch += PvlKeyword("ProductVersionId", (QString)pdsLab["ProductVersionId"]);
+  //  arch += PvlKeyword("ReleaseId",(string)pdsLab["ReleaseId"]);
+    arch += PvlKeyword("OrbitNumber", (QString)pdsLab["OrbitNumber"]);
+
+    arch += PvlKeyword("FlightSoftwareVersionId",
+                      (QString)sqube["FlightSoftwareVersionId"]);
+    arch += PvlKeyword("CommandSequenceNumber",
+                      (QString)sqube["CommandSequenceNumber"]);
+    arch += PvlKeyword("Description", (QString)sqube["Description"]);
+    isis3.addGroup(arch);
+
+    // Create the Kernel Group
+    PvlGroup kerns("Kernels");
+    if(instId == "THEMIS_IR") {
+      kerns += PvlKeyword("NaifFrameCode", toString(-53031));
+    }
+    else {
+      kerns += PvlKeyword("NaifFrameCode", toString(-53032));
+    }
+    isis3.addGroup(kerns);
+  }
+}
\ No newline at end of file
diff --git a/isis/src/odyssey/apps/thm2isis/thm2isis.h b/isis/src/odyssey/apps/thm2isis/thm2isis.h
new file mode 100644
index 0000000000000000000000000000000000000000..6cdc701ce7c407f563def28dba985f27a54bc39f
--- /dev/null
+++ b/isis/src/odyssey/apps/thm2isis/thm2isis.h
@@ -0,0 +1,12 @@
+#ifndef thm2isis_h // Change this to your app name in all lower case suffixed with _h (e.g. campt_h, cam2map_h etc.)
+#define thm2isis_h
+
+#include "Cube.h"
+#include "UserInterface.h"
+
+namespace Isis {
+
+  extern void thm2isis(UserInterface &ui);
+}
+
+#endif
\ No newline at end of file
diff --git a/isis/src/odyssey/apps/thm2isis/tsts/Makefile b/isis/src/odyssey/apps/thm2isis/tsts/Makefile
deleted file mode 100644
index 46d84c74c297304e943452a44e06b111f179a92b..0000000000000000000000000000000000000000
--- a/isis/src/odyssey/apps/thm2isis/tsts/Makefile
+++ /dev/null
@@ -1,4 +0,0 @@
-BLANKS = "%-6s"    
-LENGTH = "%-40s"
-
-include $(ISISROOT)/make/isismake.tststree
diff --git a/isis/src/odyssey/apps/thm2isis/tsts/case01/Makefile b/isis/src/odyssey/apps/thm2isis/tsts/case01/Makefile
deleted file mode 100644
index b42526e8f822654b34440855d73712ffc5c4d98d..0000000000000000000000000000000000000000
--- a/isis/src/odyssey/apps/thm2isis/tsts/case01/Makefile
+++ /dev/null
@@ -1,9 +0,0 @@
-APPNAME = thm2isis
-
-include $(ISISROOT)/make/isismake.tsts
-
-commands:
-	$(APPNAME) from=$(INPUT)/I00831002RDR.QUB \
-	to=$(OUTPUT)/truth.cub > /dev/null;
-	cathist from=$(OUTPUT)/truth.cub \
-	to=$(OUTPUT)/historyTruth.pvl > /dev/null;
diff --git a/isis/src/odyssey/apps/thm2isis/tsts/case02/Makefile b/isis/src/odyssey/apps/thm2isis/tsts/case02/Makefile
deleted file mode 100644
index 50a0983c6b5456a35448cdfb4e07b7d83e154ba8..0000000000000000000000000000000000000000
--- a/isis/src/odyssey/apps/thm2isis/tsts/case02/Makefile
+++ /dev/null
@@ -1,9 +0,0 @@
-APPNAME = thm2isis
-
-include $(ISISROOT)/make/isismake.tsts
-
-commands:
-	$(APPNAME) from=$(INPUT)/V00821003RDR.QUB \
-	to=$(OUTPUT)/thm2isis-Truth3.cub+32bit > /dev/null;
-	cathist from=$(OUTPUT)/thm2isis-Truth3.even.cub+32bit \
-	to=$(OUTPUT)/historyTruth.pvl > /dev/null;
diff --git a/isis/tests/FunctionalTestsThm2isis.cpp b/isis/tests/FunctionalTestsThm2isis.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1b3552784b6a79e9dd9ada55f309658038d28a20
--- /dev/null
+++ b/isis/tests/FunctionalTestsThm2isis.cpp
@@ -0,0 +1,232 @@
+#include "Fixtures.h"
+#include "Pvl.h"
+#include "PvlGroup.h"
+#include "Histogram.h"
+#include "TestUtilities.h"
+
+#include "thm2isis.h"
+
+#include "gtest/gtest.h"
+
+using namespace Isis;
+
+static QString APP_XML = FileName("$ISISROOT/bin/xml/thm2isis.xml").expanded();
+
+TEST_F(TempTestingFiles, FunctionalTestThm2isisVis) {
+  // tempDir exists if the fixture subclasses TempTestingFiles, which most do
+  QString outCubeFileName = tempDir.path() + "/test.cub";
+  QVector<QString> args = {"from=data/thm2isis/V00821003RDR_cropped.QUB",  "to="+outCubeFileName};
+
+  UserInterface options(APP_XML, args);
+  try {
+    thm2isis(options);
+  }
+  catch (IException &e) {
+    FAIL() << "Unable to open image: " << e.what() << std::endl;
+  }
+  
+  // open even cube
+  Cube evenCube( tempDir.path() + "/test.even.cub");
+  Pvl *isisLabel = evenCube.label();
+
+  // Dimensions Group
+  EXPECT_EQ(evenCube.sampleCount(), 1024);
+  EXPECT_EQ(evenCube.lineCount(), 200);
+  EXPECT_EQ(evenCube.bandCount(), 5);
+
+  // Pixels Group
+  EXPECT_EQ(PixelTypeName(evenCube.pixelType()), "Real");
+  EXPECT_EQ(ByteOrderName(evenCube.byteOrder()), "Lsb");
+  EXPECT_DOUBLE_EQ(evenCube.base(), 0.0);
+  EXPECT_DOUBLE_EQ(evenCube.multiplier(), 1.0);
+
+  // Instrument Group
+  PvlGroup &inst = isisLabel->findGroup("Instrument", Pvl::Traverse);
+  EXPECT_EQ(inst["SpacecraftName"][0].toStdString(), "MARS_ODYSSEY");
+  EXPECT_EQ(inst["InstrumentId"][0].toStdString(), "THEMIS_VIS" );
+  EXPECT_EQ(inst["TargetName"][0].toStdString(), "MARS" );
+  EXPECT_EQ(inst["SpacecraftClockCount"][0].toStdString(), "698642092.025" );
+  EXPECT_EQ(inst["ExposureDuration"][0].toStdString(), "6.0" );
+  EXPECT_EQ(inst["StartTime"][0].toStdString(), "2002-02-20T03:14:02.471000" );
+  EXPECT_EQ(inst["StopTime"][0].toStdString(), "2002-02-20T03:14:09.471000" );
+  EXPECT_EQ(inst["Framelets"][0].toStdString(), "Even" );
+  EXPECT_EQ(inst["InterframeDelay"][0].toStdString(), "1.0" );
+  EXPECT_EQ(int(inst["NumFramelets"]), 1);
+
+  // Archive Group
+  PvlGroup &archive = isisLabel->findGroup("Archive", Pvl::Traverse);
+  EXPECT_EQ(archive["DataSetId"][0].toStdString(), "ODY-M-THM-3-VISRDR-V1.0" );
+  EXPECT_EQ(archive["ProductId"][0].toStdString(), "V00821003RDR" );
+  EXPECT_EQ(archive["ProductCreationTime"][0].toStdString(), "2003-07-08T03:07:17" );
+  EXPECT_EQ(double(archive["ProductVersionId"]), 1.3);
+
+  // BandBin Group
+  PvlGroup &bandbin = isisLabel->findGroup("BandBin", Pvl::Traverse);
+  EXPECT_EQ(bandbin["OriginalBand"].size(), 5);
+  EXPECT_EQ(bandbin["Center"].size(), 5);
+  EXPECT_EQ(bandbin["Width"].size(), 5);
+  EXPECT_EQ(bandbin["FilterNumber"].size(), 5);
+
+  // Kernels Group
+  PvlGroup &kernel = isisLabel->findGroup("Kernels", Pvl::Traverse);
+  EXPECT_EQ(int(kernel["NaifFrameCode"]), -53032);
+
+  std::unique_ptr<Histogram> hist (evenCube.histogram());
+
+  EXPECT_NEAR(hist->Average(), 0.0012095900723426705, 0.0001);
+  EXPECT_NEAR(hist->Sum(), 9.5743556655943394, .00001);
+  EXPECT_EQ(hist->ValidPixels(), 7920);
+  EXPECT_NEAR(hist->StandardDeviation(), 2.241887e-05, .00001);
+
+  // open odd cube
+  Cube oddCube( tempDir.path() + "/test.odd.cub");
+  isisLabel = oddCube.label();
+
+  // Dimensions Group
+  EXPECT_EQ(oddCube.sampleCount(), 1024);
+  EXPECT_EQ(oddCube.lineCount(), 200);
+  EXPECT_EQ(oddCube.bandCount(), 5);
+
+  // Pixels Group
+  EXPECT_EQ(PixelTypeName(oddCube.pixelType()), "Real");
+  EXPECT_EQ(ByteOrderName(oddCube.byteOrder()), "Lsb");
+  EXPECT_DOUBLE_EQ(oddCube.base(), 0.0);
+  EXPECT_DOUBLE_EQ(oddCube.multiplier(), 1.0);
+
+  // Instrument Group
+  inst = isisLabel->findGroup("Instrument", Pvl::Traverse);
+  EXPECT_EQ(inst["SpacecraftName"][0].toStdString(), "MARS_ODYSSEY");
+  EXPECT_EQ(inst["InstrumentId"][0].toStdString(), "THEMIS_VIS" );
+  EXPECT_EQ(inst["TargetName"][0].toStdString(), "MARS" );
+  EXPECT_EQ(inst["SpacecraftClockCount"][0].toStdString(), "698642092.025" );
+  EXPECT_EQ(inst["ExposureDuration"][0].toStdString(), "6.0" );
+  EXPECT_EQ(inst["StartTime"][0].toStdString(), "2002-02-20T03:14:02.471000" );
+  EXPECT_EQ(inst["StopTime"][0].toStdString(), "2002-02-20T03:14:09.471000" );
+  EXPECT_EQ(inst["Framelets"][0].toStdString(), "Odd" );
+  EXPECT_EQ(inst["InterframeDelay"][0].toStdString(), "1.0" );
+  EXPECT_EQ(int(inst["NumFramelets"]), 1);
+
+  // Archive Group
+  archive = isisLabel->findGroup("Archive", Pvl::Traverse);
+  EXPECT_EQ(archive["DataSetId"][0].toStdString(), "ODY-M-THM-3-VISRDR-V1.0" );
+  EXPECT_EQ(archive["ProductId"][0].toStdString(), "V00821003RDR" );
+  EXPECT_EQ(archive["ProductCreationTime"][0].toStdString(), "2003-07-08T03:07:17" );
+  EXPECT_EQ(double(archive["ProductVersionId"]), 1.3);
+
+  // BandBin Group
+  bandbin = isisLabel->findGroup("BandBin", Pvl::Traverse);
+  EXPECT_EQ(bandbin["OriginalBand"].size(), 5);
+  EXPECT_EQ(bandbin["Center"].size(), 5);
+  EXPECT_EQ(bandbin["Width"].size(), 5);
+  EXPECT_EQ(bandbin["FilterNumber"].size(), 5);
+
+  // Kernels Group
+  kernel = isisLabel->findGroup("Kernels", Pvl::Traverse);
+  EXPECT_EQ(int(kernel["NaifFrameCode"]), -53032);
+
+  hist.reset(oddCube.histogram());
+
+  EXPECT_NEAR(hist->Average(), 0.0012095900723426705, 0.0001);
+  EXPECT_NEAR(hist->Sum(), 228.48262293543667, .00001);
+  EXPECT_EQ(hist->ValidPixels(), 188100);
+  EXPECT_NEAR(hist->StandardDeviation(), 2.241887e-05, .00001);
+}
+
+
+TEST_F(TempTestingFiles, FunctionalTestThm2isisIr) {
+  QString newLabelPath = "data/thm2isis/I00831002RDR_cropped.QUB" ;
+
+  // tempDir exists if the fixture subclasses TempTestingFiles, which most do
+  QString outCubeFileName = tempDir.path() + "/test.cub";
+  QVector<QString> args = {"from=" + newLabelPath,  "to="+outCubeFileName};
+
+  UserInterface options(APP_XML, args);
+  try {
+    thm2isis(options);
+  }
+  catch (IException &e) {
+    FAIL() << "Unable to open image: " << e.what() << std::endl;
+  }
+  
+  Cube oCube(outCubeFileName);
+  Pvl *isisLabel = oCube.label();
+
+  // Dimensions Group
+  EXPECT_EQ(oCube.sampleCount(), 10);
+  EXPECT_EQ(oCube.lineCount(), 5);
+  EXPECT_EQ(oCube.bandCount(), 10);
+
+  // Pixels Group
+  EXPECT_EQ(PixelTypeName(oCube.pixelType()), "Real");
+  EXPECT_EQ(ByteOrderName(oCube.byteOrder()), "Lsb");
+  EXPECT_DOUBLE_EQ(oCube.base(), 0.0);
+  EXPECT_DOUBLE_EQ(oCube.multiplier(), 1.0);
+
+  // Instrument Group
+  PvlGroup &inst = isisLabel->findGroup("Instrument", Pvl::Traverse);
+
+  EXPECT_EQ(inst["SpacecraftName"][0].toStdString(), "MARS_ODYSSEY");
+  EXPECT_EQ(inst["InstrumentId"][0].toStdString(), "THEMIS_IR" );
+  EXPECT_EQ(inst["TargetName"][0].toStdString(), "MARS" );
+  EXPECT_EQ(inst["SpacecraftClockCount"][0].toStdString(), "698713127.000" );
+  EXPECT_EQ(inst["StartTime"][0].toStdString(), "2002-02-20T22:57:57.253000" );
+  EXPECT_EQ(inst["StopTime"][0].toStdString(), "2002-02-20T23:00:56.983000" );
+  EXPECT_EQ((int)inst["GainNumber"], 16);
+
+  // Archive Group
+  PvlGroup &archive = isisLabel->findGroup("Archive", Pvl::Traverse);
+  EXPECT_EQ(archive["DataSetId"][0].toStdString(), "ODY-M-THM-3-IRRDR-V1.0" );
+  EXPECT_EQ(archive["ProductId"][0].toStdString(), "I00831002RDR" );
+  EXPECT_EQ(archive["ProductCreationTime"][0].toStdString(), "2003-03-12T12:59:33" );
+  EXPECT_EQ(double(archive["ProductVersionId"]), 1.4);
+
+  // BandBin Group
+  PvlGroup &bandbin = isisLabel->findGroup("BandBin", Pvl::Traverse);
+  EXPECT_EQ(bandbin["OriginalBand"].size(), 10);
+  EXPECT_EQ(bandbin["Center"].size(), 10);
+  EXPECT_EQ(bandbin["Width"].size(), 10);
+  EXPECT_EQ(bandbin["FilterNumber"].size(), 10);
+
+  std::unique_ptr<Histogram> hist (oCube.histogram());
+ 
+  EXPECT_NEAR(hist->Minimum(), 0.00029065093258395791, 0.0001);
+  EXPECT_NEAR(hist->Maximum(), 0.00064912717789411545, 0.0001); 
+  EXPECT_NEAR(hist->Average(), 0.00047608536842744795, 0.0001);
+  EXPECT_NEAR(hist->Sum(), 0.023804268421372399, .00001);
+  EXPECT_EQ(hist->ValidPixels(), 50);
+  EXPECT_NEAR(hist->StandardDeviation(), 0.00011232993701816659, .00001);
+}
+
+
+TEST_F(TempTestingFiles, FunctionalTestThm2isisOutAttributes) {
+  // tempDir exists if the fixture subclasses TempTestingFiles, which most do
+  QString outCubeFileName = tempDir.path() + "/test.cub+msb+8bit+0.0012:0.0013";
+  QVector<QString> args = {"from=data/thm2isis/V00821003RDR_cropped.QUB",  "to="+outCubeFileName};
+
+  UserInterface options(APP_XML, args);
+  try {
+    thm2isis(options);
+  }
+  catch (IException &e) {
+    FAIL() << "Unable to open image: " << e.what() << std::endl;
+  }
+  
+  // open even cube
+  Cube evenCube( tempDir.path() + "/test.even.cub");
+
+  // Pixels Group
+  EXPECT_EQ(PixelTypeName(evenCube.pixelType()).toStdString(), "UnsignedByte");
+  EXPECT_EQ(ByteOrderName(evenCube.byteOrder()).toStdString(), "Msb");
+  EXPECT_DOUBLE_EQ(evenCube.base(), 0.001199604743083);
+  EXPECT_DOUBLE_EQ(evenCube.multiplier(), 3.95256916996048e-07);
+
+  std::unique_ptr<Histogram> hist (evenCube.histogram());
+   
+  EXPECT_NEAR(hist->Minimum(), 0.0012, 0.0001);
+  EXPECT_NEAR(hist->Maximum(), 0.0013, 0.0001); 
+  EXPECT_NEAR(hist->Average(), 0.00122, 0.0001);
+  EXPECT_NEAR(hist->Sum(), 6.6702830039524876, .00001);
+  EXPECT_EQ(hist->ValidPixels(), 5472);
+  EXPECT_NEAR(hist->StandardDeviation(), 1.5069986471567319e-05, .00001);
+}
+
diff --git a/isis/tests/data/thm2isis/I00831002RDR_cropped.QUB b/isis/tests/data/thm2isis/I00831002RDR_cropped.QUB
new file mode 100644
index 0000000000000000000000000000000000000000..3a1333879681bca88f705b71f1677b7ca6fad3c1
Binary files /dev/null and b/isis/tests/data/thm2isis/I00831002RDR_cropped.QUB differ
diff --git a/isis/tests/data/thm2isis/V00821003RDR_cropped.QUB b/isis/tests/data/thm2isis/V00821003RDR_cropped.QUB
new file mode 100644
index 0000000000000000000000000000000000000000..6fa54c65681a27d7a9ca4e2aa6753d64f0174e8e
Binary files /dev/null and b/isis/tests/data/thm2isis/V00821003RDR_cropped.QUB differ