JSBSim Flight Dynamics Model  1.1.11 (13 Feb 2022)
An Open Source Flight Dynamics and Control Software Library in C++
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 
28 FUNCTIONAL DESCRIPTION
29 --------------------------------------------------------------------------------
30 
31 
32 HISTORY
33 --------------------------------------------------------------------------------
34 
35 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
36 COMMENTS, 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 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
45 INCLUDES
46 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
47 
48 #include <iomanip>
49 
50 #include "FGFDMExec.h"
51 #include "FGStandardAtmosphere.h"
52 
53 namespace JSBSim {
54 
55 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
56 CLASS IMPLEMENTATION
57 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
58 
60  : FGAtmosphere(fdmex), StdSLpressure(StdDaySLpressure), TemperatureBias(0.0),
61  TemperatureDeltaGradient(0.0), VaporMassFraction(0.0),
62  SaturatedVaporPressure(0.0), StdAtmosTemperatureTable(9),
63  MaxVaporMassFraction(10)
64 {
65  Name = "FGStandardAtmosphere";
66 
67  // This is the U.S. Standard Atmosphere table for temperature in degrees
68  // Rankine, based on geometric altitude. The table values are often given
69  // in literature relative to geopotential altitude.
70  //
71  // GeoMet Alt Temp GeoPot Alt GeoMet Alt
72  // (ft) (deg R) (km) (km)
73  // -------- -------- ---------- ----------
74  //StdAtmosTemperatureTable << 0.00 << 518.67 // 0.000 0.000
75  // << 36151.80 << 389.97 // 11.000 11.019
76  // << 65823.90 << 389.97 // 20.000 20.063
77  // << 105518.06 << 411.60 // 32.000 32.162
78  // << 155348.07 << 487.20 // 47.000 47.350
79  // << 168676.12 << 487.20 // 51.000 51.413
80  // << 235570.77 << 386.40 // 71.000 71.802
81  // << 282152.08 << 336.50 // 84.852 86.000
82  // << 298556.40 << 336.50; // 91.000 - First layer in high altitude regime
83 
84  // GeoPot Alt Temp GeoPot Alt GeoMet Alt
85  // (ft) (deg R) (km) (km)
86  // ----------- -------- ---------- ----------
87  StdAtmosTemperatureTable << 0.0000 << 518.67 // 0.000 0.000
88  << 36089.2388 << 389.97 // 11.000 11.019
89  << 65616.7979 << 389.97 // 20.000 20.063
90  << 104986.8766 << 411.57 // 32.000 32.162
91  << 154199.4751 << 487.17 // 47.000 47.350
92  << 167322.8346 << 487.17 // 51.000 51.413
93  << 232939.6325 << 386.37 // 71.000 71.802
94  << 278385.8268 << 336.5028 // 84.852 86.000
95  << 298556.4304 << 336.5028; // 91.000 - First layer in high altitude regime
96 
97 
98  // This is the maximum water vapor mass fraction in ppm (parts per million) of
99  // dry air measured in the atmosphere according to the ISA 1976 document.
100  // Values at altitude below 8 km are record high. All other values are 1%
101  // high.
102 
103  // Geopot Alt Water Geopot Alt
104  // (ft) (ppm) (km)
105  // ---------- ----- ----------
106  MaxVaporMassFraction << 0.0000 << 35000. // 0.0000 - Record high
107  << 3280.8399 << 31000. // 1.0000
108  << 6561.6798 << 28000. // 2.0000
109  << 13123.3596 << 22000. // 4.0000
110  << 19685.0394 << 8900. // 6.0000
111  << 26246.7192 << 4700. // 8.0000 - Record high
112  << 32808.3990 << 1300. // 10.0000 - 1% high
113  << 39370.0787 << 230. // 12.0000
114  << 45931.7585 << 48. // 14.0000
115  << 52493.4383 << 38.; // 16.0000 - 1% high
116 
117  unsigned int numRows = StdAtmosTemperatureTable.GetNumRows();
118 
119  // Initialize the standard atmosphere lapse rates.
121  StdLapseRates = LapseRates;
122 
123  // Assume the altitude to fade out the gradient at is at the highest
124  // altitude in the table. Above that, other functions are used to
125  // calculate temperature.
126  GradientFadeoutAltitude = StdAtmosTemperatureTable(numRows, 0);
127 
128  // Initialize the standard atmosphere pressure break points.
129  PressureBreakpoints.resize(numRows);
130  CalculatePressureBreakpoints(StdSLpressure);
131  StdPressureBreakpoints = PressureBreakpoints;
132 
133  StdSLtemperature = StdAtmosTemperatureTable(1, 1);
134  StdSLdensity = StdSLpressure / (Rdry * StdSLtemperature);
135 
137  StdSLsoundspeed = sqrt(SHRatio*Rdry*StdSLtemperature);
138 
139  bind();
140  Debug(0);
141 }
142 
143 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
144 
146 {
147  Debug(1);
148 }
149 
150 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
151 
152 bool FGStandardAtmosphere::InitModel(void)
153 {
154 
155  // Assume the altitude to fade out the gradient at is at the highest
156  // altitude in the table. Above that, other functions are used to
157  // calculate temperature.
158  GradientFadeoutAltitude = StdAtmosTemperatureTable(StdAtmosTemperatureTable.GetNumRows(), 0);
159 
160  TemperatureDeltaGradient = 0.0;
161  TemperatureBias = 0.0;
162  LapseRates = StdLapseRates;
163 
164  PressureBreakpoints = StdPressureBreakpoints;
165 
166  SLpressure = StdSLpressure;
167  SLtemperature = StdSLtemperature;
168  SLdensity = StdSLdensity;
169  SLsoundspeed = StdSLsoundspeed;
170 
171  Calculate(0.0);
172 
173 // PrintStandardAtmosphereTable();
174 
175  return true;
176 }
177 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
178 
179 void FGStandardAtmosphere::Calculate(double altitude)
180 {
181  FGAtmosphere::Calculate(altitude);
182  SaturatedVaporPressure = CalculateVaporPressure(Temperature);
183  ValidateVaporMassFraction(altitude);
184 }
185 
186 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
187 // Get the actual pressure as modeled at a specified altitude
188 // These calculations are from equations 33a and 33b in the U.S. Standard
189 // Atmosphere document referenced in the documentation for this code.
190 
191 double FGStandardAtmosphere::GetPressure(double altitude) const
192 {
193  double GeoPotAlt = GeopotentialAltitude(altitude);
194 
195  // Iterate through the altitudes to find the current Base Altitude
196  // in the table. That is, if the current altitude (the argument passed in)
197  // is 20000 ft, then the base altitude from the table is 0.0. If the
198  // passed-in altitude is 40000 ft, the base altitude is 36089.2388 ft (and
199  // the index "b" is 2 - the second entry in the table).
200  double BaseAlt = StdAtmosTemperatureTable(1,0);
201  unsigned int numRows = StdAtmosTemperatureTable.GetNumRows();
202  unsigned int b;
203 
204  for (b=0; b < numRows-2; ++b) {
205  double testAlt = StdAtmosTemperatureTable(b+2,0);
206  if (GeoPotAlt < testAlt)
207  break;
208  BaseAlt = testAlt;
209  }
210 
211  double Tmb = GetTemperature(GeometricAltitude(BaseAlt));
212  double deltaH = GeoPotAlt - BaseAlt;
213  double Lmb = LapseRates[b];
214 
215  if (Lmb != 0.0) {
216  double Exp = g0 / (Rdry*Lmb);
217  double factor = Tmb/(Tmb + Lmb*deltaH);
218  return PressureBreakpoints[b]*pow(factor, Exp);
219  } else
220  return PressureBreakpoints[b]*exp(-g0*deltaH/(Rdry*Tmb));
221 }
222 
223 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
224 
226 {
227  SLpressure = ConvertToPSF(pressure, unit);
229  CalculatePressureBreakpoints(SLpressure);
230 }
231 
232 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
233 
235 {
236  SLsoundspeed = sqrt(SHRatio*Reng*SLtemperature);
238 }
239 
240 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
241 // Get the modeled temperature at a specified altitude, including any bias or gradient
242 // effects.
243 
244 double FGStandardAtmosphere::GetTemperature(double altitude) const
245 {
246  double GeoPotAlt = GeopotentialAltitude(altitude);
247 
248  double T;
249 
250  if (GeoPotAlt >= 0.0) {
251  T = StdAtmosTemperatureTable.GetValue(GeoPotAlt);
252 
253  if (GeoPotAlt <= GradientFadeoutAltitude)
254  T -= TemperatureDeltaGradient * GeoPotAlt;
255  }
256  else {
257  // We don't need to add TemperatureDeltaGradient*GeoPotAlt here because
258  // the lapse rate vector already accounts for the temperature gradient.
259  T = StdAtmosTemperatureTable.GetValue(0.0) + GeoPotAlt*LapseRates[0];
260  }
261 
262  T += TemperatureBias;
263 
264  if (GeoPotAlt <= GradientFadeoutAltitude)
265  T += TemperatureDeltaGradient * GradientFadeoutAltitude;
266 
267  return T;
268 }
269 
270 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
271 // Retrieves the standard temperature at a particular altitude.
272 
273 double FGStandardAtmosphere::GetStdTemperature(double altitude) const
274 {
275  double GeoPotAlt = GeopotentialAltitude(altitude);
276 
277  if (GeoPotAlt >= 0.0)
278  return StdAtmosTemperatureTable.GetValue(GeoPotAlt);
279  else
280  return StdAtmosTemperatureTable.GetValue(0.0) + GeoPotAlt*LapseRates[0];
281 }
282 
283 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
284 
285 double FGStandardAtmosphere::GetStdPressure(double altitude) const
286 {
287  double GeoPotAlt = GeopotentialAltitude(altitude);
288 
289  // Iterate through the altitudes to find the current Base Altitude
290  // in the table. That is, if the current altitude (the argument passed in)
291  // is 20000 ft, then the base altitude from the table is 0.0. If the
292  // passed-in altitude is 40000 ft, the base altitude is 36089.2388 ft (and
293  // the index "b" is 2 - the second entry in the table).
294  double BaseAlt = StdAtmosTemperatureTable(1,0);
295  unsigned int numRows = StdAtmosTemperatureTable.GetNumRows();
296  unsigned int b;
297 
298  for (b=0; b < numRows-2; ++b) {
299  double testAlt = StdAtmosTemperatureTable(b+2,0);
300  if (GeoPotAlt < testAlt)
301  break;
302  BaseAlt = testAlt;
303  }
304 
305  double Tmb = GetStdTemperature(GeometricAltitude(BaseAlt));
306  double deltaH = GeoPotAlt - BaseAlt;
307  double Lmb = LapseRates[b];
308 
309  if (Lmb != 0.0) {
310  double Exp = g0 / (Rdry*Lmb);
311  double factor = Tmb/(Tmb + Lmb*deltaH);
312  return StdPressureBreakpoints[b]*pow(factor, Exp);
313  } else
314  return StdPressureBreakpoints[b]*exp(-g0*deltaH/(Rdry*Tmb));
315 }
316 
317 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
318 // Get the standard density at a specified altitude
319 
320 double FGStandardAtmosphere::GetStdDensity(double altitude) const
321 {
322  return GetStdPressure(altitude)/(Rdry * GetStdTemperature(altitude));
323 }
324 
325 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
326 
328 {
329  double targetTemp = ConvertToRankine(t, unit);
330  double GeoPotAlt = GeopotentialAltitude(h);
331 
332  TemperatureBias = targetTemp - GetStdTemperature(h);
333 
334  if (GeoPotAlt <= GradientFadeoutAltitude)
335  TemperatureBias -= TemperatureDeltaGradient * (GradientFadeoutAltitude - GeoPotAlt);
336 
337  CalculatePressureBreakpoints(SLpressure);
338 
339  SLtemperature = GetTemperature(0.0);
341 }
342 
343 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
344 
346 {
347  if (unit == eCelsius || unit == eKelvin)
348  t *= 1.80; // If temp delta "t" is given in metric, scale up to English
349 
350  TemperatureBias = t;
351  CalculatePressureBreakpoints(SLpressure);
352 
353  SLtemperature = GetTemperature(0.0);
355 }
356 
357 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
358 // This function calculates a bias based on the supplied temperature for sea
359 // level. The bias is applied to the entire temperature profile at all altitudes.
360 // Internally, the Rankine scale is used for calculations, so any temperature
361 // supplied must be converted to that unit.
362 
364 {
365  SetTemperature(t, 0.0, unit);
366 }
367 
368 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
369 // Sets a Sea Level temperature delta that is ramped out by 86 km (282,152 ft).
370 
372 {
373  SetTemperatureGradedDelta(deltemp, 0.0, unit);
374 }
375 
376 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
377 // Sets a temperature delta at the supplied altitude that is ramped out by 86 km.
378 // After this calculation is performed, the lapse rates and pressure breakpoints
379 // must be recalculated. Since we are calculating a delta here and not an actual
380 // temperature, we only need to be concerned about a scale factor and not
381 // the actual temperature itself.
382 
384 {
385  if (unit == eCelsius || unit == eKelvin)
386  deltemp *= 1.80; // If temp delta "t" is given in metric, scale up to English
387 
388  TemperatureDeltaGradient = deltemp/(GradientFadeoutAltitude - GeopotentialAltitude(h));
390  CalculatePressureBreakpoints(SLpressure);
391 
392  SLtemperature = GetTemperature(0.0);
394 }
395 
396 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
397 
399 {
400  std::cout << "Altitude (ft) Temp (F) Pressure (psf) Density (sl/ft3)" << std::endl;
401  std::cout << "------------- -------- -------------- ----------------" << std::endl;
402  for (int i=0; i<280000; i+=1000) {
403  Calculate(i);
404  std::cout << std::setw(12) << std::setprecision(2) << i
405  << " " << std::setw(9) << std::setprecision(2) << Temperature - 459.67
406  << " " << std::setw(13) << std::setprecision(4) << Pressure
407  << " " << std::setw(18) << std::setprecision(8) << Density
408  << std::endl;
409  }
410 
411  // Re-execute the Run() method to reset the calculated values
412  Run(false);
413 }
414 
415 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
416 // This function calculates (or recalculates) the lapse rate over an altitude range
417 // where the "bh" in this case refers to the index of the base height in the
418 // StdAtmosTemperatureTable table. This function should be called anytime the
419 // temperature table is altered, such as when a gradient is applied across the
420 // temperature table for a range of altitudes.
421 
423 {
424  unsigned int numRows = StdAtmosTemperatureTable.GetNumRows();
425  LapseRates.clear();
426 
427  for (unsigned int bh=0; bh < numRows-1; bh++)
428  {
429  double t0 = StdAtmosTemperatureTable(bh+1,1);
430  double t1 = StdAtmosTemperatureTable(bh+2,1);
431  double h0 = StdAtmosTemperatureTable(bh+1,0);
432  double h1 = StdAtmosTemperatureTable(bh+2,0);
433  LapseRates.push_back((t1 - t0) / (h1 - h0) - TemperatureDeltaGradient);
434  }
435 }
436 
437 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
438 
440 {
441  PressureBreakpoints[0] = SLpress;
442 
443  for (unsigned int b=0; b<PressureBreakpoints.size()-1; b++) {
444  double BaseTemp = StdAtmosTemperatureTable(b+1,1);
445  double BaseAlt = StdAtmosTemperatureTable(b+1,0);
446  double UpperAlt = StdAtmosTemperatureTable(b+2,0);
447  double deltaH = UpperAlt - BaseAlt;
448  double Tmb = BaseTemp
449  + TemperatureBias
450  + (GradientFadeoutAltitude - BaseAlt)*TemperatureDeltaGradient;
451  if (LapseRates[b] != 0.00) {
452  double Lmb = LapseRates[b];
453  double Exp = g0 / (Rdry*Lmb);
454  double factor = Tmb/(Tmb + Lmb*deltaH);
455  PressureBreakpoints[b+1] = PressureBreakpoints[b]*pow(factor, Exp);
456  } else {
457  PressureBreakpoints[b+1] = PressureBreakpoints[b]*exp(-g0*deltaH/(Rdry*Tmb));
458  }
459  }
460 }
461 
462 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
463 
465 {
466  TemperatureBias = TemperatureDeltaGradient = 0.0;
468  CalculatePressureBreakpoints(SLpressure);
469 
470  SLtemperature = StdSLtemperature;
472 }
473 
474 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
475 
477 {
478  SLpressure = StdSLpressure;
480  CalculatePressureBreakpoints(StdSLpressure);
481 }
482 
483 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
484 
486 {
487  StdDensityBreakpoints.clear();
488  for (unsigned int i = 0; i < StdPressureBreakpoints.size(); i++)
489  StdDensityBreakpoints.push_back(StdPressureBreakpoints[i] / (Rdry * StdAtmosTemperatureTable(i + 1, 1)));
490 }
491 
492 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
493 
494 double FGStandardAtmosphere::CalculateDensityAltitude(double density, double geometricAlt)
495 {
496  // Work out which layer we're dealing with
497  unsigned int b = 0;
498  for (; b < StdDensityBreakpoints.size() - 2; b++) {
499  if (density >= StdDensityBreakpoints[b + 1])
500  break;
501  }
502 
503  // Get layer properties
504  double Tmb = StdAtmosTemperatureTable(b + 1, 1);
505  double Hb = StdAtmosTemperatureTable(b + 1, 0);
506  double Lmb = StdLapseRates[b];
507  double pb = StdDensityBreakpoints[b];
508 
509  double density_altitude = 0.0;
510 
511  // https://en.wikipedia.org/wiki/Barometric_formula for density solved for H
512  if (Lmb != 0.0) {
513  double Exp = -1.0 / (1.0 + g0/(Rdry*Lmb));
514  density_altitude = Hb + (Tmb / Lmb) * (pow(density / pb, Exp) - 1);
515  } else {
516  double Factor = -Rdry*Tmb / g0;
517  density_altitude = Hb + Factor * log(density / pb);
518  }
519 
520  return GeometricAltitude(density_altitude);
521 }
522 
523 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
524 
525 double FGStandardAtmosphere::CalculatePressureAltitude(double pressure, double geometricAlt)
526 {
527  // Work out which layer we're dealing with
528  unsigned int b = 0;
529  for (; b < StdPressureBreakpoints.size() - 2; b++) {
530  if (pressure >= StdPressureBreakpoints[b + 1])
531  break;
532  }
533 
534  // Get layer properties
535  double Tmb = StdAtmosTemperatureTable(b + 1, 1);
536  double Hb = StdAtmosTemperatureTable(b + 1, 0);
537  double Lmb = StdLapseRates[b];
538  double Pb = StdPressureBreakpoints[b];
539 
540  double pressure_altitude = 0.0;
541 
542  if (Lmb != 0.00) {
543  // Equation 33(a) from ISA document solved for H
544  double Exp = -Rdry*Lmb / g0;
545  pressure_altitude = Hb + (Tmb / Lmb) * (pow(pressure / Pb, Exp) - 1);
546  } else {
547  // Equation 33(b) from ISA document solved for H
548  double Factor = -Rdry*Tmb / g0;
549  pressure_altitude = Hb + Factor * log(pressure / Pb);
550  }
551 
552  return GeometricAltitude(pressure_altitude);
553 }
554 
555 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
556 
558 {
559  double temperature_degC = RankineToCelsius(temperature);
560  return a*exp(b*temperature_degC/(c+temperature_degC));
561 }
562 
563 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
564 
566 {
567  if (SaturatedVaporPressure < Pressure) {
568  double VaporPressure = Pressure*VaporMassFraction / (VaporMassFraction+Rdry/Rwater);
569  if (VaporPressure > SaturatedVaporPressure)
570  VaporMassFraction = Rdry * SaturatedVaporPressure / (Rwater * (Pressure - SaturatedVaporPressure));
571  }
572 
573  double GeoPotAlt = GeopotentialAltitude(h);
574  double maxFraction = 1E-6*MaxVaporMassFraction.GetValue(GeoPotAlt);
575 
576  if ((VaporMassFraction > maxFraction) || (VaporMassFraction < 0.0))
577  VaporMassFraction = maxFraction;
578 
579  // Update the gas constant factor
580  Reng = (VaporMassFraction*Rwater + Rdry)/(1.0 + VaporMassFraction);
581 }
582 
583 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
584 
586 {
587  double altitude = CalculatePressureAltitude(Pressure, 0.0);
588  double VaporPressure = CalculateVaporPressure(ConvertToRankine(dewpoint, unit));
589  VaporMassFraction = Rdry * VaporPressure / (Rwater * (Pressure - VaporPressure));
590  ValidateVaporMassFraction(altitude);
591 }
592 
593 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
594 
596 {
597  double dewpoint_degC;
598  double VaporPressure = Pressure*VaporMassFraction / (VaporMassFraction+Rdry/Rwater);
599 
600  if (VaporPressure <= 0.0)
601  dewpoint_degC = -c;
602  else {
603  double x = log(VaporPressure/a);
604  dewpoint_degC = c*x / (b - x);
605  }
606 
607  return ConvertFromRankine(1.8*(dewpoint_degC + 273.15), to);
608 }
609 
610 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
611 
613 {
614  double altitude = CalculatePressureAltitude(Pressure, 0.0);
615  double VaporPressure = ConvertToPSF(Pa, unit);
616  VaporMassFraction = Rdry * VaporPressure / (Rwater * (Pressure - VaporPressure));
617  ValidateVaporMassFraction(altitude);
618 }
619 
620 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
621 
623 {
624  double VaporPressure = Pressure*VaporMassFraction / (VaporMassFraction+Rdry/Rwater);
625  return ConvertFromPSF(VaporPressure, to);
626 }
627 
628 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
629 
631 {
632  return ConvertFromPSF(SaturatedVaporPressure, to);
633 }
634 
635 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
636 
638 {
639  double VaporPressure = Pressure*VaporMassFraction / (VaporMassFraction+Rdry/Rwater);
640  return 100.0*VaporPressure/SaturatedVaporPressure;
641 }
642 
643 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
644 
646 {
647  double altitude = CalculatePressureAltitude(Pressure, 0.0);
648  double VaporPressure = 0.01*RH*SaturatedVaporPressure;
649  VaporMassFraction = Rdry * VaporPressure / (Rwater * (Pressure - VaporPressure));
650  ValidateVaporMassFraction(altitude);
651 }
652 
653 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
654 
656 {
657  return VaporMassFraction*1E6;
658 }
659 
660 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
661 
663 {
664  double altitude = CalculatePressureAltitude(Pressure, 0.0);
665  VaporMassFraction = frac*1E-6;
666  ValidateVaporMassFraction(altitude);
667 }
668 
669 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
670 
671 void FGStandardAtmosphere::bind(void)
672 {
673  typedef double (FGStandardAtmosphere::*PMFi)(int) const;
674  typedef void (FGStandardAtmosphere::*PMF)(int, double);
675  PropertyManager->Tie("atmosphere/delta-T", this, eRankine,
678  PropertyManager->Tie("atmosphere/SL-graded-delta-T", this, eRankine,
681  PropertyManager->Tie("atmosphere/P-sl-psf", this, ePSF,
682  (PMFi)&FGStandardAtmosphere::GetPressureSL,
684  PropertyManager->Tie("atmosphere/dew-point-R", this, eRankine,
687  PropertyManager->Tie("atmosphere/vapor-pressure-psf", this, ePSF,
690  PropertyManager->Tie("atmosphere/saturated-vapor-pressure-psf", this, ePSF,
692  PropertyManager->Tie("atmosphere/RH", this,
695  PropertyManager->Tie("atmosphere/vapor-fraction-ppm", this,
698 }
699 
700 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
701 // The bitmasked value choices are as follows:
702 // unset: In this case (the default) JSBSim would only print
703 // out the normally expected messages, essentially echoing
704 // the config files as they are read. If the environment
705 // variable is not set, debug_lvl is set to 1 internally
706 // 0: This requests JSBSim not to output any messages
707 // whatsoever.
708 // 1: This value explicity requests the normal JSBSim
709 // startup messages
710 // 2: This value asks for a message to be printed out when
711 // a class is instantiated
712 // 4: When this value is set, a message is displayed when a
713 // FGModel object executes its Run() method
714 // 8: When this value is set, various runtime state variables
715 // are printed out periodically
716 // 16: When set various parameters are sanity checked and
717 // a message is printed out when they go out of bounds
718 
719 void FGStandardAtmosphere::Debug(int from)
720 {
721  if (debug_lvl <= 0) return;
722 
723  if (debug_lvl & 1) { // Standard console startup message output
724  if (from == 0) { // Constructor
725  }
726  }
727  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
728  if (from == 0) std::cout << "Instantiated: FGStandardAtmosphere" << std::endl;
729  if (from == 1) std::cout << "Destroyed: FGStandardAtmosphere" << std::endl;
730  }
731  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
732  }
733  if (debug_lvl & 8 ) { // Runtime state variables
734  }
735  if (debug_lvl & 16) { // Sanity checking
736  }
737  if (debug_lvl & 128) { //
738  }
739  if (debug_lvl & 64) {
740  if (from == 0) { // Constructor
741  }
742  }
743 }
744 
745 } // namespace JSBSim
JSBSim::FGFDMExec
Encapsulates the JSBSim simulation executive.
Definition: FGFDMExec.h:185
JSBSim::FGAtmosphere::eTemperature
eTemperature
Enums for specifying temperature units.
Definition: FGAtmosphere.h:80
JSBSim::FGStandardAtmosphere::CalculateVaporPressure
double CalculateVaporPressure(double temperature)
Calculate the pressure of water vapor with the Magnus formula.
Definition: FGStandardAtmosphere.cpp:557
JSBSim::FGStandardAtmosphere::CalculateSLSoundSpeedAndDensity
void CalculateSLSoundSpeedAndDensity(void)
Calculate the SL density and sound speed.
Definition: FGStandardAtmosphere.cpp:234
JSBSim::FGStandardAtmosphere::ResetSLPressure
virtual void ResetSLPressure()
Resets the sea level to the Standard sea level pressure, and recalculates dependent parameters so tha...
Definition: FGStandardAtmosphere.cpp:476
JSBSim::FGAtmosphere::ConvertToRankine
double ConvertToRankine(double t, eTemperature unit) const
Converts to Rankine from one of several unit systems.
Definition: FGAtmosphere.cpp:169
JSBSim::FGAtmosphere::GetPressure
virtual double GetPressure(void) const
Returns the pressure in psf.
Definition: FGAtmosphere.h:144
JSBSim::FGStandardAtmosphere::SetTemperatureGradedDelta
virtual void SetTemperatureGradedDelta(double t, double h, eTemperature unit=eFahrenheit)
Sets the temperature delta value at the supplied altitude/elevation above sea level,...
Definition: FGStandardAtmosphere.cpp:383
JSBSim::FGStandardAtmosphere::SetTemperature
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...
Definition: FGStandardAtmosphere.cpp:327
JSBSim::FGStandardAtmosphere::FGStandardAtmosphere
FGStandardAtmosphere(FGFDMExec *)
Constructor.
Definition: FGStandardAtmosphere.cpp:59
JSBSim::FGAtmosphere::ePressure
ePressure
Enums for specifying pressure units.
Definition: FGAtmosphere.h:83
JSBSim::FGStandardAtmosphere::GetSaturatedVaporPressure
double GetSaturatedVaporPressure(ePressure to) const
Returns the saturated pressure of water vapor.
Definition: FGStandardAtmosphere.cpp:630
JSBSim::FGAtmosphere::ConvertToPSF
double ConvertToPSF(double t, ePressure unit=ePSF) const
Converts to PSF (pounds per square foot) from one of several unit systems.
Definition: FGAtmosphere.cpp:221
JSBSim::FGStandardAtmosphere::SetVaporPressure
void SetVaporPressure(ePressure unit, double Pv)
Sets the partial pressure of water vapor.
Definition: FGStandardAtmosphere.cpp:612
JSBSim::FGAtmosphere::g0
static constexpr double g0
Sea-level acceleration of gravity - ft/s^2.
Definition: FGAtmosphere.h:268
JSBSim::FGStandardAtmosphere::SetVaporMassFractionPPM
void SetVaporMassFractionPPM(double frac)
Sets the vapor mass per million of dry air mass units.
Definition: FGStandardAtmosphere.cpp:662
JSBSim::FGAtmosphere::ConvertFromPSF
double ConvertFromPSF(double t, ePressure unit=ePSF) const
Converts from PSF (pounds per square foot) to one of several unit systems.
Definition: FGAtmosphere.cpp:245
JSBSim::FGStandardAtmosphere::CalculateDensityAltitude
double CalculateDensityAltitude(double density, double geometricAlt) override
Calculates the density altitude given any temperature or pressure bias.
Definition: FGStandardAtmosphere.cpp:494
JSBSim::FGStandardAtmosphere::CalculatePressureAltitude
double CalculatePressureAltitude(double pressure, double geometricAlt) override
Calculates the pressure altitude given any temperature or pressure bias.
Definition: FGStandardAtmosphere.cpp:525
JSBSim::FGStandardAtmosphere::GeometricAltitude
double GeometricAltitude(double geopotalt) const
Convert a geopotential altitude to a geometric altitude.
Definition: FGStandardAtmosphere.h:322
JSBSim::FGStandardAtmosphere::CalculateStdDensityBreakpoints
void CalculateStdDensityBreakpoints()
Calculate the atmospheric density breakpoints at the altitudes in the standard temperature table.
Definition: FGStandardAtmosphere.cpp:485
JSBSim::FGAtmosphere::Reng
static double Reng
Specific gas constant for air - ft*lbf/slug/R.
Definition: FGAtmosphere.h:270
JSBSim::FGStandardAtmosphere::Calculate
void Calculate(double altitude) override
Calculate the atmosphere for the given altitude.
Definition: FGStandardAtmosphere.cpp:179
JSBSim::FGStandardAtmosphere::StdSLtemperature
double StdSLtemperature
Standard sea level conditions.
Definition: FGStandardAtmosphere.h:286
JSBSim::FGStandardAtmosphere::PrintStandardAtmosphereTable
virtual void PrintStandardAtmosphereTable()
Prints the U.S. Standard Atmosphere table.
Definition: FGStandardAtmosphere.cpp:398
JSBSim::FGAtmosphere
Models an empty, abstract base atmosphere class.
Definition: FGAtmosphere.h:76
JSBSim::FGStandardAtmosphere::ResetSLTemperature
virtual void ResetSLTemperature()
This function resets the model to apply no bias or delta gradient to the temperature.
Definition: FGStandardAtmosphere.cpp:464
JSBSim::FGAtmosphere::Calculate
virtual void Calculate(double altitude)
Calculate the atmosphere for the given altitude.
Definition: FGAtmosphere.cpp:107
JSBSim::FGStandardAtmosphere::SetTemperatureBias
virtual void SetTemperatureBias(eTemperature unit, double t)
Sets the temperature bias to be added to the standard temperature at all altitudes.
Definition: FGStandardAtmosphere.cpp:345
JSBSim::FGStandardAtmosphere::GetVaporPressure
double GetVaporPressure(ePressure to) const
Returns the partial pressure of water vapor.
Definition: FGStandardAtmosphere.cpp:622
JSBSim::FGJSBBase::RankineToCelsius
static constexpr double RankineToCelsius(double rankine)
Converts from degrees Rankine to degrees Celsius.
Definition: FGJSBBase.h:209
JSBSim::FGStandardAtmosphere::CalculateSLDensity
void CalculateSLDensity(void)
Calculate the SL density.
Definition: FGStandardAtmosphere.h:352
JSBSim::FGStandardAtmosphere::SetTemperatureSL
void SetTemperatureSL(double t, eTemperature unit=eFahrenheit) override
Sets the Sea Level temperature, if it is to be different than the standard.
Definition: FGStandardAtmosphere.cpp:363
JSBSim::FGStandardAtmosphere::~FGStandardAtmosphere
virtual ~FGStandardAtmosphere()
Destructor.
Definition: FGStandardAtmosphere.cpp:145
JSBSim::FGStandardAtmosphere::SetDewPoint
void SetDewPoint(eTemperature unit, double dewpoint)
Sets the dew point.
Definition: FGStandardAtmosphere.cpp:585
JSBSim::FGStandardAtmosphere::GeopotentialAltitude
double GeopotentialAltitude(double geometalt) const
Convert a geometric altitude to a geopotential altitude.
Definition: FGStandardAtmosphere.h:319
JSBSim::FGAtmosphere::ConvertFromRankine
double ConvertFromRankine(double t, eTemperature unit) const
Converts from Rankine to one of several unit systems.
Definition: FGAtmosphere.cpp:195
JSBSim::FGStandardAtmosphere::GetStdPressure
virtual double GetStdPressure(double altitude) const
Returns the standard pressure at the specified altitude.
Definition: FGStandardAtmosphere.cpp:285
JSBSim::FGStandardAtmosphere::GetRelativeHumidity
double GetRelativeHumidity(void) const
Returns the relative humidity in percent.
Definition: FGStandardAtmosphere.cpp:637
JSBSim::FGStandardAtmosphere::GetStdTemperature
virtual double GetStdTemperature(double altitude) const
Returns the standard temperature in degrees Rankine at a specified altitude.
Definition: FGStandardAtmosphere.cpp:273
JSBSim::FGStandardAtmosphere::ValidateVaporMassFraction
void ValidateVaporMassFraction(double geometricAlt)
Validate the value of the vapor mass fraction.
Definition: FGStandardAtmosphere.cpp:565
JSBSim::FGAtmosphere::GetTemperature
virtual double GetTemperature() const
Returns the actual, modeled temperature at the current altitude in degrees Rankine.
Definition: FGAtmosphere.h:109
JSBSim::FGStandardAtmosphere::SetSLTemperatureGradedDelta
virtual void SetSLTemperatureGradedDelta(eTemperature unit, double t)
Sets a Sea Level temperature delta that is ramped out by 86 km.
Definition: FGStandardAtmosphere.cpp:371
JSBSim::FGStandardAtmosphere::GetDewPoint
double GetDewPoint(eTemperature to) const
Returns the dew point.
Definition: FGStandardAtmosphere.cpp:595
JSBSim::FGStandardAtmosphere::GetTemperatureBias
virtual double GetTemperatureBias(eTemperature to) const
Returns the temperature bias over the sea level value in degrees Rankine.
Definition: FGStandardAtmosphere.h:142
JSBSim::FGStandardAtmosphere::a
static constexpr double a
Sonntag constants based on ref [2].
Definition: FGStandardAtmosphere.h:365
JSBSim::FGStandardAtmosphere::SetRelativeHumidity
void SetRelativeHumidity(double RH)
Sets the relative humidity.
Definition: FGStandardAtmosphere.cpp:645
JSBSim::FGStandardAtmosphere::GetStdDensity
virtual double GetStdDensity(double altitude) const
Returns the standard density at a specified altitude.
Definition: FGStandardAtmosphere.cpp:320
JSBSim::FGStandardAtmosphere::GetTemperatureDeltaGradient
virtual double GetTemperatureDeltaGradient(eTemperature to)
Returns the temperature gradient to be applied on top of the standard temperature gradient.
Definition: FGStandardAtmosphere.h:147
JSBSim::FGAtmosphere::Run
bool Run(bool Holding) override
Runs the atmosphere forces model; called by the Executive.
Definition: FGAtmosphere.cpp:94
JSBSim::FGStandardAtmosphere::GetVaporMassFractionPPM
double GetVaporMassFractionPPM(void) const
Returns the vapor mass per million of dry air mass units (ppm).
Definition: FGStandardAtmosphere.cpp:655
JSBSim::FGStandardAtmosphere::CalculateLapseRates
void CalculateLapseRates()
Recalculate the lapse rate vectors when the temperature profile is altered in a way that would change...
Definition: FGStandardAtmosphere.cpp:422
JSBSim::FGStandardAtmosphere
Models the 1976 U.S.
Definition: FGStandardAtmosphere.h:98
JSBSim::FGStandardAtmosphere::CalculatePressureBreakpoints
void CalculatePressureBreakpoints(double SLpress)
Calculate (or recalculate) the atmospheric pressure breakpoints at the altitudes in the standard temp...
Definition: FGStandardAtmosphere.cpp:439
JSBSim::FGStandardAtmosphere::SetPressureSL
void SetPressureSL(ePressure unit, double pressure) override
Sets the sea level pressure for modeling an off-standard pressure profile.
Definition: FGStandardAtmosphere.cpp:225
JSBSim::FGPropertyManager::Tie
void Tie(const std::string &name, T *pointer)
Tie a property to an external variable.
Definition: FGPropertyManager.h:449