X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fenvironment%2Fmetar.cxx;h=75109b75651d03eb413a08b9975696ec02315384;hb=f61fe80f6096bff9f7061849f713fdf9204aa945;hp=c6da78de6ebc1395b1f544510fadf3b5791014e2;hpb=b2a4cd488dfcfbf1d02fa41f2dfa5ad39aabb13a;p=simgear.git diff --git a/simgear/environment/metar.cxx b/simgear/environment/metar.cxx index c6da78de..75109b75 100644 --- a/simgear/environment/metar.cxx +++ b/simgear/environment/metar.cxx @@ -16,7 +16,7 @@ // // 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 +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // $Id$ @@ -24,11 +24,14 @@ * @file metar.cxx * Interface for encoded Meteorological Aerodrome Reports (METAR). */ +#ifdef HAVE_CONFIG_H +# include +#endif #include #include +#include -#include #include #include @@ -36,32 +39,28 @@ #define NaN SGMetarNaN +using std::string; +using std::map; +using std::vector; + /** - * The constructor takes a Metar string, or a four-letter ICAO code. In the - * latter case the metar string is downloaded from - * http://weather.noaa.gov/pub/data/observations/metar/stations/. + * The constructor takes a Metar string * The constructor throws sg_io_exceptions on failure. The "METAR" * keyword has no effect (apart from incrementing the group counter * @a grpcount) and can be left away. A keyword "SPECI" is * likewise accepted. * * @param m ICAO station id or metar string - * @param proxy proxy host (optional; default: "") - * @param port proxy port (optional; default: "80") - * @param auth proxy authorization information (optional; default: "") * * @par Examples: * @code * SGMetar *m = new SGMetar("METAR KSFO 061656Z 19004KT 9SM SCT100 OVC200 08/03 A3013"); * double t = m->getTemperature_F(); * delete m; - * - * SGMetar n("KSFO", "proxy.provider.foo", "3128", "proxy-password"); - * double d = n.getDewpoint_C(); + * @endcode */ -SGMetar::SGMetar(const string& m, const string& proxy, const string& port, - const string& auth, const time_t time) : +SGMetar::SGMetar(const string& m) : _grpcount(0), _x_proxy(false), _year(-1), @@ -83,16 +82,10 @@ SGMetar::SGMetar(const string& m, const string& proxy, const string& port, _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, time); - } else { - _data = new char[m.length() + 2]; // make room for " \0" - strcpy(_data, m.c_str()); - _url = _data; - } + _data = new char[m.length() + 2]; // make room for " \0" + strcpy(_data, m.c_str()); + _url = _data; + normalizeData(); _m = _data; @@ -156,7 +149,7 @@ void SGMetar::useCurrentDate() { struct tm now; time_t now_sec = time(0); -#if defined( _MSC_VER ) || defined ( __MINGW32__ ) +#ifdef _WIN32 now = *gmtime(&now_sec); #else gmtime_r(&now_sec, &now); @@ -165,83 +158,6 @@ void SGMetar::useCurrentDate() _month = now.tm_mon + 1; } - -/** - * If called with "KSFO" loads data from - * @code - * http://weather.noaa.gov/pub/data/observations/metar/stations/KSFO.TXT. - * @endcode - * Throws sg_io_exception on failure. Gives up after waiting longer than 10 seconds. - * - * @param id four-letter ICAO Metar station code, e.g. "KSFO". - * @param proxy proxy host (optional; default: "") - * @param port proxy port (optional; default: "80") - * @param auth proxy authorization information (optional; default: "") - * @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, 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; - - SGSocket *sock = new SGSocket(host, port.empty() ? "80" : port, "tcp"); - sock->set_timeout(10000); - if (!sock->open(SG_IO_OUT)) { - delete sock; - throw sg_io_exception("cannot connect to ", sg_location(host)); - } - - string get = "GET "; - if (!proxy.empty()) - get += "http://weather.noaa.gov"; - - 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()); - - int i; - - // skip HTTP header - while ((i = sock->readline(buf, buflen))) { - if (i <= 2 && isspace(buf[0]) && (!buf[1] || isspace(buf[1]))) - break; - if (!strncmp(buf, "X-MetarProxy: ", 13)) - _x_proxy = true; - } - if (i) { - i = sock->readline(buf, buflen); - if (i) - sock->readline(&buf[i], buflen); - } - - sock->close(); - delete sock; - - char *b = buf; - scanBoundary(&b); - if (*b == '<') - throw sg_io_exception("no metar data available from ", - sg_location(_url)); - - char *metar = new char[strlen(b) + 2]; // make room for " \0" - strcpy(metar, b); - return metar; -} - - /** * Replace any number of subsequent spaces by just one space, and add * a trailing space. This makes scanning for things like "ALL RWY" easier. @@ -462,11 +378,13 @@ bool SGMetar::scanVisibility() int modifier = SGMetarVisibility::EQUALS; // \d{4}(N|NE|E|SE|S|SW|W|NW)? if (scanNumber(&m, &i, 4)) { - if (*m == 'E') + if( strncmp( m, "NDV",3 ) == 0 ) { + m+=3; // tolerate NDV (no directional validation) + } else if (*m == 'E') { m++, dir = 90; - else if (*m == 'W') + } else if (*m == 'W') { m++, dir = 270; - else if (*m == 'N') { + } else if (*m == 'N') { m++; if (*m == 'E') m++, dir = 45; @@ -482,7 +400,7 @@ bool SGMetar::scanVisibility() m++, dir = 225; else dir = 180; - } + } if (i == 0) i = 50, modifier = SGMetarVisibility::LESS_THAN; else if (i == 9999) @@ -490,7 +408,6 @@ bool SGMetar::scanVisibility() distance = i; } else { // M?(\d{1,2}|\d{1,2}/\d{1,2}|\d{1,2} \d{1,2}/\d{1,2})(SM|KM) - modifier = 0; if (*m == 'M') m++, modifier = SGMetarVisibility::LESS_THAN; @@ -589,7 +506,7 @@ bool SGMetar::scanRwyVisRange() r._max_visibility._distance = to; if (*m == '/') // this is not in the spec! - *m++; + m++; if (*m == 'D') m++, r._min_visibility._tendency = SGMetarVisibility::DECREASING; else if (*m == 'N') @@ -610,8 +527,8 @@ bool SGMetar::scanRwyVisRange() static const struct Token special[] = { { "NSW", "no significant weather" }, - { "VCSH", "showers in the vicinity" }, - { "VCTS", "thunderstorm in the vicinity" }, +/* { "VCSH", "showers in the vicinity" }, + { "VCTS", "thunderstorm in the vicinity" }, */ { 0, 0 } }; @@ -664,6 +581,7 @@ bool SGMetar::scanWeather() char *m = _m; string weather; const struct Token *a; + if ((a = scanToken(&m, special))) { if (!scanBoundary(&m)) return false; @@ -673,32 +591,35 @@ bool SGMetar::scanWeather() } string pre, post; - int intensity = 0; + struct Weather w; if (*m == '-') - m++, pre = "light ", intensity = 1; + m++, pre = "light ", w.intensity = LIGHT; else if (*m == '+') - m++, pre = "heavy ", intensity = 3; + m++, pre = "heavy ", w.intensity = HEAVY; else if (!strncmp(m, "VC", 2)) - m += 2, post = "in the vicinity "; + m += 2, post = "in the vicinity ", w.vincinity=true; else - pre = "moderate ", intensity = 2; + pre = "moderate ", w.intensity = MODERATE; int i; for (i = 0; i < 3; i++) { if (!(a = scanToken(&m, description))) break; + w.descriptions.push_back(a->id); weather += string(a->text) + " "; } + for (i = 0; i < 3; i++) { if (!(a = scanToken(&m, phenomenon))) break; + w.phenomena.push_back(a->id); weather += string(a->text) + " "; if (!strcmp(a->id, "RA")) - _rain = intensity; + _rain = w.intensity; else if (!strcmp(a->id, "HA")) - _hail = intensity; + _hail = w.intensity; else if (!strcmp(a->id, "SN")) - _snow = intensity; + _snow = w.intensity; } if (!weather.length()) return false; @@ -708,6 +629,8 @@ bool SGMetar::scanWeather() weather = pre + weather + post; weather.erase(weather.length() - 1); _weather.push_back(weather); + if( w.phenomena.size() > 0 ) + _weather2.push_back( w ); _grpcount++; return true; } @@ -736,7 +659,7 @@ static const struct Token cloud_types[] = { { 0, 0 } }; - +#include // (FEW|SCT|BKN|OVC|SKC|CLR|CAVOK|VV)([0-9]{3}|///)?[:cloud_type:]? bool SGMetar::scanSkyCondition() { @@ -744,8 +667,17 @@ bool SGMetar::scanSkyCondition() int i; SGMetarCloud cl; + if (!strncmp(m, "//////", 6)) { + m += 6; + if (!scanBoundary(&m)) + return false; + _m = m; + return true; + } + if (!strncmp(m, "CLR", i = 3) // clear || !strncmp(m, "SKC", i = 3) // sky clear + || !strncmp(m, "NCD", i = 3) // nil cloud detected || !strncmp(m, "NSC", i = 3) // no significant clouds || !strncmp(m, "CAVOK", i = 5)) { // ceiling and visibility OK (implies 9999) m += i; @@ -753,7 +685,7 @@ bool SGMetar::scanSkyCondition() return false; if (i == 3) { - cl._coverage = 0; + cl._coverage = SGMetarCloud::COVERAGE_CLEAR; _clouds.push_back(cl); } else { _cavok = true; @@ -765,13 +697,13 @@ bool SGMetar::scanSkyCondition() if (!strncmp(m, "VV", i = 2)) // vertical visibility ; else if (!strncmp(m, "FEW", i = 3)) - cl._coverage = 1; + cl._coverage = SGMetarCloud::COVERAGE_FEW; else if (!strncmp(m, "SCT", i = 3)) - cl._coverage = 2; + cl._coverage = SGMetarCloud::COVERAGE_SCATTERED; else if (!strncmp(m, "BKN", i = 3)) - cl._coverage = 3; + cl._coverage = SGMetarCloud::COVERAGE_BROKEN; else if (!strncmp(m, "OVC", i = 3)) - cl._coverage = 4; + cl._coverage = SGMetarCloud::COVERAGE_OVERCAST; else return false; m += i; @@ -784,7 +716,7 @@ bool SGMetar::scanSkyCondition() } else if (!scanNumber(&m, &i, 3)) i = -1; - if (cl._coverage == -1) { + if (cl._coverage == SGMetarCloud::COVERAGE_NIL) { if (!scanBoundary(&m)) return false; if (i == -1) // 'VV///' @@ -833,7 +765,7 @@ bool SGMetar::scanTemperature() return false; if (!scanBoundary(&m)) { if (!strncmp(m, "XX", 2)) // not spec compliant! - m += 2, sign = 0; + m += 2, sign = 0, dew = temp; else { sign = 1; if (*m == 'M') @@ -1171,7 +1103,7 @@ const struct Token *SGMetar::scanToken(char **str, const struct Token *list) { const struct Token *longest = 0; int maxlen = 0, len; - char *s; + const char *s; for (int i = 0; (s = list[i].id); i++) { len = strlen(s); if (!strncmp(s, *str, len) && len > maxlen) { @@ -1184,13 +1116,29 @@ const struct Token *SGMetar::scanToken(char **str, const struct Token *list) } -void SGMetarCloud::set(double alt, int cov) +void SGMetarCloud::set(double alt, Coverage cov) { _altitude = alt; if (cov != -1) _coverage = cov; } +SGMetarCloud::Coverage SGMetarCloud::getCoverage( const std::string & coverage ) +{ + if( coverage == "clear" ) return COVERAGE_CLEAR; + if( coverage == "few" ) return COVERAGE_FEW; + if( coverage == "scattered" ) return COVERAGE_SCATTERED; + if( coverage == "broken" ) return COVERAGE_BROKEN; + if( coverage == "overcast" ) return COVERAGE_OVERCAST; + return COVERAGE_NIL; +} + +const char * SGMetarCloud::COVERAGE_NIL_STRING = "nil"; +const char * SGMetarCloud::COVERAGE_CLEAR_STRING = "clear"; +const char * SGMetarCloud::COVERAGE_FEW_STRING = "few"; +const char * SGMetarCloud::COVERAGE_SCATTERED_STRING = "scattered"; +const char * SGMetarCloud::COVERAGE_BROKEN_STRING = "broken"; +const char * SGMetarCloud::COVERAGE_OVERCAST_STRING = "overcast"; void SGMetarVisibility::set(double dist, int dir, int mod, int tend) {