Files
OpenSpace/modules/space/horizonsfile.cpp

259 lines
10 KiB
C++

/*****************************************************************************************
* *
* OpenSpace *
* *
* Copyright (c) 2014-2022 *
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
* software and associated documentation files (the "Software"), to deal in the Software *
* without restriction, including without limitation the rights to use, copy, modify, *
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
* permit persons to whom the Software is furnished to do so, subject to the following *
* conditions: *
* *
* The above copyright notice and this permission notice shall be included in all copies *
* or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
#include <modules/space/horizonsfile.h>
#include <ghoul/fmt.h>
#include <ghoul/filesystem/filesystem.h>
#include <ghoul/logging/logmanager.h>
#include <filesystem>
#include <fstream>
namespace {
constexpr const char* _loggerCat = "HorizonsFile";
constexpr const char* ApiSource = "NASA/JPL Horizons API";
constexpr const char* CurrentVersion = "1.1";
} // namespace
namespace openspace {
HorizonsFile::HorizonsFile()
: _file()
{}
HorizonsFile::HorizonsFile(std::filesystem::path file)
: _file(std::move(file))
{}
void HorizonsFile::setFile(std::filesystem::path file) {
_file = file;
}
const std::filesystem::path& HorizonsFile::file() const {
return _file;
}
std::filesystem::path& HorizonsFile::file() {
return _file;
}
HorizonsFile::HorizonsResult HorizonsFile::isValidAnswer(const json& answer) {
// Signature and version
auto signatureIt = answer.find("signature");
if (signatureIt != answer.end()) {
json signature = *signatureIt;
auto sourceIt = signature.find("source");
if (sourceIt != signature.end()) {
if (*sourceIt != ApiSource) {
LWARNING(fmt::format("Horizons answer from unkown source '{}'", *sourceIt));
}
}
else {
LWARNING("Could not find source information, source might not be acceptable");
}
auto versionIt = signature.find("version");
if (versionIt != signature.end()) {
if (*versionIt != CurrentVersion) {
LWARNING(fmt::format("Unknown Horizons version '{}' found. The "
"currently supported version is {}", *versionIt, CurrentVersion));
}
}
else {
LWARNING("Could not find version information, version might not be supported");
}
}
else {
LWARNING("Could not find signature information");
}
// Errors
auto it = answer.find("error");
if (it != answer.end()) {
// There was an error
std::string errorMessage = *it;
// Projected output length (~X) exceeds 90024 line max -- change step-size
if (errorMessage.find("Projected output length") != std::string::npos) {
return HorizonsFile::HorizonsResult::ErrorSize;
}
// STEP_SIZE too big, exceeds available span.
else if (errorMessage.find("STEP_SIZE too big") != std::string::npos) {
return HorizonsFile::HorizonsResult::ErrorSpan;
}
// No ephemeris for target "X" after A.D. Y UT
else if (errorMessage.find("No ephemeris for target") != std::string::npos) {
return HorizonsFile::HorizonsResult::ErrorTimeRange;
}
// No site matches. Use "*@body" to list, "c@body" to enter coords, ?! for help.
else if (errorMessage.find("No site matches") != std::string::npos ||
errorMessage.find("Cannot find central body") != std::string::npos)
{
return HorizonsFile::HorizonsResult::ErrorNoObserver;
}
// Observer table for X / Y->Y disallowed.
else if (errorMessage.find("disallowed") != std::string::npos) {
return HorizonsFile::HorizonsResult::ErrorObserverTargetSame;
}
// Insufficient ephemeris data has been loaded to compute the state of X
// relative to Y at the ephemeris epoch Z;
else if (errorMessage.find("Insufficient ephemeris data") != std::string::npos) {
return HorizonsFile::HorizonsResult::ErrorNoData;
}
// # E. Lon DXY DZ Observatory Name;
// -- - -------- ------ - ------ - ----------------;
// * Observer station *
// Multiple matching stations found.
else if (errorMessage.find("Multiple matching stations found") != std::string::npos) {
return HorizonsFile::HorizonsResult::MultipleObserverStations;
}
// Unknown error
else {
LERROR(errorMessage);
return HorizonsFile::HorizonsResult::UnknownError;
}
}
return HorizonsFile::HorizonsResult::Valid;
}
// Check whether the given Horizons file is valid or not
// Return an error code with what is the problem if there was one
HorizonsFile::HorizonsResult HorizonsFile::isValidHorizonsFile() const {
std::ifstream fileStream(_file);
if (!fileStream.good()) {
return HorizonsFile::HorizonsResult::Empty;
}
// The header of a Horizons file has a lot of information about the
// query that can tell us if the file is valid or not.
// The line $$SOE indicates start of data.
std::string line;
bool foundTarget = false;
std::getline(fileStream, line);
std::getline(fileStream, line); // First line is just stars (*) no information, skip
// Valid Target?
if (fileStream.good() && (line.find("Revised") != std::string::npos || line.find("JPL") != std::string::npos)) {
// If the target is valid, the first line is the date it was last revised
// In case of comets it says the Source in the top
foundTarget = true;
}
HorizonsFile::HorizonsResult result = HorizonsFile::HorizonsResult::UnknownError;
while (fileStream.good() && line.find("$$SOE") == std::string::npos) {
// Selected time range too big and step size too small?
if (line.find("change step-size") != std::string::npos) {
fileStream.close();
return HorizonsFile::HorizonsResult::ErrorSize;
}
// Selected time range too big for avalable time span?
if (line.find("STEP_SIZE too big") != std::string::npos) {
fileStream.close();
return HorizonsFile::HorizonsResult::ErrorSpan;
}
// Outside valid time range?
if (line.find("No ephemeris for target") != std::string::npos) {
// Available time range is located several lines before this in the file
// The avalable time range is persed later
fileStream.close();
return HorizonsFile::HorizonsResult::ErrorTimeRange;
}
// Valid Observer?
if (line.find("No site matches") != std::string::npos ||
line.find("Cannot find central body") != std::string::npos)
{
fileStream.close();
return HorizonsFile::HorizonsResult::ErrorNoObserver;
}
// Are observer and target the same?
if (line.find("disallowed") != std::string::npos)
{
fileStream.close();
return HorizonsFile::HorizonsResult::ErrorObserverTargetSame;
}
// Enough data?
if (line.find("Insufficient ephemeris data") != std::string::npos)
{
fileStream.close();
return HorizonsFile::HorizonsResult::ErrorNoData;
}
// Multiple Observer stations?
if (line.find("Multiple matching stations found") != std::string::npos) {
result = HorizonsFile::HorizonsResult::MultipleObserverStations;
}
// Multiple matching major bodies?
if (line.find("Multiple major-bodies match string") != std::string::npos) {
// Target
if (!foundTarget) {
// If target was not found then it is the target that has multiple matches
result = HorizonsFile::HorizonsResult::MultipleTarget;
}
// Observer
else {
result = HorizonsFile::HorizonsResult::MultipleObserver;
}
}
// Multiple matching small bodies?
if (line.find("Small-body Index Search Results") != std::string::npos) {
// Small bodies can only be targets not observers
result = HorizonsFile::HorizonsResult::MultipleTarget;
}
// No Target?
if (line.find("No matches found") != std::string::npos) {
fileStream.close();
return HorizonsFile::HorizonsResult::ErrorNoTarget;
}
std::getline(fileStream, line);
}
if (result != HorizonsFile::HorizonsResult::UnknownError) {
fileStream.close();
return result;
}
// If we reached end of file before we found the start of data then it is
// not a valid file
if (fileStream.good()) {
fileStream.close();
return HorizonsFile::HorizonsResult::Valid;
}
else {
fileStream.close();
return HorizonsFile::HorizonsResult::UnknownError;
}
}
} // namespace openspace