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:
@@ -26,6 +26,7 @@
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <time.h>
|
||||
|
||||
#include <simgear/io/sg_socket.hxx>
|
||||
#include <simgear/debug/logstream.hxx>
|
||||
@@ -59,8 +60,10 @@
|
||||
* double d = n.getDewpoint_C();
|
||||
* @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),
|
||||
_x_proxy(false),
|
||||
_year(-1),
|
||||
_month(-1),
|
||||
_day(-1),
|
||||
@@ -74,13 +77,17 @@ SGMetar::SGMetar(const string& m, const string& proxy, const string& port, const
|
||||
_wind_range_to(-1),
|
||||
_temp(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])) {
|
||||
for (int i = 0; i < 4; i++)
|
||||
_icao[i] = toupper(m[i]);
|
||||
_icao[4] = '\0';
|
||||
_data = loadData(_icao, proxy, port, auth);
|
||||
_data = loadData(_icao, proxy, port, auth, time);
|
||||
} else {
|
||||
_data = new char[m.length() + 2]; // make room for " \0"
|
||||
strcpy(_data, m.c_str());
|
||||
@@ -92,7 +99,8 @@ SGMetar::SGMetar(const string& m, const string& proxy, const string& port, const
|
||||
_icao[0] = '\0';
|
||||
|
||||
// NOAA preample
|
||||
scanPreambleDate();
|
||||
if (!scanPreambleDate())
|
||||
useCurrentDate();
|
||||
scanPreambleTime();
|
||||
|
||||
// METAR header
|
||||
@@ -127,6 +135,7 @@ SGMetar::SGMetar(const string& m, const string& proxy, const string& port, const
|
||||
delete[] _data;
|
||||
throw sg_io_exception("metar data incomplete (" + _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
|
||||
* @code
|
||||
@@ -157,10 +176,15 @@ SGMetar::~SGMetar()
|
||||
* @return pointer to Metar data string, allocated by new char[].
|
||||
* @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 path = "/pub/data/observations/metar/stations/";
|
||||
|
||||
path += string(id) + ".TXT";
|
||||
_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 ";
|
||||
if (!proxy.empty())
|
||||
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());
|
||||
|
||||
if (!auth.empty()) {
|
||||
get = "Proxy-Authorization: " + auth + "\r\n";
|
||||
sock->writestring(get.c_str());
|
||||
}
|
||||
|
||||
sock->writestring("\r\n");
|
||||
|
||||
int i;
|
||||
const int buflen = 512;
|
||||
char buf[2 * buflen];
|
||||
|
||||
// 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])))
|
||||
break;
|
||||
if (!strncmp(buf, "X-MetarProxy: ", 9))
|
||||
_x_proxy = true;
|
||||
}
|
||||
if (i) {
|
||||
i = sock->readline(buf, buflen);
|
||||
if (i)
|
||||
@@ -392,7 +417,7 @@ bool SGMetar::scanWind()
|
||||
if (gust != NaN)
|
||||
_gust_speed = gust * factor;
|
||||
_grpcount++;
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -643,14 +668,15 @@ bool SGMetar::scanWeather()
|
||||
}
|
||||
|
||||
string pre, post;
|
||||
int intensity = 0;
|
||||
if (*m == '-')
|
||||
m++, pre = "light ";
|
||||
m++, pre = "light ", intensity = 1;
|
||||
else if (*m == '+')
|
||||
m++, pre = "heavy ";
|
||||
m++, pre = "heavy ", intensity = 3;
|
||||
else if (!strncmp(m, "VC", 2))
|
||||
m += 2, post = "in the vicinity ";
|
||||
else
|
||||
pre = "moderate ";
|
||||
pre = "moderate ", intensity = 2;
|
||||
|
||||
int i;
|
||||
for (i = 0; i < 3; i++) {
|
||||
@@ -662,6 +688,12 @@ bool SGMetar::scanWeather()
|
||||
if (!(a = scanToken(&m, phenomenon)))
|
||||
break;
|
||||
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())
|
||||
return false;
|
||||
@@ -714,8 +746,13 @@ bool SGMetar::scanSkyCondition()
|
||||
m += i;
|
||||
if (!scanBoundary(&m))
|
||||
return false;
|
||||
cl._coverage = 0;
|
||||
_clouds.push_back(cl);
|
||||
|
||||
if (i == 3) {
|
||||
cl._coverage = 0;
|
||||
_clouds.push_back(cl);
|
||||
} else {
|
||||
_cavok = true;
|
||||
}
|
||||
_m = m;
|
||||
return true;
|
||||
}
|
||||
@@ -907,10 +944,11 @@ bool SGMetar::scanRunwayReport()
|
||||
|
||||
if (!strncmp(m, "CLRD", 4)) {
|
||||
m += 4; // runway cleared
|
||||
r._deposit = "cleared";
|
||||
r._deposit_string = "cleared";
|
||||
} else {
|
||||
if (scanNumber(&m, &i, 1)) {
|
||||
r._deposit = runway_deposit[i];
|
||||
r._deposit = i;
|
||||
r._deposit_string = runway_deposit[i];
|
||||
} else if (*m == '/')
|
||||
m++;
|
||||
else
|
||||
@@ -1137,4 +1175,24 @@ const struct Token *SGMetar::scanToken(char **str, const struct Token *list)
|
||||
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
|
||||
|
||||
@@ -67,6 +67,8 @@ public:
|
||||
DECREASING
|
||||
};
|
||||
|
||||
void set(double dist, int dir = -1, int mod = -1, int tend = -1);
|
||||
|
||||
inline double getVisibility_m() const { return _distance; }
|
||||
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; }
|
||||
@@ -87,7 +89,8 @@ class SGMetarRunway {
|
||||
friend class SGMetar;
|
||||
public:
|
||||
SGMetarRunway() :
|
||||
_deposit(0),
|
||||
_deposit(-1),
|
||||
_deposit_string(0),
|
||||
_extent(-1),
|
||||
_extent_string(0),
|
||||
_depth(NaN),
|
||||
@@ -96,7 +99,8 @@ public:
|
||||
_comment(0),
|
||||
_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 const char *getExtentString() const { return _extent_string; }
|
||||
inline double getDepth() const { return _depth; }
|
||||
@@ -110,7 +114,8 @@ public:
|
||||
protected:
|
||||
SGMetarVisibility _min_visibility;
|
||||
SGMetarVisibility _max_visibility;
|
||||
const char *_deposit;
|
||||
int _deposit;
|
||||
const char *_deposit_string;
|
||||
int _extent;
|
||||
const char *_extent_string;
|
||||
double _depth;
|
||||
@@ -131,6 +136,8 @@ public:
|
||||
_type(0),
|
||||
_type_long(0) {}
|
||||
|
||||
void set(double alt, int cov = -1);
|
||||
|
||||
inline int getCoverage() const { return _coverage; }
|
||||
inline double getAltitude_m() const { return _altitude; }
|
||||
inline double getAltitude_ft() const { return _altitude == NaN ? NaN : _altitude * SG_METER_TO_FEET; }
|
||||
@@ -147,7 +154,8 @@ protected:
|
||||
|
||||
class SGMetar {
|
||||
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();
|
||||
|
||||
enum ReportType {
|
||||
@@ -159,6 +167,7 @@ public:
|
||||
|
||||
inline const char *getData() const { return _data; }
|
||||
inline const char *getUnusedData() const { return _m; }
|
||||
inline const bool getProxy() const { return _x_proxy; }
|
||||
inline const char *getId() const { return _icao; }
|
||||
inline int getYear() const { return _year; }
|
||||
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_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;
|
||||
|
||||
inline vector<SGMetarCloud>& getClouds() { return _clouds; }
|
||||
@@ -202,6 +216,7 @@ public:
|
||||
protected:
|
||||
string _url;
|
||||
int _grpcount;
|
||||
bool _x_proxy;
|
||||
char *_data;
|
||||
char *_m;
|
||||
char _icao[5];
|
||||
@@ -219,6 +234,10 @@ protected:
|
||||
double _temp;
|
||||
double _dewp;
|
||||
double _pressure;
|
||||
int _rain;
|
||||
int _hail;
|
||||
int _snow;
|
||||
bool _cavok;
|
||||
|
||||
SGMetarVisibility _min_visibility;
|
||||
SGMetarVisibility _max_visibility;
|
||||
@@ -230,6 +249,7 @@ protected:
|
||||
|
||||
bool scanPreambleDate();
|
||||
bool scanPreambleTime();
|
||||
void useCurrentDate();
|
||||
|
||||
bool scanType();
|
||||
bool scanId();
|
||||
@@ -253,7 +273,8 @@ protected:
|
||||
int scanNumber(char **str, int *num, int min, int max = 0);
|
||||
bool scanBoundary(char **str);
|
||||
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();
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user