X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FEnvironment%2Fmetarproperties.cxx;h=1ebdd33b0da805cc6f8ad59ea6513560b7fca24b;hb=9745a6fce3de367cf29c37829b312090e84c2487;hp=23d16b59c76a71f5a196e06e659e6d263c80a1eb;hpb=29161c21146ee344729de79c283d33991086b2ca;p=flightgear.git diff --git a/src/Environment/metarproperties.cxx b/src/Environment/metarproperties.cxx index 23d16b59c..1ebdd33b0 100644 --- a/src/Environment/metarproperties.cxx +++ b/src/Environment/metarproperties.cxx @@ -24,12 +24,19 @@ # include #endif +#include // for strlen + #include "metarproperties.hxx" #include "fgmetar.hxx" #include "environment.hxx" #include "atmosphere.hxx" +#include "metarairportfilter.hxx" #include #include +#include +#include +#include +#include
using std::string; @@ -37,6 +44,70 @@ 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; + + SGGeod location(SGGeod::fromDegFt(lon, lat, alt)); + _time.update( location, 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,15 @@ MetarProperties::MetarProperties( SGPropertyNode_ptr rootNode ) : _rain(0.0), _hail(0.0), _snow(0.0), - _snow_cover(false) + _snow_cover(false), + _day(0), + _hour(0), + _minute(0), + _cavok(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 +152,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 ); @@ -100,33 +178,62 @@ MetarProperties::MetarProperties( SGPropertyNode_ptr rootNode ) : _tiedProperties.Tie("hail-norm", &_hail ); _tiedProperties.Tie("snow-norm", &_snow); _tiedProperties.Tie("snow-cover", &_snow_cover ); + _tiedProperties.Tie("day", &_day ); + _tiedProperties.Tie("hour", &_hour ); + _tiedProperties.Tie("minute", &_minute ); _tiedProperties.Tie("decoded", this, &MetarProperties::get_decoded ); + _tiedProperties.Tie("cavok", &_cavok ); } MetarProperties::~MetarProperties() { + delete _magneticVariation; } +void MetarProperties::invalidate() +{ + if( _metarValidNode->getBoolValue() ) + _metarValidNode->setBoolValue(false); +} static const double thickness_value[] = { 0, 65, 600, 750, 1000 }; -void MetarProperties::set_metar( const char * metar ) +const char* MetarProperties::get_metar() const +{ + if (!_metar) return ""; + return _metar->getData(); +} + +void MetarProperties::set_metar( const char * metarString ) { - _metar = metar; - SGSharedPtr m; + if ((metarString == NULL) || (strlen(metarString) == 0)) { + setMetar(m); + return; + } + try { - m = new FGMetar( _metar ); + m = new FGMetar( metarString ); } 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: " << metarString ); _metarValidNode->setBoolValue(false); return; } - + + setMetar(m); +} + +void MetarProperties::setMetar( SGSharedPtr m ) +{ + _metar = m; _decoded.clear(); + if (!m) { + return; + } + 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 +250,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 +264,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 +281,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 +326,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 +403,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 ); @@ -281,7 +417,65 @@ void MetarProperties::set_metar( const char * metar ) _hail = m->getHail(); _snow = m->getSnow(); _snow_cover = m->getSnowCover(); + _day = m->getDay(); + _hour = m->getHour(); + _minute = m->getMinute(); + _cavok = m->getCAVOK(); _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