diff --git a/include/usgscsm/UsgsAstroLsSensorModel.h b/include/usgscsm/UsgsAstroLsSensorModel.h index 3ce3880f1f380109a6b7c843b51736d03e0571d0..ffb28049bcd8885765570de62045c6a4aea6e6ab 100644 --- a/include/usgscsm/UsgsAstroLsSensorModel.h +++ b/include/usgscsm/UsgsAstroLsSensorModel.h @@ -976,7 +976,14 @@ class UsgsAstroLsSensorModel : public csm::RasterGM, // Compute the determinant of a 3x3 matrix double determinant3x3(double mat[9]) const; - + + // A function whose value will be 0 when the line a given ground + // point projects into is found. The obtained line will be + // approxPt.line + t. + double calcDetectorLineErr(double t, csm::ImageCoord const& approxPt, + const csm::EcefCoord& groundPt, + const std::vector<double>& adj) const; + csm::NoCorrelationModel _no_corr_model; // A way to report no correlation // between images is supported std::vector<double> diff --git a/src/UsgsAstroLsSensorModel.cpp b/src/UsgsAstroLsSensorModel.cpp index 3b39ee66a24aacc705d8972c30e1a4a35c65fc0d..3ff95ab0b02a1bcff9d717e2f7fd978268a94446 100644 --- a/src/UsgsAstroLsSensorModel.cpp +++ b/src/UsgsAstroLsSensorModel.cpp @@ -648,36 +648,6 @@ void UsgsAstroLsSensorModel::updateState() { // Set focal length variance m_covariance[15 * num_params + 15] = positionVariance; - - // Test if a pixel projected to the ground and projected back - // returns to the original location. If not, flip the sign of - // m_iTransL. It is very important here to pick a pixel which is not - // an integer, as this test may succeed for integer pixels and fail - // for non-integer ones. Also, need to pick the answer with the - // smallest achieved precision, even when neither of them is smaller - // than the desired precision. - - lineCtr = round(m_nLines / 2.0) + 0.5; - sampCtr = round(m_nSamples / 2.0) + 0.5; - - double desiredPrecision = 0.001; - ip = csm::ImageCoord(lineCtr, sampCtr); - csm::EcefCoord xyz = imageToGround(ip, refHeight); - - double achievedPrecision1 = 1.0; - // Will use m_iTransL on the next line - ip = groundToImage(xyz, desiredPrecision, &achievedPrecision1); - - double achievedPrecision2 = 1.0; - for (int it = 0; it < sizeof(m_iTransL)/sizeof(double); it++) - m_iTransL[it] = -m_iTransL[it]; // use a flipped m_iTransL - ip = groundToImage(xyz, desiredPrecision, &achievedPrecision2); - - if (std::abs(achievedPrecision1) <= std::abs(achievedPrecision2)) { - // Flip back m_iTransL as the original was better - for (int it = 0; it < sizeof(m_iTransL)/sizeof(double); it++) - m_iTransL[it] = -m_iTransL[it]; - } } //--------------------------------------------------------------------------- @@ -707,36 +677,38 @@ csm::ImageCoord UsgsAstroLsSensorModel::groundToImage( const csm::EcefCoord& groundPt, const std::vector<double>& adj, double desiredPrecision, double* achievedPrecision, csm::WarningList* warnings) const { - // Search for the line, sample coordinate that viewed a given ground point. - // This method first uses a linear approximation to get an initial point. - // Then the detector offset for the line is continuously computed and - // applied to the line until we achieve the desired precision. - + csm::ImageCoord approxPt; computeLinearApproximation(groundPt, approxPt); - std::vector<double> detectorView; - double detectorLine = m_nLines; - double detectorSample = 0; - double count = 0; - double timei; - - while (abs(detectorLine) > desiredPrecision && ++count < 15) { - timei = getImageTime(approxPt); - detectorView = computeDetectorView(timei, groundPt, adj); - - // Convert to detector line - detectorLine = m_iTransL[0] + m_iTransL[1] * detectorView[0] + - m_iTransL[2] * detectorView[1] + m_detectorLineOrigin - - m_startingDetectorLine; - detectorLine /= m_detectorLineSumming; - - // Convert to image line - approxPt.line += detectorLine; + // Search for the (line, sample) coordinate that views a given + // ground point. Set this up as a root-finding problem and use the + // secant method. + int count = 0; + double t0 = 0.0; + double lineErr0 = calcDetectorLineErr(t0, approxPt, groundPt, adj); + double t1 = lineErr0 * 0.1; // need to stay close or else there's overshooting + double lineErr1 = calcDetectorLineErr(t1, approxPt, groundPt, adj); + while (std::abs(lineErr1) > desiredPrecision && ++count < 15) { + + if (lineErr1 == lineErr0) + break; // avoid division by 0 + + // Secant method update + // https://en.wikipedia.org/wiki/Secant_method + double t2 = t1 - lineErr1 * (t1 - t0) / (lineErr1 - lineErr0); + double lineErr2 = calcDetectorLineErr(t2, approxPt, groundPt, adj); + + // Update for the next step + t0 = t1; lineErr0 = lineErr1; + t1 = t2; lineErr1 = lineErr2; } - timei = getImageTime(approxPt); - detectorView = computeDetectorView(timei, groundPt, adj); + // Update the line with the found value + approxPt.line += t1; + + double timei = getImageTime(approxPt); + std::vector<double> detectorView = computeDetectorView(timei, groundPt, adj); // Invert distortion double distortedFocalX, distortedFocalY; @@ -745,10 +717,10 @@ csm::ImageCoord UsgsAstroLsSensorModel::groundToImage( desiredPrecision); // Convert to detector line and sample - detectorLine = m_iTransL[0] + m_iTransL[1] * distortedFocalX + - m_iTransL[2] * distortedFocalY; - detectorSample = m_iTransS[0] + m_iTransS[1] * distortedFocalX + - m_iTransS[2] * distortedFocalY; + double detectorLine = m_iTransL[0] + m_iTransL[1] * distortedFocalX + + m_iTransL[2] * distortedFocalY; + double detectorSample = m_iTransS[0] + m_iTransS[1] * distortedFocalX + + m_iTransS[2] * distortedFocalY; // Convert to image sample line double finalUpdate = (detectorLine + m_detectorLineOrigin - m_startingDetectorLine) / @@ -766,7 +738,7 @@ csm::ImageCoord UsgsAstroLsSensorModel::groundToImage( approxPt.samp) if (warnings && (desiredPrecision > 0.0) && - (abs(finalUpdate) > desiredPrecision)) { + (std::abs(finalUpdate) > desiredPrecision)) { warnings->push_back(csm::Warning( csm::Warning::PRECISION_NOT_MET, "Desired precision not achieved.", "UsgsAstroLsSensorModel::groundToImage()")); @@ -2650,3 +2622,26 @@ csm::EcefVector UsgsAstroLsSensorModel::getSunPosition( } return sunPosition; } + +// A function whose value will be 0 when the line a given ground point +// projects into is found. The obtained line will be approxPt.line + +// t. +double UsgsAstroLsSensorModel::calcDetectorLineErr(double t, csm::ImageCoord const& approxPt, + const csm::EcefCoord& groundPt, + const std::vector<double>& adj) const { + + csm::ImageCoord currPt = approxPt; + currPt.line += t; + + double timei = getImageTime(currPt); + std::vector<double> detectorView = computeDetectorView(timei, groundPt, adj); + + // Convert to detector line + double detectorLine = m_iTransL[0] + m_iTransL[1] * detectorView[0] + + m_iTransL[2] * detectorView[1] + m_detectorLineOrigin - + m_startingDetectorLine; + detectorLine /= m_detectorLineSumming; + + return detectorLine; +} +