def test_02(self): # Truth values from NASA RP 1046 Value = SA.alt2density_ratio(8000) Truth = 0.06011 / 0.076474 self.assertTrue(RE(Value, Truth) <= 1e-5)
def test_02(self): # Truth values from NASA RP 1046 Value = SA.alt2density_ratio(8000) Truth = 0.06011 / 0.076474 self.failUnless(RE(Value, Truth) <= 1e-5)
def test_03(self): # truth value calculated from program at: # http://www.sworld.com.au/steven/space/atmosphere/ Value = SA.alt2density_ratio(1999, alt_units='m') Truth = .82168 self.assertTrue(RE(Value, Truth) <= 5e-5)
def test_03(self): # truth value calculated from program at: # http://www.sworld.com.au/steven/space/atmosphere/ Value = SA.alt2density_ratio(1999, alt_units='m') Truth = .82168 self.failUnless(RE(Value, Truth) <= 5e-5)
def _pwr_std_temp(rpm, MP, altitude): """ Returns the power at a given rpm, MP and altitude, assuming standard temperature. Units are n/mn, inches HG and feet. Example: Determine power at 2700 rpm, 28.6 inches HG manifold pressure and 0 ft altitude: >>> _pwr_std_temp(2700, 28.2, 0) 179.78232558139536 """ # get the power at sea level (i.e. point B on the left side of the # Lycoming power chart) # get pwr at two even hundreds of rpm, and then interpolate if rpm >= 2600: rpm1 = 2600 rpm2 = 2700 elif rpm >= 2400: rpm1 = 2400 rpm2 = 2600 elif rpm >= 2200: rpm1 = 2200 rpm2 = 2400 else: rpm1 = 2000 rpm2 = 2400 pwr_SL1 = _pwr_sl(rpm1, MP) pwr_SL2 = _pwr_sl(rpm2, MP) # get power at full throttle at this rpm and MP at altitude (i.e. point A on the right side of the Lycoming power chart) # density ratio at point A on the right side of the Lycoming power chart) pwr_FT1, DR_FT1 = _hp_at_MP_and_altitude(rpm1, MP) pwr_FT2, DR_FT2 = _hp_at_MP_and_altitude(rpm2, MP) # density ratio at sea level DR_sl = 1 # density ratio for the actual conditions (i.e. point D on the right side # of the Lycoming power chart) DR_test = SA.alt2density_ratio(altitude) pwr_std_temp1 = pwr_SL1 + (DR_test - DR_sl) * \ (pwr_FT1 - pwr_SL1) / (DR_FT1 - DR_sl) pwr_std_temp2 = pwr_SL2 + (DR_test - DR_sl) * \ (pwr_FT2 - pwr_SL2) / (DR_FT2 - DR_sl) pwr_std_temp = pwr_std_temp1 + \ (rpm - rpm1) * (pwr_std_temp2 - pwr_std_temp1) / (rpm2 - rpm1) return pwr_std_temp
def _hp_at_FT(rpm, altitude): """ Returns the horsepower at full throttle at a given rpm and altitude. """ # get density ratio from the altitude DR = SA.alt2density_ratio(altitude) # get power at rpm hp_sl = HP_FT[rpm]['hp_sl'] hp_23K = HP_FT[rpm]['hp_23K'] hp_at_FT = hp_sl - (1 - DR) * (hp_sl - hp_23K) / (1 - DR_23K) return hp_at_FT
def _pwr_std_temp(rpm, MP, altitude): """ Returns the power at a given rpm, MP and altitude, assuming standard temperature. Units are n/mn, inches HG and feet. Example: Determine power at 2700 rpm, 28.6 inches HG manifold pressure and 0 ft altitude: >>> _pwr_std_temp(2700, 28.2, 0) 179.78232558139536 """ # get the power at sea level (i.e. point B on the left side of the Lycoming power chart) # get pwr at two even hundreds of rpm, and then interpolate if rpm >= 2600: rpm1 = 2600 rpm2 = 2700 elif rpm >= 2400: rpm1 = 2400 rpm2 = 2600 elif rpm >= 2200: rpm1 = 2200 rpm2 = 2400 else: rpm1 = 2000 rpm2 = 2400 pwr_SL1 = _pwr_sl(rpm1, MP) pwr_SL2 = _pwr_sl(rpm2, MP) # get power at full throttle at this rpm and MP at altitude (i.e. point A on the right side of the Lycoming power chart) # density ratio at point A on the right side of the Lycoming power chart) pwr_FT1, DR_FT1 = _hp_at_MP_and_altitude(rpm1, MP) pwr_FT2, DR_FT2 = _hp_at_MP_and_altitude(rpm2, MP) # density ratio at sea level DR_sl = 1 # density ratio for the actual conditions (i.e. point D on the right side of the Lycoming power chart) DR_test = SA.alt2density_ratio(altitude) pwr_std_temp1 = pwr_SL1 + (DR_test - DR_sl) * (pwr_FT1 - pwr_SL1) / (DR_FT1 - DR_sl) pwr_std_temp2 = pwr_SL2 + (DR_test - DR_sl) * (pwr_FT2 - pwr_SL2) / (DR_FT2 - DR_sl) pwr_std_temp = pwr_std_temp1 + (rpm - rpm1) * (pwr_std_temp2 - pwr_std_temp1) / (rpm2 - rpm1) return pwr_std_temp
def test_01(self): PR = SA.alt2density_ratio(0) self.assertEqual(PR, 1)
def _pwr_std_temp(rpm, MP, altitude): """ Returns the power at a given rpm, MP and altitude, assuming standard temperature. Units are n/mn, inches HG and feet. Example: Determine power at 2700 rpm, 28.6 inches HG manifold pressure and 0 ft altitude: >>> _pwr_std_temp(2700, 28.6, 0) 200.0 """ # get the power at sea level (i.e. point B on the left side of the Lycoming power chart) # get pwr at two even hundreds of rpm, and then interpolate if rpm >= 2600: rpm1 = 2600 elif rpm <= 1800: rpm1 = 1800 else: rpm1 = rpm - rpm % 100 rpm2 = rpm1 + 100 pwr_SL1 = _pwr_sl(rpm1, MP) pwr_SL2 = _pwr_sl(rpm2, MP) # print "SL Pwr 1=", pwr_SL1 # print "SL Pwr 2=", pwr_SL2 # get power at full throttle at this rpm and MP at altitude (i.e. point A on the right side of the Lycoming power chart) # density ratio at point A on the right side of the Lycoming power chart) pwr_FT1, DR_FT1 = _hp_at_MP_and_altitude(rpm1, MP) pwr_FT2, DR_FT2 = _hp_at_MP_and_altitude(rpm2, MP) # print "FT pwr 1=", pwr_FT1 # print "FT pwr 2=", pwr_FT2 # print "DR FT 1=", DR_FT1 # print "DR FT 2=", DR_FT2 # density ratio at sea level DR_sl = 1 # density ratio for the actual conditions (i.e. point D on the right side of the Lycoming power chart) DR_test = SA.alt2density_ratio(altitude) # print "DR_test=", DR_test # function is unstable if the DR at FT is close to 1. This sends the slope off to unpredictable values. slope1 = (pwr_FT1 - pwr_SL1) / (DR_FT1 - DR_sl) slope2 = (pwr_FT2 - pwr_SL2) / (DR_FT2 - DR_sl) if MP > 28: if slope1 < -80: slope1 = -62 elif slope1 > -60: slope1 = -62 if slope2 < -80: slope2 = -62 elif slope2 > -60: slope2 = -62 # print "slope1=", slope1 # print "slope2=", slope2 pwr_std_temp1 = pwr_SL1 + (DR_test - DR_sl) * slope1 pwr_std_temp2 = pwr_SL2 + (DR_test - DR_sl) * slope2 # print "Pwr Std Temp 1=", pwr_std_temp1 # print "Pwr Std Temp 2=", pwr_std_temp2 pwr_std_temp = pwr_std_temp1 + (rpm - rpm1) * ( pwr_std_temp2 - pwr_std_temp1) / (rpm2 - rpm1) return pwr_std_temp
#! /usr/bin/env python import std_atm as SA FF0 = 17 for alt in range(0, 20001, 2000): sigma = SA.alt2density_ratio(alt) FF = sigma * FF0 * 2700 / 2700 print "%.0f %.1f" % (alt, FF)
def _pwr_std_temp(rpm, MP, altitude): """ Returns the power at a given rpm, MP and altitude, assuming standard temperature. Units are n/mn, inches HG and feet. Example: Determine power at 2700 rpm, 28.6 inches HG manifold pressure and 0 ft altitude: >>> _pwr_std_temp(2700, 28.6, 0) 200.0 """ # get the power at sea level (i.e. point B on the left side of the Lycoming power chart) # get pwr at two even hundreds of rpm, and then interpolate if rpm >= 2600: rpm1 = 2600 elif rpm <= 1800: rpm1 = 1800 else: rpm1 = rpm - rpm % 100 rpm2 = rpm1 + 100 pwr_SL1 = _pwr_sl(rpm1, MP) pwr_SL2 = _pwr_sl(rpm2, MP) # print "SL Pwr 1=", pwr_SL1 # print "SL Pwr 2=", pwr_SL2 # get power at full throttle at this rpm and MP at altitude (i.e. point A on the right side of the Lycoming power chart) # density ratio at point A on the right side of the Lycoming power chart) pwr_FT1, DR_FT1 = _hp_at_MP_and_altitude(rpm1, MP) pwr_FT2, DR_FT2 = _hp_at_MP_and_altitude(rpm2, MP) # print "FT pwr 1=", pwr_FT1 # print "FT pwr 2=", pwr_FT2 # print "DR FT 1=", DR_FT1 # print "DR FT 2=", DR_FT2 # density ratio at sea level DR_sl = 1 # density ratio for the actual conditions (i.e. point D on the right side of the Lycoming power chart) DR_test = SA.alt2density_ratio(altitude) # print "DR_test=", DR_test # function is unstable if the DR at FT is close to 1. This sends the slope off to unpredictable values. slope1=(pwr_FT1 - pwr_SL1) / (DR_FT1 - DR_sl) slope2=(pwr_FT2 - pwr_SL2) / (DR_FT2 - DR_sl) if MP > 28: if slope1 < -80: slope1=-62 elif slope1> -60: slope1=-62 if slope2< -80: slope2 = -62 elif slope2> -60: slope2=-62 # print "slope1=", slope1 # print "slope2=", slope2 pwr_std_temp1 = pwr_SL1 + (DR_test - DR_sl) * slope1 pwr_std_temp2 = pwr_SL2 + (DR_test - DR_sl) * slope2 # print "Pwr Std Temp 1=", pwr_std_temp1 # print "Pwr Std Temp 2=", pwr_std_temp2 pwr_std_temp = pwr_std_temp1 + (rpm - rpm1) * (pwr_std_temp2 - pwr_std_temp1) / (rpm2 - rpm1) return pwr_std_temp
def cruise_data(prop, cruise_A=821.27884302, cruise_B=3.8201670757e-05,\ cruise_power = 130, mixture = 'econ', cruise_rpm=2400, climb_weight=1800., \ descent_weight=1600., alt_max=20000., fuel_units='USG', alt_interval=500., \ isa_dev=0, temp_units='C', rv='8', wing_area=110., TO_fuel = 1.5, TO_dist = 0, \ climb_pwr = 'max', climb_pwr_factor=0.9, \ climb_speed = 'norm', \ descent_tas=180, descent_ROD=-500., descent_angle='', speed_units='kt', \ descent_rpm=2100., descent_sfc=0.45, fuel_reserve=8, output='raw'): """ Returns a table of cruise performance vs altitude. The items in each row of the table are altitude, time, fuel burned and distance. Time is in units of minutes, rounded to the nearest half minute. Fuel units are selectable, with a default of USG. Distances are in nm. The output may be specified as raw, latex (a LaTeX table for the POH), data (suitable for graphing with gnuplot) or array. descent_angle is the flight path angle in degrees. It overrides the rate of descent (ROD) if provided. """ Wt_std = 1800. Wt_ratio = climb_weight/Wt_std climb_data_a = climb_data(prop, weight = climb_weight, alt_max = alt_max, TO_fuel = TO_fuel, TO_dist = TO_dist, fuel_units = fuel_units, alt_interval = alt_interval, isa_dev = isa_dev, temp_units = temp_units, rv = rv, wing_area = wing_area, pwr = climb_pwr, pwr_factor=climb_pwr_factor, climb_speed = climb_speed, output = 'array') descent_data_a = descent_data(prop, weight=descent_weight, alt_max=alt_max, fuel_units=fuel_units, alt_interval=alt_interval, isa_dev=isa_dev, temp_units=temp_units, rv=rv, wing_area=wing_area, tas=descent_tas, ROD=descent_ROD, angle=descent_angle, speed_units=speed_units, rpm=descent_rpm, sfc=descent_sfc, output='array') cruise_ff = IO.pwr2ff(cruise_power, cruise_rpm, mixture=mixture) if output == 'raw': print "Climb speed = %s and climb power = %s" % (climb_speed, climb_pwr) print "Cruise power = %.0f and cruise fuel flow = %.2f" % (cruise_power, cruise_ff) print S.center('Altitude', 10), print S.center('TAS', 10), print S.center('Range', 10), print S.center('Fuel Flow', 10) print S.center('(ft)', 10), print S.center('(kt)', 10), print S.center('(nm)', 10), print S.center('(USG/h)', 10) elif output == 'array': array = [] elif output == 'data': if descent_angle: angle_text = 'a descent angle of %s degrees' % descent_angle else: angle_text = 'a descent rate of %i ft/mn' % descent_ROD print '# RV-8 Cruise Range Chart - Wheel Pants OFF' print '# descent at %.0f KTAS and %s' % (descent_tas, angle_text) print '# %i%% power with fuel flow of %.1f USG/hr' % (cruise_power / 2., cruise_ff) print '# altitude range' print '# ft KTAS' elif output == 'latex': line.append(str(locale.format('%.0f', calt, True))) line.append(str(locale.format('%.1f', cruise_tas))) line.append(str(locale.format('%.0f', total_dist))) # print line pass for item in zip(climb_data_a, descent_data_a): ([calt, ctime, cfuel, cdist], [dalt, dtime, dfuel, ddist]) = item cruise_fuel = 42 - (cfuel + dfuel + fuel_reserve) sigma = SA.alt2density_ratio(calt) cruise_tas = 1/12.*((8*cruise_A*cruise_B*Wt_ratio**2 + (3.*M.sqrt(3.)*cruise_power**2*sigma + M.sqrt(-256*cruise_A**3.*cruise_B*Wt_ratio**6 + 27.*cruise_power**4*sigma**2))**(2/3.)*2**(1/3.)*cruise_B**(2/3.))**(3./4)*3.**(3./4)*sigma**(1/4.) + M.sqrt(12.*M.sqrt(3.*M.sqrt(3.)*cruise_power**2*sigma + M.sqrt(-256*cruise_A**3.*cruise_B*Wt_ratio**6 + 27.*cruise_power**4*sigma**2))*cruise_B**(1/3.)*cruise_power*sigma - M.sqrt(8*cruise_A*cruise_B*Wt_ratio**2 + (3.*M.sqrt(3.)*cruise_power**2*sigma + M.sqrt(-256*cruise_A**3.*cruise_B*Wt_ratio**6 + 27.*cruise_power**4*sigma**2))**(2/3.)*2**(1/3.)*cruise_B**(2/3.))*(8*3.**(1/4.)*cruise_A*cruise_B**(1/3.)*Wt_ratio**2*M.sqrt(sigma) + (3.*M.sqrt(3.)*cruise_power**2*sigma + M.sqrt(-256*cruise_A**3.*cruise_B*Wt_ratio**6 + 27.*cruise_power**4*sigma**2))**(2/3.)*2**(1/3.)*3.**(1/4.)*M.sqrt(sigma)))*3.**(5/8)*abs(cruise_B)**(1/3.))*2**(2/3.)/((3.*M.sqrt(3.)*cruise_power**2*sigma + M.sqrt(-256*cruise_A**3.*cruise_B*Wt_ratio**6 + 27.*cruise_power**4*sigma**2))**(1/6.)*(8*cruise_A*cruise_B*Wt_ratio**2 + (3.*M.sqrt(3.)*cruise_power**2*sigma + M.sqrt(-256*cruise_A**3.*cruise_B*Wt_ratio**6 + 27.*cruise_power**4*sigma**2))**(2/3.)*2**(1/3.)*cruise_B**(2/3.))**(1/4.)*cruise_B**(2/3.)*sigma**(3./4)) cruise_dist = cruise_fuel * cruise_tas / cruise_ff cruise_time = cruise_dist / cruise_tas * 60 total_dist = cdist + cruise_dist + ddist total_time = ctime + cruise_time + dtime # print "%5.0f %.1f %.1f %.1f %5.1f" % (calt, cruise_tas, cruise_fuel, cruise_dist, total_dist) if output == 'raw': print S.rjust(locale.format('%.0f', calt, True), 7), print S.rjust('%.1f' % (cruise_tas), 10), print S.rjust('%.1f' % (total_dist), 10), print S.rjust('%.1f' % (cruise_ff), 10) elif output == 'data': print '%.1f\t%.0f' % (total_dist, calt)
def cruise_data(prop, cruise_A=821.27884302, cruise_B=3.8201670757e-05,\ cruise_power = 130, mixture = 'econ', cruise_rpm=2400, climb_weight=1800., \ descent_weight=1600., alt_max=20000., fuel_units='USG', alt_interval=500., \ isa_dev=0, temp_units='C', rv='8', wing_area=110., TO_fuel = 1.5, TO_dist = 0, \ climb_pwr = 'max', climb_pwr_factor=0.9, \ climb_speed = 'norm', \ descent_tas=180, descent_ROD=-500., descent_angle='', speed_units='kt', \ descent_rpm=2100., descent_sfc=0.45, fuel_reserve=8, output='raw'): """ Returns a table of cruise performance vs altitude. The items in each row of the table are altitude, time, fuel burned and distance. Time is in units of minutes, rounded to the nearest half minute. Fuel units are selectable, with a default of USG. Distances are in nm. The output may be specified as raw, latex (a LaTeX table for the POH), data (suitable for graphing with gnuplot) or array. descent_angle is the flight path angle in degrees. It overrides the rate of descent (ROD) if provided. """ Wt_std = 1800. Wt_ratio = climb_weight / Wt_std climb_data_a = climb_data(prop, weight=climb_weight, alt_max=alt_max, TO_fuel=TO_fuel, TO_dist=TO_dist, fuel_units=fuel_units, alt_interval=alt_interval, isa_dev=isa_dev, temp_units=temp_units, rv=rv, wing_area=wing_area, pwr=climb_pwr, pwr_factor=climb_pwr_factor, climb_speed=climb_speed, output='array') descent_data_a = descent_data(prop, weight=descent_weight, alt_max=alt_max, fuel_units=fuel_units, alt_interval=alt_interval, isa_dev=isa_dev, temp_units=temp_units, rv=rv, wing_area=wing_area, tas=descent_tas, ROD=descent_ROD, angle=descent_angle, speed_units=speed_units, rpm=descent_rpm, sfc=descent_sfc, output='array') cruise_ff = IO.pwr2ff(cruise_power, cruise_rpm, mixture=mixture) if output == 'raw': print("Climb speed = %s and climb power = %s" % (climb_speed, climb_pwr)) print("Cruise power = %.0f and cruise fuel flow = %.2f" % (cruise_power, cruise_ff)) print(S.center('Altitude', 10), end=' ') print(S.center('TAS', 10), end=' ') print(S.center('Range', 10), end=' ') print(S.center('Fuel Flow', 10)) print(S.center('(ft)', 10), end=' ') print(S.center('(kt)', 10), end=' ') print(S.center('(nm)', 10), end=' ') print(S.center('(USG/h)', 10)) elif output == 'array': array = [] elif output == 'data': if descent_angle: angle_text = 'a descent angle of %s degrees' % descent_angle else: angle_text = 'a descent rate of %i ft/mn' % descent_ROD print('# RV-8 Cruise Range Chart - Wheel Pants OFF') print('# descent at %.0f KTAS and %s' % (descent_tas, angle_text)) print('# %i%% power with fuel flow of %.1f USG/hr' % (cruise_power / 2., cruise_ff)) print('# altitude range') print('# ft KTAS') elif output == 'latex': line.append(str(locale.format('%.0f', calt, True))) line.append(str(locale.format('%.1f', cruise_tas))) line.append(str(locale.format('%.0f', total_dist))) # print line pass for item in zip(climb_data_a, descent_data_a): ([calt, ctime, cfuel, cdist], [dalt, dtime, dfuel, ddist]) = item cruise_fuel = 42 - (cfuel + dfuel + fuel_reserve) sigma = SA.alt2density_ratio(calt) cruise_tas = 1 / 12. * ( (8 * cruise_A * cruise_B * Wt_ratio**2 + (3. * M.sqrt(3.) * cruise_power**2 * sigma + M.sqrt(-256 * cruise_A**3. * cruise_B * Wt_ratio**6 + 27. * cruise_power**4 * sigma**2))**(2 / 3.) * 2** (1 / 3.) * cruise_B**(2 / 3.))**(3. / 4) * 3.**(3. / 4) * sigma** (1 / 4.) + M.sqrt( 12. * M.sqrt(3. * M.sqrt(3.) * cruise_power**2 * sigma + M.sqrt(-256 * cruise_A**3. * cruise_B * Wt_ratio**6 + 27. * cruise_power**4 * sigma**2)) * cruise_B** (1 / 3.) * cruise_power * sigma - M.sqrt(8 * cruise_A * cruise_B * Wt_ratio**2 + (3. * M.sqrt(3.) * cruise_power**2 * sigma + M.sqrt(-256 * cruise_A**3. * cruise_B * Wt_ratio**6 + 27. * cruise_power**4 * sigma**2))** (2 / 3.) * 2**(1 / 3.) * cruise_B**(2 / 3.)) * (8 * 3.**(1 / 4.) * cruise_A * cruise_B** (1 / 3.) * Wt_ratio**2 * M.sqrt(sigma) + (3. * M.sqrt(3.) * cruise_power**2 * sigma + M.sqrt(-256 * cruise_A**3. * cruise_B * Wt_ratio**6 + 27. * cruise_power**4 * sigma**2))**(2 / 3.) * 2** (1 / 3.) * 3.**(1 / 4.) * M.sqrt(sigma))) * 3.** (5 / 8) * abs(cruise_B)**(1 / 3.)) * 2**(2 / 3.) / ( (3. * M.sqrt(3.) * cruise_power**2 * sigma + M.sqrt(-256 * cruise_A**3. * cruise_B * Wt_ratio**6 + 27. * cruise_power**4 * sigma**2))**(1 / 6.) * (8 * cruise_A * cruise_B * Wt_ratio**2 + (3. * M.sqrt(3.) * cruise_power**2 * sigma + M.sqrt(-256 * cruise_A**3. * cruise_B * Wt_ratio**6 + 27. * cruise_power**4 * sigma**2))**(2 / 3.) * 2** (1 / 3.) * cruise_B**(2 / 3.))**(1 / 4.) * cruise_B**(2 / 3.) * sigma**(3. / 4)) cruise_dist = cruise_fuel * cruise_tas / cruise_ff cruise_time = cruise_dist / cruise_tas * 60 total_dist = cdist + cruise_dist + ddist total_time = ctime + cruise_time + dtime # print "%5.0f %.1f %.1f %.1f %5.1f" % (calt, cruise_tas, cruise_fuel, cruise_dist, total_dist) if output == 'raw': print(S.rjust(locale.format('%.0f', calt, True), 7), end=' ') print(S.rjust('%.1f' % (cruise_tas), 10), end=' ') print(S.rjust('%.1f' % (total_dist), 10), end=' ') print(S.rjust('%.1f' % (cruise_ff), 10)) elif output == 'data': print('%.1f\t%.0f' % (total_dist, calt))