diff --git a/include/usgscsm/UsgsAstroLsSensorModel.h b/include/usgscsm/UsgsAstroLsSensorModel.h
index fcc304834da648562aa1df1750ae2068a654edc7..a4a82876328947a662ae28d96cd3d2c93e04f067 100644
--- a/include/usgscsm/UsgsAstroLsSensorModel.h
+++ b/include/usgscsm/UsgsAstroLsSensorModel.h
@@ -1018,15 +1018,6 @@ private:
       double&       z,
       int&          mode ) const;
 
-   // Computes the height above ellipsoid for an input ECF coordinate
-   void computeElevation (
-      const double& x,
-      const double& y,
-      const double& z,
-      double&       height,
-      double&       achieved_precision,
-      const double& desired_precision) const;
-
    // determines the sensor velocity accounting for parameter adjustments.
    void getAdjSensorPosVel(
       const double& time,
diff --git a/include/usgscsm/UsgsAstroSarSensorModel.h b/include/usgscsm/UsgsAstroSarSensorModel.h
index af79e94e9fdbec6924df844af61662858e7d19e6..98f436ac892671ab75c2641a16d8c52ded7f6cf4 100644
--- a/include/usgscsm/UsgsAstroSarSensorModel.h
+++ b/include/usgscsm/UsgsAstroSarSensorModel.h
@@ -210,7 +210,9 @@ class UsgsAstroSarSensorModel : public csm::RasterGM, virtual public csm::Settab
     double groundRangeToSlantRange(double groundRange, const std::vector<double> &coeffs) const;
 
     csm::EcefVector getSpacecraftPosition(double time) const;
+
     csm::EcefVector getSunPosition(const double imageTime) const;
+
     std::vector<double> getRangeCoefficients(double time) const;
 
     ////////////////////////////
diff --git a/include/usgscsm/Utilities.h b/include/usgscsm/Utilities.h
index b1d2abe9a4ccd0b3c253aca2d94835d2a2c0b521..4990a10d27b8860f5a4e600043d18d4d224fe356 100644
--- a/include/usgscsm/Utilities.h
+++ b/include/usgscsm/Utilities.h
@@ -85,6 +85,15 @@ double secantRoot(
     double epsilon = 1e-10,
     int maxIterations = 30);
 
+double computeEllipsoidElevation(
+    double x,
+    double y,
+    double z,
+    double semiMajor,
+    double semiMinor,
+    double desired_precision = 0.001,
+    double* achieved_precision = nullptr);
+
 // Vector operations
 csm::EcefVector operator*(double scalar, const csm::EcefVector &vec);
 csm::EcefVector operator*(const csm::EcefVector &vec, double scalar);
diff --git a/src/UsgsAstroLsSensorModel.cpp b/src/UsgsAstroLsSensorModel.cpp
index 93daa56117dac0262f846c34227c2b1bba47eea4..2c5618399cc6c1c31e4729b3cf313a223570dd88 100644
--- a/src/UsgsAstroLsSensorModel.cpp
+++ b/src/UsgsAstroLsSensorModel.cpp
@@ -1018,8 +1018,9 @@ csm::EcefLocus UsgsAstroLsSensorModel::imageToProximateImagingLocus(
    double z = ground_pt.z;
 
    // Elevation at input ground point
-   double height, aPrec;
-   computeElevation(x, y, z, height, aPrec, desired_precision);
+   double height = computeEllipsoidElevation(
+         x, y, z,
+         m_majorAxis, m_minorAxis, desired_precision);
 
    // Ground point on object ray with the same elevation
    csm::EcefCoord gp1 = imageToGround(
@@ -1050,10 +1051,11 @@ csm::EcefLocus UsgsAstroLsSensorModel::imageToProximateImagingLocus(
    gp2.y = gp1.y + scale * dy2;
    gp2.z = gp1.z + scale * dz2;
 
-   double hLocus;
-   computeElevation(gp2.x, gp2.y, gp2.z, hLocus, aPrec, desired_precision);
+   double hLocus = computeEllipsoidElevation(
+         gp2.x, gp2.y, gp2.z,
+         m_majorAxis, m_minorAxis, desired_precision);
    locus.point = imageToGround(
-      image_pt, hLocus, desired_precision, achieved_precision, warnings);
+         image_pt, hLocus, desired_precision, achieved_precision, warnings);
 
    locus.direction.x = dx2;
    locus.direction.y = dy2;
@@ -2051,76 +2053,6 @@ void UsgsAstroLsSensorModel::lightAberrationCorr(
                                "{} {} {}", dxl, dyl, dzl)
 }
 
-//***************************************************************************
-// UsgsAstroLsSensorModel::computeElevation
-//***************************************************************************
-void UsgsAstroLsSensorModel::computeElevation(
-   const double& x,
-   const double& y,
-   const double& z,
-   double&       height,
-   double&       achieved_precision,
-   const double& desired_precision) const
-{
-   MESSAGE_LOG(m_logger, "Calculating computeElevation for {} {} {}"
-                               "with desired precision {}",
-                              x, y, z, desired_precision)
-   // Compute elevation given xyz
-   // Requires semi-major-axis and eccentricity-square
-   const int MKTR = 10;
-   double ecc_sqr = 1.0 - m_minorAxis * m_minorAxis / m_majorAxis / m_majorAxis;
-   double ep2 = 1.0 - ecc_sqr;
-   double d2 = x * x + y * y;
-   double d = sqrt(d2);
-   double h = 0.0;
-   int ktr = 0;
-   double hPrev, r;
-
-   // Suited for points near equator
-   if (d >= z)
-   {
-      double tt, zz, n;
-      double tanPhi = z / d;
-      do
-      {
-         hPrev = h;
-         tt = tanPhi * tanPhi;
-         r = m_majorAxis / sqrt(1.0 + ep2 * tt);
-         zz = z + r * ecc_sqr * tanPhi;
-         n = r * sqrt(1.0 + tt);
-         h = sqrt(d2 + zz * zz) - n;
-         tanPhi = zz / d;
-         ktr++;
-      } while (MKTR > ktr && fabs(h - hPrev) > desired_precision);
-      MESSAGE_LOG(m_logger, "computeElevation: point is near equator")
-   }
-
-   // Suited for points near the poles
-   else
-   {
-      double cc, dd, nn;
-      double cotPhi = d / z;
-      do
-      {
-         hPrev = h;
-         cc = cotPhi * cotPhi;
-         r = m_majorAxis / sqrt(ep2 + cc);
-         dd = d - r * ecc_sqr * cotPhi;
-         nn = r * sqrt(1.0 + cc) * ep2;
-         h = sqrt(dd * dd + z * z) - nn;
-         cotPhi = dd / z;
-         ktr++;
-      } while (MKTR > ktr && fabs(h - hPrev) > desired_precision);
-      MESSAGE_LOG(m_logger, "computeElevation: point is near poles")
-   }
-
-   height = h;
-   achieved_precision = fabs(h - hPrev);
-   MESSAGE_LOG(m_logger, "computeElevation: height {} with achieved"
-                               "precision of {}",
-                                height, achieved_precision)
-}
-
 //***************************************************************************
 // UsgsAstroLsSensorModel::losEllipsoidIntersect
 //**************************************************************************
@@ -2173,7 +2105,7 @@ void UsgsAstroLsSensorModel::losEllipsoidIntersect(
    }
    double scale, scale1, h, slope;
    double sprev, hprev;
-   double aPrec, sTerm;
+   double sTerm;
    int ktr = 0;
 
    // Compute ground point vector
@@ -2187,7 +2119,7 @@ void UsgsAstroLsSensorModel::losEllipsoidIntersect(
    x = xc + scale * xl;
    y = yc + scale * yl;
    z = zc + scale * zl;
-   computeElevation(x, y, z, h, aPrec, desired_precision);
+   h = computeEllipsoidElevation(x, y, z, m_majorAxis, m_minorAxis, desired_precision);
    slope = -1;
 
    achieved_precision = fabs(height - h);
@@ -2515,9 +2447,10 @@ void UsgsAstroLsSensorModel::setLinearApproximation()
 
   csm::EcefCoord refPt = getReferencePoint();
 
-  double height, aPrec;
   double desired_precision = 0.01;
-  computeElevation(refPt.x, refPt.y, refPt.z, height, aPrec, desired_precision);
+  double height = computeEllipsoidElevation(
+        refPt.x, refPt.y, refPt.z,
+        m_majorAxis, m_minorAxis, desired_precision);
   if (std::isnan(height))
   {
     MESSAGE_LOG(m_logger, "setLinearApproximation: computeElevation of"
diff --git a/src/UsgsAstroSarSensorModel.cpp b/src/UsgsAstroSarSensorModel.cpp
index 7209180a4884883464679db7e25178f4a42fc634..fac1920fb32f307c813e9098bab258b643366f12 100644
--- a/src/UsgsAstroSarSensorModel.cpp
+++ b/src/UsgsAstroSarSensorModel.cpp
@@ -513,14 +513,29 @@ csm::EcefCoord UsgsAstroSarSensorModel::imageToGround(
   double inTrackComp = dot(spacecraftPosition, vHat);
 
   // Compute the substituted values
-  // TODO properly handle ellipsoid radii
-  double radiusSqr = m_majorAxis * m_majorAxis;
-  double alpha = (radiusSqr - slantRange * slantRange - positionMag * positionMag) / (2 * nadirComp);
-  // TODO use right/left look to determine +/-
-  double beta = -sqrt(slantRange * slantRange - alpha * alpha);
-  csm::EcefVector groundVec = alpha * tHat + beta * uHat + spacecraftPosition;
+  // Iterate to find proper radius value
+  double pointRadius = m_majorAxis + height;
+  double radiusSqr;
+  double pointHeight;
+  csm::EcefVector groundVec;
+  do {
+    radiusSqr = pointRadius * pointRadius;
+    double alpha = (radiusSqr - slantRange * slantRange - positionMag * positionMag) / (2 * nadirComp);
+    double beta = sqrt(slantRange * slantRange - alpha * alpha);
+    if (m_lookDirection == LEFT) {
+      beta *= -1;
+    }
+    groundVec = alpha * tHat + beta * uHat + spacecraftPosition;
+    pointHeight = computeEllipsoidElevation(
+        groundVec.x, groundVec.y, groundVec.z,
+        m_majorAxis, m_minorAxis);
+    pointRadius -= (pointHeight - height);
+  } while(fabs(pointHeight - height) > desiredPrecision);
+
 
-  return csm::EcefCoord(groundVec.x, groundVec.y, groundVec.z);
+  csm::EcefCoord groundPt(groundVec.x, groundVec.y, groundVec.z);
+
+  return groundPt;
 }
 
 csm::EcefCoordCovar UsgsAstroSarSensorModel::imageToGround(
@@ -611,8 +626,33 @@ csm::EcefLocus UsgsAstroSarSensorModel::imageToProximateImagingLocus(
     double* achievedPrecision,
     csm::WarningList* warnings) const
 {
-  return csm::EcefLocus(0.0, 0.0, 0.0,
-                        0.0, 0.0, 0.0);
+  // Compute the slant range
+  double time = m_startingEphemerisTime + (imagePt.line - 0.5) * m_exposureDuration;
+  double groundRange = imagePt.samp * m_scaledPixelWidth;
+  std::vector<double> coeffs = getRangeCoefficients(time);
+  double slantRange = groundRangeToSlantRange(groundRange, coeffs);
+
+  // Project the sensor to ground point vector onto the 0 doppler plane
+  // then compute the closest point at the slant range to that
+  csm::EcefVector spacecraftPosition = getSpacecraftPosition(time);
+  csm::EcefVector spacecraftVelocity = getSensorVelocity(time);
+  csm::EcefVector groundVec(groundPt.x, groundPt.y, groundPt.z);
+  csm::EcefVector lookVec = normalized(rejection(groundVec - spacecraftPosition, spacecraftVelocity));
+  csm::EcefVector closestVec = spacecraftPosition + slantRange * lookVec;
+
+
+  // Compute the tangent at the closest point
+  csm::EcefVector tangent;
+  if (m_lookDirection == LEFT) {
+    tangent = cross(spacecraftVelocity, lookVec);
+  }
+  else {
+    tangent = cross(lookVec, spacecraftVelocity);
+  }
+  tangent = normalized(tangent);
+
+  return csm::EcefLocus(closestVec.x, closestVec.y, closestVec.z,
+                        tangent.x,    tangent.y,    tangent.z);
 }
 
 csm::EcefLocus UsgsAstroSarSensorModel::imageToRemoteImagingLocus(
@@ -621,8 +661,32 @@ csm::EcefLocus UsgsAstroSarSensorModel::imageToRemoteImagingLocus(
     double* achievedPrecision,
     csm::WarningList* warnings) const
 {
-  return csm::EcefLocus(0.0, 0.0, 0.0,
-                        0.0, 0.0, 0.0);
+  // Compute the slant range
+  double time = m_startingEphemerisTime + (imagePt.line - 0.5) * m_exposureDuration;
+  double groundRange = imagePt.samp * m_scaledPixelWidth;
+  std::vector<double> coeffs = getRangeCoefficients(time);
+  double slantRange = groundRangeToSlantRange(groundRange, coeffs);
+
+  // Project the negative sensor position vector onto the 0 doppler plane
+  // then compute the closest point at the slant range to that
+  csm::EcefVector spacecraftPosition = getSpacecraftPosition(time);
+  csm::EcefVector spacecraftVelocity = getSensorVelocity(time);
+  csm::EcefVector lookVec = normalized(rejection(-1 * spacecraftPosition, spacecraftVelocity));
+  csm::EcefVector closestVec = spacecraftPosition + slantRange * lookVec;
+
+
+  // Compute the tangent at the closest point
+  csm::EcefVector tangent;
+  if (m_lookDirection == LEFT) {
+    tangent = cross(spacecraftVelocity, lookVec);
+  }
+  else {
+    tangent = cross(lookVec, spacecraftVelocity);
+  }
+  tangent = normalized(tangent);
+
+  return csm::EcefLocus(closestVec.x, closestVec.y, closestVec.z,
+                        tangent.x,    tangent.y,    tangent.z);
 }
 
 csm::ImageCoord UsgsAstroSarSensorModel::getImageStart() const
diff --git a/src/Utilities.cpp b/src/Utilities.cpp
index 38cbfa7cdbcbef78a8e48d3bb9c6a25990fe1102..0a18184cb79207a8106452495425da3e6dc4d7eb 100644
--- a/src/Utilities.cpp
+++ b/src/Utilities.cpp
@@ -404,6 +404,69 @@ double secantRoot(double lowerBound, double upperBound, std::function<double(dou
   }
 }
 
+double computeEllipsoidElevation(
+  double x,
+  double y,
+  double z,
+  double semiMajor,
+  double semiMinor,
+  double desired_precision,
+  double* achieved_precision)
+{
+  // Compute elevation given xyz
+  // Requires semi-major-axis and eccentricity-square
+  const int MKTR = 10;
+  double ecc_sqr = 1.0 - semiMinor * semiMinor / semiMajor / semiMajor;
+  double ep2 = 1.0 - ecc_sqr;
+  double d2 = x * x + y * y;
+  double d = sqrt(d2);
+  double h = 0.0;
+  int ktr = 0;
+  double hPrev, r;
+
+  // Suited for points near equator
+  if (d >= z)
+  {
+     double tt, zz, n;
+     double tanPhi = z / d;
+     do
+     {
+        hPrev = h;
+        tt = tanPhi * tanPhi;
+        r = semiMajor / sqrt(1.0 + ep2 * tt);
+        zz = z + r * ecc_sqr * tanPhi;
+        n = r * sqrt(1.0 + tt);
+        h = sqrt(d2 + zz * zz) - n;
+        tanPhi = zz / d;
+        ktr++;
+     } while (MKTR > ktr && fabs(h - hPrev) > desired_precision);
+  }
+
+  // Suited for points near the poles
+  else
+  {
+     double cc, dd, nn;
+     double cotPhi = d / z;
+     do
+     {
+        hPrev = h;
+        cc = cotPhi * cotPhi;
+        r = semiMajor / sqrt(ep2 + cc);
+        dd = d - r * ecc_sqr * cotPhi;
+        nn = r * sqrt(1.0 + cc) * ep2;
+        h = sqrt(dd * dd + z * z) - nn;
+        cotPhi = dd / z;
+        ktr++;
+     } while (MKTR > ktr && fabs(h - hPrev) > desired_precision);
+  }
+
+  if (achieved_precision) {
+    *achieved_precision = fabs(h - hPrev);
+  }
+
+  return h;
+}
+
 
 csm::EcefVector operator*(double scalar, const csm::EcefVector &vec)
 {
diff --git a/tests/SarTests.cpp b/tests/SarTests.cpp
index a8279c736a346661db834ba3914ec6702e56d7eb..2bd7eaab900b70b4737e737d0fbef36e095a84bc 100644
--- a/tests/SarTests.cpp
+++ b/tests/SarTests.cpp
@@ -48,12 +48,12 @@ TEST_F(SarSensorModel, Center) {
   csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
   // TODO these tolerances are bad
   EXPECT_NEAR(groundPt.x, 1737391.90602155, 1e-2);
-  EXPECT_NEAR(groundPt.y, 3749.98835331, 1e-2);
+  EXPECT_NEAR(groundPt.y, -3749.98835331, 1e-2);
   EXPECT_NEAR(groundPt.z, -3749.99708833, 1e-2);
 }
 
 TEST_F(SarSensorModel, GroundToImage) {
-  csm::EcefCoord groundPt(1737391.90602155, 3749.98835331, -3749.99708833);
+  csm::EcefCoord groundPt(1737391.90602155, -3749.98835331, -3749.99708833);
   csm::ImageCoord imagePt = sensorModel->groundToImage(groundPt, 0.001);
   EXPECT_NEAR(imagePt.line, 500.0, 0.001);
   // Due to position interpolation, the sample is slightly less accurate than the line
@@ -81,3 +81,26 @@ TEST_F(SarSensorModel, getRangeCoefficients) {
   EXPECT_NEAR(coeffs[2], 5.377926500384916e-07, 1e-8);
   EXPECT_NEAR(coeffs[3], -1.3072206620088014e-15, 1e-8);
 }
+
+TEST_F(SarSensorModel, imageToProximateImagingLocus) {
+  csm::EcefLocus locus = sensorModel->imageToProximateImagingLocus(
+      csm::ImageCoord(500.0, 500.0),
+      csm::EcefCoord(1737291.90643026, -3750.17585202, -3749.78124955));
+  EXPECT_NEAR(locus.point.x, 1737391.90602155, 1e-2);
+  EXPECT_NEAR(locus.point.y, -3749.98835331, 1e-2);
+  EXPECT_NEAR(locus.point.z, -3749.99708833, 1e-2);
+  EXPECT_NEAR(locus.direction.x, 0.0018750001892442036, 1e-5);
+  EXPECT_NEAR(locus.direction.y, -0.9999982421774111, 1e-5);
+  EXPECT_NEAR(locus.direction.z, -4.047002203562211e-06, 1e-5);
+}
+
+TEST_F(SarSensorModel, imageToRemoteImagingLocus) {
+  csm::EcefLocus locus = sensorModel->imageToRemoteImagingLocus(
+      csm::ImageCoord(500.0, 500.0));
+      EXPECT_NEAR(locus.point.x, 1737388.3904556315, 1e-2);
+      EXPECT_NEAR(locus.point.y, 0.0, 1e-2);
+      EXPECT_NEAR(locus.point.z, -3749.9807653094517, 1e-2);
+      EXPECT_NEAR(locus.direction.x, 0.0, 1e-8);
+      EXPECT_NEAR(locus.direction.y, -1.0, 1e-8);
+      EXPECT_NEAR(locus.direction.z, 0.0, 1e-8);
+}