diff --git a/CMakeLists.txt b/CMakeLists.txt index 700968bf30b41e2d4a0a26392e48f6ab4be494c0..62ed6767a2c1634e8f6aad680a5c2b31d1a3b8ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,16 +21,20 @@ find_package(Python REQUIRED COMPONENTS Development) find_package(nlohmann_json REQUIRED) # Library setup +set(ALE_BUILD_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include/") +set(ALE_INSTALL_INCLUDE_DIR "include/ale") add_library(ale SHARED ${CMAKE_CURRENT_SOURCE_DIR}/src/ale.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/src/Rotation.cpp) -set(ALE_BUILD_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/include/") + ${CMAKE_CURRENT_SOURCE_DIR}/src/Rotation.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Isd.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/Util.cpp) set(ALE_HEADERS "${ALE_BUILD_INCLUDE_DIR}/ale.h" - "${ALE_BUILD_INCLUDE_DIR}/Rotation.h") -set(ALE_INSTALL_INCLUDE_DIR "include/ale") + "${ALE_BUILD_INCLUDE_DIR}/Rotation.h" + "${ALE_BUILD_INCLUDE_DIR}/Isd.h" + "${ALE_BUILD_INCLUDE_DIR}/Util.h") set_target_properties(ale PROPERTIES - VERSION ${PROJECT_VERSION} - SOVERSION 0) + VERSION ${PROJECT_VERSION} + SOVERSION 0) # Use generator expressions so that downstream projects can use this target target_include_directories(ale PUBLIC diff --git a/include/Distortion.h b/include/Distortion.h new file mode 100644 index 0000000000000000000000000000000000000000..dd51f8646313d01595707a0a991860aed5f98bb3 --- /dev/null +++ b/include/Distortion.h @@ -0,0 +1,14 @@ +#ifndef ALE_DISTORTION_H +#define ALE_DISTORTION_H + +namespace ale { + enum DistortionType { + TRANSVERSE, + RADIAL, + KAGUYALISM, + DAWNFC, + LROLROCNAC + }; +} + +#endif diff --git a/include/Isd.h b/include/Isd.h new file mode 100644 index 0000000000000000000000000000000000000000..c1a544cb15e16e7be0f7bf7b406743298ee45eab --- /dev/null +++ b/include/Isd.h @@ -0,0 +1,78 @@ +#ifndef ALE_ISD_H +#define ALE_ISD_H + +#include <string> +#include <vector> +#include <map> + +#include "Util.h" +#include "Distortion.h" + +//#include "Rotation.h" +//#include "State.h" + +namespace ale { + + using json = nlohmann::json; + + class Isd { + public: + + Isd(std::string); + + std::string usgscsm_name_model; + std::string name_platform; + std::string image_id; + std::string name_sensor; + + double semi_major; + double semi_minor; + + double detector_sample_summing; + double detector_line_summing; + + double focal_length; + double focal_uncertainty; + + double detector_center_line; + double detector_center_sample; + + // should probably be ints + double starting_detector_line; + double starting_detector_sample; + + std::vector<double> focal2pixel_line; + std::vector<double> focal2pixel_sample; + + // maybe change + DistortionType distortion_model; + std::vector<double> distortion_coefficients; + + unsigned int image_lines; + unsigned int image_samples; + + double max_reference_height; + double min_reference_height; + + std::vector<std::vector<double>> line_scan_rate; + + double starting_ephemeris_time; + double center_ephemeris_time; + + double t0_ephemeris_time; + double dt_ephemeris_time; + + double t0_quaternions; + double dt_quaternions; + + json naif_keywords; + + //Positions sensor_pos; + //Positions sun_pos; + + //Rotation sensor_orientation; + //Rotation body_orientaion; + }; +} + +#endif diff --git a/include/Util.h b/include/Util.h new file mode 100644 index 0000000000000000000000000000000000000000..c7cd3d0093ebaa617196fad956a6332952e1b6f3 --- /dev/null +++ b/include/Util.h @@ -0,0 +1,46 @@ +#ifndef ALE_UTIL_H +#define ALE_UTIL_H + +#include <string> +#include <nlohmann/json.hpp> + +#include "Isd.h" +#include "Distortion.h" + +namespace ale { + using json = nlohmann::json; + + double getMinHeight(nlohmann::json isd); + std::string getSensorModelName(json isd); + std::string getImageId(json isd); + std::string getSensorName(json isd); + std::string getPlatformName(json isd); + std::string getLogFile(nlohmann::json isd); + int getTotalLines(json isd); + int getTotalSamples(json isd); + double getStartingTime(json isd); + double getCenterTime(json isd); + std::vector<std::vector<double>> getLineScanRate(json isd); + int getSampleSumming(json isd); + int getLineSumming(json isd); + double getFocalLength(json isd); + double getFocalLengthUncertainty(json isd); + std::vector<double> getFocal2PixelLines(json isd); + std::vector<double> getFocal2PixelSamples(json isd); + double getDetectorCenterLine(json isd); + double getDetectorCenterSample(json isd); + double getDetectorStartingLine(json isd); + double getDetectorStartingSample(json isd); + double getMinHeight(json isd); + double getMaxHeight(json isd); + double getSemiMajorRadius(json isd); + double getSemiMinorRadius(json isd); + DistortionType getDistortionModel(json isd); + std::vector<double> getDistortionCoeffs(json isd); + std::vector<double> getSunPositions(json isd); + std::vector<double> getSensorPositions(json isd); + std::vector<double> getSensorVelocities(json isd); + std::vector<double> getSensorOrientations(json isd); +} + +#endif diff --git a/include/ale.h b/include/ale.h index fb7841ba50cbb6831dfab782f1e50232453b3c8a..ee268126c0aac3a4a20ef3e9cb995a3cfb006649 100644 --- a/include/ale.h +++ b/include/ale.h @@ -1,7 +1,6 @@ #ifndef ALE_INCLUDE_ALE_H #define ALE_INCLUDE_ALE_H -#include <nlohmann/json.hpp> #include <string> #include <vector> @@ -15,9 +14,9 @@ namespace ale { /// Interpolation enum for defining different methods of interpolation enum interpolation { /// Interpolate using linear interpolation - linear, + LINEAR, /// Interpolate using spline interpolation - spline + SPLINE }; /** diff --git a/src/Isd.cpp b/src/Isd.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cd0e1456484c3d443fad31c38683460967dba718 --- /dev/null +++ b/src/Isd.cpp @@ -0,0 +1,44 @@ +#include "Isd.h" +#include "Util.h" + + +ale::Isd::Isd(std::string isd_file) { + json isd = json::parse(isd_file); + + usgscsm_name_model = getSensorModelName(isd); + image_id = getImageId(isd); + name_platform = getPlatformName(isd); + name_sensor = getSensorName(isd); + + image_lines = getTotalLines(isd); + image_samples = getTotalSamples(isd); + + starting_ephemeris_time = getStartingTime(isd); + center_ephemeris_time = getCenterTime(isd); + + line_scan_rate = getLineScanRate(isd); + + detector_sample_summing = getSampleSumming(isd); + detector_line_summing = getLineSumming(isd); + + focal_length = getFocalLength(isd); + focal_uncertainty = getFocalLengthUncertainty(isd); + + focal2pixel_line = getFocal2PixelLines(isd); + focal2pixel_sample = getFocal2PixelSamples(isd); + + detector_center_line = getDetectorCenterLine(isd); + detector_center_sample = getDetectorCenterSample(isd); + + starting_detector_line = getDetectorStartingLine(isd); + starting_detector_sample = getDetectorStartingSample(isd); + + min_reference_height = getMinHeight(isd); + max_reference_height = getMaxHeight(isd); + + semi_major = getSemiMajorRadius(isd); + semi_minor = getSemiMinorRadius(isd); + + distortion_model = getDistortionModel(isd); + distortion_coefficients = getDistortionCoeffs(isd); +} diff --git a/src/Util.cpp b/src/Util.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f93d76561d7ce0763c2f2b4a1871935b9efa5470 --- /dev/null +++ b/src/Util.cpp @@ -0,0 +1,472 @@ +#include <stdexcept> + +#include "Util.h" + +std::string ale::getSensorModelName(json isd) { + std::string name = ""; + try { + name = isd.at("name_model"); + } catch (...) { + throw std::runtime_error("Could not parse the sensor model name."); + } + return name; +} + +std::string ale::getImageId(json isd) { + std::string id = ""; + try { + id = isd.at("image_identifier"); + } catch (...) { + throw std::runtime_error("Could not parse the image identifier."); + } + return id; +} + +std::string ale::getSensorName(json isd) { + std::string name = ""; + try { + name = isd.at("name_sensor"); + } catch (...) { + throw std::runtime_error("Could not parse the sensor name."); + } + return name; +} + +std::string ale::getPlatformName(json isd) { + std::string name = ""; + try { + name = isd.at("name_platform"); + } catch (...) { + throw std::runtime_error("Could not parse the platform name."); + } + return name; +} + +std::string ale::getLogFile(nlohmann::json isd) { + std::string file = ""; + try { + file = isd.at("log_file"); + } catch (...) { + throw std::runtime_error("Could not parse the log filename."); + } + return file; +} + +int ale::getTotalLines(json isd) { + int lines = 0; + try { + lines = isd.at("image_lines"); + } catch (...) { + throw std::runtime_error( + "Could not parse the number of lines in the image."); + } + return lines; +} + +int ale::getTotalSamples(json isd) { + int samples = 0; + try { + samples = isd.at("image_samples"); + } catch (...) { + throw std::runtime_error( + "Could not parse the number of samples in the image."); + } + return samples; +} + +double ale::getStartingTime(json isd) { + double time = 0.0; + try { + time = isd.at("starting_ephemeris_time"); + } catch (...) { + throw std::runtime_error("Could not parse the image start time."); + } + return time; +} + +double ale::getCenterTime(json isd) { + double time = 0.0; + try { + time = isd.at("center_ephemeris_time"); + } catch (...) { + throw std::runtime_error("Could not parse the center image time."); + } + return time; +} + +std::vector<std::vector<double>> ale::getLineScanRate(json isd) { + std::vector<std::vector<double>> lines; + try { + for (auto &scanRate : isd.at("line_scan_rate")) { + lines.push_back(scanRate.get<std::vector<double>>()); + } + } catch (...) { + throw std::runtime_error("Could not parse the integration start lines in " + "the integration time table."); + } + return lines; +} + + +int ale::getSampleSumming(json isd) { + int summing = 0; + try { + summing = isd.at("detector_sample_summing"); + } catch (...) { + throw std::runtime_error( + "Could not parse the sample direction detector pixel summing."); + } + return summing; +} + +int ale::getLineSumming(json isd) { + int summing = 0; + try { + summing = isd.at("detector_line_summing"); + } catch (...) { + throw std::runtime_error( + "Could not parse the line direction detector pixel summing."); + } + return summing; +} + +double ale::getFocalLength(json isd) { + double length = 0.0; + try { + length = isd.at("focal_length_model").at("focal_length"); + } catch (...) { + throw std::runtime_error("Could not parse the focal length."); + } + return length; +} + +double ale::getFocalLengthUncertainty(json isd) { + double uncertainty = 1.0; + try { + uncertainty = isd.at("focal_length_model").value("focal_uncertainty", uncertainty); + } catch (...) { + throw std::runtime_error("Could not parse the focal length uncertainty."); + } + return uncertainty; +} + +std::vector<double> ale::getFocal2PixelLines(json isd) { + std::vector<double> transformation; + try { + transformation = isd.at("focal2pixel_lines").get<std::vector<double>>(); + } catch (...) { + throw std::runtime_error("Could not parse the focal plane coordinate to " + "detector lines transformation."); + } + return transformation; +} + +std::vector<double> ale::getFocal2PixelSamples(json isd) { + std::vector<double> transformation; + try { + transformation = isd.at("focal2pixel_samples").get<std::vector<double>>(); + } catch (...) { + throw std::runtime_error("Could not parse the focal plane coordinate to " + "detector samples transformation."); + } + return transformation; +} + +double ale::getDetectorCenterLine(json isd) { + double line; + try { + line = isd.at("detector_center").at("line"); + } catch (...) { + throw std::runtime_error("Could not parse the detector center line."); + } + return line; +} + +double ale::getDetectorCenterSample(json isd) { + double sample; + try { + sample = isd.at("detector_center").at("sample"); + } catch (...) { + throw std::runtime_error("Could not parse the detector center sample."); + } + return sample; +} + +double ale::getDetectorStartingLine(json isd) { + double line; + try { + line = isd.at("starting_detector_line"); + } catch (...) { + throw std::runtime_error("Could not parse the detector starting line."); + } + return line; +} + +double ale::getDetectorStartingSample(json isd) { + double sample; + try { + sample = isd.at("starting_detector_sample"); + } catch (...) { + throw std::runtime_error("Could not parse the detector starting sample."); + } + return sample; +} + +double ale::getMinHeight(json isd) { + double height = 0.0; + try { + json referenceHeight = isd.at("reference_height"); + json minHeight = referenceHeight.at("minheight"); + height = minHeight.get<double>(); + } catch (...) { + throw std::runtime_error( + "Could not parse the minimum height above the reference ellipsoid."); + } + return height; +} + +double ale::getMaxHeight(json isd) { + double height = 0.0; + try { + json referenceHeight = isd.at("reference_height"); + json maxHeight = referenceHeight.at("maxheight"); + + height = maxHeight.get<double>(); + } catch (...) { + throw std::runtime_error( + "Could not parse the maximum height above the reference ellipsoid."); + } + return height; +} + +double ale::getSemiMajorRadius(json isd) { + double radius = 0.0; + try { + json radii = isd.at("radii"); + json semiMajor = radii.at("semimajor"); + radius = semiMajor.get<double>(); + + } catch (...) { + throw std::runtime_error( + "Could not parse the reference ellipsoid semimajor radius."); + } + return radius; +} + +double ale::getSemiMinorRadius(json isd) { + double radius = 0.0; + try { + json radii = isd.at("radii"); + json semiMinor = radii.at("semiminor"); + radius = semiMinor.get<double>(); + + } catch (...) { + throw std::runtime_error( + "Could not parse the reference ellipsoid semiminor radius."); + } + return radius; +} + +// Converts the distortion model name from the ISD (string) to the enumeration +// type. Defaults to transverse +ale::DistortionType ale::getDistortionModel(json isd) { + try { + json distoriton_subset = isd.at("optical_distortion"); + + json::iterator it = distoriton_subset.begin(); + + std::string distortion = (std::string)it.key(); + + if (distortion.compare("transverse") == 0) { + return DistortionType::TRANSVERSE; + } else if (distortion.compare("radial") == 0) { + return DistortionType::RADIAL; + } else if (distortion.compare("kaguyalism") == 0) { + return DistortionType::KAGUYALISM; + } else if (distortion.compare("dawnfc") == 0) { + return DistortionType::DAWNFC; + } else if (distortion.compare("lrolrocnac") == 0) { + return DistortionType::LROLROCNAC; + } + } catch (...) { + throw std::runtime_error("Could not parse the distortion model."); + } + return DistortionType::TRANSVERSE; +} + +std::vector<double> ale::getDistortionCoeffs(json isd) { + std::vector<double> coefficients; + + ale::DistortionType distortion = getDistortionModel(isd); + + switch (distortion) { + case DistortionType::TRANSVERSE: { + try { + std::vector<double> coefficientsX, coefficientsY; + + coefficientsX = isd.at("optical_distortion") + .at("transverse") + .at("x") + .get<std::vector<double>>(); + coefficientsX.resize(10, 0.0); + + coefficientsY = isd.at("optical_distortion") + .at("transverse") + .at("y") + .get<std::vector<double>>(); + coefficientsY.resize(10, 0.0); + + coefficients = coefficientsX; + + coefficients.insert(coefficients.end(), coefficientsY.begin(), + coefficientsY.end()); + return coefficients; + } catch (...) { + throw std::runtime_error( + "Could not parse a set of transverse distortion model coefficients."); + coefficients = std::vector<double>(20, 0.0); + coefficients[1] = 1.0; + coefficients[12] = 1.0; + } + } break; + case DistortionType::RADIAL: { + try { + coefficients = isd.at("optical_distortion") + .at("radial") + .at("coefficients") + .get<std::vector<double>>(); + + return coefficients; + } catch (...) { + throw std::runtime_error( + "Could not parse the radial distortion model coefficients."); + coefficients = std::vector<double>(3, 0.0); + } + } break; + case DistortionType::KAGUYALISM: { + try { + + std::vector<double> coefficientsX = isd.at("optical_distortion") + .at("kaguyalism") + .at("x") + .get<std::vector<double>>(); + std::vector<double> coefficientsY = isd.at("optical_distortion") + .at("kaguyalism") + .at("y") + .get<std::vector<double>>(); + double boresightX = isd.at("optical_distortion") + .at("kaguyalism") + .at("boresight_x") + .get<double>(); + double boresightY = isd.at("optical_distortion") + .at("kaguyalism") + .at("boresight_y") + .get<double>(); + + coefficientsX.resize(4, 0.0); + coefficientsY.resize(4, 0.0); + coefficientsX.insert(coefficientsX.begin(), boresightX); + coefficientsY.insert(coefficientsY.begin(), boresightY); + coefficientsX.insert(coefficientsX.end(), coefficientsY.begin(), + coefficientsY.end()); + + return coefficientsX; + } catch (...) { + throw std::runtime_error("Could not parse a set of Kaguya LISM " + "distortion model coefficients."); + coefficients = std::vector<double>(8, 0.0); + } + } break; + case DistortionType::DAWNFC: { + try { + coefficients = isd.at("optical_distortion") + .at("dawnfc") + .at("coefficients") + .get<std::vector<double>>(); + + return coefficients; + } catch (...) { + throw std::runtime_error( + "Could not parse the dawn radial distortion model coefficients."); + coefficients = std::vector<double>(1, 0.0); + } + } break; + case DistortionType::LROLROCNAC: { + try { + coefficients = isd.at("optical_distortion") + .at("lrolrocnac") + .at("coefficients") + .get<std::vector<double>>(); + return coefficients; + } catch (...) { + throw std::runtime_error( + "Could not parse the lrolrocnac distortion model coefficients."); + coefficients = std::vector<double>(1, 0.0); + } + } break; + } + throw std::runtime_error( + "Could not parse the distortion model coefficients."); + + return coefficients; +} + +std::vector<double> ale::getSunPositions(json isd) { + std::vector<double> positions; + try { + json jayson = isd.at("sun_position"); + for (auto &location : jayson.at("positions")) { + positions.push_back(location[0].get<double>()); + positions.push_back(location[1].get<double>()); + positions.push_back(location[2].get<double>()); + } + } catch (...) { + throw std::runtime_error("Could not parse the sun positions."); + } + return positions; +} + +std::vector<double> ale::getSensorPositions(json isd) { + std::vector<double> positions; + try { + json jayson = isd.at("sensor_position"); + for (auto &location : jayson.at("positions")) { + positions.push_back(location[0].get<double>()); + positions.push_back(location[1].get<double>()); + positions.push_back(location[2].get<double>()); + } + } catch (...) { + throw std::runtime_error("Could not parse the sensor positions."); + } + return positions; +} + +std::vector<double> ale::getSensorVelocities(json isd) { + std::vector<double> velocities; + try { + json jayson = isd.at("sensor_position"); + for (auto &velocity : jayson.at("velocities")) { + velocities.push_back(velocity[0].get<double>()); + velocities.push_back(velocity[1].get<double>()); + velocities.push_back(velocity[2].get<double>()); + } + } catch (...) { + throw std::runtime_error("Could not parse the sensor velocities."); + } + return velocities; +} + +std::vector<double> ale::getSensorOrientations(json isd) { + std::vector<double> quaternions; + try { + for (auto &quaternion : isd.at("sensor_orientation").at("quaternions")) { + quaternions.push_back(quaternion[0]); + quaternions.push_back(quaternion[1]); + quaternions.push_back(quaternion[2]); + quaternions.push_back(quaternion[3]); + } + } catch (...) { + throw std::runtime_error("Could not parse the sensor orientations."); + } + return quaternions; +} diff --git a/tests/ctests/AleTest.cpp b/tests/ctests/AleTest.cpp index 9171974f8dfec98551467b4c7a56ed367ceb8075..4e690a3392e8edd8eeaf5ac13ce7bf909663ae3b 100644 --- a/tests/ctests/AleTest.cpp +++ b/tests/ctests/AleTest.cpp @@ -1,6 +1,7 @@ #include "gtest/gtest.h" #include "ale.h" +#include "Isd.h" #include <stdexcept> #include <cmath> @@ -12,13 +13,14 @@ using namespace std; + TEST(PositionInterpTest, LinearInterp) { vector<double> times = { -3, -2, -1, 0, 1, 2}; vector<vector<double>> data = {{ -3, -2, -1, 0, 1, 2}, { 9, 4, 1, 0, 1, 4}, {-27, -8, -1, 0, 1, 8}}; - vector<double> coordinate = ale::getPosition(data, times, -1.5, ale::linear); + vector<double> coordinate = ale::getPosition(data, times, -1.5, ale::LINEAR); ASSERT_EQ(3, coordinate.size()); EXPECT_DOUBLE_EQ(-1.5, coordinate[0]); @@ -34,7 +36,7 @@ TEST(PositionInterpTest, FourCoordinates) { {-27, -8, -1, 0, 1, 8}, { 25, 0, -5, 25, 3, 6}}; - EXPECT_THROW(ale::getPosition(data, times, 0.0, ale::linear), + EXPECT_THROW(ale::getPosition(data, times, 0.0, ale::LINEAR), invalid_argument); } @@ -43,13 +45,13 @@ TEST(LinearInterpTest, ExampleInterpolation) { vector<double> times = {0, 1, 2, 3}; vector<double> data = {0, 2, 1, 0}; - EXPECT_DOUBLE_EQ(0.0, ale::interpolate(data, times, 0.0, ale::linear, 0)); - EXPECT_DOUBLE_EQ(1.0, ale::interpolate(data, times, 0.5, ale::linear, 0)); - EXPECT_DOUBLE_EQ(2.0, ale::interpolate(data, times, 1.0, ale::linear, 0)); - EXPECT_DOUBLE_EQ(1.5, ale::interpolate(data, times, 1.5, ale::linear, 0)); - EXPECT_DOUBLE_EQ(1.0, ale::interpolate(data, times, 2.0, ale::linear, 0)); - EXPECT_DOUBLE_EQ(0.5, ale::interpolate(data, times, 2.5, ale::linear, 0)); - EXPECT_DOUBLE_EQ(0.0, ale::interpolate(data, times, 3.0, ale::linear, 0)); + EXPECT_DOUBLE_EQ(0.0, ale::interpolate(data, times, 0.0, ale::LINEAR, 0)); + EXPECT_DOUBLE_EQ(1.0, ale::interpolate(data, times, 0.5, ale::LINEAR, 0)); + EXPECT_DOUBLE_EQ(2.0, ale::interpolate(data, times, 1.0, ale::LINEAR, 0)); + EXPECT_DOUBLE_EQ(1.5, ale::interpolate(data, times, 1.5, ale::LINEAR, 0)); + EXPECT_DOUBLE_EQ(1.0, ale::interpolate(data, times, 2.0, ale::LINEAR, 0)); + EXPECT_DOUBLE_EQ(0.5, ale::interpolate(data, times, 2.5, ale::LINEAR, 0)); + EXPECT_DOUBLE_EQ(0.0, ale::interpolate(data, times, 3.0, ale::LINEAR, 0)); } @@ -57,7 +59,7 @@ TEST(LinearInterpTest, NoPoints) { vector<double> times = {}; vector<double> data = {}; - EXPECT_THROW(ale::interpolate(data, times, 0.0, ale::linear, 0), + EXPECT_THROW(ale::interpolate(data, times, 0.0, ale::LINEAR, 0), invalid_argument); } @@ -66,7 +68,7 @@ TEST(LinearInterpTest, DifferentCounts) { vector<double> times = { -3, -2, -1, 0, 2}; vector<double> data = { -3, -2, 1, 2}; - EXPECT_THROW(ale::interpolate(data, times, 0.0, ale::linear, 0), + EXPECT_THROW(ale::interpolate(data, times, 0.0, ale::LINEAR, 0), invalid_argument); } @@ -75,9 +77,9 @@ TEST(LinearInterpTest, Extrapolate) { vector<double> times = {0, 1, 2, 3}; vector<double> data = {0, 2, 1, 0}; - EXPECT_THROW(ale::interpolate(data, times, -1.0, ale::linear, 0), + EXPECT_THROW(ale::interpolate(data, times, -1.0, ale::LINEAR, 0), invalid_argument); - EXPECT_THROW(ale::interpolate(data, times, 4.0, ale::linear, 0), + EXPECT_THROW(ale::interpolate(data, times, 4.0, ale::LINEAR, 0), invalid_argument); } @@ -93,16 +95,16 @@ TEST(SplineInterpTest, ExampleInterpolation) { // The spline interpolation is only ~1e-10 so we have to define a tolerance double tolerance = 1e-10; - EXPECT_NEAR(0.0, ale::interpolate(data, times, 0.0, ale::spline, 0), tolerance); + EXPECT_NEAR(0.0, ale::interpolate(data, times, 0.0, ale::SPLINE, 0), tolerance); EXPECT_NEAR(2.8 * 0.5 - 0.8 * 0.125, - ale::interpolate(data, times, 0.5, ale::spline, 0), tolerance); - EXPECT_NEAR(2.0, ale::interpolate(data, times, 1.0, ale::spline, 0), tolerance); + ale::interpolate(data, times, 0.5, ale::SPLINE, 0), tolerance); + EXPECT_NEAR(2.0, ale::interpolate(data, times, 1.0, ale::SPLINE, 0), tolerance); EXPECT_NEAR(3.375 - 5.4 * 2.25 + 8.2 * 1.5 - 1.8, - ale::interpolate(data, times, 1.5, ale::spline, 0), tolerance); - EXPECT_NEAR(1.0, ale::interpolate(data, times, 2.0, ale::spline, 0), tolerance); + ale::interpolate(data, times, 1.5, ale::SPLINE, 0), tolerance); + EXPECT_NEAR(1.0, ale::interpolate(data, times, 2.0, ale::SPLINE, 0), tolerance); EXPECT_NEAR(-0.2 * 15.625 + 1.8 * 6.25 - 6.2 * 2.5 + 7.8, - ale::interpolate(data, times, 2.5, ale::spline, 0), tolerance); - EXPECT_NEAR(0.0, ale::interpolate(data, times, 3.0, ale::spline, 0), tolerance); + ale::interpolate(data, times, 2.5, ale::SPLINE, 0), tolerance); + EXPECT_NEAR(0.0, ale::interpolate(data, times, 3.0, ale::SPLINE, 0), tolerance); } @@ -110,7 +112,7 @@ TEST(SplineInterpTest, NoPoints) { vector<double> times = {}; vector<double> data = {}; - EXPECT_THROW(ale::interpolate(data, times, 0.0, ale::spline, 0), + EXPECT_THROW(ale::interpolate(data, times, 0.0, ale::SPLINE, 0), invalid_argument); } @@ -119,7 +121,7 @@ TEST(SplineInterpTest, DifferentCounts) { vector<double> times = { -3, -2, -1, 0, 2}; vector<double> data = { -3, -2, 1, 2}; - EXPECT_THROW(ale::interpolate(data, times, 0.0, ale::spline, 0), + EXPECT_THROW(ale::interpolate(data, times, 0.0, ale::SPLINE, 0), invalid_argument); } @@ -128,9 +130,9 @@ TEST(SplineInterpTest, Extrapolate) { vector<double> times = {0, 1, 2, 3}; vector<double> data = {0, 2, 1, 0}; - EXPECT_THROW(ale::interpolate(data, times, -1.0, ale::spline, 0), + EXPECT_THROW(ale::interpolate(data, times, -1.0, ale::SPLINE, 0), invalid_argument); - EXPECT_THROW(ale::interpolate(data, times, 4.0, ale::spline, 0), + EXPECT_THROW(ale::interpolate(data, times, 4.0, ale::SPLINE, 0), invalid_argument); } @@ -283,18 +285,23 @@ TEST(RotationInterpTest, ExampleGetRotation) { // simple test, only checks if API hit correctly and output is normalized vector<double> times = {0, 1, 2, 3}; vector<vector<double>> rots({{1,1,1,1}, {0,0,0,0}, {1,1,1,1}, {0,0,0,0}}); - vector<double> r = ale::getRotation(rots, times, 2, ale::linear); + vector<double> r = ale::getRotation(rots, times, 2, ale::LINEAR); Eigen::Quaterniond quat(r[0], r[1], r[2], r[3]); EXPECT_DOUBLE_EQ(1, quat.norm()); } - TEST(RotationInterpTest, GetRotationDifferentCounts) { // incorrect params vector<double> times = {0, 1, 2}; vector<vector<double>> rots({{1,1,1,1}, {0,0,0,0}, {1,1,1,1}, {0,0,0,0}}); - EXPECT_THROW(ale::getRotation(rots, times, 2, ale::linear), invalid_argument); + EXPECT_THROW(ale::getRotation(rots, times, 2, ale::LINEAR), invalid_argument); +} + +TEST(RotationInterpTest, InvalidTime) { + vector<double> times = {0, 1, 2}; + vector<vector<double>> rots({{1,1,1,1}, {0,0,0,0}, {1,1,1,1}}); + EXPECT_THROW(ale::getRotation(rots, times, 3, ale::LINEAR), invalid_argument); } TEST(PyInterfaceTest, LoadInvalidLabel) { @@ -310,9 +317,48 @@ TEST(PyInterfaceTest, LoadValidLabel) { TEST(AngularVelocityInterpTest, ExampleGetRotation) { vector<double> times = {0, 1}; vector<vector<double>> rots({{0,0}, {1,0}, {0,1}, {0,0}}); - vector<double> av = ale::getAngularVelocity(rots, times, 0.5, ale::linear); + vector<double> av = ale::getAngularVelocity(rots, times, 0.5, ale::LINEAR); EXPECT_DOUBLE_EQ(0, av[0]); EXPECT_DOUBLE_EQ(0, av[1]); EXPECT_DOUBLE_EQ(2 * sqrt(2), av[2]); } + +TEST(AngularVelocityInterpTest, InvalidTime) { + vector<double> times = {0, 1, 2}; + vector<vector<double>> rots({{0,0}, {1,0}, {0,1}, {0,0}}); + EXPECT_THROW(ale::getAngularVelocity(rots, times, 3, ale::LINEAR), invalid_argument); +} + +TEST(VelocityInterpTest, ExampleGetVelocity) { + vector<double> times = {0, 1, 2}; + vector<vector<double>> coords({{1, 1, 1}, {2, 2, 2}, {3, 3, 3}}); + vector<double> v = ale::getVelocity(coords, times, 1, ale::LINEAR); + + //not sure what the values are expected to look like + EXPECT_DOUBLE_EQ(v[0], 0); + EXPECT_DOUBLE_EQ(v[1], 0); + EXPECT_DOUBLE_EQ(v[2], 0); +} + +TEST(VelocityInterpTest, InvalidTime) { + vector<double> times = {0, 1, 2}; + vector<vector<double>> coords({{1, 1, 1}, {2, 2, 2}, {3, 3, 3}}); + EXPECT_THROW(ale::getVelocity(coords, times, 3, ale::LINEAR), invalid_argument); +} + +TEST(Interpolation, Derivative2) +{ + //only checks that case 2 of the switch block is hit + vector<double> points = {0, 0, 0}; + vector<double> times = {0, 1, 2}; + + EXPECT_DOUBLE_EQ(ale::interpolate(points, times, 1, ale::LINEAR, 2), 0); +} + +TEST(Interpolation, InvalidDerivative) { + vector<double> points = {0, 0, 0}; + vector<double> times = {0, 1, 2}; + + EXPECT_THROW(ale::interpolate(points, times, 1, ale::LINEAR, 3), invalid_argument); +} diff --git a/tests/ctests/IsdTests.cpp b/tests/ctests/IsdTests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..112b552142270226c651af1a1c36802dbc72cc79 --- /dev/null +++ b/tests/ctests/IsdTests.cpp @@ -0,0 +1,678 @@ +#include <string> +#include <fstream> +#include <streambuf> + +#include "gtest/gtest.h" + +#include "ale.h" +#include "Isd.h" +#include "Util.h" + +void ASSERT_DOUBLE_VECTOR_EQ(std::vector<double> v1, std::vector<double> v2) { + ASSERT_EQ(v1.size(), v2.size()) << "The two input vectors are different in size"; + + for(size_t i = 0; i < v1.size(); i++) { + EXPECT_DOUBLE_EQ(v1[i], v2[i]) << "Arrays at " << i << " are not equal."; + } +} + + +void ASSERT_DOUBLE_2D_VECTOR_EQ(std::vector<std::vector<double>> v1, std::vector<std::vector<double>> v2) { + ASSERT_EQ(v1.size(), v2.size()) << "The two input vectors are different in size"; + + for(size_t i = 0; i < v1.size(); i++) { + ASSERT_EQ(v1[i].size(), v2[i].size()) << "The two input vectors at " << i << "are different in size"; + + for(size_t j = 0; j < v1[i].size(); j++) { + EXPECT_DOUBLE_EQ(v1[i][j], v2[i][j]) << "Arrays at " << i << ", " << j << " are not equal."; + } + } +} + +TEST(Isd, Constructor) { + std::string json_str = "{\"image_identifier\":\"TEST_IMAGE\",\"sensor_position\":{\"ephemeris_times\":[297088762.24158406,297088762.3917441,297088762.5419041,297088762.69206405,297088762.84222406,297088762.9923841],\"positions\":[[-1885.29806756,913.1652236,-2961.966828],[-1885.59280128,912.7436266,-2961.91056824],[-1885.88749707,912.32201117,-2961.85424884],[-1886.18215477,911.90037749,-2961.79786985],[-1886.47677475,911.47872522,-2961.7414312],[-1886.77135665,911.05705456,-2961.68493293]],\"velocities\":[[-1.9629237646703683,-2.80759072221274,0.37446657801485306],[-1.9626712192798401,-2.807713482051373,0.3748636774173111],[-1.9624186346660286,-2.807836185534424,0.3752607691067297],[-1.9621660109346446,-2.8079588326107823,0.37565785291714804],[-1.9619133478903363,-2.8080814233753033,0.37605492915558875],[-1.961660645638678,-2.8082039577768683,0.37645199765665144]],\"position_units\":\"KM\",\"time_units\":\"S\",\"reference_frame\":1},\"sun_position\":{\"ephemeris_times\":[297088762.24158406,297088762.3917441,297088762.5419041,297088762.69206405,297088762.84222406,297088762.9923841],\"positions\":[[-1885.29806756,913.1652236,-2961.966828]],\"velocities\":[[-1.9629237646703683,-2.80759072221274,0.37446657801485306]],\"position_units\":\"KM\",\"time_units\":\"S\",\"reference_frame\":1},\"sensor_orientation\":{\"time_dependent_framess\":[-74000,-74900,1],\"constant_frames\":[-74021,-74020,-74699,-74690,-74000],\"reference_frame\":1,\"constant_rotation\":[0.9999995608798441,-1.51960241928035e-05,0.0009370214510594064,1.5276552075356694e-05,0.9999999961910578,-8.593317911879532e-05,-0.000937020141647677,8.594745584079714e-05,0.9999995573030465],\"ephemeris_times\":[297088762.24158406,297088762.3917441,297088762.5419041,297088762.69206405,297088762.84222406,297088762.9923841],\"quaternions\":[[0.42061125,0.18606223,-0.23980124,0.85496338],[0.42062261,0.18612356,-0.23976951,0.85495335],[0.42063547,0.18618438,-0.23973759,0.85494273],[0.42064763,0.18624551,-0.2397057,0.85493237],[0.42065923,0.18630667,-0.23967382,0.85492228],[0.42067144,0.18636687,-0.23964185,0.85491211]],\"angular_velocities\":[[-0.0006409728984903079,0.0005054077299115119,0.0004718267948468069],[-0.0006410700774431097,0.0005044862657976017,0.0004731836236807216],[-0.0006408186407087456,0.0004992170698116158,0.0004802237192760833],[-0.0006363961683672021,0.0004989647975959612,0.00047654664046286975],[-0.0006376443791903504,0.0004996117504290811,0.00047678850931380653],[-0.0006404093657132724,0.0005028749658176146,0.0004805228583087444]]},\"body_rotation\":{\"time_dependent_frames\":[10014,1],\"reference_frame\":1,\"ephemeris_times\":[297088762.24158406,297088762.3917441,297088762.5419041,297088762.69206405,297088762.84222406,297088762.9923841],\"quaternions\":[[-0.8371209459443085,0.2996928944391797,0.10720760458181891,0.4448811306448063],[-0.8371185783490869,0.2996934649760026,0.1072060096645597,0.4448855856569007],[-0.8371162107293473,0.2996940355045328,0.10720441474371896,0.44489004065791765],[-0.8371138430875174,0.2996946060241849,0.1072028198209324,0.44489449564328926],[-0.8371114754203602,0.2996951765357392,0.10720122489401934,0.44489895061910595],[-0.8371091077303039,0.29969574703861046,0.10719962996461516,0.4449034055807993]],\"angular_velocities\":[[3.16238646979841e-05,-2.880432898124293e-05,5.6520131658726165e-05],[3.1623864697983686e-05,-2.880432898124763e-05,5.652013165872402e-05],[3.162386469798325e-05,-2.880432898125237e-05,5.652013165872185e-05],[3.162386469798283e-05,-2.880432898125708e-05,5.6520131658719694e-05],[3.1623864697982405e-05,-2.8804328981261782e-05,5.6520131658717505e-05],[3.162386469798195e-05,-2.88043289812665e-05,5.652013165871536e-05]]},\"radii\":{\"semimajor\":3396.19,\"semiminor\":3376.2,\"unit\":\"km\"},\"detector_sample_summing\":1,\"detector_line_summing\":1,\"focal_length_model\":{\"focal_length\":352.9271664},\"detector_center\":{\"line\":0.430442527,\"sample\":2542.96099},\"starting_detector_line\":0,\"starting_detector_sample\":0,\"focal2pixel_lines\":[0.0,142.85714285714,0.0],\"focal2pixel_samples\":[0.0,0.0,142.85714285714],\"optical_distortion\":{\"radial\":{\"coefficients\":[-0.0073433925920054505,2.8375878636241697e-05,1.2841989124027099e-08]}},\"image_lines\":400,\"image_samples\":5056,\"name_platform\":\"MARS_RECONNAISSANCE_ORBITER\",\"name_sensor\":\"CONTEXT CAMERA\",\"reference_height\":{\"maxheight\":1000,\"minheight\":-1000,\"unit\":\"m\"},\"name_model\":\"USGS_ASTRO_LINE_SCANNER_SENSOR_MODEL\",\"interpolation_method\":\"lagrange\",\"line_scan_rate\":[[0.5,-0.37540000677108765,0.001877]],\"starting_ephemeris_time\":297088762.24158406,\"center_ephemeris_time\":297088762.61698407,\"t0_ephemeris\":-0.37540000677108765,\"dt_ephemeris\":0.15016000270843505,\"t0_quaternion\":-0.37540000677108765,\"dt_quaternion\":0.15016000270843505,\"naif_keywords\":{}}"; + + ale::Isd isd(json_str); + + ASSERT_EQ(isd.usgscsm_name_model, "USGS_ASTRO_LINE_SCANNER_SENSOR_MODEL"); + ASSERT_EQ(isd.name_platform, "MARS_RECONNAISSANCE_ORBITER"); + ASSERT_EQ(isd.image_id, "TEST_IMAGE"); + ASSERT_EQ(isd.name_sensor, "CONTEXT CAMERA"); + ASSERT_DOUBLE_EQ(isd.semi_major, 3396.19); + ASSERT_DOUBLE_EQ(isd.semi_minor, 3376.2); + + ASSERT_EQ(isd.detector_sample_summing, 1); + ASSERT_EQ(isd.detector_line_summing, 1); + + ASSERT_DOUBLE_EQ(isd.focal_length, 352.9271664); + ASSERT_DOUBLE_EQ(isd.focal_uncertainty, 1); + ASSERT_DOUBLE_EQ(isd.detector_center_line, 0.430442527); + ASSERT_DOUBLE_EQ(isd.detector_center_sample, 2542.96099); + + ASSERT_DOUBLE_EQ(isd.starting_detector_line, 0); + ASSERT_DOUBLE_EQ(isd.starting_detector_sample, 0); + + ASSERT_DOUBLE_VECTOR_EQ(isd.focal2pixel_line, std::vector<double>({0.0, 142.85714285714, 0.0})); + ASSERT_DOUBLE_VECTOR_EQ(isd.focal2pixel_sample, std::vector<double>({0.0, 0.0, 142.85714285714})); + + ASSERT_EQ(isd.distortion_model, ale::DistortionType::RADIAL); + ASSERT_DOUBLE_VECTOR_EQ(isd.distortion_coefficients, std::vector<double>({-0.0073433925920054505, 2.8375878636241697e-05, 1.2841989124027099e-08})); + + ASSERT_EQ(isd.image_lines, 400); + ASSERT_EQ(isd.image_samples, 5056); + + ASSERT_DOUBLE_EQ(isd.max_reference_height, 1000); + ASSERT_DOUBLE_EQ(isd.min_reference_height, -1000); + + ASSERT_DOUBLE_2D_VECTOR_EQ(isd.line_scan_rate, std::vector<std::vector<double>>({std::vector<double>({0.5, -0.37540000677108765, 0.001877})})); + + ASSERT_DOUBLE_EQ(isd.starting_ephemeris_time, 297088762.24158406); + ASSERT_DOUBLE_EQ(isd.center_ephemeris_time, 297088762.61698407); +} + +TEST(Isd, LogFile) { + ale::json j; + j["log_file"] = "fake/path"; + EXPECT_STREQ(ale::getLogFile(j).c_str(), "fake/path"); +} + +TEST(Isd, TransverseDistortion) { + ale::json trans; + trans["optical_distortion"]["transverse"]["x"] = {1}; + trans["optical_distortion"]["transverse"]["y"] = {2}; + + std::vector<double> coeffs = ale::getDistortionCoeffs(trans); + EXPECT_EQ(ale::getDistortionModel(trans), ale::DistortionType::TRANSVERSE); + ASSERT_EQ(coeffs.size(), 20); + EXPECT_DOUBLE_EQ(coeffs[0], 1); + EXPECT_DOUBLE_EQ(coeffs[10], 2); +} + +TEST(Isd, RadialDistortion) { + ale::json radial; + radial["optical_distortion"]["radial"]["coefficients"] = {1, 2}; + + std::vector<double> coeffs = ale::getDistortionCoeffs(radial); + EXPECT_EQ(ale::getDistortionModel(radial), ale::DistortionType::RADIAL); + ASSERT_EQ(coeffs.size(), 2); + EXPECT_DOUBLE_EQ(coeffs[0], 1); + EXPECT_DOUBLE_EQ(coeffs[1], 2); +} + +TEST(Isd, KaguyaLISMDistortion) { + ale::json kaguya; + kaguya["optical_distortion"]["kaguyalism"]["x"] = {1}; + kaguya["optical_distortion"]["kaguyalism"]["y"] = {2}; + kaguya["optical_distortion"]["kaguyalism"]["boresight_x"] = 3; + kaguya["optical_distortion"]["kaguyalism"]["boresight_y"] = 4; + + std::vector<double> coeffs = ale::getDistortionCoeffs(kaguya); + EXPECT_EQ(ale::getDistortionModel(kaguya), ale::DistortionType::KAGUYALISM); + ASSERT_EQ(coeffs.size(), 10); + EXPECT_DOUBLE_EQ(coeffs[0], 3); + EXPECT_DOUBLE_EQ(coeffs[1], 1); + EXPECT_DOUBLE_EQ(coeffs[5], 4); + EXPECT_DOUBLE_EQ(coeffs[6], 2); +} + +TEST(Isd, DawnFCDistortion) { + ale::json dawn; + dawn["optical_distortion"]["dawnfc"]["coefficients"] = {1, 2}; + std::vector<double> coeffs = ale::getDistortionCoeffs(dawn); + EXPECT_EQ(ale::getDistortionModel(dawn), ale::DistortionType::DAWNFC); + ASSERT_EQ(coeffs.size(), 2); + EXPECT_DOUBLE_EQ(coeffs[0], 1); + EXPECT_DOUBLE_EQ(coeffs[1], 2); +} + +TEST(Isd, LroLrocNACDistortion) { + ale::json lro; + lro["optical_distortion"]["lrolrocnac"]["coefficients"] = {1, 2}; + std::vector<double> coeffs = ale::getDistortionCoeffs(lro); + EXPECT_EQ(ale::getDistortionModel(lro), ale::DistortionType::LROLROCNAC); + ASSERT_EQ(coeffs.size(), 2); + EXPECT_DOUBLE_EQ(coeffs[0], 1); + EXPECT_DOUBLE_EQ(coeffs[1], 2); +} + +TEST(Isd, UnrecognizedDistortion) { + ale::json j; + j["optical_distortion"]["foo"]["x"] = {1}; + + EXPECT_EQ(ale::getDistortionModel(j), ale::DistortionType::TRANSVERSE); +} + +TEST(Isd, BadLogFile) { + ale::json j; + EXPECT_THROW(ale::getLogFile(j), std::runtime_error); +} + +TEST(Isd, GetSunPositions) { + ale::json j; + j["sun_position"]["positions"] = {{1, 2, 3}, {4, 5, 6}}; + std::vector<double> positions = ale::getSunPositions(j); + ASSERT_EQ(positions.size(), 6); + EXPECT_DOUBLE_EQ(positions[0], 1); + EXPECT_DOUBLE_EQ(positions[1], 2); + EXPECT_DOUBLE_EQ(positions[2], 3); + EXPECT_DOUBLE_EQ(positions[3], 4); + EXPECT_DOUBLE_EQ(positions[4], 5); + EXPECT_DOUBLE_EQ(positions[5], 6); +} + +TEST(Isd, NoSunPositions) { + ale::json j; + try { + ale::getSunPositions(j); + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the sun positions."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the sun positions.\""; + } +} + +TEST(Isd, GetSensorPositions) { + ale::json j; + j["sensor_position"]["positions"] = {{1, 2, 3}, {4, 5, 6}}; + std::vector<double> positions = ale::getSensorPositions(j); + ASSERT_EQ(positions.size(), 6); + EXPECT_DOUBLE_EQ(positions[0], 1); + EXPECT_DOUBLE_EQ(positions[1], 2); + EXPECT_DOUBLE_EQ(positions[2], 3); + EXPECT_DOUBLE_EQ(positions[3], 4); + EXPECT_DOUBLE_EQ(positions[4], 5); + EXPECT_DOUBLE_EQ(positions[5], 6); +} + +TEST(Isd, NoSensorPositions) { + ale::json j; + try { + ale::getSensorPositions(j); + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the sensor positions."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the sensor positions.\""; + } +} + +TEST(Isd, GetSensorVelocities) +{ + ale::json j; + j["sensor_position"]["velocities"] = {{1, 2, 3}, {4, 5, 6}}; + std::vector<double> velocities = ale::getSensorVelocities(j); + ASSERT_EQ(velocities.size(), 6); + EXPECT_DOUBLE_EQ(velocities[0], 1); + EXPECT_DOUBLE_EQ(velocities[1], 2); + EXPECT_DOUBLE_EQ(velocities[2], 3); + EXPECT_DOUBLE_EQ(velocities[3], 4); + EXPECT_DOUBLE_EQ(velocities[4], 5); + EXPECT_DOUBLE_EQ(velocities[5], 6); +} + +TEST(Isd, NoSensorVelocities) { + ale::json j; + try { + ale::getSensorVelocities(j); + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the sensor velocities."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the sensor velocities.\""; + } +} + +TEST(Isd, GetSensorOrientations) +{ + ale::json j; + j["sensor_orientation"]["quaternions"] = {{1, 2, 3, 4}}; + std::vector<double> quats = ale::getSensorOrientations(j); + ASSERT_EQ(quats.size(), 4); + EXPECT_DOUBLE_EQ(quats[0], 1); + EXPECT_DOUBLE_EQ(quats[1], 2); + EXPECT_DOUBLE_EQ(quats[2], 3); + EXPECT_DOUBLE_EQ(quats[3], 4); +} + +TEST(Isd, NoSensorOrientations) { + ale::json j; + try { + ale::getSensorOrientations(j); + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the sensor orientations."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the sensor orientations.\""; + } +} + +TEST(Isd, BadNameModel) { + std::string bad_json_str("{}"); + try { + ale::getSensorModelName(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the sensor model name."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the sensor model name.\""; + } +} + +TEST(Isd, BadNamePlatform) { + std::string bad_json_str("{}"); + try { + ale::getPlatformName(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + ASSERT_EQ(std::string(e.what()), "Could not parse the platform name."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the platform name.\""; + } +} + +TEST(Isd, BadImageID) { + std::string bad_json_str("{}"); + try { + ale::getImageId(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + ASSERT_EQ(std::string(e.what()), "Could not parse the image identifier."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the image identifier.\""; + } +} + +TEST(Isd, BadSensorName) { + std::string bad_json_str("{}"); + try { + ale::getSensorName(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + ASSERT_EQ(std::string(e.what()), "Could not parse the sensor name."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the sensor name.\""; + } +} + +TEST(Isd, BadRadii) { + std::string bad_json_str("{}"); + try { + ale::getSemiMajorRadius(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the reference ellipsoid semimajor radius."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the reference ellipsoid semimajor radius.\""; + } + + try { + ale::getSemiMinorRadius(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the reference ellipsoid semiminor radius."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the reference ellipsoid semiminor radius.\""; + } +} + +TEST(Isd, BadSumming) { + std::string bad_json_str("{}"); + try { + ale::getSampleSumming(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the sample direction detector pixel summing."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the sample direction detector pixel summing.\""; + } + + try { + ale::getLineSumming(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the line direction detector pixel summing."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the line direction detector pixel summing.\""; + } +} + +TEST(Isd, BadFocalLength) { + std::string bad_json_str("{}"); + try { + ale::getFocalLength(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the focal length."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the focal length.\""; + } + + try { + ale::getFocalLengthUncertainty(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the focal length uncertainty."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the focal length uncertainty.\""; + } +} + +TEST(Isd, BadDetectorCenter) { + std::string bad_json_str("{}"); + try { + ale::getDetectorCenterSample(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the detector center sample."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the detector center sample.\""; + } + + try { + ale::getDetectorCenterLine(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the detector center line."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the detector center line.\""; + } +} + +TEST(Isd, BadStartingDetector) { + std::string bad_json_str("{}"); + try { + ale::getDetectorStartingSample(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the detector starting sample."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the detector starting sample.\""; + } + + try { + ale::getDetectorStartingLine(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the detector starting line."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the detector starting line.\""; + } +} + +TEST(Isd, BadFocal2Pixel) { + std::string bad_json_str("{}"); + try { + ale::getFocal2PixelSamples(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the focal plane coordinate to " + "detector samples transformation."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the focal plane coordinate to " + "detector samples transformation.\""; + } + + try { + ale::getFocal2PixelLines(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the focal plane coordinate to " + "detector lines transformation."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the focal plane coordinate to " + "detector lines transformation.\""; + } +} + +TEST(Isd, BadDistortionModel) { + std::string bad_json_str("{}"); + try { + ale::getDistortionModel(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the distortion model."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the distortion model.\""; + } +} + +TEST(Isd, BadDistortionTransverse) { + ale::json bad_json; + bad_json["optical_distortion"]["transverse"]["x"] = {"NaN"}; + bad_json["optical_distortion"]["transverse"]["y"] = {"NaN"}; + + try { + ale::getDistortionCoeffs(bad_json); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse a set of transverse distortion model coefficients."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse a set of transverse distortion model coefficients.\""; + } +} + +TEST(Isd, BadDistortionRadial) { + ale::json bad_json; + bad_json["optical_distortion"]["radial"]["coefficients"] = {"NaN"}; + + try { + ale::getDistortionCoeffs(bad_json); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the radial distortion model coefficients."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the radial distortion model coefficients.\""; + } +} + +TEST(Isd, BadDistortionDawnFC) { + ale::json bad_json; + bad_json["optical_distortion"]["dawnfc"]["coefficients"] = {"NaN"}; + + try { + ale::getDistortionCoeffs(bad_json); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the dawn radial distortion model coefficients."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the dawn radial distortion model coefficients.\""; + } +} + +TEST(Isd, BadDistortionKaguyaLISM) { + ale::json bad_json; + bad_json["optical_distortion"]["kaguyalism"]["x"] = {"NaN"}; + bad_json["optical_distortion"]["kaguyalism"]["y"] = {"NaN"}; + try { + ale::getDistortionCoeffs(bad_json); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse a set of Kaguya LISM distortion model coefficients."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse a set of Kaguya LISM distortion model coefficients.\""; + } +} + +TEST(Isd, BadDistortionLroLrocNac) { + ale::json bad_json; + bad_json["optical_distortion"]["lrolrocnac"]["coefficients"] = {"NaN"}; + try { + ale::getDistortionCoeffs(bad_json); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the lrolrocnac distortion model coefficients."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the lrolrocnac distortion model coefficients.\""; + } +} + +TEST(Isd, BadImageSize) { + std::string bad_json_str("{}"); + try { + ale::getTotalSamples(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the number of samples in the image."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the number of samples in the image.\""; + } + + try { + ale::getTotalLines(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the number of lines in the image."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the number of lines in the image.\""; + } +} + +TEST(Isd, BadReferenceHeight) { + std::string bad_json_str("{}"); + try { + ale::getMinHeight(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the minimum height above the reference ellipsoid."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the minimum height above the reference ellipsoid.\""; + } + + try { + ale::getMaxHeight(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the maximum height above the reference ellipsoid."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the maximum height above the reference ellipsoid.\""; + } +} + +TEST(Isd, BadLineScanRate) { + std::string bad_json_str("{}"); + try { + ale::getLineScanRate(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + ASSERT_EQ(std::string(e.what()), "Could not parse the integration start lines in the integration time table."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the integration start lines in the integration time table.\""; + } +} + +TEST(Isd, BadEphemerisTimes) { + std::string bad_json_str("{}"); + try { + ale::getStartingTime(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the image start time."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the image start time.\""; + } + + try { + ale::getCenterTime(bad_json_str); + // Code that should throw an IException + FAIL() << "Expected an exception to be thrown"; + } + catch(std::exception &e) { + EXPECT_EQ(std::string(e.what()), "Could not parse the center image time."); + } + catch(...) { + FAIL() << "Expected an Excpetion with message: \"Could not parse the center image time.\""; + } +}