A Kerbal Space Program mod that adds balloon parts to create aerostats
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

140 lines
6.5 KiB

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);
}
}
}