23#include <simgear/constants.h>
31 return tSec * 0.5 * (v1 + v2);
37 if (
fgGetNode(
"/aircraft/performance/bracket")) {
38 readPerformanceData();
48 auto bracket = bracketForAltitude(altitudeFt);
49 return bracket->gsForAltitude(altitudeFt);
54 auto bracket = bracketForAltitude(targetAltFt);
55 auto d = bracket->descendDistanceM(bracket->atOrBelowAltitudeFt, targetAltFt);
64 const double gsMPS = bracket->gsForAltitude(targetAltFt) * SG_KT_TO_MPS;
65 const double t = distanceM / gsMPS;
66 return targetAltFt + bracket->descentRateFPM * (t / 60.0);
71 auto bracket = bracketForAltitude(initialAltFt);
72 auto d = bracket->climbDistanceM(initialAltFt, bracket->atOrBelowAltitudeFt);
81 const double gsMPS = bracket->gsForAltitude(initialAltFt) * SG_KT_TO_MPS;
82 const double t = distanceM / gsMPS;
83 return initialAltFt + bracket->climbRateFPM * (t / 60.0);
89 const auto tagsNode =
fgGetNode(
"/sim/tags");
93 for (
auto t : tagsNode->getChildren(
"tag")) {
94 r.push_back(t->getStringValue());
101 auto it = std::find(t.begin(), t.end(), s);
102 return it != t.end();
105std::string AircraftPerformance::heuristicCatergoryFromTags()
const
124void AircraftPerformance::icaoCategoryData()
126 std::string propCat =
fgGetString(
"/aircraft/performance/icao-category");
127 if (propCat.empty()) {
128 propCat = heuristicCatergoryFromTags();
131 const char aircraftCategory = propCat.front();
133 switch (aircraftCategory) {
135 _perfData.push_back(Bracket(4000, 600, 1200, 75));
136 _perfData.push_back(Bracket(10000, 600, 1200, 140));
140 _perfData.push_back(Bracket(4000, 100, 1200, 100));
141 _perfData.push_back(Bracket(10000, 800, 1200, 160));
142 _perfData.push_back(Bracket(18000, 600, 1800, 200));
146 _perfData.push_back(Bracket(4000, 1800, 1800, 150));
147 _perfData.push_back(Bracket(10000, 1800, 1800, 200));
148 _perfData.push_back(Bracket(18000, 1200, 1800, 270));
149 _perfData.push_back(Bracket(60000, 800, 1200, 0.80,
true ));
155 _perfData.push_back(Bracket(4000, 1800, 1800, 180));
156 _perfData.push_back(Bracket(10000, 1800, 1800, 230));
157 _perfData.push_back(Bracket(18000, 1200, 1800, 270));
158 _perfData.push_back(Bracket(60000, 800, 1200, 0.87,
true ));
163void AircraftPerformance::readPerformanceData()
165 for (
auto nd :
fgGetNode(
"/aircraft/performance/")->getChildren(
"bracket")) {
166 const int atOrBelowAlt = nd->getIntValue(
"at-or-below-ft");
167 const int climbFPM = nd->getIntValue(
"climb-rate-fpm");
168 const int descentFPM = nd->getIntValue(
"descent-rate-fpm");
169 bool isMach = nd->hasChild(
"speed-mach");
172 speed = nd->getDoubleValue(
"speed-mach");
174 speed = nd->getIntValue(
"speed-ias-knots");
177 Bracket b(atOrBelowAlt, climbFPM, descentFPM, speed, isMach);
178 _perfData.push_back(b);
182auto AircraftPerformance::bracketForAltitude(
int altitude)
const
183 -> PerformanceVec::const_iterator
185 assert(!_perfData.empty());
186 if (_perfData.front().atOrBelowAltitudeFt >=
altitude)
187 return _perfData.begin();
189 for (
auto it = _perfData.begin(); it != _perfData.end(); ++it) {
190 if (it->atOrBelowAltitudeFt >
altitude) {
195 return _perfData.end() - 1;
198auto AircraftPerformance::rangeForAltitude(
int lowAltitude,
int highAltitude)
const
201 return {bracketForAltitude(lowAltitude), bracketForAltitude(highAltitude)};
204void AircraftPerformance::traverseAltitudeRange(
int initialElevationFt,
int targetElevationFt,
205 TraversalFunc tf)
const
207 auto r = rangeForAltitude(initialElevationFt, targetElevationFt);
208 if (r.first == r.second) {
209 tf(*r.first, initialElevationFt, targetElevationFt);
213 if (initialElevationFt < targetElevationFt) {
214 tf(*r.first, initialElevationFt, r.first->atOrBelowAltitudeFt);
215 int previousBracketCapAltitude = r.first->atOrBelowAltitudeFt;
216 for (
auto bracket = r.first + 1; bracket != r.second; ++bracket) {
217 tf(*bracket, previousBracketCapAltitude, bracket->atOrBelowAltitudeFt);
218 previousBracketCapAltitude = bracket->atOrBelowAltitudeFt;
221 tf(*r.second, previousBracketCapAltitude, targetElevationFt);
223 int nextBracketCapAlt = (r.first - 1)->atOrBelowAltitudeFt;
224 tf(*r.first, initialElevationFt, nextBracketCapAlt);
225 for (
auto bracket = r.first - 1; bracket != r.second; --bracket) {
226 nextBracketCapAlt = (r.first - 1)->atOrBelowAltitudeFt;
227 tf(*bracket, bracket->atOrBelowAltitudeFt, nextBracketCapAlt);
230 tf(*r.second, nextBracketCapAlt, targetElevationFt);
237 TraversalFunc tf = [&result](
const Bracket& bk,
int alt1,
int alt2) {
238 result += (alt1 > alt2) ? bk.descendDistanceM(alt1, alt2) : bk.climbDistanceM(alt1, alt2);
240 traverseAltitudeRange(initialElevationFt, targetElevationFt, tf);
241 return result * SG_METER_TO_NM;
247 TraversalFunc tf = [&result](
const Bracket& bk,
int alt1,
int alt2) {
248 SG_LOG(SG_GENERAL, SG_INFO,
"Range:" << alt1 <<
" " << alt2);
249 result += (alt1 > alt2) ? bk.descendTime(alt1, alt2) : bk.climbTime(alt1, alt2);
251 traverseAltitudeRange(initialElevationFt, targetElevationFt, tf);
257 auto b = bracketForAltitude(cruiseAltitudeFt);
258 return (cruiseDistanceNm / b->gsForAltitude(cruiseAltitudeFt)) * 3600.0;
263 if (altitudeFt > 36089)
267 const double T_r = .0019812;
268 return 15.0 - (altitudeFt * T_r);
289 const double k = 6.8755856e-6;
290 const double MgRT = 5.2558797;
291 const double MgRT_tr = 4.806346e-5;
292 const double P_0 = 29.92126;
294 const double P_Tr = 0.2233609 * P_0;
295 const double altAboveTr =
altitude - 36089;
296 return P_Tr * exp(MgRT_tr * altAboveTr);
298 return P_0 * pow(1.0 - (k *
altitude), MgRT);
306 DP=P_0*((1 + 0.2*(IAS/CS_0)^2)^3.5 -1)
307 M=(5*( (DP/P + 1)^(2/7) -1) )^0.5 (*)
309 const double Cs_0 = 661.4786;
310 const double P_0 = 29.92126;
311 const double iasCsRatio = iasKnots / Cs_0;
314 const double DP = P_0 * (pow(1.0 + 0.2 * pow(iasCsRatio, 2.0), 3.5) - 1.0);
316 const double pressureRatio = DP / P + 1.0;
317 const double M = pow(5.0 * (pow(pressureRatio, 2.0 / 7.0) - 1.0), 0.5);
319 SG_LOG(SG_GENERAL, SG_INFO,
"computeMachFromIAS: computed Mach is supersonic, fix for shock wave");
339 const double TAS = mach * CS;
343int AircraftPerformance::Bracket::gsForAltitude(
int altitude)
const
355double AircraftPerformance::Bracket::climbTime(
int alt1,
int alt2)
const
357 return (alt2 - alt1) /
static_cast<double>(climbRateFPM) * 60.0;
360double AircraftPerformance::Bracket::climbDistanceM(
int alt1,
int alt2)
const
362 const double t = climbTime(alt1, alt2);
364 SG_KT_TO_MPS * gsForAltitude(alt1),
365 SG_KT_TO_MPS * gsForAltitude(alt2));
368double AircraftPerformance::Bracket::descendTime(
int alt1,
int alt2)
const
370 return (alt1 - alt2) /
static_cast<double>(descentRateFPM) * 60.0;
373double AircraftPerformance::Bracket::descendDistanceM(
int alt1,
int alt2)
const
375 const double t = descendTime(alt1, alt2);
377 SG_KT_TO_MPS * gsForAltitude(alt1),
378 SG_KT_TO_MPS * gsForAltitude(alt2));
384 From the aviation formulary again
385 In a steady turn, in no wind, with bank angle, b at an airspeed v
389 With
R in feet, v in knots, b in degrees and w in degrees/sec (inconsistent units!), numerical constants are introduced:
391 R =v^2/(11.23*tan(0.01745*b))
392 (Example) At 100 knots, with a 45 degree bank, the radius of turn is 100^2/(11.23*tan(0.01745*45))= 891 feet.
394 The bank angle b_s
for a standard rate turn is given by:
396 b_s = 57.3*atan(v/362.1)
397 (Example)
for 100 knots, b_s = 57.3*atan(100/362.1) = 15.4 degrees
399 Working in meter-per-second and radians removes a bunch of constants again.
402 const double gs = gsKts * SG_KT_TO_MPS;
403 const double bankAngleRad = atan(gsKts/362.1);
404 const double r = (gs * gs)/(SG_g0_m_p_s2 * tan(bankAngleRad));
for runways and taxiways.
std::string fgGetString(const char *name, const char *defaultValue)
Get a string value for a property.
std::vector< std::string > string_list
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
const char ICAO_AIRCRAFT_CATEGORY_B
const char ICAO_AIRCRAFT_CATEGORY_E
const char ICAO_AIRCRAFT_CATEGORY_C
const char ICAO_AIRCRAFT_CATEGORY_A
const char ICAO_AIRCRAFT_CATEGORY_D
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.