From c2cdb320c72984901d5f77cace08d080e881643b Mon Sep 17 00:00:00 2001 From: Jesse Mapel <jam826@nau.edu> Date: Wed, 3 Apr 2019 15:08:10 -0700 Subject: [PATCH] Fixed lagrange interpolation reading bad data with low number of points (#209) * moved lagrange interpolation to utilities for testability * Added lagrange interpolation tests --- include/usgscsm/UsgsAstroLsSensorModel.h | 11 -- include/usgscsm/Utilities.h | 14 +- src/UsgsAstroLsSensorModel.cpp | 130 ------------------ src/Utilities.cpp | 121 +++++++++++++++++ tests/UtilitiesTests.cpp | 159 +++++++++++++++++++++++ 5 files changed, 289 insertions(+), 146 deletions(-) diff --git a/include/usgscsm/UsgsAstroLsSensorModel.h b/include/usgscsm/UsgsAstroLsSensorModel.h index 5329ede..1bb1823 100644 --- a/include/usgscsm/UsgsAstroLsSensorModel.h +++ b/include/usgscsm/UsgsAstroLsSensorModel.h @@ -953,17 +953,6 @@ private: double& dyl, double& dzl) const; - // Lagrange interpolation of variable order. - void lagrangeInterp ( - const int& numTime, - const double* valueArray, - const double& startTime, - const double& delTime, - const double& time, - const int& vectorLength, - const int& i_order, - double* valueVector ) const; - // Intersects a LOS at a specified height above the ellipsoid. void losEllipsoidIntersect ( const double& height, diff --git a/include/usgscsm/Utilities.h b/include/usgscsm/Utilities.h index 719a724..70ffd3e 100644 --- a/include/usgscsm/Utilities.h +++ b/include/usgscsm/Utilities.h @@ -43,11 +43,15 @@ void createCameraLookVector( const double& focalLength, double cameraLook[]); -//void calculateAttitudeCorrection( -// const double& time, -// -// double attCorr[9]); -// +void lagrangeInterp ( + const int& numTime, + const double* valueArray, + const double& startTime, + const double& delTime, + const double& time, + const int& vectorLength, + const int& i_order, + double* valueVector); // Methods for checking/accessing the ISD diff --git a/src/UsgsAstroLsSensorModel.cpp b/src/UsgsAstroLsSensorModel.cpp index 2aa6336..c43da11 100644 --- a/src/UsgsAstroLsSensorModel.cpp +++ b/src/UsgsAstroLsSensorModel.cpp @@ -1726,136 +1726,6 @@ void UsgsAstroLsSensorModel::lightAberrationCorr( dzl = cfac * vz; } -//*************************************************************************** -// UsgsAstroLsSensorModel::lagrangeInterp -//*************************************************************************** -void UsgsAstroLsSensorModel::lagrangeInterp( - const int& numTime, - const double* valueArray, - const double& startTime, - const double& delTime, - const double& time, - const int& vectorLength, - const int& i_order, - double* valueVector) const -{ - // Lagrange interpolation for uniform post interval. - // Largest order possible is 8th. Points far away from - // data center are handled gracefully to avoid failure. - - // Compute index - - double fndex = (time - startTime) / delTime; - int index = int(fndex); - - //Time outside range - //printf("%f | %i\n", fndex, index); - //if (index < 0 || index >= numTime - 1) { - // printf("TIME ISSUE\n"); - // double d1 = fndex / (numTime - 1); - // double d0 = 1.0 - d1; - // int indx0 = vectorLength * (numTime - 1); - // for (int i = 0; i < vectorLength; i++) - // { - // valueVector[i] = d0 * valueArray[i] + d1 * valueArray[indx0 + i]; - // } - // return; - //} - - if (index < 0) - { - index = 0; - } - if (index > numTime - 2) - { - index = numTime - 2; - } - - // Define order, max is 8 - - int order; - if (index >= 3 && index < numTime - 4) { - order = 8; - } - else if (index == 2 || index == numTime - 4) { - order = 6; - } - else if (index == 1 || index == numTime - 3) { - order = 4; - } - else if (index == 0 || index == numTime - 2) { - order = 2; - } - if (order > i_order) { - order = i_order; - } - - // Compute interpolation coefficients - double tp3, tp2, tp1, tm1, tm2, tm3, tm4, d[8]; - double tau = fndex - index; - if (order == 2) { - tm1 = tau - 1; - d[0] = -tm1; - d[1] = tau; - } - else if (order == 4) { - tp1 = tau + 1; - tm1 = tau - 1; - tm2 = tau - 2; - d[0] = -tau * tm1 * tm2 / 6.0; - d[1] = tp1 * tm1 * tm2 / 2.0; - d[2] = -tp1 * tau * tm2 / 2.0; - d[3] = tp1 * tau * tm1 / 6.0; - } - else if (order == 6) { - tp2 = tau + 2; - tp1 = tau + 1; - tm1 = tau - 1; - tm2 = tau - 2; - tm3 = tau - 3; - d[0] = -tp1 * tau * tm1 * tm2 * tm3 / 120.0; - d[1] = tp2 * tau * tm1 * tm2 * tm3 / 24.0; - d[2] = -tp2 * tp1 * tm1 * tm2 * tm3 / 12.0; - d[3] = tp2 * tp1 * tau * tm2 * tm3 / 12.0; - d[4] = -tp2 * tp1 * tau * tm1 * tm3 / 24.0; - d[5] = tp2 * tp1 * tau * tm1 * tm2 / 120.0; - } - else if (order == 8) { - tp3 = tau + 3; - tp2 = tau + 2; - tp1 = tau + 1; - tm1 = tau - 1; - tm2 = tau - 2; - tm3 = tau - 3; - tm4 = tau - 4; - // Why are the denominators hard coded, as it should be x[0] - x[i] - d[0] = -tp2 * tp1 * tau * tm1 * tm2 * tm3 * tm4 / 5040.0; - d[1] = tp3 * tp1 * tau * tm1 * tm2 * tm3 * tm4 / 720.0; - d[2] = -tp3 * tp2 * tau * tm1 * tm2 * tm3 * tm4 / 240.0; - d[3] = tp3 * tp2 * tp1 * tm1 * tm2 * tm3 * tm4 / 144.0; - d[4] = -tp3 * tp2 * tp1 * tau * tm2 * tm3 * tm4 / 144.0; - d[5] = tp3 * tp2 * tp1 * tau * tm1 * tm3 * tm4 / 240.0; - d[6] = -tp3 * tp2 * tp1 * tau * tm1 * tm2 * tm4 / 720.0; - d[7] = tp3 * tp2 * tp1 * tau * tm1 * tm2 * tm3 / 5040.0; - } - - // Compute interpolated point - int indx0 = index - order / 2 + 1; - for (int i = 0; i < vectorLength; i++) - { - valueVector[i] = 0.0; - } - - for (int i = 0; i < order; i++) - { - int jndex = vectorLength * (indx0 + i); - for (int j = 0; j < vectorLength; j++) - { - valueVector[j] += d[i] * valueArray[jndex + j]; - } - } -} - //*************************************************************************** // UsgsAstroLsSensorModel::computeElevation //*************************************************************************** diff --git a/src/Utilities.cpp b/src/Utilities.cpp index b601de8..6a33c34 100644 --- a/src/Utilities.cpp +++ b/src/Utilities.cpp @@ -1,4 +1,5 @@ #include "Utilities.h" +#include <Error.h> using json = nlohmann::json; @@ -114,6 +115,126 @@ void createCameraLookVector( cameraLook[2] /= magnitude; } +// Lagrange Interpolation for equally spaced data +void lagrangeInterp( + const int& numTime, + const double* valueArray, + const double& startTime, + const double& delTime, + const double& time, + const int& vectorLength, + const int& i_order, + double* valueVector) { + // Lagrange interpolation for uniform post interval. + // Largest order possible is 8th. Points far away from + // data center are handled gracefully to avoid failure. + + if (numTime < 2) { + throw csm::Error( + csm::Error::INDEX_OUT_OF_RANGE, + "At least 2 points are required to perform Lagrange interpolation.", + "lagrangeInterp"); + } + + // Compute index + + double fndex = (time - startTime) / delTime; + int index = int(fndex); + + if (index < 0) + { + index = 0; + } + if (index > numTime - 2) + { + index = numTime - 2; + } + + // Define order, max is 8 + + int order; + if (index >= 3 && index < numTime - 4) { + order = 8; + } + else if (index >= 2 && index < numTime - 3) { + order = 6; + } + else if (index >= 1 && index < numTime - 2) { + order = 4; + } + else { + order = 2; + } + if (order > i_order) { + order = i_order; + } + + // Compute interpolation coefficients + double tp3, tp2, tp1, tm1, tm2, tm3, tm4, d[8]; + double tau = fndex - index; + if (order == 2) { + tm1 = tau - 1; + d[0] = -tm1; + d[1] = tau; + } + else if (order == 4) { + tp1 = tau + 1; + tm1 = tau - 1; + tm2 = tau - 2; + d[0] = -tau * tm1 * tm2 / 6.0; + d[1] = tp1 * tm1 * tm2 / 2.0; + d[2] = -tp1 * tau * tm2 / 2.0; + d[3] = tp1 * tau * tm1 / 6.0; + } + else if (order == 6) { + tp2 = tau + 2; + tp1 = tau + 1; + tm1 = tau - 1; + tm2 = tau - 2; + tm3 = tau - 3; + d[0] = -tp1 * tau * tm1 * tm2 * tm3 / 120.0; + d[1] = tp2 * tau * tm1 * tm2 * tm3 / 24.0; + d[2] = -tp2 * tp1 * tm1 * tm2 * tm3 / 12.0; + d[3] = tp2 * tp1 * tau * tm2 * tm3 / 12.0; + d[4] = -tp2 * tp1 * tau * tm1 * tm3 / 24.0; + d[5] = tp2 * tp1 * tau * tm1 * tm2 / 120.0; + } + else if (order == 8) { + tp3 = tau + 3; + tp2 = tau + 2; + tp1 = tau + 1; + tm1 = tau - 1; + tm2 = tau - 2; + tm3 = tau - 3; + tm4 = tau - 4; + // Why are the denominators hard coded, as it should be x[0] - x[i] + d[0] = -tp2 * tp1 * tau * tm1 * tm2 * tm3 * tm4 / 5040.0; + d[1] = tp3 * tp1 * tau * tm1 * tm2 * tm3 * tm4 / 720.0; + d[2] = -tp3 * tp2 * tau * tm1 * tm2 * tm3 * tm4 / 240.0; + d[3] = tp3 * tp2 * tp1 * tm1 * tm2 * tm3 * tm4 / 144.0; + d[4] = -tp3 * tp2 * tp1 * tau * tm2 * tm3 * tm4 / 144.0; + d[5] = tp3 * tp2 * tp1 * tau * tm1 * tm3 * tm4 / 240.0; + d[6] = -tp3 * tp2 * tp1 * tau * tm1 * tm2 * tm4 / 720.0; + d[7] = tp3 * tp2 * tp1 * tau * tm1 * tm2 * tm3 / 5040.0; + } + + // Compute interpolated point + int indx0 = index - order / 2 + 1; + for (int i = 0; i < vectorLength; i++) + { + valueVector[i] = 0.0; + } + + for (int i = 0; i < order; i++) + { + int jndex = vectorLength * (indx0 + i); + for (int j = 0; j < vectorLength; j++) + { + valueVector[j] += d[i] * valueArray[jndex + j]; + } + } +} + // convert a measurement double metric_conversion(double val, std::string from, std::string to) { json typemap = { diff --git a/tests/UtilitiesTests.cpp b/tests/UtilitiesTests.cpp index 5473dc5..4e7010c 100644 --- a/tests/UtilitiesTests.cpp +++ b/tests/UtilitiesTests.cpp @@ -67,3 +67,162 @@ TEST(UtilitiesTests, createCameraLookVector) { EXPECT_NEAR(cameraLook[1], 0.007999744, 1e-8); EXPECT_NEAR(cameraLook[2], -0.999968001, 1e-8); } + +TEST(UtilitiesTests, lagrangeInterp1Point) { + int numTime = 1; + std::vector<double> singlePoint = {1}; + std::vector<double> interpPoint = {0}; + double startTime = 0; + double delTime = 1; + double time = 0; + int vectorLength = 1; + int order = 8; + + try { + lagrangeInterp(numTime, &singlePoint[0], startTime, delTime, + time, vectorLength, order, &interpPoint[0]); + FAIL() << "Expected an error"; + } + catch(csm::Error &e) { + EXPECT_EQ(e.getError(), csm::Error::INDEX_OUT_OF_RANGE); + } + catch(...) { + FAIL() << "Expected csm INDEX_OUT_OF_RANGE error"; + } +} + +TEST(UtilitiesTests, lagrangeInterp2ndOrder) { + int numTime = 8; + std::vector<double> interpValues = {1, 2, 4, 8, 16, 32, 64, 128}; + std::vector<double> outputValue = {0}; + double startTime = 0; + double delTime = 1; + double time = 3.5; + int vectorLength = 1; + int order = 2; + + lagrangeInterp(numTime, &interpValues[0], startTime, delTime, + time, vectorLength, order, &outputValue[0]); + EXPECT_DOUBLE_EQ(outputValue[0], 24.0 / 2.0); +} + +TEST(UtilitiesTests, lagrangeInterp4thOrder) { + int numTime = 8; + std::vector<double> interpValues = {1, 2, 4, 8, 16, 32, 64, 128}; + std::vector<double> outputValue = {0}; + double startTime = 0; + double delTime = 1; + double time = 3.5; + int vectorLength = 1; + int order = 4; + + lagrangeInterp(numTime, &interpValues[0], startTime, delTime, + time, vectorLength, order, &outputValue[0]); + EXPECT_DOUBLE_EQ(outputValue[0], 180.0 / 16.0); +} + +TEST(UtilitiesTests, lagrangeInterp6thOrder) { + int numTime = 8; + std::vector<double> interpValues = {1, 2, 4, 8, 16, 32, 64, 128}; + std::vector<double> outputValue = {0}; + double startTime = 0; + double delTime = 1; + double time = 3.5; + int vectorLength = 1; + int order = 6; + + lagrangeInterp(numTime, &interpValues[0], startTime, delTime, + time, vectorLength, order, &outputValue[0]); + EXPECT_DOUBLE_EQ(outputValue[0], 2898.0 / 256.0); +} + +TEST(UtilitiesTests, lagrangeInterp8thOrder) { + int numTime = 8; + std::vector<double> interpValues = {1, 2, 4, 8, 16, 32, 64, 128}; + std::vector<double> outputValue = {0}; + double startTime = 0; + double delTime = 1; + double time = 3.5; + int vectorLength = 1; + int order = 8; + + lagrangeInterp(numTime, &interpValues[0], startTime, delTime, + time, vectorLength, order, &outputValue[0]); + EXPECT_DOUBLE_EQ(outputValue[0], 23169.0 / 2048.0); +} + +TEST(UtilitiesTests, lagrangeInterpReduced2ndOrder) { + int numTime = 8; + std::vector<double> interpValues = {1, 2, 4, 8, 16, 32, 64, 128}; + std::vector<double> outputValue = {0}; + double startTime = 0; + double delTime = 1; + double time = 0.5; + int vectorLength = 1; + int order = 8; + + lagrangeInterp(numTime, &interpValues[0], startTime, delTime, + time, vectorLength, order, &outputValue[0]); + EXPECT_DOUBLE_EQ(outputValue[0], 3.0 / 2.0); + + time = 6.5; + lagrangeInterp(numTime, &interpValues[0], startTime, delTime, + time, vectorLength, order, &outputValue[0]); + EXPECT_DOUBLE_EQ(outputValue[0], 192.0 / 2.0); +} + +TEST(UtilitiesTests, lagrangeInterpReduced4thOrder) { + int numTime = 8; + std::vector<double> interpValues = {1, 2, 4, 8, 16, 32, 64, 128}; + std::vector<double> outputValue = {0}; + double startTime = 0; + double delTime = 1; + double time = 1.5; + int vectorLength = 1; + int order = 8; + + lagrangeInterp(numTime, &interpValues[0], startTime, delTime, + time, vectorLength, order, &outputValue[0]); + EXPECT_DOUBLE_EQ(outputValue[0], 45.0 / 16.0); + + time = 5.5; + lagrangeInterp(numTime, &interpValues[0], startTime, delTime, + time, vectorLength, order, &outputValue[0]); + EXPECT_DOUBLE_EQ(outputValue[0], 720.0 / 16.0); +} + +TEST(UtilitiesTests, lagrangeInterpReduced6thOrder) { + int numTime = 8; + std::vector<double> interpValues = {1, 2, 4, 8, 16, 32, 64, 128}; + std::vector<double> outputValue = {0}; + double startTime = 0; + double delTime = 1; + double time = 2.5; + int vectorLength = 1; + int order = 8; + + lagrangeInterp(numTime, &interpValues[0], startTime, delTime, + time, vectorLength, order, &outputValue[0]); + EXPECT_DOUBLE_EQ(outputValue[0], 1449.0 / 256.0); + + time = 4.5; + lagrangeInterp(numTime, &interpValues[0], startTime, delTime, + time, vectorLength, order, &outputValue[0]); + EXPECT_DOUBLE_EQ(outputValue[0], 5796.0 / 256.0); +} + +TEST(UtilitiesTests, lagrangeInterp2D) { + int numTime = 2; + std::vector<double> interpValues = {0, 1, 1, 2}; + std::vector<double> outputValue = {0, 0}; + double startTime = 0; + double delTime = 1; + double time = 0.5; + int vectorLength = 2; + int order = 2; + + lagrangeInterp(numTime, &interpValues[0], startTime, delTime, + time, vectorLength, order, &outputValue[0]); + EXPECT_DOUBLE_EQ(outputValue[0], 0.5); + EXPECT_DOUBLE_EQ(outputValue[1], 1.5); +} -- GitLab