using System; using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; namespace Aerostats { public class ModuleBalloon : PartModule { public float MaxBalloonVolume { get; private set; } public float GasDensity { get; private set; } public float BalloonDragCoeff { get; private set; } public float BalloonEmptyMass { get; private set; } public float LiftingGasQuantity { get; private set; } public float CurrentVolume { get; private set; } public float BalloonLift { get; private set; } public bool IsVenting { get; private set; } /// /// Inflation ratio of the balloon (0=empty, 1=maximum) /// private float Inflation = 0; public float Radius { get; private set; } /// /// Must be called right after creating a balloon /// public void Initialize(float emptyMass, Vector3 initialVelocity, float maxVolume, float gasDensity, float dragCoeff) { MaxBalloonVolume = maxVolume; GasDensity = gasDensity; BalloonDragCoeff = dragCoeff; BalloonEmptyMass = emptyMass; gameObject.AddComponent(); rigidbody.mass = BalloonEmptyMass; rigidbody.velocity = initialVelocity; // start with the same velocity or everything explodes when deploying from a moving vessel rigidbody.angularDrag = 10.0f; InjectGas(0.0001f); } /// /// Injects (positive quantity) or removes (negative quantity) gas, and returns the contained quantity after that operation /// public float InjectGas(float quantity) { LiftingGasQuantity += quantity; float currentMaxQuantity = GetCurrentMaxQuantity(); // balloon security valve if (LiftingGasQuantity > currentMaxQuantity) { IsVenting = true; Util.PostSingleScreenMessage("security valve", "Some gas has been vented by the balloon security valve"); LiftingGasQuantity = currentMaxQuantity; } else { IsVenting = false; } Vector3d worldPos = vessel.GetWorldPos3D(); CelestialBody body = vessel.mainBody; float externalTemperature = (float)FlightGlobals.getExternalTemperature(worldPos, body); float balloonInternalTemperature = externalTemperature; float externalPressure = Math.Max((float)FlightGlobals.getStaticPressure(worldPos, body) * 1000.0f, 0.00001f); float currentGasMoles = GasUtilities.StpVolumeToMoles * LiftingGasQuantity; CurrentVolume = currentGasMoles * GasUtilities.R * balloonInternalTemperature / externalPressure; Inflation = CurrentVolume / MaxBalloonVolume; Util.PostSingleScreenMessage("inflation", "Inflation = " + (Inflation * 100.0f).ToString("0.00") + "%"); float airDensity = (float)FlightGlobals.getAtmDensity(externalPressure / 1000.0f, externalTemperature, body); float currentGasDensity = GasDensity * balloonInternalTemperature / GasUtilities.ZeroCelsius / externalPressure * GasUtilities.StandardPressure; BalloonLift = CurrentVolume * airDensity; float balloonGasMass = currentGasDensity * CurrentVolume; Util.PostSingleScreenMessage("lift", "Air density = " + airDensity + "kg/m^3, Lift = " + (BalloonLift - balloonGasMass) + "kg"); // V = 4/3*pi*r^3 Radius = Mathf.Pow(CurrentVolume * 0.75f / Mathf.PI, 0.333f); float scale = Radius * 2.0f + 0.1f; transform.localScale = new Vector3(scale, scale, scale); rigidbody.mass = (BalloonEmptyMass + balloonGasMass) * 0.001f; return LiftingGasQuantity; } /// /// Computes the maximum gas quantity that the balloon can current hold, given the external temperature and pressure conditions /// public float GetCurrentMaxQuantity() { Vector3d worldPos = vessel.GetWorldPos3D(); CelestialBody body = vessel.mainBody; float externalTemperature = (float)FlightGlobals.getExternalTemperature(worldPos, body); float balloonInternalTemperature = externalTemperature; float externalPressure = Math.Max((float)FlightGlobals.getStaticPressure(worldPos, body) * 1000.0f, 0.00001f); Util.PostSingleScreenMessage("external atmo", "Temperature = " + externalTemperature + "K, Pressure=" + externalPressure + "Pa"); float currentMaxQuantity = MaxBalloonVolume / GasUtilities.R / balloonInternalTemperature * externalPressure / GasUtilities.StpVolumeToMoles; return currentMaxQuantity; } private void FixedUpdate() { Vector3d worldPos = vessel.GetWorldPos3D(); CelestialBody body = vessel.mainBody; InjectGas(0); // update security valve venting var gravityAccel = FlightGlobals.getGeeForceAtPosition(worldPos); //Util.PostSingleScreenMessage("gravity", "Gravity accel = (" + gravityAccel.x + ", " + gravityAccel.y + ", " + gravityAccel.z + ")"); float externalTemperature = (float)FlightGlobals.getExternalTemperature(worldPos, body); float balloonInternalTemperature = externalTemperature; float externalPressure = Math.Max((float)FlightGlobals.getStaticPressure(worldPos, body) * 1000.0f, 0.00001f); float airDensity = (float)FlightGlobals.getAtmDensity(externalPressure / 1000.0f, externalTemperature, body); rigidbody.AddForce(-gravityAccel * BalloonLift / 1000.0f, ForceMode.Force); // balloon drag var airVelocity = rigidbody.velocity + Krakensbane.GetFrameVelocity() /*- vessel.mainBody.getRFrmVel(vessel.GetWorldPos3D())*/; float sqVel = (float)airVelocity.magnitude; sqVel *= sqVel; float dragForce = 0.5f * airDensity * sqVel * BalloonDragCoeff * (Mathf.PI * Radius * Radius); Util.PostSingleScreenMessage("balloon drag", "Drag = " + dragForce + "N"); rigidbody.AddForce(-airVelocity.normalized * dragForce / 1000.0f, ForceMode.Force); } } }