diff --git a/.gitignore b/.gitignore
index e9e8e08ab2c9bb73ea88cc085136d14d75fa450b..0018c1869c29de44d73579f7124e41d05e0cc467 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,9 +6,6 @@ include/csm/*
 # iPython checkpoint items
 .ipynb_checkpoints
 
-# Ignore any executables
-bin/*
-
 # Ignore any Mac stuff
 .DS_Store
 
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9e2a8415c34c22a2a514adf098c5fd99806893b2..f8bf4c7c2ee9cb785e5aa85a9c280ac6a73ff391 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -80,10 +80,13 @@ target_link_libraries(usgscsm
                       ${ALE_TARGET}
                       nlohmann_json::nlohmann_json)
 
+add_executable(usgscsm_cam_test bin/usgscsm_cam_test.cc)
+target_link_libraries(usgscsm_cam_test
+    usgscsm)
 
 install(TARGETS usgscsm LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
 install(DIRECTORY ${USGSCSM_INCLUDE_DIRS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
-
+install(TARGETS usgscsm_cam_test RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
 
 # Optional build tests
 option (USGSCSM_BUILD_TESTS "Build tests" ON)
@@ -100,4 +103,5 @@ if(USGSCSM_BUILD_TESTS)
   include(CTest)
   enable_testing()
   add_subdirectory(tests)
+
 endif()
diff --git a/bin/usgscsm_cam_test.cc b/bin/usgscsm_cam_test.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c624a52e9b826a9823d86880f11dc98fc6c7cbe8
--- /dev/null
+++ b/bin/usgscsm_cam_test.cc
@@ -0,0 +1,86 @@
+// A tool to perform some basic tests and operations on a CSM camera model.
+// 
+// Functionality:
+//
+// - Load a CSM model in ISD format.
+//
+// Future functionality:
+// 
+// - Test projecting rays from the camera to ground and vice-versa.
+// - Load a CSM model state (stored in a .json file, just like
+//   an ISD model).
+// - Ability to export a CSM model in ISD format to a CSM model state file. 
+
+#include <usgscsm/UsgsAstroPlugin.h>
+#include <csm/RasterGM.h>
+#include <UsgsAstroLsSensorModel.h>
+
+#include <iostream>
+int main(int argc, char **argv) {
+
+  if (argc != 2) {
+    std::cerr << "Usage: " << argv[0] << " <model file>" << std::endl;
+    return 1;
+  }
+
+  // This is needed to trigger loading libusgscsm.so. Otherwise 0
+  // plugins are detected.
+  UsgsAstroLsSensorModel lsModel;
+
+  // Load the isd
+  std::string model_file = argv[1];
+  csm::Isd isd(model_file);
+  std::cout << "Loading model: " << model_file << std::endl;
+  
+  // Check if loading the model worked
+  bool success = false;
+
+  std::shared_ptr<csm::RasterGM> model;
+  
+  // Try all detected plugins and models for each plugin
+  csm::PluginList plugins = csm::Plugin::getList();
+  for (auto iter = plugins.begin(); iter != plugins.end(); iter++) {
+    
+    const csm::Plugin* csm_plugin = (*iter);
+
+    std::cout << "Detected CSM plugin: " << csm_plugin->getPluginName()  << "\n";
+    // For each plugin, loop through the available models.
+    size_t num_models = csm_plugin->getNumModels();
+    std::cout << "Number of models for this plugin: " << num_models << "\n";
+    for (size_t i = 0; i < num_models; i++) {
+
+      std::string model_name = (*iter)->getModelName(i);
+
+      csm::WarningList* warnings = NULL;
+
+      if (csm_plugin->canModelBeConstructedFromISD(isd, model_name)) {
+
+        csm::Model *csm = csm_plugin->constructModelFromISD(isd, model_name, warnings);
+        csm::RasterGM *modelPtr = dynamic_cast<csm::RasterGM*>(csm);
+
+        if (modelPtr == NULL) {
+          std::cerr << "Could not load correctly a CSM model of type: "
+                    << model_name << "\n";
+          return 1;
+        } else {
+          // Assign it to a smart pointer which will handle its deallocation
+          model = std::shared_ptr<csm::RasterGM>(modelPtr);
+          success = true;
+          std::cout << "Loaded CSM model of type " << model_name
+                    << " from " << model_file << ".\n";
+        }
+      }
+    }
+  }
+  
+  if (!success) {
+    std::cerr << "Failed to load a CSM model from: " << model_file << ".\n";
+    return 1;
+  }
+
+  csm::ImageVector image_size = model->getImageSize();
+  std::cout << "Camera image rows and columns: "
+            << image_size.samp << ' ' << image_size.line << "\n";
+
+  return 0;
+}
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index c107704737ccd6f68249bb993ee8a716c0bf9b54..f28fd1c2c52a035322593e8df5e460308521a375 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -17,4 +17,9 @@ else()
     target_link_libraries(runCSMCameraModelTests usgscsm ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES} pthread)
 endif()
 
+# Test the test_usgscsm_cam_test program
+add_test(NAME test_usgscsm_cam_test_linescan
+    COMMAND usgscsm_cam_test data/orbitalLineScan.json
+    WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests)
+      
 gtest_discover_tests(runCSMCameraModelTests WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/tests)