diff --git a/include/usgscsm/Utilities.h b/include/usgscsm/Utilities.h
index 5034abea94b429ecda3d6fc1fdb2fb795d05d86b..cb7f55160737b650fdbc236c404ed6bb09c00055 100644
--- a/include/usgscsm/Utilities.h
+++ b/include/usgscsm/Utilities.h
@@ -78,12 +78,25 @@ double brentRoot(
   std::function<double(double)> func,
   double epsilon = 1e-10);
 
-double secantRoot(
-    double lowerBound,
-    double upperBound,
-    std::function<double(double)> func,
-    double epsilon = 1e-10,
-    int maxIterations = 30);
+// Evaluate a polynomial function.
+// Coefficients should be ordered least order to greatest I.E. {1, 2, 3} is 1 + 2x + 3x^2
+double evaluatePolynomial(
+  const std::vector<double> &coeffs,
+  double x);
+
+// Evaluate the derivative of a polynomial function.
+// Coefficients should be ordered least order to greatest I.E. {1, 2, 3} is 1 + 2x + 3x^2
+double evaluatePolynomialDerivative(
+  const std::vector<double> &coeffs,
+  double x);
+
+// Find a root of a polynomial using Newton's method.
+// Coefficients should be ordered least order to greatest I.E. {1, 2, 3} is 1 + 2x + 3x^2
+double polynomialRoot(
+  const std::vector<double> &coeffs,
+  double guess,
+  double threshold = 1e-10,
+  int maxIterations = 30);
 
 double computeEllipsoidElevation(
     double x,
diff --git a/src/UsgsAstroSarSensorModel.cpp b/src/UsgsAstroSarSensorModel.cpp
index b75a9c1ad6446159e6712d4ef7ca8042ca11c586..48ae73335afdf51151699fe81844208d57dff41a 100644
--- a/src/UsgsAstroSarSensorModel.cpp
+++ b/src/UsgsAstroSarSensorModel.cpp
@@ -45,7 +45,7 @@ const csm::param::Type
 string UsgsAstroSarSensorModel::constructStateFromIsd(
     const string imageSupportData,
     csm::WarningList *warnings){
-  
+
   MESSAGE_LOG("UsgsAstroSarSensorModel constructing state from ISD, with {}", imageSupportData);
   json isd = json::parse(imageSupportData);
   json state = {};
@@ -227,7 +227,7 @@ void UsgsAstroSarSensorModel::reset() {
 
 void UsgsAstroSarSensorModel::replaceModelState(const string& argState){
   reset();
-  
+
   MESSAGE_LOG("Replacing model state with: {}", argState);
   auto stateJson = json::parse(argState);
 
@@ -286,7 +286,7 @@ void UsgsAstroSarSensorModel::replaceModelState(const string& argState){
     csm::ImageCoord ip(lineCtr, sampCtr);
     MESSAGE_LOG("updateState: center image coordinate set to {} {}",
                               lineCtr, sampCtr)
-    
+
     double refHeight = 0;
     m_referencePointXyz = imageToGround(ip, refHeight);
     MESSAGE_LOG("updateState: reference point (x, y, z) {} {} {}",
@@ -386,18 +386,18 @@ csm::ImageCoord UsgsAstroSarSensorModel::groundToImage(
 double UsgsAstroSarSensorModel::dopplerShift(
     csm::EcefCoord groundPt,
     double tolerance) const {
-  MESSAGE_LOG("Calculating doppler shift with: {}, {}, {}, and tolerance {}.", 
+  MESSAGE_LOG("Calculating doppler shift with: {}, {}, {}, and tolerance {}.",
               groundPt.x, groundPt.y, groundPt.z, tolerance);
   csm::EcefVector groundVec(groundPt.x ,groundPt.y, groundPt.z);
   std::function<double(double)> dopplerShiftFunction = [this, groundVec](double time) {
     csm::EcefVector spacecraftPosition = getSpacecraftPosition(time);
     csm::EcefVector spacecraftVelocity = getSensorVelocity(time);
     csm::EcefVector lookVector = spacecraftPosition - groundVec;
-    
+
     double slantRange = norm(lookVector);
-    
+
     double dopplerShift = -2.0 * dot(lookVector, spacecraftVelocity) / (slantRange * m_wavelength);
-    
+
     return dopplerShift;
    };
 
@@ -409,7 +409,7 @@ double UsgsAstroSarSensorModel::dopplerShift(
 
 double UsgsAstroSarSensorModel::slantRange(csm::EcefCoord surfPt,
     double time) const {
-  MESSAGE_LOG("Calculating slant range with: {}, {}, {}, and time {}.", 
+  MESSAGE_LOG("Calculating slant range with: {}, {}, {}, and time {}.",
               surfPt.x, surfPt.y, surfPt.z, time);
   csm::EcefVector surfVec(surfPt.x ,surfPt.y, surfPt.z);
   csm::EcefVector spacecraftPosition = getSpacecraftPosition(time);
@@ -426,29 +426,17 @@ double UsgsAstroSarSensorModel::slantRangeToGroundRange(
 
   std::vector<double> coeffs = getRangeCoefficients(time);
 
-  // Calculates the ground range from the slant range.
-  std::function<double(double)> slantRangeToGroundRangeFunction =
-    [this, coeffs, slantRange](double groundRange){
-   return slantRange - groundRangeToSlantRange(groundRange, coeffs);
-  };
-
   // Need to come up with an initial guess when solving for ground
-  // range given slant range. Compute the ground range at the
-  // near and far edges of the image by evaluating the sample-to-
-  // ground-range equation: groundRange=(sample-1)*scaled_pixel_width
-  // at the edges of the image. We also need to add some padding to
-  // allow for solving for coordinates that are slightly outside of
-  // the actual image area. Use sample=-0.25*image_samples and
-  // sample=1.25*image_samples.
-  double minGroundRangeGuess = (-0.25 * m_nSamples - 1.0) * m_scaledPixelWidth;
-  double maxGroundRangeGuess = (1.25 * m_nSamples - 1.0) * m_scaledPixelWidth;
+  // range given slant range. Naively use the middle of the image.
+  double guess = 0.5 * m_nSamples * m_scaledPixelWidth;
 
   // Tolerance to 1/20th of a pixel for now.
-  return secantRoot(minGroundRangeGuess, maxGroundRangeGuess, slantRangeToGroundRangeFunction, tolerance);
+  coeffs[0] -= slantRange;
+  return polynomialRoot(coeffs, guess, tolerance);
 }
 
 double UsgsAstroSarSensorModel::groundRangeToSlantRange(double groundRange, const std::vector<double> &coeffs) const {
-  return coeffs[0] + groundRange * (coeffs[1] + groundRange * (coeffs[2] + groundRange * coeffs[3]));
+  return evaluatePolynomial(coeffs, groundRange);
 }
 
 
@@ -578,7 +566,7 @@ csm::EcefCoordCovar UsgsAstroSarSensorModel::imageToGround(
     double desiredPrecision,
     double* achievedPrecision,
     csm::WarningList* warnings) const {
-  MESSAGE_LOG("Calculating imageToGroundWith: {}, {}, {}, {}, {}", imagePt.samp, imagePt.line, 
+  MESSAGE_LOG("Calculating imageToGroundWith: {}, {}, {}, {}, {}", imagePt.samp, imagePt.line,
               height, heightVariance, desiredPrecision);
   // Image to ground with error propagation
   // Use numerical partials
diff --git a/src/Utilities.cpp b/src/Utilities.cpp
index 07eb6759a201c43b179555c5f46560322d322f0b..e452881316c59dc18cde3fa5bdf0939ba20660fe 100644
--- a/src/Utilities.cpp
+++ b/src/Utilities.cpp
@@ -279,129 +279,124 @@ void lagrangeInterp(
   }
 }
 
-double brentRoot(
-  double lowerBound,
-  double upperBound,
-  std::function<double(double)> func,
-  double epsilon) {
-    double counterPoint = lowerBound;
-    double currentPoint = upperBound;
-    double counterFunc = func(counterPoint);
-    double currentFunc = func(currentPoint);
-    if (counterFunc * currentFunc > 0.0) {
-      throw std::invalid_argument("Function values at the boundaries have the same sign [brentRoot].");
-    }
-    if (fabs(counterFunc) < fabs(currentFunc)) {
-      std::swap(counterPoint, currentPoint);
-      std::swap(counterFunc, currentFunc);
-    }
-
-    double previousPoint = counterPoint;
-    double previousFunc = counterFunc;
-    double evenOlderPoint = previousPoint;
-    double nextPoint;
-    double nextFunc;
-    int iteration = 0;
-    bool bisected = true;
-
-    do {
-      // Inverse quadratic interpolation
-      if (counterFunc != previousFunc && counterFunc != currentFunc && currentFunc != previousFunc) {
-        nextPoint = (counterPoint * currentFunc * previousFunc) / ((counterFunc - currentFunc) * (counterFunc - previousFunc));
-        nextPoint += (currentPoint * counterFunc * previousFunc) / ((currentFunc - counterFunc) * (currentFunc - previousFunc));
-        nextPoint += (previousPoint * currentFunc * counterFunc) / ((previousFunc - counterFunc) * (previousFunc - currentFunc));
-      }
-      // Secant method
-      else {
-        nextPoint = currentPoint - currentFunc * (currentPoint - counterPoint) / (currentFunc - counterFunc);
-      }
-
-      // Bisection method
-      if (((currentPoint - nextPoint) * (nextPoint - (3 * counterPoint + currentPoint) / 4) < 0) ||
-          (bisected && fabs(nextPoint - currentPoint) >= fabs(currentPoint - previousPoint) / 2) ||
-          (!bisected && fabs(nextPoint - currentPoint) >= fabs(previousPoint - evenOlderPoint) / 2) ||
-          (bisected && fabs(currentPoint - previousPoint) < epsilon) ||
-          (!bisected && fabs(previousPoint - evenOlderPoint) < epsilon)) {
-        nextPoint = (currentPoint + counterPoint) / 2;
-        bisected = true;
-      }
-      else {
-        bisected = false;
-      }
-
-      // Setup for next iteration
-      evenOlderPoint = previousPoint;
-      previousPoint = currentPoint;
-      previousFunc = currentFunc;
-      nextFunc = func(nextPoint);
-      if (counterFunc * nextFunc < 0) {
-        currentPoint = nextPoint;
-        currentFunc = nextFunc;
-      }
-      else {
-        counterPoint = nextPoint;
-        counterFunc = nextFunc;
-      }
-    } while (++iteration < 30 && fabs(counterPoint - currentPoint) > epsilon);
-
-    return nextPoint;
-  }
-
-double secantRoot(double lowerBound, double upperBound, std::function<double(double)> func,
-                  double epsilon, int maxIters) {
-  bool found = false;
-
-  double x0 = lowerBound;
-  double x1 = upperBound;
-  double f0 = func(x0);
-  double f1 = func(x1);
-  double diff = 0;
-  double x2 = 0;
-  double f2 = 0;
-
-  // Make sure we bound the root (f = 0.0)
-  if (f0 * f1 > 0.0) {
-    throw std::invalid_argument("Function values at the boundaries have the same sign [secantRoot].");
-  }
-
-  // Order the bounds
-  if (f1 < f0) {
-    std::swap(x0, x1);
-    std::swap(f0, f1);
-  }
-
-  for (int iteration=0; iteration < maxIters; iteration++) {
-    x2 = x1 - f1 * (x1 - x0)/(f1 - f0);
-    f2 = func(x2);
+double brentRoot(double lowerBound,
+                 double upperBound,
+                 std::function<double(double)> func,
+                 double epsilon) {
+  double counterPoint = lowerBound;
+  double currentPoint = upperBound;
+  double counterFunc = func(counterPoint);
+  double currentFunc = func(currentPoint);
+  if (counterFunc * currentFunc > 0.0) {
+    throw std::invalid_argument("Function values at the boundaries have the same sign [brentRoot].");
+  }
+  if (fabs(counterFunc) < fabs(currentFunc)) {
+    std::swap(counterPoint, currentPoint);
+    std::swap(counterFunc, currentFunc);
+  }
+
+  double previousPoint = counterPoint;
+  double previousFunc = counterFunc;
+  double evenOlderPoint = previousPoint;
+  double nextPoint;
+  double nextFunc;
+  int iteration = 0;
+  bool bisected = true;
+
+  do {
+    // Inverse quadratic interpolation
+    if (counterFunc != previousFunc && counterFunc != currentFunc && currentFunc != previousFunc) {
+      nextPoint = (counterPoint * currentFunc * previousFunc) / ((counterFunc - currentFunc) * (counterFunc - previousFunc));
+      nextPoint += (currentPoint * counterFunc * previousFunc) / ((currentFunc - counterFunc) * (currentFunc - previousFunc));
+      nextPoint += (previousPoint * currentFunc * counterFunc) / ((previousFunc - counterFunc) * (previousFunc - currentFunc));
+    }
+    // Secant method
+    else {
+      nextPoint = currentPoint - currentFunc * (currentPoint - counterPoint) / (currentFunc - counterFunc);
+    }
 
-    // Update the bounds for the next iteration
-    if (f2 < 0.0) {
-      diff = x1 - x2;
-      x1 = x2;
-      f1 = f2;
+    // Bisection method
+    if (((currentPoint - nextPoint) * (nextPoint - (3 * counterPoint + currentPoint) / 4) < 0) ||
+        (bisected && fabs(nextPoint - currentPoint) >= fabs(currentPoint - previousPoint) / 2) ||
+        (!bisected && fabs(nextPoint - currentPoint) >= fabs(previousPoint - evenOlderPoint) / 2) ||
+        (bisected && fabs(currentPoint - previousPoint) < epsilon) ||
+        (!bisected && fabs(previousPoint - evenOlderPoint) < epsilon)) {
+      nextPoint = (currentPoint + counterPoint) / 2;
+      bisected = true;
     }
     else {
-      diff = x0 - x2;
-      x0 = x2;
-      f0 = f2;
+      bisected = false;
     }
 
-    // Check to see if we're done
-    if ((fabs(diff) <= epsilon) || (f2 == 0.0) ) {
-      found = true;
-      break;
+    // Setup for next iteration
+    evenOlderPoint = previousPoint;
+    previousPoint = currentPoint;
+    previousFunc = currentFunc;
+    nextFunc = func(nextPoint);
+    if (counterFunc * nextFunc < 0) {
+      currentPoint = nextPoint;
+      currentFunc = nextFunc;
     }
+    else {
+      counterPoint = nextPoint;
+      counterFunc = nextFunc;
+    }
+  } while (++iteration < 30 && fabs(counterPoint - currentPoint) > epsilon);
+
+  return nextPoint;
+}
+
+double evaluatePolynomial(const std::vector<double> &coeffs,
+                          double x) {
+  if (coeffs.empty()) {
+    throw std::invalid_argument("Polynomial coeffs must be non-empty.");
+  }
+  auto revIt = coeffs.crbegin();
+  double value = *revIt;
+  ++revIt;
+  for (; revIt != coeffs.crend(); ++revIt) {
+    value *= x;
+    value += *revIt;
   }
+  return value;
+}
 
-  if (found) {
-   return x2;
+double evaluatePolynomialDerivative(const std::vector<double> &coeffs,
+                                    double x) {
+  if (coeffs.empty()) {
+    throw std::invalid_argument("Polynomial coeffs must be non-empty.");
   }
-  else {
-    throw csm::Error(
-    csm::Error::UNKNOWN_ERROR,
-    "Could not find a root of the function using the secant method",
-    "secantRoot");
+  int i = coeffs.size() - 1;
+  double value = i * coeffs[i];
+  --i;
+  for (; i > 0; --i) {
+    value *= x;
+    value += i * coeffs[i];
   }
+  return value;
+}
+
+double polynomialRoot(const std::vector<double> &coeffs,
+                      double guess,
+                      double threshold,
+                      int maxIterations) {
+  double root = guess;
+  double polyValue = evaluatePolynomial(coeffs, root);
+  double polyDeriv = 0.0;
+  for (int iteration = 0; iteration < maxIterations+1; iteration++) {
+    if (fabs(polyValue) < threshold) {
+      return root;
+    }
+    polyDeriv = evaluatePolynomialDerivative(coeffs, root);
+    if (fabs(polyDeriv) < 1e-15) {
+      throw std::invalid_argument("Derivative at guess (" +
+                                  std::to_string(guess) + ") is too close to 0.");
+    }
+    root -= polyValue / polyDeriv;
+    polyValue = evaluatePolynomial(coeffs, root);
+  }
+  throw std::invalid_argument("Root finder did not converge after " +
+                              std::to_string(maxIterations) + " iterations");
 }
 
 double computeEllipsoidElevation(
diff --git a/tests/UtilitiesTests.cpp b/tests/UtilitiesTests.cpp
index 4dde5acc18b8585526d25ee70d7b5906d54645a7..f228c63c43a29eed7c306fb4346a4649af427b07 100644
--- a/tests/UtilitiesTests.cpp
+++ b/tests/UtilitiesTests.cpp
@@ -350,13 +350,25 @@ TEST(UtilitiesTests, brentRoot) {
   EXPECT_THROW(brentRoot(-3.0, 3.0, testPoly), std::invalid_argument);
 }
 
-TEST(UtilitiesTests, secantRoot) {
-  std::function<double(double)> testPoly = [](double x) {
-    return (x - 2) * (x + 1) * (x + 7);
-  };
-  EXPECT_NEAR(secantRoot(1.0, 3.0, testPoly, 1e-10), 2.0, 1e-10);
-  EXPECT_NEAR(secantRoot(0.0, -3.0, testPoly, 1e-10), -1.0, 1e-10);
-  EXPECT_THROW(secantRoot(-1000.0, 1000.0, testPoly), csm::Error);
+TEST(UtilitiesTests, polynomialEval) {
+  std::vector<double> coeffs = {-12.0, 4.0, -3.0, 1.0};
+  EXPECT_DOUBLE_EQ(evaluatePolynomial(coeffs, -1.0), -20.0);
+  EXPECT_DOUBLE_EQ(evaluatePolynomialDerivative(coeffs, -1.0), 13.0);
+  EXPECT_DOUBLE_EQ(evaluatePolynomial(coeffs, 0.0), -12.0);
+  EXPECT_DOUBLE_EQ(evaluatePolynomialDerivative(coeffs,  0.0), 4.0);
+  EXPECT_DOUBLE_EQ(evaluatePolynomial(coeffs, 2.0), -8.0);
+  EXPECT_DOUBLE_EQ(evaluatePolynomialDerivative(coeffs,  2.0), 4.0);
+  EXPECT_DOUBLE_EQ(evaluatePolynomial(coeffs, 3.5), 8.125);
+  EXPECT_DOUBLE_EQ(evaluatePolynomialDerivative(coeffs, 3.5), 19.75);
+  EXPECT_THROW(evaluatePolynomial(std::vector<double>(), 0.0), std::invalid_argument);
+  EXPECT_THROW(evaluatePolynomialDerivative(std::vector<double>(), 0.0), std::invalid_argument);
+}
+
+TEST(UtilitiesTests, polynomialRoot) {
+  std::vector<double> oneRootCoeffs = {-12.0, 4.0, -3.0, 1.0}; // roots are 3, +-2i
+  std::vector<double> noRootCoeffs = {4.0, 0.0, 1.0}; // roots are +-2i
+  EXPECT_NEAR(polynomialRoot(oneRootCoeffs, 0.0), 3.0, 1e-10);
+  EXPECT_THROW(polynomialRoot(noRootCoeffs, 0.0), std::invalid_argument);
 }
 
 TEST(UtilitiesTests, vectorProduct) {