From 56bbb366461fec54462951a0c2da5b4ae3da04a5 Mon Sep 17 00:00:00 2001
From: Oleg Alexandrov <oleg.alexandrov@gmail.com>
Date: Mon, 9 Aug 2021 18:47:21 +0300
Subject: [PATCH] Add fix for ground to image calculation for LRO NAC cameras
 (#341)

* Add fix for ground to image calculation for LRO NAC cameras

* Make the choice of the sign of m_iTransL only once at model loading

* Reword a comment

* Robustness fix, must always ensure fractional part is 0.5

* Remove the abs, cannot assume those values are positive

* Added opposite flight and detector test

Co-authored-by: Jesse Mapel <jmapel@usgs.gov>
---
 src/UsgsAstroLsSensorModel.cpp         |  32 +++-
 tests/Fixtures.h                       |  26 +++
 tests/LineScanCameraTests.cpp          |   8 +
 tests/data/flippedOrbitalLineScan.json | 241 +++++++++++++++++++++++++
 4 files changed, 306 insertions(+), 1 deletion(-)
 create mode 100644 tests/data/flippedOrbitalLineScan.json

diff --git a/src/UsgsAstroLsSensorModel.cpp b/src/UsgsAstroLsSensorModel.cpp
index b329d34..1fe6bed 100644
--- a/src/UsgsAstroLsSensorModel.cpp
+++ b/src/UsgsAstroLsSensorModel.cpp
@@ -648,6 +648,36 @@ 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];
+  }
 }
 
 //---------------------------------------------------------------------------
@@ -2045,7 +2075,7 @@ std::vector<double> UsgsAstroLsSensorModel::computeDetectorView(
                        bodyToCamera[5] * bodyLookY +
                        bodyToCamera[8] * bodyLookZ;
   MESSAGE_LOG(
-      "computeDetectorView: look vector (camrea ref frame)"
+      "computeDetectorView: look vector (camera ref frame)"
       "{} {} {}",
       cameraLookX, cameraLookY, cameraLookZ)
 
diff --git a/tests/Fixtures.h b/tests/Fixtures.h
index a6c8848..d606fb0 100644
--- a/tests/Fixtures.h
+++ b/tests/Fixtures.h
@@ -261,6 +261,32 @@ class OrbitalLineScanSensorModel : public ::testing::Test {
   }
 };
 
+class FlippedOrbitalLineScanSensorModel : public ::testing::Test {
+ protected:
+  csm::Isd isd;
+  UsgsAstroLsSensorModel *sensorModel;
+
+  void SetUp() override {
+    sensorModel = NULL;
+
+    isd.setFilename("data/flippedOrbitalLineScan.img");
+    UsgsAstroPlugin cameraPlugin;
+
+    csm::Model *model = cameraPlugin.constructModelFromISD(
+        isd, "USGS_ASTRO_LINE_SCANNER_SENSOR_MODEL");
+    sensorModel = dynamic_cast<UsgsAstroLsSensorModel *>(model);
+
+    ASSERT_NE(sensorModel, nullptr);
+  }
+
+  void TearDown() override {
+    if (sensorModel) {
+      delete sensorModel;
+      sensorModel = NULL;
+    }
+  }
+};
+
 class TwoLineScanSensorModels : public ::testing::Test {
  protected:
   csm::Isd isd;
diff --git a/tests/LineScanCameraTests.cpp b/tests/LineScanCameraTests.cpp
index 8ae9bd8..f08abb7 100644
--- a/tests/LineScanCameraTests.cpp
+++ b/tests/LineScanCameraTests.cpp
@@ -276,6 +276,14 @@ TEST_F(OrbitalLineScanSensorModel, ReferenceDateTime) {
   EXPECT_EQ(date, "2000-01-01T00:16:39Z");
 }
 
+TEST_F(FlippedOrbitalLineScanSensorModel, OppositeFlightDetector) {
+  csm::ImageCoord imagePt(8.7, 8.0);
+  csm::EcefCoord groundPt = sensorModel->imageToGround(imagePt, 0.0);
+  csm::ImageCoord backProjImagePt = sensorModel->groundToImage(groundPt);
+  EXPECT_NEAR(imagePt.line, backProjImagePt.line, 0.001);
+  EXPECT_NEAR(imagePt.samp, backProjImagePt.samp, 0.001);
+}
+
 TEST_F(TwoLineScanSensorModels, CrossCovariance) {
   std::vector<double> crossCovars =
       sensorModel1->getCrossCovarianceMatrix(*sensorModel2);
diff --git a/tests/data/flippedOrbitalLineScan.json b/tests/data/flippedOrbitalLineScan.json
new file mode 100644
index 0000000..3059d9d
--- /dev/null
+++ b/tests/data/flippedOrbitalLineScan.json
@@ -0,0 +1,241 @@
+{
+  "name_model" : "USGS_ASTRO_LINE_SCANNER_SENSOR_MODEL",
+  "name_platform" : "TEST_PLATFORM",
+  "name_sensor" : "TEST_SENSOR",
+  "center_ephemeris_time": 1000.0,
+  "starting_ephemeris_time": 999.2,
+  "line_scan_rate": [
+    [0.5, -0.85, 0.1]
+  ],
+  "detector_sample_summing": 1,
+  "detector_line_summing": 1,
+  "t0_ephemeris": -0.8,
+  "dt_ephemeris": 0.1,
+  "t0_quaternion": -0.8,
+  "dt_quaternion": 0.1,
+  "focal2pixel_lines": [0.0, 10.0, 0.0],
+  "focal2pixel_samples": [0.0, 0.0, 10.0],
+  "focal_length_model": {
+    "focal_length": 50
+  },
+  "image_lines": 16,
+  "image_samples": 16,
+  "detector_center" : {
+    "line" : 0.5,
+    "sample" : 8.0
+  },
+  "interpolation_method": "lagrange",
+  "optical_distortion": {
+    "radial": {
+      "coefficients": [0.0, 0.0, 0.0]
+    }
+  },
+  "radii": {
+    "semimajor": 1000,
+    "semiminor": 1000,
+    "unit":"km"
+  },
+  "reference_height": {
+    "maxheight": 1000,
+    "minheight": -1000,
+    "unit": "m"
+  },
+  "instrument_position": {
+    "unit": "km",
+    "positions": [
+      [1050.0, 0.0, -0.0],
+      [1049.99999524, 0.0, -0.1],
+      [1049.99998095, 0.0, -0.2],
+      [1049.99995714, 0.0, -0.3],
+      [1049.99992381, 0.0, -0.39999999],
+      [1049.99988095, 0.0, -0.49999998],
+      [1049.99982857, 0.0, -0.59999997],
+      [1049.99976667, 0.0, -0.69999995],
+      [1049.99969524, 0.0, -0.79999992],
+      [1049.99961429, 0.0, -0.89999989],
+      [1049.99952381, 0.0, -0.99999985],
+      [1049.99942381, 0.0, -1.0999998],
+      [1049.99931429, 0.0, -1.19999974],
+      [1049.99919524, 0.0, -1.29999967],
+      [1049.99906667, 0.0, -1.39999959],
+      [1049.99892857, 0.0, -1.49999949]
+    ],
+    "velocities": [
+      [-0.0, 0.0, -1.0500],
+      [-0.1, 0.0, -1.04999999524],
+      [-0.2, 0.0, -1.04999998095],
+      [-0.3, 0.0, -1.04999995714],
+      [-0.39999999, 0.0 ,-1.04999992381],
+      [-0.49999998, 0.0 ,-1.04999988095],
+      [-0.59999997, 0.0 ,-1.04999982857],
+      [-0.69999995, 0.0 ,-1.04999976667],
+      [-0.79999992, 0.0 ,-1.04999969524],
+      [-0.89999989, 0.0 ,-1.04999961429],
+      [-0.99999985, 0.0 ,-1.04999952381],
+      [-1.0999998, 0.0 ,-1.04999942381],
+      [-1.19999974, 0.0 ,-1.04999931429],
+      [-1.29999967, 0.0 ,-1.04999919524],
+      [-1.39999959, 0.0 ,-1.04999906667],
+      [-1.49999949, 0.0, -1.04999892857]
+    ],
+    "ephemeris_times": [
+      999.2,
+      999.3,
+      999.4,
+      999.5,
+      999.6,
+      999.7,
+      999.8,
+      999.9,
+      1000.0,
+      1000.1,
+      1000.2,
+      1000.3,
+      1000.4,
+      1000.5,
+      1000.6,
+      1000.7
+    ],
+    "reference_frame": 1
+  },
+  "instrument_pointing": {
+    "quaternions": [
+      [0.70710678, 0.0, -0.70710678, 0.0],
+      [0.70707311, 0.0, -0.70714045, 0.0],
+      [0.70703943, 0.0, -0.70717412, 0.0],
+      [0.70700576, 0.0, -0.70720779, 0.0],
+      [0.70697208, 0.0, -0.70724146, 0.0],
+      [0.7069384,  0.0, -0.70727512, 0.0],
+      [0.70690472, 0.0, -0.70730878, 0.0],
+      [0.70687104, 0.0, -0.70734244, 0.0],
+      [0.70683736, 0.0, -0.7073761 , 0.0],
+      [0.70680367, 0.0, -0.70740976, 0.0],
+      [0.70676998, 0.0, -0.70744342, 0.0],
+      [0.70673629, 0.0, -0.70747707, 0.0],
+      [0.7067026,  0.0, -0.70751073, 0.0],
+      [0.70666891, 0.0, -0.70754438, 0.0],
+      [0.70663522, 0.0, -0.70757803, 0.0],
+      [0.70660152, 0.0, -0.70761168, 0.0]
+    ],
+    "ephemeris_times": [
+      999.2,
+      999.3,
+      999.4,
+      999.5,
+      999.6,
+      999.7,
+      999.8,
+      999.9,
+      1000.0,
+      1000.1,
+      1000.2,
+      1000.3,
+      1000.4,
+      1000.5,
+      1000.6,
+      1000.7
+    ],
+    "angular_velocities": [
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0]
+    ],
+    "reference_frame": 1
+  },
+  "body_rotation": {
+    "quaternions": [
+      [1.0, 0.0, 0.0, 0.0],
+      [1.0, 0.0, 0.0, 0.0],
+      [1.0, 0.0, 0.0, 0.0],
+      [1.0, 0.0, 0.0, 0.0],
+      [1.0, 0.0, 0.0, 0.0],
+      [1.0, 0.0, 0.0, 0.0],
+      [1.0, 0.0, 0.0, 0.0],
+      [1.0, 0.0, 0.0, 0.0],
+      [1.0, 0.0, 0.0, 0.0],
+      [1.0, 0.0, 0.0, 0.0],
+      [1.0, 0.0, 0.0, 0.0],
+      [1.0, 0.0, 0.0, 0.0],
+      [1.0, 0.0, 0.0, 0.0],
+      [1.0, 0.0, 0.0, 0.0],
+      [1.0, 0.0, 0.0, 0.0],
+      [1.0, 0.0, 0.0, 0.0]
+    ],
+    "ephemeris_times": [
+      999.2,
+      999.3,
+      999.4,
+      999.5,
+      999.6,
+      999.7,
+      999.8,
+      999.9,
+      1000.0,
+      1000.1,
+      1000.2,
+      1000.3,
+      1000.4,
+      1000.5,
+      1000.6,
+      1000.7
+    ],
+    "angular_velocities": [
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0],
+      [0.0, 0.0, 0.0]
+    ],
+    "reference_frame": 1,
+    "constant_frames": [1, 2, 3],
+    "time_dependent_frames": [4, 5, 6]
+  },
+  "starting_detector_line": 1,
+  "starting_detector_sample": 0,
+  "sun_position": {
+    "positions": [
+      [0.100, 0.100, 0.100],
+      [0.150, 0.150, 0.150],
+      [0.200, 0.200, 0.200],
+      [0.250, 0.250, 0.250]
+    ],
+    "velocities": [
+      [0.125, 0.125, 0.125],
+      [0.125, 0.125, 0.125],
+      [0.125, 0.125, 0.125],
+      [0.125, 0.125, 0.125]
+    ],
+    "ephemeris_times": [
+      999.2,
+      999.6,
+      1000.0,
+      1000.4
+    ],
+    "reference_frame": 1,
+    "unit": "km"
+  }
+}
-- 
GitLab