@ -16,6 +16,8 @@ namespace Aerostats
// In this class, 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
// In this class, 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
private static readonly float R = 8.314f ;
private static readonly float R = 8.314f ;
private static readonly float ZeroCelsius = 2 7 3.15f ;
private static readonly float StandardPressure = 1 0 0 0 0 0.0f ;
private static readonly float StpVolumeToMoles = 1 0 0 0 0 0.0f / R / 2 7 3.15f ;
private static readonly float StpVolumeToMoles = 1 0 0 0 0 0.0f / R / 2 7 3.15f ;
/// <summary>
/// <summary>
@ -60,9 +62,34 @@ namespace Aerostats
/// If the balloon can not store the target amount of gas (because the maximum volume has been reached), the system won't try to inject more gas, to avoid venting through the security valve of the balloon.
/// If the balloon can not store the target amount of gas (because the maximum volume has been reached), the system won't try to inject more gas, to avoid venting through the security valve of the balloon.
/// </summary>
/// </summary>
[KSPField(isPersistant = true, guiActive = true)]
[KSPField(isPersistant = true, guiActive = true)]
[UI_FloatRange(minValue = 0.0f , maxValue = 20000.0f, stepIncrement = 1.0f)]
[UI_FloatRange(minValue = 0, maxValue = 20000.0f, stepIncrement = 1.0f)]
public float LiftingGasTargetQuantity ;
public float LiftingGasTargetQuantity ;
/// <summary>
/// Mass of the empty balloon, in kg
/// </summary>
[KSPField(isPersistant = false, guiActive = false)]
public float BalloonEmptyMass = 5 0.0f ;
/// <summary>
/// Minimum amount of gas that will be use to inflate the balloon at the beginning.
/// This is needed if you want the balloon to lift itself right after staging.
/// </summary>
[KSPField(isPersistant = false, guiActive = false)]
public float MinimumFillQuantity = 7 5.0f ;
/// <summary>
/// Drag coefficient of the balloon
/// </summary>
[KSPField(isPersistant = false, guiActive = false)]
public float BalloonDragCoeff = 0.3f ;
/// <summary>
/// Force applied by the spring between the balloon and the vessel, proportional to spring extension, in newton per meter
/// </summary>
[KSPField(isPersistant = false, guiActive = false)]
public float SpringHardness = 2 0 0 0 0.0f ;
private bool Staged ;
private bool Staged ;
/// <summary>
/// <summary>
@ -76,6 +103,8 @@ namespace Aerostats
/// </summary>
/// </summary>
private float Inflation = 0 ;
private float Inflation = 0 ;
private GameObject Balloon ;
private void PlayAnimation ( string animationName , float animationSpeed )
private void PlayAnimation ( string animationName , float animationSpeed )
{
{
foreach ( Animation animation in part . FindModelAnimators ( animationName ) )
foreach ( Animation animation in part . FindModelAnimators ( animationName ) )
@ -117,12 +146,22 @@ namespace Aerostats
{
{
UnityEngine . Debug . Log ( "Aerostats: staged" ) ;
UnityEngine . Debug . Log ( "Aerostats: staged" ) ;
ScreenMessages . PostScreenMessage ( "staged" ) ;
ScreenMessages . PostScreenMessage ( "staged" ) ;
PlayAnimation ( DeployAnimation , 1.0f ) ;
//PlayAnimation(DeployAnimation, 1.0f);
Balloon = GameObject . CreatePrimitive ( PrimitiveType . Sphere ) ;
Balloon . transform . position = part . Rigidbody . position + part . Rigidbody . transform . up ;
Balloon . AddComponent < Rigidbody > ( ) ;
Balloon . rigidbody . mass = BalloonEmptyMass ;
Balloon . rigidbody . angularDrag = 1 0.0f ;
LiftingGasQuantity = Math . Min ( MinimumFillQuantity , RemainingCompressedGas ) ;
RemainingCompressedGas - = LiftingGasQuantity ;
}
}
private void FixedUpdate ( )
private void FixedUpdate ( )
{
{
if ( ! Staged & & part . inverseStage > = Staging . CurrentStage )
if ( ! Staged & & GameSettings . LAUNCH_STAGES . GetKeyDown ( ) & & vessel . isActiveVessel & & ( part . inverseStage = = Staging . CurrentStage - 1 | | Staging . CurrentStage = = 0 ) )
{
{
Staged = true ;
Staged = true ;
OnStaged ( ) ;
OnStaged ( ) ;
@ -130,21 +169,25 @@ namespace Aerostats
if ( Staged )
if ( Staged )
{
{
if ( ! IsAnimationPlaying ( DeployAnimation ) )
//if (!IsAnimationPlaying(DeployAnimation) )
{
{
float externalTemperature = ( float ) FlightGlobals . getExternalTemperature ( ) ;
float externalTemperature = ( float ) FlightGlobals . getExternalTemperature ( ) ;
float balloonInternalTemperature = externalTemperature ;
float balloonInternalTemperature = externalTemperature ;
float externalPressure = Math . Max ( ( float ) FlightGlobals . getStaticPressure ( ) * 1 0 0 0 , 0.00001f ) ;
float externalPressure = Math . Max ( ( float ) FlightGlobals . getStaticPressure ( ) * 1 0 0 0.0f , 0.00001f ) ;
Util . PostSingleScreenMessage ( "external atmo" , "Temperature = " + externalTemperature + "K, Pressure=" + externalPressure + "Pa" ) ;
Util . PostSingleScreenMessage ( "external atmo" , "Temperature = " + externalTemperature + "K, Pressure=" + externalPressure + "Pa" ) ;
float currentMaxQuantity = MaxBalloonVolume / R / balloonInternalTemperature * externalPressure / StpVolumeToMoles ;
float currentMaxQuantity = MaxBalloonVolume / R / balloonInternalTemperature * externalPressure / StpVolumeToMoles ;
LiftingGasTargetQuantity = Math . Max ( MinimumFillQuantity , LiftingGasTargetQuantity ) ;
if ( LiftingGasTargetQuantity > LiftingGasQuantity )
if ( LiftingGasTargetQuantity > LiftingGasQuantity )
{
{
// infalting balloon
float stepFinalQuantity = Math . Min ( LiftingGasQuantity + MaxGasFillRate * Time . fixedDeltaTime , Math . Min ( LiftingGasTargetQuantity , currentMaxQuantity ) ) ;
float stepFinalQuantity = Math . Min ( LiftingGasQuantity + MaxGasFillRate * Time . fixedDeltaTime , Math . Min ( LiftingGasTargetQuantity , currentMaxQuantity ) ) ;
float step = Math . Max ( stepFinalQuantity - LiftingGasQuantity , 0.0f ) ;
float step = Math . Max ( stepFinalQuantity - LiftingGasQuantity , 0.0f ) ;
LiftingGasQuantity + = step ;
LiftingGasQuantity + = step ;
RemainingCompressedGas - = step ;
RemainingCompressedGas - = step ;
// gas exhausted
if ( RemainingCompressedGas < 0 )
if ( RemainingCompressedGas < 0 )
{
{
Util . PostSingleScreenMessage ( "out of gas" , "Lifting gas reserve exhausted" ) ;
Util . PostSingleScreenMessage ( "out of gas" , "Lifting gas reserve exhausted" ) ;
@ -154,6 +197,7 @@ namespace Aerostats
}
}
else
else
{
{
// deflating balloon
LiftingGasQuantity = Math . Max ( LiftingGasQuantity - MaxGasVentRate * Time . fixedDeltaTime , LiftingGasTargetQuantity ) ;
LiftingGasQuantity = Math . Max ( LiftingGasQuantity - MaxGasVentRate * Time . fixedDeltaTime , LiftingGasTargetQuantity ) ;
}
}
@ -170,16 +214,56 @@ namespace Aerostats
Inflation = currentGasVolume / MaxBalloonVolume ;
Inflation = currentGasVolume / MaxBalloonVolume ;
Util . PostSingleScreenMessage ( "inflation" , "Inflation = " + ( Inflation * 1 0 0.0f ) . ToString ( "0.00" ) + "%" ) ;
Util . PostSingleScreenMessage ( "inflation" , "Inflation = " + ( Inflation * 1 0 0.0f ) . ToString ( "0.00" ) + "%" ) ;
SetInflation ( Inflation ) ;
//SetInflation(Inflation) ;
float airDensity = ( float ) FlightGlobals . getAtmDensity ( externalPressure / 1 0 0 0.0f , externalTemperature , vessel . mainBody ) ;
float airDensity = ( float ) FlightGlobals . getAtmDensity ( externalPressure / 1 0 0 0.0f , externalTemperature , vessel . mainBody ) ;
float balloonLift = currentGasVolume * ( airDensity - GasDensity ) ;
float currentGasDensity = GasDensity * balloonInternalTemperature / ZeroCelsius / externalPressure * StandardPressure ;
Util . PostSingleScreenMessage ( "lift" , "Air density = " + airDensity + "kg/m^3, Lift = " + balloonLift + "kg" ) ;
float balloonLift = currentGasVolume * airDensity ;
float balloonGasMass = currentGasDensity * currentGasVolume ;
Util . PostSingleScreenMessage ( "lift" , "Air density = " + airDensity + "kg/m^3, Lift = " + ( balloonLift - balloonGasMass ) + "kg" ) ;
var gravityAccel = FlightGlobals . getGeeForceAtPosition ( vessel . GetWorldPos3D ( ) ) ;
var gravityAccel = FlightGlobals . getGeeForceAtPosition ( vessel . GetWorldPos3D ( ) ) ;
//Util.PostSingleScreenMessage("gravity", "Gravity accel = (" + gravityAccel.x + ", " + gravityAccel.y + ", " + gravityAccel.z + ")");
//Util.PostSingleScreenMessage("gravity", "Gravity accel = (" + gravityAccel.x + ", " + gravityAccel.y + ", " + gravityAccel.z + ")");
part . Rigidbody . AddForce ( - gravityAccel * balloonLift / 1 0 0 0.0f , ForceMode . Force ) ;
// V = 4/3*pi*r^3
float radius = Mathf . Pow ( currentGasVolume * 0.75f / Mathf . PI , 0.333f ) ;
float scale = radius + 0.1f ;
Balloon . transform . localScale = new Vector3 ( scale , scale , scale ) ;
Balloon . rigidbody . mass = ( BalloonEmptyMass + balloonGasMass ) * 0.001f ;
Balloon . rigidbody . AddForce ( - gravityAccel * balloonLift / 1 0 0 0.0f , ForceMode . Force ) ;
// balloon drag
var airVelocity = Balloon . 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" ) ;
Balloon . rigidbody . AddForce ( - airVelocity . normalized * dragForce / 1 0 0 0.0f , ForceMode . Force ) ;
// spring between balloon and base
float restLength = 2.0f + radius ;
var springVec = Balloon . rigidbody . position - part . rigidbody . position ;
float springLength = springVec . magnitude ;
float springForceMag = 0 ;
if ( springLength > restLength )
{
float tensingLength = springLength - restLength ;
springForceMag = tensingLength * SpringHardness ;
var springForce = springVec * ( springForceMag / springLength * 0.001f ) ;
part . rigidbody . AddForce ( springForce , ForceMode . Force ) ;
Balloon . rigidbody . AddForceAtPosition ( - springForce , Balloon . rigidbody . position - Balloon . transform . up * radius , ForceMode . Force ) ;
}
Util . PostSingleScreenMessage ( "spring force" , "Spring force = " + springForceMag + "N" ) ;
}
}
}
}
}
}
public void Update ( )
{
if ( Balloon ! = null )
{
Gizmos . DrawLine ( part . transform . position , Balloon . transform . position - Balloon . transform . up * Balloon . transform . localScale . x ) ;
}
}
}
}
}
}