/// <summary> /// Generates a random orbit in as similar a manner to stock as possible. /// </summary> /// <returns>The orbit of a randomly selected member of the population.</returns> /// /// <exception cref="InvalidOperationException">Thrown if cannot produce stockalike /// orbits. The program will be in a consistent state in the event of an exception.</exception> public Orbit drawOrbit() { CelestialBody kerbin = FlightGlobals.Bodies.Find(body => body.isHomeWorld); CelestialBody dres = FlightGlobals.Bodies.Find(body => body.name.Equals("Dres")); if (dres != null && reachedBody(dres) && UnityEngine.Random.Range(0, 4) == 0) { // Drestroids double a = RandomDist.drawLogUniform(0.55, 0.65) * dres.sphereOfInfluence; double e = RandomDist.drawRayleigh(0.005); double i = RandomDist.drawRayleigh(0.005); // lAn takes care of negative inclinations double lAn = RandomDist.drawAngle(); double aPe = RandomDist.drawAngle(); double mEp = Math.PI / 180.0 * RandomDist.drawAngle(); double epoch = Planetarium.GetUniversalTime(); Debug.Log("[CustomAsteroids]: " + Localizer.Format("#autoLOC_CustomAsteroids_LogOrbit", a, e, i, aPe, lAn, mEp, epoch)); return(new Orbit(i, e, a, lAn, aPe, mEp, epoch, dres)); } if (kerbin != null) { // Kerbin interceptors double delay = RandomDist.drawUniform(12.5, 55.0); Debug.Log("[CustomAsteroids]: " + Localizer.Format("#autoLOC_CustomAsteroids_LogDefault", delay)); return(Orbit.CreateRandomOrbitFlyBy(kerbin, delay)); } throw new InvalidOperationException( Localizer.Format("#autoLOC_CustomAsteroids_ErrorDefaultNoKerbin")); }
/// <summary> /// Creates a hyperbolic orbit around the given celestial body. /// </summary> /// <returns>The newly created orbit, with state vectors anchored to its periapsis.</returns> /// <param name="body">The celestial body at the focus of the orbit.</param> /// <param name="periapsis">The periapsis (from the body center), in meters. Must not be /// negative.</param> /// <param name="vInf">The excess speed associated with the orbit, in meters per second. /// Must be positive.</param> /// <param name="utPeri">The absolute time of periapsis passage.</param> /// /// <exception cref="ArgumentException">Thrown if either <c>periapsis</c> or <c>vInf</c> /// are out of bounds.</exception> static Orbit createHyperbolicOrbit (CelestialBody body, double periapsis, double vInf, double utPeri) { if (vInf <= 0.0) { throw new ArgumentException ( Localizer.Format ("#autoLOC_CustomAsteroids_ErrorHyperBadVInf", vInf), nameof (vInf)); } if (periapsis < 0.0) { throw new ArgumentException ( Localizer.Format ("#autoLOC_CustomAsteroids_ErrorHyperBadPeri", periapsis), nameof (periapsis)); } double a = -body.gravParameter / (vInf * vInf); double e = 1.0 - periapsis / a; // Random orientation, to be consistent with CreateRandomOrbitFlyBy double i = RandomDist.drawIsotropic (); double lAn = RandomDist.drawAngle (); double aPe = RandomDist.drawAngle (); Debug.Log ($"[CustomAsteroids]: " + Localizer.Format ("#autoLOC_CustomAsteroids_LogHyperOrbit", body.bodyName, a, e, i, aPe, lAn)); return new Orbit (i, e, a, lAn, aPe, 0.0, utPeri, body); }
/// <summary> /// Randomly selects an asteroid class. The selection is weighted by the proportions passed /// to the method. /// </summary> /// /// <param name="typeRatios">The proportions in which to select the types.</param> /// <returns>The selected asteroid class. Shall not be null.</returns> /// /// <exception cref="InvalidOperationException">Thrown if there are no types from /// which to choose, or if all proportions are zero, or if any proportion is /// negative.</exception> internal static string drawAsteroidType <Dummy> (Proportions <Dummy> typeRatios) { try { return(RandomDist.weightedSample(typeRatios.asPairList())); } catch (ArgumentException e) { throw new InvalidOperationException( Localizer.Format("#autoLOC_CustomAsteroids_ErrorNoClass2"), e); } }
public Tuple <double, double> drawTrackingTime() { Tuple <float, float> trackTimes = AsteroidManager.getOptions().getUntrackedTimes(); double lifetime = RandomDist.drawUniform(trackTimes.Item1, trackTimes.Item2) * SECONDS_PER_EARTH_DAY; double maxLifetime = trackTimes.Item2 * SECONDS_PER_EARTH_DAY; return(Tuple.Create(lifetime, maxLifetime)); }
/// <summary> /// Randomly selects an asteroid set. The selection is weighted by the spawn rate of /// each population; a set with a rate of 2.0 is twice as likely to be chosen as one /// with a rate of 1.0. /// </summary> /// <returns>A reference to the selected asteroid set. Shall not be null.</returns> /// /// <exception cref="InvalidOperationException">Thrown if there are no sets from /// which to choose, or if all spawn rates are zero, or if any rate is negative</exception> internal AsteroidSet drawAsteroidSet() { try { var bins = new List <Pair <AsteroidSet, double> > (); foreach (AsteroidSet x in asteroidPops) { bins.Add(new Pair <AsteroidSet, double> (x, x.getSpawnRate())); } return(RandomDist.weightedSample(bins)); } catch (ArgumentException e) { throw new InvalidOperationException( Localizer.Format("#autoLOC_CustomAsteroids_ErrorNoGroup"), e); } }
/// <summary> /// Returns the time until the next asteroid should be detected. Does not throw exceptions. /// </summary> /// <returns>The number of seconds before an asteroid detection, or infinity if asteroids /// should not spawn.</returns> private static double asteroidWaitTime() { double rate = AsteroidManager.spawnRate(); // asteroids per day if (rate > 0.0) { // Waiting time in a Poisson process follows an exponential distribution rate /= SECONDS_PER_EARTH_DAY; // asteroids per second return(RandomDist.drawExponential(1.0 / rate)); } else { return(double.PositiveInfinity); } }
protected override void checkSpawn() { double ut = Planetarium.GetUniversalTime(); try { // Cap at one asteroid per tick; this is unlikely to reduce the rate even at 100,000× if (areAsteroidsTrackable() && RandomDist.drawUniform(0, 1) < asteroidChance(ut - lastCheck)) { Debug.Log("[FixedRateSpawner]: " + Localizer.Format("#autoLOC_CustomAsteroids_LogSpawnInterval", ut)); spawnAsteroid(); } } finally { lastCheck = ut; } }
public UntrackedObjectClass drawAsteroidSize() { if (sizeRatios != null) { string sizeName = RandomDist.weightedSample(sizeRatios.asPairList()); if (Enum.TryParse(sizeName, true, out UntrackedObjectClass size)) { return(size); } else { Util.errorToPlayerLoc("#autoLOC_CustomAsteroids_ErrorNoSize", sizeName, name); Debug.LogError($"[CustomAsteroids]: Invalid asteroid size {sizeName} in group {name}."); return(UntrackedObjectClass.C); } } else { // Stock asteroids appear to be hardcoded to be from size A to E, even though there are more classes now return((UntrackedObjectClass)(int) (stockSizeCurve.Evaluate((float)RandomDist.drawUniform(0, 1)) * 5)); } }
/// <summary> /// Generates a random number consistent with the distribution. The program state shall be /// unchanged in the event of an exception. /// </summary> /// <returns>The desired random variate. The distribution depends on this object's internal /// data.</returns> /// /// <exception cref="ArgumentException">Thrown if the parameters are inappropriate /// for the distribution.</exception> /// <exception cref="InvalidOperationException">Thrown if the distribution is /// invalid.</exception> internal double draw() { switch (dist) { case Distribution.Uniform: return(RandomDist.drawUniform(min, max)); case Distribution.LogUniform: return(RandomDist.drawLogUniform(min, max)); case Distribution.Gaussian: case Distribution.Normal: return(RandomDist.drawNormal(avg, stdDev)); case Distribution.LogNormal: if (avg <= 0.0) { throw new ArgumentException( Localizer.Format("#autoLOC_CustomAsteroids_ErrorLogNormMean", avg)); } if (stdDev <= 0.0) { throw new ArgumentException( Localizer.Format("#autoLOC_CustomAsteroids_ErrorLogNormStd", stdDev)); } double quad = Math.Sqrt(avg * avg + stdDev * stdDev); double mu = Math.Log(avg * avg / quad); double sigma = Math.Sqrt(2 * Math.Log(quad / avg)); return(RandomDist.drawLognormal(mu, sigma)); case Distribution.Rayleigh: return(RandomDist.drawRayleigh(avg * Math.Sqrt(2.0 / Math.PI))); case Distribution.Exponential: return(RandomDist.drawExponential(avg)); case Distribution.Gamma: if (avg <= 0.0) { throw new ArgumentException( Localizer.Format("#autoLOC_CustomAsteroids_ErrorGammaMean", avg)); } if (stdDev <= 0.0) { throw new ArgumentException( Localizer.Format("#autoLOC_CustomAsteroids_ErrorGammaStd", stdDev)); } double k = (avg / stdDev); k = k * k; double theta = stdDev * stdDev / avg; return(RandomDist.drawGamma(k, theta)); case Distribution.Beta: if (avg <= min || avg >= max) { throw new ArgumentException( Localizer.Format("#autoLOC_CustomAsteroids_ErrorBetaMean", min, avg, max)); } if (stdDev <= 0.0) { throw new ArgumentException( Localizer.Format("#autoLOC_CustomAsteroids_ErrorBetaStd", stdDev)); } double scaledMean = (avg - min) / (max - min); double scaledStdDev = stdDev / (max - min); double scaledVar = scaledStdDev * scaledStdDev; double factor = (scaledMean - scaledMean * scaledMean - scaledVar) / scaledVar; double alpha = scaledMean * factor; double beta = (1.0 - scaledMean) * factor; return(min + (max - min) * RandomDist.drawBeta(alpha, beta)); case Distribution.Isotropic: return(RandomDist.drawIsotropic()); default: throw new InvalidOperationException( Localizer.Format("#autoLOC_CustomAsteroids_ErrorBadDistribution", dist)); } }
public UntrackedObjectClass drawAsteroidSize() { // Asteroids appear to be hardcoded to be from size A to E, even though there are more classes now return((UntrackedObjectClass)(int) (stockSizeCurve.Evaluate((float)RandomDist.drawUniform(0.0, 1.0)) * 5)); }