public override Orbit drawOrbit () { CelestialBody body = AsteroidManager.getPlanetByName (targetBody); Debug.Log ("[CustomAsteroids]: " + Localizer.Format ("#autoLOC_CustomAsteroids_LogOrbitDraw", getName ())); double deltaV = wrappedDraw (vSoi, getName (), "vSoi"); double deltaT = wrappedDraw (warnTime, getName (), "warnTime"); if (deltaV <= 0.0) { throw new InvalidOperationException ( Localizer.Format ("#autoLOC_CustomAsteroids_ErrorFlybyBadVInf", getName (), deltaV)); } // Negative deltaT is allowed double peri; switch (approach.getParam ()) { case ApproachRange.Type.ImpactParameter: double b = wrappedDraw (approach, getName (), "approach"); if (b < 0.0) { throw new InvalidOperationException ( Localizer.Format ("#autoLOC_CustomAsteroids_ErrorFlybyBadB", getName (), b)); } double a = -body.gravParameter / (deltaV * deltaV); double x = b / a; peri = a * (1.0 - Math.Sqrt (x * x + 1.0)); break; case ApproachRange.Type.Periapsis: peri = wrappedDraw (approach, getName (), "approach"); if (peri < 0.0) { throw new InvalidOperationException ( Localizer.Format ("#autoLOC_CustomAsteroids_ErrorFlybyBadPeri", getName (), peri)); } break; default: throw new InvalidOperationException ( Localizer.Format ("#autoLOC_CustomAsteroids_ErrorFlybyBadApproach", getName (), approach.getParam ())); } #if DEBUG Debug.Log ($"[CustomAsteroids]: " + Localizer.Format ("#autoLOC_CustomAsteroids_LogFlyby", peri, targetBody, deltaT / (6.0 * 3600.0), deltaV) ); #endif Orbit newOrbit = createHyperbolicOrbit (body, peri, deltaV, Planetarium.GetUniversalTime () + deltaT); // Sun has sphereOfInfluence = +Infinity, so condition will always fail for Sun-centric // orbit. No special treatment needed for the case where the orbit lies entirely outside // the SoI while (needsSoITransition (newOrbit)) { newOrbit = patchToParent (newOrbit); } newOrbit.UpdateFromUT (Planetarium.GetUniversalTime ()); return newOrbit; }
/// <summary> /// Returns the desired property of a known celestial body. /// </summary> /// /// <param name="planet">The exact, case-sensitive name of the celestial body. Assumes all /// loaded celestial bodies have unique names.</param> /// <param name="property">The short name of the property to recover. Must be one /// of ("rad", "soi", "sma", "per", "apo", "ecc", "inc", "ape", "lan", "mna0", "mnl0", /// "prot", "psun", "porb", "vesc", "vorb", "vmin", or "vmax"). /// The only properties supported for Sun are "rad", "soi", "prot", and "vesc".</param> /// <returns>The value of <c>property</c> appropriate for <c>planet</c>. Distances are given /// in meters, angles are given in degrees.</returns> /// /// <exception cref="ArgumentException">Thrown if no planet named <c>name</c> exists, or if /// <c>property</c> does not have one of the allowed values. The program state shapp remain /// unchanged in the event of an exception.</exception> protected static double getPlanetProperty(string planet, string property) { CelestialBody body = AsteroidManager.getPlanetByName(planet); switch (property.ToLower()) { case "rad": return(body.Radius); case "soi": return(body.sphereOfInfluence); case "prot": return(body.rotates ? body.rotationPeriod : double.PositiveInfinity); case "psol": if (body.solarRotationPeriod) { return(body.solarDayLength); } else { throw new ArgumentException( Localizer.Format("#autoLOC_CustomAsteroids_ErrorPlanetNoDay", planet), nameof(property)); } case "vesc": return(Math.Sqrt(2 * body.gravParameter / body.Radius)); default: if (body.GetOrbitDriver() == null) { throw new ArgumentException( Localizer.Format("#autoLOC_CustomAsteroids_ErrorPlanetNoOrbit", planet), nameof(planet)); } Orbit orbit = body.GetOrbit(); switch (property.ToLower()) { case "sma": return(orbit.semiMajorAxis); case "per": return(orbit.PeR); case "apo": return(orbit.ApR); case "ecc": return(orbit.eccentricity); case "inc": return(orbit.inclination); case "ape": return(orbit.argumentOfPeriapsis); case "lpe": // Ignore inclination: http://en.wikipedia.org/wiki/Longitude_of_periapsis return(orbit.LAN + orbit.argumentOfPeriapsis); case "lan": return(orbit.LAN); case "mna0": return(meanAnomalyAtUT(orbit, 0.0) * 180.0 / Math.PI); case "mnl0": return(anomalyToLong(meanAnomalyAtUT(orbit, 0.0) * 180.0 / Math.PI, orbit.inclination, orbit.argumentOfPeriapsis, orbit.LAN)); case "porb": return(orbit.period); case "vorb": // Need circumference of an ellipse; closed form does not exist double sum = orbit.semiMajorAxis + orbit.semiMinorAxis; double diff = orbit.semiMajorAxis - orbit.semiMinorAxis; double h = diff * diff / (sum * sum); double correction = 1.0; for (int n = 1; n < 10; n++) { double coeff = Util.DoubleFactorial(2 * n - 1) / (Math.Pow(2, n) * Util.Factorial(n)) / (2 * n - 1); correction += coeff * coeff * Math.Pow(h, n); } return(Math.PI * sum * correction / orbit.period); case "vmin": return(orbit.getOrbitalSpeedAtDistance(orbit.ApR)); case "vmax": return(orbit.getOrbitalSpeedAtDistance(orbit.PeR)); default: throw new ArgumentException( Localizer.Format("#autoLOC_CustomAsteroids_ErrorPlanetBadProperty", property), nameof(property)); } } }
public override Orbit drawOrbit() { // Would like to only calculate this once, but I don't know for sure that this object // will be initialized after FlightGlobals CelestialBody orbitee = AsteroidManager.getPlanetByName(centralBody); Debug.Log($"[CustomAsteroids]: " + Localizer.Format("#autoLOC_CustomAsteroids_LogOrbitDraw", getName())); // Properties with only one reasonable parametrization double e = wrappedDraw(eccentricity, getName(), "eccentricity"); if (e < 0.0) { throw new InvalidOperationException( Localizer.Format("#autoLOC_CustomAsteroids_ErrorOrbitBadEcc", getName(), e)); } // Sign of inclination is redundant with 180-degree shift in longitude of ascending node // So it's ok to just have positive inclinations double i = wrappedDraw(inclination, getName(), "inclination"); double lAn = wrappedDraw(ascNode, getName(), "ascNode"); // Position of periapsis double aPe; double peri = wrappedDraw(periapsis, getName(), "periapsis"); switch (periapsis.getParam()) { case PeriRange.Type.Argument: aPe = peri; break; case PeriRange.Type.Longitude: aPe = peri - lAn; break; default: throw new InvalidOperationException( Localizer.Format("#autoLOC_CustomAsteroids_ErrorOrbitBadPeri", getName(), periapsis.getParam())); } // Semimajor axis double a; double size = wrappedDraw(orbitSize, getName(), "orbitSize"); switch (orbitSize.getParam()) { case SizeRange.Type.SemimajorAxis: a = size; break; case SizeRange.Type.Periapsis: a = size / (1.0 - e); break; case SizeRange.Type.Apoapsis: if (e > 1.0) { throw new InvalidOperationException( Localizer.Format("#autoLOC_CustomAsteroids_ErrorOrbitHyperbolicApo", getName(), e)); } a = size / (1.0 + e); break; default: throw new InvalidOperationException( Localizer.Format("#autoLOC_CustomAsteroids_ErrorOrbitBadSize", getName(), orbitSize.getParam())); } // Mean anomaly at given epoch double mEp, epoch; double phase = wrappedDraw(orbitPhase, getName(), "orbitPhase"); switch (orbitPhase.getParam()) { case PhaseRange.PhaseType.MeanAnomaly: // Mean anomaly is the ONLY orbital angle that needs to be given in radians mEp = Math.PI / 180.0 * phase; break; case PhaseRange.PhaseType.MeanLongitude: mEp = Math.PI / 180.0 * longToAnomaly(phase, i, aPe, lAn); break; default: throw new InvalidOperationException( Localizer.Format("#autoLOC_CustomAsteroids_ErrorOrbitBadPhase", getName(), orbitPhase.getParam())); } switch (orbitPhase.getEpoch()) { case PhaseRange.EpochType.GameStart: epoch = 0.0; break; case PhaseRange.EpochType.Now: epoch = Planetarium.GetUniversalTime(); break; default: throw new InvalidOperationException( Localizer.Format("#autoLOC_CustomAsteroids_ErrorOrbitBadEpoch", getName(), orbitPhase.getEpoch())); } // Fix accidentally hyperbolic orbits if (a * (1.0 - e) < 0.0) { a = -a; } Debug.Log($"[CustomAsteroids]: " + Localizer.Format("#autoLOC_CustomAsteroids_LogOrbit", a, e, i, aPe, lAn, mEp, epoch)); // Does Orbit(...) throw exceptions? Orbit newOrbit = new Orbit(i, e, a, lAn, aPe, mEp, epoch, orbitee); frameTransform(newOrbit); newOrbit.UpdateFromUT(Planetarium.GetUniversalTime()); return(newOrbit); }