1 // metar interface class demo
3 // Written by Melchior FRANZ, started December 2003.
5 // Copyright (C) 2003 Melchior FRANZ - mfranz@aon.at
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
26 #include <simgear/debug/logstream.hxx>
27 #include <simgear/structure/exception.hxx>
28 #include <simgear/environment/metar.hxx>
30 using std::ostringstream;
33 #if defined(__linux__) || defined( __sun__ ) ||defined(__CYGWIN__) || defined( __FreeBSD__ )
34 # define R "\033[31;1m" // red
35 # define G "\033[32;1m" // green
36 # define Y "\033[33;1m" // yellow
37 # define B "\033[34;1m" // blue
38 # define M "\033[35;1m" // magenta
39 # define C "\033[36;1m" // cyan
40 # define W "\033[37;1m" // white
41 # define N "\033[m" // normal
54 const char *azimuthName(double d);
55 double rnd(double number, int digits);
56 void printReport(SGMetar *m);
57 void printVisibility(SGMetarVisibility *v);
58 void printArgs(SGMetar *m, double airport_elevation);
61 const char *azimuthName(double d)
64 "N", "NNE", "NE", "ENE",
65 "E", "ESE", "SE", "SSE",
66 "S", "SSW", "SW", "WSW",
67 "W", "WNW", "NW", "NNW"
74 return dir[int(d / 22.5)];
78 // round double to 10^g
79 double rnd(double r, int g = 0)
81 double f = pow(10.0, g);
82 return f * rint(r / f);
86 ostream& operator<<(ostream& s, SGMetarVisibility& v)
89 int m = v.getModifier();
91 if (m == SGMetarVisibility::GREATER_THAN)
93 else if (m == SGMetarVisibility::LESS_THAN)
99 double dist = rnd(v.getVisibility_m(), 1);
101 buf << rnd(dist, 1) << " m";
103 buf << rnd(dist / 1000.0, -1) << " km";
105 const char *dir = "";
107 if ((i = v.getDirection()) != -1) {
108 dir = azimuthName(i);
111 buf << "\t\t\t\t\t" << mod << rnd(v.getVisibility_sm(), -1) << " US-miles " << dir;
112 return s << buf.str();
116 void printReport(SGMetar *m)
118 #define NaN SGMetarNaN
124 if ((i = m->getReportType()) == SGMetar::AUTO)
125 s = "\t\t(automatically generated)";
126 else if (i == SGMetar::COR)
127 s = "\t\t(manually corrected)";
128 else if (i == SGMetar::RTD)
129 s = "\t\t(routine delayed)";
133 cout << "METAR Report" << s << endl;
134 cout << "============" << endl;
135 cout << "Airport-Id:\t\t" << m->getId() << endl;
139 int year = m->getYear();
140 int month = m->getMonth();
141 cout << "Report time:\t\t";
142 if (year != -1 && month != -1)
143 cout << year << '/' << month << '/' << m->getDay();
144 cout << ' ' << m->getHour() << ':';
145 cout << std::setw(2) << std::setfill('0') << m->getMinute() << " UTC" << endl;
149 SGMetarVisibility minvis = m->getMinVisibility();
150 SGMetarVisibility maxvis = m->getMaxVisibility();
151 double min = minvis.getVisibility_m();
152 double max = maxvis.getVisibility_m();
155 cout << "min. Visibility:\t" << minvis << endl;
156 cout << "max. Visibility:\t" << maxvis << endl;
158 cout << "Visibility:\t\t" << minvis << endl;
162 // directed visibility
163 SGMetarVisibility *dirvis = m->getDirVisibility();
164 for (i = 0; i < 8; i++, dirvis++)
165 if (dirvis->getVisibility_m() != NaN)
166 cout << "\t\t\t" << *dirvis << endl;
169 // vertical visibility
170 SGMetarVisibility vertvis = m->getVertVisibility();
171 if ((d = vertvis.getVisibility_ft()) != NaN)
172 cout << "Vert. visibility:\t" << vertvis << endl;
173 else if (vertvis.getModifier() == SGMetarVisibility::NOGO)
174 cout << "Vert. visibility:\timpossible to determine" << endl;
178 d = m->getWindSpeed_kmh();
179 cout << "Wind:\t\t\t";
181 cout << "none" << endl;
183 if ((i = m->getWindDir()) == -1)
184 cout << "from variable directions";
186 cout << "from the " << azimuthName(i) << " (" << i << "°)";
187 cout << " at " << rnd(d, -1) << " km/h";
189 cout << "\t\t" << rnd(m->getWindSpeed_kt(), -1) << " kt";
190 cout << " = " << rnd(m->getWindSpeed_mph(), -1) << " mph";
191 cout << " = " << rnd(m->getWindSpeed_mps(), -1) << " m/s";
194 if ((d = m->getGustSpeed_kmh()) != NaN) {
195 cout << "\t\t\twith gusts at " << rnd(d, -1) << " km/h";
196 cout << "\t\t\t" << rnd(m->getGustSpeed_kt(), -1) << " kt";
197 cout << " = " << rnd(m->getGustSpeed_mph(), -1) << " mph";
198 cout << " = " << rnd(m->getGustSpeed_mps(), -1) << " m/s";
202 int from = m->getWindRangeFrom();
203 int to = m->getWindRangeTo();
205 cout << "\t\t\tvariable from " << azimuthName(from);
206 cout << " to " << azimuthName(to);
207 cout << " (" << from << "°--" << to << "°)" << endl;
212 // temperature/humidity/air pressure
213 if ((d = m->getTemperature_C()) != NaN) {
214 cout << "Temperature:\t\t" << d << "°C\t\t\t\t\t";
215 cout << rnd(m->getTemperature_F(), -1) << "°F" << endl;
217 if ((d = m->getDewpoint_C()) != NaN) {
218 cout << "Dewpoint:\t\t" << d << "°C\t\t\t\t\t";
219 cout << rnd(m->getDewpoint_F(), -1) << "°F" << endl;
220 cout << "Rel. Humidity:\t\t" << rnd(m->getRelHumidity()) << "%" << endl;
223 if ((d = m->getPressure_hPa()) != NaN) {
224 cout << "Pressure:\t\t" << rnd(d) << " hPa\t\t\t\t";
225 cout << rnd(m->getPressure_inHg(), -2) << " in. Hg" << endl;
230 vector<string> wv = m->getWeather();
231 vector<string>::iterator weather;
232 for (i = 0, weather = wv.begin(); weather != wv.end(); weather++, i++) {
233 cout << (i ? ", " : "Weather:\t\t") << weather->c_str();
240 const char *coverage_string[5] = {
241 "clear skies", "few clouds", "scattered clouds", "broken clouds", "sky overcast"
243 vector<SGMetarCloud> cv = m->getClouds();
244 vector<SGMetarCloud>::iterator cloud;
245 for (lineno = 0, cloud = cv.begin(); cloud != cv.end(); cloud++, lineno++) {
246 cout << (lineno ? "\t\t\t" : "Sky condition:\t\t");
248 if ((i = cloud->getCoverage()) != -1)
249 cout << coverage_string[i];
250 if ((d = cloud->getAltitude_ft()) != NaN)
251 cout << " at " << rnd(d, 1) << " ft";
252 if ((s = cloud->getTypeLongString()))
253 cout << " (" << s << ')';
255 cout << "\t\t\t" << rnd(cloud->getAltitude_m(), 1) << " m";
261 map<string, SGMetarRunway> rm = m->getRunways();
262 map<string, SGMetarRunway>::iterator runway;
263 for (runway = rm.begin(); runway != rm.end(); runway++) {
265 if (!strcmp(runway->first.c_str(), "ALL"))
266 cout << "All runways:\t\t";
268 cout << "Runway " << runway->first << ":\t\t";
269 SGMetarRunway rwy = runway->second;
271 // assemble surface string
272 vector<string> surface;
273 if ((s = rwy.getDeposit()) && strlen(s))
274 surface.push_back(s);
275 if ((s = rwy.getExtentString()) && strlen(s))
276 surface.push_back(s);
277 if ((d = rwy.getDepth()) != NaN) {
278 sprintf(buf, "%.0lf mm", d * 1000.0);
279 surface.push_back(buf);
281 if ((s = rwy.getFrictionString()) && strlen(s))
282 surface.push_back(s);
283 if ((d = rwy.getFriction()) != NaN) {
284 sprintf(buf, "friction: %.2lf", d);
285 surface.push_back(buf);
288 if (surface.size()) {
289 vector<string>::iterator rwysurf = surface.begin();
290 for (i = 0; rwysurf != surface.end(); rwysurf++, i++) {
298 // assemble visibility string
299 SGMetarVisibility minvis = rwy.getMinVisibility();
300 SGMetarVisibility maxvis = rwy.getMaxVisibility();
301 if ((d = minvis.getVisibility_m()) != NaN) {
303 cout << endl << "\t\t\t";
306 if (maxvis.getVisibility_m() != d) {
307 cout << endl << "\t\t\t" << maxvis << endl;
311 if (rwy.getWindShear()) {
313 cout << endl << "\t\t\t";
314 cout << "critical wind shear" << endl;
323 void printArgs(SGMetar *m, double airport_elevation)
325 #define NaN SGMetarNaN
331 sprintf(buf, "--airport=%s ", m->getId());
335 sprintf(buf, "--start-date-gmt=%4d:%02d:%02d:%02d:%02d:00 ",
336 m->getYear(), m->getMonth(), m->getDay(),
337 m->getHour(), m->getMinute());
341 const char *coverage_string[5] = {
342 "clear", "few", "scattered", "broken", "overcast"
344 vector<SGMetarCloud> cv = m->getClouds();
345 vector<SGMetarCloud>::iterator cloud;
346 for (i = 0, cloud = cv.begin(); i < 5; i++) {
348 double altitude = -99999;
349 if (cloud != cv.end()) {
350 coverage = cloud->getCoverage();
351 altitude = coverage ? cloud->getAltitude_ft() + airport_elevation : -99999;
354 sprintf(buf, "--prop:/environment/clouds/layer[%d]/coverage=%s ", i, coverage_string[coverage]);
356 sprintf(buf, "--prop:/environment/clouds/layer[%d]/elevation-ft=%.0lf ", i, altitude);
358 sprintf(buf, "--prop:/environment/clouds/layer[%d]/thickness-ft=500 ", i);
362 // environment (temperature, dewpoint, visibility, pressure)
363 // metar sets don't provide aloft information; we have to
364 // set the same values for all boundary levels
365 int wind_dir = m->getWindDir();
366 double visibility = m->getMinVisibility().getVisibility_m();
367 double dewpoint = m->getDewpoint_C();
368 double temperature = m->getTemperature_C();
369 double pressure = m->getPressure_inHg();
370 double wind_speed = m->getWindSpeed_kt();
371 double elevation = -100;
372 for (i = 0; i < 3; i++, elevation += 2000.0) {
373 sprintf(buf, "--prop:/environment/config/boundary/entry[%d]/", i);
374 int pos = strlen(buf);
376 sprintf(&buf[pos], "elevation-ft=%.0lf", elevation);
378 sprintf(&buf[pos], "turbulence-norm=%.0lf", 0.0);
381 if (visibility != NaN) {
382 sprintf(&buf[pos], "visibility-m=%.0lf", visibility);
385 if (temperature != NaN) {
386 sprintf(&buf[pos], "temperature-degc=%.0lf", temperature);
389 if (dewpoint != NaN) {
390 sprintf(&buf[pos], "dewpoint-degc=%.0lf", dewpoint);
393 if (pressure != NaN) {
394 sprintf(&buf[pos], "pressure-sea-level-inhg=%.0lf", pressure);
397 if (wind_dir != NaN) {
398 sprintf(&buf[pos], "wind-from-heading-deg=%d", wind_dir);
401 if (wind_speed != NaN) {
402 sprintf(&buf[pos], "wind-speed-kt=%.0lf", wind_speed);
408 int range_from = m->getWindRangeFrom();
409 int range_to = m->getWindRangeTo();
410 double gust_speed = m->getGustSpeed_kt();
411 if (wind_speed != NaN && wind_dir != -1) {
412 strcpy(buf, "--wind=");
413 if (range_from != -1 && range_to != -1)
414 sprintf(&buf[strlen(buf)], "%d:%d", range_from, range_to);
416 sprintf(&buf[strlen(buf)], "%d", wind_dir);
417 sprintf(&buf[strlen(buf)], "@%.0lf", wind_speed);
418 if (gust_speed != NaN)
419 sprintf(&buf[strlen(buf)], ":%.0lf", gust_speed);
425 cout << "fgfs" << endl;
426 vector<string>::iterator arg;
427 for (i = 0, arg = args.begin(); arg != args.end(); i++, arg++) {
428 cout << "\t" << *arg << endl;
438 const char *metar_list[] = {
439 "LOWW", "VHHH", "ULLI", "EHTW", "EFHK", "CYXU", 0, // note the trailing zero
440 "CYGK", "CYOW", "CYQY", "CYTZ", "CYXU", "EBBR", "EDDB", "EDDK", "EDVE", "EFHF",
441 "EFHK", "EGLC", "EGLL", "EHTW", "EIDW", "ENGM", "GMMN", "KART", "KBFI", "KBOS",
442 "KCCR", "KCEZ", "KCOF", "KDAL", "KDEN", "KDSM", "KEDW", "KEMT", "KENW", "KHON",
443 "KIGM", "KJFK", "KLAX", "KMCI", "KMKE", "KMLB", "KMSY", "KNBC", "KOAK", "KORD",
444 "KPNE", "KSAC", "KSAN", "KSEA", "KSFO", "KSJC", "KSMF", "KSMO", "KSNS", "KSQL",
445 "KSUN", "LBSF", "LEMD", "LFPG", "LFPO", "LGAT", "LHBP", "LIPQ", "LIRA", "LKPR",
446 "LLJR", "LOWG", "LOWI", "LOWK", "LOWL", "LOWS", "LOWW", "LOWZ", "LOXA", "LOXT",
447 "LOXZ", "LSZH", "LYBE", "NZWP", "ORBS", "PHNL", "ULLI", "VHHH", "WMKB", "YSSY",
452 int main(int argc, char *argv[])
454 const char **src = metar_list;
456 src = (const char **)&argv[1];
458 for (int i = 0; src[i]; i++) {
459 const char *icao = src[i];
462 SGMetar *m = new SGMetar(icao);
463 //SGMetar *m = new SGMetar("2004/01/11 01:20\nLOWG 110120Z AUTO VRB01KT 0050 1600N R35/0600 FG M06/M06 Q1019 88//////\n");
465 printf(G"INPUT: %s\n"N, m->getData());
466 const char *unused = m->getUnusedData();
468 printf(R"UNUSED: %s\n"N, unused);
474 } catch (const sg_io_exception& e) {
475 fprintf(stderr, R"ERROR: %s\n\n"N, e.getFormattedMessage().c_str());