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
+++ /dev/null
-// 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 <iomanip>
-#include <sstream>
-
-#include <simgear/debug/logstream.hxx>
-#include <simgear/structure/exception.hxx>
-
-#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<string> wv = m->getWeather();
- vector<string>::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<FGMetarCloud> cv = m->getClouds();
- vector<FGMetarCloud>::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<string, FGMetarRunway> rm = m->getRunways();
- map<string, FGMetarRunway>::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<string> 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<string>::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<string> 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<FGMetarCloud> cv = m->getClouds();
- vector<FGMetarCloud>::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<string>::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;
-}
-
-
+++ /dev/null
-// 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 <string>
-
-#include <simgear/io/sg_socket.hxx>
-#include <simgear/debug/logstream.hxx>
-#include <simgear/structure/exception.hxx>
-
-#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
+++ /dev/null
-// 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 <vector>
-#include <map>
-#include <string>
-
-#include <simgear/constants.h>
-
-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<FGMetarCloud>& getClouds() { return _clouds; }
- inline map<string, FGMetarRunway>& getRunways() { return _runways; }
- inline vector<string>& 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<FGMetarCloud> _clouds;
- map<string, FGMetarRunway> _runways;
- vector<string> _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
EXTRA_DIST = 3dfx.sh runfgfs.in runfgfs.bat.in
-bin_PROGRAMS = fgfs
+bin_PROGRAMS = fgfs metar
noinst_SCRIPTS = runfgfs.bat runfgfs
$(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
--- /dev/null
+// 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 <iomanip>
+#include <sstream>
+
+#include <simgear/debug/logstream.hxx>
+#include <simgear/structure/exception.hxx>
+#include <simgear/environment/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(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<string> wv = m->getWeather();
+ vector<string>::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<SGMetarCloud> cv = m->getClouds();
+ vector<SGMetarCloud>::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<string, SGMetarRunway> rm = m->getRunways();
+ map<string, SGMetarRunway>::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<string> 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<string>::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<string> 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<SGMetarCloud> cv = m->getClouds();
+ vector<SGMetarCloud>::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<string>::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;
+}
+
+