Select Git revision
LineScanCameraTests.cpp
-
Jesse Mapel authoredJesse Mapel authored
LineScanCameraTests.cpp 11.12 KiB
#define _USE_MATH_DEFINES
#include "Fixtures.h"
#include "UsgsAstroLsSensorModel.h"
#include "UsgsAstroPlugin.h"
#include <gtest/gtest.h>
#include <nlohmann/json.hpp>
#include <math.h>
#include <iostream>
using json = nlohmann::json;
TEST_F(ConstVelocityLineScanSensorModel, State) {
std::string modelState = sensorModel->getModelState();
sensorModel->replaceModelState(modelState);
// When this is different, the output is very hard to parse
// TODO implement JSON diff for gtest
EXPECT_EQ(sensorModel->getModelState(), modelState);
}
// Fly by tests
TEST_F(ConstVelocityLineScanSensorModel, Center) {
csm::ImageCoord imagePt(8.0, 8.0);
csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
EXPECT_NEAR(groundPt.x, 9.99999500000, 1e-10);
EXPECT_NEAR(groundPt.y, 0.0, 1e-10);
EXPECT_NEAR(groundPt.z, 0.00999999500, 1e-10);
}
TEST_F(ConstVelocityLineScanSensorModel, Inversion) {
double achievedPrecision;
csm::ImageCoord imagePt(8.5, 8);
csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
csm::ImageCoord imageReprojPt =
sensorModel->groundToImage(groundPt, 0.001, &achievedPrecision);
EXPECT_LT(achievedPrecision, 0.001);
EXPECT_NEAR(imagePt.line, imageReprojPt.line, 1e-3);
EXPECT_NEAR(imagePt.samp, imageReprojPt.samp, 1e-3);
}
TEST_F(ConstVelocityLineScanSensorModel, OffBody) {
csm::ImageCoord imagePt(0.0, 4.0);
csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
EXPECT_NEAR(groundPt.x, 0.04799688020, 1e-10);
EXPECT_NEAR(groundPt.y, -7.99961602495, 1e-10);
EXPECT_NEAR(groundPt.z, 16.00004799688, 1e-10);
}
TEST_F(ConstVelocityLineScanSensorModel, ProximateImageLocus) {
csm::ImageCoord imagePt(8.0, 8.0);
csm::EcefCoord groundPt(10, 2, 1);
double precision;
csm::WarningList warnings;
csm::EcefLocus remoteLocus = sensorModel->imageToRemoteImagingLocus(imagePt);
csm::EcefLocus locus = sensorModel->imageToProximateImagingLocus(
imagePt, groundPt, 0.001, &precision, &warnings);
double locusToGroundX = locus.point.x - groundPt.x;
double locusToGroundY = locus.point.y - groundPt.y;
double locusToGroundZ = locus.point.z - groundPt.z;
EXPECT_NEAR(locus.direction.x, remoteLocus.direction.x, 1e-10);
EXPECT_NEAR(locus.direction.y, remoteLocus.direction.y, 1e-10);
EXPECT_NEAR(locus.direction.z, remoteLocus.direction.z, 1e-10);
EXPECT_NEAR(locusToGroundX * locus.direction.x +
locusToGroundY * locus.direction.y +
locusToGroundZ * locus.direction.z,
0.0, 1e-10);
EXPECT_LT(precision, 0.001);
EXPECT_TRUE(warnings.empty());
}
TEST_F(ConstVelocityLineScanSensorModel, RemoteImageLocus) {
csm::ImageCoord imagePt(8.5, 8.0);
double precision;
csm::WarningList warnings;
csm::EcefLocus locus = sensorModel->imageToRemoteImagingLocus(
imagePt, 0.001, &precision, &warnings);
csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
double lookX = groundPt.x - locus.point.x;
double lookY = groundPt.y - locus.point.y;
double lookZ = groundPt.z - locus.point.z;
double lookMag = sqrt(lookX * lookX + lookY * lookY + lookZ * lookZ);
lookX /= lookMag;
lookY /= lookMag;
lookZ /= lookMag;
EXPECT_NEAR(locus.direction.x, lookX, 1e-10);
EXPECT_NEAR(locus.direction.y, lookY, 1e-10);
EXPECT_NEAR(locus.direction.z, lookZ, 1e-10);
EXPECT_NEAR(locus.point.x, 1000.0, 1e-10);
EXPECT_NEAR(locus.point.y, 0.0, 1e-10);
EXPECT_NEAR(locus.point.z, 0.0, 1e-10);
EXPECT_LT(precision, 0.001);
EXPECT_TRUE(warnings.empty());
}
TEST_F(ConstVelocityLineScanSensorModel, calculateAttitudeCorrection) {
std::vector<double> adj;
double attCorr[9];
adj.resize(15, 0);
// Pi/2 with simply compensating for member variable m_flyingHeight in
// UsgsAstroLsSensorModel
adj[7] = (M_PI / 2) * sensorModel->m_flyingHeight;
sensorModel->calculateAttitudeCorrection(999.5, adj, attCorr);
// EXPECT_NEARs are used here instead of EXPECT_DOUBLE_EQs because index 0 and
// 8 of the matrix are evaluating to 6.12...e-17. This is too small to be
// worried about here, but EXPECT_DOUBLE_EQ is too sensitive.
EXPECT_NEAR(attCorr[0], 0, 1e-8);
EXPECT_NEAR(attCorr[1], 0, 1e-8);
EXPECT_NEAR(attCorr[2], 1, 1e-8);
EXPECT_NEAR(attCorr[3], 0, 1e-8);
EXPECT_NEAR(attCorr[4], 1, 1e-8);
EXPECT_NEAR(attCorr[5], 0, 1e-8);
EXPECT_NEAR(attCorr[6], -1, 1e-8);
EXPECT_NEAR(attCorr[7], 0, 1e-8);
EXPECT_NEAR(attCorr[8], 0, 1e-8);
}
TEST_F(OrbitalLineScanSensorModel, getIlluminationDirectionStationary) {
// Get state information, replace sun position / velocity to hit third case:
// One position, no velocity.
std::string state = sensorModel->getModelState();
json jState = stateAsJson(state);
jState["m_sunPosition"] = std::vector<double>{100.0, 100.0, 100.0};
jState["m_sunVelocity"] = std::vector<double>{};
sensorModel->replaceModelState(jState.dump());
csm::ImageCoord imagePt(8.5, 8);
csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
csm::EcefVector direction = sensorModel->getIlluminationDirection(groundPt);
// Calculate expected sun direction
// These are the ground point coordinates minus constant sun positions.
double expected_x = groundPt.x - sensorModel->m_sunPosition[0];
double expected_y = groundPt.y - sensorModel->m_sunPosition[1];
double expected_z = groundPt.z - sensorModel->m_sunPosition[2];
// normalize
double scale = sqrt((expected_x * expected_x) + (expected_y * expected_y) +
(expected_z * expected_z));
expected_x /= scale;
expected_y /= scale;
expected_z /= scale;
EXPECT_DOUBLE_EQ(direction.x, expected_x);
EXPECT_DOUBLE_EQ(direction.y, expected_y);
EXPECT_DOUBLE_EQ(direction.z, expected_z);
}
TEST_F(OrbitalLineScanSensorModel, getSunPositionLagrange) {
csm::EcefVector sunPosition = sensorModel->getSunPosition(-.6);
EXPECT_NEAR(sunPosition.x, 125, 1e-11);
EXPECT_NEAR(sunPosition.y, 125, 1e-11);
EXPECT_NEAR(sunPosition.z, 125, 1e-11);
}
TEST_F(OrbitalLineScanSensorModel, getSunPositionLinear) {
// Get state information, replace sun position / velocity to hit third case:
// One position, no velocity.
std::string state = sensorModel->getModelState();
json jState = stateAsJson(state);
jState["m_sunPosition"] = std::vector<double>{100.0, 100.0, 100.0};
jState["m_sunVelocity"] = std::vector<double>{50.0, 50.0, 50.0};
sensorModel->replaceModelState(jState.dump());
csm::EcefVector sunPosition = sensorModel->getSunPosition(.5);
EXPECT_DOUBLE_EQ(sunPosition.x, 125);
EXPECT_DOUBLE_EQ(sunPosition.y, 125);
EXPECT_DOUBLE_EQ(sunPosition.z, 125);
}
TEST_F(OrbitalLineScanSensorModel, getSunPositionStationary) {
// Get state information, replace sun position / velocity to hit third case:
// One position, no velocity.
std::string state = sensorModel->getModelState();
json jState = stateAsJson(state);
jState["m_sunPosition"] = std::vector<double>{100.0, 100.0, 100.0};
jState["m_sunVelocity"] = std::vector<double>{};
sensorModel->replaceModelState(jState.dump());
csm::EcefVector sunPosition = sensorModel->getSunPosition(1);
EXPECT_DOUBLE_EQ(sunPosition.x, 100);
EXPECT_DOUBLE_EQ(sunPosition.y, 100);
EXPECT_DOUBLE_EQ(sunPosition.z, 100);
}
TEST_F(OrbitalLineScanSensorModel, Center) {
csm::ImageCoord imagePt(8.5, 8.0);
csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
EXPECT_NEAR(groundPt.x, 999999.67004351015, 1e-9);
EXPECT_NEAR(groundPt.y, 0.0, 1e-9);
EXPECT_NEAR(groundPt.z, -812.35021438796923, 1e-9);
}
TEST_F(OrbitalLineScanSensorModel, Inversion) {
double achievedPrecision;
for (double line = 0.5; line < 16; line++) {
csm::ImageCoord imagePt(line, 8);
csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
csm::ImageCoord imageReprojPt =
sensorModel->groundToImage(groundPt, 0.001, &achievedPrecision);
// groundToImage has a default precision of 0.001m and each pixel is 100m
// so we should be within 0.1 pixels
EXPECT_LT(achievedPrecision, 0.001);
EXPECT_NEAR(imagePt.line, imageReprojPt.line, 0.1);
EXPECT_NEAR(imagePt.samp, imageReprojPt.samp, 0.1);
}
}
TEST_F(OrbitalLineScanSensorModel, ImageToGroundHeight) {
csm::ImageCoord imagePt(8.5, 8);
csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 100.0);
double height = sqrt(groundPt.x * groundPt.x + groundPt.y * groundPt.y +
groundPt.z * groundPt.z);
EXPECT_DOUBLE_EQ(height, 1000100);
}
TEST_F(OrbitalLineScanSensorModel, InversionHeight) {
for (double line = 0.5; line < 16; line++) {
csm::ImageCoord imagePt(line, 8);
csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 100.0);
csm::ImageCoord imageReprojPt = sensorModel->groundToImage(groundPt);
// groundToImage has a default precision of 0.001m and each pixel is 100m
// so we should be within 0.1 pixels
EXPECT_NEAR(imagePt.line, imageReprojPt.line, 0.1);
EXPECT_NEAR(imagePt.samp, imageReprojPt.samp, 0.1);
}
}
TEST_F(OrbitalLineScanSensorModel, InversionReallyHigh) {
for (double line = 0.5; line < 16; line++) {
csm::ImageCoord imagePt(line, 8);
csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 4900.0);
csm::ImageCoord imageReprojPt = sensorModel->groundToImage(groundPt);
// groundToImage has a default precision of 0.001m and each pixel is 2m
// so we should be within 0.002 pixels
EXPECT_NEAR(imagePt.line, imageReprojPt.line, 0.002);
EXPECT_NEAR(imagePt.samp, imageReprojPt.samp, 0.002);
}
}
TEST_F(OrbitalLineScanSensorModel, ReferenceDateTime) {
std::string date = sensorModel->getReferenceDateAndTime();
EXPECT_EQ(date, "2000-01-01T00:16:39Z");
}
TEST_F(TwoLineScanSensorModels, CrossCovariance) {
std::vector<double> crossCovars =
sensorModel1->getCrossCovarianceMatrix(*sensorModel2);
ASSERT_EQ(crossCovars.size(), sensorModel1->getNumParameters() *
sensorModel2->getNumParameters());
for (int i = 0; i < sensorModel1->getNumParameters(); i++) {
for (int j = 0; j < sensorModel2->getNumParameters(); j++) {
EXPECT_EQ(crossCovars[i * sensorModel2->getNumParameters() + j], 0.0)
<< "Value at row " << i << " column " << j;
}
}
std::vector<double> covars =
sensorModel1->getCrossCovarianceMatrix(*sensorModel1);
ASSERT_EQ(covars.size(), 16 * 16);
for (int i = 0; i < 16; i++) {
for (int j = 0; j < 16; j++) {
if (i == j) {
EXPECT_GT(covars[i * 16 + j], 0.0)
<< "Value at row " << i << " column " << j;
} else {
EXPECT_EQ(covars[i * 16 + j], 0.0)
<< "Value at row " << i << " column " << j;
}
}
}
std::vector<double> fixedCovars = sensorModel1->getCrossCovarianceMatrix(
*sensorModel1, csm::param::NON_ADJUSTABLE);
EXPECT_EQ(fixedCovars.size(), 0);
}
TEST_F(ConstVelocityLineScanSensorModel, FocalLengthAdjustment) {
csm::ImageCoord imagePt(8.5, 4.0);
sensorModel->setParameterValue(15, 0.9 * sensorModel->m_halfSwath);
csm::EcefLocus locus = sensorModel->imageToRemoteImagingLocus(imagePt);
double scale = sqrt(5 * 5 + 0.4 * 0.4 + 0.05 * 0.05);
EXPECT_DOUBLE_EQ(locus.direction.x, -5.0 / scale);
EXPECT_DOUBLE_EQ(locus.direction.y, -0.4 / scale);
EXPECT_DOUBLE_EQ(locus.direction.z, -0.05 / scale);
}