#include #include #include #include #include using namespace std; #include #include "config.h" #include "xpDefines.h" #include "xpUtil.h" #ifndef _GETOPT_H #include "xpGetopt.h" #endif #ifdef HAVE_LIBX11 #include #include #else #include "ParseGeom.h" extern "C" { extern int XParseGeometry(const char *string, int *x, int *y, unsigned int *width, unsigned int *height); } #endif #include "keywords.h" #include "findBodyXYZ.h" #include "Options.h" #include "parseColor.h" #include "PlanetProperties.h" #include "libannotate/libannotate.h" #include "libplanet/Planet.h" #include "libprojection/libprojection.h" extern void printVersion(); Options* Options::instance_ = NULL; Options* Options::getInstance() { if (instance_ == NULL) instance_ = new Options; return(instance_); } Options::~Options() { // delete instance_; } Options::Options() : arcSpacing_(0.1), background_(""), baseMag_(10.0), centerSelected_(false), configFile_(defaultConfigFile), dateFormat_("%c %Z"), displayMode_(ROOT), drawLabel_(false), drawUTCLabel_(false), dynamicOrigin_(""), font_(defaultFont), fontSize_(12), fork_(false), fov_(-1), fovMode_(RADIUS), geometryMask_(NoValue), geometrySelected_(false), glare_(28), grsLon_(94), grsSet_(false), hibernate_(0), idleWait_(0), interpolateOriginFile_(false), jplFile_(""), labelMask_(XNegative), labelX_(-15), labelY_(15), labelString_(""), latitude_(0), lightTime_(false), localTime_(-1), logMagStep_(0.4), longitude_(0), makeCloudMaps_(false), markerBounds_(""), north_(BODY), numTimes_(0), oppositeSide_(false), origin_(SUN), originFile_(""), originID_(0), originMode_(LBR), originSet_(false), outputBase_(""), outputExt_(defaultMapExt), outputStartIndex_(0), oX_(0), oY_(0), oZ_(0), pango_(false), pathRelativeTo_(SUN), pathRelativeToID_(-1), post_command_(""), prev_command_(""), primary_(SUN), printEphemeris_(false), projection_(MULTIPLE), projectionMode_(MULTIPLE), quality_(80), radius_(0.45), random_(false), rangeSpecified_(false), range_(1000), rotate_(0), rotate0_(0), saveDesktopFile_(false), starFreq_(0.001), star_map(defaultStarMap), sunLat_(0), sunLon_(0), target_(EARTH), targetID_(0), targetMode_(BODY), targetSet_(false), timewarp(1), tmpDir_(""), transparency_(false), transpng_(false), tX_(0), tY_(0), tZ_(0), universalTime_(true), useCurrentTime_(true), verbosity_(0), virtual_root(false), wait(300), width(512), height(512), windowX_(0), windowY_(0), windowTitle_(""), xid_(0) { memset(color_, 0, 3); color_[0] = 255; // default label color is red searchdir.push_back(DATADIR); #if defined(HAVE_AQUA) || defined(HAVE_LIBX11) char *homeDir = getenv("HOME"); if (homeDir != NULL) { ostringstream xplanetDir; #ifdef HAVE_AQUA xplanetDir << homeDir << "/Library/Xplanet"; #else xplanetDir << homeDir << "/.xplanet"; #endif searchdir.push_back(xplanetDir.str()); } #endif searchdir.push_back("xplanet"); struct timeval time; gettimeofday(&time, NULL); time_t t = time.tv_sec; julianDay_ = toJulian(gmtime(static_cast (&t))->tm_year + 1900, gmtime(static_cast (&t))->tm_mon + 1, gmtime(static_cast (&t))->tm_mday, gmtime(static_cast (&t))->tm_hour, gmtime(static_cast (&t))->tm_min, gmtime(static_cast (&t))->tm_sec); tv_sec = get_tv_sec(julianDay_); srandom((unsigned int) tv_sec); } void Options::parseArgs(int argc, char **argv) { static struct option long_options[] = { {"arc_file", required_argument, NULL, ARC_FILE}, {"arc_spacing", required_argument, NULL, ARC_SPACING}, {"background", required_argument, NULL, BACKGROUND}, {"base_magnitude", required_argument, NULL, BASEMAG}, {"body", required_argument, NULL, TARGET}, {"center", required_argument, NULL, CENTER}, {"color", required_argument, NULL, COLOR}, {"config", required_argument, NULL, CONFIG_FILE}, {"date", required_argument, NULL, DATE}, {"date_format", required_argument, NULL, DATE_FORMAT}, {"dynamic_origin", required_argument, NULL, DYNAMIC_ORIGIN}, {"ephemeris_file", required_argument, NULL, JPL_FILE}, {"font", required_argument, NULL, FONT}, {"fontsize", required_argument, NULL, FONTSIZE}, {"fork", no_argument, NULL, FORK}, {"fov", required_argument, NULL, FOV}, {"geometry", required_argument, NULL, GEOMETRY}, {"glare", required_argument, NULL, GLARE}, {"gmtlabel", no_argument, NULL, UTCLABEL}, {"grs_longitude", required_argument, NULL, GRS_LON}, {"hibernate", required_argument, NULL, HIBERNATE}, {"idlewait", required_argument, NULL, IDLEWAIT}, {"interpolate_origin_file", no_argument, NULL, INTERPOLATE_ORIGIN_FILE}, {"jdate", required_argument, NULL, JDATE}, {"label", no_argument, NULL, LABEL}, {"labelpos", required_argument, NULL, LABELPOS}, {"label_string", required_argument, NULL, LABEL_STRING}, {"latitude", required_argument, NULL, LATITUDE}, {"light_time", no_argument, NULL, LIGHT_TIME}, {"localtime", required_argument, NULL, LOCALTIME}, {"log_magstep", required_argument, NULL, LOGMAGSTEP}, {"longitude", required_argument, NULL, LONGITUDE}, {"make_cloud_maps",no_argument, NULL, MAKECLOUDMAPS}, {"marker_file", required_argument, NULL, MARKER_FILE}, {"markerbounds", required_argument, NULL, MARKER_BOUNDS}, {"north", required_argument, NULL, NORTH}, {"num_times", required_argument, NULL, NUM_TIMES}, {"origin", required_argument, NULL, ORIGIN}, {"origin_file", required_argument, NULL, ORIGINFILE}, {"output", required_argument, NULL, OUTPUT}, {"output_start_index",required_argument,NULL,OUTPUT_START_INDEX}, {"pango", no_argument, NULL, PANGO}, {"path_relative_to", required_argument, NULL, PATH_RELATIVE_TO}, {"post_command", required_argument, NULL, POST_COMMAND}, {"prev_command", required_argument, NULL, PREV_COMMAND}, {"print_ephemeris",no_argument, NULL, EPHEMERIS}, {"projection", required_argument, NULL, PROJECTION}, {"proj_param", required_argument, NULL, PROJECTIONPARAMETER}, {"quality", required_argument, NULL, QUALITY}, {"radius", required_argument, NULL, RADIUS}, {"random", no_argument, NULL, RANDOM}, {"range", required_argument, NULL, RANGE}, {"rotate", required_argument, NULL, ROTATE}, {"save_desktop_file", no_argument, NULL, SAVE_DESKTOP_FILE}, {"searchdir", required_argument, NULL, SEARCHDIR}, {"spice_ephemeris", required_argument, NULL, SPICE_EPHEMERIS}, {"spice_file", required_argument, NULL, SPICE_FILE}, {"starfreq", required_argument, NULL, STARFREQ}, {"starmap", required_argument, NULL, STARMAP}, {"target", required_argument, NULL, TARGET}, {"tt", no_argument, NULL, TERRESTRIAL}, {"timewarp", required_argument, NULL, TIMEWARP}, {"tmpdir", required_argument, NULL, TMPDIR}, {"transparency", no_argument, NULL, TRANSPARENT}, {"transpng", required_argument, NULL, TRANSPNG}, {"utclabel", no_argument, NULL, UTCLABEL}, {"verbosity", required_argument, NULL, VERBOSITY}, {"version", no_argument, NULL, VERSIONNUMBER}, {"vroot", no_argument, NULL, VROOT}, {"wait", required_argument, NULL, WAIT}, {"window", no_argument, NULL, WINDOW}, {"window-id", required_argument, NULL, XWINID}, {"window_title", required_argument, NULL, WINDOWTITLE}, {"XID", required_argument, NULL, XWINID}, {"xscreensaver", no_argument, NULL, VROOT}, {NULL, 0, NULL, 0} }; int this_option; int option_index = 0; while((this_option = getopt_long_only(argc, argv, "+", long_options, &option_index)) >= 0) { switch (this_option) { case ARC_FILE: arcFiles_.push_back(optarg); break; case ARC_SPACING: sscanf(optarg, "%lf", &arcSpacing_); if (arcSpacing_ < 0) { ostringstream errMsg; errMsg << "Arc spacing must be > 0\n"; xpWarn(errMsg.str(), __FILE__, __LINE__); arcSpacing_ = 0.1; } break; case BACKGROUND: background_ = optarg; break; case BASEMAG: sscanf(optarg, "%lf", &baseMag_); break; case CENTER: { unsigned int w, h; int x, y; int mask = XParseGeometry(optarg, &x, &y, &w, &h); centerSelected_ = ((mask & XValue) && (mask & YValue)); centerX_ = x; centerY_ = y; } break; case COLOR: parseColor(optarg, color_); break; case CONFIG_FILE: configFile_ = optarg; break; case DATE: { long int yyyymmdd, hhmmss; sscanf(optarg, "%ld.%ld", &yyyymmdd, &hhmmss); int yyyymm = yyyymmdd / 100; int year = yyyymm/100; int month = abs(yyyymm - year * 100); int day = abs((int) yyyymmdd - yyyymm * 100); int hhmm = hhmmss / 100; int hour = hhmm / 100; int min = hhmm - hour * 100; int sec = hhmmss - hhmm * 100; julianDay_ = toJulian(year, month, day, hour, min, sec); tv_sec = get_tv_sec(julianDay_); useCurrentTime_ = false; } break; case DATE_FORMAT: dateFormat_ = optarg; break; case DYNAMIC_ORIGIN: dynamicOrigin_ = optarg; originMode_ = LBR; break; case EPHEMERIS: printEphemeris_ = true; break; case FONT: #ifdef HAVE_LIBFREETYPE font_.assign(optarg); #else { ostringstream errMsg; errMsg << "Sorry, this binary was built without FreeType " << "support. The -" << long_options[option_index].name << " option will be ignored.\n"; xpWarn(errMsg.str(), __FILE__, __LINE__); } #endif break; case FONTSIZE: { #ifdef HAVE_LIBFREETYPE int val; sscanf(optarg, "%d", &val); if (val > 0) fontSize_ = val; #else { ostringstream errMsg; errMsg << "Sorry, this binary was built without FreeType " << "support. The -" << long_options[option_index].name << " option will be ignored.\n"; xpWarn(errMsg.str(), __FILE__, __LINE__); } #endif } break; case FORK: fork_ = true; break; case FOV: sscanf(optarg, "%lf", &fov_); if (fov_ <= 0) { xpWarn("FOV must be positive.\n", __FILE__, __LINE__); } else { fov_ *= deg_to_rad; fovMode_ = FOV; } break; case GEOMETRY: { geometryMask_ = XParseGeometry(optarg, &windowX_, &windowY_, &width, &height); geometrySelected_ = ((geometryMask_ & WidthValue) && (geometryMask_ & HeightValue)); } break; case GLARE: { double g; sscanf(optarg, "%lf", &g); if (g > 0) glare_ = g; } break; case GRS_LON: sscanf(optarg, "%lf", &grsLon_); grsLon_ = fmod(grsLon_, 360); grsSet_ = true; break; case HIBERNATE: sscanf(optarg, "%lu", &hibernate_); hibernate_ *= 1000; break; case IDLEWAIT: sscanf(optarg, "%lu", &idleWait_); idleWait_ *= 1000; break; case INTERPOLATE_ORIGIN_FILE: interpolateOriginFile_ = true; break; case JDATE: sscanf(optarg, "%lf", &julianDay_); tv_sec = get_tv_sec(julianDay_); useCurrentTime_ = false; break; case JPL_FILE: jplFile_ = optarg; break; case LABEL: drawLabel_ = true; break; case LABELPOS: { unsigned int temp; labelMask_ = XParseGeometry(optarg, &labelX_, &labelY_, &temp, &temp); if (labelMask_ & (WidthValue | HeightValue)) { xpWarn("width and height supplied in -labelpos will be ignored\n", __FILE__, __LINE__); } drawLabel_ = true; } break; case LABEL_STRING: labelString_ = optarg; drawLabel_ = true; break; case LATITUDE: sscanf(optarg, "%lf", &latitude_); if (latitude_ < -90) latitude_ = -90; if (latitude_ > 90) latitude_ = 90; latitude_ *= deg_to_rad; originMode_ = LBR; break; case LIGHT_TIME: lightTime_ = true; break; case LOCALTIME: sscanf(optarg, "%lf", &localTime_); if (localTime_ < 0 || localTime_ > 24) { localTime_ = fmod(localTime_, 24.); if (localTime_ < 0) localTime_ += 24; ostringstream errStr; errStr << "localtime set to " << localTime_ << "\n"; xpWarn(errStr.str(), __FILE__, __LINE__); } originMode_ = LBR; break; case LOGMAGSTEP: sscanf(optarg, "%lf", &logMagStep_); break; case LONGITUDE: sscanf(optarg, "%lf", &longitude_); longitude_ = fmod(longitude_, 360); longitude_ *= deg_to_rad; originMode_ = LBR; break; case MAKECLOUDMAPS: displayMode_ = OUTPUT; makeCloudMaps_ = true; break; case MARKER_BOUNDS: markerBounds_.assign(optarg); break; case MARKER_FILE: markerFiles_.push_back(optarg); break; case NORTH: { char *lowercase = optarg; char *ptr = optarg; while (*ptr) *ptr++ = tolower(*optarg++); if (strncmp(lowercase, "galactic", 1) == 0) north_ = GALACTIC; else if (strncmp(lowercase, "orbit", 1) == 0) north_ = ORBIT; else if (strncmp(lowercase, "path", 1) == 0) north_ = PATH; else if (strncmp(lowercase, "terrestrial", 1) == 0) north_ = TERRESTRIAL; else { if (strncmp(lowercase, "body", 1) != 0) xpWarn("Unknown value for -north, using body\n", __FILE__, __LINE__); north_ = BODY; } } break; case NUM_TIMES: sscanf(optarg, "%d", &numTimes_); if (numTimes_ < 0) numTimes_ = 0; break; case ORIGIN: { char *name = optarg; if (name[0] == '-') { oppositeSide_ = true; name++; } origin_ = Planet::parseBodyName(name); switch (origin_) { case ABOVE_ORBIT: originMode_ = ABOVE; break; case BELOW_ORBIT: originMode_ = BELOW; break; case MAJOR_PLANET: originMode_ = MAJOR; break; case NAIF: if (strlen(name) < 5) { ostringstream errMsg; errMsg << "NAIF id must be specified " << "(e.g. naif-82 for Cassini)\n"; xpWarn(errMsg.str(), __FILE__, __LINE__); } else { sscanf(name+4, "%d", &originID_); } originMode_ = LBR; break; case NORAD: if (strlen(name) < 6) { ostringstream errMsg; errMsg << "NORAD id must be specified " << "(e.g. NORAD20580 for Hubble)\n"; xpWarn(errMsg.str(), __FILE__, __LINE__); } else { sscanf(name+5, "%d", &originID_); } originMode_ = LBR; break; case RANDOM_BODY: originMode_ = RANDOM; break; case SAME_SYSTEM: originMode_ = SYSTEM; break; case ALONG_PATH: case UNKNOWN_BODY: xpWarn("Invalid origin specified, using SUN\n", __FILE__, __LINE__); origin_ = SUN; default: originMode_ = BODY; break; } } break; case ORIGINFILE: originFile_ = optarg; originMode_ = LBR; break; case TRANSPNG: transpng_ = true; // fall through to OUTPUT block case OUTPUT: outputBase_ = optarg; if (outputBase_.find('.') == string::npos) { outputExt_ = defaultMapExt; } else { outputExt_.assign(outputBase_, outputBase_.rfind('.'), outputBase_.size()); outputBase_.assign(outputBase_, 0, outputBase_.rfind('.')); } displayMode_ = OUTPUT; geometrySelected_ = true; break; case OUTPUT_START_INDEX: sscanf(optarg, "%d", &outputStartIndex_); break; case PANGO: #ifdef HAVE_LIBPANGOFT2 pango_ = true; #else { ostringstream errMsg; errMsg << "Sorry, this binary was built without Pango " << "support. The -" << long_options[option_index].name << " option will be ignored.\n"; xpWarn(errMsg.str(), __FILE__, __LINE__); } #endif break; case PATH_RELATIVE_TO: pathRelativeTo_ = Planet::parseBodyName(optarg); switch (pathRelativeTo_) { case NAIF: if (strlen(optarg) < 5) { ostringstream errMsg; errMsg << "NAIF id must be specified " << "(e.g. naif-82 for Cassini)\n"; xpWarn(errMsg.str(), __FILE__, __LINE__); } else { sscanf(optarg+4, "%d", &pathRelativeToID_); } break; case NORAD: if (strlen(optarg) < 6) { ostringstream errMsg; errMsg << "NORAD id must be specified " << "(e.g. NORAD20580 for Hubble)\n"; xpWarn(errMsg.str(), __FILE__, __LINE__); } else { sscanf(optarg+5, "%d", &pathRelativeToID_); } break; case RANDOM_BODY: case ABOVE_ORBIT: case ALONG_PATH: case BELOW_ORBIT: case DEFAULT: case MAJOR_PLANET: case SAME_SYSTEM: case UNKNOWN_BODY: xpWarn("Unknown body specified for path, using SUN\n", __FILE__, __LINE__); pathRelativeTo_ = SUN; default: break; } break; case POST_COMMAND: post_command_.assign(optarg); break; case PREV_COMMAND: prev_command_.assign(optarg); break; case PROJECTION: projectionMode_ = getProjectionType(optarg); break; case PROJECTIONPARAMETER: { double d; sscanf(optarg, "%lf", &d); projectionParameters_.push_back(d * deg_to_rad); } break; case QUALITY: sscanf(optarg, "%d", &quality_); if (quality_ < 0) quality_ = 0; if (quality_ > 100) quality_ = 100; break; case RADIUS: sscanf(optarg, "%lf", &radius_); if (radius_ <= 0) xpExit("radius must be positive\n", __FILE__, __LINE__); radius_ /= 100; fov_ = 1; // just a sneaky way to know that -radius has // been set fovMode_ = RADIUS; break; case RANDOM: random_ = true; originMode_ = LBR; // This block is repeated in setOrigin(), but this way the // user can use -random to set a random rotation and then // set the sub-observer point another way, like with // -origin sun longitude_ = random() % 360; longitude_ *= deg_to_rad; // Weight random latitudes towards the equator latitude_ = (random() % 2000)/1000.0 - 1; latitude_ = asin(latitude_); rotate0_ = random() % 360; rotate0_ *= deg_to_rad; rotate_ = rotate0_; break; case RANGE: sscanf(optarg, "%lf", &range_); rangeSpecified_ = (range_ > 1); if (!rangeSpecified_) { range_ = 1000; xpWarn("range must be greater than 1\n", __FILE__, __LINE__); } break; case ROTATE: sscanf(optarg, "%lf", &rotate0_); rotate0_ = fmod(rotate0_, 360.) * deg_to_rad; rotate_ = rotate0_; break; case SAVE_DESKTOP_FILE: saveDesktopFile_ = true; break; case SEARCHDIR: searchdir.push_back(optarg); break; case SPICE_EPHEMERIS: { #ifdef HAVE_CSPICE int id; sscanf(optarg, "%d", &id); spiceEphemeris_.push_back(id); #else ostringstream errMsg; errMsg << "Sorry, this binary was built without SPICE " << "support. The -" << long_options[option_index].name << " option will be ignored.\n"; xpWarn(errMsg.str(), __FILE__, __LINE__); #endif } break; case SPICE_FILE: { #ifdef HAVE_CSPICE spiceFiles_.push_back(optarg); #else ostringstream errMsg; errMsg << "Sorry, this binary was built without SPICE " << "support. The -" << long_options[option_index].name << " option will be ignored.\n"; xpWarn(errMsg.str(), __FILE__, __LINE__); #endif } break; case STARFREQ: sscanf(optarg, "%lf", &starFreq_); if (starFreq_ < 0) starFreq_ = 0; else if (starFreq_ > 1) starFreq_ = 1; break; case STARMAP: star_map = optarg; break; case TARGET: target_ = Planet::parseBodyName(optarg); switch (target_) { case ALONG_PATH: targetMode_ = LOOKAT; break; case MAJOR_PLANET: targetMode_ = MAJOR; break; case NAIF: if (strlen(optarg) < 5) { ostringstream errMsg; errMsg << "NAIF id must be specified " << "(e.g. naif-82 for Cassini)\n"; xpWarn(errMsg.str(), __FILE__, __LINE__); } else { sscanf(optarg+4, "%d", &targetID_); targetMode_ = LOOKAT; } break; case NORAD: if (strlen(optarg) < 6) { ostringstream errMsg; errMsg << "NORAD id must be specified " << "(e.g. NORAD20580 for Hubble)\n"; xpWarn(errMsg.str(), __FILE__, __LINE__); } else { sscanf(optarg+5, "%d", &targetID_); targetMode_ = LOOKAT; } break; case RANDOM_BODY: targetMode_ = RANDOM; break; case ABOVE_ORBIT: case BELOW_ORBIT: case SAME_SYSTEM: case UNKNOWN_BODY: xpWarn("Unknown target body specified, using EARTH\n", __FILE__, __LINE__); target_ = EARTH; default: targetMode_ = BODY; break; } break; case TERRESTRIAL: universalTime_ = false; break; case TIMEWARP: sscanf(optarg, "%lf", &timewarp); useCurrentTime_ = false; break; case TMPDIR: tmpDir_.assign(optarg); tmpDir_ += separator; break; case TRANSPARENT: transparency_ = true; break; case UTCLABEL: drawLabel_ = true; drawUTCLabel_ = true; break; case VERBOSITY: sscanf(optarg, "%d", &verbosity_); break; case VERSIONNUMBER: printVersion(); exit(EXIT_SUCCESS); break; case VROOT: virtual_root = true; break; case WAIT: sscanf(optarg, "%d", &wait); break; case WINDOWTITLE: windowTitle_.assign(optarg); // fall through case WINDOW: displayMode_ = WINDOW; geometrySelected_ = true; break; default: case XWINID: { if (optarg[0] == '0' && (optarg[1] == 'x' || optarg[1] == 'X')) { sscanf(optarg, "%lx", &xid_); } else { sscanf(optarg, "%lu", &xid_); } displayMode_ = WINDOW; } break; case UNKNOWN: { cout << "Valid options to Xplanet are:\n"; unsigned int i = 0; while (1) { if (long_options[i].name == NULL) break; printf("-%-20s", long_options[i].name); if (long_options[i].has_arg) cout << " (needs argument)"; cout << endl; i++; } exit(EXIT_SUCCESS); } break; } } if (optind < argc) { string errMsg("unrecognized options: "); while (optind < argc) { errMsg += argv[optind++]; errMsg += " "; } errMsg += "\n"; if (long_options[option_index].has_arg) { errMsg += "Perhaps you didn't supply an argument to -"; errMsg += long_options[option_index].name; errMsg += "?\n"; } xpExit(errMsg, __FILE__, __LINE__); } // useCurrentTime is false if: // 1) -date or -jdate is used // 2) -timewarp is used // 3) -origin_file is used AND -interpolate_origin_file is not // used if (useCurrentTime_) { if (!originFile_.empty() && !interpolateOriginFile_) { useCurrentTime_ = false; } } if (!originFile_.empty() || !dynamicOrigin_.empty()) originMode_ = LBR; // A number of options are meaningless if we're not looking at a // planetary body. if (targetMode_ == LOOKAT) { if (projectionMode_ != MULTIPLE) { ostringstream errStr; errStr << "Can't use -projection option without a " << "planetary body\n"; xpWarn(errStr.str(), __FILE__, __LINE__); projectionMode_ = MULTIPLE; } if (fovMode_ != FOV) { fov_ = 45 * deg_to_rad; fovMode_ = FOV; } if (north_ == BODY || north_ == ORBIT) { north_ = TERRESTRIAL; } } } void Options::getOrigin(double &X, double &Y, double &Z) { X = oX_; Y = oY_; Z = oZ_; } void Options::setOrigin(const double X, const double Y, const double Z) { oX_ = X; oY_ = Y; oZ_ = Z; } void Options::getTarget(double &X, double &Y, double &Z) { X = tX_; Y = tY_; Z = tZ_; } void Options::setTarget(const double X, const double Y, const double Z) { tX_ = X; tY_ = Y; tZ_ = Z; } void Options::setOrigin(PlanetProperties *planetProperties[]) { switch (originMode_) { case LBR: { if (random_) { longitude_ = random() % 360; longitude_ *= deg_to_rad; // Weight random latitudes towards the equator latitude_ = (random() % 2000)/1000.0 - 1; latitude_ = asin(latitude_); rotate0_ = random() % 360; rotate0_ *= deg_to_rad; rotate_ = rotate0_; } if (origin_ == NAIF || origin_ == NORAD) { findBodyXYZ(julianDay_, origin_, originID_, oX_, oY_, oZ_); } else { if (!targetSet_) xpExit("Target body not set!\n", __FILE__, __LINE__); body referenceBody = target_; if (!originFile_.empty() || !dynamicOrigin_.empty()) referenceBody = origin_; Planet p(julianDay_, referenceBody); p.calcHeliocentricEquatorial(); if (localTime_ >= 0) { double subSolarLat = 0; double subSolarLon = 0; p.XYZToPlanetographic(0, 0, 0, subSolarLat, subSolarLon); longitude_ = (subSolarLon - M_PI + p.Flipped() * localTime_ * M_PI / 12); } p.PlanetographicToXYZ(oX_, oY_, oZ_, latitude_, longitude_, range_); } } break; case MAJOR: case RANDOM: case SYSTEM: { if (!targetSet_) xpExit("Target body not set!\n", __FILE__, __LINE__); // check to see that at least one body has random_origin=true bool no_random_origin = true; for (int i = 0; i < RANDOM_BODY; i++) { if (i == target_) continue; if (planetProperties[i]->RandomOrigin()) { no_random_origin = false; break; } } if (no_random_origin) { string errMsg("Target is "); errMsg += planetProperties[target_]->Name(); errMsg += ", random_origin is false for all other bodies. "; errMsg += "Check your config file.\n"; xpExit(errMsg, __FILE__, __LINE__); } bool found_origin = false; while (!found_origin) { origin_ = static_cast (random() % RANDOM_BODY); if (verbosity_ > 1) { ostringstream msg; msg << "target = " << body_string[target_] << ", origin = " << body_string[origin_] << endl; xpMsg(msg.str(), __FILE__, __LINE__); } if (origin_ == target_) continue; if (originMode_ == RANDOM) { found_origin = true; } else { Planet o(julianDay_, origin_); if (originMode_ == MAJOR) { found_origin = (o.Primary() == SUN); } else if (originMode_ == SYSTEM) { // SYSTEM means one of three things: // 1) target and origin have same primary // 2) target is origin's primary // 3) origin is target's primary found_origin = ((primary_ == o.Primary() || o.Primary() == target_ || origin_ == primary_)); } } if (found_origin) found_origin = planetProperties[origin_]->RandomOrigin(); } // while (!found_origin) } // fall through case BODY: { findBodyXYZ(julianDay_, origin_, originID_, oX_, oY_, oZ_); if (oppositeSide_) { oX_ = 2*tX_ - oX_; oY_ = 2*tY_ - oY_; oZ_ = 2*tZ_ - oZ_; } } break; case ABOVE: case BELOW: { if (!targetSet_) xpExit("Target body not set!\n", __FILE__, __LINE__); double pX, pY, pZ; findBodyXYZ(julianDay_, primary_, -1, pX, pY, pZ); double vX, vY, vZ; findBodyVelocity(julianDay_, target_, targetID_, primary_, -1, vX, vY, vZ); // cross product of position and velocity vectors points to the // orbital north pole double pos[3] = { tX_ - pX, tY_ - pY, tZ_ - pZ }; double vel[3] = { vX, vY, vZ }; double north[3]; cross(pos, vel, north); double mag = sqrt(dot(north, north)); Planet primary(julianDay_, primary_); double dist = FAR_DISTANCE * primary.Radius(); oX_ = dist * north[0]/mag + pX; oY_ = dist * north[1]/mag + pY; oZ_ = dist * north[2]/mag + pZ; const double radius = sqrt(pos[0] * pos[0] + pos[1] * pos[1] + pos[2] * pos[2]); if (fov_ < 0) // will only be zero if -fov or -radius haven't // been specified { fov_ = 4*radius/dist; // fit the orbit on the screen fovMode_ = FOV; } if (originMode_ == BELOW) { oX_ -= 2 * (oX_ - pX); oY_ -= 2 * (oY_ - pX); oZ_ -= 2 * (oZ_ - pX); } } break; } if (rangeSpecified_ && targetMode_ != LOOKAT) { Planet p2(julianDay_, target_); p2.calcHeliocentricEquatorial(); p2.XYZToPlanetographic(oX_, oY_, oZ_, latitude_, longitude_); p2.PlanetographicToXYZ(oX_, oY_, oZ_, latitude_, longitude_, range_); } originSet_ = true; } void Options::setTarget(PlanetProperties *planetProperties[]) { switch (targetMode_) { case MAJOR: case RANDOM: { bool no_random_target = true; for (int i = 0; i < RANDOM_BODY; i++) { if (planetProperties[i]->RandomTarget()) { no_random_target = false; break; } } if (no_random_target) { ostringstream errMsg; errMsg << "random_target is false for all bodies. " << "Check your config file.\n"; xpExit(errMsg.str(), __FILE__, __LINE__); } bool found_target = false; while (!found_target) { target_ = (body) (random() % RANDOM_BODY); found_target = planetProperties[target_]->RandomTarget(); if (found_target && targetMode_ == MAJOR) { Planet p(julianDay_, target_); found_target = (p.Primary() == SUN); } } } // fall through case BODY: { Planet t(julianDay_, target_); primary_ = t.Primary(); } break; case LOOKAT: if (target_ == NAIF || target_ == ALONG_PATH) { primary_ = SUN; } else if (target_ == NORAD) { primary_ = EARTH; } break; default: xpExit("Unknown target mode?\n", __FILE__, __LINE__); } if (target_ == ALONG_PATH) { findBodyVelocity(julianDay_, origin_, originID_, pathRelativeTo_, pathRelativeToID_, tX_, tY_, tZ_); tX_ *= FAR_DISTANCE; tY_ *= FAR_DISTANCE; tZ_ *= FAR_DISTANCE; } else { findBodyXYZ(julianDay_, target_, targetID_, tX_, tY_, tZ_); } targetSet_ = true; } void Options::incrementTime(const double sec) { julianDay_ += sec/86400; tv_sec = get_tv_sec(julianDay_); } void Options::setTime(const double jd) { julianDay_ = jd; tv_sec = get_tv_sec(julianDay_); } int Options::OriginMode() const { int returnVal = originMode_; if (originMode_ == MAJOR || originMode_ == RANDOM || originMode_ == SYSTEM) returnVal = BODY; return(returnVal); }