From 33b6694311634aca283cc98e3a8e41e43e3ad7d0 Mon Sep 17 00:00:00 2001
From: ehofman <ehofman>
Date: Thu, 20 Jan 2005 09:28:45 +0000
Subject: [PATCH] Melchior FRANZ:

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)
---
 src/Environment/Makefile.am          |   3 +-
 src/Environment/environment_ctrl.cxx |  79 ++++++-------
 src/Environment/environment_ctrl.hxx |   9 +-
 src/Environment/fgmetar.cxx          | 159 +++++++++++++++++++++++++++
 src/Environment/fgmetar.hxx          |  56 ++++++++++
 src/Main/metar_main.cxx              |   2 +-
 6 files changed, 257 insertions(+), 51 deletions(-)
 create mode 100644 src/Environment/fgmetar.cxx
 create mode 100644 src/Environment/fgmetar.hxx

diff --git a/src/Environment/Makefile.am b/src/Environment/Makefile.am
index e3f7450d1..a16996170 100644
--- a/src/Environment/Makefile.am
+++ b/src/Environment/Makefile.am
@@ -7,6 +7,7 @@ noinst_LIBRARIES = libEnvironment.a
 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
diff --git a/src/Environment/environment_ctrl.cxx b/src/Environment/environment_ctrl.cxx
index 0e7a6db37..d426d0e6e 100644
--- a/src/Environment/environment_ctrl.cxx
+++ b/src/Environment/environment_ctrl.cxx
@@ -541,7 +541,13 @@ FGMetarEnvironmentCtrl::fetch_data( const string &icao )
         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: "
@@ -564,28 +570,24 @@ FGMetarEnvironmentCtrl::fetch_data( const string &icao )
 
 
 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);
@@ -593,37 +595,22 @@ FGMetarEnvironmentCtrl::update_metar_properties( SGMetar *m )
         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;
@@ -638,14 +625,11 @@ FGMetarEnvironmentCtrl::update_metar_properties( SGMetar *m )
         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);
@@ -673,6 +657,11 @@ FGMetarEnvironmentCtrl::update_metar_properties( SGMetar *m )
         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());
 }
 
 
diff --git a/src/Environment/environment_ctrl.hxx b/src/Environment/environment_ctrl.hxx
index bb3460484..b48ee5f96 100644
--- a/src/Environment/environment_ctrl.hxx
+++ b/src/Environment/environment_ctrl.hxx
@@ -47,6 +47,7 @@ SG_USING_STD(vector);
 class SGPropertyNode;
 
 #include "environment.hxx"
+#include "fgmetar.hxx"
 
 
 
@@ -141,16 +142,16 @@ private:
 };
 
 
-// A convenience wrapper around SGMetar
+// A convenience wrapper around FGMetar
 struct FGMetarResult {
     string icao;
-    SGMetar *m;
+    FGMetar *m;
 };
 
 
 
 /**
- * Interplation controller using the SGMetar class
+ * Interplation controller using the FGMetar class
  */
 class FGMetarEnvironmentCtrl : public FGEnvironmentCtrl
 {
@@ -178,7 +179,7 @@ private:
     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:
diff --git a/src/Environment/fgmetar.cxx b/src/Environment/fgmetar.cxx
new file mode 100644
index 000000000..0f71c8e4b
--- /dev/null
+++ b/src/Environment/fgmetar.cxx
@@ -0,0 +1,159 @@
+// 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;
+}
+
diff --git a/src/Environment/fgmetar.hxx b/src/Environment/fgmetar.hxx
new file mode 100644
index 000000000..67874d492
--- /dev/null
+++ b/src/Environment/fgmetar.hxx
@@ -0,0 +1,56 @@
+// 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
diff --git a/src/Main/metar_main.cxx b/src/Main/metar_main.cxx
index 4519975d0..03805806b 100644
--- a/src/Main/metar_main.cxx
+++ b/src/Main/metar_main.cxx
@@ -265,7 +265,7 @@ void printReport(SGMetar *m)
 
 		// 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);