#include "Utilities.h" #include #include #include #include #include #include #include #include #include "ale/Distortion.h" using json = nlohmann::json; // Calculates a rotation matrix from Euler angles // in - euler[3] // out - rotationMatrix[9] void calculateRotationMatrixFromEuler(double euler[], double rotationMatrix[]) { double cos_a = cos(euler[0]); double sin_a = sin(euler[0]); double cos_b = cos(euler[1]); double sin_b = sin(euler[1]); double cos_c = cos(euler[2]); double sin_c = sin(euler[2]); rotationMatrix[0] = cos_b * cos_c; rotationMatrix[1] = -cos_a * sin_c + sin_a * sin_b * cos_c; rotationMatrix[2] = sin_a * sin_c + cos_a * sin_b * cos_c; rotationMatrix[3] = cos_b * sin_c; rotationMatrix[4] = cos_a * cos_c + sin_a * sin_b * sin_c; rotationMatrix[5] = -sin_a * cos_c + cos_a * sin_b * sin_c; rotationMatrix[6] = -sin_b; rotationMatrix[7] = sin_a * cos_b; rotationMatrix[8] = cos_a * cos_b; } // uses a quaternion to calculate a rotation matrix. // in - q[4] // out - rotationMatrix[9] void calculateRotationMatrixFromQuaternions(double q[4], double rotationMatrix[9]) { double norm = sqrt(q[0] * q[0] + q[1] * q[1] + q[2] * q[2] + q[3] * q[3]); q[0] /= norm; q[1] /= norm; q[2] /= norm; q[3] /= norm; rotationMatrix[0] = q[0] * q[0] - q[1] * q[1] - q[2] * q[2] + q[3] * q[3]; rotationMatrix[1] = 2 * (q[0] * q[1] - q[2] * q[3]); rotationMatrix[2] = 2 * (q[0] * q[2] + q[1] * q[3]); rotationMatrix[3] = 2 * (q[0] * q[1] + q[2] * q[3]); rotationMatrix[4] = -q[0] * q[0] + q[1] * q[1] - q[2] * q[2] + q[3] * q[3]; rotationMatrix[5] = 2 * (q[1] * q[2] - q[0] * q[3]); rotationMatrix[6] = 2 * (q[0] * q[2] - q[1] * q[3]); rotationMatrix[7] = 2 * (q[1] * q[2] + q[0] * q[3]); rotationMatrix[8] = -q[0] * q[0] - q[1] * q[1] + q[2] * q[2] + q[3] * q[3]; } // Compute the distorted focal plane coordinate for a given image pixel // in - line // in - sample // in - sampleOrigin - the origin of the ccd coordinate system relative to the // top left of the ccd in - lineOrigin - the origin of the ccd coordinate system // relative to the top left of the ccd in - sampleSumming in - startingSample - // first ccd sample for the image in - iTransS[3] - the transformation from // focal plane to ccd samples in - iTransL[3] - the transformation from focal // plane to ccd lines out - distortedX out - distortedY void computeDistortedFocalPlaneCoordinates( const double &line, const double &sample, const double &sampleOrigin, const double &lineOrigin, const double &sampleSumming, const double &lineSumming, const double &startingSample, const double &startingLine, const double iTransS[], const double iTransL[], double &distortedX, double &distortedY) { double detSample = sample * sampleSumming + startingSample; double detLine = line * lineSumming + startingLine; double m11 = iTransL[1]; double m12 = iTransL[2]; double m21 = iTransS[1]; double m22 = iTransS[2]; double t1 = detLine - lineOrigin - iTransL[0]; double t2 = detSample - sampleOrigin - iTransS[0]; double determinant = m11 * m22 - m12 * m21; double p11 = m22 / determinant; double p12 = -m12 / determinant; double p21 = -m21 / determinant; double p22 = m11 / determinant; distortedX = p11 * t1 + p12 * t2; distortedY = p21 * t1 + p22 * t2; } // Compute the de-jittered pixel coordinate given a jittered image coordinate // a set of jitter coefficients and line exposure times for rolling shutter. // Jitter coefficients are in largest power first order. There is no constant // coefficient. For example {1, 2, 3} would correspond to 1*t^3 + 2*t&2 + 3*t. void removeJitter( const double &line, const double &sample, const std::vector lineJitterCoeffs, const std::vector sampleJitterCoeffs, const std::vector lineTimes, double &dejitteredLine, double &dejitteredSample) { // Check input if (lineJitterCoeffs.size() != sampleJitterCoeffs.size() || lineTimes.size() == 0) { throw csm::Error( csm::Error::INDEX_OUT_OF_RANGE, "Jitter coefficient vectors must be the same size.", "removeJitter"); } if (lineTimes.size() == 0) { throw csm::Error( csm::Error::INDEX_OUT_OF_RANGE, "Line exposure times must be non-empty.", "removeJitter"); } double lineJitter = 0; double sampleJitter = 0; // Bound line index to the vector of line exposure times; double time = lineTimes[std::max(std::min((int)std::round(line), (int)lineTimes.size()), 1) - 1]; for (unsigned int n = 0; n < lineJitterCoeffs.size(); n++) { double timeTerm = pow(time, lineJitterCoeffs.size() - n); lineJitter += lineJitterCoeffs[n] * timeTerm; sampleJitter += sampleJitterCoeffs[n] * timeTerm; } dejitteredLine = line - lineJitter; dejitteredSample = sample - sampleJitter; return; } // Compute the jittered pixel coordinate given a de-jittered image coordinate // a set of jitter coefficients and line exposure times for rolling shutter. // Jitter coefficients are in largest power first order. There is no constant // coefficient. For example {1, 2, 3} would correspond to 1*t^3 + 2*t&2 + 3*t. // This uses an iterative method so a tolerance and maximum number of iteration // are required to determine when to stop iterating. void addJitter( const double &line, const double &sample, const double &tolerance, const int &maxIts, const std::vector lineJitterCoeffs, const std::vector sampleJitterCoeffs, const std::vector lineTimes, double &jitteredLine, double &jitteredSample) { int iteration = 0; double dejitteredLine = line - 1; double dejitteredSample = sample - 1; double currentLine = line; double currentSample = sample; while (iteration < maxIts) { removeJitter( currentLine, currentSample, lineJitterCoeffs, sampleJitterCoeffs, lineTimes, dejitteredLine, dejitteredSample); if (fabs(dejitteredLine - line) < tolerance && fabs(dejitteredSample - sample) < tolerance) { break; } currentLine = line + currentLine - dejitteredLine; currentSample = sample + currentSample - dejitteredSample; iteration++; } jitteredLine = currentLine; jitteredSample = currentSample; return; } // Compute the image pixel for a distorted focal plane coordinate // in - line // in - sample // in - sampleOrigin - the origin of the ccd coordinate system relative to the // top left of the ccd in - lineOrigin - the origin of the ccd coordinate system // relative to the top left of the ccd in - sampleSumming in - startingSample - // first ccd sample for the image in - iTransS[3] - the transformation from // focal plane to ccd samples in - iTransL[3] - the transformation from focal // plane to ccd lines out - natFocalPlane void computePixel(const double &distortedX, const double &distortedY, const double &sampleOrigin, const double &lineOrigin, const double &sampleSumming, const double &lineSumming, const double &startingSample, const double &startingLine, const double iTransS[], const double iTransL[], double &line, double &sample) { double centeredSample = iTransS[0] + iTransS[1] * distortedX + iTransS[2] * distortedY; double centeredLine = iTransL[0] + iTransL[1] * distortedX + iTransL[2] * distortedY; double detSample = centeredSample + sampleOrigin; double detLine = centeredLine + lineOrigin; sample = (detSample - startingSample) / sampleSumming; line = (detLine - startingLine) / lineSumming; } // Define imaging ray in image space (In other words, create a look vector in // camera space) in - undistortedFocalPlaneX in - undistortedFocalPlaneY in - // zDirection - Either 1 or -1. The direction of the boresight in - focalLength // out - cameraLook[3] void createCameraLookVector(const double &undistortedFocalPlaneX, const double &undistortedFocalPlaneY, const double &zDirection, const double &focalLength, double cameraLook[]) { cameraLook[0] = -undistortedFocalPlaneX * zDirection; cameraLook[1] = -undistortedFocalPlaneY * zDirection; cameraLook[2] = -focalLength; double magnitude = sqrt(cameraLook[0] * cameraLook[0] + cameraLook[1] * cameraLook[1] + cameraLook[2] * cameraLook[2]); cameraLook[0] /= magnitude; cameraLook[1] /= magnitude; 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 = static_cast(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 t0 = fndex - index; if (order == 2) { tm1 = t0 - 1; d[0] = -tm1; d[1] = t0; } else if (order == 4) { tp1 = t0 + 1; tm1 = t0 - 1; tm2 = t0 - 2; d[0] = -t0 * tm1 * tm2 / 6.0; d[1] = tp1 * tm1 * tm2 / 2.0; d[2] = -tp1 * t0 * tm2 / 2.0; d[3] = tp1 * t0 * tm1 / 6.0; } else if (order == 6) { tp2 = t0 + 2; tp1 = t0 + 1; tm1 = t0 - 1; tm2 = t0 - 2; tm3 = t0 - 3; d[0] = -tp1 * t0 * tm1 * tm2 * tm3 / 120.0; d[1] = tp2 * t0 * tm1 * tm2 * tm3 / 24.0; d[2] = -tp2 * tp1 * tm1 * tm2 * tm3 / 12.0; d[3] = tp2 * tp1 * t0 * tm2 * tm3 / 12.0; d[4] = -tp2 * tp1 * t0 * tm1 * tm3 / 24.0; d[5] = tp2 * tp1 * t0 * tm1 * tm2 / 120.0; } else if (order == 8) { tp3 = t0 + 3; tp2 = t0 + 2; tp1 = t0 + 1; tm1 = t0 - 1; tm2 = t0 - 2; tm3 = t0 - 3; tm4 = t0 - 4; // The denominators are hard-coded because the sampling is uniform // hence they can be computed explicitly. // Note: Trying pre-compute the many repeated multiplications // below does not speed things up. d[0] = -tp2 * tp1 * t0 * tm1 * tm2 * tm3 * tm4 / 5040.0; d[1] = tp3 * tp1 * t0 * tm1 * tm2 * tm3 * tm4 / 720.0; d[2] = -tp3 * tp2 * t0 * tm1 * tm2 * tm3 * tm4 / 240.0; d[3] = tp3 * tp2 * tp1 * tm1 * tm2 * tm3 * tm4 / 144.0; d[4] = -tp3 * tp2 * tp1 * t0 * tm2 * tm3 * tm4 / 144.0; d[5] = tp3 * tp2 * tp1 * t0 * tm1 * tm3 * tm4 / 240.0; d[6] = -tp3 * tp2 * tp1 * t0 * tm1 * tm2 * tm4 / 720.0; d[7] = tp3 * tp2 * tp1 * t0 * 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]; } } } double brentRoot(double lowerBound, double upperBound, std::function func, double epsilon) { double counterPoint = lowerBound; double currentPoint = upperBound; double counterFunc = func(counterPoint); double currentFunc = func(currentPoint); if (counterFunc * currentFunc > 0.0) { throw std::invalid_argument( "Function values at the boundaries have the same sign [brentRoot]."); } if (fabs(counterFunc) < fabs(currentFunc)) { std::swap(counterPoint, currentPoint); std::swap(counterFunc, currentFunc); } double previousPoint = counterPoint; double previousFunc = counterFunc; double evenOlderPoint = previousPoint; double nextPoint; double nextFunc; int iteration = 0; bool bisected = true; do { // Inverse quadratic interpolation if (counterFunc != previousFunc && counterFunc != currentFunc && currentFunc != previousFunc) { nextPoint = (counterPoint * currentFunc * previousFunc) / ((counterFunc - currentFunc) * (counterFunc - previousFunc)); nextPoint += (currentPoint * counterFunc * previousFunc) / ((currentFunc - counterFunc) * (currentFunc - previousFunc)); nextPoint += (previousPoint * currentFunc * counterFunc) / ((previousFunc - counterFunc) * (previousFunc - currentFunc)); } else { // Secant method nextPoint = currentPoint - currentFunc * (currentPoint - counterPoint) / (currentFunc - counterFunc); } // Bisection method if (((currentPoint - nextPoint) * (nextPoint - (3 * counterPoint + currentPoint) / 4) < 0) || (bisected && fabs(nextPoint - currentPoint) >= fabs(currentPoint - previousPoint) / 2) || (!bisected && fabs(nextPoint - currentPoint) >= fabs(previousPoint - evenOlderPoint) / 2) || (bisected && fabs(currentPoint - previousPoint) < epsilon) || (!bisected && fabs(previousPoint - evenOlderPoint) < epsilon)) { nextPoint = (currentPoint + counterPoint) / 2; bisected = true; } else { bisected = false; } // Setup for next iteration evenOlderPoint = previousPoint; previousPoint = currentPoint; previousFunc = currentFunc; nextFunc = func(nextPoint); // This is a bugfix. Without it, the code gets lost and can't find the solution. // See also the implementation at https://en.wikipedia.org/wiki/Brent%27s_method if (nextFunc == 0) return nextPoint; if (counterFunc * nextFunc < 0) { currentPoint = nextPoint; currentFunc = nextFunc; } else { counterPoint = nextPoint; counterFunc = nextFunc; } } while (++iteration < 30 && fabs(counterPoint - currentPoint) > epsilon); return nextPoint; } double evaluatePolynomial(const std::vector &coeffs, double x) { if (coeffs.empty()) { throw std::invalid_argument("Polynomial coeffs must be non-empty."); } auto revIt = coeffs.crbegin(); double value = *revIt; ++revIt; for (; revIt != coeffs.crend(); ++revIt) { value *= x; value += *revIt; } return value; } double evaluatePolynomialDerivative(const std::vector &coeffs, double x) { if (coeffs.empty()) { throw std::invalid_argument("Polynomial coeffs must be non-empty."); } int i = coeffs.size() - 1; double value = i * coeffs[i]; --i; for (; i > 0; --i) { value *= x; value += i * coeffs[i]; } return value; } double polynomialRoot(const std::vector &coeffs, double guess, double threshold, int maxIterations) { double root = guess; double polyValue = evaluatePolynomial(coeffs, root); double polyDeriv = 0.0; for (int iteration = 0; iteration < maxIterations + 1; iteration++) { if (fabs(polyValue) < threshold) { return root; } polyDeriv = evaluatePolynomialDerivative(coeffs, root); if (fabs(polyDeriv) < 1e-15) { throw std::invalid_argument("Derivative at guess (" + std::to_string(guess) + ") is too close to 0."); } root -= polyValue / polyDeriv; polyValue = evaluatePolynomial(coeffs, root); } throw std::invalid_argument("Root finder did not converge after " + std::to_string(maxIterations) + " iterations"); } double computeEllipsoidElevation(double x, double y, double z, double semiMajor, double semiMinor, double desired_precision, double *achieved_precision) { // Compute elevation given xyz // Requires semi-major-axis and eccentricity-square const int MKTR = 10; double ecc_sqr = 1.0 - semiMinor * semiMinor / semiMajor / semiMajor; double ep2 = 1.0 - ecc_sqr; double d2 = x * x + y * y; double d = sqrt(d2); double h = 0.0; int ktr = 0; double hPrev, r; // Suited for points near equator if (d >= z) { double tt, zz, n; double tanPhi = z / d; do { hPrev = h; tt = tanPhi * tanPhi; r = semiMajor / sqrt(1.0 + ep2 * tt); zz = z + r * ecc_sqr * tanPhi; n = r * sqrt(1.0 + tt); h = sqrt(d2 + zz * zz) - n; tanPhi = zz / d; ktr++; } while (MKTR > ktr && fabs(h - hPrev) > desired_precision); } else { // Suited for points near the poles double cc, dd, nn; double cotPhi = d / z; do { hPrev = h; cc = cotPhi * cotPhi; r = semiMajor / sqrt(ep2 + cc); dd = d - r * ecc_sqr * cotPhi; nn = r * sqrt(1.0 + cc) * ep2; h = sqrt(dd * dd + z * z) - nn; cotPhi = dd / z; ktr++; } while (MKTR > ktr && fabs(h - hPrev) > desired_precision); } if (achieved_precision) { *achieved_precision = fabs(h - hPrev); } return h; } csm::EcefVector operator*(double scalar, const csm::EcefVector &vec) { return csm::EcefVector(scalar * vec.x, scalar * vec.y, scalar * vec.z); } csm::EcefVector operator*(const csm::EcefVector &vec, double scalar) { return scalar * vec; } csm::EcefVector operator/(const csm::EcefVector &vec, double scalar) { return 1.0 / scalar * vec; } csm::EcefVector operator+(const csm::EcefVector &vec1, const csm::EcefVector &vec2) { return csm::EcefVector(vec1.x + vec2.x, vec1.y + vec2.y, vec1.z + vec2.z); } csm::EcefVector operator-(const csm::EcefVector &vec1, const csm::EcefVector &vec2) { return csm::EcefVector(vec1.x - vec2.x, vec1.y - vec2.y, vec1.z - vec2.z); } double dot(const csm::EcefVector &vec1, const csm::EcefVector &vec2) { return vec1.x * vec2.x + vec1.y * vec2.y + vec1.z * vec2.z; } csm::EcefVector cross(const csm::EcefVector &vec1, const csm::EcefVector &vec2) { return csm::EcefVector(vec1.y * vec2.z - vec1.z * vec2.y, vec1.z * vec2.x - vec1.x * vec2.z, vec1.x * vec2.y - vec1.y * vec2.x); } double norm(const csm::EcefVector &vec) { return sqrt(vec.x * vec.x + vec.y * vec.y + vec.z * vec.z); } csm::EcefVector normalized(const csm::EcefVector &vec) { return vec / norm(vec); } csm::EcefVector projection(const csm::EcefVector &vec1, const csm::EcefVector &vec2) { return dot(vec1, vec2) / dot(vec2, vec2) * vec2; } csm::EcefVector rejection(const csm::EcefVector &vec1, const csm::EcefVector &vec2) { return vec1 - projection(vec1, vec2); } // convert a measurement double metric_conversion(double val, std::string from, std::string to) { json typemap = {{"m", 0}, {"km", 3}}; // everything to lowercase std::transform(from.begin(), from.end(), from.begin(), ::tolower); std::transform(to.begin(), to.end(), to.begin(), ::tolower); return val * pow(10, typemap[from].get() - typemap[to].get()); } std::string getSensorModelName(json isd, csm::WarningList *list) { std::string name = ""; try { name = isd.at("name_model"); } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the sensor model name.", "Utilities::getSensorModelName()")); } } return name; } std::string getImageId(json isd, csm::WarningList *list) { std::string id = ""; try { id = isd.at("image_identifier"); } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the image identifier.", "Utilities::getImageId()")); } } return id; } std::string getSensorName(json isd, csm::WarningList *list) { std::string name = ""; try { name = isd.at("name_sensor"); } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the sensor name.", "Utilities::getSensorName()")); } } return name; } std::string getPlatformName(json isd, csm::WarningList *list) { std::string name = ""; try { name = isd.at("name_platform"); } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the platform name.", "Utilities::getPlatformName()")); } } return name; } std::string getLogFile(nlohmann::json isd, csm::WarningList *list) { std::string file = ""; try { file = isd.at("log_file"); } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the log filename.", "Utilities::getLogFile()")); } } return file; } int getTotalLines(json isd, csm::WarningList *list) { int lines = 0; try { lines = isd.at("image_lines"); } catch (...) { if (list) { list->push_back( csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the number of lines in the image.", "Utilities::getTotalLines()")); } } return lines; } int getTotalSamples(json isd, csm::WarningList *list) { int samples = 0; try { samples = isd.at("image_samples"); } catch (...) { if (list) { list->push_back( csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the number of samples in the image.", "Utilities::getTotalSamples()")); } } return samples; } double getStartingTime(json isd, csm::WarningList *list) { double time = 0.0; try { time = isd.at("starting_ephemeris_time"); } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the image start time.", "Utilities::getStartingTime()")); } } return time; } double getCenterTime(json isd, csm::WarningList *list) { double time = 0.0; try { time = isd.at("center_ephemeris_time"); } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the center image time.", "Utilities::getCenterTime()")); } } return time; } double getEndingTime(json isd, csm::WarningList *list) { double time = 0.0; try { time = isd.at("ending_ephemeris_time"); } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the ending image time.", "Utilities::getEndingTime()")); } } return time; } std::vector getIntegrationStartLines( std::vector> lineScanRate, csm::WarningList *list) { std::vector lines; try { for (auto &scanRate : lineScanRate) { if (scanRate.size() != 3) { throw csm::Error( csm::Error::SENSOR_MODEL_NOT_CONSTRUCTIBLE, "Unable to parse integration start lines from line " "scan rate due to malformed vector. Expected vector size of 3.", "Utilities::getIntegrationStartLines()"); } lines.push_back(scanRate[0]); } } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the integration start " "lines in the integration time table.", "Utilities::getIntegrationStartLines()")); } } return lines; } std::vector getIntegrationStartTimes( std::vector> lineScanRate, csm::WarningList *list) { std::vector times; try { for (auto &scanRate : lineScanRate) { if (scanRate.size() != 3) { throw csm::Error( csm::Error::SENSOR_MODEL_NOT_CONSTRUCTIBLE, "Unable to parse integration start times from line " "scan rate due to malformed vector. Expected vector size of 3.", "Utilities::getIntegrationStartTimes()"); } times.push_back(scanRate[1]); } } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the integration start " "times in the integration time table.", "Utilities::getIntegrationStartTimes()")); } } return times; } std::vector getIntegrationTimes( std::vector> lineScanRate, csm::WarningList *list) { std::vector times; try { for (auto &scanRate : lineScanRate) { if (scanRate.size() != 3) { throw csm::Error( csm::Error::SENSOR_MODEL_NOT_CONSTRUCTIBLE, "Unable to parse integration times from line " "scan rate due to malformed vector. Expected vector size of 3.", "Utilities::getIntegrationTimes()"); } times.push_back(scanRate[2]); } } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the integration times in " "the integration time table.", "Utilities::getIntegrationTimes()")); } } return times; } int getSampleSumming(json isd, csm::WarningList *list) { int summing = 0; try { summing = isd.at("detector_sample_summing"); } catch (...) { if (list) { list->push_back(csm::Warning( csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the sample direction detector pixel summing.", "Utilities::getSampleSumming()")); } } return summing; } int getLineSumming(json isd, csm::WarningList *list) { int summing = 0; try { summing = isd.at("detector_line_summing"); } catch (...) { if (list) { list->push_back(csm::Warning( csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the line direction detector pixel summing.", "Utilities::getLineSumming()")); } } return summing; } double getFocalLength(json isd, csm::WarningList *list) { double length = 0.0; try { length = isd.at("focal_length_model").at("focal_length"); } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the focal length.", "Utilities::getFocalLength()")); } } return length; } double getFocalLengthEpsilon(json isd, csm::WarningList *list) { double epsilon = 0.0; try { epsilon = isd.at("focal_length_model").at("focal_epsilon"); } catch (...) { if (list) { list->push_back( csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the focal length uncertainty.", "Utilities::getFocalLengthEpsilon()")); } } return epsilon; } std::vector getFocal2PixelLines(json isd, csm::WarningList *list) { std::vector transformation; try { transformation = isd.at("focal2pixel_lines").get>(); } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the focal plane coordinate " "to detector lines transformation.", "Utilities::getFocal2PixelLines()")); } } return transformation; } std::vector getFocal2PixelSamples(json isd, csm::WarningList *list) { std::vector transformation; try { transformation = isd.at("focal2pixel_samples").get>(); } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the focal plane coordinate " "to detector samples transformation.", "Utilities::getFocal2PixelSamples()")); } } return transformation; } double getDetectorCenterLine(json isd, csm::WarningList *list) { double line; try { line = isd.at("detector_center").at("line"); } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the detector center line.", "Utilities::getDetectorCenterLine()")); } } return line; } double getDetectorCenterSample(json isd, csm::WarningList *list) { double sample; try { sample = isd.at("detector_center").at("sample"); } catch (...) { if (list) { list->push_back( csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the detector center sample.", "Utilities::getDetectorCenterSample()")); } } return sample; } double getDetectorStartingLine(json isd, csm::WarningList *list) { double line; try { line = isd.at("starting_detector_line"); } catch (...) { if (list) { list->push_back( csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the detector starting line.", "Utilities::getDetectorStartingLine()")); } } return line; } double getDetectorStartingSample(json isd, csm::WarningList *list) { double sample; try { sample = isd.at("starting_detector_sample"); } catch (...) { if (list) { list->push_back( csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the detector starting sample.", "Utilities::getDetectorStartingSample()")); } } return sample; } double getMinHeight(json isd, csm::WarningList *list) { double height = 0.0; try { json referenceHeight = isd.at("reference_height"); json minHeight = referenceHeight.at("minheight"); json unit = referenceHeight.at("unit"); height = metric_conversion(minHeight.get(), unit.get()); } catch (...) { if (list) { list->push_back(csm::Warning( csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the minimum height above the reference ellipsoid.", "Utilities::getMinHeight()")); } } return height; } double getMaxHeight(json isd, csm::WarningList *list) { double height = 0.0; try { json referenceHeight = isd.at("reference_height"); json maxHeight = referenceHeight.at("maxheight"); json unit = referenceHeight.at("unit"); height = metric_conversion(maxHeight.get(), unit.get()); } catch (...) { if (list) { list->push_back(csm::Warning( csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the maximum height above the reference ellipsoid.", "Utilities::getMaxHeight()")); } } return height; } double getSemiMajorRadius(json isd, csm::WarningList *list) { double radius = 0.0; try { json radii = isd.at("radii"); json semiMajor = radii.at("semimajor"); json unit = radii.at("unit"); radius = metric_conversion(semiMajor.get(), unit.get()); } catch (...) { if (list) { list->push_back(csm::Warning( csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the reference ellipsoid semimajor radius.", "Utilities::getSemiMajorRadius()")); } } return radius; } double getSemiMinorRadius(json isd, csm::WarningList *list) { double radius = 0.0; try { json radii = isd.at("radii"); json semiMinor = radii.at("semiminor"); json unit = radii.at("unit"); radius = metric_conversion(semiMinor.get(), unit.get()); } catch (...) { if (list) { list->push_back(csm::Warning( csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the reference ellipsoid semiminor radius.", "Utilities::getSemiMinorRadius()")); } } return radius; } // Converts the distortion model name from the ISD (string) to the enumeration // type. Defaults to transverse DistortionType getDistortionModel(json isd, csm::WarningList *list) { 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 (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the distortion model.", "Utilities::getDistortionModel()")); } } return DistortionType::TRANSVERSE; } DistortionType getDistortionModel(int aleDistortionModel, csm::WarningList *list) { try { ale::DistortionType aleDistortionType; aleDistortionType = (ale::DistortionType)aleDistortionModel; if (aleDistortionType == ale::DistortionType::TRANSVERSE) { return DistortionType::TRANSVERSE; } else if (aleDistortionType == ale::DistortionType::RADIAL) { return DistortionType::RADIAL; } else if (aleDistortionType == ale::DistortionType::KAGUYALISM) { return DistortionType::KAGUYALISM; } else if (aleDistortionType == ale::DistortionType::DAWNFC) { return DistortionType::DAWNFC; } else if (aleDistortionType == ale::DistortionType::LROLROCNAC) { return DistortionType::LROLROCNAC; }else if (aleDistortionType == ale::DistortionType::CAHVOR) { return DistortionType::CAHVOR; } } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the distortion model.", "Utilities::getDistortionModel()")); } } return DistortionType::TRANSVERSE; } std::vector getDistortionCoeffs(json isd, csm::WarningList *list) { std::vector coefficients; DistortionType distortion = getDistortionModel(isd); switch (distortion) { case DistortionType::TRANSVERSE: { try { std::vector coefficientsX, coefficientsY; coefficientsX = isd.at("optical_distortion") .at("transverse") .at("x") .get>(); coefficientsX.resize(10, 0.0); coefficientsY = isd.at("optical_distortion") .at("transverse") .at("y") .get>(); coefficientsY.resize(10, 0.0); coefficients = coefficientsX; coefficients.insert(coefficients.end(), coefficientsY.begin(), coefficientsY.end()); return coefficients; } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse a set of transverse " "distortion model coefficients.", "Utilities::getDistortion()")); } coefficients = std::vector(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>(); return coefficients; } catch (...) { if (list) { list->push_back(csm::Warning( csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the radial distortion model coefficients.", "Utilities::getDistortion()")); } coefficients = std::vector(3, 0.0); } } break; case DistortionType::KAGUYALISM: { try { std::vector coefficientsX = isd.at("optical_distortion") .at("kaguyalism") .at("x") .get>(); std::vector coefficientsY = isd.at("optical_distortion") .at("kaguyalism") .at("y") .get>(); double boresightX = isd.at("optical_distortion") .at("kaguyalism") .at("boresight_x") .get(); double boresightY = isd.at("optical_distortion") .at("kaguyalism") .at("boresight_y") .get(); 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 (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse a set of Kaguya LISM " "distortion model coefficients.", "Utilities::getDistortion()")); } coefficients = std::vector(8, 0.0); } } break; case DistortionType::DAWNFC: { try { coefficients = isd.at("optical_distortion") .at("dawnfc") .at("coefficients") .get>(); return coefficients; } catch (...) { if (list) { list->push_back(csm::Warning( csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the dawn radial distortion model coefficients.", "Utilities::getDistortion()")); } coefficients = std::vector(1, 0.0); } } break; case DistortionType::LROLROCNAC: { try { coefficients = isd.at("optical_distortion") .at("lrolrocnac") .at("coefficients") .get>(); return coefficients; } catch (...) { if (list) { list->push_back(csm::Warning( csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the lrolrocnac distortion model coefficients.", "Utilities::getDistortion()")); } coefficients = std::vector(1, 0.0); } } break; case DistortionType::CAHVOR: { try { coefficients = isd.at("optical_distortion") .at("cahvor") .at("coefficients") .get>(); return coefficients; } catch (...) { if (list) { list->push_back(csm::Warning( csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the radial distortion model coefficients.", "Utilities::getDistortion()")); } coefficients = std::vector(6, 0.0); } } break; } if (list) { list->push_back( csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the distortion model coefficients.", "Utilities::getDistortion()")); } return coefficients; } std::vector getSunPositions(json isd, csm::WarningList *list) { std::vector positions; try { json jayson = isd.at("sun_position"); json unit = jayson.at("unit"); for (auto &location : jayson.at("positions")) { positions.push_back(metric_conversion(location[0].get(), unit.get())); positions.push_back(metric_conversion(location[1].get(), unit.get())); positions.push_back(metric_conversion(location[2].get(), unit.get())); } } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the sun positions.", "Utilities::getSunPositions()")); } } return positions; } std::vector getSunVelocities(json isd, csm::WarningList *list) { std::vector velocities; try { json jayson = isd.at("sun_position"); json unit = jayson.at("unit"); for (auto &location : jayson.at("velocities")) { velocities.push_back(metric_conversion(location[0].get(), unit.get())); velocities.push_back(metric_conversion(location[1].get(), unit.get())); velocities.push_back(metric_conversion(location[2].get(), unit.get())); } } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the sun velocities.", "Utilities::getSunVelocities()")); } } return velocities; } std::vector getSensorPositions(json isd, csm::WarningList *list) { std::vector positions; try { json jayson = isd.at("sensor_position"); json unit = jayson.at("unit"); for (auto &location : jayson.at("positions")) { positions.push_back(metric_conversion(location[0].get(), unit.get())); positions.push_back(metric_conversion(location[1].get(), unit.get())); positions.push_back(metric_conversion(location[2].get(), unit.get())); } } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the sensor positions.", "Utilities::getSensorPositions()")); } } return positions; } std::vector getSensorVelocities(json isd, csm::WarningList *list) { std::vector velocities; try { json jayson = isd.at("sensor_position"); json unit = jayson.at("unit"); for (auto &velocity : jayson.at("velocities")) { velocities.push_back(metric_conversion(velocity[0].get(), unit.get())); velocities.push_back(metric_conversion(velocity[1].get(), unit.get())); velocities.push_back(metric_conversion(velocity[2].get(), unit.get())); } } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the sensor velocities.", "Utilities::getSensorVelocities()")); } } return velocities; } std::vector getSensorOrientations(json isd, csm::WarningList *list) { std::vector 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 (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the sensor orientations.", "Utilities::getSensorOrientations()")); } } return quaternions; } double getExposureDuration(nlohmann::json isd, csm::WarningList *list) { double duration; try { duration = isd.at("line_exposure_duration"); } catch (...) { if (list) { list->push_back( csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the line exposure duration.", "Utilities::getExposureDuration()")); } } return duration; } double getScaledPixelWidth(nlohmann::json isd, csm::WarningList *list) { double width; try { width = isd.at("scaled_pixel_width"); } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the scaled pixel width.", "Utilities::getScaledPixelWidth()")); } } return width; } std::string getLookDirection(nlohmann::json isd, csm::WarningList *list) { std::string lookDirection = ""; try { lookDirection = isd.at("look_direction"); } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the scaled pixel width.", "Utilities::getScaledPixelWidth()")); } } return lookDirection; } std::vector getScaleConversionTimes(nlohmann::json isd, csm::WarningList *list) { std::vector time; try { time = isd.at("range_conversion_times").get>(); } catch (...) { if (list) { list->push_back( csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the range conversion times.", "Utilities::getScaleConversionTimes()")); } } return time; } std::vector getScaleConversionCoefficients(nlohmann::json isd, csm::WarningList *list) { std::vector coefficients; try { for (auto &location : isd.at("range_conversion_coefficients")) { coefficients.push_back(location[0]); coefficients.push_back(location[1]); coefficients.push_back(location[2]); coefficients.push_back(location[3]); } } catch (...) { if (list) { list->push_back( csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the range conversion coefficients.", "Utilities::getScaleConversionCoefficients()")); } } return coefficients; } double getWavelength(json isd, csm::WarningList *list) { double wavelength = 0.0; try { wavelength = isd.at("wavelength"); } catch (...) { if (list) { list->push_back(csm::Warning(csm::Warning::DATA_NOT_AVAILABLE, "Could not parse the wavelength.", "Utilities::getWavelength()")); } } return wavelength; } json stateAsJson(std::string modelState) { // Remove special characters from string sanitize(modelState); std::size_t foundFirst = modelState.find_first_of("{"); std::size_t foundLast = modelState.find_last_of("}"); if (foundFirst == std::string::npos) { foundFirst = 0; } return json::parse(modelState.begin() + foundFirst, modelState.begin() + foundLast + 1); } void sanitize(std::string &input){ // Replaces characters from the string that are not printable with newlines std::replace_if(input.begin(), input.end(), [](int c){return !::isprint(c);}, '\n'); } // Read a file's content in a single string bool readFileInString(std::string const& filename, std::string & str) { str.clear(); // clear the output std::ifstream ifs(filename.c_str()); if (!ifs.is_open()) { std::cout << "Cannot open file: " << filename << std::endl; return false; } ifs.seekg(0, std::ios::end); str.reserve(ifs.tellg()); ifs.seekg(0, std::ios::beg); str.assign((std::istreambuf_iterator(ifs)), std::istreambuf_iterator()); ifs.close(); return true; } // Apply a rotation to a vector of quaternions. void applyRotationToQuatVec(ale::Rotation const& r, std::vector & quaternions) { int num_quat = quaternions.size(); for (int it = 0; it < num_quat/4; it++) { double * q = &quaternions[4*it]; // Note that quaternion in q is stored in order x, y, z, w, while // the rotation matrix wants it as w, x, y, z. std::vector trans_q = (r * ale::Rotation(q[3], q[0], q[1], q[2])).toQuaternion(); // Modify in-place q[0] = trans_q[1]; q[1] = trans_q[2]; q[2] = trans_q[3]; q[3] = trans_q[0]; } } // Apply a rotation and translation to a vector of xyz vectors. void applyRotationTranslationToXyzVec(ale::Rotation const& r, ale::Vec3d const& t, std::vector & xyz) { int num_xyz = xyz.size(); for (int it = 0; it < num_xyz/3; it++) { double * p = &xyz[3*it]; ale::Vec3d p_vec(p[0], p[1], p[2]); // Apply the rotation p_vec = r(p_vec); // Apply the translation p_vec += t; // Modify in-place p[0] = p_vec.x; p[1] = p_vec.y; p[2] = p_vec.z; } } // Convert ephemeris time, in seconds since January 1, 2000 12:00:00 AM GMT, // to a calendar time string, such as 2000-01-01T00:16:40Z. std::string ephemTimeToCalendarTime(double ephemTime) { // Care must be taken to not use mktime() or localtime() as their // precise value depends on if a location respects Daylight Savings Time. std::time_t y2k = 946684800; // January 1, 2000 12:00:00 AM GMT std::time_t finalTime = ephemTime + y2k; char buffer[22]; strftime(buffer, 22, "%Y-%m-%dT%H:%M:%SZ", std::gmtime(&finalTime)); buffer[21] = '\0'; return buffer; }