From: ehofman Date: Mon, 2 Feb 2004 10:14:20 +0000 (+0000) Subject: Move the new metar class from FlightGear to SimGear X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=7ffce3ac6af9df5c9437ac21d5f211edcbcadd8f;p=flightgear.git Move the new metar class from FlightGear to SimGear --- diff --git a/src/Environment/Makefile.am b/src/Environment/Makefile.am index fb7607919..e3f7450d1 100644 --- a/src/Environment/Makefile.am +++ b/src/Environment/Makefile.am @@ -7,16 +7,6 @@ noinst_LIBRARIES = libEnvironment.a libEnvironment_a_SOURCES = \ environment.cxx environment.hxx \ environment_mgr.cxx environment_mgr.hxx \ - environment_ctrl.cxx environment_ctrl.hxx \ - metar.cxx metar.hxx - -bin_PROGRAMS = metar - -metar_SOURCES = metar-main.cxx metar.cxx metar.hxx - -metar_LDADD = \ - -lsgio -lsgbucket -lsgmisc -lsgstructure -lsgdebug \ - -lplibnet -lplibul \ - -lz $(base_LIBS) + environment_ctrl.cxx environment_ctrl.hxx INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src diff --git a/src/Environment/metar-main.cxx b/src/Environment/metar-main.cxx deleted file mode 100644 index 273320573..000000000 --- a/src/Environment/metar-main.cxx +++ /dev/null @@ -1,482 +0,0 @@ -// metar interface class demo -// -// Written by Melchior FRANZ, started December 2003. -// -// Copyright (C) 2003 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$ - -#include -#include - -#include -#include - -#include "metar.hxx" - -using std::ostringstream; - -// text color -#if defined(__linux__) || defined( __sun__ ) ||defined(__CYGWIN__) || defined( __FreeBSD__ ) -# define R "\033[31;1m" // red -# define G "\033[32;1m" // green -# define Y "\033[33;1m" // yellow -# define B "\033[34;1m" // blue -# define M "\033[35;1m" // magenta -# define C "\033[36;1m" // cyan -# define W "\033[37;1m" // white -# define N "\033[m" // normal -#else -# define R "" -# define G "" -# define Y "" -# define B "" -# define M "" -# define C "" -# define W "" -# define N "" -#endif - - -const char *azimuthName(double d); -double rnd(double number, int digits); -void printReport(Metar *m); -void printVisibility(FGMetarVisibility *v); -void printArgs(Metar *m, double airport_elevation); - - -const char *azimuthName(double d) -{ - const char *dir[] = { - "N", "NNE", "NE", "ENE", - "E", "ESE", "SE", "SSE", - "S", "SSW", "SW", "WSW", - "W", "WNW", "NW", "NNW" - }; - d += 11.25; - while (d < 0) - d += 360; - while (d >= 360) - d -= 360; - return dir[int(d / 22.5)]; -} - - -// round double to 10^g -double rnd(double r, int g = 0) -{ - double f = pow(10.0, g); - return f * rint(r / f); -} - - -ostream& operator<<(ostream& s, FGMetarVisibility& v) -{ - ostringstream buf; - int m = v.getModifier(); - const char *mod; - if (m == FGMetarVisibility::GREATER_THAN) - mod = ">="; - else if (m == FGMetarVisibility::LESS_THAN) - mod = "<"; - else - mod = ""; - buf << mod; - - double dist = rnd(v.getVisibility_m(), 1); - if (dist < 1000.0) - buf << rnd(dist, 1) << " m"; - else - buf << rnd(dist / 1000.0, -1) << " km"; - - const char *dir = ""; - int i; - if ((i = v.getDirection()) != -1) { - dir = azimuthName(i); - buf << " " << dir; - } - buf << "\t\t\t\t\t" << mod << rnd(v.getVisibility_sm(), -1) << " US-miles " << dir; - return s << buf.str(); -} - - -void printReport(Metar *m) -{ -#define NaN FGMetarNaN - const char *s; - char buf[256]; - double d; - int i, lineno; - - if ((i = m->getReportType()) == Metar::AUTO) - s = "\t\t(automatically generated)"; - else if (i == Metar::COR) - s = "\t\t(manually corrected)"; - else if (i == Metar::RTD) - s = "\t\t(routine delayed)"; - else - s = ""; - - cout << "METAR Report" << s << endl; - cout << "============" << endl; - cout << "Airport-Id:\t\t" << m->getId() << endl; - - - // date/time - int year = m->getYear(); - int month = m->getMonth(); - cout << "Report time:\t\t"; - if (year != -1 && month != -1) - cout << year << '/' << month << '/' << m->getDay(); - cout << ' ' << m->getHour() << ':'; - cout << std::setw(2) << std::setfill('0') << m->getMinute() << " UTC" << endl; - - - // visibility - FGMetarVisibility minvis = m->getMinVisibility(); - FGMetarVisibility maxvis = m->getMaxVisibility(); - double min = minvis.getVisibility_m(); - double max = maxvis.getVisibility_m(); - if (min != NaN) { - if (max != NaN) { - cout << "min. Visibility:\t" << minvis << endl; - cout << "max. Visibility:\t" << maxvis << endl; - } else - cout << "Visibility:\t\t" << minvis << endl; - } - - - // directed visibility - FGMetarVisibility *dirvis = m->getDirVisibility(); - for (i = 0; i < 8; i++, dirvis++) - if (dirvis->getVisibility_m() != NaN) - cout << "\t\t\t" << *dirvis << endl; - - - // vertical visibility - FGMetarVisibility vertvis = m->getVertVisibility(); - if ((d = vertvis.getVisibility_ft()) != NaN) - cout << "Vert. visibility:\t" << vertvis << endl; - else if (vertvis.getModifier() == FGMetarVisibility::NOGO) - cout << "Vert. visibility:\timpossible to determine" << endl; - - - // wind - d = m->getWindSpeed_kmh(); - cout << "Wind:\t\t\t"; - if (d < .1) - cout << "none" << endl; - else { - if ((i = m->getWindDir()) == -1) - cout << "from variable directions"; - else - cout << "from the " << azimuthName(i) << " (" << i << "°)"; - cout << " at " << rnd(d, -1) << " km/h"; - - cout << "\t\t" << rnd(m->getWindSpeed_kt(), -1) << " kt"; - cout << " = " << rnd(m->getWindSpeed_mph(), -1) << " mph"; - cout << " = " << rnd(m->getWindSpeed_mps(), -1) << " m/s"; - cout << endl; - - if ((d = m->getGustSpeed_kmh()) != NaN) { - cout << "\t\t\twith gusts at " << rnd(d, -1) << " km/h"; - cout << "\t\t\t" << rnd(m->getGustSpeed_kt(), -1) << " kt"; - cout << " = " << rnd(m->getGustSpeed_mph(), -1) << " mph"; - cout << " = " << rnd(m->getGustSpeed_mps(), -1) << " m/s"; - cout << endl; - } - - int from = m->getWindRangeFrom(); - int to = m->getWindRangeTo(); - if (from != to) { - cout << "\t\t\tvariable from " << azimuthName(from); - cout << " to " << azimuthName(to); - cout << " (" << from << "°--" << to << "°)" << endl; - } - } - - - // temperature/humidity/air pressure - if ((d = m->getTemperature_C()) != NaN) { - cout << "Temperature:\t\t" << d << "°C\t\t\t\t\t"; - cout << rnd(m->getTemperature_F(), -1) << "°F" << endl; - - if ((d = m->getDewpoint_C()) != NaN) { - cout << "Dewpoint:\t\t" << d << "°C\t\t\t\t\t"; - cout << rnd(m->getDewpoint_F(), -1) << "°F" << endl; - cout << "Rel. Humidity:\t\t" << rnd(m->getRelHumidity()) << "%" << endl; - } - } - if ((d = m->getPressure_hPa()) != NaN) { - cout << "Pressure:\t\t" << rnd(d) << " hPa\t\t\t\t"; - cout << rnd(m->getPressure_inHg(), -2) << " in. Hg" << endl; - } - - - // weather phenomena - vector wv = m->getWeather(); - vector::iterator weather; - for (i = 0, weather = wv.begin(); weather != wv.end(); weather++, i++) { - cout << (i ? ", " : "Weather:\t\t") << weather->c_str(); - } - if (i) - cout << endl; - - - // cloud layers - const char *coverage_string[5] = { - "clear skies", "few clouds", "scattered clouds", "broken clouds", "sky overcast" - }; - vector cv = m->getClouds(); - vector::iterator cloud; - for (lineno = 0, cloud = cv.begin(); cloud != cv.end(); cloud++, lineno++) { - cout << (lineno ? "\t\t\t" : "Sky condition:\t\t"); - - if ((i = cloud->getCoverage()) != -1) - cout << coverage_string[i]; - if ((d = cloud->getAltitude_ft()) != NaN) - cout << " at " << rnd(d, 1) << " ft"; - if ((s = cloud->getTypeLongString())) - cout << " (" << s << ')'; - if (d != NaN) - cout << "\t\t\t" << rnd(cloud->getAltitude_m(), 1) << " m"; - cout << endl; - } - - - // runways - map rm = m->getRunways(); - map::iterator runway; - for (runway = rm.begin(); runway != rm.end(); runway++) { - lineno = 0; - if (!strcmp(runway->first.c_str(), "ALL")) - cout << "All runways:\t\t"; - else - cout << "Runway " << runway->first << ":\t\t"; - FGMetarRunway rwy = runway->second; - - // assemble surface string - vector surface; - if ((s = rwy.getDeposit()) && strlen(s)) - surface.push_back(s); - if ((s = rwy.getExtentString()) && strlen(s)) - surface.push_back(s); - if ((d = rwy.getDepth()) != NaN) { - sprintf(buf, "%.0lf mm", d * 1000.0); - surface.push_back(buf); - } - if ((s = rwy.getFrictionString()) && strlen(s)) - surface.push_back(s); - if ((d = rwy.getFriction()) != NaN) { - sprintf(buf, "friction: %.2lf", d); - surface.push_back(buf); - } - - if (surface.size()) { - vector::iterator rwysurf = surface.begin(); - for (i = 0; rwysurf != surface.end(); rwysurf++, i++) { - if (i) - cout << ", "; - cout << *rwysurf; - } - lineno++; - } - - // assemble visibility string - FGMetarVisibility minvis = rwy.getMinVisibility(); - FGMetarVisibility maxvis = rwy.getMaxVisibility(); - if ((d = minvis.getVisibility_m()) != NaN) { - if (lineno++) - cout << endl << "\t\t\t"; - cout << minvis; - } - if (maxvis.getVisibility_m() != d) { - cout << endl << "\t\t\t" << maxvis << endl; - lineno++; - } - - if (rwy.getWindShear()) { - if (lineno++) - cout << endl << "\t\t\t"; - cout << "critical wind shear" << endl; - } - cout << endl; - } - cout << endl; -#undef NaN -} - - -void printArgs(Metar *m, double airport_elevation) -{ -#define NaN FGMetarNaN - vector args; - char buf[256]; - int i; - - // ICAO id - sprintf(buf, "--airport=%s ", m->getId()); - args.push_back(buf); - - // report time - sprintf(buf, "--start-date-gmt=%4d:%02d:%02d:%02d:%02d:00 ", - m->getYear(), m->getMonth(), m->getDay(), - m->getHour(), m->getMinute()); - args.push_back(buf); - - // cloud layers - const char *coverage_string[5] = { - "clear", "few", "scattered", "broken", "overcast" - }; - vector cv = m->getClouds(); - vector::iterator cloud; - for (i = 0, cloud = cv.begin(); i < 5; i++) { - int coverage = 0; - double altitude = -99999; - if (cloud != cv.end()) { - coverage = cloud->getCoverage(); - altitude = coverage ? cloud->getAltitude_ft() + airport_elevation : -99999; - cloud++; - } - sprintf(buf, "--prop:/environment/clouds/layer[%d]/coverage=%s ", i, coverage_string[coverage]); - args.push_back(buf); - sprintf(buf, "--prop:/environment/clouds/layer[%d]/elevation-ft=%.0lf ", i, altitude); - args.push_back(buf); - sprintf(buf, "--prop:/environment/clouds/layer[%d]/thickness-ft=500 ", i); - args.push_back(buf); - } - - // environment (temperature, dewpoint, visibility, pressure) - // metar sets don't provide aloft information; we have to - // set the same values for all boundary levels - int wind_dir = m->getWindDir(); - double visibility = m->getMinVisibility().getVisibility_m(); - double dewpoint = m->getDewpoint_C(); - double temperature = m->getTemperature_C(); - double pressure = m->getPressure_inHg(); - double wind_speed = m->getWindSpeed_kt(); - double elevation = -100; - for (i = 0; i < 3; i++, elevation += 2000.0) { - sprintf(buf, "--prop:/environment/config/boundary/entry[%d]/", i); - int pos = strlen(buf); - - sprintf(&buf[pos], "elevation-ft=%.0lf", elevation); - args.push_back(buf); - sprintf(&buf[pos], "turbulence-norm=%.0lf", 0.0); - args.push_back(buf); - - if (visibility != NaN) { - sprintf(&buf[pos], "visibility-m=%.0lf", visibility); - args.push_back(buf); - } - if (temperature != NaN) { - sprintf(&buf[pos], "temperature-degc=%.0lf", temperature); - args.push_back(buf); - } - if (dewpoint != NaN) { - sprintf(&buf[pos], "dewpoint-degc=%.0lf", dewpoint); - args.push_back(buf); - } - if (pressure != NaN) { - sprintf(&buf[pos], "pressure-sea-level-inhg=%.0lf", pressure); - args.push_back(buf); - } - if (wind_dir != NaN) { - sprintf(&buf[pos], "wind-from-heading-deg=%d", wind_dir); - args.push_back(buf); - } - if (wind_speed != NaN) { - sprintf(&buf[pos], "wind-speed-kt=%.0lf", wind_speed); - args.push_back(buf); - } - } - - // wind dir@speed - int range_from = m->getWindRangeFrom(); - int range_to = m->getWindRangeTo(); - double gust_speed = m->getGustSpeed_kt(); - if (wind_speed != NaN && wind_dir != -1) { - strcpy(buf, "--wind="); - if (range_from != -1 && range_to != -1) - sprintf(&buf[strlen(buf)], "%d:%d", range_from, range_to); - else - sprintf(&buf[strlen(buf)], "%d", wind_dir); - sprintf(&buf[strlen(buf)], "@%.0lf", wind_speed); - if (gust_speed != NaN) - sprintf(&buf[strlen(buf)], ":%.0lf", gust_speed); - args.push_back(buf); - } - - - // output everything - cout << "fgfs" << endl; - vector::iterator arg; - for (i = 0, arg = args.begin(); arg != args.end(); i++, arg++) { - cout << "\t" << *arg << endl; - } - cout << endl; -#undef NaN -} - - - - - -const char *metar_list[] = { - "LOWW", "VHHH", "ULLI", "EHTW", "EFHK", "CYXU", 0, // note the trailing zero - "CYGK", "CYOW", "CYQY", "CYTZ", "CYXU", "EBBR", "EDDB", "EDDK", "EDVE", "EFHF", - "EFHK", "EGLC", "EGLL", "EHTW", "EIDW", "ENGM", "GMMN", "KART", "KBFI", "KBOS", - "KCCR", "KCEZ", "KCOF", "KDAL", "KDEN", "KDSM", "KEDW", "KEMT", "KENW", "KHON", - "KIGM", "KJFK", "KLAX", "KMCI", "KMKE", "KMLB", "KMSY", "KNBC", "KOAK", "KORD", - "KPNE", "KSAC", "KSAN", "KSEA", "KSFO", "KSJC", "KSMF", "KSMO", "KSNS", "KSQL", - "KSUN", "LBSF", "LEMD", "LFPG", "LFPO", "LGAT", "LHBP", "LIPQ", "LIRA", "LKPR", - "LLJR", "LOWG", "LOWI", "LOWK", "LOWL", "LOWS", "LOWW", "LOWZ", "LOXA", "LOXT", - "LOXZ", "LSZH", "LYBE", "NZWP", "ORBS", "PHNL", "ULLI", "VHHH", "WMKB", "YSSY", - 0 -}; - - -int main(int argc, char *argv[]) -{ - const char **src = metar_list; - if (argc > 1) - src = (const char **)&argv[1]; - - for (int i = 0; src[i]; i++) { - const char *icao = src[i]; - - try { - Metar *m = new Metar(icao); - //Metar *m = new Metar("2004/01/11 01:20\nLOWG 110120Z AUTO VRB01KT 0050 1600N R35/0600 FG M06/M06 Q1019 88//////\n"); - - printf(G"INPUT: %s\n"N, m->getData()); - const char *unused = m->getUnusedData(); - if (*unused) - printf(R"UNUSED: %s\n"N, unused); - - printReport(m); - //printArgs(m, 0.0); - - delete m; - } catch (const sg_io_exception& e) { - fprintf(stderr, R"ERROR: %s\n\n"N, e.getFormattedMessage().c_str()); - } - } - return 0; -} - - diff --git a/src/Environment/metar.cxx b/src/Environment/metar.cxx deleted file mode 100644 index c99157fd7..000000000 --- a/src/Environment/metar.cxx +++ /dev/null @@ -1,1101 +0,0 @@ -// metar interface class -// -// Written by Melchior FRANZ, started December 2003. -// -// Copyright (C) 2003 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 metar.cxx - * Interface for encoded Metar aviation weather data. - */ - -#include - -#include -#include -#include - -#include "metar.hxx" - -#define NaN FGMetarNaN - -/** - * 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 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. - * - * @par Examples: - * @code - * Metar *m = new Metar("METAR KSFO 061656Z 19004KT 9SM SCT100 OVC200 08/03 A3013"); - * double t = m->getTemperature(); - * delete m; - * - * Metar n("KSFO"); - * double d = n.getDewpoint_C(); - * @endcode - */ -Metar::Metar(const char *m) : - _grpcount(0), - _year(-1), - _month(-1), - _day(-1), - _hour(-1), - _minute(-1), - _report_type(-1), - _wind_dir(-1), - _wind_speed(NaN), - _gust_speed(NaN), - _wind_range_from(-1), - _wind_range_to(-1), - _temp(NaN), - _dewp(NaN), - _pressure(NaN) -{ - int i; - if (isalpha(m[0]) && isalpha(m[1]) && isalpha(m[2]) && isalpha(m[3]) && !m[4]) { - for (i = 0; i < 4; i++) - _icao[i] = toupper(m[i]); - _icao[4] = '\0'; - _data = loadData(_icao); - } else { - _data = new char[strlen(m) + 1]; - strcpy(_data, m); - } - normalizeData(); - - _m = _data; - _icao[0] = '\0'; - - // NOAA preample - scanPreambleDate(); - scanPreambleTime(); - - // METAR header - scanType(); - if (!scanId() || !scanDate()) - throw sg_io_exception("metar data incomplete"); - scanModifier(); - - // base set - scanWind(); - scanVariability(); - while (scanVisibility()) ; - while (scanRwyVisRange()) ; - while (scanWeather()) ; - while (scanSkyCondition()) ; - scanTemperature(); - scanPressure(); - while (scanSkyCondition()) ; - while (scanRunwayReport()) ; - scanWindShear(); - - // appendix - while (scanColorState()) ; - scanTrendForecast(); - while (scanRunwayReport()) ; - scanRemainder(); - scanRemark(); - - if (_grpcount < 4) - throw sg_io_exception("metar data invalid"); -} - - -/** - * Clears lists and maps to discourage access after destruction. - */ -Metar::~Metar() -{ - _clouds.clear(); - _runways.clear(); - _weather.clear(); - delete[] _data; -} - - -/** - * 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". - * @return pointer to Metar data string, allocated by new char[]. - */ -char *Metar::loadData(const char *id) -{ - string host = "weather.noaa.gov"; - string path = "/pub/data/observations/metar/stations/"; - path += string(id) + ".TXT"; - string get = string("GET ") + path + " HTTP/1.0\r\n\r\n"; - - SGSocket *sock = new SGSocket(host, "80", "tcp"); - sock->set_timeout(10000); - if (!sock->open(SG_IO_OUT)) { - delete sock; - string err = "failed to load metar data from http://" + host + path; - throw sg_io_exception(err); - } - - sock->writestring(get.c_str()); - - int i; - const int buflen = 512; - char buf[2 * buflen]; - - // skip HTTP header - while ((i = sock->readline(buf, buflen))) - if (i <= 2 && isspace(buf[0]) && (!buf[1] || isspace(buf[1]))) - break; - if (i) { - i = sock->readline(buf, buflen); - if (i) - sock->readline(&buf[i], buflen); - } - - sock->close(); - delete sock; - - char *metar = new char[strlen(buf) + 1]; - strcpy(metar, buf); - return metar; -} - - -/** - * Replace any number of subsequent spaces by just one space. - * This makes scanning for things like "ALL RWY" easier. - */ -void Metar::normalizeData() -{ - char *src, *dest; - for (src = dest = _data; (*dest++ = *src++); ) - while (*src == ' ' && src[1] == ' ') - src++; -} - - -// \d\d\d\d/\d\d/\d\d -bool Metar::scanPreambleDate() -{ - char *m = _m; - int year, month, day; - if (!scanNumber(&m, &year, 4)) - return false; - if (*m++ != '/') - return false; - if (!scanNumber(&m, &month, 2)) - return false; - if (*m++ != '/') - return false; - if (!scanNumber(&m, &day, 2)) - return false; - if (!scanBoundary(&m)) - return false; - _year = year; - _month = month; - _day = day; - _m = m; - return true; -} - - -// \d\d:\d\d -bool Metar::scanPreambleTime() -{ - char *m = _m; - int hour, minute; - if (!scanNumber(&m, &hour, 2)) - return false; - if (*m++ != ':') - return false; - if (!scanNumber(&m, &minute, 2)) - return false; - if (!scanBoundary(&m)) - return false; - _hour = hour; - _minute = minute; - _m = m; - return true; -} - - -// (METAR|SPECI) -bool Metar::scanType() -{ - if (strncmp(_m, "METAR ", 6) && strncmp(_m, "SPECI ", 6)) - return false; - _m += 6; - _grpcount++; - return true; -} - - -// [A-Z]{4} -bool Metar::scanId() -{ - char *m = _m; - if (!(isupper(*m++) && isupper(*m++) && isupper(*m++) && isupper(*m++))) - return false; - if (!scanBoundary(&m)) - return false; - strncpy(_icao, _m, 4); - _icao[4] = '\0'; - _m = m; - _grpcount++; - return true; -} - - -// \d{6}Z -bool Metar::scanDate() -{ - char *m = _m; - int day, hour, minute; - if (!scanNumber(&m, &day, 2)) - return false; - if (!scanNumber(&m, &hour, 2)) - return false; - if (!scanNumber(&m, &minute, 2)) - return false; - if (*m++ != 'Z') - return false; - if (!scanBoundary(&m)) - return false; - _day = day; - _hour = hour; - _minute = minute; - _m = m; - _grpcount++; - return true; -} - - -// (NIL|AUTO|COR|RTD) -bool Metar::scanModifier() -{ - char *m = _m; - int type; - if (!strncmp(m, "NIL", 3)) { - _m += strlen(_m); - return true; - } - if (!strncmp(m, "AUTO", 4)) // automatically generated - m += 4, type = AUTO; - else if (!strncmp(m, "COR", 3)) // manually corrected - m += 3, type = COR; - else if (!strncmp(m, "RTD", 3)) // routine delayed - m += 3, type = RTD; - else - return false; - if (!scanBoundary(&m)) - return false; - _report_type = type; - _m = m; - _grpcount++; - return true; -} - - -// (\d{3}|VRB)\d{1,3}(G\d{2,3})?(KT|KMH|MPS) -bool Metar::scanWind() -{ - char *m = _m; - int dir; - if (!strncmp(m, "VRB", 3)) - m += 3, dir = -1; - else if (!scanNumber(&m, &dir, 3)) - return false; - - int i; - if (!scanNumber(&m, &i, 2, 3)) - return false; - double speed = i; - - double gust = NaN; - if (*m == 'G') { - m++; - if (!scanNumber(&m, &i, 2, 3)) - return false; - gust = i; - } - double factor; - if (!strncmp(m, "KT", 2)) - m += 2, factor = SG_KT_TO_MPS; - else if (!strncmp(m, "KMH", 3)) - m += 3, factor = SG_KMH_TO_MPS; - else if (!strncmp(m, "KPH", 3)) // ?? - m += 3, factor = SG_KMH_TO_MPS; - else if (!strncmp(m, "MPS", 3)) - m += 3, factor = 1.0; - else - return false; - if (!scanBoundary(&m)) - return false; - _m = m; - _wind_dir = dir; - _wind_speed = speed * factor; - if (gust != NaN) - _gust_speed = gust * factor; - _grpcount++; - return false; -} - - -// \d{3}V\d{3} -bool Metar::scanVariability() -{ - char *m = _m; - int from, to; - if (!scanNumber(&m, &from, 3)) - return false; - if (*m++ != 'V') - return false; - if (!scanNumber(&m, &to, 3)) - return false; - if (!scanBoundary(&m)) - return false; - _m = m; - _wind_range_from = from; - _wind_range_to = to; - _grpcount++; - return true; -} - - -bool Metar::scanVisibility() -// TODO: if only directed vis are given, do still set min/max -{ - char *m = _m; - double distance; - int i, dir = -1; - int modifier = FGMetarVisibility::EQUALS; -// \d{4}(N|NE|E|SE|S|SW|W|NW)? - if (scanNumber(&m, &i, 4)) { - if (*m == 'E') - m++, dir = 90; - else if (*m == 'W') - m++, dir = 270; - else if (*m == 'N') { - m++; - if (*m == 'E') - m++, dir = 45; - else if (*m == 'W') - m++, dir = 315; - else - dir = 0; - } else if (*m == 'S') { - m++; - if (*m == 'E') - m++, dir = 135; - else if (*m == 'W') - m++, dir = 225; - else - dir = 180; - } - if (i == 0) - i = 50, modifier = FGMetarVisibility::LESS_THAN; - else if (i == 9999) - i++, modifier = FGMetarVisibility::GREATER_THAN; - 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 = FGMetarVisibility::LESS_THAN; - - if (!scanNumber(&m, &i, 1, 2)) - return false; - distance = i; - - if (*m == '/') { - m++; - if (!scanNumber(&m, &i, 1, 2)) - return false; - distance /= i; - } else if (*m == ' ') { - m++; - int denom; - if (!scanNumber(&m, &i, 1, 2)) - return false; - if (*m++ != '/') - return false; - if (!scanNumber(&m, &denom, 1, 2)) - return false; - distance += (double)i / denom; - } - - if (!strncmp(m, "SM", 2)) - distance *= SG_SM_TO_METER, m += 2; - else if (!strncmp(m, "KM", 2)) - distance *= 1000, m += 2; - else - return false; - } - if (!scanBoundary(&m)) - return false; - - FGMetarVisibility *v; - if (dir != -1) - v = &_dir_visibility[dir / 45]; - else if (_min_visibility._distance == NaN) - v = &_min_visibility; - else - v = &_max_visibility; - - v->_distance = distance; - v->_modifier = modifier; - v->_direction = dir; - _m = m; - _grpcount++; - return true; -} - - -// R\d\d[LCR]?/([PM]?\d{4}V)?[PM]?\d{4}(FT)?[DNU]? -bool Metar::scanRwyVisRange() -{ - char *m = _m; - int i; - FGMetarRunway r; - if (*m++ != 'R') - return false; - if (!scanNumber(&m, &i, 2)) - return false; - if (*m == 'L' || *m == 'C' || *m == 'R') - m++; - - char id[4]; - strncpy(id, _m + 1, i = m - _m - 1); - id[i] = '\0'; - - if (*m++ != '/') - return false; - - int from, to; - if (*m == 'P') - m++, r._min_visibility._modifier = FGMetarVisibility::GREATER_THAN; - else if (*m == 'M') - m++, r._min_visibility._modifier = FGMetarVisibility::LESS_THAN; - if (!scanNumber(&m, &from, 4)) - return false; - if (*m == 'V') { - m++; - if (*m == 'P') - m++, r._max_visibility._modifier = FGMetarVisibility::GREATER_THAN; - else if (*m == 'M') - m++, r._max_visibility._modifier = FGMetarVisibility::LESS_THAN; - if (!scanNumber(&m, &to, 4)) - return false; - } else - to = from; - - if (!strncmp(m, "FT", 2)) { - from = int(from * SG_FEET_TO_METER); - to = int(to * SG_FEET_TO_METER); - m += 2; - } - r._min_visibility._distance = from; - r._max_visibility._distance = to; - - if (*m == '/') // this is not in the spec! - *m++; - if (*m == 'D') - m++, r._min_visibility._tendency = FGMetarVisibility::DECREASING; - else if (*m == 'N') - m++, r._min_visibility._tendency = FGMetarVisibility::STABLE; - else if (*m == 'U') - m++, r._min_visibility._tendency = FGMetarVisibility::INCREASING; - - if (!scanBoundary(&m)) - return false; - _m = m; - - _runways[id]._min_visibility = r._min_visibility; - _runways[id]._max_visibility = r._max_visibility; - _grpcount++; - return true; -} - - -static const struct Token special[] = { - "NSW", "no significant weather", - "VCSH", "showers in the vicinity", - "VCTS", "thunderstorm in the vicinity", - 0, 0 -}; - - -static const struct Token description[] = { - "SH", "showers of", - "TS", "thunderstorm with", - "BC", "patches of", - "BL", "blowing", - "DR", "low drifting", - "FZ", "freezing", - "MI", "shallow", - "PR", "partial", - 0, 0 -}; - - -static const struct Token phenomenon[] = { - "DZ", "drizzle", - "GR", "hail", - "GS", "small hail and/or snow pellets", - "IC", "ice crystals", - "PE", "ice pellets", - "RA", "rain", - "SG", "snow grains", - "SN", "snow", - "UP", "unknown precipitation", - "BR", "mist", - "DU", "widespread dust", - "FG", "fog", - "FGBR", "fog bank", - "FU", "smoke", - "HZ", "haze", - "PY", "spray", - "SA", "sand", - "VA", "volcanic ash", - "DS", "duststorm", - "FC", "funnel cloud/tornado waterspout", - "PO", "well-developed dust/sand whirls", - "SQ", "squalls", - "SS", "sandstorm", - "UP", "unknown", // ... due to failed automatic acquisition - 0, 0 -}; - - -// (+|-|VC)?(NSW|MI|PR|BC|DR|BL|SH|TS|FZ)?((DZ|RA|SN|SG|IC|PE|GR|GS|UP){0,3})(BR|FG|FU|VA|DU|SA|HZ|PY|PO|SQ|FC|SS|DS){0,3} -bool Metar::scanWeather() -{ - char *m = _m; - string weather; - const struct Token *a; - if ((a = scanToken(&m, special))) { - if (!scanBoundary(&m)) - return false; - _weather.push_back(a->text); - _m = m; - return true; - } - - string pre, post; - if (*m == '-') - m++, pre = "light "; - else if (*m == '+') - m++, pre = "heavy "; - else if (!strncmp(m, "VC", 2)) - m += 2, post = "in the vicinity "; - else - pre = "moderate "; - - int i; - for (i = 0; i < 3; i++) { - if (!(a = scanToken(&m, description))) - break; - weather += string(a->text) + " "; - } - for (i = 0; i < 3; i++) { - if (!(a = scanToken(&m, phenomenon))) - break; - weather += string(a->text) + " "; - } - if (!weather.length()) - return false; - if (!scanBoundary(&m)) - return false; - _m = m; - weather = pre + weather + post; - weather.erase(weather.length() - 1); - _weather.push_back(weather); - _grpcount++; - return true; -} - - -static const struct Token cloud_types[] = { - "AC", "altocumulus", - "ACC", "altocumulus castellanus", - "ACSL", "altocumulus standing lenticular", - "AS", "altostratus", - "CB", "cumulonimbus", - "CBMAM", "cumulonimbus mammatus", - "CC", "cirrocumulus", - "CCSL", "cirrocumulus standing lenticular", - "CI", "cirrus", - "CS", "cirrostratus", - "CU", "cumulus", - "CUFRA", "cumulus fractus", - "NS", "nimbostratus", - "SAC", "stratoaltocumulus", // guessed - "SC", "stratocumulus", - "SCSL", "stratocumulus standing lenticular", - "ST", "stratus", - "STFRA", "stratus fractus", - "TCU", "towering cumulus", - 0, 0 -}; - - -// (FEW|SCT|BKN|OVC|SKC|CLR|CAVOK|VV)([0-9]{3}|///)?[:cloud_type:]? -bool Metar::scanSkyCondition() -{ - char *m = _m; - int i; - FGMetarCloud cl; - - if (!strncmp(m, "CLR", i = 3) // clear - || !strncmp(m, "SKC", i = 3) // sky clear - || !strncmp(m, "NSC", i = 3) // no significant clouds - || !strncmp(m, "CAVOK", i = 5)) { // ceiling and visibility OK (implies 9999) - m += i; - if (!scanBoundary(&m)) - return false; - cl._coverage = 0; - _clouds.push_back(cl); - _m = m; - return true; - } - - if (!strncmp(m, "VV", i = 2)) // vertical visibility - ; - else if (!strncmp(m, "FEW", i = 3)) - cl._coverage = 1; - else if (!strncmp(m, "SCT", i = 3)) - cl._coverage = 2; - else if (!strncmp(m, "BKN", i = 3)) - cl._coverage = 3; - else if (!strncmp(m, "OVC", i = 3)) - cl._coverage = 4; - else - return false; - m += i; - - if (!strncmp(m, "///", 3)) // vis not measurable (e.g. because of heavy snowing) - m += 3, i = -1; - else if (scanBoundary(&m)) { - _m = m; - return true; // ignore single OVC/BKN/... - } else if (!scanNumber(&m, &i, 3)) - i = -1; - - if (cl._coverage == -1) { - if (!scanBoundary(&m)) - return false; - if (i == -1) // 'VV///' - _vert_visibility._modifier = FGMetarVisibility::NOGO; - else - _vert_visibility._distance = i * 100 * SG_FEET_TO_METER; - _m = m; - return true; - } - - if (i != -1) - cl._altitude = i * 100 * SG_FEET_TO_METER; - - const struct Token *a; - if ((a = scanToken(&m, cloud_types))) { - cl._type = a->id; - cl._type_long = a->text; - } - if (!scanBoundary(&m)) - return false; - _clouds.push_back(cl); - _m = m; - _grpcount++; - return true; -} - - -// M?[0-9]{2}/(M?[0-9]{2})? (spec) -// (M?[0-9]{2}|XX)/(M?[0-9]{2}|XX)? (Namibia) -bool Metar::scanTemperature() -{ - char *m = _m; - int sign = 1, temp, dew; - if (!strncmp(m, "XX/XX", 5)) { // not spec compliant! - _m += 5; - return scanBoundary(&_m); - } - - if (*m == 'M') - m++, sign = -1; - if (!scanNumber(&m, &temp, 2)) - return false; - temp *= sign; - - if (*m++ != '/') - return false; - if (!scanBoundary(&m)) { - if (!strncmp(m, "XX", 2)) // not spec compliant! - m += 2, sign = 0; - else { - sign = 1; - if (*m == 'M') - m++, sign = -1; - if (!scanNumber(&m, &dew, 2)) - return false; - } - if (!scanBoundary(&m)) - return false; - if (sign) - _dewp = sign * dew; - } - _temp = temp; - _m = m; - _grpcount++; - return true; -} - - -double Metar::getRelHumidity() const -{ - if (_temp == NaN || _dewp == NaN) - return NaN; - double dewp = pow(10, 7.5 * _dewp / (237.7 + _dewp)); - double temp = pow(10, 7.5 * _temp / (237.7 + _temp)); - return dewp * 100 / temp; -} - - -// [AQ]\d{4} (spec) -// [AQ]\d{2}(\d{2}|//) (Namibia) -bool Metar::scanPressure() -{ - char *m = _m; - double factor; - int press, i; - - if (*m == 'A') - factor = SG_INHG_TO_PA / 100; - else if (*m == 'Q') - factor = 100; - else - return false; - m++; - if (!scanNumber(&m, &press, 2)) - return false; - press *= 100; - if (!strncmp(m, "//", 2)) // not spec compliant! - m += 2; - else if (scanNumber(&m, &i, 2)) - press += i; - else - return false; - if (!scanBoundary(&m)) - return false; - _pressure = press * factor; - _m = m; - _grpcount++; - return true; -} - - -static const char *runway_deposit[] = { - "clear and dry", - "damp", - "wet or puddles", - "frost", - "dry snow", - "wet snow", - "slush", - "ice", - "compacted snow", - "frozen ridges" -}; - - -static const char *runway_deposit_extent[] = { - 0, "1-10%", "11-25%", 0, 0, "26-50%", 0, 0, 0, "51-100%" -}; - - -static const char *runway_friction[] = { - 0, - "poor braking action", - "poor/medium braking action", - "medium braking action", - "medium/good braking action", - "good braking action", - 0, 0, 0, - "friction: unreliable measurement" -}; - - -// \d\d(CLRD|[\d/]{4})(\d\d|//) -bool Metar::scanRunwayReport() -{ - char *m = _m; - int i; - char id[4]; - FGMetarRunway r; - - if (!scanNumber(&m, &i, 2)) - return false; - if (i == 88) - strcpy(id, "ALL"); - else if (i == 99) - strcpy(id, "REP"); // repetition of previous report - else if (i >= 50) { - i -= 50; - id[0] = i / 10 + '0', id[1] = i % 10 + '0', id[2] = 'R', id[3] = '\0'; - } else - id[0] = i / 10 + '0', id[1] = i % 10 + '0', id[2] = '\0'; - - if (!strncmp(m, "CLRD", 4)) { - m += 4; // runway cleared - r._deposit = "cleared"; - } else { - if (scanNumber(&m, &i, 1)) { - r._deposit = runway_deposit[i]; - } else if (*m == '/') - m++; - else - return false; - if (*m == '1' || *m == '2' || *m == '5' || *m == '9') { // extent of deposit - r._extent = *m - '0'; - r._extent_string = runway_deposit_extent[*m - '0']; - } else if (*m != '/') - return false; - m++; - i = -1; - if (!strncmp(m, "//", 2)) - m += 2; - else if (!scanNumber(&m, &i, 2)) - return false; - - if (i == 0) - r._depth = 0.5; // < 1 mm deep (let's say 0.5 :-) - else if (i > 0 && i <= 90) - r._depth = i / 1000.0; // i mm deep - else if (i >= 92 && i <= 98) - r._depth = (i - 90) / 20.0; - else if (i == 99) - r._comment = "runway not in use"; - else if (i == -1) // no depth given ("//") - ; - else - return false; - } - i = -1; - if (m[0] == '/' && m[1] == '/') - m += 2; - else if (!scanNumber(&m, &i, 2)) - return false; - if (i >= 1 && i < 90) { - r._friction = i / 100.0; - } else if ((i >= 91 && i <= 95) || i == 99) { - r._friction_string = runway_friction[i - 90]; - } - if (!scanBoundary(&m)) - return false; - - _runways[id]._deposit = r._deposit; - _runways[id]._extent = r._extent; - _runways[id]._extent_string = r._extent_string; - _runways[id]._depth = r._depth; - _runways[id]._friction = r._friction; - _runways[id]._friction_string = r._friction_string; - _runways[id]._comment = r._comment; - _m = m; - _grpcount++; - return true; -} - - -// WS (ALL RWYS?|RWY ?\d\d[LCR]?)? -bool Metar::scanWindShear() -{ - char *m = _m; - if (strncmp(m, "WS", 2)) - return false; - m += 2; - if (!scanBoundary(&m)) - return false; - - if (!strncmp(m, "ALL", 3)) { - m += 3; - if (!scanBoundary(&m)) - return false; - if (strncmp(m, "RWY", 3)) - return false; - m += 3; - if (*m == 'S') - m++; - if (!scanBoundary(&m)) - return false; - _runways["ALL"]._wind_shear = true; - _m = m; - return true; - } - - char id[4], *mm; - int i, cnt; - for (cnt = 0;; cnt++) { // ?? - if (strncmp(m, "RWY", 3)) - break; - m += 3; - scanBoundary(&m); - mm = m; - if (!scanNumber(&m, &i, 2)) - return false; - if (*m == 'L' || *m == 'C' || *m == 'R') - m++; - strncpy(id, mm, i = m - mm); - id[i] = '\0'; - if (!scanBoundary(&m)) - return false; - _runways[id]._wind_shear = true; - } - if (!cnt) - _runways["ALL"]._wind_shear = true; - _m = m; - return true; -} - - -bool Metar::scanTrendForecast() -{ - char *m = _m; - if (strncmp(m, "NOSIG", 5)) - return false; - - m += 5; - if (!scanBoundary(&m)) - return false; - _m = m; - return true; -} - - -// (BLU|WHT|GRN|YLO|AMB|RED) -static const struct Token colors[] = { - "BLU", "Blue", // 2500 ft, 8.0 km - "WHT", "White", // 1500 ft, 5.0 km - "GRN", "Green", // 700 ft, 3.7 km - "YLO", "Yellow", // 300 ft, 1.6 km - "AMB", "Amber", // 200 ft, 0.8 km - "RED", "Red", // <200 ft, <0.8 km - 0, 0 -}; - - -bool Metar::scanColorState() -{ - char *m = _m; - const struct Token *a; - if (!(a = scanToken(&m, colors))) - return false; - if (!scanBoundary(&m)) - return false; - //printf(Y"Code %s\n"N, a->text); - _m = m; - return true; -} - - -bool Metar::scanRemark() -{ - if (strncmp(_m, "RMK", 3)) - return false; - _m += 3; - if (!scanBoundary(&_m)) - return false; - - while (*_m) { - if (!scanRunwayReport()) { - while (*_m && !isspace(*_m)) - _m++; - scanBoundary(&_m); - } - } - return true; -} - - -bool Metar::scanRemainder() -{ - char *m = _m; - if (!(strncmp(m, "NOSIG", 5))) { - m += 5; - if (scanBoundary(&m)) - _m = m; //_comment.push_back("No significant tendency"); - } - - if (!scanBoundary(&m)) - return false; - _m = m; - return true; -} - - -bool Metar::scanBoundary(char **s) -{ - if (**s && !isspace(**s)) - return false; - while (isspace(**s)) - (*s)++; - return true; -} - - -int Metar::scanNumber(char **src, int *num, int min, int max) -{ - int i; - char *s = *src; - *num = 0; - for (i = 0; i < min; i++) { - if (!isdigit(*s)) - return 0; - else - *num = *num * 10 + *s++ - '0'; - } - for (; i < max && isdigit(*s); i++) - *num = *num * 10 + *s++ - '0'; - *src = s; - return i; -} - - -// find longest match of str in list -const struct Token *Metar::scanToken(char **str, const struct Token *list) -{ - const struct Token *longest = 0; - int maxlen = 0, len; - char *s; - for (int i = 0; (s = list[i].id); i++) { - len = strlen(s); - if (!strncmp(s, *str, len) && len > maxlen) { - maxlen = len; - longest = &list[i]; - } - } - *str += maxlen; - return longest; -} - -#undef NaN diff --git a/src/Environment/metar.hxx b/src/Environment/metar.hxx deleted file mode 100644 index 8dcb55705..000000000 --- a/src/Environment/metar.hxx +++ /dev/null @@ -1,261 +0,0 @@ -// metar interface class -// -// Written by Melchior FRANZ, started December 2003. -// -// Copyright (C) 2003 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 _METAR_HXX -#define _METAR_HXX - -#include -#include -#include - -#include - -SG_USING_STD(vector); -SG_USING_STD(map); -SG_USING_STD(string); - -const double FGMetarNaN = -1E20; -#define NaN FGMetarNaN - -struct Token { - char *id; - char *text; -}; - - -class Metar; - -class FGMetarVisibility { - friend class Metar; -public: - FGMetarVisibility() : - _distance(NaN), - _direction(-1), - _modifier(EQUALS), - _tendency(NONE) {} - - enum Modifier { - NOGO, - EQUALS, - LESS_THAN, - GREATER_THAN - }; - - enum Tendency { - NONE, - STABLE, - INCREASING, - DECREASING - }; - - 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; } - inline int getDirection() const { return _direction; } - inline int getModifier() const { return _modifier; } - inline int getTendency() const { return _tendency; } - -protected: - double _distance; - int _direction; - int _modifier; - int _tendency; -}; - - -// runway condition (surface and visibility) -class FGMetarRunway { - friend class Metar; -public: - FGMetarRunway() : - _deposit(0), - _extent(-1), - _extent_string(0), - _depth(NaN), - _friction(NaN), - _friction_string(0), - _comment(0), - _wind_shear(false) {} - - inline const char *getDeposit() const { return _deposit; } - inline double getExtent() const { return _extent; } - inline const char *getExtentString() const { return _extent_string; } - inline double getDepth() const { return _depth; } - inline double getFriction() const { return _friction; } - inline const char *getFrictionString() const { return _friction_string; } - inline const char *getComment() const { return _comment; } - inline const bool getWindShear() const { return _wind_shear; } - inline FGMetarVisibility getMinVisibility() const { return _min_visibility; } - inline FGMetarVisibility getMaxVisibility() const { return _max_visibility; } - -protected: - FGMetarVisibility _min_visibility; - FGMetarVisibility _max_visibility; - const char *_deposit; - int _extent; - const char *_extent_string; - double _depth; - double _friction; - const char *_friction_string; - const char *_comment; - bool _wind_shear; -}; - - -// cloud layer -class FGMetarCloud { - friend class Metar; -public: - FGMetarCloud() : - _coverage(-1), - _altitude(NaN), - _type(0), - _type_long(0) {} - - 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; } - inline char *getTypeString() const { return _type; } - inline char *getTypeLongString() const { return _type_long; } - -protected: - int _coverage; // quarters: 0 -> clear ... 4 -> overcast - double _altitude; // 1000 m - char *_type; // CU - char *_type_long; // cumulus -}; - - -class Metar { -public: - Metar(const char *m); - Metar(const string m) { Metar(m.c_str()); } - ~Metar(); - - enum ReportType { - NONE, - AUTO, - COR, - RTD - }; - - inline const char *getData() const { return _data; } - inline const char *getUnusedData() const { return _m; } - inline const char *getId() const { return _icao; } - inline int getYear() const { return _year; } - inline int getMonth() const { return _month; } - inline int getDay() const { return _day; } - inline int getHour() const { return _hour; } - inline int getMinute() const { return _minute; } - inline int getReportType() const { return _report_type; } - - inline int getWindDir() const { return _wind_dir; } - inline double getWindSpeed_mps() const { return _wind_speed; } - inline double getWindSpeed_kmh() const { return _wind_speed == NaN ? NaN : _wind_speed * 3.6; } - inline double getWindSpeed_kt() const { return _wind_speed == NaN ? NaN : _wind_speed * SG_MPS_TO_KT; } - inline double getWindSpeed_mph() const { return _wind_speed == NaN ? NaN : _wind_speed * SG_MPS_TO_MPH; } - - inline double getGustSpeed_mps() const { return _gust_speed; } - inline double getGustSpeed_kmh() const { return _gust_speed == NaN ? NaN : _gust_speed * 3.6; } - inline double getGustSpeed_kt() const { return _gust_speed == NaN ? NaN : _gust_speed * SG_MPS_TO_KT; } - inline double getGustSpeed_mph() const { return _gust_speed == NaN ? NaN : _gust_speed * SG_MPS_TO_MPH; } - - inline int getWindRangeFrom() const { return _wind_range_from; } - inline int getWindRangeTo() const { return _wind_range_to; } - - inline FGMetarVisibility& getMinVisibility() { return _min_visibility; } - inline FGMetarVisibility& getMaxVisibility() { return _max_visibility; } - inline FGMetarVisibility& getVertVisibility() { return _vert_visibility; } - inline FGMetarVisibility *getDirVisibility() { return _dir_visibility; } - - inline double getTemperature_C() const { return _temp; } - inline double getTemperature_F() const { return _temp == NaN ? NaN : 1.8 * _temp + 32; } - inline double getDewpoint_C() const { return _dewp; } - inline double getDewpoint_F() const { return _dewp == NaN ? NaN : 1.8 * _dewp + 32; } - 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; } - - double getRelHumidity() const; - - inline vector& getClouds() { return _clouds; } - inline map& getRunways() { return _runways; } - inline vector& getWeather() { return _weather; } - -protected: - int _grpcount; - char *_data; - char *_m; - char _icao[5]; - int _year; - int _month; - int _day; - int _hour; - int _minute; - int _report_type; - int _wind_dir; - double _wind_speed; - double _gust_speed; - int _wind_range_from; - int _wind_range_to; - double _temp; - double _dewp; - double _pressure; - - FGMetarVisibility _min_visibility; - FGMetarVisibility _max_visibility; - FGMetarVisibility _vert_visibility; - FGMetarVisibility _dir_visibility[8]; - vector _clouds; - map _runways; - vector _weather; - - bool scanPreambleDate(); - bool scanPreambleTime(); - - bool scanType(); - bool scanId(); - bool scanDate(); - bool scanModifier(); - bool scanWind(); - bool scanVariability(); - bool scanVisibility(); - bool scanRwyVisRange(); - bool scanSkyCondition(); - bool scanWeather(); - bool scanTemperature(); - bool scanPressure(); - bool scanRunwayReport(); - bool scanWindShear(); - bool scanTrendForecast(); - bool scanColorState(); - bool scanRemark(); - bool scanRemainder(); - - 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); - void normalizeData(); -}; - -#undef NaN -#endif // _METAR_HXX diff --git a/src/Main/Makefile.am b/src/Main/Makefile.am index c244aaf0f..dbc93968d 100644 --- a/src/Main/Makefile.am +++ b/src/Main/Makefile.am @@ -26,7 +26,7 @@ AM_CXXFLAGS = -DPKGLIBDIR=\"$(pkglibdir)\" EXTRA_DIST = 3dfx.sh runfgfs.in runfgfs.bat.in -bin_PROGRAMS = fgfs +bin_PROGRAMS = fgfs metar noinst_SCRIPTS = runfgfs.bat runfgfs @@ -95,4 +95,11 @@ fgfs_LDADD = \ $(opengl_LIBS) \ $(audio_LIBS) +metar_SOURCES = metar_main.cxx + +metar_LDADD = \ + -lsgenvironment -lsgio -lsgbucket -lsgmisc -lsgstructure -lsgdebug \ + -lplibnet -lplibul \ + -lz $(base_LIBS) + INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src diff --git a/src/Main/metar_main.cxx b/src/Main/metar_main.cxx new file mode 100644 index 000000000..4fdc9dd95 --- /dev/null +++ b/src/Main/metar_main.cxx @@ -0,0 +1,481 @@ +// metar interface class demo +// +// Written by Melchior FRANZ, started December 2003. +// +// Copyright (C) 2003 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$ + +#include +#include + +#include +#include +#include + +using std::ostringstream; + +// text color +#if defined(__linux__) || defined( __sun__ ) ||defined(__CYGWIN__) || defined( __FreeBSD__ ) +# define R "\033[31;1m" // red +# define G "\033[32;1m" // green +# define Y "\033[33;1m" // yellow +# define B "\033[34;1m" // blue +# define M "\033[35;1m" // magenta +# define C "\033[36;1m" // cyan +# define W "\033[37;1m" // white +# define N "\033[m" // normal +#else +# define R "" +# define G "" +# define Y "" +# define B "" +# define M "" +# define C "" +# define W "" +# define N "" +#endif + + +const char *azimuthName(double d); +double rnd(double number, int digits); +void printReport(SGMetar *m); +void printVisibility(SGMetarVisibility *v); +void printArgs(SGMetar *m, double airport_elevation); + + +const char *azimuthName(double d) +{ + const char *dir[] = { + "N", "NNE", "NE", "ENE", + "E", "ESE", "SE", "SSE", + "S", "SSW", "SW", "WSW", + "W", "WNW", "NW", "NNW" + }; + d += 11.25; + while (d < 0) + d += 360; + while (d >= 360) + d -= 360; + return dir[int(d / 22.5)]; +} + + +// round double to 10^g +double rnd(double r, int g = 0) +{ + double f = pow(10.0, g); + return f * rint(r / f); +} + + +ostream& operator<<(ostream& s, SGMetarVisibility& v) +{ + ostringstream buf; + int m = v.getModifier(); + const char *mod; + if (m == SGMetarVisibility::GREATER_THAN) + mod = ">="; + else if (m == SGMetarVisibility::LESS_THAN) + mod = "<"; + else + mod = ""; + buf << mod; + + double dist = rnd(v.getVisibility_m(), 1); + if (dist < 1000.0) + buf << rnd(dist, 1) << " m"; + else + buf << rnd(dist / 1000.0, -1) << " km"; + + const char *dir = ""; + int i; + if ((i = v.getDirection()) != -1) { + dir = azimuthName(i); + buf << " " << dir; + } + buf << "\t\t\t\t\t" << mod << rnd(v.getVisibility_sm(), -1) << " US-miles " << dir; + return s << buf.str(); +} + + +void printReport(SGMetar *m) +{ +#define NaN SGMetarNaN + const char *s; + char buf[256]; + double d; + int i, lineno; + + if ((i = m->getReportType()) == SGMetar::AUTO) + s = "\t\t(automatically generated)"; + else if (i == SGMetar::COR) + s = "\t\t(manually corrected)"; + else if (i == SGMetar::RTD) + s = "\t\t(routine delayed)"; + else + s = ""; + + cout << "METAR Report" << s << endl; + cout << "============" << endl; + cout << "Airport-Id:\t\t" << m->getId() << endl; + + + // date/time + int year = m->getYear(); + int month = m->getMonth(); + cout << "Report time:\t\t"; + if (year != -1 && month != -1) + cout << year << '/' << month << '/' << m->getDay(); + cout << ' ' << m->getHour() << ':'; + cout << std::setw(2) << std::setfill('0') << m->getMinute() << " UTC" << endl; + + + // visibility + SGMetarVisibility minvis = m->getMinVisibility(); + SGMetarVisibility maxvis = m->getMaxVisibility(); + double min = minvis.getVisibility_m(); + double max = maxvis.getVisibility_m(); + if (min != NaN) { + if (max != NaN) { + cout << "min. Visibility:\t" << minvis << endl; + cout << "max. Visibility:\t" << maxvis << endl; + } else + cout << "Visibility:\t\t" << minvis << endl; + } + + + // directed visibility + SGMetarVisibility *dirvis = m->getDirVisibility(); + for (i = 0; i < 8; i++, dirvis++) + if (dirvis->getVisibility_m() != NaN) + cout << "\t\t\t" << *dirvis << endl; + + + // vertical visibility + SGMetarVisibility vertvis = m->getVertVisibility(); + if ((d = vertvis.getVisibility_ft()) != NaN) + cout << "Vert. visibility:\t" << vertvis << endl; + else if (vertvis.getModifier() == SGMetarVisibility::NOGO) + cout << "Vert. visibility:\timpossible to determine" << endl; + + + // wind + d = m->getWindSpeed_kmh(); + cout << "Wind:\t\t\t"; + if (d < .1) + cout << "none" << endl; + else { + if ((i = m->getWindDir()) == -1) + cout << "from variable directions"; + else + cout << "from the " << azimuthName(i) << " (" << i << "°)"; + cout << " at " << rnd(d, -1) << " km/h"; + + cout << "\t\t" << rnd(m->getWindSpeed_kt(), -1) << " kt"; + cout << " = " << rnd(m->getWindSpeed_mph(), -1) << " mph"; + cout << " = " << rnd(m->getWindSpeed_mps(), -1) << " m/s"; + cout << endl; + + if ((d = m->getGustSpeed_kmh()) != NaN) { + cout << "\t\t\twith gusts at " << rnd(d, -1) << " km/h"; + cout << "\t\t\t" << rnd(m->getGustSpeed_kt(), -1) << " kt"; + cout << " = " << rnd(m->getGustSpeed_mph(), -1) << " mph"; + cout << " = " << rnd(m->getGustSpeed_mps(), -1) << " m/s"; + cout << endl; + } + + int from = m->getWindRangeFrom(); + int to = m->getWindRangeTo(); + if (from != to) { + cout << "\t\t\tvariable from " << azimuthName(from); + cout << " to " << azimuthName(to); + cout << " (" << from << "°--" << to << "°)" << endl; + } + } + + + // temperature/humidity/air pressure + if ((d = m->getTemperature_C()) != NaN) { + cout << "Temperature:\t\t" << d << "°C\t\t\t\t\t"; + cout << rnd(m->getTemperature_F(), -1) << "°F" << endl; + + if ((d = m->getDewpoint_C()) != NaN) { + cout << "Dewpoint:\t\t" << d << "°C\t\t\t\t\t"; + cout << rnd(m->getDewpoint_F(), -1) << "°F" << endl; + cout << "Rel. Humidity:\t\t" << rnd(m->getRelHumidity()) << "%" << endl; + } + } + if ((d = m->getPressure_hPa()) != NaN) { + cout << "Pressure:\t\t" << rnd(d) << " hPa\t\t\t\t"; + cout << rnd(m->getPressure_inHg(), -2) << " in. Hg" << endl; + } + + + // weather phenomena + vector wv = m->getWeather(); + vector::iterator weather; + for (i = 0, weather = wv.begin(); weather != wv.end(); weather++, i++) { + cout << (i ? ", " : "Weather:\t\t") << weather->c_str(); + } + if (i) + cout << endl; + + + // cloud layers + const char *coverage_string[5] = { + "clear skies", "few clouds", "scattered clouds", "broken clouds", "sky overcast" + }; + vector cv = m->getClouds(); + vector::iterator cloud; + for (lineno = 0, cloud = cv.begin(); cloud != cv.end(); cloud++, lineno++) { + cout << (lineno ? "\t\t\t" : "Sky condition:\t\t"); + + if ((i = cloud->getCoverage()) != -1) + cout << coverage_string[i]; + if ((d = cloud->getAltitude_ft()) != NaN) + cout << " at " << rnd(d, 1) << " ft"; + if ((s = cloud->getTypeLongString())) + cout << " (" << s << ')'; + if (d != NaN) + cout << "\t\t\t" << rnd(cloud->getAltitude_m(), 1) << " m"; + cout << endl; + } + + + // runways + map rm = m->getRunways(); + map::iterator runway; + for (runway = rm.begin(); runway != rm.end(); runway++) { + lineno = 0; + if (!strcmp(runway->first.c_str(), "ALL")) + cout << "All runways:\t\t"; + else + cout << "Runway " << runway->first << ":\t\t"; + SGMetarRunway rwy = runway->second; + + // assemble surface string + vector surface; + if ((s = rwy.getDeposit()) && strlen(s)) + surface.push_back(s); + if ((s = rwy.getExtentString()) && strlen(s)) + surface.push_back(s); + if ((d = rwy.getDepth()) != NaN) { + sprintf(buf, "%.0lf mm", d * 1000.0); + surface.push_back(buf); + } + if ((s = rwy.getFrictionString()) && strlen(s)) + surface.push_back(s); + if ((d = rwy.getFriction()) != NaN) { + sprintf(buf, "friction: %.2lf", d); + surface.push_back(buf); + } + + if (surface.size()) { + vector::iterator rwysurf = surface.begin(); + for (i = 0; rwysurf != surface.end(); rwysurf++, i++) { + if (i) + cout << ", "; + cout << *rwysurf; + } + lineno++; + } + + // assemble visibility string + SGMetarVisibility minvis = rwy.getMinVisibility(); + SGMetarVisibility maxvis = rwy.getMaxVisibility(); + if ((d = minvis.getVisibility_m()) != NaN) { + if (lineno++) + cout << endl << "\t\t\t"; + cout << minvis; + } + if (maxvis.getVisibility_m() != d) { + cout << endl << "\t\t\t" << maxvis << endl; + lineno++; + } + + if (rwy.getWindShear()) { + if (lineno++) + cout << endl << "\t\t\t"; + cout << "critical wind shear" << endl; + } + cout << endl; + } + cout << endl; +#undef NaN +} + + +void printArgs(SGMetar *m, double airport_elevation) +{ +#define NaN SGMetarNaN + vector args; + char buf[256]; + int i; + + // ICAO id + sprintf(buf, "--airport=%s ", m->getId()); + args.push_back(buf); + + // report time + sprintf(buf, "--start-date-gmt=%4d:%02d:%02d:%02d:%02d:00 ", + m->getYear(), m->getMonth(), m->getDay(), + m->getHour(), m->getMinute()); + args.push_back(buf); + + // cloud layers + const char *coverage_string[5] = { + "clear", "few", "scattered", "broken", "overcast" + }; + vector cv = m->getClouds(); + vector::iterator cloud; + for (i = 0, cloud = cv.begin(); i < 5; i++) { + int coverage = 0; + double altitude = -99999; + if (cloud != cv.end()) { + coverage = cloud->getCoverage(); + altitude = coverage ? cloud->getAltitude_ft() + airport_elevation : -99999; + cloud++; + } + sprintf(buf, "--prop:/environment/clouds/layer[%d]/coverage=%s ", i, coverage_string[coverage]); + args.push_back(buf); + sprintf(buf, "--prop:/environment/clouds/layer[%d]/elevation-ft=%.0lf ", i, altitude); + args.push_back(buf); + sprintf(buf, "--prop:/environment/clouds/layer[%d]/thickness-ft=500 ", i); + args.push_back(buf); + } + + // environment (temperature, dewpoint, visibility, pressure) + // metar sets don't provide aloft information; we have to + // set the same values for all boundary levels + int wind_dir = m->getWindDir(); + double visibility = m->getMinVisibility().getVisibility_m(); + double dewpoint = m->getDewpoint_C(); + double temperature = m->getTemperature_C(); + double pressure = m->getPressure_inHg(); + double wind_speed = m->getWindSpeed_kt(); + double elevation = -100; + for (i = 0; i < 3; i++, elevation += 2000.0) { + sprintf(buf, "--prop:/environment/config/boundary/entry[%d]/", i); + int pos = strlen(buf); + + sprintf(&buf[pos], "elevation-ft=%.0lf", elevation); + args.push_back(buf); + sprintf(&buf[pos], "turbulence-norm=%.0lf", 0.0); + args.push_back(buf); + + if (visibility != NaN) { + sprintf(&buf[pos], "visibility-m=%.0lf", visibility); + args.push_back(buf); + } + if (temperature != NaN) { + sprintf(&buf[pos], "temperature-degc=%.0lf", temperature); + args.push_back(buf); + } + if (dewpoint != NaN) { + sprintf(&buf[pos], "dewpoint-degc=%.0lf", dewpoint); + args.push_back(buf); + } + if (pressure != NaN) { + sprintf(&buf[pos], "pressure-sea-level-inhg=%.0lf", pressure); + args.push_back(buf); + } + if (wind_dir != NaN) { + sprintf(&buf[pos], "wind-from-heading-deg=%d", wind_dir); + args.push_back(buf); + } + if (wind_speed != NaN) { + sprintf(&buf[pos], "wind-speed-kt=%.0lf", wind_speed); + args.push_back(buf); + } + } + + // wind dir@speed + int range_from = m->getWindRangeFrom(); + int range_to = m->getWindRangeTo(); + double gust_speed = m->getGustSpeed_kt(); + if (wind_speed != NaN && wind_dir != -1) { + strcpy(buf, "--wind="); + if (range_from != -1 && range_to != -1) + sprintf(&buf[strlen(buf)], "%d:%d", range_from, range_to); + else + sprintf(&buf[strlen(buf)], "%d", wind_dir); + sprintf(&buf[strlen(buf)], "@%.0lf", wind_speed); + if (gust_speed != NaN) + sprintf(&buf[strlen(buf)], ":%.0lf", gust_speed); + args.push_back(buf); + } + + + // output everything + cout << "fgfs" << endl; + vector::iterator arg; + for (i = 0, arg = args.begin(); arg != args.end(); i++, arg++) { + cout << "\t" << *arg << endl; + } + cout << endl; +#undef NaN +} + + + + + +const char *metar_list[] = { + "LOWW", "VHHH", "ULLI", "EHTW", "EFHK", "CYXU", 0, // note the trailing zero + "CYGK", "CYOW", "CYQY", "CYTZ", "CYXU", "EBBR", "EDDB", "EDDK", "EDVE", "EFHF", + "EFHK", "EGLC", "EGLL", "EHTW", "EIDW", "ENGM", "GMMN", "KART", "KBFI", "KBOS", + "KCCR", "KCEZ", "KCOF", "KDAL", "KDEN", "KDSM", "KEDW", "KEMT", "KENW", "KHON", + "KIGM", "KJFK", "KLAX", "KMCI", "KMKE", "KMLB", "KMSY", "KNBC", "KOAK", "KORD", + "KPNE", "KSAC", "KSAN", "KSEA", "KSFO", "KSJC", "KSMF", "KSMO", "KSNS", "KSQL", + "KSUN", "LBSF", "LEMD", "LFPG", "LFPO", "LGAT", "LHBP", "LIPQ", "LIRA", "LKPR", + "LLJR", "LOWG", "LOWI", "LOWK", "LOWL", "LOWS", "LOWW", "LOWZ", "LOXA", "LOXT", + "LOXZ", "LSZH", "LYBE", "NZWP", "ORBS", "PHNL", "ULLI", "VHHH", "WMKB", "YSSY", + 0 +}; + + +int main(int argc, char *argv[]) +{ + const char **src = metar_list; + if (argc > 1) + src = (const char **)&argv[1]; + + for (int i = 0; src[i]; i++) { + const char *icao = src[i]; + + try { + SGMetar *m = new SGMetar(icao); + //SGMetar *m = new SGMetar("2004/01/11 01:20\nLOWG 110120Z AUTO VRB01KT 0050 1600N R35/0600 FG M06/M06 Q1019 88//////\n"); + + printf(G"INPUT: %s\n"N, m->getData()); + const char *unused = m->getUnusedData(); + if (*unused) + printf(R"UNUSED: %s\n"N, unused); + + printReport(m); + //printArgs(m, 0.0); + + delete m; + } catch (const sg_io_exception& e) { + fprintf(stderr, R"ERROR: %s\n\n"N, e.getFormattedMessage().c_str()); + } + } + return 0; +} + +