]> git.mxchange.org Git - flightgear.git/blob - src/Environment/fgclouds.cxx
Add missing include files needed by the new math code under windows
[flightgear.git] / src / Environment / fgclouds.cxx
1 // Build a cloud layer based on metar
2 //
3 // Written by Harald JOHNSEN, started April 2005.
4 //
5 // Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
20 //
21 //
22
23 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #endif
26
27 #include <Main/fg_props.hxx>
28
29 #include <simgear/constants.h>
30 #include <simgear/sound/soundmgr_openal.hxx>
31 #include <simgear/scene/sky/sky.hxx>
32 #include <simgear/environment/visual_enviro.hxx>
33 #include <simgear/scene/sky/cloudfield.hxx>
34 #include <simgear/scene/sky/newcloud.hxx>
35 #include <simgear/math/sg_random.h>
36 #include <Main/globals.hxx>
37 #include <Airports/simple.hxx>
38 #include <Main/util.hxx>
39
40 #include "environment_ctrl.hxx"
41 #include "environment_mgr.hxx"
42 #include "fgmetar.hxx"
43 #include "fgclouds.hxx"
44
45 extern SGSky *thesky;
46
47
48 FGClouds::FGClouds(FGEnvironmentCtrl * controller) :
49         station_elevation_ft(0.0),
50         _controller( controller ),
51         snd_lightning(NULL),
52     last_scenario( "none" ),
53     last_env_config( new SGPropertyNode() ),
54     last_env_clouds( new SGPropertyNode() )
55 {
56         update_event = 0;
57         fgSetString("/environment/weather-scenario", last_scenario.c_str());
58 }
59 FGClouds::~FGClouds() {
60 }
61
62 int FGClouds::get_update_event(void) const {
63         return update_event;
64 }
65 void FGClouds::set_update_event(int count) {
66         update_event = count;
67         build();
68 }
69
70 void FGClouds::init(void) {
71         if( snd_lightning == NULL ) {
72                 snd_lightning = new SGSoundSample(globals->get_fg_root().c_str(), "Sounds/thunder.wav");
73                 snd_lightning->set_max_dist(7000.0f);
74                 snd_lightning->set_reference_dist(3000.0f);
75                 SGSoundMgr *soundMgr = globals->get_soundmgr();
76                 soundMgr->add( snd_lightning, "thunder" );
77                 sgEnviro.set_soundMgr( soundMgr );
78         }
79 }
80
81 SGNewCloud *FGClouds::buildCloud(SGPropertyNode *cloud_def_root, const string& name) {
82         SGPropertyNode *cld_def=NULL;
83
84         cld_def = cloud_def_root->getChild(name.c_str());
85         string base_name = name.substr(0,2);
86         if( !cld_def ) {
87                 if( name[2] == '-' ) {
88                         cld_def = cloud_def_root->getChild(base_name.c_str());
89                 }
90                 if( !cld_def )
91                         return NULL;
92         }
93         string familly = cld_def->getStringValue("familly", base_name.c_str());
94         SGNewCloud *cld = new SGNewCloud(familly);
95         for(int i = 0; i < cld_def->nChildren() ; i++) {
96                 SGPropertyNode *abox = cld_def->getChild(i);
97                 if( strcmp(abox->getName(), "box") == 0) {
98                         double x = abox->getDoubleValue("x");
99                         double y = abox->getDoubleValue("y");
100                         double z = abox->getDoubleValue("z");
101                         double size = abox->getDoubleValue("size");
102                         int type = abox->getIntValue("type", SGNewCloud::CLbox_standard);
103                         cld->addContainer(x, y, z, size, (SGNewCloud::CLbox_type) type);
104                 }
105         }
106         cld->genSprites();
107         return cld;
108 }
109
110 void FGClouds::buildLayer(SGCloudField *layer, const string& name, double alt, double coverage) {
111         struct {
112                 string name;
113                 double count;
114         } tCloudVariety[20];
115         int CloudVarietyCount = 0;
116         double totalCount = 0.0;
117
118         SGPropertyNode *cloud_def_root = fgGetNode("/environment/cloudlayers/clouds", false);
119         SGPropertyNode *layer_def_root = fgGetNode("/environment/cloudlayers/layers", false);
120
121         layer->clear();
122         // when we don't generate clouds the layer is rendered in 2D
123         if( coverage == 0.0 )
124                 return;
125         if( layer_def_root == NULL || cloud_def_root == NULL)
126                 return;
127         if( name == "ci" || name == "sc" || name == "st")
128                 return;
129
130         SGPropertyNode *layer_def=NULL;
131
132         layer_def = layer_def_root->getChild(name.c_str());
133         if( !layer_def ) {
134                 if( name[2] == '-' ) {
135                         string base_name = name.substr(0,2);
136                         layer_def = layer_def_root->getChild(base_name.c_str());
137                 }
138                 if( !layer_def )
139                         return;
140         }
141
142         double grid_x_size = layer_def->getDoubleValue("grid-x-size", 1000.0);
143         double grid_y_size = layer_def->getDoubleValue("grid-y-size", 1000.0);
144         double grid_x_rand = layer_def->getDoubleValue("grid-x-rand", grid_x_size);
145         double grid_y_rand = layer_def->getDoubleValue("grid-y-rand", grid_y_size);
146         double grid_z_rand = layer_def->getDoubleValue("grid-z-rand");
147
148         for(int i = 0; i < layer_def->nChildren() ; i++) {
149                 SGPropertyNode *acloud = layer_def->getChild(i);
150                 if( strcmp(acloud->getName(), "cloud") == 0) {
151                         string cloud_name = acloud->getStringValue("name");
152                         tCloudVariety[CloudVarietyCount].name = cloud_name;
153                         double count = acloud->getDoubleValue("count", 1.0);
154                         tCloudVariety[CloudVarietyCount].count = count;
155                         int variety = 0;
156                         cloud_name = cloud_name + "-%d";
157                         char variety_name[50];
158                         do {
159                                 variety++;
160                                 snprintf(variety_name, sizeof(variety_name), cloud_name.c_str(), variety);
161                         } while( cloud_def_root->getChild(variety_name, 0, false) );
162
163                         totalCount += count;
164                         if( CloudVarietyCount < 20 )
165                                 CloudVarietyCount++;
166                 }
167         }
168         totalCount = 1.0 / totalCount;
169         double currCoverage = 0.0;
170
171         for(double px = 0.0; px < SGCloudField::fieldSize; px += grid_x_size) {
172                 for(double py = 0.0; py < SGCloudField::fieldSize; py += grid_y_size) {
173                         double x = px + grid_x_rand * (sg_random() - 0.5);
174                         double y = py + grid_y_rand * (sg_random() - 0.5);
175                         double z = alt + grid_z_rand * (sg_random() - 0.5);
176                         double choice = sg_random();
177                         currCoverage += coverage;
178                         if( currCoverage < 1.0 )
179                                 continue;
180                         currCoverage -= 1.0;
181
182                         for(int i = 0; i < CloudVarietyCount ; i ++) {
183                                 choice -= tCloudVariety[i].count * totalCount;
184                                 if( choice <= 0.0 ) {
185                                         SGNewCloud *cld = buildCloud(cloud_def_root, tCloudVariety[i].name);
186                                         sgVec3 pos={x,z,y};
187                                         if( cld )
188                                                 layer->addCloud(pos, cld);
189
190                                         break;
191                                 }
192                         }
193                 }
194         }
195
196 }
197
198 // TODO:call this after real metar updates
199 void FGClouds::buildMETAR(void) {
200         SGPropertyNode *metar_root = fgGetNode("/environment", true);
201
202         double wind_speed_kt     = metar_root->getDoubleValue("wind-speed-kt");
203         double temperature_degc  = metar_root->getDoubleValue("temperature-sea-level-degc");
204         double dewpoint_degc     = metar_root->getDoubleValue("dewpoint-sea-level-degc");
205         double pressure_mb              = metar_root->getDoubleValue("pressure-sea-level-inhg") * SG_INHG_TO_PA / 100.0;
206
207         double dewp = pow(10.0, 7.5 * dewpoint_degc / (237.7 + dewpoint_degc));
208         double temp = pow(10.0, 7.5 * temperature_degc / (237.7 + temperature_degc));
209         double rel_humidity = dewp * 100 / temp;
210
211         // formule d'Epsy, base d'un cumulus
212         double cumulus_base = 122.0 * (temperature_degc - dewpoint_degc);
213         double stratus_base = 100.0 * (100.0 - rel_humidity) * SG_FEET_TO_METER;
214
215         bool cu_seen = false;
216
217         for(int iLayer = 0 ; iLayer < thesky->get_cloud_layer_count(); iLayer++) {
218                 SGPropertyNode *cloud_root = fgGetNode("/environment/clouds/layer", iLayer, true);
219
220                 double alt_ft = cloud_root->getDoubleValue("elevation-ft");
221                 double alt_m = alt_ft * SG_FEET_TO_METER;
222                 string coverage = cloud_root->getStringValue("coverage");
223                 double coverage_norm = 0.0;
224                 if( coverage == "few" )
225                         coverage_norm = 2.0/8.0;        // <1-2
226                 else if( coverage == "scattered" )
227                         coverage_norm = 4.0/8.0;        // 3-4
228                 else if( coverage == "broken" )
229                         coverage_norm = 6.0/8.0;        // 5-7
230                 else if( coverage == "overcast" )
231                         coverage_norm = 8.0/8.0;        // 8
232
233                 string layer_type = "nn";
234                 if( coverage == "cirrus" ) {
235                         layer_type = "ci";
236                 } else if( alt_ft > 16500 ) {
237 //                      layer_type = "ci|cs|cc";
238                         layer_type = "ci";
239                 } else if( alt_ft > 6500 ) {
240 //                      layer_type = "as|ac|ns";
241                         layer_type = "ac";
242                         if( pressure_mb < 1005.0 && coverage_norm >= 5.5 )
243                                 layer_type = "ns";
244                 } else {
245 //                      layer_type = "st|cu|cb|sc";
246                         // +/- 20% from stratus probable base
247                         if( stratus_base * 0.80 < alt_m && stratus_base * 1.40 > alt_m )
248                                 layer_type = "st";
249                         // +/- 20% from cumulus probable base
250                         else if( cumulus_base * 0.80 < alt_m && cumulus_base * 1.20 > alt_m )
251                                 layer_type = "cu";
252                         else {
253                                 // above formulae is far from perfect
254                                 if ( alt_ft < 2000 )
255                                         layer_type = "st";
256                                 else if( alt_ft < 4500 )
257                                         layer_type = "cu";
258                                 else
259                                         layer_type = "sc";
260                         }
261                 }
262
263                 SGCloudField *layer3D = thesky->get_cloud_layer(iLayer)->get_layer3D();
264                 buildLayer(layer3D, layer_type, alt_m, coverage_norm);
265         }
266 }
267
268 // copy from FGMetarEnvironmentCtrl until better
269 void
270 FGClouds::update_metar_properties( const FGMetar *m )
271 {
272     int i;
273     double d;
274     char s[128];
275
276     fgSetString("/environment/metar/station-id", m->getId());
277     fgSetDouble("/environment/metar/min-visibility-m",
278                 m->getMinVisibility().getVisibility_m() );
279     fgSetDouble("/environment/metar/max-visibility-m",
280                 m->getMaxVisibility().getVisibility_m() );
281
282     const SGMetarVisibility *dirvis = m->getDirVisibility();
283     for (i = 0; i < 8; i++, dirvis++) {
284         const char *min = "/environment/metar/visibility[%d]/min-m";
285         const char *max = "/environment/metar/visibility[%d]/max-m";
286
287         d = dirvis->getVisibility_m();
288
289         snprintf(s, 128, min, i);
290         fgSetDouble(s, d);
291         snprintf(s, 128, max, i);
292         fgSetDouble(s, d);
293     }
294
295     fgSetInt("/environment/metar/base-wind-range-from",
296              m->getWindRangeFrom() );
297     fgSetInt("/environment/metar/base-wind-range-to",
298              m->getWindRangeTo() );
299     fgSetDouble("/environment/metar/base-wind-speed-kt",
300                 m->getWindSpeed_kt() );
301     fgSetDouble("/environment/metar/gust-wind-speed-kt",
302                 m->getGustSpeed_kt() );
303     fgSetDouble("/environment/metar/temperature-degc",
304                 m->getTemperature_C() );
305     fgSetDouble("/environment/metar/dewpoint-degc",
306                 m->getDewpoint_C() );
307     fgSetDouble("/environment/metar/rel-humidity-norm",
308                 m->getRelHumidity() );
309     fgSetDouble("/environment/metar/pressure-inhg",
310                 m->getPressure_inHg() );
311
312     vector<SGMetarCloud> cv = m->getClouds();
313     vector<SGMetarCloud>::const_iterator cloud;
314
315     const char *cl = "/environment/clouds/layer[%i]";
316     for (i = 0, cloud = cv.begin(); cloud != cv.end(); cloud++, i++) {
317         const char *coverage_string[5] = 
318             { "clear", "few", "scattered", "broken", "overcast" };
319         const double thickness[5] = { 0, 65, 600,750, 1000};
320         int q;
321
322         snprintf(s, 128, cl, i);
323         strncat(s, "/coverage", 128);
324         q = cloud->getCoverage();
325         fgSetString(s, coverage_string[q] );
326
327         snprintf(s, 128, cl, i);
328         strncat(s, "/elevation-ft", 128);
329         fgSetDouble(s, cloud->getAltitude_ft() + station_elevation_ft);
330
331         snprintf(s, 128, cl, i);
332         strncat(s, "/thickness-ft", 128);
333         fgSetDouble(s, thickness[q]);
334
335         snprintf(s, 128, cl, i);
336         strncat(s, "/span-m", 128);
337         fgSetDouble(s, 40000.0);
338     }
339
340     for (; i < FGEnvironmentMgr::MAX_CLOUD_LAYERS; i++) {
341         snprintf(s, 128, cl, i);
342         strncat(s, "/coverage", 128);
343         fgSetString(s, "clear");
344
345         snprintf(s, 128, cl, i);
346         strncat(s, "/elevation-ft", 128);
347         fgSetDouble(s, -9999);
348
349         snprintf(s, 128, cl, i);
350         strncat(s, "/thickness-ft", 128);
351         fgSetDouble(s, 0);
352
353         snprintf(s, 128, cl, i);
354         strncat(s, "/span-m", 128);
355         fgSetDouble(s, 40000.0);
356     }
357
358     fgSetDouble("/environment/metar/rain-norm", m->getRain());
359     fgSetDouble("/environment/metar/hail-norm", m->getHail());
360     fgSetDouble("/environment/metar/snow-norm", m->getSnow());
361     fgSetBool("/environment/metar/snow-cover", m->getSnowCover());
362 }
363
364 void
365 FGClouds::update_env_config ()
366 {
367     fgSetupWind( fgGetDouble("/environment/metar/base-wind-range-from"),
368                  fgGetDouble("/environment/metar/base-wind-range-to"),
369                  fgGetDouble("/environment/metar/base-wind-speed-kt"),
370                  fgGetDouble("/environment/metar/gust-wind-speed-kt") );
371
372     fgDefaultWeatherValue( "visibility-m",
373                            fgGetDouble("/environment/metar/min-visibility-m") );
374 #if 0
375     set_temp_at_altitude( fgGetDouble("/environment/metar/temperature-degc"),
376                           station_elevation_ft );
377     set_dewpoint_at_altitude( fgGetDouble("/environment/metar/dewpoint-degc"),
378                               station_elevation_ft );
379 #endif
380     fgDefaultWeatherValue( "pressure-sea-level-inhg",
381                            fgGetDouble("/environment/metar/pressure-inhg") );
382 }
383
384
385 void FGClouds::setLayer( int iLayer, float alt_ft, const string& coverage, const string& layer_type ) {
386         double coverage_norm = 0.0;
387         if( coverage == "few" )
388                 coverage_norm = 2.0/8.0;        // <1-2
389         else if( coverage == "scattered" )
390                 coverage_norm = 4.0/8.0;        // 3-4
391         else if( coverage == "broken" )
392                 coverage_norm = 6.0/8.0;        // 5-7
393         else if( coverage == "overcast" )
394                 coverage_norm = 8.0/8.0;        // 8
395
396         SGCloudField *layer3D = thesky->get_cloud_layer(iLayer)->get_layer3D();
397         buildLayer(layer3D, layer_type, station_elevation_ft + alt_ft * SG_FEET_TO_METER, coverage_norm);
398 }
399
400 void FGClouds::buildScenario( const string& scenario ) {
401         string fakeMetar="";
402         string station = fgGetString("/environment/metar/station-id", "XXXX");
403
404         // fetch station elevation if exists
405     if( station == "XXXX" )
406         station_elevation_ft = fgGetDouble("/position/ground-elev-m", 0.0);
407     else {
408         const FGAirport* a = globals->get_airports()->search( station );
409         station_elevation_ft = (a ? a->getElevation() : 0.0);
410     }
411
412         for(int iLayer = 0 ; iLayer < thesky->get_cloud_layer_count(); iLayer++) {
413                 thesky->get_cloud_layer(iLayer)->get_layer3D()->clear();
414         }
415
416         station += " 011000Z ";
417         if( scenario == "Fair weather" ) {
418                 fakeMetar = "15003KT 12SM SCT033 FEW200 20/08 Q1015 NOSIG";
419                 setLayer(0, 3300.0, "scattered", "cu");
420         } else if( scenario == "Thunderstorm" ) {
421                 fakeMetar = "15012KT 08SM TSRA SCT040 BKN070 20/12 Q0995";
422                 setLayer(0, 4000.0, "scattered", "cb");
423                 setLayer(1, 7000.0, "scattered", "ns");
424         } else 
425                 return;
426         FGMetar *m = new FGMetar( station + fakeMetar );
427         update_metar_properties( m );
428         update_env_config();
429         // propagate aloft tables
430         _controller->reinit();
431
432         fgSetString("/environment/metar/last-metar", m->getData());
433         // TODO:desactivate real metar updates
434         if( scenario == "Fair weather" ) {
435                 fgSetString("/environment/clouds/layer[1]/coverage", "cirrus");
436         }
437 }
438
439
440 void FGClouds::build(void) {
441         string scenario = fgGetString("/environment/weather-scenario", "METAR");
442
443     if( scenario == last_scenario)
444         return;
445     if( last_scenario == "none" ) {
446         // save clouds and weather conditions
447         SGPropertyNode *param = fgGetNode("/environment/config", true);
448         copyProperties( param, last_env_config );
449         param = fgGetNode("/environment/clouds", true);
450         copyProperties( param, last_env_clouds );
451     }
452         if( scenario == "METAR" ) {
453                 string realMetar = fgGetString("/environment/metar/real-metar", "");
454                 if( realMetar != "" ) {
455                         fgSetString("/environment/metar/last-metar", realMetar.c_str());
456                         FGMetar *m = new FGMetar( realMetar );
457                         update_metar_properties( m );
458                         update_env_config();
459                         // propagate aloft tables
460                         _controller->reinit();
461                 }
462                 buildMETAR();
463         }
464     else if( scenario == "none" ) {
465         // restore clouds and weather conditions
466         SGPropertyNode *param = fgGetNode("/environment/config", true);
467         copyProperties( last_env_config, param );
468         param = fgGetNode("/environment/clouds", true);
469         copyProperties( last_env_clouds, param );
470         fgSetDouble("/environment/metar/rain-norm", 0.0);
471         fgSetDouble("/environment/metar/snow-norm", 0.0);
472 //          update_env_config();
473             // propagate aloft tables
474             _controller->reinit();
475                 buildMETAR();
476     }
477         else
478                 buildScenario( scenario );
479     last_scenario = scenario;
480
481         // ...
482         if( snd_lightning == NULL )
483                 init();
484 }