The following patches to SimGear & FlightGear ...
- create an FGMetar abstraction layer, whose purpose is:
* provide defaults for unset values
* interpolate/randomize data (GREATER_THAN)
* derive additional values (time, age, snow cover)
* consider minimum identifier (CAVOK, mil. color codes)
- add rain/hail/snow/snowcover support on the METAR side
- add max age of METAR data handling (currently set to
- add support for an external METAR cache proxy server
- add CAVOK handling
- set missing year/month in regular METAR messages
- fix a small bug in metar.cxx (wrong return value)
libEnvironment_a_SOURCES = \
environment.cxx environment.hxx \
environment_mgr.cxx environment_mgr.hxx \
- environment_ctrl.cxx environment_ctrl.hxx
+ environment_ctrl.cxx environment_ctrl.hxx \
+ fgmetar.cxx fgmetar.hxx
INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src
string host = proxy_host->getStringValue();
string auth = proxy_auth->getStringValue();
string port = proxy_port->getStringValue();
- result.m = new SGMetar( icao, host, port, auth);
+ result.m = new FGMetar( icao, host, port, auth);
+
+ if (result.m->getAge_min() > 4 * 60) {
+ SG_LOG( SG_GENERAL, SG_WARN, "METAR data too old");
+ delete result.m;
+ result.m = NULL;
+ }
} catch (const sg_io_exception& e) {
SG_LOG( SG_GENERAL, SG_WARN, "Error fetching live weather data: "
void
-FGMetarEnvironmentCtrl::update_metar_properties( SGMetar *m )
+FGMetarEnvironmentCtrl::update_metar_properties( FGMetar *m )
{
int i;
- double d, dt;
+ double d;
char s[128];
- d = m->getMinVisibility().getVisibility_m();
- d = (d != SGMetarNaN) ? d : 10000;
- fgSetDouble("/environment/metar/min-visibility-m", d);
-
- dt = m->getMaxVisibility().getVisibility_m();
- d = (dt != SGMetarNaN) ? dt : d;
- fgSetDouble("/environment/metar/max-visibility-m", d);
+ 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";
- char s[128];
d = dirvis->getVisibility_m();
- d = (d != SGMetarNaN) ? d : 10000;
snprintf(s, 128, min, i);
fgSetDouble(s, d);
fgSetDouble(s, d);
}
- i = m->getWindDir();
- if ( i == -1 ) {
- fgSetInt("/environment/metar/base-wind-range-from",
- m->getWindRangeFrom() );
- fgSetInt("/environment/metar/base-wind-range-to",
- m->getWindRangeTo() );
- } else {
- fgSetInt("/environment/metar/base-wind-range-from", i);
- fgSetInt("/environment/metar/base-wind-range-to", i);
- }
+ 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() );
-
- d = m->getGustSpeed_kt();
- d = (d != SGMetarNaN) ? d : 0.0;
- fgSetDouble("/environment/metar/gust-wind-speed-kt", d);
-
- d = m->getTemperature_C();
- if (d != SGMetarNaN) {
- dt = m->getDewpoint_C();
- dt = (dt != SGMetarNaN) ? dt : 0.0;
- fgSetDouble("/environment/metar/dewpoint-degc", dt);
- fgSetDouble("/environment/metar/rel-humidity-norm",
- m->getRelHumidity() );
- }
- d = (d != SGMetarNaN) ? d : 15.0;
- fgSetDouble("/environment/metar/temperature-degc", d);
-
- d = m->getPressure_inHg();
- d = (d != SGMetarNaN) ? d : 30.0;
- fgSetDouble("/environment/metar/pressure-inhg", d);
+ 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;
snprintf(s, 128, cl, i);
strncat(s, "/coverage", 128);
q = cloud->getCoverage();
- q = (q != -1 ) ? q : 0;
fgSetString(s, coverage_string[q] );
snprintf(s, 128, cl, i);
strncat(s, "/elevation-ft", 128);
- d = cloud->getAltitude_ft();
- d = (d != SGMetarNaN) ? d : -9999;
- fgSetDouble(s, d + station_elevation_ft);
+ fgSetDouble(s, cloud->getAltitude_ft() + station_elevation_ft);
snprintf(s, 128, cl, i);
strncat(s, "/thickness-ft", 128);
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());
}
class SGPropertyNode;
#include "environment.hxx"
+#include "fgmetar.hxx"
\f
};
-// A convenience wrapper around SGMetar
+// A convenience wrapper around FGMetar
struct FGMetarResult {
string icao;
- SGMetar *m;
+ FGMetar *m;
};
\f
/**
- * Interplation controller using the SGMetar class
+ * Interplation controller using the FGMetar class
*/
class FGMetarEnvironmentCtrl : public FGEnvironmentCtrl
{
SGPropertyNode *proxy_auth;
FGMetarResult fetch_data( const string &icao );
- virtual void update_metar_properties( SGMetar *m );
+ virtual void update_metar_properties( FGMetar *m );
void update_env_config();
private:
--- /dev/null
+// metar interface class
+//
+// Written by Melchior FRANZ, started January 2005.
+//
+// Copyright (C) 2005 Melchior FRANZ - mfranz@aon.at
+//
+// 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
+//
+// $Id$
+
+/**
+ * @file fgmetar.cxx
+ * Implements FGMetar class that inherits from SGMetar.
+ *
+ * o provide defaults for unset values
+ * o interpolate/randomize data (GREATER_THAN)
+ * o derive additional values (time, age, snow cover)
+ * o consider minimum identifier (CAVOK, mil. color codes)
+ *
+ * TODO
+ * - NSC & mil. color codes
+ */
+
+#include <simgear/math/sg_random.h>
+#include <simgear/timing/sg_time.hxx>
+#include <Main/fg_props.hxx>
+
+#include "fgmetar.hxx"
+
+
+FGMetar::FGMetar(const string& icao, const string& proxy, const string& port, const string& auth) :
+ SGMetar(icao, proxy, port, auth, _rq_time = globals->get_time_params()->get_cur_time()),
+ _snow_cover(false)
+{
+ int i;
+ double d;
+
+ // CAVOK: visibility >= 10km; lowest cloud layer >= 5000 ft; any coverage
+ if (getCAVOK()) {
+ if (_min_visibility.getVisibility_m() == SGMetarNaN)
+ _min_visibility.set(12000.0);
+
+ if (_max_visibility.getVisibility_m() == SGMetarNaN)
+ _min_visibility.set(12000.0);
+
+ vector<SGMetarCloud> cv = _clouds;;
+ if (!cv.size()) {
+ SGMetarCloud cl;
+ cl.set(5500 * SG_FEET_TO_METER, 2);
+ _clouds.push_back(cl);
+ }
+ }
+
+ // visibility
+ d = _min_visibility.getVisibility_m();
+ if (d == SGMetarNaN)
+ d = 10000.0;
+ if (_min_visibility.getModifier() == SGMetarVisibility::GREATER_THAN)
+ d += 15000.0;// * sg_random();
+ _min_visibility.set(d);
+
+ if (_max_visibility.getVisibility_m() == SGMetarNaN)
+ _max_visibility.set(d);
+
+ for (i = 0; i < 8; i++) {
+ d = _dir_visibility[i].getVisibility_m();
+ if (d == SGMetarNaN)
+ _dir_visibility[i].set(10000.0);
+ if (_dir_visibility[i].getModifier() == SGMetarVisibility::GREATER_THAN)
+ d += 15000.0;// * sg_random();
+ _dir_visibility[i].set(d);
+ }
+
+ // wind
+ if (_wind_dir == -1) {
+ if (_wind_range_from == -1) {
+ _wind_dir = 0;
+ _wind_range_from = 0;
+ _wind_range_to = 359;
+ } else {
+ _wind_dir = (_wind_range_from + _wind_range_to) / 2;
+ }
+ } else if (_wind_range_from == -1) {
+ _wind_range_from = _wind_range_to = _wind_dir;
+ }
+
+ if (_gust_speed == SGMetarNaN)
+ _gust_speed = 0.0;
+
+ // clouds
+ vector<SGMetarCloud> cv = _clouds;
+ vector<SGMetarCloud>::iterator cloud;
+
+ for (i = 0, cloud = cv.begin(); cloud != cv.end(); cloud++, i++) {
+ int cov = cloud->getCoverage();
+ if (cov == -1)
+ cov = 0;
+
+ double alt = cloud->getAltitude_ft();
+ if (alt == SGMetarNaN)
+ alt = -9999;
+
+ cloud->set(alt, cov);
+ }
+
+
+ // temperature/pressure
+ if (_temp == SGMetarNaN)
+ _temp = 15.0;
+
+ if (_dewp == SGMetarNaN)
+ _dewp = 0.0;
+
+ if (_pressure == SGMetarNaN)
+ _pressure = 30.0 * SG_INHG_TO_PA;
+
+ // snow cover
+ map<string, SGMetarRunway> rm = getRunways();
+ map<string, SGMetarRunway>::iterator runway;
+ for (runway = rm.begin(); runway != rm.end(); runway++) {
+ SGMetarRunway rwy = runway->second;
+ if (rwy.getDeposit() >= 3 ) {
+ _snow_cover = true;
+ break;
+ }
+ }
+ if (_temp < 5.0 && _snow)
+ _snow_cover = true;
+ if (_temp < 1.0 && getRelHumidity() > 80)
+ _snow_cover = true;
+
+ _time = sgTimeGetGMT(_year - 1900, _month - 1, _day, _hour, _minute, 0);
+
+ SG_LOG(SG_GENERAL, SG_INFO, _data);
+ if (_x_proxy)
+ SG_LOG(SG_GENERAL, SG_INFO, "METAR from proxy");
+ else
+ SG_LOG(SG_GENERAL, SG_INFO, "METAR from weather.noaa.gov");
+}
+
+
+long FGMetar::getAge_min() const
+{
+ time_t now = _x_proxy ? _rq_time : time(0);
+ return (now - _time) / 60;
+}
+
--- /dev/null
+// metar interface class
+//
+// Written by Melchior FRANZ, started January 2005.
+//
+// Copyright (C) 2005 Melchior FRANZ - mfranz@aon.at
+//
+// 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
+//
+// $Id$
+
+#ifndef _FGMETAR_HXX
+#define _FGMETAR_HXX
+
+#include <vector>
+#include <map>
+#include <string>
+#include <time.h>
+
+#include <simgear/environment/metar.hxx>
+
+SG_USING_STD(vector);
+SG_USING_STD(map);
+SG_USING_STD(string);
+
+
+class FGMetar : public SGMetar {
+public:
+ FGMetar(const string& icao, const string& proxy = "", const string& port = "",
+ const string &auth = "");
+
+ long getAge_min() const;
+ time_t getTime() const { return _time; }
+ double getRain() const { return _rain / 3.0; }
+ double getHail() const { return _hail / 3.0; }
+ double getSnow() const { return _snow / 3.0; }
+ bool getSnowCover() const { return _snow_cover; }
+
+private:
+ time_t _rq_time;
+ time_t _time;
+ bool _snow_cover;
+};
+
+#endif // _FGMETAR_HXX
// assemble surface string
vector<string> surface;
- if ((s = rwy.getDeposit()) && strlen(s))
+ if ((s = rwy.getDepositString()) && strlen(s))
surface.push_back(s);
if ((s = rwy.getExtentString()) && strlen(s))
surface.push_back(s);