def alt2temp(H, alt_units=default_alt_units, temp_units=default_temp_units):
    """Return the standard temperature for the specified altitude.  Altitude
    units may be feet ('ft'), metres ('m'), statute miles, ('sm') or 
    nautical miles ('nm').  Temperature units may be degrees C, F, K or R 
    ('C', 'F', 'K' or 'R')  
    
    If the units are not specified, the units in default_units.py are used.
    
    Examples:
    
    Calculate the standard temperature (in default temperature units) at 
    5,000 (default altitude units):
    >>> alt2temp(5000)
    5.0939999999999941
    
    Calculate the standard temperature in deg F at sea level:
    >>> alt2temp(0, temp_units = 'F')
    59.0
    
    Calculate the standard temperature in deg K at 11,000 m:
    >>> alt2temp(11000, alt_units = 'm', temp_units = 'K')
    216.64999999999998
    
    Calculate the standard temperature at 11 statute miles in deg R:
    >>> alt2temp(11, alt_units = 'sm', temp_units = 'R')
    389.96999999999997
    
    The input value may be an expression:
    >>> alt2temp(11 * 5280, temp_units = 'R')
    389.96999999999997
    
    """

    # Validated to 84000 m
    # uses meters and degrees K for the internal calculations

    # function tested in tests/test_std_atm.py

    H = U.len_conv(H, from_units=alt_units, to_units='km')

    if H <= 11:
        temp = T0 + H * L0
    elif H <= 20:
        temp = T11
    elif H <= 32:
        temp = T20 + (H - 20) * L20
    elif H <= 47:
        temp = T32 + (H - 32) * L32
    elif H <= 51:
        temp = T47
    elif H <= 71:
        temp = T51 + (H - 51) * L51
    elif H <= 84.852:
        temp = T71 + (H - 71) * L71
    else:
        raise ValueError(
            'This function is only implemented for altitudes of 84.852 km and below.'
        )

    return U.temp_conv(temp, to_units=temp_units, from_units='K')
def temp2speed_of_sound(temp,
                        temp_units=default_temp_units,
                        speed_units=default_speed_units):
    """
    Return the speed of sound, given the air temperature.  
    
    The temperature units may be deg C, F, K or R ('C', 'F', 'K' or 'R').
    
    The speed units may be 'kt', 'mph', 'km/h', 'm/s' and 'ft/s'.

    If the units are not specified, the units in default_units.py are used.
    
    Examples:
    
    Determine speed of sound in knots at 15 deg (default temperature units):
    >>> temp2speed_of_sound(15)
    661.47882487301808
    
    Determine speed of sound in mph at 120 deg F:
    >>> temp2speed_of_sound(120, speed_units = 'mph', temp_units = 'F')
    804.73500154991291
    """

    # function tested in tests/test_std_atm.py

    temp = U.temp_conv(temp, from_units=temp_units, to_units='K')

    speed_of_sound = M.sqrt((1.4 * Rd) * temp)
    speed_of_sound = U.speed_conv(speed_of_sound,
                                  from_units='m/s',
                                  to_units=speed_units)

    return speed_of_sound
def density_alt2temp(
    density_alt_seek,
    press_alt,
    alt_units=default_alt_units,
    temp_units=default_temp_units,
):
    """
    Return temperature to achieve a desired density altitude.

    If the units are not specified, the units in default_units.py are used.
    """

    low = -100  # initial lower guess
    high = 100  # initial upper guess

    # confirm initial low and high are OK:

    da_low = density_alt(press_alt, low, alt_units=alt_units)
    if da_low > density_alt_seek:
        raise ValueError('Initial low guess too high.')

    da_high = density_alt(press_alt, high, alt_units=alt_units)
    if da_high < density_alt_seek:
        raise ValueError('Initial high guess too low.')

    guess = (low + high) / 2.
    da_guess = density_alt(press_alt, guess, alt_units=alt_units)

    # keep iterating until da is within 1 ft of desired value

    while M.fabs(da_guess - density_alt_seek) > 1:
        if da_guess > density_alt_seek:
            high = guess
        else:
            low = guess

        guess = (low + high) / 2.
        da_guess = density_alt(press_alt, guess, alt_units=alt_units)

    guess = U.temp_conv(guess, from_units='C', to_units=temp_units)

    return guess
def sat_press(
    T='FALSE',
    DP='FALSE',
    RH=0.0,
    temp_units=default_temp_units,
    press_units=default_press_units,
):
    """
    Return the saturated vapour pressure of water.  Either the dew point, or 
    the temperature and the relative humidity must be specified.  If both the
    dew point and relative humidity are specified, the relative humidity value
    is ignored.
    
    If the temperature and dew point are both specified, the dew point cannot
    be greater than the temperature:
    
    If the units are not specified, the units in default_units.py are used.

    >>> sat_press(T=10, DP=11)
    Traceback (most recent call last):
      File '<stdin>', line 1, in <module>
      File 'std_atm.py', line 795, in sat_press
        raise ValueError, 'The dew point cannot be greater than the temperature.'
    ValueError: The dew point cannot be greater than the temperature.
    
    Dew point is 11 deg (default temperature units).  Find the water vapour
    pressure in default pressure units:
    >>> sat_press(DP=11)
    0.38741015927568667
    
    Dew point is 65 deg F.  Find the water vapour pressure in default pressure units:
    >>> sat_press(DP=65, temp_units = 'F')
    0.62207710701956165
    
    Dew point is 212 deg F (the boiling point of water at sea level).
    Find the water vapour pressure in lb per sq. inch:
    >>> sat_press(DP=212, temp_units = 'F', press_units = 'psi')
    14.696764873564959

    Temperature is 30 deg C.  Find the water vapour pressure in default pressure units:
    for 50% relative humidity:
    >>> sat_press(T=30, RH = 0.5)
    0.62647666996057927
    """

    if DP != 'FALSE':

        # use dew point method

        if T != 'FALSE':
            if DP > T:
                raise ValueError(
                    'The dew point cannot be greater than the temperature.')

        DP = U.temp_conv(DP, from_units=temp_units, to_units='C')

        # calculate vapour pressure

        Pv = _sat_press(DP) * 100
    else:

        if RH == 'FALSE':
            raise ValueError(
                'Either DP (dew point) or RH (relative humidity) must be specified.'
            )

    # relative humidity is specified
    # confirm relative humidity is in range

        if RH < 0 or RH > 1:
            raise ValueError(
                'The relative humidity must be in the range of 0 to 1.')

        if T == 'FALSE':
            raise ValueError(
                'If the relative humidity is specified, the temperature must also be specified.'
            )

        T = U.temp_conv(T, from_units=temp_units, to_units='C')

        Pv = _sat_press(T) * 100
        Pv *= RH

    Pv = U.press_conv(Pv, from_units='pa', to_units=press_units)

    return Pv
def density_alt(
    H,
    T,
    alt_setting=P0,
    DP='FALSE',
    RH=0.0,
    alt_units=default_alt_units,
    temp_units=default_temp_units,
):
    """
    Return density altitude, given the pressure altitude and the 
    temperature with altitudes in units of feet ('ft'), metres ('m'), 
    statute miles, ('sm') or nautical miles ('nm'), and temperature in units
    of deg C, F, K or R ('C', 'F', 'K' or 'R').
    
    Mandatory parametres:
    H = altitude
    T = temperature
    
    Optional parametres:
    alt_setting = altimeter setting (defaults to 29.9213 if not provided
    DP = dew point
    RH = relative humidity
    alt_units = units for the altitude.  'ft', 'm', or 'km'.  
    temp_units = units for the temperature and dew point.  'C', 'F', 'K' 
                 or 'R'.
    
    The altimeter setting units are assumed to be inches of HG, unless the 
    value is greater than 35.  In this case the units are assumed to be mb.
    
    If the dew point or relative humidity are not specified, the air is 
    assumed to be completely dry.  If both the dew point and relative humidity
    are specified, the relative humidity value is ignored.
    
    If the units are not specified, the units in default_units.py are used.

    The method is from: http://wahiduddin.net/calc/density_altitude.htm
    
    Examples:
    
    Calculate the density altitude in default altitude units for a pressure 
    altitude of 7000 default altitude units and a temperature of 15 deg 
    (default temperature units).  The altimeter setting is not specified, so it 
    defaults to standard pressure of 29.9213 in HG or 1013.25 mb:
    >>> density_alt(7000, 15)
    8595.3465863232504
    
    Calculate the density altitude in default altitude units for a pressure 
    altitude of 7000 default altitude units and a temperature of 85 deg F.  
    The altimeter setting is not specified, so it defaults to standard pressure 
    of 29.9213 in HG or 1013.25 mb.  The dew point and relative humidity are 
    not specified, so the air is assumed to be dry:
    >>> density_alt(7000, 85, temp_units = 'F')
    10159.10696106757
    
    Calculate the density altitude in default altitude units for a pressure 
    altitude of 7000 default altitude units, an altimeter setting of 29.80 and
    a temperature of 85 deg F and a dew point of 55 deg F:
    >>> density_alt(7000, 85, 29.80, 55, temp_units = 'F')
    10522.776013011618
    
    Calculate the density altitude in metres for a pressure altitude of 
    2000 m, an altimeter setting of 1010 mb,  a temperature of 15 deg (default 
    temperature units) and a relative humidity of 50%:
    >>> density_alt(2000, 15, 1010, alt_units = 'm', RH = 0.5)
    2529.8230634449737
    
    The dew point may be specified in one of two ways: as the fourth 
    argument on the command line, or via the keyword argument DP.
    >>> density_alt(2000, 15, 1010, alt_units = 'm', DP = 5)
    2530.7528237990618
        
    The relative humidity must be in the range of 0 to 1:
    >>> density_alt(2000, 15, 1010, alt_units = 'm', RH = 1.1)
    Traceback (most recent call last):
      File '<stdin>', line 1, in ?
      File 'std_atm.py', line 533, in density_alt
    raise ValueError, 'The relative humidity must be in the range of 0 to 1.'
    ValueError: The relative humidity must be in the range of 0 to 1.
    """

    Rv = 461.495  # gas constant for water vapour

    # saturated vapour pressure

    if DP == 'FALSE' and RH == 0:
        Pv = 0
    else:
        Pv = sat_press(T, DP, RH, temp_units, press_units='pa')

    # dry air pressure

    Pd = dry_press(H,
                   Pv,
                   alt_setting=alt_setting,
                   alt_units=alt_units,
                   press_units='pa')

    T = U.temp_conv(T, from_units=temp_units, to_units='K')
    D = Pd / (Rd * T) + Pv / (Rv * T)

    DR = D / Rho0

    return density_ratio2alt(DR, alt_units)
def density_alt_table(
    density_alt_seek,
    alt_range=2000,
    alt_inc=100,
    alt_units=default_alt_units,
    temp_units=default_temp_units,
    multi_units=False,
    file='',
    format='text',
):
    """
    Return a text or html table of required temperature vs pressure altitude.

    If the units are not specified, the units in default_units.py are used.
    """

    line_buffer = []
    if format == 'text':
        line_buffer.append(
            'Pressure altitudes and temperatures for a density ')
        line_buffer.append('altitude of ' + str(density_alt_seek) + ' ' +
                           alt_units)
        line_buffer.append('(assuming dry air)\n')
        if multi_units:
            line_buffer.append(' Pressure    Temp      Temp')
            line_buffer.append(' Altitude')
            line_buffer.append('   (' + alt_units + ')     (deg C)   (deg F)')
        else:
            line_buffer.append(' Pressure    Temp')
            line_buffer.append(' Altitude')
            line_buffer.append('   (' + alt_units + ')     (deg ' +
                               temp_units + ')')
    elif format == 'html':
        print('creating html')
    else:
        raise ValueError('Invalid format.  Must be either "text" or "html"')

    if multi_units:
        for alt in range(max(density_alt_seek - alt_range / 2., 0),
                         density_alt_seek + alt_range / 2. + alt_inc, alt_inc):
            temp_c = density_alt2temp(density_alt_seek,
                                      alt,
                                      alt_units=alt_units)
            temp_f = U.temp_conv(temp_c, from_units='C', to_units='F')
            alt_str = L.format('%.*f', (0, alt), grouping=True)
            temp_c_str = '%.1f' % temp_c
            temp_f_str = '%.1f' % temp_f
            line_buffer.append(
                alt_str.rjust(6) + temp_c_str.rjust(11) + temp_f_str.rjust(10))
    else:
        for alt in range(max(density_alt_seek - alt_range / 2., 0),
                         density_alt_seek + alt_range / 2. + alt_inc, alt_inc):
            alt_str = L.format('%.*f', (0, alt), grouping=True)
            temp_str = '%.1f' % density_alt2temp(density_alt_seek,
                                                 alt,
                                                 temp_units=temp_units,
                                                 alt_units=alt_units)
            line_buffer.append(alt_str.rjust(6) + temp_str.rjust(11))

    if file != '':
        OUT = open(file, 'w')
        for line in line_buffer:
            OUT.write(line + '\n')

        print('file selected')
    else:
        return '\n'.join(line_buffer)