X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FEnvironment%2Fmetarproperties.cxx;h=aa67b355a7b8fa9bfadcfb6a08c8f6ff0322e8ba;hb=89b41395d861a49ad543af166c0bd41c14dd0d2b;hp=23d16b59c76a71f5a196e06e659e6d263c80a1eb;hpb=9e764ed4b0680d5bb6de7de6f5174dd4f0805b5c;p=flightgear.git diff --git a/src/Environment/metarproperties.cxx b/src/Environment/metarproperties.cxx index 23d16b59c..aa67b355a 100644 --- a/src/Environment/metarproperties.cxx +++ b/src/Environment/metarproperties.cxx @@ -28,8 +28,13 @@ #include "fgmetar.hxx" #include "environment.hxx" #include "atmosphere.hxx" +#include "metarairportfilter.hxx" #include #include +#include +#include +#include +#include
using std::string; @@ -37,6 +42,72 @@ namespace Environment { static vector coverage_string; +/** + * @brief Helper class to wrap SGMagVar functionality and cache the variation and dip for + * a certain position. + */ +class MagneticVariation : public SGMagVar { +public: + /** + * Constructor + */ + MagneticVariation() : _lat(1), _lon(1), _alt(1) { + recalc( 0.0, 0.0, 0.0 ); + } + + /** + * @brief get the magnetic variation for a specific position at the current time + * @param lon the positions longitude in degrees + * @param lat the positions latitude in degrees + * @param alt the positions height above MSL (aka altitude) in feet + * @return the magnetic variation in degrees + */ + double get_variation_deg( double lon, double lat, double alt ); + + /** + * @brief get the magnetic dip for a specific position at the current time + * @param lon the positions longitude in degrees + * @param lat the positions latitude in degrees + * @param alt the positions height above MSL (aka altitude) in feet + * @return the magnetic dip in degrees + */ + double get_dip_deg( double lon, double lat, double alt ); +private: + void recalc( double lon, double lat, double alt ); + SGTime _time; + double _lat, _lon, _alt; +}; + +inline void MagneticVariation::recalc( double lon, double lat, double alt ) +{ + // calculation of magnetic variation is expensive. Cache the position + // and perform this calculation only if it has changed + if( _lon != lon || _lat != lat || _alt != alt ) { + SG_LOG(SG_ENVIRONMENT, SG_DEBUG, "Recalculating magvar for lon=" << lon << ", lat=" << lat << ", alt=" << alt ); + _lon = lon; + _lat = lat; + _alt = alt; + + lon *= SGD_DEGREES_TO_RADIANS; + lat *= SGD_DEGREES_TO_RADIANS; + alt *= SG_FEET_TO_METER; + _time.update( lon, lat, 0, 0 ); + update( lon, lat, alt, _time.getJD() ); + } +} + +inline double MagneticVariation::get_variation_deg( double lon, double lat, double alt ) +{ + recalc( lon, lat, alt ); + return get_magvar() * SGD_RADIANS_TO_DEGREES; +} + +inline double MagneticVariation::get_dip_deg( double lon, double lat, double alt ) +{ + recalc( lon, lat, alt ); + return get_magdip() * SGD_RADIANS_TO_DEGREES; +} + MetarProperties::MetarProperties( SGPropertyNode_ptr rootNode ) : _rootNode(rootNode), _metarValidNode( rootNode->getNode( "valid", true ) ), @@ -62,10 +133,11 @@ MetarProperties::MetarProperties( SGPropertyNode_ptr rootNode ) : _rain(0.0), _hail(0.0), _snow(0.0), - _snow_cover(false) + _snow_cover(false), + _magneticVariation(new MagneticVariation()) { // Hack to avoid static initialization order problems on OSX - if( coverage_string.size() == 0 ) { + if( coverage_string.empty() ) { coverage_string.push_back(SGCloudLayer::SG_CLOUD_CLEAR_STRING); coverage_string.push_back(SGCloudLayer::SG_CLOUD_FEW_STRING); coverage_string.push_back(SGCloudLayer::SG_CLOUD_SCATTERED_STRING); @@ -76,18 +148,20 @@ MetarProperties::MetarProperties( SGPropertyNode_ptr rootNode ) : _metarValidNode->setBoolValue( false ); _tiedProperties.setRoot( _rootNode ); _tiedProperties.Tie("data", this, &MetarProperties::get_metar, &MetarProperties::set_metar ); - _tiedProperties.Tie("station-id", this, &MetarProperties::get_station_id ); + _tiedProperties.Tie("station-id", this, &MetarProperties::get_station_id, &MetarProperties::set_station_id ); _tiedProperties.Tie("station-elevation-ft", &_station_elevation ); _tiedProperties.Tie("station-latitude-deg", &_station_latitude ); _tiedProperties.Tie("station-longitude-deg", &_station_longitude ); + _tiedProperties.Tie("station-magnetic-variation-deg", this, &MetarProperties::get_magnetic_variation_deg ); + _tiedProperties.Tie("station-magnetic-dip-deg", this, &MetarProperties::get_magnetic_dip_deg ); _tiedProperties.Tie("min-visibility-m", &_min_visibility ); _tiedProperties.Tie("max-visibility-m", &_max_visibility ); _tiedProperties.Tie("base-wind-range-from", &_base_wind_range_from ); _tiedProperties.Tie("base-wind-range-to", &_base_wind_range_to ); - _tiedProperties.Tie("base-wind-speed-kt", &_wind_speed ); - _tiedProperties.Tie("base-wind-dir-deg", &_base_wind_dir ); - _tiedProperties.Tie("base-wind-from-north-fps", &_wind_from_north_fps ); - _tiedProperties.Tie("base-wind-from-east-fps", &_wind_from_east_fps ); + _tiedProperties.Tie("base-wind-speed-kt", this, &MetarProperties::get_wind_speed, &MetarProperties::set_wind_speed ); + _tiedProperties.Tie("base-wind-dir-deg", this, &MetarProperties::get_base_wind_dir, &MetarProperties::set_base_wind_dir ); + _tiedProperties.Tie("base-wind-from-north-fps", this, &MetarProperties::get_wind_from_north_fps, &MetarProperties::set_wind_from_north_fps ); + _tiedProperties.Tie("base-wind-from-east-fps",this, &MetarProperties::get_wind_from_east_fps, &MetarProperties::set_wind_from_east_fps ); _tiedProperties.Tie("gust-wind-speed-kt", &_gusts ); _tiedProperties.Tie("temperature-degc", &_temperature ); _tiedProperties.Tie("dewpoint-degc", &_dewpoint ); @@ -105,6 +179,7 @@ MetarProperties::MetarProperties( SGPropertyNode_ptr rootNode ) : MetarProperties::~MetarProperties() { + delete _magneticVariation; } @@ -119,14 +194,14 @@ void MetarProperties::set_metar( const char * metar ) m = new FGMetar( _metar ); } catch( sg_io_exception ) { - SG_LOG( SG_GENERAL, SG_WARN, "Can't parse metar: " << _metar ); + SG_LOG( SG_ENVIRONMENT, SG_WARN, "Can't parse metar: " << _metar ); _metarValidNode->setBoolValue(false); return; } _decoded.clear(); const vector weather = m->getWeather(); - for( vector::const_iterator it = weather.begin(); it != weather.end(); it++ ) { + for( vector::const_iterator it = weather.begin(); it != weather.end(); ++it ) { if( false == _decoded.empty() ) _decoded.append(", "); _decoded.append(*it); } @@ -143,14 +218,11 @@ void MetarProperties::set_metar( const char * metar ) vis->setDoubleValue("max-m", v); } - _base_wind_dir = m->getWindDir(); + set_base_wind_dir(m->getWindDir()); _base_wind_range_from = m->getWindRangeFrom(); _base_wind_range_to = m->getWindRangeTo(); - _wind_speed = m->getWindSpeed_kt(); + set_wind_speed(m->getWindSpeed_kt()); - double speed_fps = _wind_speed * SG_NM_TO_METER * SG_METER_TO_FEET / 3600.0; - _wind_from_north_fps = speed_fps * cos((double)_base_wind_dir * SGD_DEGREES_TO_RADIANS); - _wind_from_east_fps = speed_fps * sin((double)_base_wind_dir * SGD_DEGREES_TO_RADIANS); _gusts = m->getGustSpeed_kt(); _temperature = m->getTemperature_C(); _dewpoint = m->getDewpoint_C(); @@ -160,13 +232,15 @@ void MetarProperties::set_metar( const char * metar ) { // 1. check the id given in the metar FGAirport* a = FGAirport::findByIdent(m->getId()); -/* + // 2. if unknown, find closest airport with metar to current position if( a == NULL ) { - SGGeod pos = SGGeod::fromDeg(_longitude_n->getDoubleValue(), _latitude_n->getDoubleValue()); - a = FGAirport::findClosest(pos, 10000.0, &_airportWithMetarFilter); + SGGeod pos = SGGeod::fromDeg( + fgGetDouble( "/position/longitude-deg", 0.0 ), + fgGetDouble( "/position/latitude-deg", 0.0 ) ); + a = FGAirport::findClosest(pos, 10000.0, MetarAirportFilter::instance() ); } -*/ + // 3. otherwise use ground elevation if( a != NULL ) { _station_elevation = a->getElevation(); @@ -175,12 +249,10 @@ void MetarProperties::set_metar( const char * metar ) _station_longitude = towerPosition.getLongitudeDeg(); _station_id = a->ident(); } else { - _station_elevation = 0.0; - _station_latitude = 0.0; - _station_longitude = 0.0; + _station_elevation = fgGetDouble("/position/ground-elev-m", 0.0 ) * SG_METER_TO_FEET; + _station_latitude = fgGetDouble( "/position/latitude-deg", 0.0 ); + _station_longitude = fgGetDouble( "/position/longitude-deg", 0.0 ); _station_id = "XXXX"; -// station_elevation_ft = _ground_elevation_n->getDoubleValue() * SG_METER_TO_FEET; -// _station_id = m->getId(); } } @@ -222,43 +294,74 @@ void MetarProperties::set_metar( const char * metar ) // if its fog(FG) (might be shallow(MI) or patches(BC)) or haze (HZ) or mist(BR) if( phenomenon == "FG" ) isFG = true; if( phenomenon == "HZ" ) isHZ = true; + if( phenomenon == "BR" ) isBR = true; if( description == "MI" ) isMI = true; if( description == "BC" ) isBC = true; - if( description == "BR" ) isBR = true; } } } - vector cv = m->getClouds(); - vector::const_iterator cloud, cloud_end = cv.end(); - { static const char * LAYER = "layer"; SGPropertyNode_ptr cloudsNode = _rootNode->getNode("clouds", true ); const vector & metarClouds = m->getClouds(); unsigned layerOffset = 0; // Oh, this is ugly! - bool setGroundCloudLayer = _rootNode->getBoolValue("set-ground-cloud-layer", false ); - if( setGroundCloudLayer && isFG ) { - // make sure fog actually starts at ground and set it's bottom at a constant + // fog/mist/haze cloud layer does not work with 3d clouds yet :-( + bool setGroundCloudLayer = _rootNode->getBoolValue("set-ground-cloud-layer", false ) && + false == (fgGetBool("/sim/rendering/shader-effects", false ) && + fgGetBool("/sim/rendering/clouds3d-enable", false ) ); + + if( setGroundCloudLayer ) { + // create a cloud layer #0 starting at the ground if its fog, mist or haze + + // make sure layer actually starts at ground and set it's bottom at a constant // value below the station's elevation - const double LAYER_BOTTOM_BELOW_STATION_ELEVATION=200; + const double LAYER_BOTTOM_STATION_OFFSET = + fgGetDouble( "/environment/params/fog-mist-haze-layer/offset-from-station-elevation-ft", -200 ); + + SGMetarCloud::Coverage coverage = SGMetarCloud::COVERAGE_NIL; + double thickness = 0; + double alpha = 1.0; + + if( isFG ) { // fog + coverage = SGMetarCloud::getCoverage( isBC ? + fgGetString( "/environment/params/fog-mist-haze-layer/fog-bc-2dlayer-coverage", SGMetarCloud::COVERAGE_SCATTERED_STRING ) : + fgGetString( "/environment/params/fog-mist-haze-layer/fog-2dlayer-coverage", SGMetarCloud::COVERAGE_BROKEN_STRING ) + ); + + thickness = isMI ? + fgGetDouble("/environment/params/fog-mist-haze-layer/fog-shallow-thickness-ft",30) - LAYER_BOTTOM_STATION_OFFSET : // shallow fog, 10m/30ft + fgGetDouble("/environment/params/fog-mist-haze-layer/fog-thickness-ft",500) - LAYER_BOTTOM_STATION_OFFSET; // fog, 150m/500ft + alpha = fgGetDouble("/environment/params/fog-mist-haze-layer/fog-2dlayer-alpha", 1.0); + } else if( isBR ) { // mist + coverage = SGMetarCloud::getCoverage(fgGetString("/environment/params/fog-mist-haze-layer/mist-2dlayer-coverage", SGMetarCloud::COVERAGE_OVERCAST_STRING)); + thickness = fgGetDouble("/environment/params/fog-mist-haze-layer/mist-thickness-ft",2000) - LAYER_BOTTOM_STATION_OFFSET; + alpha = fgGetDouble("/environment/params/fog-mist-haze-layer/mist-2dlayer-alpha",0.8); + } else if( isHZ ) { // haze + coverage = SGMetarCloud::getCoverage(fgGetString("/environment/params/fog-mist-haze-layer/mist-2dlayer-coverage", SGMetarCloud::COVERAGE_OVERCAST_STRING)); + thickness = fgGetDouble("/environment/params/fog-mist-haze-layer/haze-thickness-ft",2000) - LAYER_BOTTOM_STATION_OFFSET; + alpha = fgGetDouble("/environment/params/fog-mist-haze-layer/haze-2dlayer-alpha",0.6); + } - // fog - create a cloud layer #0 starting at the ground - // fog is "overcast" by default of "broken" for patches of fog - SGPropertyNode_ptr layerNode = cloudsNode->getChild(LAYER, 0, true ); - SGMetarCloud::Coverage coverage = isBC ? SGMetarCloud::COVERAGE_SCATTERED : SGMetarCloud::COVERAGE_BROKEN; - layerNode->setDoubleValue( "coverage-type", SGCloudLayer::getCoverageType(coverage_string[coverage]) ); - layerNode->setStringValue( "coverage", coverage_string[coverage] ); - layerNode->setDoubleValue( "elevation-ft", _station_elevation - LAYER_BOTTOM_BELOW_STATION_ELEVATION ); - layerNode->setDoubleValue( "thickness-ft", isMI ? - 30 + LAYER_BOTTOM_BELOW_STATION_ELEVATION : // shallow fog, 10m/30ft - 500 + LAYER_BOTTOM_BELOW_STATION_ELEVATION ); // fog, 150m/500ft - layerNode->setDoubleValue( "visibility-m", _min_visibility ); - _min_visibility = _max_visibility = 20000.0; // assume good visibility above the fog - layerOffset = 1; // shudder - } else if( setGroundCloudLayer && isHZ ) { - } + if( coverage != SGMetarCloud::COVERAGE_NIL ) { + + // if there is a layer above the fog, limit the top to one foot below that layer's bottom + if( metarClouds.size() > 0 && metarClouds[0].getCoverage() != SGMetarCloud::COVERAGE_CLEAR ) + thickness = metarClouds[0].getAltitude_ft() - LAYER_BOTTOM_STATION_OFFSET - 1; + + SGPropertyNode_ptr layerNode = cloudsNode->getChild(LAYER, 0, true ); + layerNode->setDoubleValue( "coverage-type", SGCloudLayer::getCoverageType(coverage_string[coverage]) ); + layerNode->setStringValue( "coverage", coverage_string[coverage] ); + layerNode->setDoubleValue( "elevation-ft", _station_elevation + LAYER_BOTTOM_STATION_OFFSET ); + layerNode->setDoubleValue( "thickness-ft", thickness ); + layerNode->setDoubleValue( "visibility-m", _min_visibility ); + layerNode->setDoubleValue( "alpha", alpha ); + _min_visibility = _max_visibility = + fgGetDouble("/environment/params/fog-mist-haze-layer/visibility-above-layer-m",20000.0); // assume good visibility above the fog + layerOffset = 1; // shudder + } + } for( unsigned i = 0; i < 5-layerOffset; i++ ) { SGPropertyNode_ptr layerNode = cloudsNode->getChild(LAYER, i+layerOffset, true ); @@ -268,6 +371,7 @@ void MetarProperties::set_metar( const char * metar ) -9999.0 : metarClouds[i].getAltitude_ft() + _station_elevation; + layerNode->setDoubleValue( "alpha", 1.0 ); layerNode->setStringValue( "coverage", coverage_string[coverage] ); layerNode->setDoubleValue( "coverage-type", SGCloudLayer::getCoverageType(coverage_string[coverage]) ); layerNode->setDoubleValue( "elevation-ft", elevation ); @@ -284,4 +388,58 @@ void MetarProperties::set_metar( const char * metar ) _metarValidNode->setBoolValue(true); } +void MetarProperties::setStationId( const std::string & value ) +{ + set_station_id(simgear::strutils::strip(value).c_str()); +} + +double MetarProperties::get_magnetic_variation_deg() const +{ + return _magneticVariation->get_variation_deg( _station_longitude, _station_latitude, _station_elevation ); +} + +double MetarProperties::get_magnetic_dip_deg() const +{ + return _magneticVariation->get_dip_deg( _station_longitude, _station_latitude, _station_elevation ); +} + +static inline void calc_wind_hs( double north_fps, double east_fps, int & heading_deg, double & speed_kt ) +{ + speed_kt = sqrt((north_fps)*(north_fps)+(east_fps)*(east_fps)) * 3600.0 / (SG_NM_TO_METER * SG_METER_TO_FEET); + heading_deg = SGMiscd::roundToInt( + SGMiscd::normalizeAngle2( atan2( east_fps, north_fps ) ) * SGD_RADIANS_TO_DEGREES ); +} + +void MetarProperties::set_wind_from_north_fps( double value ) +{ + _wind_from_north_fps = value; + calc_wind_hs( _wind_from_north_fps, _wind_from_east_fps, _base_wind_dir, _wind_speed ); +} + +void MetarProperties::set_wind_from_east_fps( double value ) +{ + _wind_from_east_fps = value; + calc_wind_hs( _wind_from_north_fps, _wind_from_east_fps, _base_wind_dir, _wind_speed ); +} + +static inline void calc_wind_ne( double heading_deg, double speed_kt, double & north_fps, double & east_fps ) +{ + double speed_fps = speed_kt * SG_NM_TO_METER * SG_METER_TO_FEET / 3600.0; + north_fps = speed_fps * cos(heading_deg * SGD_DEGREES_TO_RADIANS); + east_fps = speed_fps * sin(heading_deg * SGD_DEGREES_TO_RADIANS); +} + +void MetarProperties::set_base_wind_dir( double value ) +{ + _base_wind_dir = value; + calc_wind_ne( (double)_base_wind_dir, _wind_speed, _wind_from_north_fps, _wind_from_east_fps ); +} + +void MetarProperties::set_wind_speed( double value ) +{ + _wind_speed = value; + calc_wind_ne( (double)_base_wind_dir, _wind_speed, _wind_from_north_fps, _wind_from_east_fps ); +} + + } // namespace Environment