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 cafe_speed(): """ Check the calculated speeds against the data from the CAFE tests for the RV-8A """ 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') calc_tas = speed(prop, dalt, wt, IO.pwr(rpm, mp, dalt), rpm, \ speed_units = 'mph') print 'Actual TAS =', tas, 'Calc TAS =', calc_tas
def cafe_speed(): """ Check the calculated speeds against the data from the CAFE tests for the RV-8A """ 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') calc_tas = speed(prop, dalt, wt, IO.pwr(rpm, mp, dalt), rpm, \ speed_units = 'mph') print('Actual TAS =', tas, 'Calc TAS =', calc_tas)
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 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 tas2cl( tas, altitude, weight, wing_area, temperature='std', load_factor=1, speed_units=default_speed_units, alt_units=default_alt_units, temp_units=default_temp_units, weight_units=default_weight_units, area_units=default_area_units, ): """ Returns the coefficient of lift, given true airspeed, 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. """ eas = A.tas2eas( tas, altitude, temperature, speed_units, alt_units, temp_units, ) Cl = eas2cl( eas, weight, wing_area, load_factor, speed_units, weight_units, area_units, ) return Cl
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 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 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 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 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