// set boost parameters: // maximum MAP, the two boost pressures to maintain, the two rated altitudes (km), // the cost for the second boost mode, and the switch altitude (in km), or -1f for auto /// <summary> /// Set turbo/supercharger parameters /// </summary> /// <param name="wastegate">maximum MAP</param> /// <param name="boost0">boost mode 1 multiplier</param> /// <param name="boost1">boost mode 2 multiplier (or zero for 1-stage)</param> /// <param name="rated0">rated altitude in km for mode 1</param> /// <param name="rated1">rated altitude in km for mode 2</param> /// <param name="cost1">cost in HP for mode 2</param> /// <param name="switchAlt"></param> /// <param name="turbo"></param> /// <returns></returns> public bool SetBoostParams(double wastegate, double boost0, double boost1, double rated0, double rated1, double cost1, double switchAlt, bool turbo) { bool retVal = false; double pres0, pres1, switchpres; // get pressure at rated alts CelestialBody body = FindHomeworld(); if (body != null) { pres0 = body.GetPressure(rated0) * 1000; pres1 = body.GetPressure(rated1) * 1000; switchpres = body.GetPressure(switchAlt) * 1000; } else { pres0 = GetPressure(rated0); pres1 = GetPressure(rated1); switchpres = GetPressure(switchAlt); } if (boost0 > 0d) { _boostMults[0] = boost0 / pres0; _maxMP = wastegate; retVal = true; } if (boost1 > 0d) { _boostMults[1] = boost1 / pres1; _boostCosts[1] = cost1; } else { _boostMults[1] = 0d; _boostCosts[1] = 0d; } if (switchAlt >= 0d) { _boostSwitch = switchpres; } else { _boostSwitch = 0d; } MonoBehaviour.print("*AJE* Setting boost params. MaxMP = " + wastegate + ", boosts = " + _boostMults[0] + "/" + _boostMults[1] + ", switch " + _boostSwitch + " from " + boost0 + "@" + rated0 + ", " + boost1 + "@" + rated1); _hasSuper = !turbo && boost0 > 1.0d; return(retVal); }
// return proportion of ionizing radiation not blocked by atmosphere public static double GammaTransparency(CelestialBody body, double altitude) { // deal with underwater & fp precision issues altitude = Math.Abs(altitude); // get pressure double static_pressure = body.GetPressure(altitude); if (static_pressure > 0.0) { // get density double density = body.GetDensity(static_pressure, body.GetTemperature(altitude)); // math, you know double Ra = body.Radius + altitude; double Ya = body.atmosphereDepth - altitude; double path = Math.Sqrt(Ra * Ra + 2.0 * Ra * Ya + Ya * Ya) - Ra; double factor = body.GetSolarPowerFactor(density) * Ya / path; // poor man atmosphere composition contribution if (body.atmosphereContainsOxygen || body.ocean) { factor = 1.0 - Math.Pow(1.0 - factor, 0.015); } return(factor); } return(1.0); }
private List <DisplayItem> getAtmData(CelestialBody body) { var items = new List <DisplayItem>(); if (!body.atmosphere) { return(items); } var maxHeight = DisplayItem.Create("Atmopshere Ends: ", body.atmosphereDepth.ToString("N0") + "m"); items.Add(maxHeight); var oxygen = DisplayItem.Create("Oxygen?: ", body.atmosphereContainsOxygen ? "Yes" : "No"); items.Add(oxygen); var kPAASL = body.GetPressure(0d); var atmASL = kPAASL * PhysicsGlobals.KpaToAtmospheres; var aslDisplay = string.Format("{0}kPa ({1}atm)", kPAASL.ToString("F2"), atmASL.ToString("F2")); var aslPressure = DisplayItem.Create("Atm. ASL: ", aslDisplay); items.Add(aslPressure); var surfaceTemp = DisplayItem.Create("Surface Temp: ", body.GetTemperature(0.0).ToString("0.##") + "K"); items.Add(surfaceTemp); return(items); }
protected override Vector3 UpdatePosition() { if (EditorLogic.RootPart == null) { /* DragCubes can get NaNed without this check */ return Vector3.zero; } if (RCSBuildAid.Mode != PluginMode.Parachutes) { return Vector3.zero; } hasParachutes = RCSBuildAid.Parachutes.Count > 0; body = Settings.selected_body; altitude = MenuParachutes.altitude; temperature = body.GetTemperature (altitude); pressure = body.GetPressure (altitude); density = body.GetDensity (pressure, temperature); mach = (float)(speed / body.GetSpeedOfSound(pressure, density)); gravity = body.gravity(altitude); findCenterOfDrag(); speed = Vt = calculateTerminalVelocity (); /* unless I go at mach speeds I don't care about this reynolds = (float)(density * speed); reynoldsDragMult = PhysicsGlobals.DragCurvePseudoReynolds.Evaluate (reynolds); */ dragForce.Vector = calculateDragForce (); return position; }
/* * From Trajectories * Copyright 2014, Youen Toupin * This method is part of Trajectories, under MIT license. * StockAeroUtil by atomicfury */ /// <summary> /// Gets the air density (rho) for the specified altitude on the specified body. /// This is an approximation, because actual calculations, taking sun exposure into account to compute air temperature, require to know the actual point on the body where the density is to be computed (knowing the altitude is not enough). /// However, the difference is small for high altitudes, so it makes very little difference for trajectory prediction. /// From StockAeroUtil.cs from Trajectories /// </summary> /// <param name="body"></param> /// <param name="altitude">Altitude above sea level (in meters)</param> /// <returns></returns> public static double GetDensity(this CelestialBody body, double altitude) { if (!body.atmosphere) { return(0); } if (altitude > body.atmosphereDepth) { return(0); } double pressure = body.GetPressure(altitude); // get an average day/night temperature at the equator double sunDot = 0.5; float sunAxialDot = 0; double atmosphereTemperatureOffset = (double)body.latitudeTemperatureBiasCurve.Evaluate(0) + (double)body.latitudeTemperatureSunMultCurve.Evaluate(0) * sunDot + (double)body.axialTemperatureSunMultCurve.Evaluate(sunAxialDot); double temperature = // body.GetFullTemperature(altitude, atmosphereTemperatureOffset); body.GetTemperature(altitude) + (double)body.atmosphereTemperatureSunMultCurve.Evaluate((float)altitude) * atmosphereTemperatureOffset; return(body.GetDensity(pressure, temperature)); }
protected override Vector3 UpdatePosition() { if (EditorLogic.RootPart == null) { /* DragCubes can get NaNed without this check */ return(Vector3.zero); } if (RCSBuildAid.Mode != PluginMode.Parachutes) { return(Vector3.zero); } hasParachutes = RCSBuildAid.Parachutes.Count > 0; body = Settings.selected_body; altitude = MenuParachutes.altitude; temperature = body.GetTemperature(altitude); pressure = body.GetPressure(altitude); density = body.GetDensity(pressure, temperature); mach = (float)(speed / body.GetSpeedOfSound(pressure, density)); gravity = body.gravity(altitude); findCenterOfDrag(); speed = Vt = calculateTerminalVelocity(); /* unless I go at mach speeds I don't care about this * reynolds = (float)(density * speed); * reynoldsDragMult = PhysicsGlobals.DragCurvePseudoReynolds.Evaluate (reynolds); */ dragForce.Vector = calculateDragForce(); return(position); }
private void UpdateEditor() { UpdatePanels(); if (!_updateSimulator) { return; } _updateSimulator = false; try { SimManager.Gravity = _currentBody.GeeASL * GRAVITY; if (Atmosphere) { SimManager.Atmosphere = _currentBody.GetPressure(_atmosphereDepth) * PhysicsGlobals.KpaToAtmospheres; } else { SimManager.Atmosphere = 0; } SimManager.Mach = _mach; SimManager.RequestSimulation(); SimManager.TryStartSimulation(); } catch (Exception e) { BasicLogger.Exception(e, "BasicDeltaV.Update()"); } }
public void SimulateAeroProperties(out Vector3 aeroForce, out Vector3 aeroTorque, Vector3 velocityWorldVector, double altitude) { // Rodhern: It seems that this method, 'SimulateAeroProperties', is only used in FARAPI, which in turn can be used by say KSPTrajectories. // The parameter 'FARCenterQuery dummy' is from a code fix by Benjamin Chung (commit 18fbb9d29431679a4de9dfc22a443f400d2d4f8b). FARCenterQuery center = new FARCenterQuery(); FARCenterQuery dummy = new FARCenterQuery(); float pressure; float density; float temperature; float speedOfSound; CelestialBody body = vessel.mainBody; //Calculate main gas properties pressure = (float)body.GetPressure(altitude); temperature = (float)body.GetTemperature(altitude); density = (float)body.GetDensity(pressure, temperature); speedOfSound = (float)body.GetSpeedOfSound(pressure, density); if (pressure <= 0 || temperature <= 0 || density <= 0 || speedOfSound <= 0) { aeroForce = Vector3.zero; aeroTorque = Vector3.zero; return; } float velocityMag = velocityWorldVector.magnitude; float machNumber = velocityMag / speedOfSound; float reynoldsNumber = (float)FARAeroUtil.CalculateReynoldsNumber(density, Length, velocityMag, machNumber, temperature, body.atmosphereAdiabaticIndex); float reynoldsPerLength = reynoldsNumber / (float)Length; float pseudoKnudsenNumber = machNumber / (reynoldsNumber + machNumber); float skinFriction = (float)FARAeroUtil.SkinFrictionDrag(reynoldsNumber, machNumber); FlightEnv fenv = FlightEnv.NewPredicted(vessel.mainBody, altitude, machNumber); if (_currentAeroSections != null) { for (int i = 0; i < _currentAeroSections.Count; i++) { FARAeroSection curSection = _currentAeroSections[i]; if (curSection != null) { curSection.PredictionCalculateAeroForces(density, machNumber, reynoldsPerLength, pseudoKnudsenNumber, skinFriction, velocityWorldVector, center); } } for (int i = 0; i < _legacyWingModels.Count; i++) { FARWingAerodynamicModel curWing = _legacyWingModels[i]; if ((object)curWing != null) { Vector3d force = curWing.PrecomputeCenterOfLift(velocityWorldVector, fenv, dummy); center.AddForce(curWing.transform.position, force); } } } aeroForce = center.force; aeroTorque = center.TorqueAt(vessel.CoM); }
// return proportion of ionizing radiation not blocked by atmosphere public static double GammaTransparency(CelestialBody body, double altitude) { // deal with underwater & fp precision issues altitude = Math.Abs(altitude); // get pressure var staticPressure = body.GetPressure(altitude); if (staticPressure > 0.0) { // get density var density = body.GetDensity(staticPressure, body.GetTemperature(altitude)); // math, you know var radius = body.Radius + altitude; var depth = body.atmosphereDepth - altitude; var path = Math.Sqrt(radius * radius + 2.0 * radius * depth + depth * depth) - radius; var factor = body.GetSolarPowerFactor(density) * depth / path; // poor man atmosphere composition contribution if (body.atmosphereContainsOxygen || body.ocean) { factor = 1.0 - Math.Pow(1.0 - factor, 0.015); } return(factor); } return(1.0); }
public static float ASLPressure(this CelestialBody body) { /* body.atmodspherePressureSeaLevel seems to fail sometimes for some reason, * for Laythe is 0.6 atm but sometimes it would be 0.8 atm. f**k me */ // return (float)body.atmospherePressureSeaLevel; return((float)body.GetPressure(0)); }
// -------------------------------------------------------------------------- // ATMOSPHERE // -------------------------------------------------------------------------- // return proportion of flux not blocked by atmosphere // - position: sampling point // - sun_dir: normalized vector from sampling point to the sun public static double AtmosphereFactor(CelestialBody body, Vector3d position, Vector3d sun_dir) { // get up vector & altitude Vector3d up = (position - body.position); double altitude = up.magnitude; up /= altitude; altitude -= body.Radius; altitude = Math.Abs(altitude); //< deal with underwater & fp precision issues double static_pressure = body.GetPressure(altitude); if (static_pressure > 0.0) { double density = body.GetDensity(static_pressure, body.GetTemperature(altitude)); // nonrefracting radially symmetrical atmosphere model [Schoenberg 1929] double Ra = body.Radius + altitude; double Ya = body.atmosphereDepth - altitude; double q = Ra * Math.Max(0.0, Vector3d.Dot(up, sun_dir)); double path = Math.Sqrt(q * q + 2.0 * Ra * Ya + Ya * Ya) - q; return(body.GetSolarPowerFactor(density) * Ya / path); } return(1.0); }
public void Update() { // in editor and flight, update ui button label Events["Toggle"].guiName = Lib.StatusToggle(title, running ? "running" : "stopped"); // if in flight, and the stock planet resource system is online if (Lib.IsFlight() && ResourceMap.Instance != null) { // shortcut CelestialBody body = vessel.mainBody; // determine if overall situation is valid for extraction bool valid = false; if (deployed) { switch (type) { case 0: valid = vessel.Landed && GroundContact(); break; case 1: valid = body.ocean && (vessel.Splashed || vessel.altitude < 0.0); break; case 2: valid = body.atmosphere && vessel.altitude < body.atmosphereDepth; break; case 3: valid = vessel.altitude > body.atmosphereDepth || !body.atmosphere; break; } } // if situation is valid double abundance = 0.0; if (valid) { // get abundance AbundanceRequest request = new AbundanceRequest { ResourceType = (HarvestTypes)type, ResourceName = resource, BodyId = body.flightGlobalsIndex, Latitude = vessel.latitude, Longitude = vessel.longitude, Altitude = vessel.altitude, CheckForLock = false }; abundance = ResourceMap.Instance.GetAbundance(request); } // determine if resources can be harvested // - invalid conditions // - abundance below threshold // - pressure below threshold starved = !valid || abundance <= min_abundance || (type == 2 && body.GetPressure(vessel.altitude) <= min_pressure); // update ui Events["Toggle"].guiActive = valid; Fields["Abundance"].guiActive = valid; Fields["Abundance"].guiName = Lib.BuildString(resource, " abundance"); Abundance = abundance > double.Epsilon ? Lib.HumanReadablePerc(abundance, "F2") : "none"; } }
public static List <CrustalResource> GenerateCompositionFromCelestialBody(CelestialBody celestialBody) // generates Crustal composition based on Crustal characteristics { var bodyCrustalComposition = new List <CrustalResource>(); // instantiate a new list that this function will be returning try { // return empty if there's no crust if (!celestialBody.hasSolidSurface) { return(bodyCrustalComposition); } // Lookup homeworld CelestialBody homeworld = FlightGlobals.Bodies.SingleOrDefault(b => b.isHomeWorld); if (homeworld == null) { return(new List <CrustalResource>()); } double pressureAtSurface = celestialBody.GetPressure(0); if (celestialBody.Mass < homeworld.Mass * 10 && pressureAtSurface < 1000) { if (pressureAtSurface > 200) { // it is Venus-like/Eve-like, use Eve as a template bodyCrustalComposition = GetCrustalCompositionForBody("Eve"); } else if (celestialBody.Mass > (homeworld.Mass / 2) && celestialBody.Mass < homeworld.Mass && pressureAtSurface < 100) // it's at least half as big as the homeworld and has significant atmosphere { // it is Laythe-like, use Laythe as a template bodyCrustalComposition = GetCrustalCompositionForBody("Laythe"); } else if (celestialBody.atmosphereContainsOxygen) { // it is Earth-like, use Earth as a template bool hasEarth = FlightGlobals.Bodies.Any(b => b.name == "Earth"); // is there a planet called Earth present in KSP? bodyCrustalComposition = GetCrustalCompositionForBody(hasEarth ? "Earth" : "Kerbin"); } else { // it is a Mars-like, use Mars as template bool hasMars = FlightGlobals.Bodies.Any(b => b.name == "Mars"); // is there a planet called Mars present in KSP? bodyCrustalComposition = GetCrustalCompositionForBody(hasMars ? "Mars" : "Duna"); } } } catch (Exception ex) { Debug.LogError("[KSPI]: Exception while generating Crustal resource composition from celestial properties : " + ex.ToString()); } return(bodyCrustalComposition); }
public void SimulateAeroProperties(out Vector3 aeroForce, out Vector3 aeroTorque, Vector3 velocityWorldVector, double altitude) { FARCenterQuery center = new FARCenterQuery(); float pressure; float density; float temperature; float speedOfSound; CelestialBody body = vessel.mainBody; //Calculate main gas properties pressure = (float)body.GetPressure(altitude); temperature = (float)body.GetTemperature(altitude); density = (float)body.GetDensity(pressure, temperature); speedOfSound = (float)body.GetSpeedOfSound(pressure, density); if (pressure <= 0 || temperature <= 0 || density <= 0 || speedOfSound <= 0) { aeroForce = Vector3.zero; aeroTorque = Vector3.zero; return; } float velocityMag = velocityWorldVector.magnitude; float machNumber = velocityMag / speedOfSound; float reynoldsNumber = (float)FARAeroUtil.CalculateReynoldsNumber(density, Length, velocityMag, machNumber, temperature, body.atmosphereAdiabaticIndex); float reynoldsPerLength = reynoldsNumber / (float)Length; float skinFriction = (float)FARAeroUtil.SkinFrictionDrag(reynoldsNumber, machNumber); float pseudoKnudsenNumber = machNumber / (reynoldsNumber + machNumber); if (_currentAeroSections != null) { for (int i = 0; i < _currentAeroSections.Count; i++) { FARAeroSection curSection = _currentAeroSections[i]; if (curSection != null) { curSection.PredictionCalculateAeroForces(density, machNumber, reynoldsPerLength, pseudoKnudsenNumber, skinFriction, velocityWorldVector, center); } } for (int i = 0; i < _legacyWingModels.Count; i++) { FARWingAerodynamicModel curWing = _legacyWingModels[i]; if ((object)curWing != null) { curWing.PrecomputeCenterOfLift(velocityWorldVector, machNumber, density, center); } } } aeroForce = center.force; aeroTorque = center.TorqueAt(vessel.CoM); }
// return the reason why resource can't be harvested, or an empty string otherwise string DetectIssue(double abundance) { // shortcut CelestialBody body = vessel.mainBody; // check situation switch (type) { case 0: bool land_valid = vessel.Landed && GroundContact(); if (!land_valid) { return("no ground contact"); } break; case 1: bool ocean_valid = body.ocean && (vessel.Splashed || vessel.altitude < 0.0); if (!ocean_valid) { return("not in ocean"); } break; case 2: bool atmo_valid = body.atmosphere && vessel.altitude < body.atmosphereDepth; if (!atmo_valid) { return("not in atmosphere"); } break; case 3: bool space_valid = vessel.altitude > body.atmosphereDepth || !body.atmosphere; if (!space_valid) { return("not in space"); } break; } // check against pressure if (type == 2 && body.GetPressure(vessel.altitude) < min_pressure) { return("pressure below threshold"); } // check against abundance if (abundance < min_abundance) { return("abundance below threshold"); } // all good return(string.Empty); }
public StabilityDerivExportOutput CalculateStabilityDerivs(CelestialBody body, double alt, double machNumber, int flapSetting, bool spoilers) { if (body.GetPressure(alt) > 0) { return(CalculateStabilityDerivs(body, alt, machNumber, flapSetting, spoilers, 0, 0)); } else { return(null); } }
// return proportion of flux not blocked by atmosphere // note: this one assume the receiver is on the ground // - cos_a: cosine of angle between zenith and sun, in [0..1] range // to get an average for stats purpose, use 0.7071 public static double AtmosphereFactor(CelestialBody body, double cos_a) { double static_pressure = body.GetPressure(0.0); if (static_pressure > 0.0) { double density = body.GetDensity(static_pressure, body.GetTemperature(0.0)); body.GetSolarAtmosphericEffects(cos_a, density, out _, out double stockFluxFactor); return(stockFluxFactor); } return(1.0); }
// determine average atmospheric absorption factor over the daylight period (not the whole day) // - by doing an average of values at midday, sunrise and an intermediate value // - using the current sun direction at the given position to approximate // the influence of high latitudes and of the inclinaison of the body orbit public static double AtmosphereFactorAnalytic(CelestialBody body, Vector3d position, Vector3d sun_dir) { // only for atmospheric bodies whose rotation period is less than 120 hours if (body.rotationPeriod > 432000.0) { return(AtmosphereFactor(body, position, sun_dir)); } // get up vector & altitude Vector3d radialOut = position - body.position; double altitude = radialOut.magnitude; radialOut /= altitude; // normalize altitude -= body.Radius; altitude = Math.Abs(altitude); //< deal with underwater & fp precision issues double static_pressure = body.GetPressure(altitude); if (static_pressure > 0.0) { Vector3d[] sunDirs = new Vector3d[3]; // east - sunrise sunDirs[0] = body.getRFrmVel(position).normalized; // perpendicular vector Vector3d sunUp = Vector3d.Cross(sunDirs[0], sun_dir).normalized; // midday vector (along the radial plane + an angle depending on the original vesselSundir) sunDirs[1] = Vector3d.Cross(sunUp, sunDirs[0]).normalized; // invert midday vector if it's pointing toward the ground (checking against radial-out vector) if (Vector3d.Dot(sunDirs[1], radialOut) < 0.0) { sunDirs[1] *= -1.0; } // get an intermediate vector between sunrise and midday sunDirs[2] = (sunDirs[0] + sunDirs[1]).normalized; double density = body.GetDensity(static_pressure, body.GetTemperature(altitude)); // nonrefracting radially symmetrical atmosphere model [Schoenberg 1929] double Ra = body.Radius + altitude; double Ya = body.atmosphereDepth - altitude; double atmo_factor_analytic = 0.0; for (int i = 0; i < 3; i++) { double q = Ra * Math.Max(0.0, Vector3d.Dot(radialOut, sunDirs[i])); double path = Math.Sqrt(q * q + 2.0 * Ra * Ya + Ya * Ya) - q; atmo_factor_analytic += body.GetSolarPowerFactor(density) * Ya / path; } atmo_factor_analytic /= 3.0; return(atmo_factor_analytic); } return(1.0); }
/// <summary> /// Gets the air pressure (in Pascals) for the specified altitude (above sea level, in meters) on the specified body. /// </summary> public static double GetPressure(double altitude, CelestialBody body) { if (!body.atmosphere) { return(0); } if (altitude > body.atmosphereDepth) { return(0); } return(body.GetPressure(altitude) * 1000d); }
public void CalculateConstants(CelestialBody body, float speed, double altitude) { double atmosphereTemperatureOffset = 0; atmoTemp = body.GetFullTemperature(altitude, atmosphereTemperatureOffset); density = body.GetDensity(staticPressurekPa, atmoTemp); mach = CalculateMachNumber(body, speed, staticPressurekPa, density); staticPressurekPa = body.GetPressure(altitude); convectiveMachScale = Math.Pow(UtilMath.Clamp01( (mach - PhysicsGlobals.NewtonianMachTempLerpStartMach) / (PhysicsGlobals.NewtonianMachTempLerpEndMach - PhysicsGlobals.NewtonianMachTempLerpStartMach)), PhysicsGlobals.NewtonianMachTempLerpExponent); }
protected override bool updateMessage(ref atmoConditionStruct message) { message.atmoCharacteristics = 0; Vessel vessel = FlightGlobals.ActiveVessel; if (vessel == null) { return(false); } CelestialBody body = FlightGlobals.ActiveVessel.mainBody; if (body == null) { return(false); } if (body.atmosphere) { message.atmoCharacteristics |= AtmoConditionsBits.hasAtmosphere; if (body.atmosphereContainsOxygen) { message.atmoCharacteristics |= AtmoConditionsBits.hasOxygen; } if (body.atmosphereDepth >= vessel.altitude) { message.atmoCharacteristics |= AtmoConditionsBits.isVesselInAtmosphere; } message.temperature = (float)body.GetTemperature(vessel.altitude); message.pressure = (float)body.GetPressure(vessel.altitude); message.airDensity = (float)body.GetDensity(body.GetPressure(vessel.altitude), body.GetTemperature(vessel.altitude)); } FlightGlobals.ActiveVessel.mainBody.GetFullTemperature(FlightGlobals.ActiveVessel.CoMD); return(false); }
public AtmosphereParams(CelestialBody body, double altitude) { Alt = altitude; Body = body; if (!Body.atmosphere) { return; } P = Body.GetPressure(Alt); T = Body.GetTemperature(Alt); Rho = Body.GetDensity(P, T); Mach1 = Body.GetSpeedOfSound(P, Rho); }
// return true if a vessel is inside a breathable atmosphere public static bool Breathable(Vessel v, bool underwater) { // a vessel is inside a breathable atmosphere if: // - it is inside an atmosphere // - the atmospheric pressure is above 25kPA // - the body atmosphere is flagged as containing oxygen // - it isn't underwater CelestialBody body = v.mainBody; return(body.atmosphereContainsOxygen && body.GetPressure(v.altitude) > 25.0 && !underwater); }
public static double GetDensity(Vector3d position, CelestialBody body) { if (!body.atmosphere) return 0; double altitude = (position - body.position).magnitude - body.Radius; if (altitude > body.atmosphereDepth) return 0; double pressure = body.GetPressure(altitude); double temperature = GetTemperature(position, body); return body.GetDensity(pressure, temperature); }
// return true if inside a breathable atmosphere public static bool Breathable(Vessel v, bool underwater) { // atmosphere is breathable if: // - vessel body is the home body // - the body has an atmosphere, and it contain oxygen // - the pressure is above 25kPA // - the vessel is not underwater CelestialBody body = v.mainBody; return(body == FlightGlobals.GetHomeBody() && body.atmosphereContainsOxygen && body.GetPressure(v.altitude) > 25.0 && !underwater); }
private static void SupplementWithHelium3(IDictionary <string, AtmosphericResource> bodyAtmosphericComposition, CelestialBody body) { // if helium3 is undefined, but liquid helium is, derive it if (!bodyAtmosphericComposition.ContainsKey(KITResourceSettings.Helium3Lqd) && bodyAtmosphericComposition.TryGetValue(KITResourceSettings.Helium4Lqd, out var heliumLqd)) { var helium3Abundance = body.GetPressure(0) > 1000 ? heliumLqd.ResourceAbundance * 0.001 : heliumLqd.ResourceAbundance * 1.38e-6; Debug.Log("[KSPI]: Added helium-3 to atmosphere either abundance " + helium3Abundance); bodyAtmosphericComposition.Add(KITResourceSettings.Helium3Lqd, new AtmosphericResource(KITResourceSettings.Helium3Lqd, helium3Abundance, "Helium-3")); } // if helium3 is undefined, but helium gas is, derive it else if (!bodyAtmosphericComposition.ContainsKey(KITResourceSettings.Helium3Lqd) && bodyAtmosphericComposition.TryGetValue(KITResourceSettings.Helium4Gas, out var heliumGas)) { var helium3Abundance = body.GetPressure(0) > 1000 ? heliumGas.ResourceAbundance * 0.001 : heliumGas.ResourceAbundance * 1.38e-6; Debug.Log("[KSPI]: Added helium-3 to atmosphere either abundance " + helium3Abundance); bodyAtmosphericComposition.Add(KITResourceSettings.Helium3Lqd, new AtmosphericResource(KITResourceSettings.Helium3Lqd, helium3Abundance, "Helium-3")); } else if (bodyAtmosphericComposition.ContainsKey(KITResourceSettings.Helium3Lqd)) { Debug.Log("[KSPI]: Helium-3 is already present in atmosphere specification at " + bodyAtmosphericComposition[KITResourceSettings.Helium3Lqd].ResourceAbundance); } else { Debug.Log("[KSPI]: No Helium is present in atmosphere specification, helium-4 will not be added"); } }
// vessel.staticPressurekPa * PhysicsGlobals.KpaToAtmospheres protected void StartSimulation() { simBody = HighLogic.LoadedSceneIsEditor ? editorBody ?? Planetarium.fetch.Home : vessel.mainBody; SimManager.Gravity = 9.81 * simBody.GeeASL; SimManager.Atmosphere = simBody.atmosphere ? simBody.GetPressure(0) : 0; // TODO : use the current atmo ? SimManager.Mach = HighLogic.LoadedSceneIsEditor ? 1 : vessel.mach; SimManager.vectoredThrust = dVLinearThrust; Profiler.BeginSample("MechJebModuleStageStats.StartSimulation()"); SimManager.RequestSimulation(); SimManager.TryStartSimulation(); Profiler.EndSample(); }
// determine average atmospheric absorption factor over the daylight period (not the whole day) // - by doing an average of values at midday, sunrise and an intermediate value // - using the current sun direction at the given position to approximate // the influence of high latitudes and of the inclinaison of the body orbit public static double AtmosphereFactorAnalytic(CelestialBody body, Vector3d position, Vector3d sun_dir) { // only for atmospheric bodies whose rotation period is less than 120 hours if (body.rotationPeriod > 432000.0) { return(AtmosphereFactor(body, position, sun_dir)); } // get up vector & altitude Vector3d radialOut = position - body.position; double altitude = radialOut.magnitude; radialOut /= altitude; // normalize altitude -= body.Radius; altitude = Math.Abs(altitude); //< deal with underwater & fp precision issues double static_pressure = body.GetPressure(altitude); if (static_pressure > 0.0) { Vector3d[] sunDirs = new Vector3d[3]; // east - sunrise sunDirs[0] = body.getRFrmVel(position).normalized; // perpendicular vector Vector3d sunUp = Vector3d.Cross(sunDirs[0], sun_dir).normalized; // midday vector (along the radial plane + an angle depending on the original vesselSundir) sunDirs[1] = Vector3d.Cross(sunUp, sunDirs[0]).normalized; // invert midday vector if it's pointing toward the ground (checking against radial-out vector) if (Vector3d.Dot(sunDirs[1], radialOut) < 0.0) { sunDirs[1] *= -1.0; } // get an intermediate vector between sunrise and midday sunDirs[2] = (sunDirs[0] + sunDirs[1]).normalized; double density = body.GetDensity(static_pressure, body.GetTemperature(altitude)); double atmo_factor_analytic = 0.0; for (int i = 0; i < 3; i++) { body.GetSolarAtmosphericEffects(Vector3d.Dot(radialOut, sunDirs[i]), density, out _, out double stockFluxFactor); atmo_factor_analytic += stockFluxFactor; } atmo_factor_analytic /= 3.0; return(atmo_factor_analytic); } return(1.0); }
//Function to estimate the final velocity given a stage's mass and parachute info public static double VelocityEstimate(double mass, double chuteAreaTimesCd) { if (chuteAreaTimesCd <= 0) { return(200); } if (mass <= 0) { return(0); } CelestialBody home = Planetarium.fetch.Home; return(Math.Sqrt((2000 * mass * 9.81) / (home.GetDensity(home.GetPressure(0), home.GetTemperature(0)) * chuteAreaTimesCd))); //This is according to the formulas used by Stupid_Chris in the Real Chute drag calculator program included with Real Chute. Source: https://github.com/StupidChris/RealChute/blob/master/Drag%20Calculator/RealChute%20drag%20calculator/RCDragCalc.cs }
private void GenerateHighlightingData(ShipConstruct ship, CelestialBody body, float altitude, float speed, float aoa) { float mach, atmDensity; lock (body) { atmDensity = (float)Extensions.KSPClassExtensions.GetDensity(body, altitude); mach = speed / (float)body.GetSpeedOfSound(body.GetPressure(altitude), atmDensity); } int count = ship.parts.Count; highlightingData = new PartAeroData[count]; Vector3 inflow = AeroPredictor.InflowVect(aoa); float pseudoReDragMult; lock (PhysicsGlobals.DragCurvePseudoReynolds) pseudoReDragMult = PhysicsGlobals.DragCurvePseudoReynolds.Evaluate(atmDensity * speed); for (int i = 0; i < count; i++) { if (WindTunnelSettings.HighlightIgnoresLiftingSurfaces && ship.parts[i].HasModuleImplementing <ModuleLiftingSurface>()) { highlightingData[i] = new PartAeroData(0, 0, ship.parts[i].mass); continue; } VesselCache.SimulatedPart simPart = VesselCache.SimulatedPart.Borrow(ship.parts[i], null); Vector3 partForce = simPart.GetAero(inflow, mach, pseudoReDragMult); ModuleLiftingSurface liftingSurface = ship.parts[i].FindModuleImplementing <ModuleLiftingSurface>(); if (liftingSurface != null) { VesselCache.SimulatedLiftingSurface simLiftSurf = VesselCache.SimulatedLiftingSurface.Borrow(liftingSurface, simPart); partForce += simLiftSurf.GetForce(inflow, mach); simLiftSurf.Release(); } simPart.Release(); //Vector3 partForce = highlightingVessel.parts[i].GetAero(inflow, mach, pseudoReDragMult); //Vector3 partForce = StockAeroUtil.SimAeroForce(body, new ShipConstruct("test", "", new List<Part>() { EditorLogic.fetch.ship.parts[i] }), inflow * speed, altitude); partForce = AeroPredictor.ToFlightFrame(partForce, aoa); // (Quaternion.AngleAxis((aoa * 180 / Mathf.PI), Vector3.left) * partForce); highlightingData[i] = new PartAeroData(Math.Abs(partForce.z), Math.Abs(partForce.y), ship.parts[i].mass); } }
// return proportion of flux not blocked by atmosphere // note: this one assume the receiver is on the ground // - cos_a: cosine of angle between zenith and sun, in [0..1] range // to get an average for stats purpose, use 0.7071 public static double AtmosphereFactor(CelestialBody body, double cos_a) { double static_pressure = body.GetPressure(0.0); if (static_pressure > 0.0) { double density = body.GetDensity(static_pressure, body.GetTemperature(0.0)); // nonrefracting radially symmetrical atmosphere model [Schoenberg 1929] double Ra = body.Radius; double Ya = body.atmosphereDepth; double q = Ra * cos_a; double path = Math.Sqrt(q * q + 2.0 * Ra * Ya + Ya * Ya) - q; return(body.GetSolarPowerFactor(density) * Ya / path); } return(1.0); }
/// <summary> /// Initialize standard conditions /// Standard pressure is 101325 Pa, temperature is 288.15 K /// </summary> /// <param name="usePlanetarium">If true (and planeterium available), will get conditions from sea level at home body</param> public static EngineThermodynamics StandardConditions(bool usePlanetarium = false) { double P = 101325d; double T = 288.15d; if (usePlanetarium && Planetarium.fetch != null) { CelestialBody home = Planetarium.fetch.Home; if (home != null) { P = home.GetPressure(0d) * 1000d; T = home.GetTemperature(0d); } } return(new EngineThermodynamics(P: P, T: T)); }
/// <summary> /// Gets the air density (rho) for the specified altitude on the specified body. /// This is an approximation, because actual calculations, taking sun exposure into account to compute air temperature, require to know the actual point on the body where the density is to be computed (knowing the altitude is not enough). /// However, the difference is small for high altitudes, so it makes very little difference for trajectory prediction. /// </summary> /// <param name="altitude">Altitude above sea level (in meters)</param> /// <param name="body"></param> /// <returns></returns> public static double GetDensity(double altitude, CelestialBody body) { if (!body.atmosphere) return 0; if (altitude > body.atmosphereDepth) return 0; double pressure = body.GetPressure(altitude); // get an average day/night temperature at the equator double sunDot = 0.5; float sunAxialDot = 0; double atmosphereTemperatureOffset = (double)body.latitudeTemperatureBiasCurve.Evaluate(0) + (double)body.latitudeTemperatureSunMultCurve.Evaluate(0) * sunDot + (double)body.axialTemperatureSunMultCurve.Evaluate(sunAxialDot); double temperature = body.GetTemperature(altitude) + (double)body.atmosphereTemperatureSunMultCurve.Evaluate((float)altitude) * atmosphereTemperatureOffset; return body.GetDensity(pressure, temperature); }
//redraw the picture of the planned flight path public static void UpdateAtmoTexture(Texture2D texture, CelestialBody mainBody, double maxAltitude, bool realAtmo = false) { double scale = maxAltitude / texture.height; //meters per pixel double maxAtmosphereAltitude = mainBody.RealMaxAtmosphereAltitude(); double pressureSeaLevel = mainBody.atmospherePressureSeaLevel; for (int y = 0; y < texture.height; y++) { double alt = scale * y; if (realAtmo) { alt = mainBody.GetPressure(alt) / pressureSeaLevel; } else { alt = 1.0 - alt / maxAtmosphereAltitude; } float v = (float)(mainBody.atmosphere ? alt : 0.0F); Color c = new Color(0.0F, 0.0F, v); for (int x = 0; x < texture.width; x++) { texture.SetPixel(x, y, c); if (mainBody.atmosphere && (int)(maxAtmosphereAltitude / scale) == y) texture.SetPixel(x, y, XKCDColors.LightGreyBlue); } } texture.Apply(); }
protected void StartSimulation() { simBody = HighLogic.LoadedSceneIsEditor ? editorBody ?? Planetarium.fetch.Home : vessel.mainBody; SimManager.Gravity = 9.81 * simBody.GeeASL; SimManager.Atmosphere = (HighLogic.LoadedSceneIsEditor ? (simBody.atmosphere ? simBody.GetPressure(0) : 0) : vessel.staticPressurekPa) * PhysicsGlobals.KpaToAtmospheres; SimManager.Mach = HighLogic.LoadedSceneIsEditor ? 1 : vessel.mach; SimManager.vectoredThrust = dVLinearThrust; SimManager.Body = simBody; Profiler.BeginSample("MechJebModuleStageStats.StartSimulation()"); SimManager.RequestSimulation(); SimManager.TryStartSimulation(); Profiler.EndSample(); }
//******************************************************* public static Vector3 SimAeroForce(List<Part> parts, CelestialBody body, Vector3 v_wrld_vel, double altitude, double latitude = 0.0) { double pressure = body.GetPressure(altitude); // Lift and drag for force accumulation. Vector3d total_lift = Vector3d.zero; Vector3d total_drag = Vector3d.zero; // dynamic pressure for standard drag equation double rho = GetDensity(altitude, body); double dyn_pressure = 0.0005 * rho * v_wrld_vel.sqrMagnitude; if (rho <= 0) { return Vector3.zero; } double soundSpeed = body.GetSpeedOfSound(pressure, rho); double mach = v_wrld_vel.magnitude / soundSpeed; if (mach > 25.0) { mach = 25.0; } // Loop through all parts, accumulating drag and lift. foreach(Part p in parts) { // need checks on shielded components if (p.ShieldedFromAirstream || p.Rigidbody == null) { continue; } // Get Drag Vector3 sim_dragVectorDir = v_wrld_vel.normalized; Vector3 sim_dragVectorDirLocal = -(p.transform.InverseTransformDirection(sim_dragVectorDir)); Vector3 liftForce = new Vector3(0, 0, 0); switch(p.dragModel) { case Part.DragModel.DEFAULT: case Part.DragModel.CUBE: SmartDragCubeList cubes = new SmartDragCubeList(p, parts); DragCubeList.CubeData p_drag_data; float drag; if (cubes.None) // since 1.0.5, some parts don't have drag cubes (for example fuel lines and struts) { drag = p.maximum_drag; } else { p_drag_data = cubes.AddSurfaceDragDirection(-sim_dragVectorDirLocal, (float)mach); drag = p_drag_data.areaDrag * PhysicsGlobals.DragCubeMultiplier; liftForce = p_drag_data.liftForce; } double sim_dragScalar = dyn_pressure * (double)drag * PhysicsGlobals.DragMultiplier; total_drag += -(Vector3d)sim_dragVectorDir * sim_dragScalar; break; case Part.DragModel.SPHERICAL: total_drag += -(Vector3d)sim_dragVectorDir * (double)p.maximum_drag; break; case Part.DragModel.CYLINDRICAL: total_drag += -(Vector3d)sim_dragVectorDir * (double)Mathf.Lerp(p.minimum_drag, p.maximum_drag, Mathf.Abs(Vector3.Dot(p.partTransform.TransformDirection(p.dragReferenceVector), sim_dragVectorDir))); break; case Part.DragModel.CONIC: total_drag += -(Vector3d)sim_dragVectorDir * (double)Mathf.Lerp(p.minimum_drag, p.maximum_drag, Vector3.Angle(p.partTransform.TransformDirection(p.dragReferenceVector), sim_dragVectorDir) / 180f); break; default: // no drag to apply break; } // If it isn't a wing or lifter, get body lift. if (!p.hasLiftModule) { float simbodyLiftScalar = p.bodyLiftMultiplier * PhysicsGlobals.BodyLiftMultiplier * (float)dyn_pressure; simbodyLiftScalar *= PhysicsGlobals.GetLiftingSurfaceCurve("BodyLift").liftMachCurve.Evaluate((float)mach); Vector3 bodyLift = p.transform.rotation * (simbodyLiftScalar * liftForce); bodyLift = Vector3.ProjectOnPlane(bodyLift, sim_dragVectorDir); // Only accumulate forces for non-LiftModules total_lift += bodyLift; } // Find ModuleLifingSurface for wings and liftforce. // Should catch control surface as it is a subclass foreach (var m in p.Modules.OfType<ModuleLiftingSurface>()) { float mcs_mod = 1.0f; double liftQ = dyn_pressure * 1000; ModuleLiftingSurface wing = (ModuleLiftingSurface)m; Vector3 nVel = Vector3.zero; Vector3 liftVector = Vector3.zero; float liftdot; float absdot; wing.SetupCoefficients(v_wrld_vel, out nVel, out liftVector, out liftdot, out absdot); float simLiftScalar = Mathf.Sign(liftdot) * wing.liftCurve.Evaluate(absdot) * wing.liftMachCurve.Evaluate((float)mach); simLiftScalar *= wing.deflectionLiftCoeff; simLiftScalar = (float)(liftQ * (double)(PhysicsGlobals.LiftMultiplier * simLiftScalar)); float simdragScalar = wing.dragCurve.Evaluate(absdot) * wing.dragMachCurve.Evaluate((float)mach); simdragScalar *= wing.deflectionLiftCoeff; simdragScalar = (float)(liftQ * (double)(simdragScalar * PhysicsGlobals.LiftDragMultiplier)); Vector3 local_lift = mcs_mod * wing.GetLiftVector(liftVector, liftdot, absdot, liftQ, (float)mach); Vector3 local_drag = mcs_mod * wing.GetDragVector(nVel, absdot, liftQ); total_lift += local_lift; total_drag += local_drag; } } // RETURN STUFF Vector3 force = total_lift + total_drag; return 1000 * force; }