// AviationCalculator.js
//
// © 2003-2008 Joachim K. Hochwarth
//
// REVISION(S) >
// 1.6.0     07/29/2008 Joachim K. Hochwarth
//           SEE AviationCalculator.html

// Constants -------------------------------------------------------------------
//
p0             = 101325.0;                         // [N/m^2] = [Pa]
p1             =  22632.22842714;                  // [N/m^2] = [Pa]

p0hPa          = 1013.25;                          // [hPa]
p0inHG         =   29.92;                          // [inHG] Truncated 29.9213

T0             =    288.15;                        // [K]
T1             =    216.65;                        // [K]

h1             =  36089.2388451444;                // [ft]
h2             =  65616.7979002625;                // [ft]

dTdh0          =     -0.0019812;                   // [K/ft]
dTdh0SI        =     -0.0065;                      // [K/m]

CPascalTOPSI   = 1.45037737730209e-04;
ChPaTOinHG     = p0inHG/p0hPa;

ClbPft3TOkgPm3 = 16.0184633739601;                 // [lb/ft^3] to [kg/m^3]

CftTOm         =      0.3048;
CftTOnm        = 1.64578833693305e-04;

CnmTOm         =   1852.0;

CftPsTOkn      = CftTOnm*3600.0;
CftPsTOmph     = 3600.0/5280.0;
CftPsTOkph     = CftTOm*3600.0/1000.0;

CmPsTOkn       = 3600.0/CnmTOm;

CknTOftPs      = 1.0/(CftTOnm*3600.0);

CRGasSI        =    287.053;                       // [m^2/(s^2*K)] = [J/(kg*K)]

CgSI           =      9.80665;                     // [m/s^2]

CgRGas         = (CgSI*CftTOm)/CRGasSI;
CgRGasSI       = CgSI/CRGasSI;

CgRGas         = (CgSI*CftTOm)/CRGasSI;

CGamma         =      1.4;                         // [-]
CGammaRGas     = (CGamma*CRGasSI)/(CftTOm*CftTOm); // [ft^2/(s^2*K)]

CaSLSI         = Math.sqrt(CGamma*CRGasSI*T0);
CPressureSLSI  = 101325;                           // [Pa] = [N/m^2]
CaSLNU         = CaSLSI*CmPsTOkn;                  // [kts] Nautical Unit


CKelvinTOCelsius                 = 273.15;
CKelvinTORankine                 =   1.8;

CCelsiusTOFahrenheitLinear       =  32.0;
CCelsiusTOFahrenheitProportional =   1.8;



// -----------------------------------------------------------------------------
//
function calculateQNH()
{
  // [1] Meteorology Specification A2669 Version 4.00 Page 100

  // TODO > GLOBAL!

  R          =  287.04;    // [m^2/(s^2*K)] = [J/(kg*K)]
  u          =    0.0065;  // [m/s^2]
  g          =    9.80655; // [m/s^2]

  switch (document.pQNHForm.hQNHUnit.selectedIndex)
  {
    case 0:

      // [ft]

      h = document.pQNHForm.hQNH.value*CftTOm;

      break;

    case 1:

      // [m]

      h = document.pQNHForm.hQNH.value;

      break;

    default:

      // ERROR
  }

  if (document.pQNHForm.pQNHUnit.selectedIndex == 0)
  {
    pQFE = document.pQNHForm.pQNH.value/ChPaTOinHG;

  } else
  {
    // selectedIndex == 1 = [hPa]

    pQFE = document.pQNHForm.pQNH.value;
  }

  // TODO > Add Units & switch() statement!

  pQNH = p0hPa*Math.pow(Math.pow((pQFE/p0hPa), (R*u)/g) + (h*u)/T0, g/(R*u));

  // Unit Conversion(s) --------------------------------------------------------
  //
  if (document.pQNHForm.resultQNHUnit.selectedIndex == 0)
  {
    pQNH = pQNH*ChPaTOinHG;

  } else
  {
    // selectedIndex == 1 = [hPa]
  }

  // TODO > Number of Digits

  document.pQNHForm.resultQNH.value = pQNH;
}

function clearQNH()
{
  document.pQNHForm.hQNH.value                  = "0.00";
  document.pQNHForm.pQNH.value                  = "0.00";

  document.pQNHForm.resultQNH.value             = "0.00";

  document.pQNHForm.hQNHUnit.selectedIndex      = 0;
  document.pQNHForm.pQNHUnit.selectedIndex      = 0;

  document.pQNHForm.resultQNHUnit.selectedIndex = 0;
}

function calculateQFE()
{
  // [1] Meteorology Specification A2669 Version 4.00 Page 100

  // TODO > GLOBAL!

  R          =  287.04;    // [m^2/(s^2*K)] = [J/(kg*K)]
  u          =    0.0065;  // [m/s^2]
  g          =    9.80655; // [m/s^2]

  switch (document.pQNHForm.hQFEUnit.selectedIndex)
  {
    case 0:

      // [ft]

      h = document.pQNHForm.hQFE.value*CftTOm;

      break;

    case 1:

      // [m]

      h = document.pQNHForm.hQFE.value;

      break;

    default:

      // ERROR
  }

  if (document.pQNHForm.pQFEUnit.selectedIndex == 0)
  {
    pQNH = document.pQNHForm.pQFE.value/ChPaTOinHG;

  } else
  {
    // selectedIndex == 1 = [hPa]

    pQNH = document.pQNHForm.pQFE.value;
  }

  // TODO > Add Units & switch() statement!

  pQFE = p0hPa*Math.pow(Math.pow((pQNH/p0hPa), (R*u)/g) - ((h*u)/T0), g/(R*u));

  // Unit Conversion(s) --------------------------------------------------------
  //
  if (document.pQNHForm.resultQFEUnit.selectedIndex == 0)
  {
    pQFE = pQFE*ChPaTOinHG;

  } else
  {
    // selectedIndex == 1 = [hPa]
  }

  // TODO > Number of Digits

  document.pQNHForm.resultQFE.value = pQFE;
}

function clearQFE()
{
  document.pQNHForm.hQFE.value                  = "0.00";
  document.pQNHForm.pQFE.value                  = "0.00";

  document.pQNHForm.resultQFE.value             = "0.00";

  document.pQNHForm.hQFEUnit.selectedIndex      = 0;
  document.pQNHForm.pQFEUnit.selectedIndex      = 0;

  document.pQNHForm.resultQFEUnit.selectedIndex = 0;
}



// -----------------------------------------------------------------------------
//
function calculateCrossover()
{
  switch (document.hCrossoverForm.unitCAS.selectedIndex)
  {
    case 0:

      // [kts]

      CAS = document.hCrossoverForm.CAS.value;

      break;

    case 1:

      // [fps]

      CAS = document.hCrossoverForm.CAS.value/CknTOftPs;

      break;

    default:

      // ERROR
  }

  Mach     = document.hCrossoverForm.Mach.value;

  Pressure = (p0*((Math.pow(((Math.pow(CAS, 2)/(5*Math.pow(CaSLNU, 2))) + 1), (CGamma/(CGamma - 1)))) - 1))/((Math.pow((((Math.pow(Mach, 2))/5) + 1), (CGamma/(CGamma - 1)))) - 1);

  if (Pressure < p1)
  {
    Altitude = h1 - (T1/CgRGas)*Math.log(Pressure/p1);

  } else
  {
    Altitude = (((T0*(Math.pow((Pressure/p0) , (-dTdh0SI/CgRGasSI)))) - T0)/dTdh0);
  }

  // Unit Conversion(s) --------------------------------------------------------
  //
  if (document.hCrossoverForm.unithCrossover.selectedIndex == 1)
  {
    Altitude = Altitude*CftTOm;
  }

  // else selectedIndex == 0 in ft!

  // TODO > Number of Digits

  document.hCrossoverForm.hCrossover.value = Altitude;
}

function clearCrossover()
{
  document.hCrossoverForm.CAS.value                    = "0.00";
  document.hCrossoverForm.Mach.value                   = "0.00";

  document.hCrossoverForm.hCrossover.value             = "0.00";

  document.hCrossoverForm.unitCAS.selectedIndex        = 0;

  document.hCrossoverForm.unithCrossover.selectedIndex = 0;
}



// -----------------------------------------------------------------------------
//
function calculateStandardAtmosphere()
{
  switch (document.StandardAtmosphereForm.unitAltitude.selectedIndex)
  {
    case 0:

      // [ft]

      h = document.StandardAtmosphereForm.valueAltitude.value;

      break;

    case 1:

      // [m]

      h = document.StandardAtmosphereForm.valueAltitude.value/CftTOm;

      break;

    default:

      // ERROR
  }

  // Sanity Check ...
  //
  if (h > h2)
  {
    h = h2;

    document.StandardAtmosphereForm.valueAltitude.value        = h;
    document.StandardAtmosphereForm.unitAltitude.selectedIndex = 0;

    alert("The entered altitude was limited to the maximum allowed value!");
  }

  // Calculations --------------------------------------------------------------
  //
  if (h <= h1)
  {
    // Troposphere

    T = T0 + dTdh0*h;
    p = p0*Math.pow((T0/T), (CgRGasSI/dTdh0SI));

  } else
  {
    // Tropopause

    T = T1;
    p = p1*Math.exp((CgRGas/T1)*(h1 - h));
  }

  Rho = p/(CRGasSI*T);

  a   = Math.sqrt(CGammaRGas*T);

  // Unit Conversion(s) --------------------------------------------------------
  //
  switch (document.StandardAtmosphereForm.unitTemperature.selectedIndex)
  {
    case 0:

      // [K] Kelvin

      break;

    case 1:

      // [C] Celsius

      T = T - CKelvinTOCelsius;

      break;

    case 2:

      // [F] Fahrenheit

      T = (T - CKelvinTOCelsius)*CCelsiusTOFahrenheitProportional + CCelsiusTOFahrenheitLinear;

      break;

    case 3:

      // [R] Rankine

      T = T*CKelvinTORankine;

      break;

    default:

      // ERROR
  }

  document.StandardAtmosphereForm.resultTemperature.value = T;

  switch (document.StandardAtmosphereForm.unitPressure.selectedIndex)
  {
    case 0:

      // [hPa] hectoPascal

      p /= 100.0;

      break;

    case 1:

      // [Pa] Pascal

      break;

    case 2:

      // [psi] Pounds per Square Inch

      p *= CPascalTOPSI;

      break;

    case 3:

      // [inHg]

      p /= 100.0;
      p *= ChPaTOinHG;

      break;

    default:

      // ERROR
  }

  document.StandardAtmosphereForm.resultPressure.value = p;

  switch (document.StandardAtmosphereForm.unitDensity.selectedIndex)
  {
    case 0:

      // [kg/m^3]

      break;

    case 1:

      // [lb/ft^3]

      Rho /= ClbPft3TOkgPm3;

      break;

    default:

      // ERROR
  }

  document.StandardAtmosphereForm.resultDensity.value = Rho;

  switch (document.StandardAtmosphereForm.unitSOS.selectedIndex)
  {
    case 0:

      // [fps]

      break;

    case 1:

      // [kts]

      a *= CftPsTOkn;

      break;

    case 2:

      // [mph]

      a *= CftPsTOmph;

      break;

    case 3:

      // [kph]

      a *= CftPsTOkph;

      break;

    default:

      // ERROR
  }

  document.StandardAtmosphereForm.resultSOS.value = a;
}

function clearStandardAtmosphere()
{
  document.StandardAtmosphereForm.valueAltitude.value           = "0.00";

  document.StandardAtmosphereForm.resultTemperature.value       = "0.00";
  document.StandardAtmosphereForm.resultPressure.value          = "0.00";
  document.StandardAtmosphereForm.resultDensity.value           = "0.00";
  document.StandardAtmosphereForm.resultSOS.value               = "0.00";

  document.StandardAtmosphereForm.unitAltitude.selectedIndex    = 0;

  document.StandardAtmosphereForm.unitTemperature.selectedIndex = 0;
  document.StandardAtmosphereForm.unitPressure.selectedIndex    = 0;
  document.StandardAtmosphereForm.unitDensity.selectedIndex     = 0;
  document.StandardAtmosphereForm.unitSOSselectedIndex          = 0;
}

function calculateCAS()
{
  // Calculate Mach & TAS based on entered CAS

  switch (document.SpeedConversionForm.unitAltitude.selectedIndex)
  {
    case 0:

      // [ft]

      h = document.SpeedConversionForm.valueAltitude.value;

      break;

    case 1:

      // [m]

      h = document.SpeedConversionForm.valueAltitude.value/CftTOm;

      break;

    default:

      // ERROR
  }

  // Sanity Check ...
  //
  if (h > h2)
  {
    h = h2;

    document.SpeedConversionForm.valueAltitude.value        = h;
    document.SpeedConversionForm.unitAltitude.selectedIndex = 0;

    alert("The entered altitude was limited to the maximum allowed value!");
  }

  CAS = document.SpeedConversionForm.valueCAS.value

  switch (document.SpeedConversionForm.unitCAS.selectedIndex)
  {
    case 0:

      // [kts]

      break;

    case 1:

      // [fps]

      CAS *= CftPsTOkn;

      break;

    case 2:

      // [mph]

      CAS *= CftPsTOkn/CftPsTOmph;

      break;

    case 3:

      // [kph]

      CAS *= CftPsTOkn/CftPsTOkph;

      break;

    default:

      // ERROR
  }

  // TODO > CHECK for 20,000 [m] LIMIT!

  // Calculations --------------------------------------------------------------
  //
  if (h <= h1)
  {
    // Troposphere

    T = T0 + dTdh0*h;
    p = p0*Math.pow((T0/T), (CgRGasSI/dTdh0SI));

  } else
  {
    // Tropopause

    T = T1;
    p = p1*Math.exp((CgRGas/T1)*(h1 - h));
  }

  Rho = p/(CRGasSI*T);

  a   = Math.sqrt(CGammaRGas*T);

  TAS = Math.sqrt(5)*a*Math.sqrt(Math.pow(((CPressureSLSI/p)*(Math.pow((CAS*CAS/(5.0*CaSLNU*CaSLNU)) + 1, (CGamma/(CGamma - 1))) - 1) + 1), (CGamma - 1)/CGamma) - 1);

  Mach = TAS / a;

  // Unit Conversion(s) --------------------------------------------------------
  //
  switch (document.SpeedConversionForm.unitTAS.selectedIndex)
  {
    case 0:

      // [kts]

      TAS *= CftPsTOkn

      break;

    case 1:

      // [fps]

      break;

    case 2:

      // [mph]

      TAS *= CftPsTOmph;

      break;

    case 3:

      // [kph]

      TAS *= CftPsTOkph;

      break;

    default:

      // ERROR
  }

  document.SpeedConversionForm.valueTAS.value = TAS;
  document.SpeedConversionForm.valueMach.value = Mach;
}

function calculateMach()
{
  // Calculate CAS & TAS based on entered Mach

  switch (document.SpeedConversionForm.unitAltitude.selectedIndex)
  {
    case 0:

      // [ft]

      h = document.SpeedConversionForm.valueAltitude.value;

      break;

    case 1:

      // [m]

      h = document.SpeedConversionForm.valueAltitude.value/CftTOm;

      break;

    default:

      // ERROR
  }

  // Sanity Check ...
  //
  if (h > h2)
  {
    h = h2;

    document.SpeedConversionForm.valueAltitude.value        = h;
    document.SpeedConversionForm.unitAltitude.selectedIndex = 0;

    alert("The entered altitude was limited to the maximum allowed value!");
  }

  // Calculations --------------------------------------------------------------
  //
  if (h <= h1)
  {
    // Troposphere

    T = T0 + dTdh0*h;
    p = p0*Math.pow((T0/T), (CgRGasSI/dTdh0SI));

  } else
  {
    // Tropopause

    T = T1;
    p = p1*Math.exp((CgRGas/T1)*(h1 - h));
  }

  Rho = p/(CRGasSI*T);

  a   = Math.sqrt(CGammaRGas*T);

  TAS = document.SpeedConversionForm.valueMach.value * a;

  CAS = Math.sqrt(5)*CaSLNU*Math.sqrt(Math.pow(((p/CPressureSLSI)*(Math.pow((TAS*TAS/(5.0*a*a)) + 1, (CGamma/(CGamma - 1))) - 1) + 1), (CGamma - 1)/CGamma) - 1);

  // Unit Conversion(s) --------------------------------------------------------
  //
  switch (document.SpeedConversionForm.unitTAS.selectedIndex)
  {
    case 0:

      // [kts]

      TAS *= CftPsTOkn;

      break;

    case 1:

      // [fps]

      break;

    case 2:

      // [mph]

      TAS *= CftPsTOmph;

      break;

    case 3:

      // [kph]

      TAS *= CftPsTOkph;

      break;

    default:

      // ERROR
  }

  switch (document.SpeedConversionForm.unitCAS.selectedIndex)
  {
    case 0:

      // [kts]

      break;

    case 1:

      // [fps]

      CAS /= CftPsTOkn

      break;

    case 2:

      // [mph]

      CAS *= CftPsTOmph/CftPsTOkn;

      break;

    case 3:

      // [kph]

      CAS *= CftPsTOkph/CftPsTOkn;

      break;

    default:

      // ERROR
  }

  document.SpeedConversionForm.valueTAS.value = TAS;
  document.SpeedConversionForm.valueCAS.value = CAS;
}

function calculateTAS()
{
  // Calculate CAS & Mach based on entered TAS

  switch (document.SpeedConversionForm.unitAltitude.selectedIndex)
  {
    case 0:

      // [ft]

      h = document.SpeedConversionForm.valueAltitude.value;

      break;

    case 1:

      // [m]

      h = document.SpeedConversionForm.valueAltitude.value/CftTOm;

      break;

    default:

      // ERROR
  }

  // Sanity Check ...
  //
  if (h > h2)
  {
    h = h2;

    document.SpeedConversionForm.valueAltitude.value        = h;
    document.SpeedConversionForm.unitAltitude.selectedIndex = 0;

    alert("The entered altitude was limited to the maximum allowed value!");
  }



  TAS = document.SpeedConversionForm.valueTAS.value

  switch (document.SpeedConversionForm.unitTAS.selectedIndex)
  {
    case 0:

      // [kts]

      TAS /= CftPsTOkn;

      break;

    case 1:

      // [fps]

      break;

    case 2:

      // [mph]

      TAS /= CftPsTOmph;

      break;

    case 3:

      // [kph]

      TAS /= CftPsTOkph;

      break;

    default:

      // ERROR
  }

  // TODO > CHECK for 20,000 [m] LIMIT!

  // Calculations --------------------------------------------------------------
  //
  if (h <= h1)
  {
    // Troposphere

    T = T0 + dTdh0*h;
    p = p0*Math.pow((T0/T), (CgRGasSI/dTdh0SI));

  } else
  {
    // Tropopause

    T = T1;
    p = p1*Math.exp((CgRGas/T1)*(h1 - h));
  }

  Rho = p/(CRGasSI*T);

  a   = Math.sqrt(CGammaRGas*T);

  Mach = TAS / a;

  CAS = Math.sqrt(5)*CaSLNU*Math.sqrt(Math.pow(((p/CPressureSLSI)*(Math.pow((TAS*TAS/(5.0*a*a)) + 1, (CGamma/(CGamma - 1))) - 1) + 1), (CGamma - 1)/CGamma) - 1);



  // Unit Conversion(s) --------------------------------------------------------
  //
  switch (document.SpeedConversionForm.unitCAS.selectedIndex)
  {
    case 0:

      // [kts]

      break;

    case 1:

      // [fps]

      CAS /= CftPsTOkn

      break;

    case 2:

      // [mph]

      CAS *= CftPsTOmph/CftPsTOkn;

      break;

    case 3:

      // [kph]

      CAS *= CftPsTOkph/CftPsTOkn;

      break;

    default:

      // ERROR
  }

  document.SpeedConversionForm.valueCAS.value = CAS;
  document.SpeedConversionForm.valueMach.value = Mach;
}


function clearSpeed()
{
  document.SpeedConversionForm.valueAltitude.value        = "0.00";
  document.SpeedConversionForm.unitAltitude.selectedIndex = 0;

  document.SpeedConversionForm.valueCAS.value             = "0.00";
  document.SpeedConversionForm.unitCAS.selectedIndex      = 0;

  document.SpeedConversionForm.valueMach.value            = "0.00";

  document.SpeedConversionForm.valueTAS.value             = "0.00";
  document.SpeedConversionForm.unitTAS.selectedIndex      = 0;
}



// -----------------------------------------------------------------------------
//
function calculatePressureAltitude()
{
  switch (document.hPressureForm.unitPressure.selectedIndex)
  {
    case 0:

      // [hPa] hectoPascal

      p = document.hPressureForm.valuePressure.value*100.0;

      break;

    case 1:

      // [Pa] Pascal

      p = document.hPressureForm.valuePressure.value;

      break;

    case 2:

      // [psi] Pounds per Square Inch

      p = document.hPressureForm.valuePressure.value/CPascalTOPSI;

      break;

    case 3:

      // [inHg]

      p = document.hPressureForm.valuePressure.value*100.0/ChPaTOinHG;

      break;

    default:

      // ERROR
  }

  // TODO > CHECK for 20,000 [m] LIMIT!

  if (p >= p1)
  {
    // Troposphere

    h = T0*(Math.pow((p0/p), (dTdh0SI/CgRGasSI)) - 1.0)/dTdh0;

  } else
  {
    // Tropopause

    h = h1 - Math.log(p/p1)*T1/CgRGas;
  }

  document.hPressureForm.hPressure.value = h;
}

function clearPressureAltitude()
{
  document.hPressureForm.valuePressure.value        = "0.00";
  document.hPressureForm.unitPressure.selectedIndex = 0;

  document.hPressureForm.hPressure.value             = "0.00";
  document.hPressureForm.unithPressure.selectedIndex = 0;
}
