#include "computePointing.hpp" #include "SpiceUsr.h" #include #include #include #include #include #include #include #include #include namespace { const char * const g_programName = "computeInstrumentPointing"; int g_status = 0; enum CLParId { BODY, FRAME, HELP, // IMAGE, INSTRUMENT, MK, OUT_FILE, QUIET, SC_ID, TIME, NUMBER_PARS }; const char * const CLParams[] = { "b", "f", "h", // "img", "i", "m", "o", "q", "s", "t" }; SpiceChar ERROR_REPORT[] = "SHORT"; SpiceChar RETURN[] = "RETURN"; } namespace spice_pointing { class InvalidCommandLine : public std::runtime_error { public: InvalidCommandLine(const std::string & what): std::runtime_error(what) {} InvalidCommandLine(const InvalidCommandLine & source): std::runtime_error(source) {} }; struct CommandLinePar { CommandLinePar(const std::string & key): m_key(key), m_value(), m_isDefined(false), m_isKey(false) {} CommandLinePar(const std::string & key, const std::string & value, bool isValueKeywordName): m_key(key), m_value(value), m_isDefined(true), m_isKey(isValueKeywordName) {} CommandLinePar(const CommandLinePar & source): m_key(source.m_key), m_value(source.m_value), m_isDefined(source.m_isDefined), m_isKey(source.m_isKey) {} CommandLinePar & operator=(const CommandLinePar & source) { m_key = source.m_key; m_value = source.m_value; m_isDefined = source.m_isDefined; m_isKey = source.m_isKey; return *this; } std::string m_key; std::string m_value; bool m_isDefined; bool m_isKey; }; class CommandLineParameters { public: typedef std::map MapType; CommandLineParameters(const MapType * pars): m_pars(pars) {} virtual ~CommandLineParameters() { if (m_pars != 0) { for (MapType::const_reverse_iterator itor = m_pars->rbegin(); itor != m_pars->rend(); ++itor) { delete itor->second; } delete m_pars; } } const CommandLinePar& operator[](CLParId parId) const { return *m_pars->at(parId); } private: const MapType * const m_pars; }; CommandLineParameters * interpretCommandLine(int argc, char ** argv); void computeSpicePointing(const CommandLineParameters & pars); void usage(std::ostream & ios); SpiceDouble parseET(const std::string & time); bool isSpiceError(); // namespace spice_pointing //////////////////////////////////////////////////////////////////////////////// } std::ostream & operator<<(std::ostream & os, const spice_pointing::CommandLinePar & par) { os << "Par \"" << par.m_key << "\" " << (par.m_isDefined ? "= \"" + par.m_value + "\"" : "(undefined)"); return os; } namespace { std::string trim(const std::string & s) { std::string::const_iterator begin(s.begin()); std::string::const_iterator end(s.end()); while (begin != end && std::isspace(*begin)) { ++begin; } while (begin != end && std::isspace(*end)) { --end; } return std::string(begin, end); } void reportMissingPar(const spice_pointing::CommandLinePar & par, std::stringstream & ss, std::string & delim) { if (!par.m_isDefined) { ss << delim << "-" << par.m_key; // if (par.m_key != CLParams[HELP] && {par.m_key != CLParams[IMAGE]) { // ss << " (or -k" << par.m_key << ")"; // } delim = ", "; } } bool extractPar(spice_pointing::CommandLinePar & par, const std::string & arg) { spice_pointing::CommandLinePar newPar(par); bool foundMatch = false; std::string flag("-" + par.m_key + "="); std::string::size_type flagSize(flag.size()); // Look for -par=value. if (arg.compare(0, flagSize, flag) == 0) { newPar.m_value = arg.substr(flagSize); newPar.m_isDefined = true; foundMatch = true; // } else if (par.m_key != CLParams[HELP] && par.m_key != CLParams[IMAGE]) { // // Don't accept keyword option for either help or image file name inputs. // // // Look for -kpar=value (keyword). // flag = "-k" + par.m_key + "="; // flagSize = flag.size(); // // if (arg.compare(0, flagSize, flag) == 0) { // newPar.m_value = arg.substr(flagSize); // newPar.m_isDefined = true; // newPar.m_isKey = true; // foundMatch = true; // } } if (!foundMatch) { // Look for -par (boolean parameters, e.g., -h for help/usage). flag = "-" + par.m_key; if (arg.compare(flag) == 0) { newPar.m_value = "true"; newPar.m_isDefined = true; foundMatch = true; } } if (foundMatch) { if (par.m_isDefined) { // Check for duplicate parameters. std::stringstream ss; ss << "duplicated parameter? Attempted to parse parameter \"" + arg + "\" into already-defined parameter " << par; throw spice_pointing::InvalidCommandLine(ss.str()); } par = newPar; } return foundMatch; } void extractPar(spice_pointing::CommandLinePar & par, int argc, char ** argv) { for (int i = 1; i < argc; ++i) { extractPar(par, argv[i]); } } bool isTrue(const spice_pointing::CommandLinePar & par) { using namespace std; if (par.m_isDefined) { string parString(par.m_value); transform(parString.begin(), parString.end(), parString.begin(), ::tolower); return parString == "t" || parString == "true"; } return false; } // namespace //////////////////////////////////////////////////////////////////////////////// } namespace spice_pointing { CommandLineParameters * interpretCommandLine(int argc, char ** argv) { using namespace std; for (int i = 0; i < argc; ++i) { } CommandLineParameters::MapType * pars = new CommandLineParameters::MapType(); bool isKey = false; for (int i = 0; i < NUMBER_PARS; ++i) { const CLParId id = static_cast(i); const string & key(CLParams[i]); CommandLinePar * par = new CommandLinePar(key); // extractPar(*par, argc, argv); isKey |= par->m_isKey; pars->insert(make_pair(id, par)); } for (char ** arg = argv + 1; arg < argv + argc; ++arg) { bool foundMatch = false; for (int i = 0; i < NUMBER_PARS; ++i) { const CLParId id = static_cast(i); if (extractPar(*pars->at(id), *arg)) { foundMatch = true; // Do NOT break out of loop here so extractPar may find any duplicated parameters. } } if (!foundMatch) { throw InvalidCommandLine(string("no match for command line parameter \"") + *arg + "\""); } } if (!isTrue(*pars->at(HELP))) { // Validate all parameters prior to run if user did not request showing usage. stringstream ss; string delim; // const CommandLinePar * imageFile = pars->at(IMAGE); // // if (isKey && !imageFile->m_isDefined) { // reportMissingPar(*imageFile, ss, delim); // ss << " (image file required if using any -k options)"; // } reportMissingPar(*pars->at(BODY), ss, delim); reportMissingPar(*pars->at(FRAME), ss, delim); reportMissingPar(*pars->at(INSTRUMENT), ss, delim); reportMissingPar(*pars->at(MK), ss, delim); reportMissingPar(*pars->at(SC_ID), ss, delim); reportMissingPar(*pars->at(TIME), ss, delim); string missing = ss.str(); if (!missing.empty()) { throw InvalidCommandLine("missing required parameter(s): " + missing); } } return new CommandLineParameters(pars); } void computeSpicePointing(const CommandLineParameters & pars) { using namespace std; const char * body(pars[BODY].m_value.c_str()); const char * frame(pars[FRAME].m_value.c_str()); const char * instr(pars[INSTRUMENT].m_value.c_str()); const char * mkFile(pars[MK].m_value.c_str()); const char * scId(pars[SC_ID].m_value.c_str()); const char * time(pars[TIME].m_value.c_str()); furnsh_c(mkFile); if (isSpiceError()) { stringstream ss; ss << "unable to initialize SPICE environment using metakernel file \"" << mkFile << "\""; throw runtime_error(ss.str()); } double et = parseET(time); if (isSpiceError()) { stringstream ss; ss << "unable to parse time \"" << time << "\""; throw runtime_error(ss.str()); } double scPosition[3]; double unused[3]; double boredir[3]; double updir[3]; double frustum[12]; double sunPosition[3]; getSpacecraftState(et, scId, body, frame, scPosition, unused); if (isSpiceError()) { stringstream ss; ss << "unable to get position of spacecraft " << scId << " in the " << frame << " frame"; throw runtime_error(ss.str()); } getTargetState(et, scId, body, frame, "SUN", sunPosition, unused); if (isSpiceError()) { stringstream ss; ss << "unable to get the sun position in the " << frame << " frame"; throw runtime_error(ss.str()); } getFov(et, scId, body, frame, instr, boredir, updir, frustum); if (isSpiceError()) { stringstream ss; ss << "unable to get the " << instr << " FOV in the " << frame << " frame"; throw runtime_error(ss.str()); } const CommandLinePar & outFile = pars[OUT_FILE]; if (outFile.m_isDefined) { const char * outFileString(outFile.m_value.c_str()); saveInfoFile(outFileString, time, scPosition, boredir, updir, frustum, sunPosition); } if (!isTrue(pars[QUIET])) { writeInfo(cout, time, scPosition, boredir, updir, frustum, sunPosition); } } void usage(std::ostream & ios) { std::stringstream ss; ss << "--------------------------------------------------------------------------------" << "\nTool: " << g_programName << "\n--------------------------------------------------------------------------------" << "\nDescription: from a collection of SPICE kernels specified using a single SPICE" << "\n metakernel file, compute SPICE pointing and FOV information for an instrument" << "\n on a spacecraft with respect to a body in a particular frame at a particular" << "\n moment of time. Returns 0 for success and non-0 if a problem occurs setting" << "\n up SPICE, loading kernels, or computing the SPICE pointing information. The" << "\n computed information may be displayed and/or written to an output INFO file." << "\n" << "\n This tool may be used simply to test whether a given set of inputs would" << "\n return a valid SPICE pointing. To do this, do not specify an output file (no" << "\n -o parameter), use -q to suppress the output if desired, and check the" << "\n tool's exit value." << "\n--------------------------------------------------------------------------------" << "\nUsage: " << g_programName << " " << "\n where are:" << "\n" << "\n -m= (required), is the full path to the" << "\n metakernel file used to load all SPICE kernels" << "\n -s= (required), is the name of" << "\n the spacecraft as referenced in the SPICE kernels" << "\n -i= (required), is the name of the" << "\n instrument as referenced in the SPICE kernels" << "\n -b= (required), is the name of the body used by SPICE as the" << "\n origin from which to compute the pointing" << "\n -f= (required), is the name of the SPICE frame in which to" << "\n compute the SPICE pointing" << "\n -t=