JSBSim Flight Dynamics Model 1.3.0 (09 Apr 2026)
An Open Source Flight Dynamics and Control Software Library in C++
Loading...
Searching...
No Matches
FGStandardAtmosphere.cpp
1/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2
3 Module: FGStandardAtmosphere.cpp
4 Author: Jon Berndt, Tony Peden
5 Date started: 5/2011
6 Purpose: Models the 1976 U.S. Standard Atmosphere
7 Called by: FGFDMExec
8
9 ------------- Copyright (C) 2011 Jon S. Berndt (jon@jsbsim.org) -------------
10
11 This program is free software; you can redistribute it and/or modify it under
12 the terms of the GNU Lesser General Public License as published by the Free
13 Software Foundation; either version 2 of the License, or (at your option) any
14 later version.
15
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
19 details.
20
21 You should have received a copy of the GNU Lesser General Public License along
22 with this program; if not, write to the Free Software Foundation, Inc., 59
23 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24
25 Further information about the GNU Lesser General Public License can also be
26 found on the world wide web at http://www.gnu.org.
27
28FUNCTIONAL DESCRIPTION
29--------------------------------------------------------------------------------
30
31
32HISTORY
33--------------------------------------------------------------------------------
34
35%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36COMMENTS, REFERENCES, and NOTES
37%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
38[1] Anderson, John D. "Introduction to Flight, Third Edition", McGraw-Hill,
39 1989, ISBN 0-07-001641-0
40[2] Sonntag, D. "Important New Values of the Physical Constants of 1986,
41 Vapour Pressure Formulations based on the IST-90 and Psychrometer
42 Formulae", Z. Meteorol., 70 (5), pp. 340-344, 1990
43
44%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
45INCLUDES
46%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
47
48#include <iomanip>
49
50#include "FGFDMExec.h"
51#include "FGStandardAtmosphere.h"
52#include "input_output/FGLog.h"
53
54using namespace std;
55
56namespace JSBSim {
57
58/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
59CLASS IMPLEMENTATION
60%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
61
63 : FGAtmosphere(fdmex), StdSLpressure(StdDaySLpressure), TemperatureBias(0.0),
64 TemperatureDeltaGradient(0.0), VaporMassFraction(0.0),
65 SaturatedVaporPressure(StdDaySLpressure), StdAtmosTemperatureTable(9),
66 MaxVaporMassFraction(10)
67{
68 Name = "FGStandardAtmosphere";
69
70 // This is the U.S. Standard Atmosphere table for temperature in degrees
71 // Rankine, based on geometric altitude. The table values are often given
72 // in literature relative to geopotential altitude.
73 //
74 // GeoMet Alt Temp GeoPot Alt GeoMet Alt
75 // (ft) (deg R) (km) (km)
76 // -------- -------- ---------- ----------
77 //StdAtmosTemperatureTable << 0.00 << 518.67 // 0.000 0.000
78 // << 36151.80 << 389.97 // 11.000 11.019
79 // << 65823.90 << 389.97 // 20.000 20.063
80 // << 105518.06 << 411.60 // 32.000 32.162
81 // << 155348.07 << 487.20 // 47.000 47.350
82 // << 168676.12 << 487.20 // 51.000 51.413
83 // << 235570.77 << 386.40 // 71.000 71.802
84 // << 282152.08 << 336.50 // 84.852 86.000
85 // << 298556.40 << 336.50; // 91.000 - First layer in high altitude regime
86
87 // GeoPot Alt Temp GeoPot Alt GeoMet Alt
88 // (ft) (deg R) (km) (km)
89 // ----------- -------- ---------- ----------
90 StdAtmosTemperatureTable << 0.0000 << 518.67 // 0.000 0.000
91 << 36089.2388 << 389.97 // 11.000 11.019
92 << 65616.7979 << 389.97 // 20.000 20.063
93 << 104986.8766 << 411.57 // 32.000 32.162
94 << 154199.4751 << 487.17 // 47.000 47.350
95 << 167322.8346 << 487.17 // 51.000 51.413
96 << 232939.6325 << 386.37 // 71.000 71.802
97 << 278385.8268 << 336.5028 // 84.852 86.000
98 << 298556.4304 << 336.5028; // 91.000 - First layer in high altitude regime
99
100
101 // This is the maximum water vapor mass fraction in ppm (parts per million) of
102 // dry air measured in the atmosphere according to the ISA 1976 document.
103 // Values at altitude below 8 km are record high. All other values are 1%
104 // high.
105
106 // Geopot Alt Water Geopot Alt
107 // (ft) (ppm) (km)
108 // ---------- ----- ----------
109 MaxVaporMassFraction << 0.0000 << 35000. // 0.0000 - Record high
110 << 3280.8399 << 31000. // 1.0000
111 << 6561.6798 << 28000. // 2.0000
112 << 13123.3596 << 22000. // 4.0000
113 << 19685.0394 << 8900. // 6.0000
114 << 26246.7192 << 4700. // 8.0000 - Record high
115 << 32808.3990 << 1300. // 10.0000 - 1% high
116 << 39370.0787 << 230. // 12.0000
117 << 45931.7585 << 48. // 14.0000
118 << 52493.4383 << 38.; // 16.0000 - 1% high
119
120 unsigned int numRows = StdAtmosTemperatureTable.GetNumRows();
121
122 // Initialize the standard atmosphere lapse rates.
124 StdLapseRates = LapseRates;
125
126 // Assume the altitude to fade out the gradient at is at the highest
127 // altitude in the table. Above that, other functions are used to
128 // calculate temperature.
129 GradientFadeoutAltitude = StdAtmosTemperatureTable(numRows, 0);
130
131 // Initialize the standard atmosphere pressure break points.
132 PressureBreakpoints.resize(numRows);
133 CalculatePressureBreakpoints(StdSLpressure);
134 StdPressureBreakpoints = PressureBreakpoints;
135
136 StdSLtemperature = StdAtmosTemperatureTable(1, 1);
137 StdSLdensity = StdSLpressure / (Rdry * StdSLtemperature);
138
140 StdSLsoundspeed = sqrt(SHRatio*Rdry*StdSLtemperature);
141
142 bind();
143 Debug(0);
144}
145
146//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
147
152
153//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
154
155bool FGStandardAtmosphere::InitModel(void)
156{
157
158 // Assume the altitude to fade out the gradient at is at the highest
159 // altitude in the table. Above that, other functions are used to
160 // calculate temperature.
161 GradientFadeoutAltitude = StdAtmosTemperatureTable(StdAtmosTemperatureTable.GetNumRows(), 0);
162
163 TemperatureDeltaGradient = 0.0;
164 TemperatureBias = 0.0;
165 LapseRates = StdLapseRates;
166
167 PressureBreakpoints = StdPressureBreakpoints;
168
169 SLpressure = StdSLpressure;
170 SLtemperature = StdSLtemperature;
171 SLdensity = StdSLdensity;
172 SLsoundspeed = StdSLsoundspeed;
173
174 Calculate(0.0);
175
176// PrintStandardAtmosphereTable();
177
178 return true;
179}
180//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
181
183{
184 FGAtmosphere::Calculate(altitude);
185 SaturatedVaporPressure = CalculateVaporPressure(Temperature);
187}
188
189//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
190// Get the actual pressure as modeled at a specified altitude
191// These calculations are from equations 33a and 33b in the U.S. Standard
192// Atmosphere document referenced in the documentation for this code.
193
194double FGStandardAtmosphere::GetPressure(double altitude) const
195{
196 double GeoPotAlt = GeopotentialAltitude(altitude);
197
198 // Iterate through the altitudes to find the current Base Altitude
199 // in the table. That is, if the current altitude (the argument passed in)
200 // is 20000 ft, then the base altitude from the table is 0.0. If the
201 // passed-in altitude is 40000 ft, the base altitude is 36089.2388 ft (and
202 // the index "b" is 2 - the second entry in the table).
203 double BaseAlt = StdAtmosTemperatureTable(1,0);
204 unsigned int numRows = StdAtmosTemperatureTable.GetNumRows();
205 unsigned int b;
206
207 for (b=0; b < numRows-2; ++b) {
208 double testAlt = StdAtmosTemperatureTable(b+2,0);
209 if (GeoPotAlt < testAlt)
210 break;
211 BaseAlt = testAlt;
212 }
213
214 double Tmb = GetTemperature(GeometricAltitude(BaseAlt));
215 double deltaH = GeoPotAlt - BaseAlt;
216 double Lmb = LapseRates[b];
217
218 if (Lmb != 0.0) {
219 double Exp = g0 / (Rdry*Lmb);
220 double factor = Tmb/(Tmb + Lmb*deltaH);
221 return PressureBreakpoints[b]*pow(factor, Exp);
222 } else
223 return PressureBreakpoints[b]*exp(-g0*deltaH/(Rdry*Tmb));
224}
225
226//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
227
229{
230 double p = ConvertToPSF(pressure, unit);
231 SLpressure = ValidatePressure(p, "Sea Level pressure");
234}
235
236//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
237
239{
240 SLsoundspeed = sqrt(SHRatio*Reng*SLtemperature);
242}
243
244//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
245// Get the modeled temperature at a specified altitude, including any bias or gradient
246// effects.
247
248double FGStandardAtmosphere::GetTemperature(double altitude) const
249{
250 double GeoPotAlt = GeopotentialAltitude(altitude);
251
252 double T;
253
254 if (GeoPotAlt >= 0.0) {
255 T = StdAtmosTemperatureTable.GetValue(GeoPotAlt);
256
257 if (GeoPotAlt <= GradientFadeoutAltitude)
258 T -= TemperatureDeltaGradient * GeoPotAlt;
259 }
260 else {
261 // We don't need to add TemperatureDeltaGradient*GeoPotAlt here because
262 // the lapse rate vector already accounts for the temperature gradient.
263 T = StdAtmosTemperatureTable.GetValue(0.0) + GeoPotAlt*LapseRates[0];
264 }
265
266 T += TemperatureBias;
267
268 if (GeoPotAlt <= GradientFadeoutAltitude)
269 T += TemperatureDeltaGradient * GradientFadeoutAltitude;
270
271 return T;
272}
273
274//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
275// Retrieves the standard temperature at a particular altitude.
276
277double FGStandardAtmosphere::GetStdTemperature(double altitude) const
278{
279 double GeoPotAlt = GeopotentialAltitude(altitude);
280
281 if (GeoPotAlt >= 0.0)
282 return StdAtmosTemperatureTable.GetValue(GeoPotAlt);
283 else
284 return StdAtmosTemperatureTable.GetValue(0.0) + GeoPotAlt*LapseRates[0];
285}
286
287//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
288
289double FGStandardAtmosphere::GetStdPressure(double altitude) const
290{
291 double GeoPotAlt = GeopotentialAltitude(altitude);
292
293 // Iterate through the altitudes to find the current Base Altitude
294 // in the table. That is, if the current altitude (the argument passed in)
295 // is 20000 ft, then the base altitude from the table is 0.0. If the
296 // passed-in altitude is 40000 ft, the base altitude is 36089.2388 ft (and
297 // the index "b" is 2 - the second entry in the table).
298 double BaseAlt = StdAtmosTemperatureTable(1,0);
299 unsigned int numRows = StdAtmosTemperatureTable.GetNumRows();
300 unsigned int b;
301
302 for (b=0; b < numRows-2; ++b) {
303 double testAlt = StdAtmosTemperatureTable(b+2,0);
304 if (GeoPotAlt < testAlt)
305 break;
306 BaseAlt = testAlt;
307 }
308
309 double Tmb = GetStdTemperature(GeometricAltitude(BaseAlt));
310 double deltaH = GeoPotAlt - BaseAlt;
311 double Lmb = LapseRates[b];
312
313 if (Lmb != 0.0) {
314 double Exp = g0 / (Rdry*Lmb);
315 double factor = Tmb/(Tmb + Lmb*deltaH);
316 return StdPressureBreakpoints[b]*pow(factor, Exp);
317 } else
318 return StdPressureBreakpoints[b]*exp(-g0*deltaH/(Rdry*Tmb));
319}
320
321//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
322// Get the standard density at a specified altitude
323
324double FGStandardAtmosphere::GetStdDensity(double altitude) const
325{
326 return GetStdPressure(altitude)/(Rdry * GetStdTemperature(altitude));
327}
328
329//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
330
332{
333 double targetTemp = ConvertToRankine(t, unit);
334 double GeoPotAlt = GeopotentialAltitude(h);
335 double bias = targetTemp - GetStdTemperature(h);
336
337 if (GeoPotAlt <= GradientFadeoutAltitude)
338 bias -= TemperatureDeltaGradient * (GradientFadeoutAltitude - GeoPotAlt);
339
340 SetTemperatureBias(eRankine, bias);
342
343 SLtemperature = GetTemperature(0.0);
345}
346
347//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
348
350{
351 // Retrieve the minimum temperature in the standard atmosphere, may not be the
352 // last row in future if for example it's extended and maybe there is some
353 // temperature inversion layer etc. So run through and find the minimum.
354 const double minStdAtmosphereTemp = StdAtmosTemperatureTable.GetMinValue();
355
356 // Minimum known temperature in the universe currently
357 constexpr double minUniverseTemperature = KelvinToRankine(1.0);
358
359 if (unit == eCelsius || unit == eKelvin)
360 t *= 1.80; // If temp delta "t" is given in metric, scale up to English
361
362 TemperatureBias = t;
363 // Confirm the temperature bias isn't going to result in an atmosphere
364 // temperature lower than the lowest known temperature in the universe
365 if (minStdAtmosphereTemp + TemperatureBias < minUniverseTemperature) {
366 double minBias = minUniverseTemperature - minStdAtmosphereTemp;
367 FGLogging log(LogLevel::WARN);
368 log << "The temperature bias " << TemperatureBias << " R is too low. "
369 << "It could result in temperatures below the absolute zero." << endl
370 << "Temperature bias is therefore capped to " << minBias << endl;
371 TemperatureBias = minBias;
372 }
373
375
376 SLtemperature = GetTemperature(0.0);
378}
379
380//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
381// This function calculates a bias based on the supplied temperature for sea
382// level. The bias is applied to the entire temperature profile at all altitudes.
383// Internally, the Rankine scale is used for calculations, so any temperature
384// supplied must be converted to that unit.
385
387{
388 SetTemperature(t, 0.0, unit);
389}
390
391//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
392// Sets a Sea Level temperature delta that is ramped out by 86 km (282,152 ft).
393
395{
396 SetTemperatureGradedDelta(deltemp, 0.0, unit);
397}
398
399//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
400// Sets a temperature delta at the supplied altitude that is ramped out by 86 km.
401// After this calculation is performed, the lapse rates and pressure breakpoints
402// must be recalculated. Since we are calculating a delta here and not an actual
403// temperature, we only need to be concerned about a scale factor and not
404// the actual temperature itself.
405
407{
408 // Retrieve the minimum temperature in the standard atmosphere, may not be the
409 // last row in future if for example it's extended and maybe there is some
410 // temperature inversion layer etc. So run through and find the minimum.
411 const double minStdAtmosphereTemp = StdAtmosTemperatureTable.GetMinValue();
412 const double minDeltaTemperature = minStdAtmosphereTemp - StdSLtemperature;
413
414 if (unit == eCelsius || unit == eKelvin)
415 deltemp *= 1.80; // If temp delta "t" is given in metric, scale up to English
416
417 if (deltemp <= minDeltaTemperature) {
418 FGLogging log(LogLevel::WARN);
419 log << "The temperature delta " << deltemp << " R is too low. "
420 << "It could result in temperatures below the absolute zero." << endl
421 << "Temperature delta is therefore capped to " << minDeltaTemperature << endl;
422 deltemp = minDeltaTemperature;
423 }
424
425 TemperatureDeltaGradient = deltemp/(GradientFadeoutAltitude - GeopotentialAltitude(h));
428
429 SLtemperature = GetTemperature(0.0);
431}
432
433//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
434
436{
437 FGLogging out(LogLevel::STDOUT);
438 out << "Altitude (ft) Temp (F) Pressure (psf) Density (sl/ft3)" << std::endl;
439 out << "------------- -------- -------------- ----------------" << std::endl;
440 for (int i=0; i<280000; i+=1000) {
441 Calculate(i);
442 out << std::setw(12) << std::setprecision(2) << i
443 << " " << std::setw(9) << std::setprecision(2) << Temperature - 459.67
444 << " " << std::setw(13) << std::setprecision(4) << Pressure
445 << " " << std::setw(18) << std::setprecision(8) << Density
446 << std::endl;
447 }
448
449 // Re-execute the Run() method to reset the calculated values
450 Run(false);
451}
452
453//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
454// This function calculates (or recalculates) the lapse rate over an altitude range
455// where the "bh" in this case refers to the index of the base height in the
456// StdAtmosTemperatureTable table. This function should be called anytime the
457// temperature table is altered, such as when a gradient is applied across the
458// temperature table for a range of altitudes.
459
461{
462 unsigned int numRows = StdAtmosTemperatureTable.GetNumRows();
463 LapseRates.clear();
464
465 for (unsigned int bh=0; bh < numRows-1; bh++)
466 {
467 double t0 = StdAtmosTemperatureTable(bh+1,1);
468 double t1 = StdAtmosTemperatureTable(bh+2,1);
469 double h0 = StdAtmosTemperatureTable(bh+1,0);
470 double h1 = StdAtmosTemperatureTable(bh+2,0);
471 LapseRates.push_back((t1 - t0) / (h1 - h0) - TemperatureDeltaGradient);
472 }
473}
474
475//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
476
478{
479 PressureBreakpoints[0] = SLpress;
480
481 for (unsigned int b=0; b<PressureBreakpoints.size()-1; b++) {
482 double BaseTemp = StdAtmosTemperatureTable(b+1,1);
483 double BaseAlt = StdAtmosTemperatureTable(b+1,0);
484 double UpperAlt = StdAtmosTemperatureTable(b+2,0);
485 double deltaH = UpperAlt - BaseAlt;
486 double Tmb = BaseTemp
487 + TemperatureBias
488 + (GradientFadeoutAltitude - BaseAlt)*TemperatureDeltaGradient;
489 if (LapseRates[b] != 0.00) {
490 double Lmb = LapseRates[b];
491 double Exp = g0 / (Rdry*Lmb);
492 double factor = Tmb/(Tmb + Lmb*deltaH);
493 PressureBreakpoints[b+1] = PressureBreakpoints[b]*pow(factor, Exp);
494 } else {
495 PressureBreakpoints[b+1] = PressureBreakpoints[b]*exp(-g0*deltaH/(Rdry*Tmb));
496 }
497 }
498}
499
500//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
501
503{
504 TemperatureBias = TemperatureDeltaGradient = 0.0;
507
508 SLtemperature = StdSLtemperature;
510}
511
512//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
513
515{
516 SLpressure = StdSLpressure;
518 CalculatePressureBreakpoints(StdSLpressure);
519}
520
521//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
522
524{
525 StdDensityBreakpoints.clear();
526 for (unsigned int i = 0; i < StdPressureBreakpoints.size(); i++)
527 StdDensityBreakpoints.push_back(StdPressureBreakpoints[i] / (Rdry * StdAtmosTemperatureTable(i + 1, 1)));
528}
529
530//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
531
532double FGStandardAtmosphere::CalculateDensityAltitude(double density, double geometricAlt)
533{
534 // Work out which layer we're dealing with
535 unsigned int b = 0;
536 for (; b < StdDensityBreakpoints.size() - 2; b++) {
537 if (density >= StdDensityBreakpoints[b + 1])
538 break;
539 }
540
541 // Get layer properties
542 double Tmb = StdAtmosTemperatureTable(b + 1, 1);
543 double Hb = StdAtmosTemperatureTable(b + 1, 0);
544 double Lmb = StdLapseRates[b];
545 double pb = StdDensityBreakpoints[b];
546
547 double density_altitude = 0.0;
548
549 // https://en.wikipedia.org/wiki/Barometric_formula for density solved for H
550 if (Lmb != 0.0) {
551 double Exp = -1.0 / (1.0 + g0/(Rdry*Lmb));
552 density_altitude = Hb + (Tmb / Lmb) * (pow(density / pb, Exp) - 1);
553 } else {
554 double Factor = -Rdry*Tmb / g0;
555 density_altitude = Hb + Factor * log(density / pb);
556 }
557
558 return GeometricAltitude(density_altitude);
559}
560
561//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
562
563double FGStandardAtmosphere::CalculatePressureAltitude(double pressure, double geometricAlt)
564{
565 // Work out which layer we're dealing with
566 unsigned int b = 0;
567 for (; b < StdPressureBreakpoints.size() - 2; b++) {
568 if (pressure >= StdPressureBreakpoints[b + 1])
569 break;
570 }
571
572 // Get layer properties
573 double Tmb = StdAtmosTemperatureTable(b + 1, 1);
574 double Hb = StdAtmosTemperatureTable(b + 1, 0);
575 double Lmb = StdLapseRates[b];
576 double Pb = StdPressureBreakpoints[b];
577
578 double pressure_altitude = 0.0;
579
580 if (Lmb != 0.00) {
581 // Equation 33(a) from ISA document solved for H
582 double Exp = -Rdry*Lmb / g0;
583 pressure_altitude = Hb + (Tmb / Lmb) * (pow(pressure / Pb, Exp) - 1);
584 } else {
585 // Equation 33(b) from ISA document solved for H
586 double Factor = -Rdry*Tmb / g0;
587 pressure_altitude = Hb + Factor * log(pressure / Pb);
588 }
589
590 return GeometricAltitude(pressure_altitude);
591}
592
593//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
594
596{
597 double temperature_degC = RankineToCelsius(temperature);
598 return a*exp(b*temperature_degC/(c+temperature_degC));
599}
600
601//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
602
604{
605 if (SaturatedVaporPressure < Pressure) {
606 double VaporPressure = Pressure*VaporMassFraction / (VaporMassFraction+Rdry/Rwater);
607 if (VaporPressure > SaturatedVaporPressure)
608 VaporMassFraction = Rdry * SaturatedVaporPressure / (Rwater * (Pressure - SaturatedVaporPressure));
609 }
610
611 double GeoPotAlt = GeopotentialAltitude(h);
612 double maxFraction = 1E-6*MaxVaporMassFraction.GetValue(GeoPotAlt);
613
614 if ((VaporMassFraction > maxFraction) || (VaporMassFraction < 0.0))
615 VaporMassFraction = maxFraction;
616
617 // Update the gas constant factor
618 Reng = (VaporMassFraction*Rwater + Rdry)/(1.0 + VaporMassFraction);
619}
620
621//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
622
624{
625 double dewPoint_R = ConvertToRankine(dewpoint, unit);
626 constexpr double minDewPoint = CelsiusToRankine(-c) + 1.0;
627
628 if (dewPoint_R <= minDewPoint) {
629 FGLogging log(LogLevel::WARN);
630 log << "The dew point temperature " << dewPoint_R << " is lower than "
631 << minDewPoint << " R." << endl
632 << "Dew point is therefore capped to " << minDewPoint << endl;
633 dewPoint_R = minDewPoint;
634 }
635
636 double VaporPressure = CalculateVaporPressure(dewPoint_R);
637 SetVaporPressure(ePSF, VaporPressure);
638
639 double finalizedDewPoint = GetDewPoint(eRankine);
640 if (finalizedDewPoint < dewPoint_R) {
641 FGLogging log(LogLevel::WARN);
642 log << "Dew point temperature has been capped to " << finalizedDewPoint
643 << endl;
644 }
645}
646
647//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
648
650{
651 double dewpoint_degC;
652 double VaporPressure = Pressure*VaporMassFraction / (VaporMassFraction+Rdry/Rwater);
653
654 if (VaporPressure <= 0.0)
655 dewpoint_degC = -c;
656 else {
657 double x = log(VaporPressure/a);
658 dewpoint_degC = c*x / (b - x);
659 }
660
661 return ConvertFromRankine(CelsiusToRankine(dewpoint_degC), to);
662}
663
664//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
665
667{
668 double altitude = CalculatePressureAltitude(Pressure, 0.0);
669 double VaporPressure = ConvertToPSF(Pa, unit);
670 if (VaporPressure < 0.0) {
671 FGLogging log(LogLevel::WARN);
672 log << "The vapor pressure cannot be negative." << endl
673 << "Vapor pressure is set to 0.0" << endl;
674 VaporPressure = 0.0;
675 } else if (VaporPressure >= Pressure) {
676 FGLogging log(LogLevel::WARN);
677 log << "The vapor pressure " << VaporPressure
678 << " PSF is higher than the ambient pressure." << endl
679 << "Vapor pressure is therefore capped to " << Pressure-1.0 << endl;
680 VaporPressure = Pressure - 1.0;
681 }
682 VaporMassFraction = Rdry * VaporPressure / (Rwater * (Pressure - VaporPressure));
684}
685
686//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
687
689{
690 double VaporPressure = Pressure*VaporMassFraction / (VaporMassFraction+Rdry/Rwater);
691 return ConvertFromPSF(VaporPressure, to);
692}
693
694//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
695
697{
698 return ConvertFromPSF(SaturatedVaporPressure, to);
699}
700
701//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
702
704{
705 double VaporPressure = Pressure*VaporMassFraction / (VaporMassFraction+Rdry/Rwater);
706 return 100.0*VaporPressure/SaturatedVaporPressure;
707}
708
709//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
710
712{
713 if (RH < 0.0) {
714 FGLogging log(LogLevel::WARN);
715 log << "The relative humidity cannot be negative." << endl
716 << "Relative humidity is set to 0%" << endl;
717 RH = 0.0;
718 } else if (RH > 100.0) {
719 FGLogging log(LogLevel::WARN);
720 log << "The relative humidity cannot be higher than 100%." << endl
721 << "Relative humidity is set to 100%" << endl;
722 RH = 100.0;
723 }
724
725 double VaporPressure = 0.01*RH*SaturatedVaporPressure;
726 SetVaporPressure(ePSF, VaporPressure);
727}
728
729//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
730
732{
733 return VaporMassFraction*1E6;
734}
735
736//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
737
739{
740 double altitude = CalculatePressureAltitude(Pressure, 0.0);
741 VaporMassFraction = frac*1E-6;
743
744 if (fabs(VaporMassFraction*1E6-frac)>1E-2) {
745 FGLogging log(LogLevel::WARN);
746 log << "The vapor mass fraction " << frac << " has been capped to "
747 << VaporMassFraction*1E6 << "PPM." << endl;
748 }
749}
750
751//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
752
753void FGStandardAtmosphere::bind(void)
754{
755 PropertyManager->Tie("atmosphere/delta-T", this, eRankine,
758 PropertyManager->Tie("atmosphere/SL-graded-delta-T", this, eRankine,
761 PropertyManager->Tie<FGStandardAtmosphere, double, FGStandardAtmosphere::ePressure>("atmosphere/P-sl-psf", this, ePSF,
762 &FGStandardAtmosphere::GetPressureSL,
764 PropertyManager->Tie("atmosphere/dew-point-R", this, eRankine,
767 PropertyManager->Tie("atmosphere/vapor-pressure-psf", this, ePSF,
770 PropertyManager->Tie("atmosphere/saturated-vapor-pressure-psf", this, ePSF,
772 PropertyManager->Tie("atmosphere/RH", this,
775 PropertyManager->Tie("atmosphere/vapor-fraction-ppm", this,
778}
779
780//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
781// The bitmasked value choices are as follows:
782// unset: In this case (the default) JSBSim would only print
783// out the normally expected messages, essentially echoing
784// the config files as they are read. If the environment
785// variable is not set, debug_lvl is set to 1 internally
786// 0: This requests JSBSim not to output any messages
787// whatsoever.
788// 1: This value explicity requests the normal JSBSim
789// startup messages
790// 2: This value asks for a message to be printed out when
791// a class is instantiated
792// 4: When this value is set, a message is displayed when a
793// FGModel object executes its Run() method
794// 8: When this value is set, various runtime state variables
795// are printed out periodically
796// 16: When set various parameters are sanity checked and
797// a message is printed out when they go out of bounds
798
799void FGStandardAtmosphere::Debug(int from)
800{
801 if (debug_lvl <= 0) return;
802
803 if (debug_lvl & 1) { // Standard console startup message output
804 if (from == 0) { // Constructor
805 }
806 }
807 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
808 FGLogging log(LogLevel::DEBUG);
809 if (from == 0) log << "Instantiated: FGStandardAtmosphere" << std::endl;
810 if (from == 1) log << "Destroyed: FGStandardAtmosphere" << std::endl;
811 }
812 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
813 }
814 if (debug_lvl & 8 ) { // Runtime state variables
815 }
816 if (debug_lvl & 16) { // Sanity checking
817 }
818 if (debug_lvl & 128) { //
819 }
820 if (debug_lvl & 64) {
821 if (from == 0) { // Constructor
822 }
823 }
824}
825
826} // namespace JSBSim
Models an empty, abstract base atmosphere class.
double ConvertToRankine(double t, eTemperature unit) const
Converts to Rankine from one of several unit systems.
virtual void Calculate(double altitude)
Calculate the atmosphere for the given altitude.
double ConvertFromPSF(double t, ePressure unit=ePSF) const
Converts from PSF (pounds per square foot) to one of several unit systems.
virtual double GetPressure(void) const
Returns the pressure in psf.
eTemperature
Enums for specifying temperature units.
double ConvertFromRankine(double t, eTemperature unit) const
Converts from Rankine to one of several unit systems.
double ValidatePressure(double p, const std::string &msg, bool quiet=false) const
Check that the pressure is within plausible boundaries.
bool Run(bool Holding) override
Runs the atmosphere forces model; called by the Executive.
double ConvertToPSF(double t, ePressure unit=ePSF) const
Converts to PSF (pounds per square foot) from one of several unit systems.
ePressure
Enums for specifying pressure units.
static constexpr double g0
Sea-level acceleration of gravity - ft/s^2.
virtual double GetTemperature() const
Returns the actual, modeled temperature at the current altitude in degrees Rankine.
Encapsulates the JSBSim simulation executive.
Definition FGFDMExec.h:185
static constexpr double CelsiusToRankine(double celsius)
Converts from degrees Celsius to degrees Rankine.
Definition FGJSBBase.h:193
static constexpr double RankineToCelsius(double rankine)
Converts from degrees Rankine to degrees Celsius.
Definition FGJSBBase.h:200
static constexpr double KelvinToRankine(double kelvin)
Converts from degrees Kelvin to degrees Rankine.
Definition FGJSBBase.h:207
void CalculateStdDensityBreakpoints()
Calculate the atmospheric density breakpoints at the altitudes in the standard temperature table.
double GeometricAltitude(double geopotalt) const
Convert a geopotential altitude to a geometric altitude.
void SetVaporPressure(ePressure unit, double Pv)
Sets the partial pressure of water vapor.
virtual double GetStdPressure(double altitude) const
Returns the standard pressure at the specified altitude.
static constexpr double a
Sonntag constants based on ref [2].
double CalculateDensityAltitude(double density, double geometricAlt) override
Calculates the density altitude given any temperature or pressure bias.
double GetRelativeHumidity(void) const
Returns the relative humidity in percent.
void CalculateLapseRates()
Recalculate the lapse rate vectors when the temperature profile is altered in a way that would change...
void SetRelativeHumidity(double RH)
Sets the relative humidity.
virtual double GetTemperatureBias(eTemperature to) const
Returns the temperature bias over the sea level value in degrees Rankine.
virtual double GetStdTemperature(double altitude) const
Returns the standard temperature in degrees Rankine at a specified altitude.
void Calculate(double altitude) override
Calculate the atmosphere for the given altitude.
virtual double GetTemperatureDeltaGradient(eTemperature to) const
Returns the temperature gradient to be applied on top of the standard temperature gradient.
void SetTemperature(double t, double h, eTemperature unit=eFahrenheit) override
Sets the temperature at the supplied altitude, if it is to be different than the standard temperature...
void CalculatePressureBreakpoints(double SLpress)
Calculate (or recalculate) the atmospheric pressure breakpoints at the altitudes in the standard temp...
virtual void SetTemperatureGradedDelta(double t, double h, eTemperature unit=eFahrenheit)
Sets the temperature delta value at the supplied altitude/elevation above sea level,...
void CalculateSLDensity(void)
Calculate the SL density.
virtual void ResetSLPressure()
Resets the sea level to the Standard sea level pressure, and recalculates dependent parameters so tha...
double GetVaporPressure(ePressure to) const
Returns the partial pressure of water vapor.
double GeopotentialAltitude(double geometalt) const
Convert a geometric altitude to a geopotential altitude.
virtual void PrintStandardAtmosphereTable()
Prints the U.S. Standard Atmosphere table.
virtual double GetStdDensity(double altitude) const
Returns the standard density at a specified altitude.
FGStandardAtmosphere(FGFDMExec *)
Constructor.
virtual void SetTemperatureBias(eTemperature unit, double t)
Sets the temperature bias to be added to the standard temperature at all altitudes.
double GetSaturatedVaporPressure(ePressure to) const
Returns the saturated pressure of water vapor.
double StdSLtemperature
Standard sea level conditions.
virtual ~FGStandardAtmosphere()
Destructor.
void SetPressureSL(ePressure unit, double pressure) override
Sets the sea level pressure for modeling an off-standard pressure profile.
void SetTemperatureSL(double t, eTemperature unit=eFahrenheit) override
Sets the Sea Level temperature, if it is to be different than the standard.
double CalculateVaporPressure(double temperature)
Calculate the pressure of water vapor with the Magnus formula.
void CalculateSLSoundSpeedAndDensity(void)
Calculate the SL density and sound speed.
virtual void SetSLTemperatureGradedDelta(eTemperature unit, double t)
Sets a Sea Level temperature delta that is ramped out by 86 km.
void SetVaporMassFractionPPM(double frac)
Sets the vapor mass per million of dry air mass units.
void SetDewPoint(eTemperature unit, double dewpoint)
Sets the dew point.
void ValidateVaporMassFraction(double geometricAlt)
Validate the value of the vapor mass fraction.
virtual void ResetSLTemperature()
This function resets the model to apply no bias or delta gradient to the temperature.
double GetVaporMassFractionPPM(void) const
Returns the vapor mass per million of dry air mass units (ppm).
double CalculatePressureAltitude(double pressure, double geometricAlt) override
Calculates the pressure altitude given any temperature or pressure bias.
double GetDewPoint(eTemperature to) const
Returns the dew point.
double GetValue(void) const
Get the current table value.
Definition FGTable.cpp:465
Main namespace for the JSBSim Flight Dynamics Model.
Definition FGFDMExec.cpp:71