def gps2stall(GS, TK, Hp, T, temp_units='C', alt_units=default_alt_units, speed_units=default_speed_units, GPS_units=default_speed_units): """ Return the CAS at the stall, given four GPS ground speeds, GPS tracks, altitude and temperature. speed_units = CAS units. May be 'kt', 'mph', 'km/h', 'm/s' and 'ft/s'. GS_units = GPS ground speed units. 'kt', 'mph', 'km/h', 'm/s' and 'ft/s'. HP = pressure altitude. May be feet ('ft'), metres ('m'), kilometres ('km'), statute miles, ('sm') or nautical miles ('nm'). The temperature may be in deg C, F, K or R. If the units are not specified, the units in default_units.py are used. Conduct four stalls, in a four sided box pattern, with all stalls at the same pressure altitude. The data reduction algorithm calculates the TAS using Doug Gray's GPS to TAS method, using four different combinations of three legs. If the data quality is high, all four TAS values will be similar, with a low standard deviation. The TAS at the stall is converted to CAS, using pressure altitude and temperature. Returns CAS and standard deviation of the CAS value. Examples: >>> gps2stall([44, 104, 157, 131], [258, 29, 91, 150], 7000, 14, temp_units='F', GPS_units='km/h') (50.322235245211225, 0.6583323098888626) """ tas, std_dev = SSEC.gps2tas(GS, TK, verbose=1) tas = U.speed_conv(tas, from_units=GPS_units, to_units=speed_units) std_dev = U.speed_conv(std_dev, from_units=GPS_units, to_units=speed_units) cas = A.tas2cas(tas, Hp, T, temp_units=temp_units, alt_units=alt_units, speed_units=speed_units) std_dev = std_dev * cas / tas # factor standard deviation to reference CAS vs TAS return cas, std_dev
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 cd2drag( Cd, eas, wing_area, speed_units=default_speed_units, area_units=default_area_units, drag_units=default_weight_units, ): """ Returns the drag, given coefficient of drag, equivalent airspeed, and wing area. Example: >>> cd2drag(.138, 100, 10, speed_units='km/h', area_units='m**2',\ drag_units='N') 652.1990740740742 """ eas = U.speed_conv(eas, from_units=speed_units, to_units='m/s') wing_area = U.area_conv(wing_area, from_units=area_units, to_units='m**2') drag = (((0.5 * Rho0) * eas**2) * wing_area) * Cd drag = U.force_conv(drag, from_units='N', to_units=drag_units) return drag
def _pwr_error(eas_guess, altitude, weight, power, rpm, temp='std', temp_units='C', rv='F1', wing_area=102, speed_units='kt', flap=0, prop_eff=0.78): tas_guess = A.eas2tas(eas_guess, altitude, temp=temp, speed_units=speed_units) tas_guess_fts = U.speed_conv(tas_guess, from_units=speed_units, to_units='ft/s') power_avail = power * prop_eff drag = F.eas2drag(eas_guess, weight, wing_area, rv=rv, flap=flap, speed_units=speed_units) power_req = tas_guess_fts * drag / 550. error = power_avail - power_req return error, tas_guess
def cl2cas( Cl, altitude, weight, wing_area, load_factor=1, speed_units=default_speed_units, alt_units=default_alt_units, weight_units=default_weight_units, area_units=default_area_units, ): """ Returns the calibrated airspeed, given coefficient of lift, altitude, weight, and wing area. Load factor is an optional input. The load factor, if not provided, defaults to 1. """ weight = U.wt_conv(weight, from_units=weight_units, to_units='kg') wing_area = U.area_conv(wing_area, from_units=area_units, to_units='m**2') eas = ((((2. * weight) * g) * load_factor) / ((Rho0 * wing_area) * Cl))**0.5 eas = U.speed_conv(eas, from_units='m/s', to_units=speed_units) cas = A.eas2cas(eas, altitude, speed_units, alt_units) return cas
def cd2drag( Cd, eas, wing_area, speed_units=default_speed_units, area_units=default_area_units, drag_units=default_weight_units, ): """ Returns the drag, given coefficient of drag, equivalent airspeed, and wing area. Example: >>> cd2drag(.138, 100, 10, speed_units='km/h', area_units='m**2',\ drag_units='N') 652.19907407407425 """ eas = U.speed_conv(eas, from_units=speed_units, to_units='m/s') wing_area = U.area_conv(wing_area, from_units=area_units, to_units='m**2') drag = (((0.5 * Rho0) * eas ** 2) * wing_area) * Cd drag = U.force_conv(drag, from_units='N', to_units=drag_units) return drag
def aoc(prop, altitude, eas, weight, power, rpm, temp = 'std', temp_units = 'C', \ rv = '8', wing_area = 110, speed_units = 'kt', flap = 0): """ Returns the climb or descent gradient. Runs with zero power at 1800 lb show best glide speed for RV-8 of: 98 kt at 1800 lb 86 kt at 1400 lb best angle of climb: 66 kt at 1800 lb """ tas = A.eas2tas(eas, altitude, temp = temp, speed_units = speed_units) tas_fts = U.speed_conv(tas, from_units = speed_units, to_units = 'ft/s') prop_eff = PM.prop_eff(prop, power, rpm, tas, altitude, temp = temp, \ temp_units = 'C', speed_units = speed_units) power_avail = power * prop_eff drag = FT.eas2drag(eas, weight, wing_area, rv = rv, flap = flap, \ speed_units = speed_units) power_req = tas_fts * drag / 550. excess_power = power_avail - power_req roc = excess_power * 550 / weight gradient = roc / tas_fts return gradient
def tip_mach(tas, rpm, temperature, dia, speed_units='kt', temp_units='C', dia_units='in'): """ Returns the mach number of the propeller blade tip, given the true airspeed (tas), revolutions per minute (rpm), temperature and propeller diameter. The speed units may be specified as "kt", "mph", "km/h" or "ft/s", but default to "kt" if not specified. The temperature units may be specified as "C", "F", "K" or "R", but default to deg C if not specified. The diameter units may be specified as "in", "ft", or "m", but default to inches if not specified. """ dia = U.length_conv(dia, from_units=dia_units, to_units='m') tas = U.speed_conv(tas, from_units=speed_units, to_units='m/s') speed_of_sound = SA.temp2speed_of_sound(temperature, temp_units=temp_units, speed_units='m/s') rotation_speed = dia * rpm * M.pi / 60 tip_speed = M.sqrt(tas**2 + rotation_speed**2) tip_mach = tip_speed / speed_of_sound return tip_mach
def run_tests(cd0 = 0.0221, e = 0.825): """ Run all test cases to compare drag polar model against CAFE results. """ sum_sq = 0 wt_value_tot = 0 wing_area = 110 prop = PM.Prop('7666-4RV') for n, test in enumerate(level_test_cases): tas = test[0] dalt = test[1] wt = test[2] rpm = test[3] mp = test[4] wt_value = test[5] eas = A.tas2eas(tas, dalt, speed_units = 'mph') tas_fts = U.speed_conv(tas, from_units = 'mph', to_units = 'ft/s') cl = FT.eas2cl(eas, wt, wing_area, speed_units = 'mph') cd = FT.cl2cd_test(cl, cd0, e, flap = 0) drag = FT.cd2drag(cd, eas, wing_area, speed_units = 'mph') drag_power = drag * tas_fts / 550. power = IO.pwr(rpm, mp, dalt) prop_eff = PM.prop_eff(prop, power, rpm, tas, dalt, speed_units = 'mph') thrust_power = power * prop_eff excess_power = thrust_power - drag_power print 'Case', n, 'Altitude =', dalt, 'TAS =', tas, 'EAS =', eas, 'Excess power =', excess_power sum_sq += wt_value * excess_power ** 2 wt_value_tot += wt_value print 'Average sum of squares of excess power =', \ (sum_sq / wt_value_tot) ** 0.5
def cl2lift( Cl, eas, wing_area, speed_units=default_speed_units, lift_units=default_weight_units, area_units=default_area_units, ): """ Returns the lift, given coefficient of lift, equivalent airspeed, and wing area. """ eas = U.speed_conv(eas, from_units=speed_units, to_units='m/s') wing_area = U.area_conv(wing_area, from_units=area_units, to_units='m**2') lift = (((0.5 * Rho0) * eas ** 2.) * wing_area) * Cl if lift_units == 'kg': lift = U.force_conv(lift, 'N', 'lb') lift = U.mass_conv(lift, 'lb', 'kg') else: lift = U.force_conv(lift, 'N', lift_units) return lift
def aoc(prop, altitude, eas, weight, power, rpm, temp = 'std', temp_units = 'C', \ rv = '8', wing_area = 110, speed_units = 'kt', flap = 0): """ Returns the climb or descent gradient. Runs with zero power at 1800 lb show best glide speed for RV-8 of: 98 kt at 1800 lb 86 kt at 1400 lb best angle of climb: 66 kt at 1800 lb """ tas = A.eas2tas(eas, altitude, temp=temp, speed_units=speed_units) tas_fts = U.speed_conv(tas, from_units=speed_units, to_units='ft/s') prop_eff = PM.prop_eff(prop, power, rpm, tas, altitude, temp = temp, \ temp_units = 'C', speed_units = speed_units) power_avail = power * prop_eff drag = FT.eas2drag(eas, weight, wing_area, rv = rv, flap = flap, \ speed_units = speed_units) power_req = tas_fts * drag / 550. excess_power = power_avail - power_req roc = excess_power * 550 / weight gradient = roc / tas_fts return gradient
def cl2cas( Cl, altitude, weight, wing_area, load_factor=1, speed_units=default_speed_units, alt_units=default_alt_units, weight_units=default_weight_units, area_units=default_area_units, ): """ Returns the calibrated airspeed, given coefficient of lift, altitude, weight, and wing area. Load factor is an optional input. The load factor, if not provided, defaults to 1. """ weight = U.wt_conv(weight, from_units=weight_units, to_units='kg') wing_area = U.area_conv(wing_area, from_units=area_units, to_units='m**2') eas = ((((2. * weight) * g) * load_factor) / ((Rho0 * wing_area) * Cl)) ** 0.5 eas = U.speed_conv(eas, from_units='m/s', to_units=speed_units) cas = A.eas2cas(eas, altitude, speed_units, alt_units) return cas
def eff2thrust(eff, bhp, TAS, power_units="hp", speed_units="kt", thrust_units="lb"): """ Returns thrust, given prop efficiency, true airspeed and brake power. Matches the results from the Hartzell prop map program fairly closely. """ TAS = U.speed_conv(TAS, from_units=speed_units, to_units="m/s") bhp = U.power_conv(bhp, from_units=power_units, to_units="W") thrust = eff * bhp / TAS return U.force_conv(thrust, from_units="N", to_units=thrust_units)
def eff2thrust(eff, bhp, TAS, power_units='hp', speed_units='kt', thrust_units='lb'): """ Returns thrust, given prop efficiency, true airspeed and brake power. Matches the results from the Hartzell prop map program fairly closely. """ TAS = U.speed_conv(TAS, from_units=speed_units, to_units='m/s') bhp = U.power_conv(bhp, from_units=power_units, to_units='W') thrust = eff * bhp / TAS return U.force_conv(thrust, from_units='N', to_units=thrust_units)
def _pwr_error(eas_guess, altitude, weight, power, rpm, temp = 'std', \ temp_units = 'C', rv = 'F1', wing_area = 102, speed_units = 'kt', \ flap = 0, prop_eff=0.78): tas_guess = A.eas2tas(eas_guess, altitude, temp = temp, \ speed_units = speed_units) tas_guess_fts = U.speed_conv(tas_guess, from_units = speed_units, \ to_units = 'ft/s') power_avail = power * prop_eff drag = F.eas2drag(eas_guess, weight, wing_area, rv = rv, flap = flap,\ speed_units = speed_units) power_req = tas_guess_fts * drag / 550. error = power_avail - power_req return error, tas_guess
def _pwr_error(prop, eas_guess, altitude, weight, power, rpm, temp = 'std', \ temp_units = 'C', rv = '8', wing_area = 110, speed_units = 'kt', \ flap = 0, wheel_pants = 1): tas_guess = A.eas2tas(eas_guess, altitude, temp = temp, \ speed_units = speed_units, temp_units = temp_units) tas_guess_fts = U.speed_conv(tas_guess, from_units = speed_units, \ to_units = 'ft/s') prop_eff = PM.prop_eff(prop, power, rpm, tas_guess, altitude, temp = temp,\ temp_units = temp_units, speed_units = speed_units) power_avail = power * prop_eff drag = FT.eas2drag(eas_guess, weight, wing_area, rv = rv, flap = flap,\ speed_units = speed_units, wheel_pants = wheel_pants) power_req = tas_guess_fts * drag / 550. error = power_avail - power_req return error, tas_guess
def Re(V, L, KV, speed_units=default_speed_units, length_units=default_length_units, #kinematic_viscosity_units=default_kinematic_viscosity_units ): """ Return Reynold's number, given velocity, characteristic length and kinematic viscosity' V = velocity L = characteristic length KV = kinematic viscosity """ V = U.speed_conv(V, speed_units, 'm/s') L = U.length_conv(L, length_units, 'm') # KV = U.kinematic_viscosity_conv(KV, kinematic_viscosity_units, '??') Re = V * L / KV return Re
def advance_ratio(tas, rpm, dia, speed_units="kt", dia_units="in"): """ Returns the propeller advance ratio, J, given the revolutions per minute (rpm), true airspeed (tas), temperature and propeller diameter. The advance ratio is the forward distance that the propeller advances during one revolution, divided by the diameter of the propeller. The diameter units may be specified as "in", "ft", or "m", but default to inches if not specified. """ tas = U.speed_conv(tas, from_units=speed_units, to_units="m/s") dia = U.length_conv(dia, from_units=dia_units, to_units="m") advance_ratio = tas * 60.0 / (rpm * dia) return advance_ratio
def p(tas, dalt, wt, rpm, mp, prop): """ test function for RV-8A drag, to compare against CAFE foundation level flt data. prop is a prop_map.Prop instance. """ eas = A.tas2eas(tas, dalt, speed_units='mph') # tas = A.cas2tas(cas, dalt, speed_units = 'mph') tas_fts = U.speed_conv(tas, from_units='mph', to_units='ft/s') drag = FT.eas2drag(eas, wt, 110, rv='8a', speed_units='mph') drag_power = drag * tas_fts / 550. power = IO.pwr(rpm, mp, dalt) prop_eff = PM.prop_eff(prop, power, rpm, tas, dalt, speed_units='mph') thrust_power = power * prop_eff excess_power = thrust_power - drag_power return excess_power
def advance_ratio(tas, rpm, dia, speed_units='kt', dia_units='in'): """ Returns the propeller advance ratio, J, given the revolutions per minute (rpm), true airspeed (tas), temperature and propeller diameter. The advance ratio is the forward distance that the propeller advances during one revolution, divided by the diameter of the propeller. The diameter units may be specified as "in", "ft", or "m", but default to inches if not specified. """ tas = U.speed_conv(tas, from_units=speed_units, to_units='m/s') dia = U.length_conv(dia, from_units=dia_units, to_units='m') advance_ratio = tas * 60. / (rpm * dia) return advance_ratio
def p(tas, dalt, wt, rpm, mp, prop): """ test function for RV-8A drag, to compare against CAFE foundation level flt data. prop is a prop_map.Prop instance. """ eas = A.tas2eas(tas, dalt, speed_units = 'mph') # tas = A.cas2tas(cas, dalt, speed_units = 'mph') tas_fts = U.speed_conv(tas, from_units = 'mph', to_units = 'ft/s') drag = FT.eas2drag(eas, wt, 110, rv = '8a', speed_units = 'mph') drag_power = drag * tas_fts / 550. power = IO.pwr(rpm, mp, dalt) prop_eff = PM.prop_eff(prop, power, rpm, tas, dalt, speed_units = 'mph') thrust_power = power * prop_eff excess_power = thrust_power - drag_power return excess_power
def roc(altitude, eas, weight, power, rpm, temp = 'std', temp_units = 'C', \ rv = 'F1', wing_area = 102, speed_units = 'kt', flap = 0, \ prop_eff = 0.7, load_factor = 1): """ Returns the rate of climb or descent. """ # PM.read_data_files_csv(base_name = prop) tas = A.eas2tas(eas, altitude, temp=temp, speed_units=speed_units) tas_fts = U.speed_conv(tas, from_units=speed_units, to_units='ft/s') # prop_eff = PM.prop_eff(power, rpm, tas, altitude, temp = temp, \ # temp_units = 'C', speed_units = speed_units) power_avail = power * prop_eff drag = F.eas2drag(eas, weight, wing_area, rv = rv, flap = flap, \ speed_units = speed_units, load_factor = load_factor) power_req = tas_fts * drag / 550. excess_power = power_avail - power_req roc = excess_power * 33000 / weight return roc
def roc(prop, altitude, eas, weight, power, rpm, temp = 'std', temp_units = 'C', \ rv = '8', wing_area = 110, speed_units = 'kt', flap = 0, \ load_factor = 1, wheel_pants = 1, prop_factor=1): """ Returns the rate of climb or descent. """ # PM.read_data_files_csv(base_name = prop) tas = A.eas2tas(eas, altitude, temp = temp, speed_units = speed_units) tas_fts = U.speed_conv(tas, from_units = speed_units, to_units = 'ft/s') prop_eff = PM.prop_eff(prop, power, rpm, tas, altitude, temp = temp, \ temp_units = 'C', speed_units = speed_units) * prop_factor power_avail = power * prop_eff drag = FT.eas2drag(eas, weight, wing_area, rv = rv, flap = flap, \ speed_units = speed_units, load_factor = load_factor, wheel_pants = wheel_pants) power_req = tas_fts * drag / 550. excess_power = power_avail - power_req roc = excess_power * 33000 / weight return roc
def alt2FP_speed(engine, prop, blade_angle, alt, weight = 1800, temp = 'std', \ temp_units = 'C', rv = '8', wing_area = 110, alt_units='ft', speed_units = 'kt' , \ MP_loss = 1.322, ram = 0.5, pwr_factor=1, mixture='pwr', MP='WOT', wheel_pants=1): """ Returns the predicted speed at full throttle for aircraft with fixed pitch prop. The MP_loss is the MP lost in the induction tract at full throttle at 2700 rpm at MSL. The default value is from the Lycoming power charts. The ram is the percentage of available ram recovery pressure that is achieved in the MP. """ tas_low = 80 tas_high = 250 pwr_tolerance = .1 while 1: tas_guess = (tas_low + tas_high) / 2. tas_guess_fts = U.speed_conv(tas_guess, from_units = speed_units, to_units = 'ft/s') rpm = tas2rpm(tas_guess, engine, prop, blade_angle, alt, temp=temp, alt_units=alt_units, temp_units=temp_units, speed_units=speed_units, MP=MP) if MP == 'WOT': MP = MP_pred(tas_guess, alt, rpm, rpm_base = 2700., MP_loss = MP_loss, ram = ram, temp=temp) bhp = engine.pwr(rpm, MP, alt, temp=temp, alt_units=alt_units, temp_units=temp_units) prop_eff = PM.prop_eff(prop, bhp, rpm, tas_guess, alt, temp = temp, temp_units = temp_units, speed_units = speed_units) power_avail = bhp * prop_eff eas_guess = A.tas2eas(tas_guess, alt, temp) drag = FT.eas2drag(eas_guess, weight, wing_area, rv = rv, flap = 0, speed_units = speed_units, wheel_pants = wheel_pants) power_req = tas_guess_fts * drag / 550. if M.fabs(power_avail - power_req) < pwr_tolerance: # print "MP = %.3f" % MP # print "Pwr = %.2f"% bhp # print "Prop Efficiency = %.3f, and power available = %.1f thp" % (prop_eff, power_avail) return tas_guess, rpm, bhp if power_req > power_avail: tas_high = tas_guess else: tas_low = tas_guess
def Re( V, L, KV, speed_units=default_speed_units, length_units=default_length_units, #kinematic_viscosity_units=default_kinematic_viscosity_units ): """ Return Reynold's number, given velocity, characteristic length and kinematic viscosity' V = velocity L = characteristic length KV = kinematic viscosity """ V = U.speed_conv(V, speed_units, 'm/s') L = U.length_conv(L, length_units, 'm') # KV = U.kinematic_viscosity_conv(KV, kinematic_viscosity_units, '??') Re = V * L / KV return Re
def cl2tas( Cl, altitude, weight, wing_area, temperature='std', load_factor=1, speed_units=default_speed_units, alt_units=default_alt_units, weight_units=default_weight_units, area_units=default_area_units, temp_units=default_temp_units, ): """ Returns the true airspeed, given coefficient of lift, altitude, weight, and wing area. Temperature and load factor are optional inputs. The temperature, if not provided, defaults to the standard temperature for the altitude. The load factor, if not provided, defaults to 1. """ weight = U.wt_conv(weight, from_units=weight_units, to_units='kg') wing_area = U.area_conv(wing_area, from_units=area_units, to_units='m**2') eas = ((((2. * weight) * g) * load_factor) / ((Rho0 * wing_area) * Cl)) ** 0.5 eas = U.speed_conv(eas, from_units='m/s', to_units=speed_units) tas = A.eas2tas( eas, altitude, temperature, speed_units, alt_units, temp_units=temp_units, ) return tas
def eas2cl( eas, weight, wing_area, load_factor=1, speed_units=default_speed_units, weight_units=default_weight_units, area_units=default_area_units, ): """ Returns the coefficient of lift, given equivalent airspeed, weight, and wing area. Load factor is an optional input. The load factor, if not provided, defaults to 1. Example: if the wing area is 110 square feet, and the weight is 1800 lb, and the eas is 55 kt, in straight and level flight (so the load factor = 1), then: >>> S = 110 >>> W = 1800 >>> EAS = 55 >>> eas2cl(EAS, W, S, speed_units='kt', weight_units='lb', area_units='ft**2') 1.597820083228606 """ eas = U.speed_conv(eas, from_units=speed_units, to_units='m/s') weight = U.wt_conv(weight, from_units=weight_units, to_units='kg') wing_area = U.area_conv(wing_area, from_units=area_units, to_units='m**2') Cl = (((2. * weight) * g) * load_factor) / ((Rho0 * wing_area) * eas ** 2.) return Cl
def cl2tas( Cl, altitude, weight, wing_area, temperature='std', load_factor=1, speed_units=default_speed_units, alt_units=default_alt_units, weight_units=default_weight_units, area_units=default_area_units, temp_units=default_temp_units, ): """ Returns the true airspeed, given coefficient of lift, altitude, weight, and wing area. Temperature and load factor are optional inputs. The temperature, if not provided, defaults to the standard temperature for the altitude. The load factor, if not provided, defaults to 1. """ weight = U.wt_conv(weight, from_units=weight_units, to_units='kg') wing_area = U.area_conv(wing_area, from_units=area_units, to_units='m**2') eas = ((((2. * weight) * g) * load_factor) / ((Rho0 * wing_area) * Cl))**0.5 eas = U.speed_conv(eas, from_units='m/s', to_units=speed_units) tas = A.eas2tas( eas, altitude, temperature, speed_units, alt_units, temp_units=temp_units, ) return tas
def eas2cl( eas, weight, wing_area, load_factor=1, speed_units=default_speed_units, weight_units=default_weight_units, area_units=default_area_units, ): """ Returns the coefficient of lift, given equivalent airspeed, weight, and wing area. Load factor is an optional input. The load factor, if not provided, defaults to 1. Example: if the wing area is 110 square feet, and the weight is 1800 lb, and the eas is 55 kt, in straight and level flight (so the load factor = 1), then: >>> S = 110 >>> W = 1800 >>> EAS = 55 >>> eas2cl(EAS, W, S, speed_units='kt', weight_units='lb', area_units='ft**2') 1.597820083228606 """ eas = U.speed_conv(eas, from_units=speed_units, to_units='m/s') weight = U.wt_conv(weight, from_units=weight_units, to_units='kg') wing_area = U.area_conv(wing_area, from_units=area_units, to_units='m**2') Cl = (((2. * weight) * g) * load_factor) / ((Rho0 * wing_area) * eas**2.) return Cl
def tip_mach(tas, rpm, temperature, dia, speed_units="kt", temp_units="C", dia_units="in"): """ Returns the mach number of the propeller blade tip, given the true airspeed (tas), revolutions per minute (rpm), temperature and propeller diameter. The speed units may be specified as "kt", "mph", "km/h" or "ft/s", but default to "kt" if not specified. The temperature units may be specified as "C", "F", "K" or "R", but default to deg C if not specified. The diameter units may be specified as "in", "ft", or "m", but default to inches if not specified. """ dia = U.length_conv(dia, from_units=dia_units, to_units="m") tas = U.speed_conv(tas, from_units=speed_units, to_units="m/s") speed_of_sound = SA.temp2speed_of_sound(temperature, temp_units=temp_units, speed_units="m/s") rotation_speed = dia * rpm * M.pi / 60 tip_speed = M.sqrt(tas ** 2 + rotation_speed ** 2) tip_mach = tip_speed / speed_of_sound return tip_mach
def run_tests(cd0=0.0221, e=0.825): """ Run all test cases to compare drag polar model against CAFE results. """ sum_sq = 0 wt_value_tot = 0 wing_area = 110 prop = PM.Prop('7666-4RV') for n, test in enumerate(level_test_cases): tas = test[0] dalt = test[1] wt = test[2] rpm = test[3] mp = test[4] wt_value = test[5] eas = A.tas2eas(tas, dalt, speed_units='mph') tas_fts = U.speed_conv(tas, from_units='mph', to_units='ft/s') cl = FT.eas2cl(eas, wt, wing_area, speed_units='mph') cd = FT.cl2cd_test(cl, cd0, e, flap=0) drag = FT.cd2drag(cd, eas, wing_area, speed_units='mph') drag_power = drag * tas_fts / 550. power = IO.pwr(rpm, mp, dalt) prop_eff = PM.prop_eff(prop, power, rpm, tas, dalt, speed_units='mph') thrust_power = power * prop_eff excess_power = thrust_power - drag_power print('Case', n, 'Altitude =', dalt, 'TAS =', tas, 'EAS =', eas, 'Excess power =', excess_power) sum_sq += wt_value * excess_power**2 wt_value_tot += wt_value print('Average sum of squares of excess power =', \ (sum_sq / wt_value_tot) ** 0.5)
def cl2lift( Cl, eas, wing_area, speed_units=default_speed_units, lift_units=default_weight_units, area_units=default_area_units, ): """ Returns the lift, given coefficient of lift, equivalent airspeed, and wing area. """ eas = U.speed_conv(eas, from_units=speed_units, to_units='m/s') wing_area = U.area_conv(wing_area, from_units=area_units, to_units='m**2') lift = (((0.5 * Rho0) * eas**2.) * wing_area) * Cl if lift_units == 'kg': lift = U.force_conv(lift, 'N', 'lb') lift = U.mass_conv(lift, 'lb', 'kg') else: lift = U.force_conv(lift, 'N', lift_units) return lift
def descent_data(prop, weight=1600., alt_max=20000., fuel_units='USG', \ alt_interval=500., isa_dev=0, temp_units='C', rv='8', wing_area=110., \ tas=180., ROD=-500., angle='', speed_units='kt', rpm=2100., sfc=0.45, output='raw'): """ Returns a table of descent 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), or array. tas is the TAS in descent (overridden by the angle, if the angle is provided). angle is the flight path angle in degrees. """ tas_fts = U.speed_conv(tas, speed_units, 'ft/s') if angle: ROD = tas_fts * 60 * M.sin(angle * M.pi / 180) rod_fts = ROD / 60 tas = U.speed_conv(tas, speed_units, 'kt') alt = alt_max + alt_interval temp = SA.isa2temp(isa_dev, alt, temp_units=temp_units) time = 0 fuel_used = 0 dist = 0 if output == 'raw': print S.center('Altitude', 10), print S.center('ROD', 10), print S.center('Time', 10), print S.center('Fuel Used', 10), print S.center('Dist', 10), print S.center('Speed', 10) print S.center('(ft)', 10), print S.center('(ft/mn)', 10), print S.center('(mn)', 10), f_units = '(' + fuel_units + ')' print S.center(f_units, 10), print S.center('(nm)', 10), print S.center('(KCAS)', 10) # # data for max altitude # print S.rjust(locale.format('%.0f', alt_max, True), 7), # print S.rjust(locale.format('%.0f', round(ROD / 10.) * 10, True), 10), # print S.rjust('%.1f' % (0), 10), # print S.rjust('%.1f' % (fuel_used), 10), # print S.rjust('%.1f' % (dist), 10), # print S.rjust('%3d' % (A.tas2cas(tas, alt_max, temp, temp_units=temp_units)), 10) # elif output == 'latex': # # temp = 15 + isa_dev # MSL_line = [] # MSL_line.append(str(locale.format('%.0f', weight, True))) # MSL_line.append('0') # MSL_line.append(str(locale.format('%.0f', temp))) # MSL_line.append(str(locale.format('%.0f', A.tas2cas(tas, alt, temp, temp_units=temp_units)))) # MSL_line.append(str(locale.format('%.0f', round(ROD / 10.) * 10, True))) # MSL_line.append('0') # MSL_line.append(str(fuel_used)) # MSL_line.append(str(dist)) # # print '&'.join(MSL_line) + '\\\\' # print '\\hline' # elif output == 'array': # # no header rows, but make blank array # array = [[alt_max,0,0,0]] alts = [] RODs = [] times_temp = [] CASs = [] dists_temp = [] fuel_useds_temp = [] temps = [] calc_alt = alt_max - alt_interval / 2. while alt > 0: temp = SA.isa2temp(isa_dev, alt, temp_units = temp_units) eas = A.tas2eas(tas, alt) drag = FT.eas2drag(eas, weight) pwr_level_flt = tas_fts * drag / 550 thrust_power = pwr_level_flt + FT.Pexcess_vs_roc(weight, ROD) prop_eff = PM.prop_eff(prop, thrust_power, rpm, tas, alt, temp, temp_units=temp_units) calc_pwr = thrust_power / prop_eff #fuel_flow = IO.pwr2ff(calc_pwr, rpm, ff_units = 'lb/hr') fuel_flow = calc_pwr * sfc # print "Level flt pwr = %.1f, thrust power = %.1f, prop eff = %.3f, fuel flow = %.3f" % (pwr_level_flt, thrust_power, prop_eff, fuel_flow) slice_time = alt_interval / rod_fts * -1. slice_dist = tas_fts * slice_time slice_fuel = fuel_flow * slice_time / 3600 fuel_used += slice_fuel fuel_out = (U.avgas_conv(fuel_used, from_units = 'lb', \ to_units = fuel_units)) weight -= slice_fuel alt -= alt_interval cas_out = A.tas2cas(tas, alt, temp, temp_units=temp_units) temp_out = SA.isa2temp(isa_dev, alt) time += slice_time / 60. dist += slice_dist / 6076.115 alts.append(alt) CASs.append(cas_out) RODs.append(ROD) times_temp.append(time) fuel_useds_temp.append(fuel_out) dists_temp.append(dist) temps.append(temp_out) calc_alt += alt_interval alts.reverse() CASs.reverse() RODs.reverse() temps.reverse() times = [] fuel_useds = [] dists = [] for n, time in enumerate(times_temp): times.append(times_temp[-1] - time) fuel_useds.append(fuel_useds_temp[-1] - fuel_useds_temp[n]) dists.append(dists_temp[-1] - dists_temp[n]) times.reverse() fuel_useds.reverse() dists.reverse() if output == 'raw': for n, alt in enumerate(alts): print S.rjust(locale.format('%.0f', alt, True), 7), # calculate ROC at the displayed altitude print S.rjust(locale.format('%.0f', RODs[n], True), 10), print S.rjust('%.1f' % (times[n]), 10), print S.rjust('%.1f' % (fuel_useds[n]), 10), print S.rjust('%.1f' % (dists[n]), 10), print S.rjust('%3d' % (int(CASs[n])), 10) elif output == 'latex': for n, alt in enumerate(alts): line = [] line.append(str(locale.format('%.0f', alt, True))) line.append(str(locale.format('%.0f', round(temps[n])))) line.append(str(locale.format('%.0f', CASs[n]))) line.append(str(locale.format('%.0f', round(RODs[n] / 10.) * 10, True))) line.append(str(locale.format('%.0f', times[n]))) line.append(str(locale.format('%.1f', fuel_useds[n]))) line.append(str(locale.format('%.0f', dists[n]))) print '&' + '&'.join(line) + '\\\\' print '\\hline' elif output == 'array': array = [] for n, alt in enumerate(alts): array.append([alt, times[n], fuel_useds[n], dists[n]]) return array
def tas2ssec2(tas, ind_alt, oat, ias, std_alt = 0, speed_units=default_speed_units, alt_units=default_alt_units, temp_units=default_temp_units, press_units = default_press_units): """ Return static source position error as speed error, pressure error and altitude error at sea level using speed course method. Returns delta_Vpc, delta_Ps and delta_Hpc tas = true airspeed determined by speed course method, or GPS ind_alt = pressure altitude, corrected for instrument error oat = outside air temperature, corrected for instrument error and ram temperature rise ias = indicated airspeed, corrected for instrument error std_alt = altitude to provide delta_Hpc for delta_Vpc = error in airspeed = calibrated airspeed - indicated airspeed corrected for instrument error delta_Ps = error in the pressure sensed by the static system = pressure sensed in the static system - ambient pressure delta_Hpc = altitude error at std_alt = actual altitude - altitude sensed by the static system Uses analysis method from USAF Test Pilot School. Unlike some other methods (e.g.. that in FAA AC 23-8B, or NTPS GPS_PEC.xls), this method provides an exact conversion from TAS to CAS (some other methods assume CAS = EAS), and it accounts for the effect of position error of altitude on the conversion from TAS to CAS (some other methods assume pressure altitude = indicated pressure altitude). """ tas = U.speed_conv(tas, speed_units, 'kt') ind_alt = U.length_conv(ind_alt, alt_units, 'ft') oat = U.temp_conv(oat, temp_units, 'C') M = A.tas2mach(tas, oat, temp_units='C', speed_units='kt') if M > 1: raise ValueError, 'This method only works for Mach < 1' delta_ic = SA.alt2press_ratio(ind_alt, alt_units='ft') qcic_over_Psl = A.cas2dp(ias, speed_units='kt', press_units=press_units) / U.press_conv(constants.P0, 'pa', to_units=press_units) qcic_over_Ps = qcic_over_Psl / delta_ic Mic = A.dp_over_p2mach(qcic_over_Ps) delta_mach_pc = M - Mic if Mic > 1: raise ValueError, 'This method only works for Mach < 1' deltaPp_over_Ps = (1.4 * delta_mach_pc * (Mic + delta_mach_pc / 2)) / (1 + 0.2 * (Mic + delta_mach_pc / 2 )**2) deltaPp_over_qcic = deltaPp_over_Ps / qcic_over_Ps delta_Hpc = SA.alt2temp_ratio(std_alt, alt_units='ft') * deltaPp_over_Ps / 3.61382e-5 # experimental - alternate way to calculate delta_Hpc that gives same answer Ps = SA.alt2press(ind_alt, alt_units='ft', press_units=press_units) delta_Ps = deltaPp_over_Ps * Ps P_std = SA.alt2press(std_alt, alt_units='ft', press_units=press_units) deltaPs_std = deltaPp_over_Ps * P_std delta_Hpc2 = SA.press2alt(P_std - deltaPs_std, press_units = press_units) - std_alt delta_std_alt = SA.alt2press_ratio(std_alt, alt_units='ft') asl = U.speed_conv(constants.A0, 'm/s', 'kt') delta_Vpc_std_alt = deltaPp_over_Ps * delta_std_alt * asl**2 / (1.4 * ias * (1 + 0.2 * (ias / asl)**2)**2.5) actual_alt = SA.press2alt(Ps + delta_Ps, press_units = press_units, alt_units = 'ft') cas = A.tas2cas(tas, actual_alt, oat, speed_units='kt', alt_units='ft', temp_units='C') return delta_Vpc, delta_Ps, delta_Hpc, cas
def test_04(self): Value = U.speed_conv(1, from_units='m/s', to_units='ft/s') Truth = 1 / 0.3048 self.assertTrue(RE(Value, Truth) <= 1e-8)
def tas2ssec2(tas, ind_alt, oat, ias, std_alt=0, speed_units=default_speed_units, alt_units=default_alt_units, temp_units=default_temp_units, press_units=default_press_units): """ Return static source position error as speed error, pressure error and altitude error at sea level using speed course method. Returns delta_Vpc, delta_Ps and delta_Hpc tas = true airspeed determined by speed course method, or GPS ind_alt = pressure altitude, corrected for instrument error oat = outside air temperature, corrected for instrument error and ram temperature rise ias = indicated airspeed, corrected for instrument error std_alt = altitude to provide delta_Hpc for delta_Vpc = error in airspeed = calibrated airspeed - indicated airspeed corrected for instrument error delta_Ps = error in the pressure sensed by the static system = pressure sensed in the static system - ambient pressure delta_Hpc = altitude error at std_alt = actual altitude - altitude sensed by the static system Uses analysis method from USAF Test Pilot School. Unlike some other methods (e.g.. that in FAA AC 23-8B, or NTPS GPS_PEC.xls), this method provides an exact conversion from TAS to CAS (some other methods assume CAS = EAS), and it accounts for the effect of position error of altitude on the conversion from TAS to CAS (some other methods assume pressure altitude = indicated pressure altitude). """ tas = U.speed_conv(tas, speed_units, 'kt') ind_alt = U.length_conv(ind_alt, alt_units, 'ft') oat = U.temp_conv(oat, temp_units, 'C') M = A.tas2mach(tas, oat, temp_units='C', speed_units='kt') if M > 1: raise ValueError('This method only works for Mach < 1') delta_ic = SA.alt2press_ratio(ind_alt, alt_units='ft') qcic_over_Psl = A.cas2dp(ias, speed_units='kt', press_units=press_units) / U.press_conv( constants.P0, 'pa', to_units=press_units) qcic_over_Ps = qcic_over_Psl / delta_ic Mic = A.dp_over_p2mach(qcic_over_Ps) delta_mach_pc = M - Mic if Mic > 1: raise ValueError('This method only works for Mach < 1') deltaPp_over_Ps = (1.4 * delta_mach_pc * (Mic + delta_mach_pc / 2)) / ( 1 + 0.2 * (Mic + delta_mach_pc / 2)**2) deltaPp_over_qcic = deltaPp_over_Ps / qcic_over_Ps delta_Hpc = SA.alt2temp_ratio( std_alt, alt_units='ft') * deltaPp_over_Ps / 3.61382e-5 # experimental - alternate way to calculate delta_Hpc that gives same answer Ps = SA.alt2press(ind_alt, alt_units='ft', press_units=press_units) delta_Ps = deltaPp_over_Ps * Ps P_std = SA.alt2press(std_alt, alt_units='ft', press_units=press_units) deltaPs_std = deltaPp_over_Ps * P_std delta_Hpc2 = SA.press2alt(P_std - deltaPs_std, press_units=press_units) - std_alt delta_std_alt = SA.alt2press_ratio(std_alt, alt_units='ft') asl = U.speed_conv(constants.A0, 'm/s', 'kt') delta_Vpc_std_alt = deltaPp_over_Ps * delta_std_alt * asl**2 / ( 1.4 * ias * (1 + 0.2 * (ias / asl)**2)**2.5) actual_alt = SA.press2alt(Ps + delta_Ps, press_units=press_units, alt_units='ft') cas = A.tas2cas(tas, actual_alt, oat, speed_units='kt', alt_units='ft', temp_units='C') return delta_Vpc, delta_Ps, delta_Hpc, cas
def test_03(self): Value = U.speed_conv(1, from_units='km/h', to_units='m/s') Truth = 1000 / 3600. self.failUnless(RE(Value, Truth) <= 1e-8)
def test_05(self): Value = U.speed_conv(1, from_units='ft/s', to_units='kt') Truth = 3600. / (1852. / 0.3048) self.failUnless(RE(Value, Truth) <= 1e-8)
def test_03(self): Value = U.speed_conv(1, from_units='km/h', to_units='m/s') Truth = 1000 / 3600. self.failUnless(RE(Value, Truth) <= 1e-5)
def test_04(self): Value = U.speed_conv(1, from_units='m/s', to_units='ft/s') Truth = 1 / 0.3048 self.failUnless(RE(Value, Truth) <= 1e-5)
def test_01(self): Value = U.speed_conv(1, from_units='kt', to_units='mph') Truth = (1852. / 0.3048) / 5280 self.failUnless(RE(Value, Truth) <= 1e-5)
def test_02(self): Value = U.speed_conv(1, from_units='mph', to_units='km/h') Truth = (5280 * 0.3048) / 1000 self.failUnless(RE(Value, Truth) <= 1e-5)
def test_02(self): Value = U.speed_conv(1, from_units='mph', to_units='km/h') Truth = (5280 * 0.3048) / 1000 self.assertTrue(RE(Value, Truth) <= 1e-8)
def test_03(self): Value = U.speed_conv(1, from_units='km/h', to_units='m/s') Truth = 1000 / 3600. self.assertTrue(RE(Value, Truth) <= 1e-8)
def descent_data(prop, weight=1600., alt_max=20000., fuel_units='USG', \ alt_interval=500., isa_dev=0, temp_units='C', rv='8', wing_area=110., \ tas=180., ROD=-500., angle='', speed_units='kt', rpm=2100., sfc=0.45, output='raw'): """ Returns a table of descent 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), or array. tas is the TAS in descent (overridden by the angle, if the angle is provided). angle is the flight path angle in degrees. """ tas_fts = U.speed_conv(tas, speed_units, 'ft/s') if angle: ROD = tas_fts * 60 * M.sin(angle * M.pi / 180) rod_fts = ROD / 60 tas = U.speed_conv(tas, speed_units, 'kt') alt = alt_max + alt_interval temp = SA.isa2temp(isa_dev, alt, temp_units=temp_units) time = 0 fuel_used = 0 dist = 0 if output == 'raw': print(S.center('Altitude', 10), end=' ') print(S.center('ROD', 10), end=' ') print(S.center('Time', 10), end=' ') print(S.center('Fuel Used', 10), end=' ') print(S.center('Dist', 10), end=' ') print(S.center('Speed', 10)) print(S.center('(ft)', 10), end=' ') print(S.center('(ft/mn)', 10), end=' ') print(S.center('(mn)', 10), end=' ') f_units = '(' + fuel_units + ')' print(S.center(f_units, 10), end=' ') print(S.center('(nm)', 10), end=' ') print(S.center('(KCAS)', 10)) # # data for max altitude # print S.rjust(locale.format('%.0f', alt_max, True), 7), # print S.rjust(locale.format('%.0f', round(ROD / 10.) * 10, True), 10), # print S.rjust('%.1f' % (0), 10), # print S.rjust('%.1f' % (fuel_used), 10), # print S.rjust('%.1f' % (dist), 10), # print S.rjust('%3d' % (A.tas2cas(tas, alt_max, temp, temp_units=temp_units)), 10) # elif output == 'latex': # # temp = 15 + isa_dev # MSL_line = [] # MSL_line.append(str(locale.format('%.0f', weight, True))) # MSL_line.append('0') # MSL_line.append(str(locale.format('%.0f', temp))) # MSL_line.append(str(locale.format('%.0f', A.tas2cas(tas, alt, temp, temp_units=temp_units)))) # MSL_line.append(str(locale.format('%.0f', round(ROD / 10.) * 10, True))) # MSL_line.append('0') # MSL_line.append(str(fuel_used)) # MSL_line.append(str(dist)) # # print '&'.join(MSL_line) + '\\\\' # print '\\hline' # elif output == 'array': # # no header rows, but make blank array # array = [[alt_max,0,0,0]] alts = [] RODs = [] times_temp = [] CASs = [] dists_temp = [] fuel_useds_temp = [] temps = [] calc_alt = alt_max - alt_interval / 2. while alt > 0: temp = SA.isa2temp(isa_dev, alt, temp_units=temp_units) eas = A.tas2eas(tas, alt) drag = FT.eas2drag(eas, weight) pwr_level_flt = tas_fts * drag / 550 thrust_power = pwr_level_flt + FT.Pexcess_vs_roc(weight, ROD) prop_eff = PM.prop_eff(prop, thrust_power, rpm, tas, alt, temp, temp_units=temp_units) calc_pwr = thrust_power / prop_eff #fuel_flow = IO.pwr2ff(calc_pwr, rpm, ff_units = 'lb/hr') fuel_flow = calc_pwr * sfc # print "Level flt pwr = %.1f, thrust power = %.1f, prop eff = %.3f, fuel flow = %.3f" % (pwr_level_flt, thrust_power, prop_eff, fuel_flow) slice_time = alt_interval / rod_fts * -1. slice_dist = tas_fts * slice_time slice_fuel = fuel_flow * slice_time / 3600 fuel_used += slice_fuel fuel_out = (U.avgas_conv(fuel_used, from_units = 'lb', \ to_units = fuel_units)) weight -= slice_fuel alt -= alt_interval cas_out = A.tas2cas(tas, alt, temp, temp_units=temp_units) temp_out = SA.isa2temp(isa_dev, alt) time += slice_time / 60. dist += slice_dist / 6076.115 alts.append(alt) CASs.append(cas_out) RODs.append(ROD) times_temp.append(time) fuel_useds_temp.append(fuel_out) dists_temp.append(dist) temps.append(temp_out) calc_alt += alt_interval alts.reverse() CASs.reverse() RODs.reverse() temps.reverse() times = [] fuel_useds = [] dists = [] for n, time in enumerate(times_temp): times.append(times_temp[-1] - time) fuel_useds.append(fuel_useds_temp[-1] - fuel_useds_temp[n]) dists.append(dists_temp[-1] - dists_temp[n]) times.reverse() fuel_useds.reverse() dists.reverse() if output == 'raw': for n, alt in enumerate(alts): print(S.rjust(locale.format('%.0f', alt, True), 7), end=' ') # calculate ROC at the displayed altitude print(S.rjust(locale.format('%.0f', RODs[n], True), 10), end=' ') print(S.rjust('%.1f' % (times[n]), 10), end=' ') print(S.rjust('%.1f' % (fuel_useds[n]), 10), end=' ') print(S.rjust('%.1f' % (dists[n]), 10), end=' ') print(S.rjust('%3d' % (int(CASs[n])), 10)) elif output == 'latex': for n, alt in enumerate(alts): line = [] line.append(str(locale.format('%.0f', alt, True))) line.append(str(locale.format('%.0f', round(temps[n])))) line.append(str(locale.format('%.0f', CASs[n]))) line.append( str(locale.format('%.0f', round(RODs[n] / 10.) * 10, True))) line.append(str(locale.format('%.0f', times[n]))) line.append(str(locale.format('%.1f', fuel_useds[n]))) line.append(str(locale.format('%.0f', dists[n]))) print('&' + '&'.join(line) + '\\\\') print('\\hline') elif output == 'array': array = [] for n, alt in enumerate(alts): array.append([alt, times[n], fuel_useds[n], dists[n]]) return array
def test_01(self): Value = U.speed_conv(1, from_units='kt', to_units='mph') Truth = (1852. / 0.3048) / 5280 self.failUnless(RE(Value, Truth) <= 1e-8)
def test_05(self): Value = U.speed_conv(1, from_units='ft/s', to_units='kt') Truth = 3600. / (1852. / 0.3048) self.failUnless(RE(Value, Truth) <= 1e-5)
def test_02(self): Value = U.speed_conv(1, from_units='mph', to_units='km/h') Truth = (5280 * 0.3048) / 1000 self.failUnless(RE(Value, Truth) <= 1e-8)
def test_01(self): Value = U.speed_conv(1, from_units='kt', to_units='mph') Truth = (1852. / 0.3048) / 5280 self.assertTrue(RE(Value, Truth) <= 1e-8)
def test_04(self): Value = U.speed_conv(1, from_units='m/s', to_units='ft/s') Truth = 1 / 0.3048 self.failUnless(RE(Value, Truth) <= 1e-8)
def climb_data(prop, weight = 1800., alt_max = 20000., TO_fuel = 0, TO_dist = 0, \ fuel_units = 'USG', alt_interval = 500., isa_dev = 0, temp_units = 'C', \ rv = '8', wing_area = 110., pwr = 'max', pwr_factor=1.0, \ climb_speed = 'max', output = 'raw'): """ Returns a table of climb 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), or array. pwr may be 'max', 'cc' (cruise climb = 2500 rpm and 25") or 2500 (2500 rpm and full throttle) climb_speed may be 'max' (Vy), 'cc' (120 kt to 10,000 ft, then reducing by 4 kt/1000 ft) or 'norm' (100 kt to 10,000 ft, then reducing by 2 kt/1000 ft) Note: compared cruise range for all climb speed and power. The predicted results are all within 1.5 nm of range. Thus there is no advantage to using anything but Vy and max power. """ def _alt2ROC(prop, alt): """ calculate ROC for an altitude """ temp = SA.isa2temp(isa_dev, alt) calc_pwr = alt2pwr(alt, rpm=rpm, MP_max=MP_max, temp=temp) * pwr_factor cas = alt2roc_speed(alt, climb_speed=climb_speed) eas = A.cas2eas(cas, alt) ROC = roc(prop, alt, eas, weight, calc_pwr, rpm, rv = rv, \ wing_area = wing_area) return ROC alt = 0 time = 0 fuel_used = U.avgas_conv(TO_fuel, from_units=fuel_units, to_units='lb') weight = weight - fuel_used dist = TO_dist if pwr == 'max': # rpm = 2700 rpm = 2650 MP_max = 30 elif pwr == 'cc': rpm = 2500 MP_max = 25 elif pwr == 2500: rpm = 2500 MP_max = 30 else: raise ValueError("pwr must be one of 'max', 'cc', or 2500") if output == 'raw': print(S.center('Altitude', 10), end=' ') print(S.center('ROC', 10), end=' ') print(S.center('Time', 10), end=' ') print(S.center('Fuel Used', 10), end=' ') print(S.center('Dist', 10), end=' ') print(S.center('Speed', 10)) print(S.center('(ft)', 10), end=' ') print(S.center('(ft/mn)', 10), end=' ') print(S.center('(mn)', 10), end=' ') f_units = '(' + fuel_units + ')' print(S.center(f_units, 10), end=' ') print(S.center('(nm)', 10), end=' ') print(S.center('(KCAS)', 10)) # data for MSL print(S.rjust(locale.format('%.0f', 0, True), 7), end=' ') # calculate ROC at MSL print(S.rjust(locale.format('%.0f', round(_alt2ROC(prop, 0) / 10.) * 10, \ True), 10), end=' ') print(S.rjust('%.1f' % (0), 10), end=' ') print(S.rjust('%.1f' % (TO_fuel), 10), end=' ') print(S.rjust('%.1f' % (TO_dist), 10), end=' ') print(S.rjust('%3d' % (alt2roc_speed(0, climb_speed=climb_speed)), 10)) elif output == 'latex': temp = 15 + isa_dev MSL_line = [] MSL_line.append(str(locale.format('%.0f', weight, True))) MSL_line.append('0') MSL_line.append(str(locale.format('%.0f', temp))) MSL_line.append(str(locale.format('%.0f', alt2roc_speed(0, \ climb_speed = climb_speed)))) MSL_line.append(str(locale.format('%.0f', round(_alt2ROC(prop, 0) / 10.)\ * 10, True))) MSL_line.append('0') MSL_line.append(str(TO_fuel)) MSL_line.append(str(TO_dist)) print('&'.join(MSL_line) + '\\\\') print('\\hline') elif output == 'array': # no header rows, but make blank array array = [[0, 0, TO_fuel, 0]] calc_alt = alt_interval / 2. while calc_alt < alt_max: temp = SA.isa2temp(isa_dev, calc_alt) pwr = alt2pwr(calc_alt, rpm=rpm, MP_max=MP_max, temp=temp) calc_pwr = pwr * pwr_factor cas = alt2roc_speed(calc_alt, climb_speed=climb_speed) eas = A.cas2eas(cas, calc_alt) tas = A.cas2tas(cas, calc_alt, temp=temp, temp_units=temp_units) tas_fts = U.speed_conv(tas, from_units='kt', to_units='ft/s') ROC = roc(prop, calc_alt, eas, weight, calc_pwr, rpm, rv = rv, \ wing_area = wing_area) roc_fts = ROC / 60 fuel_flow = IO.pwr2ff(pwr, rpm, ff_units='lb/hr') slice_time = alt_interval / roc_fts slice_dist = tas_fts * slice_time slice_fuel = fuel_flow * slice_time / 3600 fuel_used += slice_fuel fuel_out = (U.avgas_conv(fuel_used, from_units = 'lb', \ to_units = fuel_units)) weight -= slice_fuel alt += alt_interval cas_out = alt2roc_speed(alt, climb_speed=climb_speed) temp_out = SA.isa2temp(isa_dev, alt) ROC = _alt2ROC(prop, alt) time += slice_time / 60 dist += slice_dist / 6076.115 if output == 'raw': print(S.rjust(locale.format('%.0f', alt, True), 7), end=' ') # calculate ROC at the displayed altitude print(S.rjust(locale.format('%.0f', ROC, True), 10), end=' ') print(S.rjust('%.1f' % (time), 10), end=' ') print(S.rjust('%.1f' % (fuel_out), 10), end=' ') print(S.rjust('%.1f' % (dist), 10), end=' ') print(S.rjust('%3d' % (int(cas_out)), 10)) elif output == 'latex': line = [] line.append(str(locale.format('%.0f', alt, True))) line.append(str(locale.format('%.0f', round(temp_out)))) line.append(str(locale.format('%.0f', cas_out))) line.append(str(locale.format('%.0f', round(ROC / 10.) * 10, True))) line.append(str(locale.format('%.0f', time))) line.append(str(locale.format('%.1f', fuel_out))) line.append(str(locale.format('%.0f', dist))) print('&' + '&'.join(line) + '\\\\') print('\\hline') elif output == 'array': array.append([alt, time, fuel_out, dist]) calc_alt += alt_interval if output == 'array': return array
def climb_data(prop, weight = 1800., alt_max = 20000., TO_fuel = 0, TO_dist = 0, \ fuel_units = 'USG', alt_interval = 500., isa_dev = 0, temp_units = 'C', \ rv = '8', wing_area = 110., pwr = 'max', pwr_factor=1.0, \ climb_speed = 'max', output = 'raw'): """ Returns a table of climb 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), or array. pwr may be 'max', 'cc' (cruise climb = 2500 rpm and 25") or 2500 (2500 rpm and full throttle) climb_speed may be 'max' (Vy), 'cc' (120 kt to 10,000 ft, then reducing by 4 kt/1000 ft) or 'norm' (100 kt to 10,000 ft, then reducing by 2 kt/1000 ft) Note: compared cruise range for all climb speed and power. The predicted results are all within 1.5 nm of range. Thus there is no advantage to using anything but Vy and max power. """ def _alt2ROC(prop, alt): """ calculate ROC for an altitude """ temp = SA.isa2temp(isa_dev, alt) calc_pwr = alt2pwr(alt, rpm = rpm, MP_max = MP_max, temp = temp) * pwr_factor cas = alt2roc_speed(alt, climb_speed = climb_speed) eas = A.cas2eas(cas, alt) ROC = roc(prop, alt, eas, weight, calc_pwr, rpm, rv = rv, \ wing_area = wing_area) return ROC alt = 0 time = 0 fuel_used = U.avgas_conv(TO_fuel, from_units = fuel_units, to_units = 'lb') weight = weight - fuel_used dist = TO_dist if pwr == 'max': # rpm = 2700 rpm = 2650 MP_max = 30 elif pwr == 'cc': rpm = 2500 MP_max = 25 elif pwr == 2500: rpm = 2500 MP_max = 30 else: raise ValueError, "pwr must be one of 'max', 'cc', or 2500" if output == 'raw': print S.center('Altitude', 10), print S.center('ROC', 10), print S.center('Time', 10), print S.center('Fuel Used', 10), print S.center('Dist', 10), print S.center('Speed', 10) print S.center('(ft)', 10), print S.center('(ft/mn)', 10), print S.center('(mn)', 10), f_units = '(' + fuel_units + ')' print S.center(f_units, 10), print S.center('(nm)', 10), print S.center('(KCAS)', 10) # data for MSL print S.rjust(locale.format('%.0f', 0, True), 7), # calculate ROC at MSL print S.rjust(locale.format('%.0f', round(_alt2ROC(prop, 0) / 10.) * 10, \ True), 10), print S.rjust('%.1f' % (0), 10), print S.rjust('%.1f' % (TO_fuel), 10), print S.rjust('%.1f' % (TO_dist), 10), print S.rjust('%3d' % (alt2roc_speed(0, climb_speed = climb_speed)), 10) elif output == 'latex': temp = 15 + isa_dev MSL_line = [] MSL_line.append(str(locale.format('%.0f', weight, True))) MSL_line.append('0') MSL_line.append(str(locale.format('%.0f', temp))) MSL_line.append(str(locale.format('%.0f', alt2roc_speed(0, \ climb_speed = climb_speed)))) MSL_line.append(str(locale.format('%.0f', round(_alt2ROC(prop, 0) / 10.)\ * 10, True))) MSL_line.append('0') MSL_line.append(str(TO_fuel)) MSL_line.append(str(TO_dist)) print '&'.join(MSL_line) + '\\\\' print '\\hline' elif output == 'array': # no header rows, but make blank array array = [[0,0,TO_fuel,0]] calc_alt = alt_interval / 2. while calc_alt < alt_max: temp = SA.isa2temp(isa_dev, calc_alt) pwr = alt2pwr(calc_alt, rpm = rpm, MP_max = MP_max, temp = temp) calc_pwr = pwr * pwr_factor cas = alt2roc_speed(calc_alt, climb_speed = climb_speed) eas = A.cas2eas(cas, calc_alt) tas = A.cas2tas(cas, calc_alt, temp = temp, temp_units = temp_units) tas_fts = U.speed_conv(tas, from_units = 'kt', to_units = 'ft/s') ROC = roc(prop, calc_alt, eas, weight, calc_pwr, rpm, rv = rv, \ wing_area = wing_area) roc_fts = ROC / 60 fuel_flow = IO.pwr2ff(pwr, rpm, ff_units = 'lb/hr') slice_time = alt_interval / roc_fts slice_dist = tas_fts * slice_time slice_fuel = fuel_flow * slice_time / 3600 fuel_used += slice_fuel fuel_out = (U.avgas_conv(fuel_used, from_units = 'lb', \ to_units = fuel_units)) weight -= slice_fuel alt += alt_interval cas_out = alt2roc_speed(alt, climb_speed = climb_speed) temp_out = SA.isa2temp(isa_dev, alt) ROC = _alt2ROC(prop, alt) time += slice_time / 60 dist += slice_dist / 6076.115 if output == 'raw': print S.rjust(locale.format('%.0f', alt, True), 7), # calculate ROC at the displayed altitude print S.rjust(locale.format('%.0f', ROC, True), 10), print S.rjust('%.1f' % (time), 10), print S.rjust('%.1f' % (fuel_out), 10), print S.rjust('%.1f' % (dist), 10), print S.rjust('%3d' % (int(cas_out)), 10) elif output == 'latex': line = [] line.append(str(locale.format('%.0f', alt, True))) line.append(str(locale.format('%.0f', round(temp_out)))) line.append(str(locale.format('%.0f', cas_out))) line.append(str(locale.format('%.0f', round(ROC / 10.) * 10, True))) line.append(str(locale.format('%.0f', time))) line.append(str(locale.format('%.1f', fuel_out))) line.append(str(locale.format('%.0f', dist))) print '&' + '&'.join(line) + '\\\\' print '\\hline' elif output == 'array': array.append([alt, time, fuel_out, dist]) calc_alt += alt_interval if output == 'array': return array
def test_05(self): Value = U.speed_conv(1, from_units='ft/s', to_units='kt') Truth = 3600. / (1852. / 0.3048) self.assertTrue(RE(Value, Truth) <= 1e-8)