1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 Module: FGStandardAtmosphere.cpp
4 Author: Jon Berndt, Tony Peden
6 Purpose: Models the 1976 U.S. Standard Atmosphere
9 ------------- Copyright (C) 2011 Jon S. Berndt (jon@jsbsim.org) -------------
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 Software
13 Foundation; either version 2 of the License, or (at your option) any later
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
21 You should have received a copy of the GNU Lesser General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
23 Place - Suite 330, Boston, MA 02111-1307, USA.
25 Further information about the GNU Lesser General Public License can also be found on
26 the world wide web at http://www.gnu.org.
28 FUNCTIONAL DESCRIPTION
29 --------------------------------------------------------------------------------
33 --------------------------------------------------------------------------------
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
41 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
48 #include "FGFDMExec.h"
49 #include "FGStandardAtmosphere.h"
53 IDENT(IdSrc,"$Id: FGStandardAtmosphere.cpp,v 1.24 2014/05/17 15:07:48 jberndt Exp $");
54 IDENT(IdHdr,ID_STANDARDATMOSPHERE);
56 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
58 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
60 FGStandardAtmosphere::FGStandardAtmosphere(FGFDMExec* fdmex) : FGAtmosphere(fdmex),
62 TemperatureDeltaGradient(0.0)
64 Name = "FGStandardAtmosphere";
66 StdAtmosTemperatureTable = new FGTable(9);
68 // This is the U.S. Standard Atmosphere table for temperature in degrees
69 // Rankine, based on geometric altitude. The table values are often given
70 // in literature relative to geopotential altitude.
72 // GeoMet Alt Temp GeoPot Alt GeoMet Alt
73 // (ft) (deg R) (km) (km)
74 // -------- -------- ---------- ----------
75 //*StdAtmosTemperatureTable << 0.00 << 518.67 // 0.000 0.000
76 // << 36151.80 << 389.97 // 11.000 11.019
77 // << 65823.90 << 389.97 // 20.000 20.063
78 // << 105518.06 << 411.60 // 32.000 32.162
79 // << 155348.07 << 487.20 // 47.000 47.350
80 // << 168676.12 << 487.20 // 51.000 51.413
81 // << 235570.77 << 386.40 // 71.000 71.802
82 // << 282152.08 << 336.50 // 84.852 86.000
83 // << 298556.40 << 336.50; // 91.000 - First layer in high altitude regime
85 // GeoPot Alt Temp GeoPot Alt GeoMet Alt
86 // (ft) (deg R) (km) (km)
87 // ----------- -------- ---------- ----------
88 *StdAtmosTemperatureTable << 0.0000 << 518.67 // 0.000 0.000
89 << 36089.2388 << 389.97 // 11.000 11.019
90 << 65616.7979 << 389.97 // 20.000 20.063
91 << 104986.8766 << 411.57 // 32.000 32.162
92 << 154199.4751 << 487.17 // 47.000 47.350
93 << 167322.8346 << 487.17 // 51.000 51.413
94 << 232939.6325 << 386.37 // 71.000 71.802
95 << 278385.8268 << 336.50 // 84.852 86.000
96 << 298556.40 << 336.50; // 91.000 - First layer in high altitude regime
98 LapseRateVector.resize(StdAtmosTemperatureTable->GetNumRows()-1);
99 PressureBreakpointVector.resize(StdAtmosTemperatureTable->GetNumRows());
101 // Assume the altitude to fade out the gradient at is at the highest
102 // altitude in the table. Above that, other functions are used to
103 // calculate temperature.
104 GradientFadeoutAltitude = (*StdAtmosTemperatureTable)(StdAtmosTemperatureTable->GetNumRows(),0);
110 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
112 FGStandardAtmosphere::~FGStandardAtmosphere()
114 delete StdAtmosTemperatureTable;
115 LapseRateVector.clear();
119 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
121 bool FGStandardAtmosphere::InitModel(void)
123 PressureBreakpointVector[0] = StdSLpressure = 2116.22; // psf
124 TemperatureDeltaGradient = 0.0;
125 TemperatureBias = 0.0;
126 CalculateLapseRates();
127 CalculatePressureBreakpoints();
129 StdSLtemperature = SLtemperature = Temperature;
130 SLpressure = Pressure;
131 StdSLdensity = SLdensity = Density;
132 StdSLsoundspeed = SLsoundspeed = Soundspeed;
134 rSLtemperature = 1/SLtemperature ;
135 rSLpressure = 1/SLpressure ;
136 rSLdensity = 1/SLdensity ;
137 rSLsoundspeed = 1/SLsoundspeed ;
139 // PrintStandardAtmosphereTable();
144 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
145 // Get the actual pressure as modeled at a specified altitude
146 // These calculations are from equations 33a and 33b in the U.S. Standard Atmosphere
147 // document referenced in the documentation for this code.
149 double FGStandardAtmosphere::GetPressure(double altitude) const
152 double pressure = 0.0;
153 double Lmb, Exp, Tmb, deltaH, factor;
154 double numRows = StdAtmosTemperatureTable->GetNumRows();
156 // Iterate through the altitudes to find the current Base Altitude
157 // in the table. That is, if the current altitude (the argument passed in)
158 // is 20000 ft, then the base altitude from the table is 0.0. If the
159 // passed-in altitude is 40000 ft, the base altitude is 36089.2388 ft (and
160 // the index "b" is 2 - the second entry in the table).
161 double testAlt = (*StdAtmosTemperatureTable)(b+1,0);
162 double GeoPotAlt = (altitude*20855531.5)/(20855531.5+altitude);
163 while ((GeoPotAlt >= testAlt) && (b <= numRows-2)) {
165 testAlt = (*StdAtmosTemperatureTable)(b+1,0);
169 double BaseAlt = (*StdAtmosTemperatureTable)(b+1,0);
170 Tmb = GetTemperature(BaseAlt);
171 deltaH = GeoPotAlt - BaseAlt;
173 if (LapseRateVector[b] != 0.00) {
174 Lmb = LapseRateVector[b];
175 Exp = Mair/(Rstar*Lmb);
176 factor = Tmb/(Tmb + Lmb*deltaH);
177 pressure = PressureBreakpointVector[b]*pow(factor, Exp);
179 pressure = PressureBreakpointVector[b]*exp(-Mair*deltaH/(Rstar*Tmb));
185 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
187 void FGStandardAtmosphere::SetPressureSL(ePressure unit, double pressure)
189 double press = ConvertToPSF(pressure, unit);
191 PressureBreakpointVector[0] = press;
192 CalculatePressureBreakpoints();
195 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
196 // Get the modeled temperature at a specified altitude, including any bias or gradient
199 double FGStandardAtmosphere::GetTemperature(double altitude) const
201 double GeoPotAlt = (altitude*20855531.5)/(20855531.5+altitude);
203 double T = StdAtmosTemperatureTable->GetValue(GeoPotAlt) + TemperatureBias;
204 if (altitude <= GradientFadeoutAltitude)
205 T += TemperatureDeltaGradient * (GradientFadeoutAltitude - altitude);
210 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
211 // Retrieves the standard temperature at a particular altitude.
213 double FGStandardAtmosphere::GetStdTemperature(double altitude) const
215 double Lk9 = 0.00658368; // deg R per foot
216 double Tinf = 1800.0; // Same as 1000 Kelvin
219 if (altitude < 298556.4) { // 91 km - station 8
221 double GeoPotAlt = (altitude*20855531.5)/(20855531.5+altitude);
222 temp = StdAtmosTemperatureTable->GetValue(GeoPotAlt);
224 } else if (altitude < 360892.4) { // 110 km - station 9
226 temp = 473.7429 - 137.38176 * sqrt(1.0 - pow((altitude - 298556.4)/65429.462, 2.0));
228 } else if (altitude < 393700.8) { // 120 km - station 10
230 temp = 432 + Lk9 * (altitude - 360892.4);
232 } else if (altitude < 3280839.9) { // 1000 km station 12
234 double lambda = 0.00001870364;
235 double eps = (altitude - 393700.8) * (20855531.5 + 393700.8) / (20855531.5 + altitude);
236 temp = Tinf - (Tinf - 648.0) * exp(-lambda*eps);
243 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
245 double FGStandardAtmosphere::GetStdPressure(double altitude) const
248 if (TemperatureBias == 0.0 && TemperatureDeltaGradient == 0.0 && PressureBreakpointVector[0] == StdSLpressure) {
249 press = GetPressure(altitude);
250 } else if (altitude <= 100000.0) {
251 GetStdPressure100K(altitude);
253 // Cannot currently retrieve the standard pressure
258 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
259 // This function calculates an approximation of the standard atmospheric pressure
260 // up to an altitude of about 100,000 ft. If the temperature and pressure are not
261 // altered for local conditions, the GetPressure(h) function should be used,
262 // as that is valid to a much higher altitude. This function is accurate to within
263 // a couple of psf up to 100K ft. This polynomial fit was determined using Excel.
265 double FGStandardAtmosphere::GetStdPressure100K(double altitude) const
267 // Limit this equation to input altitudes of 100000 ft.
268 if (altitude > 100000.0) altitude = 100000.0;
271 const double coef[5] = { 2116.217,
278 for (int pwr=1; pwr<=4; pwr++) alt[pwr] = alt[pwr-1]*altitude;
281 for (int ctr=0; ctr<=4; ctr++) press += coef[ctr]*alt[ctr];
285 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
286 // Get the standard density at a specified altitude
288 double FGStandardAtmosphere::GetStdDensity(double altitude) const
290 return GetStdPressure(altitude)/(Reng * GetStdTemperature(altitude));
293 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
295 void FGStandardAtmosphere::SetTemperature(double t, double h, eTemperature unit)
297 double targetSLtemp = ConvertToRankine(t, unit);
299 TemperatureBias = 0.0;
300 TemperatureBias = targetSLtemp - GetTemperature(h);
301 CalculatePressureBreakpoints();
304 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
306 void FGStandardAtmosphere::SetTemperatureBias(eTemperature unit, double t)
308 if (unit == eCelsius || unit == eKelvin)
309 t *= 1.80; // If temp delta "t" is given in metric, scale up to English
312 CalculatePressureBreakpoints();
315 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
316 // This function calculates a bias based on the supplied temperature for sea
317 // level. The bias is applied to the entire temperature profile at all altitudes.
318 // Internally, the Rankine scale is used for calculations, so any temperature
319 // supplied must be converted to that unit.
321 void FGStandardAtmosphere::SetTemperatureSL(double t, eTemperature unit)
323 SetTemperature(t, 0.0, unit);
326 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
327 // Sets a Sea Level temperature delta that is ramped out by 86 km (282,152 ft).
329 void FGStandardAtmosphere::SetSLTemperatureGradedDelta(eTemperature unit, double deltemp)
331 SetTemperatureGradedDelta(deltemp, 0.0, unit);
334 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
335 // Sets a temperature delta at the supplied altitude that is ramped out by 86 km.
336 // After this calculation is performed, the lapse rates and pressure breakpoints
337 // must be recalculated. Since we are calculating a delta here and not an actual
338 // temperature, we only need to be concerned about a scale factor and not
339 // the actual temperature itself.
341 void FGStandardAtmosphere::SetTemperatureGradedDelta(double deltemp, double h, eTemperature unit)
343 if (unit == eCelsius || unit == eKelvin)
344 deltemp *= 1.80; // If temp delta "t" is given in metric, scale up to English
346 TemperatureDeltaGradient = deltemp/(GradientFadeoutAltitude - h);
347 CalculateLapseRates();
348 CalculatePressureBreakpoints();
351 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
353 void FGStandardAtmosphere::PrintStandardAtmosphereTable()
355 std::cout << "Altitude (ft) Temp (F) Pressure (psf) Density (sl/ft3)" << std::endl;
356 std::cout << "------------- -------- -------------- ----------------" << std::endl;
357 for (int i=0; i<280000; i+=1000) {
359 std::cout << std::setw(12) << std::setprecision(2) << i
360 << " " << std::setw(9) << std::setprecision(2) << Temperature - 459.67
361 << " " << std::setw(13) << std::setprecision(4) << Pressure
362 << " " << std::setw(18) << std::setprecision(8) << Density
366 // Re-execute the Run() method to reset the calculated values
370 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
371 // This function calculates (or recalculates) the lapse rate over an altitude range
372 // where the "bh" in this case refers to the index of the base height in the
373 // StdAtmosTemperatureTable table. This function should be called anytime the
374 // temperature table is altered, such as when a gradient is applied across the
375 // temperature table for a range of altitudes.
377 void FGStandardAtmosphere::CalculateLapseRates()
379 for (unsigned int bh=0; bh<LapseRateVector.size(); bh++)
381 double t0 = (*StdAtmosTemperatureTable)(bh+1,1);
382 double t1 = (*StdAtmosTemperatureTable)(bh+2,1);
383 double h0 = (*StdAtmosTemperatureTable)(bh+1,0);
384 double h1 = (*StdAtmosTemperatureTable)(bh+2,0);
385 LapseRateVector[bh] = (t1 - t0) / (h1 - h0) + TemperatureDeltaGradient;
389 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
391 void FGStandardAtmosphere::CalculatePressureBreakpoints()
393 for (unsigned int b=0; b<PressureBreakpointVector.size()-1; b++) {
394 double BaseTemp = (*StdAtmosTemperatureTable)(b+1,1);
395 double BaseAlt = (*StdAtmosTemperatureTable)(b+1,0);
396 double UpperAlt = (*StdAtmosTemperatureTable)(b+2,0);
397 double deltaH = UpperAlt - BaseAlt;
398 double Tmb = BaseTemp
400 + (GradientFadeoutAltitude - BaseAlt)*TemperatureDeltaGradient;
401 if (LapseRateVector[b] != 0.00) {
402 double Lmb = LapseRateVector[b];
403 double Exp = Mair/(Rstar*Lmb);
404 double factor = Tmb/(Tmb + Lmb*deltaH);
405 PressureBreakpointVector[b+1] = PressureBreakpointVector[b]*pow(factor, Exp);
407 PressureBreakpointVector[b+1] = PressureBreakpointVector[b]*exp(-Mair*deltaH/(Rstar*Tmb));
412 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
414 void FGStandardAtmosphere::ResetSLTemperature()
416 TemperatureBias = TemperatureDeltaGradient = 0.0;
417 CalculateLapseRates();
418 CalculatePressureBreakpoints();
421 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
423 void FGStandardAtmosphere::ResetSLPressure()
425 PressureBreakpointVector[0] = StdSLpressure; // psf
426 CalculatePressureBreakpoints();
429 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
431 void FGStandardAtmosphere::bind(void)
433 typedef double (FGStandardAtmosphere::*PMFi)(int) const;
434 typedef void (FGStandardAtmosphere::*PMF)(int, double);
435 PropertyManager->Tie("atmosphere/delta-T", this, eRankine,
436 (PMFi)&FGStandardAtmosphere::GetTemperatureBias,
437 (PMF)&FGStandardAtmosphere::SetTemperatureBias);
438 PropertyManager->Tie("atmosphere/SL-graded-delta-T", this, eRankine,
439 (PMFi)&FGStandardAtmosphere::GetTemperatureDeltaGradient,
440 (PMF)&FGStandardAtmosphere::SetSLTemperatureGradedDelta);
441 PropertyManager->Tie("atmosphere/P-sl-psf", this, ePSF,
442 (PMFi)&FGStandardAtmosphere::GetPressureSL,
443 (PMF)&FGStandardAtmosphere::SetPressureSL);
446 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
447 // The bitmasked value choices are as follows:
448 // unset: In this case (the default) JSBSim would only print
449 // out the normally expected messages, essentially echoing
450 // the config files as they are read. If the environment
451 // variable is not set, debug_lvl is set to 1 internally
452 // 0: This requests JSBSim not to output any messages
454 // 1: This value explicity requests the normal JSBSim
456 // 2: This value asks for a message to be printed out when
457 // a class is instantiated
458 // 4: When this value is set, a message is displayed when a
459 // FGModel object executes its Run() method
460 // 8: When this value is set, various runtime state variables
461 // are printed out periodically
462 // 16: When set various parameters are sanity checked and
463 // a message is printed out when they go out of bounds
465 void FGStandardAtmosphere::Debug(int from)
467 if (debug_lvl <= 0) return;
469 if (debug_lvl & 1) { // Standard console startup message output
470 if (from == 0) { // Constructor
473 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
474 if (from == 0) std::cout << "Instantiated: FGStandardAtmosphere" << std::endl;
475 if (from == 1) std::cout << "Destroyed: FGStandardAtmosphere" << std::endl;
477 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
479 if (debug_lvl & 8 ) { // Runtime state variables
481 if (debug_lvl & 16) { // Sanity checking
483 if (debug_lvl & 128) { //
485 if (debug_lvl & 64) {
486 if (from == 0) { // Constructor
487 std::cout << IdSrc << std::endl;
488 std::cout << IdHdr << std::endl;
493 } // namespace JSBSim