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)
This commit is contained in:
ehofman
2005-01-20 09:28:04 +00:00
parent 3b6af2f0c2
commit 1e87dd7903
2 changed files with 108 additions and 29 deletions
+82 -24
View File
@@ -26,6 +26,7 @@
*/ */
#include <string> #include <string>
#include <time.h>
#include <simgear/io/sg_socket.hxx> #include <simgear/io/sg_socket.hxx>
#include <simgear/debug/logstream.hxx> #include <simgear/debug/logstream.hxx>
@@ -59,8 +60,10 @@
* double d = n.getDewpoint_C(); * double d = n.getDewpoint_C();
* @endcode * @endcode
*/ */
SGMetar::SGMetar(const string& m, const string& proxy, const string& port, const string& auth) : SGMetar::SGMetar(const string& m, const string& proxy, const string& port,
const string& auth, const time_t time) :
_grpcount(0), _grpcount(0),
_x_proxy(false),
_year(-1), _year(-1),
_month(-1), _month(-1),
_day(-1), _day(-1),
@@ -74,13 +77,17 @@ SGMetar::SGMetar(const string& m, const string& proxy, const string& port, const
_wind_range_to(-1), _wind_range_to(-1),
_temp(NaN), _temp(NaN),
_dewp(NaN), _dewp(NaN),
_pressure(NaN) _pressure(NaN),
_rain(false),
_hail(false),
_snow(false),
_cavok(false)
{ {
if (m.length() == 4 && isalnum(m[0]) && isalnum(m[1]) && isalnum(m[2]) && isalnum(m[3])) { if (m.length() == 4 && isalnum(m[0]) && isalnum(m[1]) && isalnum(m[2]) && isalnum(m[3])) {
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
_icao[i] = toupper(m[i]); _icao[i] = toupper(m[i]);
_icao[4] = '\0'; _icao[4] = '\0';
_data = loadData(_icao, proxy, port, auth); _data = loadData(_icao, proxy, port, auth, time);
} else { } else {
_data = new char[m.length() + 2]; // make room for " \0" _data = new char[m.length() + 2]; // make room for " \0"
strcpy(_data, m.c_str()); strcpy(_data, m.c_str());
@@ -92,7 +99,8 @@ SGMetar::SGMetar(const string& m, const string& proxy, const string& port, const
_icao[0] = '\0'; _icao[0] = '\0';
// NOAA preample // NOAA preample
scanPreambleDate(); if (!scanPreambleDate())
useCurrentDate();
scanPreambleTime(); scanPreambleTime();
// METAR header // METAR header
@@ -127,6 +135,7 @@ SGMetar::SGMetar(const string& m, const string& proxy, const string& port, const
delete[] _data; delete[] _data;
throw sg_io_exception("metar data incomplete (" + _url + ')'); throw sg_io_exception("metar data incomplete (" + _url + ')');
} }
_url = ""; _url = "";
} }
@@ -143,6 +152,16 @@ SGMetar::~SGMetar()
} }
void SGMetar::useCurrentDate()
{
struct tm now;
time_t now_sec = time(0);
gmtime_r(&now_sec, &now);
_year = now.tm_year + 1900;
_month = now.tm_mon + 1;
}
/** /**
* If called with "KSFO" loads data from * If called with "KSFO" loads data from
* @code * @code
@@ -157,10 +176,15 @@ SGMetar::~SGMetar()
* @return pointer to Metar data string, allocated by new char[]. * @return pointer to Metar data string, allocated by new char[].
* @see rfc2068.txt for proxy spec ("Proxy-Authorization") * @see rfc2068.txt for proxy spec ("Proxy-Authorization")
*/ */
char *SGMetar::loadData(const char *id, const string& proxy, const string& port, const string& auth) char *SGMetar::loadData(const char *id, const string& proxy, const string& port,
const string& auth, time_t time)
{ {
const int buflen = 512;
char buf[2 * buflen];
string host = proxy.empty() ? "weather.noaa.gov" : proxy; string host = proxy.empty() ? "weather.noaa.gov" : proxy;
string path = "/pub/data/observations/metar/stations/"; string path = "/pub/data/observations/metar/stations/";
path += string(id) + ".TXT"; path += string(id) + ".TXT";
_url = "http://weather.noaa.gov" + path; _url = "http://weather.noaa.gov" + path;
@@ -174,24 +198,25 @@ char *SGMetar::loadData(const char *id, const string& proxy, const string& port,
string get = "GET "; string get = "GET ";
if (!proxy.empty()) if (!proxy.empty())
get += "http://weather.noaa.gov"; get += "http://weather.noaa.gov";
get += path + " HTTP/1.0\r\n";
sprintf(buf, "%ld", time);
get += path + " HTTP/1.0\015\012X-Time: " + buf + "\015\012";
if (!auth.empty())
get += "Proxy-Authorization: " + auth + "\015\012";
get += "\015\012";
sock->writestring(get.c_str()); sock->writestring(get.c_str());
if (!auth.empty()) {
get = "Proxy-Authorization: " + auth + "\r\n";
sock->writestring(get.c_str());
}
sock->writestring("\r\n");
int i; int i;
const int buflen = 512;
char buf[2 * buflen];
// skip HTTP header // skip HTTP header
while ((i = sock->readline(buf, buflen))) while ((i = sock->readline(buf, buflen))) {
if (i <= 2 && isspace(buf[0]) && (!buf[1] || isspace(buf[1]))) if (i <= 2 && isspace(buf[0]) && (!buf[1] || isspace(buf[1])))
break; break;
if (!strncmp(buf, "X-MetarProxy: ", 9))
_x_proxy = true;
}
if (i) { if (i) {
i = sock->readline(buf, buflen); i = sock->readline(buf, buflen);
if (i) if (i)
@@ -392,7 +417,7 @@ bool SGMetar::scanWind()
if (gust != NaN) if (gust != NaN)
_gust_speed = gust * factor; _gust_speed = gust * factor;
_grpcount++; _grpcount++;
return false; return true;
} }
@@ -643,14 +668,15 @@ bool SGMetar::scanWeather()
} }
string pre, post; string pre, post;
int intensity = 0;
if (*m == '-') if (*m == '-')
m++, pre = "light "; m++, pre = "light ", intensity = 1;
else if (*m == '+') else if (*m == '+')
m++, pre = "heavy "; m++, pre = "heavy ", intensity = 3;
else if (!strncmp(m, "VC", 2)) else if (!strncmp(m, "VC", 2))
m += 2, post = "in the vicinity "; m += 2, post = "in the vicinity ";
else else
pre = "moderate "; pre = "moderate ", intensity = 2;
int i; int i;
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
@@ -662,6 +688,12 @@ bool SGMetar::scanWeather()
if (!(a = scanToken(&m, phenomenon))) if (!(a = scanToken(&m, phenomenon)))
break; break;
weather += string(a->text) + " "; weather += string(a->text) + " ";
if (!strcmp(a->id, "RA"))
_rain = intensity;
else if (!strcmp(a->id, "HA"))
_hail = intensity;
else if (!strcmp(a->id, "SN"))
_snow = intensity;
} }
if (!weather.length()) if (!weather.length())
return false; return false;
@@ -714,8 +746,13 @@ bool SGMetar::scanSkyCondition()
m += i; m += i;
if (!scanBoundary(&m)) if (!scanBoundary(&m))
return false; return false;
cl._coverage = 0;
_clouds.push_back(cl); if (i == 3) {
cl._coverage = 0;
_clouds.push_back(cl);
} else {
_cavok = true;
}
_m = m; _m = m;
return true; return true;
} }
@@ -907,10 +944,11 @@ bool SGMetar::scanRunwayReport()
if (!strncmp(m, "CLRD", 4)) { if (!strncmp(m, "CLRD", 4)) {
m += 4; // runway cleared m += 4; // runway cleared
r._deposit = "cleared"; r._deposit_string = "cleared";
} else { } else {
if (scanNumber(&m, &i, 1)) { if (scanNumber(&m, &i, 1)) {
r._deposit = runway_deposit[i]; r._deposit = i;
r._deposit_string = runway_deposit[i];
} else if (*m == '/') } else if (*m == '/')
m++; m++;
else else
@@ -1137,4 +1175,24 @@ const struct Token *SGMetar::scanToken(char **str, const struct Token *list)
return longest; return longest;
} }
void SGMetarCloud::set(double alt, int cov)
{
_altitude = alt;
if (cov != -1)
_coverage = cov;
}
void SGMetarVisibility::set(double dist, int dir, int mod, int tend)
{
_distance = dist;
if (dir != -1)
_direction = dir;
if (mod != -1)
_modifier = mod;
if (tend != 1)
_tendency = tend;
}
#undef NaN #undef NaN
+26 -5
View File
@@ -67,6 +67,8 @@ public:
DECREASING DECREASING
}; };
void set(double dist, int dir = -1, int mod = -1, int tend = -1);
inline double getVisibility_m() const { return _distance; } inline double getVisibility_m() const { return _distance; }
inline double getVisibility_ft() const { return _distance == NaN ? NaN : _distance * SG_METER_TO_FEET; } inline double getVisibility_ft() const { return _distance == NaN ? NaN : _distance * SG_METER_TO_FEET; }
inline double getVisibility_sm() const { return _distance == NaN ? NaN : _distance * SG_METER_TO_SM; } inline double getVisibility_sm() const { return _distance == NaN ? NaN : _distance * SG_METER_TO_SM; }
@@ -87,7 +89,8 @@ class SGMetarRunway {
friend class SGMetar; friend class SGMetar;
public: public:
SGMetarRunway() : SGMetarRunway() :
_deposit(0), _deposit(-1),
_deposit_string(0),
_extent(-1), _extent(-1),
_extent_string(0), _extent_string(0),
_depth(NaN), _depth(NaN),
@@ -96,7 +99,8 @@ public:
_comment(0), _comment(0),
_wind_shear(false) {} _wind_shear(false) {}
inline const char *getDeposit() const { return _deposit; } inline int getDeposit() const { return _deposit; }
inline const char *getDepositString() const { return _deposit_string; }
inline double getExtent() const { return _extent; } inline double getExtent() const { return _extent; }
inline const char *getExtentString() const { return _extent_string; } inline const char *getExtentString() const { return _extent_string; }
inline double getDepth() const { return _depth; } inline double getDepth() const { return _depth; }
@@ -110,7 +114,8 @@ public:
protected: protected:
SGMetarVisibility _min_visibility; SGMetarVisibility _min_visibility;
SGMetarVisibility _max_visibility; SGMetarVisibility _max_visibility;
const char *_deposit; int _deposit;
const char *_deposit_string;
int _extent; int _extent;
const char *_extent_string; const char *_extent_string;
double _depth; double _depth;
@@ -131,6 +136,8 @@ public:
_type(0), _type(0),
_type_long(0) {} _type_long(0) {}
void set(double alt, int cov = -1);
inline int getCoverage() const { return _coverage; } inline int getCoverage() const { return _coverage; }
inline double getAltitude_m() const { return _altitude; } inline double getAltitude_m() const { return _altitude; }
inline double getAltitude_ft() const { return _altitude == NaN ? NaN : _altitude * SG_METER_TO_FEET; } inline double getAltitude_ft() const { return _altitude == NaN ? NaN : _altitude * SG_METER_TO_FEET; }
@@ -147,7 +154,8 @@ protected:
class SGMetar { class SGMetar {
public: public:
SGMetar(const string& m, const string& proxy = "", const string& port = "", const string &auth = ""); SGMetar(const string& m, const string& proxy = "", const string& port = "",
const string &auth = "", const time_t time = 0);
~SGMetar(); ~SGMetar();
enum ReportType { enum ReportType {
@@ -159,6 +167,7 @@ public:
inline const char *getData() const { return _data; } inline const char *getData() const { return _data; }
inline const char *getUnusedData() const { return _m; } inline const char *getUnusedData() const { return _m; }
inline const bool getProxy() const { return _x_proxy; }
inline const char *getId() const { return _icao; } inline const char *getId() const { return _icao; }
inline int getYear() const { return _year; } inline int getYear() const { return _year; }
inline int getMonth() const { return _month; } inline int getMonth() const { return _month; }
@@ -193,6 +202,11 @@ public:
inline double getPressure_hPa() const { return _pressure == NaN ? NaN : _pressure / 100; } inline double getPressure_hPa() const { return _pressure == NaN ? NaN : _pressure / 100; }
inline double getPressure_inHg() const { return _pressure == NaN ? NaN : _pressure * SG_PA_TO_INHG; } inline double getPressure_inHg() const { return _pressure == NaN ? NaN : _pressure * SG_PA_TO_INHG; }
inline int getRain() const { return _rain; }
inline int getHail() const { return _hail; }
inline int getSnow() const { return _snow; }
inline bool getCAVOK() const { return _cavok; }
double getRelHumidity() const; double getRelHumidity() const;
inline vector<SGMetarCloud>& getClouds() { return _clouds; } inline vector<SGMetarCloud>& getClouds() { return _clouds; }
@@ -202,6 +216,7 @@ public:
protected: protected:
string _url; string _url;
int _grpcount; int _grpcount;
bool _x_proxy;
char *_data; char *_data;
char *_m; char *_m;
char _icao[5]; char _icao[5];
@@ -219,6 +234,10 @@ protected:
double _temp; double _temp;
double _dewp; double _dewp;
double _pressure; double _pressure;
int _rain;
int _hail;
int _snow;
bool _cavok;
SGMetarVisibility _min_visibility; SGMetarVisibility _min_visibility;
SGMetarVisibility _max_visibility; SGMetarVisibility _max_visibility;
@@ -230,6 +249,7 @@ protected:
bool scanPreambleDate(); bool scanPreambleDate();
bool scanPreambleTime(); bool scanPreambleTime();
void useCurrentDate();
bool scanType(); bool scanType();
bool scanId(); bool scanId();
@@ -253,7 +273,8 @@ protected:
int scanNumber(char **str, int *num, int min, int max = 0); int scanNumber(char **str, int *num, int min, int max = 0);
bool scanBoundary(char **str); bool scanBoundary(char **str);
const struct Token *scanToken(char **str, const struct Token *list); const struct Token *scanToken(char **str, const struct Token *list);
char *loadData(const char *id, const string& proxy, const string& port, const string &auth); char *loadData(const char *id, const string& proxy, const string& port,
const string &auth, time_t time);
void normalizeData(); void normalizeData();
}; };