Youen Toupin
9 years ago
5 changed files with 187 additions and 80 deletions
@ -0,0 +1,18 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using System.Text; |
||||
|
||||
namespace Aerostats |
||||
{ |
||||
public static class GasUtilities |
||||
{ |
||||
// amount of gas Q is specified in m^3 at stp (standard pressure of 100kPa and temperature of 0°C = 273K). |
||||
// Ideal gas equation P.V = n.R.T gives us the equivalent number of moles: n = P.V/R/T = 100000.Q/8.314/273 = 44.Q mol |
||||
|
||||
public static readonly float R = 8.314f; |
||||
public static readonly float ZeroCelsius = 273.15f; |
||||
public static readonly float StandardPressure = 100000.0f; |
||||
public static readonly float StpVolumeToMoles = 100000.0f / R / 273.15f; |
||||
} |
||||
} |
@ -0,0 +1,139 @@
|
||||
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; } |
||||
|
||||
/// <summary> |
||||
/// Inflation ratio of the balloon (0=empty, 1=maximum) |
||||
/// </summary> |
||||
private float Inflation = 0; |
||||
|
||||
public float Radius { get; private set; } |
||||
|
||||
/// <summary> |
||||
/// Must be called right after creating a balloon |
||||
/// </summary> |
||||
public void Initialize(float emptyMass, Vector3 initialVelocity, float maxVolume, float gasDensity, float dragCoeff) |
||||
{ |
||||
MaxBalloonVolume = maxVolume; |
||||
GasDensity = gasDensity; |
||||
BalloonDragCoeff = dragCoeff; |
||||
BalloonEmptyMass = emptyMass; |
||||
|
||||
gameObject.AddComponent<Rigidbody>(); |
||||
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); |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Injects (positive quantity) or removes (negative quantity) gas, and returns the contained quantity after that operation |
||||
/// </summary> |
||||
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; |
||||
} |
||||
|
||||
/// <summary> |
||||
/// Computes the maximum gas quantity that the balloon can current hold, given the external temperature and pressure conditions |
||||
/// </summary> |
||||
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); |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue