1
0
Fork 0
flightgear/src/Environment/fgclouds.cxx
curt 0bb1494452 David Luff:
Attached is a patch to the airport data storage that I would like committed
after review if acceptable.  Currently the storage of airports mapped by ID
is by locally created objects - about 12 Meg or so created on the stack if
I am not mistaken.  I've changed this to creating the airports on the heap,
and storing pointers to them - see FGAirportList.add(...) in
src/Airports/simple.cxx.  I believe that this is probably better practice,
and it's certainly cured some strange problems I was seeing when accessing
the airport data with some gps unit code.  Changes resulting from this have
cascaded through a few files which access the data - 11 files are modified
in all.  Melchior and Durk - you might want to test this and shout if there
are problems since the metar and traffic code are probably the biggest
users of the airport data.  I've also added a fuzzy search function that
returns the next matching airport code in ASCII sequence in order to
support gps units that have autocompletion of partially entered codes.

More generally, the simple airport class seems to have grown a lot with the
fairly recent addition of the parking, runway preference and schedule time
code.  It is no longer just an encapsulation of the global airport data
file, and has grown to 552 bytes in size when unpopulated (about 1/2 a K!).
 My personal opinion is that we should look to just store the basic data in
apt.dat for all global airports in a simple airport class, plus globally
needed data (metar available?), and then have the traffic, AI and ATC
subsystems create more advanced airports for themselves as needed in the
area of interest.  Once a significant number of airports worldwide have
ground networks and parking defined, it will be impractical and unnecessary
to store them all in memory.  That's just a thought for the future though.
2005-09-20 20:26:57 +00:00

480 lines
16 KiB
C++

// Build a cloud layer based on metar
//
// Written by Harald JOHNSEN, started April 2005.
//
// Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
//
//
#include <Main/fg_props.hxx>
#include <simgear/constants.h>
#include <simgear/sound/soundmgr_openal.hxx>
#include <simgear/scene/sky/sky.hxx>
#include <simgear/environment/visual_enviro.hxx>
#include <simgear/scene/sky/cloudfield.hxx>
#include <simgear/scene/sky/newcloud.hxx>
#include <simgear/math/sg_random.h>
#include <Main/globals.hxx>
#include <Airports/simple.hxx>
#include <Main/util.hxx>
#include "environment_ctrl.hxx"
#include "environment_mgr.hxx"
#include "fgmetar.hxx"
#include "fgclouds.hxx"
extern SGSky *thesky;
FGClouds::FGClouds(FGEnvironmentCtrl * controller) :
station_elevation_ft(0.0),
_controller( controller ),
snd_lightning(NULL),
last_scenario( "none" ),
last_env_config( new SGPropertyNode() ),
last_env_clouds( new SGPropertyNode() )
{
update_event = 0;
fgSetString("/environment/weather-scenario", last_scenario.c_str());
}
FGClouds::~FGClouds() {
}
int FGClouds::get_update_event(void) const {
return update_event;
}
void FGClouds::set_update_event(int count) {
update_event = count;
build();
}
void FGClouds::init(void) {
if( snd_lightning == NULL ) {
snd_lightning = new SGSoundSample(globals->get_fg_root().c_str(), "Sounds/thunder.wav", true);
snd_lightning->set_max_dist(7000.0f);
snd_lightning->set_reference_dist(3000.0f);
SGSoundMgr *soundMgr = globals->get_soundmgr();
soundMgr->add( snd_lightning, "thunder" );
sgEnviro.set_soundMgr( soundMgr );
}
}
SGNewCloud *FGClouds::buildCloud(SGPropertyNode *cloud_def_root, string name) {
SGPropertyNode *cld_def=NULL;
cld_def = cloud_def_root->getChild(name.c_str());
string base_name = name.substr(0,2);
if( !cld_def ) {
if( name[2] == '-' ) {
cld_def = cloud_def_root->getChild(base_name.c_str());
}
if( !cld_def )
return NULL;
}
string familly = cld_def->getStringValue("familly", base_name.c_str());
SGNewCloud *cld = new SGNewCloud(familly);
for(int i = 0; i < cld_def->nChildren() ; i++) {
SGPropertyNode *abox = cld_def->getChild(i);
if( strcmp(abox->getName(), "box") == 0) {
double x = abox->getDoubleValue("x");
double y = abox->getDoubleValue("y");
double z = abox->getDoubleValue("z");
double size = abox->getDoubleValue("size");
int type = abox->getIntValue("type", SGNewCloud::CLbox_standard);
cld->addContainer(x, y, z, size, (SGNewCloud::CLbox_type) type);
}
}
cld->genSprites();
return cld;
}
void FGClouds::buildLayer(SGCloudField *layer, string name, double alt, double coverage) {
struct {
string name;
double count;
} tCloudVariety[20];
int CloudVarietyCount = 0;
double totalCount = 0.0;
SGPropertyNode *cloud_def_root = fgGetNode("/environment/cloudlayers/clouds", false);
SGPropertyNode *layer_def_root = fgGetNode("/environment/cloudlayers/layers", false);
layer->clear();
// when we don't generate clouds the layer is rendered in 2D
if( coverage == 0.0 )
return;
if( layer_def_root == NULL || cloud_def_root == NULL)
return;
if( name == "ci" || name == "sc" || name == "st")
return;
SGPropertyNode *layer_def=NULL;
layer_def = layer_def_root->getChild(name.c_str());
if( !layer_def ) {
if( name[2] == '-' ) {
string base_name = name.substr(0,2);
layer_def = layer_def_root->getChild(base_name.c_str());
}
if( !layer_def )
return;
}
double grid_x_size = layer_def->getDoubleValue("grid-x-size", 1000.0);
double grid_y_size = layer_def->getDoubleValue("grid-y-size", 1000.0);
double grid_x_rand = layer_def->getDoubleValue("grid-x-rand", grid_x_size);
double grid_y_rand = layer_def->getDoubleValue("grid-y-rand", grid_y_size);
double grid_z_rand = layer_def->getDoubleValue("grid-z-rand");
for(int i = 0; i < layer_def->nChildren() ; i++) {
SGPropertyNode *acloud = layer_def->getChild(i);
if( strcmp(acloud->getName(), "cloud") == 0) {
string cloud_name = acloud->getStringValue("name");
tCloudVariety[CloudVarietyCount].name = cloud_name;
double count = acloud->getDoubleValue("count", 1.0);
tCloudVariety[CloudVarietyCount].count = count;
int variety = 0;
cloud_name = cloud_name + "-%d";
char variety_name[50];
do {
variety++;
snprintf(variety_name, sizeof(variety_name), cloud_name.c_str(), variety);
} while( cloud_def_root->getChild(variety_name, 0, false) );
totalCount += count;
if( CloudVarietyCount < 20 )
CloudVarietyCount++;
}
}
totalCount = 1.0 / totalCount;
double currCoverage = 0.0;
for(double px = 0.0; px < SGCloudField::fieldSize; px += grid_x_size) {
for(double py = 0.0; py < SGCloudField::fieldSize; py += grid_y_size) {
double x = px + grid_x_rand * (sg_random() - 0.5);
double y = py + grid_y_rand * (sg_random() - 0.5);
double z = alt + grid_z_rand * (sg_random() - 0.5);
double choice = sg_random();
currCoverage += coverage;
if( currCoverage < 1.0 )
continue;
currCoverage -= 1.0;
for(int i = 0; i < CloudVarietyCount ; i ++) {
choice -= tCloudVariety[i].count * totalCount;
if( choice <= 0.0 ) {
SGNewCloud *cld = buildCloud(cloud_def_root, tCloudVariety[i].name);
sgVec3 pos={x,z,y};
if( cld )
layer->addCloud(pos, cld);
break;
}
}
}
}
}
// TODO:call this after real metar updates
void FGClouds::buildMETAR(void) {
SGPropertyNode *metar_root = fgGetNode("/environment", true);
double wind_speed_kt = metar_root->getDoubleValue("wind-speed-kt");
double temperature_degc = metar_root->getDoubleValue("temperature-sea-level-degc");
double dewpoint_degc = metar_root->getDoubleValue("dewpoint-sea-level-degc");
double pressure_mb = metar_root->getDoubleValue("pressure-sea-level-inhg") * SG_INHG_TO_PA / 100.0;
double dewp = pow(10.0, 7.5 * dewpoint_degc / (237.7 + dewpoint_degc));
double temp = pow(10.0, 7.5 * temperature_degc / (237.7 + temperature_degc));
double rel_humidity = dewp * 100 / temp;
// formule d'Epsy, base d'un cumulus
double cumulus_base = 122.0 * (temperature_degc - dewpoint_degc);
double stratus_base = 100.0 * (100.0 - rel_humidity) * SG_FEET_TO_METER;
bool cu_seen = false;
for(int iLayer = 0 ; iLayer < thesky->get_cloud_layer_count(); iLayer++) {
SGPropertyNode *cloud_root = fgGetNode("/environment/clouds/layer", iLayer, true);
double alt_ft = cloud_root->getDoubleValue("elevation-ft");
double alt_m = alt_ft * SG_FEET_TO_METER;
string coverage = cloud_root->getStringValue("coverage");
double coverage_norm = 0.0;
if( coverage == "few" )
coverage_norm = 2.0/8.0; // <1-2
else if( coverage == "scattered" )
coverage_norm = 4.0/8.0; // 3-4
else if( coverage == "broken" )
coverage_norm = 6.0/8.0; // 5-7
else if( coverage == "overcast" )
coverage_norm = 8.0/8.0; // 8
string layer_type = "nn";
if( coverage == "cirrus" ) {
layer_type = "ci";
} else if( alt_ft > 16500 ) {
// layer_type = "ci|cs|cc";
layer_type = "ci";
} else if( alt_ft > 6500 ) {
// layer_type = "as|ac|ns";
layer_type = "ac";
if( pressure_mb < 1005.0 && coverage_norm >= 5.5 )
layer_type = "ns";
} else {
// layer_type = "st|cu|cb|sc";
// +/- 20% from stratus probable base
if( stratus_base * 0.80 < alt_m && stratus_base * 1.40 > alt_m )
layer_type = "st";
// +/- 20% from cumulus probable base
else if( cumulus_base * 0.80 < alt_m && cumulus_base * 1.20 > alt_m )
layer_type = "cu";
else {
// above formulae is far from perfect
if ( alt_ft < 2000 )
layer_type = "st";
else if( alt_ft < 4500 )
layer_type = "cu";
else
layer_type = "sc";
}
}
SGCloudField *layer3D = thesky->get_cloud_layer(iLayer)->get_layer3D();
buildLayer(layer3D, layer_type, alt_m, coverage_norm);
}
}
// copy from FGMetarEnvironmentCtrl until better
void
FGClouds::update_metar_properties( FGMetar *m )
{
int i;
double d;
char s[128];
fgSetString("/environment/metar/station-id", m->getId());
fgSetDouble("/environment/metar/min-visibility-m",
m->getMinVisibility().getVisibility_m() );
fgSetDouble("/environment/metar/max-visibility-m",
m->getMaxVisibility().getVisibility_m() );
SGMetarVisibility *dirvis = m->getDirVisibility();
for (i = 0; i < 8; i++, dirvis++) {
const char *min = "/environment/metar/visibility[%d]/min-m";
const char *max = "/environment/metar/visibility[%d]/max-m";
d = dirvis->getVisibility_m();
snprintf(s, 128, min, i);
fgSetDouble(s, d);
snprintf(s, 128, max, i);
fgSetDouble(s, d);
}
fgSetInt("/environment/metar/base-wind-range-from",
m->getWindRangeFrom() );
fgSetInt("/environment/metar/base-wind-range-to",
m->getWindRangeTo() );
fgSetDouble("/environment/metar/base-wind-speed-kt",
m->getWindSpeed_kt() );
fgSetDouble("/environment/metar/gust-wind-speed-kt",
m->getGustSpeed_kt() );
fgSetDouble("/environment/metar/temperature-degc",
m->getTemperature_C() );
fgSetDouble("/environment/metar/dewpoint-degc",
m->getDewpoint_C() );
fgSetDouble("/environment/metar/rel-humidity-norm",
m->getRelHumidity() );
fgSetDouble("/environment/metar/pressure-inhg",
m->getPressure_inHg() );
vector<SGMetarCloud> cv = m->getClouds();
vector<SGMetarCloud>::iterator cloud;
const char *cl = "/environment/clouds/layer[%i]";
for (i = 0, cloud = cv.begin(); cloud != cv.end(); cloud++, i++) {
const char *coverage_string[5] =
{ "clear", "few", "scattered", "broken", "overcast" };
const double thickness[5] = { 0, 65, 600,750, 1000};
int q;
snprintf(s, 128, cl, i);
strncat(s, "/coverage", 128);
q = cloud->getCoverage();
fgSetString(s, coverage_string[q] );
snprintf(s, 128, cl, i);
strncat(s, "/elevation-ft", 128);
fgSetDouble(s, cloud->getAltitude_ft() + station_elevation_ft);
snprintf(s, 128, cl, i);
strncat(s, "/thickness-ft", 128);
fgSetDouble(s, thickness[q]);
snprintf(s, 128, cl, i);
strncat(s, "/span-m", 128);
fgSetDouble(s, 40000.0);
}
for (; i < FGEnvironmentMgr::MAX_CLOUD_LAYERS; i++) {
snprintf(s, 128, cl, i);
strncat(s, "/coverage", 128);
fgSetString(s, "clear");
snprintf(s, 128, cl, i);
strncat(s, "/elevation-ft", 128);
fgSetDouble(s, -9999);
snprintf(s, 128, cl, i);
strncat(s, "/thickness-ft", 128);
fgSetDouble(s, 0);
snprintf(s, 128, cl, i);
strncat(s, "/span-m", 128);
fgSetDouble(s, 40000.0);
}
fgSetDouble("/environment/metar/rain-norm", m->getRain());
fgSetDouble("/environment/metar/hail-norm", m->getHail());
fgSetDouble("/environment/metar/snow-norm", m->getSnow());
fgSetBool("/environment/metar/snow-cover", m->getSnowCover());
}
void
FGClouds::update_env_config ()
{
fgSetupWind( fgGetDouble("/environment/metar/base-wind-range-from"),
fgGetDouble("/environment/metar/base-wind-range-to"),
fgGetDouble("/environment/metar/base-wind-speed-kt"),
fgGetDouble("/environment/metar/gust-wind-speed-kt") );
fgDefaultWeatherValue( "visibility-m",
fgGetDouble("/environment/metar/min-visibility-m") );
#if 0
set_temp_at_altitude( fgGetDouble("/environment/metar/temperature-degc"),
station_elevation_ft );
set_dewpoint_at_altitude( fgGetDouble("/environment/metar/dewpoint-degc"),
station_elevation_ft );
#endif
fgDefaultWeatherValue( "pressure-sea-level-inhg",
fgGetDouble("/environment/metar/pressure-inhg") );
}
void FGClouds::setLayer( int iLayer, float alt_ft, string coverage, string layer_type ) {
double coverage_norm = 0.0;
if( coverage == "few" )
coverage_norm = 2.0/8.0; // <1-2
else if( coverage == "scattered" )
coverage_norm = 4.0/8.0; // 3-4
else if( coverage == "broken" )
coverage_norm = 6.0/8.0; // 5-7
else if( coverage == "overcast" )
coverage_norm = 8.0/8.0; // 8
SGCloudField *layer3D = thesky->get_cloud_layer(iLayer)->get_layer3D();
buildLayer(layer3D, layer_type, station_elevation_ft + alt_ft * SG_FEET_TO_METER, coverage_norm);
}
void FGClouds::buildScenario( string scenario ) {
string fakeMetar="";
string station = fgGetString("/environment/metar/station-id", "XXXX");
// fetch station elevation if exists
if( station == "XXXX" )
station_elevation_ft = fgGetDouble("/position/ground-elev-m", 0.0);
else {
const FGAirport* a = globals->get_airports()->search( station );
station_elevation_ft = (a ? a->getElevation() : 0.0);
}
for(int iLayer = 0 ; iLayer < thesky->get_cloud_layer_count(); iLayer++) {
thesky->get_cloud_layer(iLayer)->get_layer3D()->clear();
}
station += " 011000Z ";
if( scenario == "Fair weather" ) {
fakeMetar = "15003KT 12SM SCT033 FEW200 20/08 Q1015 NOSIG";
setLayer(0, 3300.0, "scattered", "cu");
} else if( scenario == "Thunderstorm" ) {
fakeMetar = "15012KT 08SM TSRA SCT040 BKN070 20/12 Q0995";
setLayer(0, 4000.0, "scattered", "cb");
setLayer(1, 7000.0, "scattered", "ns");
} else
return;
FGMetar *m = new FGMetar( station + fakeMetar );
update_metar_properties( m );
update_env_config();
// propagate aloft tables
_controller->reinit();
fgSetString("/environment/metar/last-metar", m->getData());
// TODO:desactivate real metar updates
if( scenario == "Fair weather" ) {
fgSetString("/environment/clouds/layer[1]/coverage", "cirrus");
}
}
void FGClouds::build(void) {
string scenario = fgGetString("/environment/weather-scenario", "METAR");
if( scenario == last_scenario)
return;
if( last_scenario == "none" ) {
// save clouds and weather conditions
SGPropertyNode *param = fgGetNode("/environment/config", true);
copyProperties( param, last_env_config );
param = fgGetNode("/environment/clouds", true);
copyProperties( param, last_env_clouds );
}
if( scenario == "METAR" ) {
string realMetar = fgGetString("/environment/metar/real-metar", "");
if( realMetar != "" ) {
fgSetString("/environment/metar/last-metar", realMetar.c_str());
FGMetar *m = new FGMetar( realMetar );
update_metar_properties( m );
update_env_config();
// propagate aloft tables
_controller->reinit();
}
buildMETAR();
}
else if( scenario == "none" ) {
// restore clouds and weather conditions
SGPropertyNode *param = fgGetNode("/environment/config", true);
copyProperties( last_env_config, param );
param = fgGetNode("/environment/clouds", true);
copyProperties( last_env_clouds, param );
fgSetDouble("/environment/metar/rain-norm", 0.0);
fgSetDouble("/environment/metar/snow-norm", 0.0);
// update_env_config();
// propagate aloft tables
_controller->reinit();
buildMETAR();
}
else
buildScenario( scenario );
last_scenario = scenario;
// ...
if( snd_lightning == NULL )
init();
}