diff --git a/CMakeLists.txt b/CMakeLists.txt
index 58f2ab12c649f8202f2486524f7760c19f90681c..e656f5074213b944181f4d80df70cabef538c856 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -67,10 +67,11 @@ set(PROJ_TARGET PROJ::proj)
 
 add_library(usgscsm SHARED
             src/UsgsAstroPlugin.cpp
+            src/UsgsAstroPluginSupport.cpp
             src/UsgsAstroFrameSensorModel.cpp
             src/UsgsAstroPushFrameSensorModel.cpp
             src/UsgsAstroLsSensorModel.cpp
-            src/UsgsAstroProjectedLsSensorModel.cpp
+            src/UsgsAstroProjectedSensorModel.cpp
             src/UsgsAstroSarSensorModel.cpp
             src/Distortion.cpp
             src/Utilities.cpp
diff --git a/include/usgscsm/UsgsAstroPluginSupport.h b/include/usgscsm/UsgsAstroPluginSupport.h
new file mode 100644
index 0000000000000000000000000000000000000000..ee17d313d8d1e681179544c413b225937a9bb0fe
--- /dev/null
+++ b/include/usgscsm/UsgsAstroPluginSupport.h
@@ -0,0 +1,9 @@
+#ifndef INCLUDE_USGSCSM_USGSASTROPLUGINSUPPORT_H_
+#define INCLUDE_USGSCSM_USGSASTROPLUGINSUPPORT_H_
+
+#include <RasterGM.h>
+#include <csm/Plugin.h>
+
+csm::RasterGM *getUsgsCsmModel(const std::string &stringIsd, const std::string &modelName, csm::WarningList *warnings);
+
+#endif // INCLUDE_USGSCSM_USGSASTROPLUGINSUPPORT_H_
diff --git a/include/usgscsm/UsgsAstroProjectedLsSensorModel.h b/include/usgscsm/UsgsAstroProjectedLsSensorModel.h
deleted file mode 100644
index 56b6fbb0df050c7400cada2f2c160e3554f81200..0000000000000000000000000000000000000000
--- a/include/usgscsm/UsgsAstroProjectedLsSensorModel.h
+++ /dev/null
@@ -1,266 +0,0 @@
-/** Copyright  © 2017-2022 BAE Systems Information and Electronic Systems Integration Inc.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted
-provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this list of conditions
-and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of
-conditions and the following disclaimer in the documentation and/or other materials provided
-with the distribution.
-
-3. Neither the name of the copyright holder nor the names of its contributors may be used to
-endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
-FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
-CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
-IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/
-
-#ifndef INCLUDE_USGSCSM_USGSASTROPROJECTEDLSSENSORMODEL_H_
-#define INCLUDE_USGSCSM_USGSASTROPROJECTEDLSSENSORMODEL_H_
-
-#include <RasterGM.h>
-#include <SettableEllipsoid.h>
-
-#include<utility>
-#include<memory>
-#include<string>
-#include<vector>
-
-#include "ale/Orientations.h"
-#include "ale/States.h"
-
-#include "spdlog/spdlog.h"
-
-#include "UsgsAstroLsSensorModel.h"
-
-class UsgsAstroProjectedLsSensorModel : public UsgsAstroLsSensorModel {
- public:
-  // Initializes the class from state data as formatted
-  // in a string by the toString() method
-  void setState(const std::string& state);
-
-  virtual void replaceModelState(const std::string& stateString);
-  //> This method attempts to initialize the current model with the state
-  //  given by argState.  The argState argument can be a string previously
-  //  retrieved from the getModelState method.
-  //
-  //  If argState contains a valid state for the current model,
-  //  the internal state of the model is updated.
-  //
-  //  If the model cannot be updated to the given state, a csm::Error is
-  //  thrown and the internal state of the model is undefined.
-  //
-  //  If the argument state string is empty, the model remains unchanged.
-  //<
-
-  // This method checks to see if the model name is recognized
-  // in the input state string.
-  static std::string getModelNameFromModelState(const std::string& model_state);
-
-  std::string constructStateFromIsd(const std::string imageSupportData,
-                                    csm::WarningList* list);
-
-  // State data elements;
-  std::vector<double> m_geoTransform;
-  std::string m_projString;
-
-  // Define logging pointer and file content
-  std::shared_ptr<spdlog::logger> m_logger = spdlog::get("usgscsm_logger");
-
-  // Hardcoded
-  static const std::string _SENSOR_MODEL_NAME;  // state date element 0
-
-  static const std::string _STATE_KEYWORD[];
-
-  // Set to default values
-  void reset();
-
-  //--------------------------------------------------------------
-  // Constructors/Destructor
-  //--------------------------------------------------------------
-
-  UsgsAstroProjectedLsSensorModel();
-  ~UsgsAstroProjectedLsSensorModel();
-
-  virtual std::string getModelState() const;
-
-  // Set the sensor model based on the input state data
-  void set(const std::string& state_data);
-
-  //----------------------------------------------------------------
-  // The following public methods are implementations of
-  // the methods inherited from RasterGM and SettableEllipsoid.
-  // These are defined in the CSM API.
-  //----------------------------------------------------------------
-
-  //---
-  // Core Photogrammetry
-  //---
-  virtual csm::ImageCoord groundToImage(
-      const csm::EcefCoord& groundPt, double desiredPrecision = 0.001,
-      double* achievedPrecision = NULL,
-      csm::WarningList* warnings = NULL) const;
-
-  //> This method converts the given groundPt (x,y,z in ECEF meters) to a
-  //  returned image coordinate (line, sample in full image space pixels).
-  //
-  //  Iterative algorithms will use desiredPrecision, in meters, as the
-  //  convergence criterion, otherwise it will be ignored.
-  //
-  //  If a non-NULL achievedPrecision argument is received, it will be
-  //  populated with the actual precision, in meters, achieved by iterative
-  //  algorithms and 0.0 for deterministic algorithms.
-  //
-  //  If a non-NULL warnings argument is received, it will be populated
-  //  as applicable.
-  //<
-
-  virtual csm::ImageCoordCovar groundToImage(
-      const csm::EcefCoordCovar& groundPt, double desiredPrecision = 0.001,
-      double* achievedPrecision = NULL,
-      csm::WarningList* warnings = NULL) const;
-  //> This method converts the given groundPt (x,y,z in ECEF meters and
-  //  corresponding 3x3 covariance in ECEF meters squared) to a returned
-  //  image coordinate with covariance (line, sample in full image space
-  //  pixels and corresponding 2x2 covariance in pixels squared).
-  //
-  //  Iterative algorithms will use desiredPrecision, in meters, as the
-  //  convergence criterion, otherwise it will be ignored.
-  //
-  //  If a non-NULL achievedPrecision argument is received, it will be
-  //  populated with the actual precision, in meters, achieved by iterative
-  //  algorithms and 0.0 for deterministic algorithms.
-  //
-  //  If a non-NULL warnings argument is received, it will be populated
-  //  as applicable.
-  //<
-
-  virtual csm::EcefCoord imageToGround(const csm::ImageCoord& imagePt,
-                                       double height,
-                                       double desiredPrecision = 0.001,
-                                       double* achievedPrecision = NULL,
-                                       csm::WarningList* warnings = NULL) const;
-  //> This method converts the given imagePt (line,sample in full image
-  //  space pixels) and given height (in meters relative to the WGS-84
-  //  ellipsoid) to a returned ground coordinate (x,y,z in ECEF meters).
-  //
-  //  Iterative algorithms will use desiredPrecision, in meters, as the
-  //  convergence criterion, otherwise it will be ignored.
-  //
-  //  If a non-NULL achievedPrecision argument is received, it will be
-  //  populated with the actual precision, in meters, achieved by iterative
-  //  algorithms and 0.0 for deterministic algorithms.
-  //
-  //  If a non-NULL warnings argument is received, it will be populated
-  //  as applicable.
-  //<
-
-  virtual csm::EcefCoordCovar imageToGround(
-      const csm::ImageCoordCovar& imagePt, double height, double heightVariance,
-      double desiredPrecision = 0.001, double* achievedPrecision = NULL,
-      csm::WarningList* warnings = NULL) const;
-  //> This method converts the given imagePt (line, sample in full image
-  //  space pixels and corresponding 2x2 covariance in pixels squared)
-  //  and given height (in meters relative to the WGS-84 ellipsoid) and
-  //  corresponding heightVariance (in meters) to a returned ground
-  //  coordinate with covariance (x,y,z in ECEF meters and corresponding
-  //  3x3 covariance in ECEF meters squared).
-  //
-  //  Iterative algorithms will use desiredPrecision, in meters, as the
-  //  convergence criterion, otherwise it will be ignored.
-  //
-  //  If a non-NULL achievedPrecision argument is received, it will be
-  //  populated with the actual precision, in meters, achieved by iterative
-  //  algorithms and 0.0 for deterministic algorithms.
-  //
-  //  If a non-NULL warnings argument is received, it will be populated
-  //  as applicable.
-  //<
-
-  virtual csm::EcefLocus imageToProximateImagingLocus(
-      const csm::ImageCoord& imagePt, const csm::EcefCoord& groundPt,
-      double desiredPrecision = 0.001, double* achievedPrecision = NULL,
-      csm::WarningList* warnings = NULL) const;
-  //> This method, for the given imagePt (line, sample in full image space
-  //  pixels), returns the position and direction of the imaging locus
-  //  nearest the given groundPt (x,y,z in ECEF meters).
-  //
-  //  Note that there are two opposite directions possible.  Both are
-  //  valid, so either can be returned; the calling application can convert
-  //  to the other as necessary.
-  //
-  //  Iterative algorithms will use desiredPrecision, in meters, as the
-  //  convergence criterion for the locus position, otherwise it will be
-  //  ignored.
-  //
-  //  If a non-NULL achievedPrecision argument is received, it will be
-  //  populated with the actual precision, in meters, achieved by iterative
-  //  algorithms and 0.0 for deterministic algorithms.
-  //
-  //  If a non-NULL warnings argument is received, it will be populated
-  //  as applicable.
-  //<
-
-  virtual csm::EcefLocus imageToRemoteImagingLocus(
-      const csm::ImageCoord& imagePt, double desiredPrecision = 0.001,
-      double* achievedPrecision = NULL,
-      csm::WarningList* warnings = NULL) const;
-  //> This method, for the given imagePt (line, sample in full image space
-  //  pixels), returns the position and direction of the imaging locus
-  //  at the sensor.
-  //
-  //  Note that there are two opposite directions possible.  Both are
-  //  valid, so either can be returned; the calling application can convert
-  //  to the other as necessary.
-  //
-  //  Iterative algorithms will use desiredPrecision, in meters, as the
-  //  convergence criterion for the locus position, otherwise it will be
-  //  ignored.
-  //
-  //  If a non-NULL achievedPrecision argument is received, it will be
-  //  populated with the actual precision, in meters, achieved by iterative
-  //  algorithms and 0.0 for deterministic algorithms.
-  //
-  //  If a non-NULL warnings argument is received, it will be populated
-  //  as applicable.
-  //
-  //  Notes:
-  //
-  //  The remote imaging locus is only well-defined for optical sensors.
-  //  It is undefined for SAR sensors and might not be available for
-  //  polynomial and other non-physical models.  The
-  //  imageToProximateImagingLocus method should be used instead where
-  //  possible.
-  //<
-
-  //---
-  // Error Correction
-  //---
-
-  virtual csm::Version getVersion() const;
-  //> This method returns the version of the model code.  The Version
-  //  object can be compared to other Version objects with its comparison
-  //  operators.  Not to be confused with the CSM API version.
-  //<
-
-  virtual std::string getModelName() const;
-  //> This method returns a string identifying the name of the model.
-  //<
-
-  virtual std::string getPedigree() const;
-  //> This method returns a string that identifies the sensor,
-  //  the model type, its mode of acquisition and processing path.
-  //  For example, an optical sensor model or a cubic rational polynomial
-  //  model created from the same sensor's support data would produce
-  //  different pedigrees for each case.
-  //<
-};
-
-#endif  // INCLUDE_USGSCSM_USGSASTROPROJECTEDLSSENSORMODEL_H_
diff --git a/include/usgscsm/UsgsAstroProjectedSensorModel.h b/include/usgscsm/UsgsAstroProjectedSensorModel.h
new file mode 100644
index 0000000000000000000000000000000000000000..4763d3902dabfe62aa4872f3a3d9d673dcd8cc5e
--- /dev/null
+++ b/include/usgscsm/UsgsAstroProjectedSensorModel.h
@@ -0,0 +1,828 @@
+/** Copyright  © 2017-2022 BAE Systems Information and Electronic Systems Integration Inc.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted
+provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions
+and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of
+conditions and the following disclaimer in the documentation and/or other materials provided
+with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to
+endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/
+
+#ifndef INCLUDE_USGSCSM_USGSASTROPROJECTEDSENSORMODEL_H_
+#define INCLUDE_USGSCSM_USGSASTROPROJECTEDSENSORMODEL_H_
+
+#include <RasterGM.h>
+#include <SettableEllipsoid.h>
+
+#include<utility>
+#include<memory>
+#include<string>
+#include<vector>
+
+#include "ale/Orientations.h"
+#include "ale/States.h"
+
+#include "spdlog/spdlog.h"
+
+#include "UsgsAstroLsSensorModel.h"
+
+class UsgsAstroProjectedSensorModel : public csm::RasterGM,
+                                      virtual public csm::SettableEllipsoid {
+
+public:
+    // Initializes the class from state data as formatted
+    // in a string by the toString() method
+    void setState(const std::string &state);
+
+    virtual void replaceModelState(const std::string &stateString);
+    //> This method attempts to initialize the current model with the state
+    //  given by argState.  The argState argument can be a string previously
+    //  retrieved from the getModelState method.
+    //
+    //  If argState contains a valid state for the current model,
+    //  the internal state of the model is updated.
+    //
+    //  If the model cannot be updated to the given state, a csm::Error is
+    //  thrown and the internal state of the model is undefined.
+    //
+    //  If the argument state string is empty, the model remains unchanged.
+    //<
+
+    // This method checks to see if the model name is recognized
+    // in the input state string.
+    static std::string getModelNameFromModelState(const std::string &model_state);
+
+    std::string constructStateFromIsd(const std::string imageSupportData,
+                                      csm::WarningList *list);
+
+    // State data elements;
+    double m_majorAxis;
+    double m_minorAxis;
+    std::vector<double> m_geoTransform;
+    std::string m_projString;
+
+    // Define logging pointer and file content
+    std::shared_ptr<spdlog::logger> m_logger = spdlog::get("usgscsm_logger");
+
+    // Hardcoded
+    static const std::string _SENSOR_MODEL_NAME; // state date element 0
+
+    static const std::string _STATE_KEYWORD[];
+
+    // Set to default values
+    void reset();
+
+    //--------------------------------------------------------------
+    // Constructors/Destructor
+    //--------------------------------------------------------------
+
+    UsgsAstroProjectedSensorModel();
+    ~UsgsAstroProjectedSensorModel();
+
+    virtual std::string getModelState() const;
+
+    // Set the sensor model based on the input state data
+    void set(const std::string &state_data);
+
+    //----------------------------------------------------------------
+    // The following public methods are implementations of
+    // the methods inherited from RasterGM and SettableEllipsoid.
+    // These are defined in the CSM API.
+    //----------------------------------------------------------------
+
+    //---
+    // Core Photogrammetry
+    //---
+    virtual csm::ImageCoord groundToImage(
+        const csm::EcefCoord &groundPt, double desiredPrecision = 0.001,
+        double *achievedPrecision = NULL,
+        csm::WarningList *warnings = NULL) const;
+
+    //> This method converts the given groundPt (x,y,z in ECEF meters) to a
+    //  returned image coordinate (line, sample in full image space pixels).
+    //
+    //  Iterative algorithms will use desiredPrecision, in meters, as the
+    //  convergence criterion, otherwise it will be ignored.
+    //
+    //  If a non-NULL achievedPrecision argument is received, it will be
+    //  populated with the actual precision, in meters, achieved by iterative
+    //  algorithms and 0.0 for deterministic algorithms.
+    //
+    //  If a non-NULL warnings argument is received, it will be populated
+    //  as applicable.
+    //<
+
+    virtual csm::ImageCoordCovar groundToImage(
+        const csm::EcefCoordCovar &groundPt, double desiredPrecision = 0.001,
+        double *achievedPrecision = NULL,
+        csm::WarningList *warnings = NULL) const;
+    //> This method converts the given groundPt (x,y,z in ECEF meters and
+    //  corresponding 3x3 covariance in ECEF meters squared) to a returned
+    //  image coordinate with covariance (line, sample in full image space
+    //  pixels and corresponding 2x2 covariance in pixels squared).
+    //
+    //  Iterative algorithms will use desiredPrecision, in meters, as the
+    //  convergence criterion, otherwise it will be ignored.
+    //
+    //  If a non-NULL achievedPrecision argument is received, it will be
+    //  populated with the actual precision, in meters, achieved by iterative
+    //  algorithms and 0.0 for deterministic algorithms.
+    //
+    //  If a non-NULL warnings argument is received, it will be populated
+    //  as applicable.
+    //<
+
+    virtual csm::EcefCoord imageToGround(const csm::ImageCoord &imagePt,
+                                         double height,
+                                         double desiredPrecision = 0.001,
+                                         double *achievedPrecision = NULL,
+                                         csm::WarningList *warnings = NULL) const;
+    //> This method converts the given imagePt (line,sample in full image
+    //  space pixels) and given height (in meters relative to the WGS-84
+    //  ellipsoid) to a returned ground coordinate (x,y,z in ECEF meters).
+    //
+    //  Iterative algorithms will use desiredPrecision, in meters, as the
+    //  convergence criterion, otherwise it will be ignored.
+    //
+    //  If a non-NULL achievedPrecision argument is received, it will be
+    //  populated with the actual precision, in meters, achieved by iterative
+    //  algorithms and 0.0 for deterministic algorithms.
+    //
+    //  If a non-NULL warnings argument is received, it will be populated
+    //  as applicable.
+    //<
+
+    virtual csm::EcefCoordCovar imageToGround(
+        const csm::ImageCoordCovar &imagePt, double height, double heightVariance,
+        double desiredPrecision = 0.001, double *achievedPrecision = NULL,
+        csm::WarningList *warnings = NULL) const;
+    //> This method converts the given imagePt (line, sample in full image
+    //  space pixels and corresponding 2x2 covariance in pixels squared)
+    //  and given height (in meters relative to the WGS-84 ellipsoid) and
+    //  corresponding heightVariance (in meters) to a returned ground
+    //  coordinate with covariance (x,y,z in ECEF meters and corresponding
+    //  3x3 covariance in ECEF meters squared).
+    //
+    //  Iterative algorithms will use desiredPrecision, in meters, as the
+    //  convergence criterion, otherwise it will be ignored.
+    //
+    //  If a non-NULL achievedPrecision argument is received, it will be
+    //  populated with the actual precision, in meters, achieved by iterative
+    //  algorithms and 0.0 for deterministic algorithms.
+    //
+    //  If a non-NULL warnings argument is received, it will be populated
+    //  as applicable.
+    //<
+
+    virtual csm::EcefLocus imageToProximateImagingLocus(
+        const csm::ImageCoord &imagePt, const csm::EcefCoord &groundPt,
+        double desiredPrecision = 0.001, double *achievedPrecision = NULL,
+        csm::WarningList *warnings = NULL) const;
+    //> This method, for the given imagePt (line, sample in full image space
+    //  pixels), returns the position and direction of the imaging locus
+    //  nearest the given groundPt (x,y,z in ECEF meters).
+    //
+    //  Note that there are two opposite directions possible.  Both are
+    //  valid, so either can be returned; the calling application can convert
+    //  to the other as necessary.
+    //
+    //  Iterative algorithms will use desiredPrecision, in meters, as the
+    //  convergence criterion for the locus position, otherwise it will be
+    //  ignored.
+    //
+    //  If a non-NULL achievedPrecision argument is received, it will be
+    //  populated with the actual precision, in meters, achieved by iterative
+    //  algorithms and 0.0 for deterministic algorithms.
+    //
+    //  If a non-NULL warnings argument is received, it will be populated
+    //  as applicable.
+    //<
+
+    virtual csm::EcefLocus imageToRemoteImagingLocus(
+        const csm::ImageCoord &imagePt, double desiredPrecision = 0.001,
+        double *achievedPrecision = NULL,
+        csm::WarningList *warnings = NULL) const;
+    //> This method, for the given imagePt (line, sample in full image space
+    //  pixels), returns the position and direction of the imaging locus
+    //  at the sensor.
+    //
+    //  Note that there are two opposite directions possible.  Both are
+    //  valid, so either can be returned; the calling application can convert
+    //  to the other as necessary.
+    //
+    //  Iterative algorithms will use desiredPrecision, in meters, as the
+    //  convergence criterion for the locus position, otherwise it will be
+    //  ignored.
+    //
+    //  If a non-NULL achievedPrecision argument is received, it will be
+    //  populated with the actual precision, in meters, achieved by iterative
+    //  algorithms and 0.0 for deterministic algorithms.
+    //
+    //  If a non-NULL warnings argument is received, it will be populated
+    //  as applicable.
+    //
+    //  Notes:
+    //
+    //  The remote imaging locus is only well-defined for optical sensors.
+    //  It is undefined for SAR sensors and might not be available for
+    //  polynomial and other non-physical models.  The
+    //  imageToProximateImagingLocus method should be used instead where
+    //  possible.
+    //<
+
+    //---
+    // Monoscopic Mensuration
+    //---
+    virtual csm::ImageCoord getImageStart() const;
+    //> This method returns the starting coordinate (line, sample in full
+    //  image space pixels) for the imaging operation.  Typically (0,0).
+    //<
+
+    virtual csm::ImageVector getImageSize() const;
+    //> This method returns the number of lines and samples in full image
+    //  space pixels for the imaging operation.
+    //
+    //  Note that the model might not be valid over the entire imaging
+    //  operation.  Use getValidImageRange() to get the valid range of image
+    //  coordinates.
+    //<
+
+    virtual std::pair<csm::ImageCoord, csm::ImageCoord> getValidImageRange()
+        const;
+    //> This method returns the minimum and maximum image coordinates
+    //  (line, sample in full image space pixels), respectively, over which
+    //  the current model is valid.  The image coordinates define opposite
+    //  corners of a rectangle whose sides are parallel to the line and
+    //  sample axes.
+    //
+    //  The valid image range does not always match the full image
+    //  coverage as returned by the getImageStart and getImageSize methods.
+    //
+    //  Used in conjunction with the getValidHeightRange method, it is
+    //  possible to determine the full range of ground coordinates over which
+    //  the model is valid.
+    //<
+
+    virtual std::pair<double, double> getValidHeightRange() const;
+    //> This method returns the minimum and maximum heights (in meters
+    //  relative to WGS-84 ellipsoid), respectively, over which the model is
+    //  valid.  For example, a model for an airborne platform might not be
+    //  designed to return valid coordinates for heights above the aircraft.
+    //
+    //  If there are no limits defined for the model, (-99999.0,99999.0)
+    //  will be returned.
+    //<
+
+    virtual csm::EcefVector getIlluminationDirection(
+        const csm::EcefCoord &groundPt) const;
+    //> This method returns a vector defining the direction of
+    //  illumination at the given groundPt (x,y,z in ECEF meters).
+    //  Note that there are two opposite directions possible.  Both are
+    //  valid, so either can be returned; the calling application can convert
+    //  to the other as necessary.
+    //<
+
+    //---
+    // Time and Trajectory
+    //---
+    virtual double getImageTime(const csm::ImageCoord &imagePt) const;
+    //> This method returns the time in seconds at which the pixel at the
+    //  given imagePt (line, sample in full image space pixels) was captured
+    //
+    //  The time provided is relative to the reference date and time given
+    //  by the Model::getReferenceDateAndTime method.
+    //<
+
+    virtual csm::EcefCoord getSensorPosition(
+        const csm::ImageCoord &imagePt) const;
+    //> This method returns the position of the physical sensor
+    // (x,y,z in ECEF meters) when the pixel at the given imagePt
+    // (line, sample in full image space pixels) was captured.
+    //
+    // A csm::Error will be thrown if the sensor position is not available.
+    //<
+
+    virtual csm::EcefCoord getSensorPosition(double time) const;
+    //> This method returns the position of the physical sensor
+    //  (x,y,z meters ECEF) at the given time relative to the reference date
+    //  and time given by the Model::getReferenceDateAndTime method.
+    //<
+
+    virtual csm::EcefVector getSensorVelocity(
+        const csm::ImageCoord &imagePt) const;
+    //> This method returns the velocity of the physical sensor
+    // (x,y,z in ECEF meters per second) when the pixel at the given imagePt
+    // (line, sample in full image space pixels) was captured.
+    //<
+
+    virtual csm::EcefVector getSensorVelocity(double time) const;
+    //> This method returns the velocity of the physical sensor
+    //  (x,y,z in ECEF meters per second ) at the given time relative to the
+    //  reference date and time given by the Model::getReferenceDateAndTime
+    //  method.
+    //<
+
+    virtual csm::RasterGM::SensorPartials computeSensorPartials(
+        int index, const csm::EcefCoord &groundPt,
+        double desiredPrecision = 0.001, double *achievedPrecision = NULL,
+        csm::WarningList *warnings = NULL) const;
+    //> This is one of two overloaded methods.  This method takes only
+    //  the necessary inputs.  Some effieciency can be obtained by using the
+    //  other method.  Even more efficiency can be obtained by using the
+    //  computeAllSensorPartials method.
+    //
+    //  This method returns the partial derivatives of line and sample
+    //  (in pixels per the applicable model parameter units), respectively,
+    //  with respect to the model parameter given by index at the given
+    //  groundPt (x,y,z in ECEF meters).
+    //
+    //  Derived model implementations may wish to implement this method by
+    //  calling the groundToImage method and passing the resulting image
+    //  coordinate to the other computeSensorPartials method.
+    //
+    //  If a non-NULL achievedPrecision argument is received, it will be
+    //  populated with the highest actual precision, in meters, achieved by
+    //  iterative algorithms and 0.0 for deterministic algorithms.
+    //
+    //  If a non-NULL achievedPrecision argument is received, it will be
+    //  populated with the actual precision, in meters, achieved by iterative
+    //  algorithms and 0.0 for deterministic algorithms.
+    //
+    //  If a non-NULL warnings argument is received, it will be populated
+    //  as applicable.
+    //<
+
+    virtual csm::RasterGM::SensorPartials computeSensorPartials(
+        int index, const csm::ImageCoord &imagePt, const csm::EcefCoord &groundPt,
+        double desiredPrecision = 0.001, double *achievedPrecision = NULL,
+        csm::WarningList *warnings = NULL) const;
+    //> This is one of two overloaded methods.  This method takes
+    //  an input image coordinate for efficiency.  Even more efficiency can
+    //  be obtained by using the computeAllSensorPartials method.
+    //
+    //  This method returns the partial derivatives of line and sample
+    //  (in pixels per the applicable model parameter units), respectively,
+    //  with respect to the model parameter given by index at the given
+    //  groundPt (x,y,z in ECEF meters).
+    //
+    //  The imagePt, corresponding to the groundPt, is given so that it does
+    //  not need to be computed by the method.  Results are unpredictable if
+    //  the imagePt provided does not correspond to the result of calling the
+    //  groundToImage method with the given groundPt.
+    //
+    //  Implementations with iterative algorithms (typically ground-to-image
+    //  calls) will use desiredPrecision, in meters, as the convergence
+    //  criterion, otherwise it will be ignored.
+    //
+    //  If a non-NULL achievedPrecision argument is received, it will be
+    //  populated with the highest actual precision, in meters, achieved by
+    //  iterative algorithms and 0.0 for deterministic algorithms.
+    //
+    //  If a non-NULL warnings argument is received, it will be populated
+    //  as applicable.
+    //<
+
+    virtual std::vector<csm::RasterGM::SensorPartials> computeAllSensorPartials(
+        const csm::EcefCoord &groundPt, csm::param::Set pSet = csm::param::VALID,
+        double desiredPrecision = 0.001, double *achievedPrecision = NULL,
+        csm::WarningList *warnings = NULL) const;
+    //> This is one of two overloaded methods.  This method takes only
+    //  the necessary inputs.  Some effieciency can be obtained by using the
+    //  other method.
+    //
+    //  This method returns the partial derivatives of line and sample
+    //  (in pixels per the applicable model parameter units), respectively,
+    //  with respect to to each of the desired model parameters at the given
+    //  groundPt (x,y,z in ECEF meters).  Desired model parameters are
+    //  indicated by the given pSet.
+    //
+    //  Implementations with iterative algorithms (typically ground-to-image
+    //  calls) will use desiredPrecision, in meters, as the convergence
+    //  criterion, otherwise it will be ignored.
+    //
+    //  If a non-NULL achievedPrecision argument is received, it will be
+    //  populated with the highest actual precision, in meters, achieved by
+    //  iterative algorithms and 0.0 for deterministic algorithms.
+    //
+    //  If a non-NULL warnings argument is received, it will be populated
+    //  as applicable.
+    //
+    //  The value returned is a vector of pairs with line and sample partials
+    //  for one model parameter in each pair.  The indices of the
+    //  corresponding model parameters can be found by calling the
+    //  getParameterSetIndices method for the given pSet.
+    //
+    //  Derived models may wish to implement this directly for efficiency,
+    //  but an implementation is provided here that calls the
+    //  computeSensorPartials method for each desired parameter index.
+    //<
+
+    virtual std::vector<csm::RasterGM::SensorPartials> computeAllSensorPartials(
+        const csm::ImageCoord &imagePt, const csm::EcefCoord &groundPt,
+        csm::param::Set pSet = csm::param::VALID, double desiredPrecision = 0.001,
+        double *achievedPrecision = NULL,
+        csm::WarningList *warnings = NULL) const;
+    //> This is one of two overloaded methods.  This method takes
+    //  an input image coordinate for efficiency.
+    //
+    //  This method returns the partial derivatives of line and sample
+    //  (in pixels per the applicable model parameter units), respectively,
+    //  with respect to to each of the desired model parameters at the given
+    //  groundPt (x,y,z in ECEF meters).  Desired model parameters are
+    //  indicated by the given pSet.
+    //
+    //  The imagePt, corresponding to the groundPt, is given so that it does
+    //  not need to be computed by the method.  Results are unpredictable if
+    //  the imagePt provided does not correspond to the result of calling the
+    //  groundToImage method with the given groundPt.
+    //
+    //  Implementations with iterative algorithms (typically ground-to-image
+    //  calls) will use desiredPrecision, in meters, as the convergence
+    //  criterion, otherwise it will be ignored.
+    //
+    //  If a non-NULL achievedPrecision argument is received, it will be
+    //  populated with the highest actual precision, in meters, achieved by
+    //  iterative algorithms and 0.0 for deterministic algorithms.
+    //
+    //  If a non-NULL warnings argument is received, it will be populated
+    //  as applicable.
+    //
+    //  The value returned is a vector of pairs with line and sample partials
+    //  for one model parameter in each pair.  The indices of the
+    //  corresponding model parameters can be found by calling the
+    //  getParameterSetIndices method for the given pSet.
+    //
+    //  Derived models may wish to implement this directly for efficiency,
+    //  but an implementation is provided here that calls the
+    //  computeSensorPartials method for each desired parameter index.
+    //<
+
+    virtual std::vector<double> computeGroundPartials(
+        const csm::EcefCoord &groundPt) const;
+    //> This method returns the partial derivatives of line and sample
+    //  (in pixels per meter) with respect to the given groundPt
+    //  (x,y,z in ECEF meters).
+    //
+    //  The value returned is a vector with six elements as follows:
+    //
+    //-  [0] = line wrt x
+    //-  [1] = line wrt y
+    //-  [2] = line wrt z
+    //-  [3] = sample wrt x
+    //-  [4] = sample wrt y
+    //-  [5] = sample wrt z
+    //<
+
+    virtual const csm::CorrelationModel &getCorrelationModel() const;
+    //> This method returns a reference to a CorrelationModel.
+    //  The CorrelationModel is used to determine the correlation between
+    //  the model parameters of different models of the same type.
+    //  These correlations are used to establish the "a priori" cross-covariance
+    //  between images. While some applications (such as generation of a
+    //  replacement sensor model) may wish to call this method directly,
+    //  it is reccommended that the inherited method
+    //  GeometricModel::getCrossCovarianceMatrix() be called instead.
+    //<
+
+    virtual std::vector<double> getUnmodeledCrossCovariance(
+        const csm::ImageCoord &pt1, const csm::ImageCoord &pt2) const;
+    //> This method returns the 2x2 line and sample cross covariance
+    //  (in pixels squared) between the given imagePt1 and imagePt2 for any
+    //  model error not accounted for by the model parameters.  The error is
+    //  reported as the four terms of a 2x2 matrix, returned as a 4 element
+    //  vector.
+    //<
+
+    virtual csm::EcefCoord getReferencePoint() const;
+    //> This method returns the ground point indicating the general
+    //  location of the image.
+    //<
+
+    virtual void setReferencePoint(const csm::EcefCoord &groundPt);
+    //> This method sets the ground point indicating the general location
+    //  of the image.
+    //<
+
+    //---
+    // Sensor Model Parameters
+    //---
+    virtual int getNumParameters() const;
+    //> This method returns the number of adjustable parameters.
+    //<
+
+    virtual std::string getParameterName(int index) const;
+    //> This method returns the name for the adjustable parameter
+    //  indicated by the given index.
+    //
+    //  If the index is out of range, a csm::Error may be thrown.
+    //<
+
+    virtual std::string getParameterUnits(int index) const;
+    //> This method returns the units for the adjustable parameter
+    //  indicated by the given index.  This string is intended for human
+    //  consumption, not automated analysis.  Preferred unit names are:
+    //
+    //-    meters                "m"
+    //-    centimeters           "cm"
+    //-    millimeters           "mm"
+    //-    micrometers           "um"
+    //-    nanometers            "nm"
+    //-    kilometers            "km"
+    //-    inches-US             "inch"
+    //-    feet-US               "ft"
+    //-    statute miles         "mi"
+    //-    nautical miles        "nmi"
+    //-
+    //-    radians               "rad"
+    //-    microradians          "urad"
+    //-    decimal degrees       "deg"
+    //-    arc seconds           "arcsec"
+    //-    arc minutes           "arcmin"
+    //-
+    //-    seconds               "sec"
+    //-    minutes               "min"
+    //-    hours                 "hr"
+    //-
+    //-    steradian             "sterad"
+    //-
+    //-    none                  "unitless"
+    //-
+    //-    lines per second      "lines/sec"
+    //-    samples per second    "samples/sec"
+    //-    frames per second     "frames/sec"
+    //-
+    //-    watts                 "watt"
+    //-
+    //-    degrees Kelvin        "K"
+    //-
+    //-    gram                  "g"
+    //-    kilogram              "kg"
+    //-    pound - US            "lb"
+    //-
+    //-    hertz                 "hz"
+    //-    megahertz             "mhz"
+    //-    gigahertz             "ghz"
+    //
+    //  Units may be combined with "/" or "." to indicate division or
+    //  multiplication.  The caret symbol "^" can be used to indicate
+    //  exponentiation.  Thus "m.m" and "m^2" are the same and indicate
+    //  square meters.  The return "m/sec^2" indicates an acceleration in
+    //  meters per second per second.
+    //
+    //  Derived classes may choose to return additional unit names, as
+    //  required.
+    //<
+
+    virtual bool hasShareableParameters() const;
+    //> This method returns true if there exists at least one adjustable
+    //  parameter on the model that is shareable.  See the
+    //  isParameterShareable() method.  This method should return false if
+    //  all calls to isParameterShareable() return false.
+    //<
+
+    virtual bool isParameterShareable(int index) const;
+    //> This method returns a flag to indicate whether or not the adjustable
+    //  parameter referenced by index is shareable across models.
+    //<
+
+    virtual csm::SharingCriteria getParameterSharingCriteria(int index) const;
+    //> This method returns characteristics to indicate how the adjustable
+    //  parameter referenced by index is shareable across models.
+    //<
+
+    virtual double getParameterValue(int index) const;
+    //> This method returns the value of the adjustable parameter
+    //  referenced by the given index.
+    //<
+
+    virtual void setParameterValue(int index, double value);
+    //> This method sets the value for the adjustable parameter referenced by
+    //  the given index.
+    //<
+
+    virtual csm::param::Type getParameterType(int index) const;
+    //> This method returns the type of the adjustable parameter
+    //  referenced by the given index.
+    //<
+
+    virtual void setParameterType(int index, csm::param::Type pType);
+    //> This method sets the type of the adjustable parameter
+    //  reference by the given index.
+    //<
+
+    //---
+    // Uncertainty Propagation
+    //---
+    virtual double getParameterCovariance(int index1, int index2) const;
+    //> This method returns the covariance between the parameters
+    //  referenced by index1 and index2.  Variance of a single parameter
+    //  is indicated by specifying the samve value for index1 and index2.
+    //<
+
+    virtual void setParameterCovariance(int index1, int index2,
+                                        double covariance);
+    //> This method is used to set the covariance between the parameters
+    //  referenced by index1 and index2.  Variance of a single parameter
+    //  is indicated by specifying the samve value for index1 and index2.
+    //<
+
+    //---
+    // Error Correction
+    //---
+    virtual int getNumGeometricCorrectionSwitches() const;
+    //> This method returns the number of geometric correction switches
+    //  implemented for the current model.
+    //<
+
+    virtual std::string getGeometricCorrectionName(int index) const;
+    //> This method returns the name for the geometric correction switch
+    //  referenced by the given index.
+    //<
+
+    virtual void setGeometricCorrectionSwitch(int index, bool value,
+                                              csm::param::Type pType);
+    //> This method is used to enable/disable the geometric correction switch
+    //  referenced by the given index.
+    //<
+
+    virtual bool getGeometricCorrectionSwitch(int index) const;
+    //> This method returns the value of the geometric correction switch
+    //  referenced by the given index.
+    //<
+
+    virtual std::vector<double> getCrossCovarianceMatrix(
+        const csm::GeometricModel &comparisonModel,
+        csm::param::Set pSet = csm::param::VALID,
+        const csm::GeometricModel::GeometricModelList &otherModels =
+            csm::GeometricModel::GeometricModelList()) const;
+    //> This method returns a matrix containing the elements of the error
+    //  cross covariance between this model and a given second model
+    //  (comparisonModel).  The set of cross covariance elements returned is
+    //  indicated by pSet, which, by default, is all VALID parameters.
+    //
+    //  If comparisonModel is the same as this model, the covariance for
+    //  this model will be returned.  It is equivalent to calling
+    //  getParameterCovariance() for the same set of elements.  Note that
+    //  even if the cross covariance for a particular model type is always
+    //  zero, the covariance for this model must still be supported.
+    //
+    //  The otherModels list contains all of the models in the current
+    //  photogrammetric process; some cross-covariance implementations are
+    //  influenced by other models.  It can be omitted if it is not needed
+    //  by any models being used.
+    //
+    //  The returned vector will logically be a two-dimensional matrix of
+    //  covariances, though for simplicity it is stored in a one-dimensional
+    //  vector (STL has no two-dimensional structure).  The height (number of
+    //  rows) of this matrix is the number of parameters on the current model,
+    //  and the width (number of columns) is the number of parameters on
+    //  the comparison model.  Thus, the covariance between p1 on this model
+    //  and p2 on the comparison model is found in index (N*p1 + p2)
+    //  in the returned vector.  N is the size of the vector returned by
+    //  getParameterSetIndices() on the comparison model for the given pSet).
+    //
+    //  Note that cross covariance is often zero.  Non-zero cross covariance
+    //  can occur for models created from the same sensor (or different
+    //  sensors on the same platform).  While cross covariances can result
+    //  from a bundle adjustment involving multiple models, no mechanism
+    //  currently exists within csm to "set" the cross covariance between
+    //  models.  It should thus be assumed that the returned cross covariance
+    //  reflects the "un-adjusted" state of the models.
+    //<
+
+    virtual csm::Version getVersion() const;
+    //> This method returns the version of the model code.  The Version
+    //  object can be compared to other Version objects with its comparison
+    //  operators.  Not to be confused with the CSM API version.
+    //<
+
+    virtual std::string getModelName() const;
+    //> This method returns a string identifying the name of the model.
+    //<
+
+    virtual std::string getPedigree() const;
+    //> This method returns a string that identifies the sensor,
+    //  the model type, its mode of acquisition and processing path.
+    //  For example, an optical sensor model or a cubic rational polynomial
+    //  model created from the same sensor's support data would produce
+    //  different pedigrees for each case.
+    //<
+
+    //---
+    // Basic collection information
+    //---
+    virtual std::string getImageIdentifier() const;
+    //> This method returns an identifier to uniquely indicate the imaging
+    //  operation associated with this model.
+    //  This is the primary identifier of the model.
+    //
+    //  This method may return an empty string if the ID is unknown.
+    //<
+
+    virtual void setImageIdentifier(const std::string &imageId,
+                                    csm::WarningList *warnings = NULL);
+    //> This method sets an identifier to uniquely indicate the imaging
+    //  operation associated with this model.  Typically used for models
+    //  whose initialization does not produce an adequate identifier.
+    //
+    //  If a non-NULL warnings argument is received, it will be populated
+    //  as applicable.
+    //<
+
+    virtual std::string getSensorIdentifier() const;
+    //> This method returns an identifier to indicate the specific sensor
+    //  that was used to acquire the image.  This ID must be unique among
+    //  sensors for a given model name.  It is used to determine parameter
+    //  correlation and sharing.  Equivalent to camera or mission ID.
+    //
+    //  This method may return an empty string if the sensor ID is unknown.
+    //<
+
+    virtual std::string getPlatformIdentifier() const;
+    //> This method returns an identifier to indicate the specific platform
+    //  that was used to acquire the image.  This ID must unique among
+    //  platforms for a given model name.  It is used to determine parameter
+    //  correlation sharing.  Equivalent to vehicle or aircraft tail number.
+    //
+    //  This method may return an empty string if the platform ID is unknown.
+    //<
+
+    virtual std::string getCollectionIdentifier() const;
+    //> This method returns an identifer to indicate a collection activity
+    //  common to a set of images.  This ID must be unique among collection
+    //  activities for a given model name.  It is used to determine parameter
+    //  correlation and sharing.
+    //<
+
+    virtual std::string getTrajectoryIdentifier() const;
+    //> This method returns an identifier to indicate a trajectory common
+    //  to a set of images.  This ID must be unique among trajectories
+    //  for a given model name.  It is used to determine parameter
+    //  correlation and sharing.
+    //<
+
+    virtual std::string getSensorType() const;
+    //> This method returns a description of the sensor type (EO, IR, SAR,
+    //  etc).  See csm.h for a list of common types.  Should return
+    //  CSM_SENSOR_TYPE_UNKNOWN if the sensor type is unknown.
+    //<
+
+    virtual std::string getSensorMode() const;
+    //> This method returns a description of the sensor mode (FRAME,
+    //  PUSHBROOM, SPOT, SCAN, etc).  See csm.h for a list of common modes.
+    //  Should return CSM_SENSOR_MODE_UNKNOWN if the sensor mode is unknown.
+    //<
+
+    virtual std::string getReferenceDateAndTime() const;
+    //> This method returns an approximate date and time at which the
+    //  image was taken.  The returned string follows the ISO 8601 standard.
+    //
+    //-    Precision   Format           Example
+    //-    year        yyyy             "1961"
+    //-    month       yyyymm           "196104"
+    //-    day         yyyymmdd         "19610420"
+    //-    hour        yyyymmddThh      "19610420T20"
+    //-    minute      yyyymmddThhmm    "19610420T2000"
+    //-    second      yyyymmddThhmmss  "19610420T200000"
+    //<
+
+    //---
+    // Sensor Model State
+    //---
+    // virtual std::string setModelState(std::string stateString) const;
+    //> This method returns a string containing the data to exactly recreate
+    //  the current model.  It can be used to restore this model to a
+    //  previous state with the replaceModelState method or create a new
+    //  model object that is identical to this model.
+    //  The string could potentially be saved to a file for later use.
+    //  An empty string is returned if it is not possible to save the
+    //  current state.
+    //<
+
+    virtual csm::Ellipsoid getEllipsoid() const;
+    //> This method returns the planetary ellipsoid.
+    //<
+
+    virtual void setEllipsoid(const csm::Ellipsoid &ellipsoid);
+    //> This method sets the planetary ellipsoid.
+    //<
+
+ protected:
+  csm::RasterGM *m_camera;
+};
+
+#endif  // INCLUDE_USGSCSM_USGSASTROPROJECTEDSENSORMODEL_H_
diff --git a/src/UsgsAstroPlugin.cpp b/src/UsgsAstroPlugin.cpp
index ba24543c7b35b086583b0b4d0de821a464b7e43c..81f26c5b6a23d0173289e37f03eb99077a987f8d 100644
--- a/src/UsgsAstroPlugin.cpp
+++ b/src/UsgsAstroPlugin.cpp
@@ -23,16 +23,18 @@ IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISI
 OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/
 
 #include "UsgsAstroPlugin.h"
+#include "UsgsAstroPluginSupport.h"
 
 #include "UsgsAstroFrameSensorModel.h"
 #include "UsgsAstroLsSensorModel.h"
-#include "UsgsAstroProjectedLsSensorModel.h"
+#include "UsgsAstroProjectedSensorModel.h"
 #include "UsgsAstroPushFrameSensorModel.h"
 #include "UsgsAstroSarSensorModel.h"
 
 #include <algorithm>
 #include <cstdlib>
 #include <fstream>
+#include <stdexcept>
 #include <string>
 
 #include <csm/Error.h>
@@ -64,7 +66,7 @@ using json = nlohmann::json;
 const std::string UsgsAstroPlugin::_PLUGIN_NAME = "UsgsAstroPluginCSM";
 const std::string UsgsAstroPlugin::_MANUFACTURER_NAME = "UsgsAstrogeology";
 const std::string UsgsAstroPlugin::_RELEASE_DATE = "20190222";
-const int UsgsAstroPlugin::_N_SENSOR_MODELS = 4;
+const int UsgsAstroPlugin::_N_SENSOR_MODELS = 5;
 
 // Static Instance of itself
 const UsgsAstroPlugin UsgsAstroPlugin::m_registeredPlugin;
@@ -134,7 +136,7 @@ std::string UsgsAstroPlugin::getModelName(size_t modelIndex) const {
   std::vector<std::string> supportedModelNames = {
       UsgsAstroFrameSensorModel::_SENSOR_MODEL_NAME,
       UsgsAstroLsSensorModel::_SENSOR_MODEL_NAME,
-      UsgsAstroProjectedLsSensorModel::_SENSOR_MODEL_NAME,
+      UsgsAstroProjectedSensorModel::_SENSOR_MODEL_NAME,
       UsgsAstroSarSensorModel::_SENSOR_MODEL_NAME,
       UsgsAstroPushFrameSensorModel::_SENSOR_MODEL_NAME};
   MESSAGE_LOG(spdlog::level::debug, "Get Model Name: {}. Used index: {}",
@@ -327,76 +329,19 @@ csm::Model *UsgsAstroPlugin::constructModelFromISD(
   MESSAGE_LOG(spdlog::level::info, "Running constructModelFromISD");
   std::string stringIsd = loadImageSupportData(imageSupportDataOriginal);
 
-  MESSAGE_LOG(spdlog::level::trace, "ISD String: {}", stringIsd);
-  if (modelName == UsgsAstroFrameSensorModel::_SENSOR_MODEL_NAME) {
-    UsgsAstroFrameSensorModel *model = new UsgsAstroFrameSensorModel();
+  csm::Model *model = getUsgsCsmModel(stringIsd, modelName, warnings);
+
+  // Try to get the projected model, if not return the the unprojected model
+  try {
+    UsgsAstroProjectedSensorModel *projModel = new UsgsAstroProjectedSensorModel();
     try {
-      MESSAGE_LOG(spdlog::level::debug, "Trying to construct a UsgsAstroFrameSensorModel");
-      model->replaceModelState(
-          model->constructStateFromIsd(stringIsd, warnings));
+      MESSAGE_LOG(spdlog::level::debug, "Trying to construct a UsgsAstroProjectedSensorModel");
+      projModel->replaceModelState(
+          projModel->constructStateFromIsd(stringIsd, warnings));
       MESSAGE_LOG(spdlog::level::debug, "Constructed model: {}", modelName);
+      return projModel;
     } catch (std::exception &e) {
-      delete model;
-      csm::Error::ErrorType aErrorType =
-          csm::Error::SENSOR_MODEL_NOT_CONSTRUCTIBLE;
-      std::string aMessage = "Could not construct model [";
-      aMessage += modelName;
-      aMessage += "] with error [";
-      aMessage += e.what();
-      aMessage += "]";
-      MESSAGE_LOG(spdlog::level::err, aMessage);
-      std::string aFunction = "UsgsAstroPlugin::constructModelFromISD()";
-      throw csm::Error(aErrorType, aMessage, aFunction);
-    }
-    return model;
-  } else if (modelName == UsgsAstroLsSensorModel::_SENSOR_MODEL_NAME) {
-    UsgsAstroLsSensorModel *model = new UsgsAstroLsSensorModel();
-    try {
-      MESSAGE_LOG(spdlog::level::debug, "Trying to construct a UsgsAstroLsSensorModel");
-      model->replaceModelState(
-          model->constructStateFromIsd(stringIsd, warnings));
-    } catch (std::exception &e) {
-      delete model;
-      csm::Error::ErrorType aErrorType =
-          csm::Error::SENSOR_MODEL_NOT_CONSTRUCTIBLE;
-      std::string aMessage = "Could not construct model [";
-      aMessage += modelName;
-      aMessage += "] with error [";
-      aMessage += e.what();
-      aMessage += "]";
-      std::string aFunction = "UsgsAstroPlugin::constructModelFromISD()";
-      MESSAGE_LOG(spdlog::level::err, aMessage);
-      throw csm::Error(aErrorType, aMessage, aFunction);
-    }
-    return model;
-  } else if (modelName == UsgsAstroProjectedLsSensorModel::_SENSOR_MODEL_NAME) {
-    UsgsAstroProjectedLsSensorModel *model = new UsgsAstroProjectedLsSensorModel();
-    try {
-      MESSAGE_LOG(spdlog::level::debug, "Trying to construct a UsgsAstroProjectedLsSensorModel");
-      model->replaceModelState(
-          model->constructStateFromIsd(stringIsd, warnings));
-    } catch (std::exception &e) {
-      delete model;
-      csm::Error::ErrorType aErrorType =
-          csm::Error::SENSOR_MODEL_NOT_CONSTRUCTIBLE;
-      std::string aMessage = "Could not construct model [";
-      aMessage += modelName;
-      aMessage += "] with error [";
-      aMessage += e.what();
-      aMessage += "]";
-      std::string aFunction = "UsgsAstroPlugin::constructModelFromISD()";
-      MESSAGE_LOG(spdlog::level::err, aMessage);
-      throw csm::Error(aErrorType, aMessage, aFunction);
-    }
-    return model;
-  } else if (modelName == UsgsAstroSarSensorModel::_SENSOR_MODEL_NAME) {
-    UsgsAstroSarSensorModel *model = new UsgsAstroSarSensorModel();
-    MESSAGE_LOG(spdlog::level::debug, "Trying to construct a UsgsAstroSarSensorModel");
-    try {
-      model->replaceModelState(
-          model->constructStateFromIsd(stringIsd, warnings));
-    } catch (std::exception &e) {
-      delete model;
+      delete projModel;
       csm::Error::ErrorType aErrorType =
           csm::Error::SENSOR_MODEL_NOT_CONSTRUCTIBLE;
       std::string aMessage = "Could not construct model [";
@@ -404,37 +349,13 @@ csm::Model *UsgsAstroPlugin::constructModelFromISD(
       aMessage += "] with error [";
       aMessage += e.what();
       aMessage += "]";
-      std::string aFunction = "UsgsAstroPlugin::constructModelFromISD()";
       MESSAGE_LOG(spdlog::level::err, aMessage);
+      std::string aFunction = "UsgsAstroPlugin::getUsgsCsmModel()";
       throw csm::Error(aErrorType, aMessage, aFunction);
     }
+  } catch(std::exception &e) {
+    MESSAGE_LOG(spdlog::level::info, "Failed to make projected model with error: \n{}", e.what());
     return model;
-  } else if (modelName == UsgsAstroPushFrameSensorModel::_SENSOR_MODEL_NAME) {
-    UsgsAstroPushFrameSensorModel *model = new UsgsAstroPushFrameSensorModel();
-    MESSAGE_LOG(spdlog::level::debug, "Trying to construct a UsgsAstroPushFrameSensorModel");
-    try {
-      model->replaceModelState(
-          model->constructStateFromIsd(stringIsd, warnings));
-    } catch (std::exception &e) {
-      delete model;
-      csm::Error::ErrorType aErrorType =
-          csm::Error::SENSOR_MODEL_NOT_CONSTRUCTIBLE;
-      std::string aMessage = "Could not construct model [";
-      aMessage += modelName;
-      aMessage += "] with error [";
-      aMessage += e.what();
-      aMessage += "]";
-      std::string aFunction = "UsgsAstroPlugin::constructModelFromISD()";
-      MESSAGE_LOG(spdlog::level::err, aMessage);
-      throw csm::Error(aErrorType, aMessage, aFunction);
-    }
-    return model;
-  } else {
-    csm::Error::ErrorType aErrorType = csm::Error::SENSOR_MODEL_NOT_SUPPORTED;
-    std::string aMessage = "Model [" + modelName + "] not supported: ";
-    std::string aFunction = "UsgsAstroPlugin::constructModelFromISD()";
-    MESSAGE_LOG(spdlog::level::err, aMessage);
-    throw csm::Error(aErrorType, aMessage, aFunction);
   }
 }
 
@@ -455,9 +376,9 @@ csm::Model *UsgsAstroPlugin::constructModelFromState(
     UsgsAstroLsSensorModel *model = new UsgsAstroLsSensorModel();
     model->replaceModelState(modelState);
     return model;
-  } else if (modelName == UsgsAstroProjectedLsSensorModel::_SENSOR_MODEL_NAME) {
-    MESSAGE_LOG(spdlog::level::debug, "Constructing a UsgsAstroProjectedLsSensorModel");
-    UsgsAstroProjectedLsSensorModel *model = new UsgsAstroProjectedLsSensorModel();
+  } else if (modelName == UsgsAstroProjectedSensorModel::_SENSOR_MODEL_NAME) {
+    MESSAGE_LOG(spdlog::level::debug, "Constructing a UsgsAstroProjectedSensorModel");
+    UsgsAstroProjectedSensorModel *model = new UsgsAstroProjectedSensorModel();
     model->replaceModelState(modelState);
     return model;
   }else if (modelName == UsgsAstroSarSensorModel::_SENSOR_MODEL_NAME) {
diff --git a/src/UsgsAstroPluginSupport.cpp b/src/UsgsAstroPluginSupport.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e4d25859fd8b2b300da6928f63bec02239996e7e
--- /dev/null
+++ b/src/UsgsAstroPluginSupport.cpp
@@ -0,0 +1,97 @@
+#include "UsgsAstroPluginSupport.h"
+
+#include "UsgsAstroFrameSensorModel.h"
+#include "UsgsAstroLsSensorModel.h"
+#include "UsgsAstroPushFrameSensorModel.h"
+#include "UsgsAstroSarSensorModel.h"
+
+#include <fstream>
+
+#include "spdlog/spdlog.h"
+
+#include <nlohmann/json.hpp>
+using json = nlohmann::json;
+
+csm::RasterGM *getUsgsCsmModel(
+    const std::string &stringIsd, const std::string &modelName,
+    csm::WarningList *warnings) {
+  if (modelName == UsgsAstroFrameSensorModel::_SENSOR_MODEL_NAME) {
+    UsgsAstroFrameSensorModel *model = new UsgsAstroFrameSensorModel();
+    try {
+      model->replaceModelState(
+          model->constructStateFromIsd(stringIsd, warnings));
+    } catch (std::exception &e) {
+      delete model;
+      csm::Error::ErrorType aErrorType =
+          csm::Error::SENSOR_MODEL_NOT_CONSTRUCTIBLE;
+      std::string aMessage = "Could not construct model [";
+      aMessage += modelName;
+      aMessage += "] with error [";
+      aMessage += e.what();
+      aMessage += "]";
+      std::string aFunction = "UsgsAstroPluginSupport::getUsgsCsmModel()";
+      throw csm::Error(aErrorType, aMessage, aFunction);
+    }
+    return model;
+  } else if (modelName == UsgsAstroLsSensorModel::_SENSOR_MODEL_NAME) {
+    UsgsAstroLsSensorModel *model = new UsgsAstroLsSensorModel();
+    try {
+      model->replaceModelState(
+          model->constructStateFromIsd(stringIsd, warnings));
+    } catch (std::exception &e) {
+      delete model;
+      csm::Error::ErrorType aErrorType =
+          csm::Error::SENSOR_MODEL_NOT_CONSTRUCTIBLE;
+      std::string aMessage = "Could not construct model [";
+      aMessage += modelName;
+      aMessage += "] with error [";
+      aMessage += e.what();
+      aMessage += "]";
+      std::string aFunction = "UsgsAstroPluginSupport::getUsgsCsmModel()";
+      throw csm::Error(aErrorType, aMessage, aFunction);
+    }
+    return model;
+  } else if (modelName == UsgsAstroSarSensorModel::_SENSOR_MODEL_NAME) {
+    UsgsAstroSarSensorModel *model = new UsgsAstroSarSensorModel();
+    try {
+      model->replaceModelState(
+          model->constructStateFromIsd(stringIsd, warnings));
+    } catch (std::exception &e) {
+      delete model;
+      csm::Error::ErrorType aErrorType =
+          csm::Error::SENSOR_MODEL_NOT_CONSTRUCTIBLE;
+      std::string aMessage = "Could not construct model [";
+      aMessage += modelName;
+      aMessage += "] with error [";
+      aMessage += e.what();
+      aMessage += "]";
+      std::string aFunction = "UsgsAstroPluginSupport::getUsgsCsmModel()";
+      throw csm::Error(aErrorType, aMessage, aFunction);
+    }
+    return model;
+  } else if (modelName == UsgsAstroPushFrameSensorModel::_SENSOR_MODEL_NAME) {
+    UsgsAstroPushFrameSensorModel *model = new UsgsAstroPushFrameSensorModel();
+    try {
+      model->replaceModelState(
+          model->constructStateFromIsd(stringIsd, warnings));
+    } catch (std::exception &e) {
+      delete model;
+      csm::Error::ErrorType aErrorType =
+          csm::Error::SENSOR_MODEL_NOT_CONSTRUCTIBLE;
+      std::string aMessage = "Could not construct model [";
+      aMessage += modelName;
+      aMessage += "] with error [";
+      aMessage += e.what();
+      aMessage += "]";
+      std::string aFunction = "UsgsAstroPluginSupport::getUsgsCsmModel()";
+      throw csm::Error(aErrorType, aMessage, aFunction);
+    }
+    return model;
+  } else {
+    csm::Error::ErrorType aErrorType = csm::Error::SENSOR_MODEL_NOT_SUPPORTED;
+    std::string aMessage = "Model [" + modelName + "] not supported: ";
+    std::string aFunction = "UsgsAstroPluginSupport::getUsgsCsmModel()";
+    throw csm::Error(aErrorType, aMessage, aFunction);
+  }
+  
+}
\ No newline at end of file
diff --git a/src/UsgsAstroProjectedLsSensorModel.cpp b/src/UsgsAstroProjectedLsSensorModel.cpp
deleted file mode 100644
index d2eb6395dbacb0a42c7354834852adea82fb6d8c..0000000000000000000000000000000000000000
--- a/src/UsgsAstroProjectedLsSensorModel.cpp
+++ /dev/null
@@ -1,398 +0,0 @@
-/** Copyright  © 2017-2022 BAE Systems Information and Electronic Systems Integration Inc.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted
-provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this list of conditions
-and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of
-conditions and the following disclaimer in the documentation and/or other materials provided
-with the distribution.
-
-3. Neither the name of the copyright holder nor the names of its contributors may be used to
-endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
-FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
-CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
-IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
-OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/
-
-#include "UsgsAstroProjectedLsSensorModel.h"
-#include "Utilities.h"
-
-#include <proj.h>
-
-#include <Error.h>
-#include <nlohmann/json.hpp>
-
-#include "ale/Util.h"
-
-#define MESSAGE_LOG(...)        \
-  if (m_logger) {               \
-    m_logger->log(__VA_ARGS__); \
-  }
-
-using json = nlohmann::json;
-
-const std::string UsgsAstroProjectedLsSensorModel::_SENSOR_MODEL_NAME =
-    "USGS_ASTRO_PROJECTED_LINE_SCANNER_SENSOR_MODEL";
-
-const std::string UsgsAstroProjectedLsSensorModel::_STATE_KEYWORD[] = {
-    "m_modelName",
-    "m_imageIdentifier",
-    "m_sensorName",
-    "m_nLines",
-    "m_nSamples",
-    "m_platformFlag",
-    "m_intTimeLines",
-    "m_intTimeStartTimes",
-    "m_intTimes",
-    "m_startingEphemerisTime",
-    "m_centerEphemerisTime",
-    "m_detectorSampleSumming",
-    "m_detectorSampleSumming",
-    "m_startingDetectorSample",
-    "m_startingDetectorLine",
-    "m_ikCode",
-    "m_focalLength",
-    "m_zDirection",
-    "m_distortionType",
-    "m_opticalDistCoeffs",
-    "m_iTransS",
-    "m_iTransL",
-    "m_detectorSampleOrigin",
-    "m_detectorLineOrigin",
-    "m_majorAxis",
-    "m_minorAxis",
-    "m_platformIdentifier",
-    "m_sensorIdentifier",
-    "m_minElevation",
-    "m_maxElevation",
-    "m_dtEphem",
-    "m_t0Ephem",
-    "m_dtQuat",
-    "m_t0Quat",
-    "m_numPositions",
-    "m_numQuaternions",
-    "m_positions",
-    "m_velocities",
-    "m_quaternions",
-    "m_currentParameterValue",
-    "m_parameterType",
-    "m_referencePointXyz",
-    "m_sunPosition",
-    "m_sunVelocity",
-    "m_gsd",
-    "m_flyingHeight",
-    "m_halfSwath",
-    "m_halfTime",
-    "m_covariance",
-    "m_geoTransform",
-    "m_projString",
-};
-
-//***************************************************************************
-// UsgsAstroLineScannerSensorModel::replaceModelState
-//***************************************************************************
-void UsgsAstroProjectedLsSensorModel::replaceModelState(const std::string& stateString) {
-  reset();
-
-  auto j = stateAsJson(stateString);
-  m_geoTransform = j["m_geoTransform"].get<std::vector<double>>();
-  m_projString = j["m_projString"];
-  MESSAGE_LOG(
-      spdlog::level::trace,
-      "m_geoTransform: {} "
-      "m_projString: {} ",
-      j["m_geoTransform"].dump(), j["m_projString"].dump());
-  UsgsAstroLsSensorModel::replaceModelState(stateString);
-}
-
-//***************************************************************************
-// UsgsAstroLineScannerSensorModel::getModelNameFromModelState
-//***************************************************************************
-std::string UsgsAstroProjectedLsSensorModel::getModelNameFromModelState(
-    const std::string& model_state) {
-  // Parse the string to JSON
-  auto j = stateAsJson(model_state);
-  // If model name cannot be determined, return a blank string
-  std::string model_name;
-
-  if (j.find("m_modelName") != j.end()) {
-    model_name = j["m_modelName"];
-  } else {
-    csm::Error::ErrorType aErrorType = csm::Error::INVALID_SENSOR_MODEL_STATE;
-    std::string aMessage = "No 'm_modelName' key in the model state object.";
-    std::string aFunction = "UsgsAstroProjectedLsPlugin::getModelNameFromModelState";
-    csm::Error csmErr(aErrorType, aMessage, aFunction);
-    throw(csmErr);
-  }
-  if (model_name != _SENSOR_MODEL_NAME) {
-    csm::Error::ErrorType aErrorType = csm::Error::SENSOR_MODEL_NOT_SUPPORTED;
-    std::string aMessage = "Sensor model not supported.";
-    std::string aFunction = "UsgsAstroProjectedLsPlugin::getModelNameFromModelState()";
-    csm::Error csmErr(aErrorType, aMessage, aFunction);
-    throw(csmErr);
-  }
-  return model_name;
-}
-
-//***************************************************************************
-// UsgsAstroLineScannerSensorModel::getModelState
-//***************************************************************************
-std::string UsgsAstroProjectedLsSensorModel::getModelState() const {
-  auto state = stateAsJson(UsgsAstroLsSensorModel::getModelState());
-  state["m_geoTransform"] = m_geoTransform;
-  state["m_projString"] = m_projString;
-  MESSAGE_LOG(
-      spdlog::level::trace,
-      "m_geoTransform: {}, {}, {}, {}, {}, {} "
-      "m_projString: {} ",
-      m_geoTransform[0], 
-      m_geoTransform[1], 
-      m_geoTransform[2], 
-      m_geoTransform[3], 
-      m_geoTransform[4], 
-      m_geoTransform[5], 
-      m_projString);
-  // Use dump(2) to avoid creating the model string as a single long line
-  std::string stateString = getModelName() + "\n" + state.dump(2);
-  return stateString;
-}
-
-//***************************************************************************
-// UsgsAstroLineScannerSensorModel::reset
-//***************************************************************************
-void UsgsAstroProjectedLsSensorModel::reset() {
-  MESSAGE_LOG(spdlog::level::debug, "Running reset()");
-
-  m_geoTransform = std::vector<double>(6, 0.0);
-  m_projString = "";
-}
-
-//*****************************************************************************
-// UsgsAstroProjectedLsSensorModel Constructor
-//*****************************************************************************
-UsgsAstroProjectedLsSensorModel::UsgsAstroProjectedLsSensorModel() : UsgsAstroLsSensorModel() {}
-
-//*****************************************************************************
-// UsgsAstroProjectedLsSensorModel Destructor
-//*****************************************************************************
-UsgsAstroProjectedLsSensorModel::~UsgsAstroProjectedLsSensorModel() {}
-
-//---------------------------------------------------------------------------
-// Core Photogrammetry
-//---------------------------------------------------------------------------
-
-//***************************************************************************
-// UsgsAstroLsSensorModel::groundToImage
-//***************************************************************************
-csm::ImageCoord UsgsAstroProjectedLsSensorModel::groundToImage(
-    const csm::EcefCoord &ground_pt, double desired_precision,
-    double *achieved_precision, csm::WarningList *warnings) const {
-  
-  PJ_CONTEXT *C = proj_context_create();
-
-  /* Create a projection. */
-  PJ *isdProj = proj_create(C, (m_projString + " +type=crs").c_str());
-  if (0 == isdProj) {
-    MESSAGE_LOG(
-        spdlog::level::debug,
-        "Failed to create isd transformation object");
-    return csm::ImageCoord(0, 0);
-  }
-
-  /* Create the geocentric projection for our target */
-  std::string radius_a = "+a=" + std::to_string(m_majorAxis);
-  std::string radius_b = "+b=" + std::to_string(m_minorAxis);
-  std::string projString = "+proj=geocent " + radius_a + " " + radius_b + " +type=crs";
-  PJ *ecefProj = proj_create(C, projString.c_str());
-  if (0 == ecefProj) {
-    MESSAGE_LOG(
-        spdlog::level::debug,
-        "Failed to create geocent transformation object");
-    return csm::ImageCoord(0, 0);
-  }
-
-  // Compute the transformation from our ISIS projection to ecef
-  PJ *isdProj2ecefProj = proj_create_crs_to_crs_from_pj(C, isdProj, ecefProj, 0, 0);
-  PJ_COORD c_in;
-  c_in.xyz.x = ground_pt.x;
-  c_in.xyz.y = ground_pt.y;
-  c_in.xyz.z = ground_pt.z;
-  MESSAGE_LOG(
-      spdlog::level::info,
-      "Ground point {}, {}, {}",
-      c_in.xyz.x, c_in.xyz.y, c_in.xyz.z);
-  PJ_COORD c_out = proj_trans(isdProj2ecefProj, PJ_INV, c_in);
-  MESSAGE_LOG(
-      spdlog::level::info,
-      "Meters {}, {}",
-      c_out.xyz.x, c_out.xyz.y);
-  std::vector<double> lineSampleCoord = meterToPixel(c_out.xyz.x, c_out.xyz.y, m_geoTransform);
-  csm::ImageCoord imagePt(lineSampleCoord[0], lineSampleCoord[1]);
-  MESSAGE_LOG(
-      spdlog::level::info,
-      "groundToImage result of ({}, {})",
-      imagePt.line, imagePt.samp);
-  return imagePt;
-}
-
-//***************************************************************************
-// UsgsAstroLsSensorModel::groundToImage
-//***************************************************************************
-csm::ImageCoordCovar UsgsAstroProjectedLsSensorModel::groundToImage(
-    const csm::EcefCoordCovar &groundPt, double desired_precision,
-    double *achieved_precision, csm::WarningList *warnings) const {
-  csm::ImageCoordCovar imageCoordCovar = UsgsAstroLsSensorModel::groundToImage(groundPt, desired_precision, achieved_precision, warnings);
-  csm::ImageCoord projImagePt = groundToImage(groundPt);
-
-  imageCoordCovar.line = projImagePt.line;
-  imageCoordCovar.samp = projImagePt.samp;
-  return imageCoordCovar;
-}
-
-//***************************************************************************
-// UsgsAstroProjectedLsSensorModel::imageToGround
-//***************************************************************************
-csm::EcefCoord UsgsAstroProjectedLsSensorModel::imageToGround(
-    const csm::ImageCoord& image_pt, double height, double desired_precision,
-    double* achieved_precision, csm::WarningList* warnings) const {
-  MESSAGE_LOG(
-      spdlog::level::info,
-      "Computing imageToGround for {}, {}, {}, with desired precision {}",
-      image_pt.line, image_pt.samp, height, desired_precision);
-
-  double x = 0, y = 0, z = 0;
-  double meterLine, meterSamp;
-  std::vector<double> meterCoord = pixelToMeter(image_pt.line, image_pt.samp, m_geoTransform);
-  meterLine = meterCoord[0];
-  meterSamp = meterCoord[1];
-  PJ_CONTEXT *C = proj_context_create();
-
-  /* Create a projection. */
-  PJ *isdProj = proj_create(C, (m_projString + " +type=crs").c_str());
-  if (0 == isdProj) {
-    MESSAGE_LOG(
-        spdlog::level::debug,
-        "Failed to create isd transformation object");
-    return csm::EcefCoord(x, y, z);
-  }
-
-  /* Create the geocentric projection for our target */
-  std::string radius_a = "+a=" + std::to_string(m_majorAxis);
-  std::string radius_b = "+b=" + std::to_string(m_minorAxis);
-  std::string projString = "+proj=geocent " + radius_a + " " + radius_b + " +type=crs";
-  PJ *ecefProj = proj_create(C, projString.c_str());
-  if (0 == ecefProj) {
-    MESSAGE_LOG(
-        spdlog::level::debug,
-        "Failed to create geocent transformation object");
-    return csm::EcefCoord(x, y, z);
-  }
-
-  // Compute the transformation from our ISIS projection to ecef
-  PJ *isdProj2ecefProj = proj_create_crs_to_crs_from_pj(C, isdProj, ecefProj, 0, 0);
-  PJ_COORD c_in;
-  c_in.xy.x = meterSamp;
-  c_in.xy.y = meterLine;
-  PJ_COORD c_out = proj_trans(isdProj2ecefProj, PJ_FWD, c_in);
-  x = c_out.xyz.x, y = c_out.xyz.y, z = c_out.xyz.z;
-  MESSAGE_LOG(
-      spdlog::level::info,
-      "imageToGround result {} {} {}",
-      x, y, z);
-  return csm::EcefCoord(x, y, z);
-}
-
-//***************************************************************************
-// UsgsAstroProjectedLsSensorModel::imageToGround
-//***************************************************************************
-csm::EcefCoordCovar UsgsAstroProjectedLsSensorModel::imageToGround(
-    const csm::ImageCoordCovar& image_pt, double height, double heightVariance,
-    double desired_precision, double* achieved_precision,
-    csm::WarningList* warnings) const {
-  csm::EcefCoord groundCoord = imageToGround(image_pt, height);
-  csm::ImageCoord cameraImagePt = UsgsAstroLsSensorModel::groundToImage(groundCoord);
-  csm::ImageCoordCovar cameraImagePtCovar(cameraImagePt.line, cameraImagePt.samp);
-  
-  return UsgsAstroLsSensorModel::imageToGround(cameraImagePtCovar, height, heightVariance, desired_precision, achieved_precision, warnings);
-}
-
-//***************************************************************************
-// UsgsAstroProjectedLsSensorModel::imageToProximateImagingLocus
-//***************************************************************************
-csm::EcefLocus UsgsAstroProjectedLsSensorModel::imageToProximateImagingLocus(
-    const csm::ImageCoord& image_pt, const csm::EcefCoord& ground_pt,
-    double desired_precision, double* achieved_precision,
-    csm::WarningList* warnings) const {
-  csm::EcefCoord projGround = imageToGround(image_pt, 0);
-  csm::ImageCoord cameraImagePt = UsgsAstroLsSensorModel::groundToImage(projGround);
-
-  return UsgsAstroLsSensorModel::imageToProximateImagingLocus(cameraImagePt, ground_pt, desired_precision, achieved_precision, warnings);
-}
-
-//***************************************************************************
-// UsgsAstroProjectedLsSensorModel::imageToRemoteImagingLocus
-//***************************************************************************
-csm::EcefLocus UsgsAstroProjectedLsSensorModel::imageToRemoteImagingLocus(
-    const csm::ImageCoord& image_pt, double desired_precision,
-    double* achieved_precision, csm::WarningList* warnings) const {
-  // Go from proj x, y to latlon then ground to image
-  // Convert imagept to camera imagept
-  csm::EcefCoord groundCoord = imageToGround(image_pt, 0);
-  csm::ImageCoord cameraImagePt = UsgsAstroLsSensorModel::groundToImage(groundCoord);
-
-  return UsgsAstroLsSensorModel::imageToRemoteImagingLocus(cameraImagePt, desired_precision, achieved_precision, warnings);
-}
-
-//---------------------------------------------------------------------------
-// Sensor Model Information
-//---------------------------------------------------------------------------
-
-//***************************************************************************
-// UsgsAstroProjectedLsSensorModel::getPedigree
-//***************************************************************************
-std::string UsgsAstroProjectedLsSensorModel::getPedigree() const {
-  return "USGS_PROJECTED_LINE_SCANNER";
-}
-
-//***************************************************************************
-// UsgsAstroProjectedLsSensorModel::getSensorModelName
-//***************************************************************************
-std::string UsgsAstroProjectedLsSensorModel::getModelName() const {
-  return UsgsAstroProjectedLsSensorModel::_SENSOR_MODEL_NAME;
-}
-
-//***************************************************************************
-// UsgsAstroProjectedLsSensorModel::getVersion
-//***************************************************************************
-csm::Version UsgsAstroProjectedLsSensorModel::getVersion() const {
-  return csm::Version(1, 0, 0);
-}
-
-//***************************************************************************
-// UsgsAstroLineScannerSensorModel::constructStateFromIsd
-//***************************************************************************
-std::string UsgsAstroProjectedLsSensorModel::constructStateFromIsd(
-    const std::string imageSupportData, csm::WarningList* warnings) {
-  // return UsgsAstroLsSensorModel::constructStateFromIsd(imageSupportData, warnings);
-  json lsState = json::parse(UsgsAstroLsSensorModel::constructStateFromIsd(imageSupportData, warnings));
-  json state = json::parse(imageSupportData);
-
-  lsState["m_geoTransform"] = ale::getGeoTransform(state);
-  lsState["m_projString"] = ale::getProjection(state);
-  MESSAGE_LOG(
-      spdlog::level::trace,
-      "m_geoTransform: {} "
-      "m_projString: {} ",
-      lsState["m_geoTransform"].dump(), lsState["m_projString"].dump());
-  // The state data will still be updated when a sensor model is created since
-  // some state data is not in the ISD and requires a SM to compute them.
-  return lsState.dump();
-}
diff --git a/src/UsgsAstroProjectedSensorModel.cpp b/src/UsgsAstroProjectedSensorModel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6ee91166e5eebfe6d0c9e384622bd181132f6536
--- /dev/null
+++ b/src/UsgsAstroProjectedSensorModel.cpp
@@ -0,0 +1,824 @@
+/** Copyright  © 2017-2022 BAE Systems Information and Electronic Systems Integration Inc.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted
+provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions
+and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of
+conditions and the following disclaimer in the documentation and/or other materials provided
+with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to
+endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. **/
+
+#include "UsgsAstroProjectedSensorModel.h"
+#include "UsgsAstroPluginSupport.h"
+#include "Utilities.h"
+
+#include <proj.h>
+
+#include <Error.h>
+#include <nlohmann/json.hpp>
+
+#include "ale/Util.h"
+
+#define MESSAGE_LOG(...)        \
+  if (m_logger) {               \
+    m_logger->log(__VA_ARGS__); \
+  }
+
+using json = nlohmann::json;
+
+const std::string UsgsAstroProjectedSensorModel::_SENSOR_MODEL_NAME =
+    "USGS_ASTRO_PROJECTED_SENSOR_MODEL";
+
+const std::string UsgsAstroProjectedSensorModel::_STATE_KEYWORD[] = {
+    "m_modelName",
+    "m_imageIdentifier",
+    "m_sensorName",
+    "m_nLines",
+    "m_nSamples",
+    "m_platformFlag",
+    "m_intTimeLines",
+    "m_intTimeStartTimes",
+    "m_intTimes",
+    "m_startingEphemerisTime",
+    "m_centerEphemerisTime",
+    "m_detectorSampleSumming",
+    "m_detectorSampleSumming",
+    "m_startingDetectorSample",
+    "m_startingDetectorLine",
+    "m_ikCode",
+    "m_focalLength",
+    "m_zDirection",
+    "m_distortionType",
+    "m_opticalDistCoeffs",
+    "m_iTransS",
+    "m_iTransL",
+    "m_detectorSampleOrigin",
+    "m_detectorLineOrigin",
+    "m_majorAxis",
+    "m_minorAxis",
+    "m_platformIdentifier",
+    "m_sensorIdentifier",
+    "m_minElevation",
+    "m_maxElevation",
+    "m_dtEphem",
+    "m_t0Ephem",
+    "m_dtQuat",
+    "m_t0Quat",
+    "m_numPositions",
+    "m_numQuaternions",
+    "m_positions",
+    "m_velocities",
+    "m_quaternions",
+    "m_currentParameterValue",
+    "m_parameterType",
+    "m_referencePointXyz",
+    "m_sunPosition",
+    "m_sunVelocity",
+    "m_gsd",
+    "m_flyingHeight",
+    "m_halfSwath",
+    "m_halfTime",
+    "m_covariance",
+    "m_geoTransform",
+    "m_projString",
+};
+
+//***************************************************************************
+// UsgsAstroLineScannerSensorModel::replaceModelState
+//***************************************************************************
+void UsgsAstroProjectedSensorModel::replaceModelState(const std::string& stateString) {
+  reset();
+
+  auto j = stateAsJson(stateString);
+  m_majorAxis = j["m_majorAxis"];
+  m_minorAxis = j["m_minorAxis"];
+  m_geoTransform = j["m_geoTransform"].get<std::vector<double>>();
+  m_projString = j["m_projString"];
+  MESSAGE_LOG(
+      spdlog::level::trace,
+      "m_majorAxis: {} "
+      "m_minorAxis: {} "
+      "m_geoTransform: {} "
+      "m_projString: {} ",
+      j["m_majorAxis"].dump(), j["m_minorAxis"].dump(), j["m_geoTransform"].dump(), j["m_projString"].dump());
+  m_camera->replaceModelState(stateString);
+}
+
+//***************************************************************************
+// UsgsAstroLineScannerSensorModel::getModelNameFromModelState
+//***************************************************************************
+std::string UsgsAstroProjectedSensorModel::getModelNameFromModelState(
+    const std::string& model_state) {
+  // Parse the string to JSON
+  auto j = stateAsJson(model_state);
+  // If model name cannot be determined, return a blank string
+  std::string model_name;
+
+  if (j.find("m_modelName") != j.end()) {
+    model_name = j["m_modelName"];
+  } else {
+    csm::Error::ErrorType aErrorType = csm::Error::INVALID_SENSOR_MODEL_STATE;
+    std::string aMessage = "No 'm_modelName' key in the model state object.";
+    std::string aFunction = "UsgsAstroProjectedPlugin::getModelNameFromModelState";
+    csm::Error csmErr(aErrorType, aMessage, aFunction);
+    throw(csmErr);
+  }
+  if (model_name != _SENSOR_MODEL_NAME) {
+    csm::Error::ErrorType aErrorType = csm::Error::SENSOR_MODEL_NOT_SUPPORTED;
+    std::string aMessage = "Sensor model not supported.";
+    std::string aFunction = "UsgsAstroProjectedPlugin::getModelNameFromModelState()";
+    csm::Error csmErr(aErrorType, aMessage, aFunction);
+    throw(csmErr);
+  }
+  return model_name;
+}
+
+//***************************************************************************
+// UsgsAstroLineScannerSensorModel::getModelState
+//***************************************************************************
+std::string UsgsAstroProjectedSensorModel::getModelState() const {
+  auto state = stateAsJson(m_camera->getModelState());
+  state["m_modelName"] = _SENSOR_MODEL_NAME;
+  state["m_majorAxis"] = m_majorAxis;
+  state["m_minorAxis"] = m_minorAxis;
+  state["m_geoTransform"] = m_geoTransform;
+  state["m_projString"] = m_projString;
+  MESSAGE_LOG(
+      spdlog::level::trace,
+      "m_majorAxis: {} "
+      "m_minorAxis: {} "
+      "m_geoTransform: {}, {}, {}, {}, {}, {} "
+      "m_projString: {} ",
+      m_majorAxis,
+      m_minorAxis,
+      m_geoTransform[0], 
+      m_geoTransform[1], 
+      m_geoTransform[2], 
+      m_geoTransform[3], 
+      m_geoTransform[4], 
+      m_geoTransform[5], 
+      m_projString);
+  // Use dump(2) to avoid creating the model string as a single long line
+  std::string stateString = getModelName() + "\n" + state.dump(2);
+  return stateString;
+}
+
+//***************************************************************************
+// UsgsAstroLineScannerSensorModel::reset
+//***************************************************************************
+void UsgsAstroProjectedSensorModel::reset() {
+  MESSAGE_LOG(spdlog::level::debug, "Running reset()");
+
+  m_majorAxis = 3400000.0;
+  m_minorAxis = 3350000.0;
+  m_geoTransform = std::vector<double>(6, 0.0);
+  m_projString = "";
+}
+
+//*****************************************************************************
+// UsgsAstroProjectedSensorModel Constructor
+//*****************************************************************************
+UsgsAstroProjectedSensorModel::UsgsAstroProjectedSensorModel() {}
+
+//*****************************************************************************
+// UsgsAstroProjectedSensorModel Destructor
+//*****************************************************************************
+UsgsAstroProjectedSensorModel::~UsgsAstroProjectedSensorModel() {}
+
+//---------------------------------------------------------------------------
+// Core Photogrammetry
+//---------------------------------------------------------------------------
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::groundToImage
+//***************************************************************************
+csm::ImageCoord UsgsAstroProjectedSensorModel::groundToImage(
+    const csm::EcefCoord &ground_pt, double desired_precision,
+    double *achieved_precision, csm::WarningList *warnings) const {
+  
+  PJ_CONTEXT *C = proj_context_create();
+
+  /* Create a projection. */
+  PJ *isdProj = proj_create(C, (m_projString + " +type=crs").c_str());
+  if (0 == isdProj) {
+    MESSAGE_LOG(
+        spdlog::level::debug,
+        "Failed to create isd transformation object");
+    return csm::ImageCoord(0, 0);
+  }
+
+  /* Create the geocentric projection for our target */
+  std::string radius_a = "+a=" + std::to_string(m_majorAxis);
+  std::string radius_b = "+b=" + std::to_string(m_minorAxis);
+  std::string projString = "+proj=geocent " + radius_a + " " + radius_b + " +type=crs";
+  PJ *ecefProj = proj_create(C, projString.c_str());
+  if (0 == ecefProj) {
+    MESSAGE_LOG(
+        spdlog::level::debug,
+        "Failed to create geocent transformation object");
+    return csm::ImageCoord(0, 0);
+  }
+
+  // Compute the transformation from our ISIS projection to ecef
+  PJ *isdProj2ecefProj = proj_create_crs_to_crs_from_pj(C, isdProj, ecefProj, 0, 0);
+  PJ_COORD c_in;
+  c_in.xyz.x = ground_pt.x;
+  c_in.xyz.y = ground_pt.y;
+  c_in.xyz.z = ground_pt.z;
+  MESSAGE_LOG(
+      spdlog::level::info,
+      "Ground point {}, {}, {}",
+      c_in.xyz.x, c_in.xyz.y, c_in.xyz.z);
+  PJ_COORD c_out = proj_trans(isdProj2ecefProj, PJ_INV, c_in);
+  MESSAGE_LOG(
+      spdlog::level::info,
+      "Meters {}, {}",
+      c_out.xyz.x, c_out.xyz.y);
+  std::vector<double> lineSampleCoord = meterToPixel(c_out.xyz.x, c_out.xyz.y, m_geoTransform);
+  csm::ImageCoord imagePt(lineSampleCoord[0], lineSampleCoord[1]);
+  MESSAGE_LOG(
+      spdlog::level::info,
+      "groundToImage result of ({}, {})",
+      imagePt.line, imagePt.samp);
+  return imagePt;
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::groundToImage
+//***************************************************************************
+csm::ImageCoordCovar UsgsAstroProjectedSensorModel::groundToImage(
+    const csm::EcefCoordCovar &groundPt, double desired_precision,
+    double *achieved_precision, csm::WarningList *warnings) const {
+  csm::ImageCoordCovar imageCoordCovar = m_camera->groundToImage(groundPt, desired_precision, achieved_precision, warnings);
+  csm::ImageCoord projImagePt = groundToImage(groundPt);
+
+  imageCoordCovar.line = projImagePt.line;
+  imageCoordCovar.samp = projImagePt.samp;
+  return imageCoordCovar;
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::imageToGround
+//***************************************************************************
+csm::EcefCoord UsgsAstroProjectedSensorModel::imageToGround(
+    const csm::ImageCoord& image_pt, double height, double desired_precision,
+    double* achieved_precision, csm::WarningList* warnings) const {
+  MESSAGE_LOG(
+      spdlog::level::info,
+      "Computing imageToGround for {}, {}, {}, with desired precision {}",
+      image_pt.line, image_pt.samp, height, desired_precision);
+
+  double x = 0, y = 0, z = 0;
+  double meterLine, meterSamp;
+  std::vector<double> meterCoord = pixelToMeter(image_pt.line, image_pt.samp, m_geoTransform);
+  meterLine = meterCoord[0];
+  meterSamp = meterCoord[1];
+  PJ_CONTEXT *C = proj_context_create();
+
+  /* Create a projection. */
+  PJ *isdProj = proj_create(C, (m_projString + " +type=crs").c_str());
+  if (0 == isdProj) {
+    MESSAGE_LOG(
+        spdlog::level::debug,
+        "Failed to create isd transformation object");
+    return csm::EcefCoord(x, y, z);
+  }
+
+  /* Create the geocentric projection for our target */
+  std::string radius_a = "+a=" + std::to_string(m_majorAxis);
+  std::string radius_b = "+b=" + std::to_string(m_minorAxis);
+  std::string projString = "+proj=geocent " + radius_a + " " + radius_b + " +type=crs";
+  PJ *ecefProj = proj_create(C, projString.c_str());
+  if (0 == ecefProj) {
+    MESSAGE_LOG(
+        spdlog::level::debug,
+        "Failed to create geocent transformation object");
+    return csm::EcefCoord(x, y, z);
+  }
+
+  // Compute the transformation from our ISIS projection to ecef
+  PJ *isdProj2ecefProj = proj_create_crs_to_crs_from_pj(C, isdProj, ecefProj, 0, 0);
+  PJ_COORD c_in;
+  c_in.xy.x = meterSamp;
+  c_in.xy.y = meterLine;
+  PJ_COORD c_out = proj_trans(isdProj2ecefProj, PJ_FWD, c_in);
+  x = c_out.xyz.x, y = c_out.xyz.y, z = c_out.xyz.z;
+  MESSAGE_LOG(
+      spdlog::level::info,
+      "imageToGround result {} {} {}",
+      x, y, z);
+  return csm::EcefCoord(x, y, z);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::imageToGround
+//***************************************************************************
+csm::EcefCoordCovar UsgsAstroProjectedSensorModel::imageToGround(
+    const csm::ImageCoordCovar& image_pt, double height, double heightVariance,
+    double desired_precision, double* achieved_precision,
+    csm::WarningList* warnings) const {
+  csm::EcefCoord groundCoord = imageToGround(image_pt, height);
+  csm::ImageCoord cameraImagePt = m_camera->groundToImage(groundCoord);
+  csm::ImageCoordCovar cameraImagePtCovar(cameraImagePt.line, cameraImagePt.samp);
+  
+  return m_camera->imageToGround(cameraImagePtCovar, height, heightVariance, desired_precision, achieved_precision, warnings);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::imageToProximateImagingLocus
+//***************************************************************************
+csm::EcefLocus UsgsAstroProjectedSensorModel::imageToProximateImagingLocus(
+    const csm::ImageCoord& image_pt, const csm::EcefCoord& ground_pt,
+    double desired_precision, double* achieved_precision,
+    csm::WarningList* warnings) const {
+  csm::EcefCoord projGround = imageToGround(image_pt, 0);
+  csm::ImageCoord cameraImagePt = m_camera->groundToImage(projGround);
+
+  return m_camera->imageToProximateImagingLocus(cameraImagePt, ground_pt, desired_precision, achieved_precision, warnings);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::imageToRemoteImagingLocus
+//***************************************************************************
+csm::EcefLocus UsgsAstroProjectedSensorModel::imageToRemoteImagingLocus(
+    const csm::ImageCoord& image_pt, double desired_precision,
+    double* achieved_precision, csm::WarningList* warnings) const {
+  // Go from proj x, y to latlon then ground to image
+  // Convert imagept to camera imagept
+  csm::EcefCoord groundCoord = imageToGround(image_pt, 0);
+  csm::ImageCoord cameraImagePt = m_camera->groundToImage(groundCoord);
+
+  return m_camera->imageToRemoteImagingLocus(cameraImagePt, desired_precision, achieved_precision, warnings);
+}
+
+//---------------------------------------------------------------------------
+// Uncertainty Propagation
+//---------------------------------------------------------------------------
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::computeGroundPartials
+//***************************************************************************
+std::vector<double> UsgsAstroProjectedSensorModel::computeGroundPartials(
+    const csm::EcefCoord& ground_pt) const {
+  return m_camera->computeGroundPartials(ground_pt);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::computeSensorPartials
+//***************************************************************************
+csm::RasterGM::SensorPartials UsgsAstroProjectedSensorModel::computeSensorPartials(
+    int index, const csm::EcefCoord &groundPt, double desiredPrecision,
+    double *achievedPrecision, csm::WarningList *warnings) const {
+  MESSAGE_LOG(
+      spdlog::level::debug,
+      "Calculating computeSensorPartials for ground point {}, {}, {} with "
+      "desired precision {}",
+      groundPt.x, groundPt.y, groundPt.z, desiredPrecision)
+
+  csm::ImageCoord imagePt = m_camera->groundToImage(groundPt, desiredPrecision, achievedPrecision);
+
+  return m_camera->computeSensorPartials(index, imagePt, groundPt, desiredPrecision,
+                                         achievedPrecision, warnings);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::computeSensorPartials
+//***************************************************************************
+csm::RasterGM::SensorPartials UsgsAstroProjectedSensorModel::computeSensorPartials(
+    int index, const csm::ImageCoord &imagePt, const csm::EcefCoord &groundPt,
+    double desiredPrecision, double *achievedPrecision,
+    csm::WarningList *warnings) const {
+  return m_camera->computeSensorPartials(index, imagePt, groundPt, desiredPrecision,
+                                         achievedPrecision, warnings);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::computeAllSensorPartials
+//***************************************************************************
+std::vector<csm::RasterGM::SensorPartials>
+UsgsAstroProjectedSensorModel::computeAllSensorPartials(
+    const csm::EcefCoord& ground_pt, csm::param::Set pSet,
+    double desired_precision, double* achieved_precision,
+    csm::WarningList* warnings) const {
+
+  MESSAGE_LOG(
+      spdlog::level::info,
+      "Computing computeAllSensorPartials for ground point {}, {}, {} with "
+      "desired precision {}",
+      ground_pt.x, ground_pt.y, ground_pt.z, desired_precision)
+  csm::ImageCoord image_pt =
+      m_camera->groundToImage(ground_pt, desired_precision, achieved_precision, warnings);
+
+  return computeAllSensorPartials(image_pt, ground_pt, pSet, desired_precision,
+                                  achieved_precision, warnings);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::computeAllSensorPartials
+//***************************************************************************
+std::vector<csm::RasterGM::SensorPartials>
+UsgsAstroProjectedSensorModel::computeAllSensorPartials(
+    const csm::ImageCoord& image_pt, const csm::EcefCoord& ground_pt,
+    csm::param::Set pSet, double desired_precision, double* achieved_precision,
+    csm::WarningList* warnings) const {
+
+  return m_camera->computeAllSensorPartials(image_pt, ground_pt, pSet, desired_precision,
+                                            achieved_precision, warnings);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getParameterCovariance
+//***************************************************************************
+double UsgsAstroProjectedSensorModel::getParameterCovariance(int index1,
+                                                      int index2) const {
+  return m_camera->getParameterCovariance(index1, index2);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::setParameterCovariance
+//***************************************************************************
+void UsgsAstroProjectedSensorModel::setParameterCovariance(int index1, int index2,
+                                                    double covariance) {
+  m_camera->setParameterCovariance(index1, index2, covariance);
+}
+
+//---------------------------------------------------------------------------
+// Time and Trajectory
+//---------------------------------------------------------------------------
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getTrajectoryIdentifier
+//***************************************************************************
+std::string UsgsAstroProjectedSensorModel::getTrajectoryIdentifier() const {
+  return m_camera->getTrajectoryIdentifier();
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getReferenceDateAndTime
+//***************************************************************************
+std::string UsgsAstroProjectedSensorModel::getReferenceDateAndTime() const {
+  return m_camera->getReferenceDateAndTime();
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getImageTime
+//***************************************************************************
+double UsgsAstroProjectedSensorModel::getImageTime(
+    const csm::ImageCoord& image_pt) const {
+  return m_camera->getImageTime(image_pt);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getSensorPosition
+//***************************************************************************
+csm::EcefCoord UsgsAstroProjectedSensorModel::getSensorPosition(
+    const csm::ImageCoord& imagePt) const {
+  csm::EcefCoord ground = imageToGround(imagePt, 0);
+  csm::ImageCoord cameraImagePt = m_camera->groundToImage(ground);
+
+  return getSensorPosition(m_camera->getImageTime(cameraImagePt));
+}
+
+//***************************************************************************
+// UsgsAstroLsSensorModel::getSensorPosition
+//***************************************************************************
+csm::EcefCoord UsgsAstroProjectedSensorModel::getSensorPosition(double time) const {
+  return m_camera->getSensorPosition(time);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getSensorVelocity
+//***************************************************************************
+csm::EcefVector UsgsAstroProjectedSensorModel::getSensorVelocity(
+    const csm::ImageCoord& imagePt) const {
+  csm::EcefCoord ground = imageToGround(imagePt, 0);
+  csm::ImageCoord cameraImagePt = m_camera->groundToImage(ground);
+
+  return getSensorVelocity(m_camera->getImageTime(cameraImagePt));
+}
+
+//***************************************************************************
+// UsgsAstroLsSensorModel::getSensorPosition
+//***************************************************************************
+csm::EcefVector UsgsAstroProjectedSensorModel::getSensorVelocity(double time) const {
+  return m_camera->getSensorVelocity(time);
+}
+
+//---------------------------------------------------------------------------
+// Sensor Model Parameters
+//---------------------------------------------------------------------------
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::setParameterValue
+//***************************************************************************
+void UsgsAstroProjectedSensorModel::setParameterValue(int index, double value) {
+  m_camera->setParameterValue(index, value);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getParameterValue
+//***************************************************************************
+double UsgsAstroProjectedSensorModel::getParameterValue(int index) const {
+  return m_camera->getParameterValue(index);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getParameterName
+//***************************************************************************
+std::string UsgsAstroProjectedSensorModel::getParameterName(int index) const {
+  return m_camera->getParameterName(index);
+}
+
+std::string UsgsAstroProjectedSensorModel::getParameterUnits(int index) const {
+  // All parameters are meters or scaled to meters
+  return m_camera->getParameterUnits(index);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getNumParameters
+//***************************************************************************
+int UsgsAstroProjectedSensorModel::getNumParameters() const {
+  return m_camera->getNumParameters();
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getParameterType
+//***************************************************************************
+csm::param::Type UsgsAstroProjectedSensorModel::getParameterType(int index) const {
+  return m_camera->getParameterType(index);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::setParameterType
+//***************************************************************************
+void UsgsAstroProjectedSensorModel::setParameterType(int index,
+                                              csm::param::Type pType) {
+  m_camera->setParameterType(index, pType);
+}
+
+//---------------------------------------------------------------------------
+// Sensor Model Information
+//---------------------------------------------------------------------------
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getPedigree
+//***************************************************************************
+std::string UsgsAstroProjectedSensorModel::getPedigree() const {
+  return "USGS_PROJECTED";
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getImageIdentifier
+//***************************************************************************
+std::string UsgsAstroProjectedSensorModel::getImageIdentifier() const {
+  return m_camera->getImageIdentifier();
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::setImageIdentifier
+//***************************************************************************
+void UsgsAstroProjectedSensorModel::setImageIdentifier(const std::string& imageId,
+                                                csm::WarningList* warnings) {
+  m_camera->setImageIdentifier(imageId, warnings);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getSensorIdentifier
+//***************************************************************************
+std::string UsgsAstroProjectedSensorModel::getSensorIdentifier() const {
+  return m_camera->getSensorIdentifier();
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getPlatformIdentifier
+//***************************************************************************
+std::string UsgsAstroProjectedSensorModel::getPlatformIdentifier() const {
+  return m_camera->getPlatformIdentifier();
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::setReferencePoint
+//***************************************************************************
+void UsgsAstroProjectedSensorModel::setReferencePoint(
+    const csm::EcefCoord& ground_pt) {
+  m_camera->setReferencePoint(ground_pt);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getReferencePoint
+//***************************************************************************
+csm::EcefCoord UsgsAstroProjectedSensorModel::getReferencePoint() const {
+  // Return ground point at image center
+  return m_camera->getReferencePoint();
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getSensorModelName
+//***************************************************************************
+std::string UsgsAstroProjectedSensorModel::getModelName() const {
+  return UsgsAstroProjectedSensorModel::_SENSOR_MODEL_NAME;
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getImageStart
+//***************************************************************************
+csm::ImageCoord UsgsAstroProjectedSensorModel::getImageStart() const {
+  return m_camera->getImageStart();
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getImageSize
+//***************************************************************************
+csm::ImageVector UsgsAstroProjectedSensorModel::getImageSize() const {
+  return m_camera->getImageSize();
+}
+
+//---------------------------------------------------------------------------
+//  Monoscopic Mensuration
+//---------------------------------------------------------------------------
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getValidHeightRange
+//***************************************************************************
+std::pair<double, double> UsgsAstroProjectedSensorModel::getValidHeightRange() const {
+  return m_camera->getValidHeightRange();
+}
+
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getValidImageRange
+//***************************************************************************
+std::pair<csm::ImageCoord, csm::ImageCoord>
+UsgsAstroProjectedSensorModel::getValidImageRange() const {
+  return m_camera->getValidImageRange();
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getIlluminationDirection
+//***************************************************************************
+csm::EcefVector UsgsAstroProjectedSensorModel::getIlluminationDirection(
+    const csm::EcefCoord& groundPt) const {
+  return m_camera->getIlluminationDirection(groundPt);
+}
+
+//---------------------------------------------------------------------------
+//  Error Correction
+//---------------------------------------------------------------------------
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getNumGeometricCorrectionSwitches
+//***************************************************************************
+int UsgsAstroProjectedSensorModel::getNumGeometricCorrectionSwitches() const {
+  return m_camera->getNumGeometricCorrectionSwitches();
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getGeometricCorrectionName
+//***************************************************************************
+std::string UsgsAstroProjectedSensorModel::getGeometricCorrectionName(
+    int index) const {
+  return m_camera->getGeometricCorrectionName(index);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::setGeometricCorrectionSwitch
+//***************************************************************************
+void UsgsAstroProjectedSensorModel::setGeometricCorrectionSwitch(
+    int index, bool value, csm::param::Type pType) {
+  m_camera->setGeometricCorrectionSwitch(index, value, pType);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getGeometricCorrectionSwitch
+//***************************************************************************
+bool UsgsAstroProjectedSensorModel::getGeometricCorrectionSwitch(int index) const {
+  return m_camera->getGeometricCorrectionSwitch(index);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getCrossCovarianceMatrix
+//***************************************************************************
+std::vector<double> UsgsAstroProjectedSensorModel::getCrossCovarianceMatrix(
+    const csm::GeometricModel& comparisonModel, csm::param::Set pSet,
+    const csm::GeometricModel::GeometricModelList& otherModels) const {
+  return m_camera->getCrossCovarianceMatrix(comparisonModel, pSet, otherModels);
+}
+
+//***************************************************************************
+// UsgsAstroLineScannerSensorModel::getCorrelationModel
+//***************************************************************************
+const csm::CorrelationModel& UsgsAstroProjectedSensorModel::getCorrelationModel()
+    const {
+  return m_camera->getCorrelationModel();
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getUnmodeledCrossCovariance
+//***************************************************************************
+std::vector<double> UsgsAstroProjectedSensorModel::getUnmodeledCrossCovariance(
+    const csm::ImageCoord& pt1, const csm::ImageCoord& pt2) const {
+  return m_camera->getUnmodeledCrossCovariance(pt1, pt2);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getCollectionIdentifier
+//***************************************************************************
+std::string UsgsAstroProjectedSensorModel::getCollectionIdentifier() const {
+  return m_camera->getCollectionIdentifier();
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::hasShareableParameters
+//***************************************************************************
+bool UsgsAstroProjectedSensorModel::hasShareableParameters() const {
+  return m_camera->hasShareableParameters();
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::isParameterShareable
+//***************************************************************************
+bool UsgsAstroProjectedSensorModel::isParameterShareable(int index) const {
+  // Parameter sharing is not supported for this sensor
+  return m_camera->isParameterShareable(index);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getParameterSharingCriteria
+//***************************************************************************
+csm::SharingCriteria UsgsAstroProjectedSensorModel::getParameterSharingCriteria(
+    int index) const {
+  return m_camera->getParameterSharingCriteria(index);
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getSensorType
+//***************************************************************************
+std::string UsgsAstroProjectedSensorModel::getSensorType() const {
+  return m_camera->getSensorType();
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getSensorMode
+//***************************************************************************
+std::string UsgsAstroProjectedSensorModel::getSensorMode() const {
+  return m_camera->getSensorMode();
+}
+
+//***************************************************************************
+// UsgsAstroProjectedSensorModel::getVersion
+//***************************************************************************
+csm::Version UsgsAstroProjectedSensorModel::getVersion() const {
+  return csm::Version(1, 0, 0);
+}
+
+//***************************************************************************
+// UsgsAstroLineScannerSensorModel::getEllipsoid
+//***************************************************************************
+csm::Ellipsoid UsgsAstroProjectedSensorModel::getEllipsoid() const {
+  return csm::Ellipsoid(m_majorAxis, m_minorAxis);
+}
+
+void UsgsAstroProjectedSensorModel::setEllipsoid(const csm::Ellipsoid& ellipsoid) {
+  m_majorAxis = ellipsoid.getSemiMajorRadius();
+  m_minorAxis = ellipsoid.getSemiMinorRadius();
+  // Needs to set cameras ellipsoid but RasterGM is not a SettableEllipsoid
+}
+
+//***************************************************************************
+// UsgsAstroLineScannerSensorModel::constructStateFromIsd
+//***************************************************************************
+std::string UsgsAstroProjectedSensorModel::constructStateFromIsd(
+    const std::string imageSupportData, csm::WarningList* warnings) {
+  json state = json::parse(imageSupportData);
+  std::string modelName = ale::getSensorModelName(state);
+
+  m_camera = getUsgsCsmModel(imageSupportData, modelName, warnings);
+  json projState = stateAsJson(m_camera->getModelState());
+
+  // Force update the modelName
+  projState["m_modelName"] = _SENSOR_MODEL_NAME;
+  projState["m_geoTransform"] = ale::getGeoTransform(state);
+  projState["m_projString"] = ale::getProjection(state);
+  MESSAGE_LOG(
+      spdlog::level::trace,
+      "m_geoTransform: {} "
+      "m_projString: {} ",
+      projState["m_geoTransform"].dump(), projState["m_projString"].dump());
+  // The state data will still be updated when a sensor model is created since
+  // some state data is not in the ISD and requires a SM to compute them.
+  return projState.dump();
+}
\ No newline at end of file
diff --git a/src/Utilities.cpp b/src/Utilities.cpp
index 5599c252995bd5b394eb387afc4f130a3d2ec690..dfcfae3b042c867b84c465664ad11b4176d316d4 100644
--- a/src/Utilities.cpp
+++ b/src/Utilities.cpp
@@ -1,6 +1,5 @@
 #include "Utilities.h"
 
-
 #include <Error.h>
 #include <cmath>
 #include <stack>