public void TODistancePackAiCorrectionTest() { var para = new TOParameters( 0.0, 1000.0, // elevation 210.0, // rwy heading -1.8, // slope 240.0, // wind direction 10.0, // wind speed 4.0, // oat 1000.0, // QHN true, // surface is wet? 250.0 * 1000.0, // weight kg 0, // thrust rating AntiIceOption.EngAndWing, false, // packs on 0); // flaps var calc = new TOCalculator(perfTable, para); double distanceMeter = calc.TakeoffDistanceMeter(); Assert.AreEqual(ExpectedDistance2(para), distanceMeter, 1E-7); }
public void WhenRwyIsEnoughButUnableToClimbShouldThrowException() { var para = new TOParameters( 4000.0, // rwy length 1000.0, // elevation 210.0, // rwy heading -1.8, // slope 240.0, // wind direction 10.0, // wind speed 4.0, // oat 1000.0, // QHN false, // surface is wet? 250.0 * 1000.0, // weight kg 0, // thrust rating AntiIceOption.Off, true, // packs on 0); // flaps var calc = new TOCalculator(perfTable, para); double wt = calc.ClimbLimitWeightTon(); // Make it heavier than climb limit weight. PropertySetter.Set(para, "WeightKg", wt * 1000.0 + 1.0); Assert.Throws <PoorClimbPerformanceException>(() => new TOReportGenerator(perfTable, para).TakeOffReport()); }
private static double ExpectedClimbLimit1(TOParameters para) { var table = perfTable.Tables[para.FlapsIndex]; double pressAlt = PressureAltitudeFt(para.RwyElevationFt, para.QNH); return(table.ClimbLimitWt.ClimbLimitWeight(pressAlt, para.OatCelsius)); }
public void TODistanceDerateTest() { var para = new TOParameters( 0.0, 1000.0, // elevation 210.0, // rwy heading -1.8, // slope 240.0, // wind direction 10.0, // wind speed 4.0, // oat 1000.0, // QHN false, // surface is wet? 250.0 * 1000.0, // weight kg 2, // thrust rating AntiIceOption.Off, true, // packs on 0); // flaps var calc = new TOCalculator(perfTable, para); double distanceMeter = calc.TakeoffDistanceMeter(); Assert.AreEqual( ExpectedDistance1( para, perfTable.Tables[para.FlapsIndex] .AlternateThrustTables[para.ThrustRating - 1] .EquivalentFullThrustWeight( para.WeightKg / 1000.0, AlternateThrustTable.TableType.Dry)), distanceMeter, 1E-7); }
/// <summary> /// Computes the required takeoff distance. /// </summary> /// <exception cref="Exception"></exception> public static double TakeOffDistanceMeter(AirbusPerfTable t, TOParameters p) { var tables = GetTables(t, p); if (tables.Count == 0) { throw new Exception("No data for selected flaps"); } var pressAlt = ConversionTools.PressureAltitudeFt(p.RwyElevationFt, p.QNH); var inverseTables = tables.Select(x => GetInverseTable(x, pressAlt, t, p)); var distancesFt = inverseTables.Select(x => x.ValueAt(p.WeightKg * 0.001 * Constants.KgLbRatio)) .ToArray(); var d = (distancesFt.Length == 1) ? distancesFt[0] : Interpolate1D.Interpolate( tables.Select(x => x.IsaOffset).ToArray(), distancesFt, IsaOffset(p)); // The slope and wind correction is not exactly correct according to // performance xml file comments. However, the table itsel is probably // not that precise anyways. return(Constants.FtMeterRatio * (d - SlopeAndWindCorrectionFt(d, t, p))); }
// Use the length of first argument instead of the one in Parameters. private static double SlopeAndWindCorrectionFt(double lengthFt, AirbusPerfTable t, TOParameters p) { var windCorrectedFt = lengthFt + WindCorrectionFt(lengthFt, t, p); return(windCorrectedFt - lengthFt + SlopeCorrectionFt(t, p, windCorrectedFt)); }
public void ClimbLimitWtTest() { var para = new TOParameters( 2500.0, // rwy length 1000.0, // elevation 210.0, // rwy heading -1.8, // slope 240.0, // wind direction 10.0, // wind speed 4.0, // oat 1000.0, // QHN false, // surface is wet? 250.0 * 1000.0, // weight kg 2, // thrust rating AntiIceOption.Off, false, // packs on 0); // flaps var calc = new TOCalculator(perfTable, para); double distanceMeter = calc.ClimbLimitWeightTon(); double wtWithPackCorrection = ExpectedClimbLimit1(para) + 1.7; double expectedLimitWt = perfTable.Tables[para.FlapsIndex] .AlternateThrustTables[para.ThrustRating - 1] .CorrectedLimitWeight(wtWithPackCorrection, AlternateThrustTable.TableType.Climb); Assert.AreEqual(expectedLimitWt, distanceMeter, 1E-7); }
/// <summary> /// Error can be None, NoDataForSelectedFlaps, or RunwayTooShort. /// </summary> /// <exception cref="RunwayTooShortException"></exception> /// <exception cref="Exception"></exception> public static TOReport TakeOffReport(AirbusPerfTable t, TOParameters p, double tempIncrement = 1.0) { var d = TakeOffDistanceMeter(t, p); var primary = new TOReportRow(p.OatCelsius, d, p.RwyLengthMeter - d); if (primary.RwyRemainingMeter < 0) { throw new RunwayTooShortException(); } var rows = new List <TOReportRow>(); double maxOat = 67; for (double oat = p.OatCelsius + tempIncrement; oat <= maxOat; oat += tempIncrement) { try { var q = p.CloneWithOat(oat); d = TakeOffDistanceMeter(t, q); var remaining = q.RwyLengthMeter - d; if (remaining < 0) { break; } rows.Add(new TOReportRow(oat, d, remaining)); } catch { } } return(new TOReport(primary, rows)); }
private static double WetCorrection1000LB(double lengthFt, AirbusPerfTable t, TOParameters p) { if (!p.SurfaceWet) { return(0.0); } return(t.WetCorrectionTable.ValueAt(lengthFt)); }
private static double WindCorrectionFt(double lengthFt, AirbusPerfTable t, TOParameters p) { var headwind = p.WindSpeedKnots * Math.Cos(ToRadian(p.RwyHeading - p.WindHeading)); return((headwind >= 0 ? t.HeadwindCorrectionTable.ValueAt(lengthFt) : t.TailwindCorrectionTable.ValueAt(lengthFt)) * headwind); }
private static double SlopeCorrectionFt(AirbusPerfTable t, TOParameters p, double windCorrectedLengthFt) { var len = windCorrectedLengthFt; var s = p.RwySlopePercent; return((s >= 0 ? t.UphillCorrectionTable.ValueAt(len) : t.DownHillCorrectionTable.ValueAt(len)) * -s); }
// The table is for limit weight. This method constructs a table of // takeoff distance. (x: weight 1000 LB, f: runway length ft) // Wet runway and bleed air corrections are applied here. private static Table1D GetInverseTable(TableDataNode n, double pressAlt, AirbusPerfTable t, TOParameters p) { var table = n.Table; var len = table.y; var weight = len.Select(i => table.ValueAt(pressAlt, i) - WetAndBleedAirCorrection1000LB(i, t, p)); return(new Table1D(weight.ToArray(), len.ToArray())); }
private static double ExpectedLimitWt1(TOParameters para) { var table = perfTable.Tables[para.FlapsIndex]; double pressAlt = PressureAltitudeFt(para.RwyElevationFt, para.QNH); double windSpd = para.WindSpeed * Math.Cos(ToRadian(para.WindHeading - para.RwyHeading)); double slopeCorrectedLength = table.SlopeCorrDry.CorrectedLength( para.RwyLengthMeter, para.RwySlope); double windCorrectedLength = table.WindCorrDry.CorrectedLength( slopeCorrectedLength, windSpd); return(table.WeightTableDry.FieldLimitWeight( pressAlt, windCorrectedLength, para.OatCelsius)); }
private static double BleedAirCorrection1000LB(AirbusPerfTable t, TOParameters p) { if (p.PacksOn) { return(t.PacksOnCorrection); } if (p.AntiIce == AntiIceOption.EngAndWing) { return(t.AllAICorrection); } if (p.AntiIce == AntiIceOption.Engine) { return(t.EngineAICorrection); } return(0.0); }
// Returns whether continue to calculate. private bool CheckWeight(TOParameters para) { if (para.WeightKg > ac.MaxTOWtKg || para.WeightKg < ac.OewKg) { var result = parentControl.ShowDialog( "Takeoff weight is not within valid range. Continue to calculate?", MsgBoxIcon.Warning, "", DefaultButton.Button2, "Yes", "No"); return(result == MsgBoxResult.Button1); } return(true); }
private static double ExpectedDistance1(TOParameters para, double wtTon) { double headWind = para.WindSpeed * Math.Cos(ToRadian(para.WindHeading - para.RwyHeading)); double pressAlt = PressureAltitudeFt(para.RwyElevationFt, para.QNH); var table = perfTable.Tables[para.FlapsIndex]; double correctedLength = table.WeightTableDry.CorrectedLengthRequired( pressAlt, para.OatCelsius, wtTon); double slopeCorrectedLength = table.WindCorrDry.SlopeCorrectedLength( headWind, correctedLength); return(table.SlopeCorrDry.FieldLengthRequired( para.RwySlope, slopeCorrectedLength)); }
private static void AssertRwyRemaining(TOReport report, TOCalculator calc, TOParameters para) { var primary = report.PrimaryResult; double expectedRemaining = para.RwyLengthMeter - calc.TakeoffDistanceMeter(primary.OatCelsius); Assert.AreEqual(expectedRemaining, primary.RwyRemainingMeter, 0.5); foreach (var i in report.AssumedTemp) { expectedRemaining = para.RwyLengthMeter - calc.TakeoffDistanceMeter(i.OatCelsius); Assert.AreEqual(expectedRemaining, i.RwyRemainingMeter, 0.5); } }
public static TOParameters CloneWithOat(this TOParameters p, double oatCelcius) { return(new TOParameters( p.RwyLengthMeter, p.RwyElevationFt, p.RwyHeading, p.RwySlopePercent, p.WindHeading, p.WindSpeedKnots, oatCelcius, p.QNH, p.SurfaceWet, p.WeightKg, p.ThrustRating, p.AntiIce, p.PacksOn, p.FlapsIndex)); }
// Returns whether continue to calculate. private bool CheckWeight(TOParameters para) { var(a, _) = FindTable.Find(tables, aircrafts, regComboBox.Text); var ac = a.Config; if (para.WeightKg > ac.MaxTOWtKg || para.WeightKg < ac.OewKg) { var result = this.ShowDialog( "Takeoff weight is not within valid range. Continue to calculate?", MsgBoxIcon.Warning, "", DefaultButton.Button2, "Yes", "No"); return(result == MsgBoxResult.Button1); } return(true); }
private static double ExpectedDistance2(TOParameters para) { double headWind = para.WindSpeedKnots * Math.Cos(ToRadian(para.WindHeading - para.RwyHeading)); double pressAlt = PressureAltitudeFt(para.RwyElevationFt, para.QNH); double wtTon = (para.WeightKg + 2200.0 - 500.0) / 1000.0; var table = perfTable.Tables[para.FlapsIndex]; double correctedLength = table.WeightTableWet.CorrectedLengthRequired( pressAlt, para.OatCelsius, wtTon); double slopeCorrectedLength = table.WindCorrWet.SlopeCorrectedLength( headWind, correctedLength); return(table.SlopeCorrWet.FieldLengthRequired( para.RwySlopePercent, slopeCorrectedLength)); }
public void WhenRwyIsTooShortShouldThrowException() { var para = new TOParameters( 0.0, // rwy length 1000.0, // elevation 210.0, // rwy heading -1.8, // slope 240.0, // wind direction 10.0, // wind speed 4.0, // oat 1000.0, // QHN false, // surface is wet? 250.0 * 1000.0, // weight kg 0, // thrust rating AntiIceOption.Off, true, // packs on 0); // flaps Assert.Throws <RunwayTooShortException>(() => new TOReportGenerator(perfTable, para).TakeOffReport()); }
// Returns best matching tables, returning list can have: // 0 element if no matching flaps, or // 1 element if only 1 table matches the flaps setting, or // 2 elements if more than 1 table match the flaps, these two tables are // the ones most suitable for ISA offset interpolation. private static List <TableDataNode> GetTables(AirbusPerfTable t, TOParameters p) { var allFlaps = t.AvailableFlaps().ToList(); if (p.FlapsIndex >= allFlaps.Count) { return(new List <TableDataNode>()); } var flaps = allFlaps.ElementAt(p.FlapsIndex); var sameFlaps = t.Tables.Where(x => x.Flaps == flaps).ToList(); if (sameFlaps.Count == 1) { return(sameFlaps); } var ordered = sameFlaps.OrderBy(x => x.IsaOffset).ToList(); var isaOffset = IsaOffset(p); var skip = ordered.Where(x => isaOffset > x.IsaOffset).Count() - 1; var actualSkip = Numbers.LimitToRange(skip, 0, ordered.Count - 2); return(ordered.Skip(actualSkip).Take(2).ToList()); }
public void TakeOffReportTest() { var para = new TOParameters( 4000.0, // rwy length 1000.0, // elevation 210.0, // rwy heading -1.8, // slope 240.0, // wind direction 10.0, // wind speed 4.0, // oat 1000.0, // QHN false, // surface is wet? 250.0 * 1000.0, // weight kg 0, // thrust rating AntiIceOption.Off, true, // packs on 0); // flaps var report = new TOReportGenerator(perfTable, para).TakeOffReport(); var calc = new TOCalculator(perfTable, para); AssertReport(report, calc, para); }
private static void AssertReport(TOReport report, TOCalculator calc, TOParameters para) { // Rwy remaining AssertRwyRemaining(report, calc, para); // Primary result. var primary = report.PrimaryResult; Assert.AreEqual(para.OatCelsius, primary.OatCelsius, 0.5); double expectedDis = calc.TakeoffDistanceMeter(primary.OatCelsius); Assert.AreEqual(expectedDis, primary.RwyRequiredMeter, 0.5); // Assumed temperatures foreach (var i in report.AssumedTemp) { expectedDis = calc.TakeoffDistanceMeter(i.OatCelsius); Assert.AreEqual(expectedDis, i.RwyRequiredMeter, 0.5); } }
private static double WetAndBleedAirCorrection1000LB(double lengthFt, AirbusPerfTable t, TOParameters p) => WetCorrection1000LB(lengthFt, t, p) + BleedAirCorrection1000LB(t, p);
public TOReport GetReport(TOParameters p, double tempIncrementCelsius) { return(Calculator.TakeOffReport(PerfTable, p, tempIncrementCelsius)); }
private static double IsaOffset(TOParameters p) => p.OatCelsius - ConversionTools.IsaTemp(p.RwyElevationFt);
private static double ExpectedDistance1(TOParameters para) { return(ExpectedDistance1(para, para.WeightKg / 1000.0)); }
public TOReport GetReport(TOParameters p, double tempIncrementCelsius) { return(new TOReportGenerator(PerfTable, p).TakeOffReport(tempIncrementCelsius)); }