]> git.mxchange.org Git - flightgear.git/blob - src/Main/metar_main.cxx
Merge commit 'refs/merge-requests/1552' of git@gitorious.org:fg/flightgear into next
[flightgear.git] / src / Main / metar_main.cxx
1 // metar interface class demo
2 //
3 // Written by Melchior FRANZ, started December 2003.
4 //
5 // Copyright (C) 2003  Melchior FRANZ - mfranz@aon.at
6 //
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.
11 //
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.
16 //
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, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23 #include <iomanip>
24 #include <sstream>
25 #include <iostream>
26 #include <string.h>
27 #include <time.h>
28 #include <cstdlib>
29
30 #include <simgear/debug/logstream.hxx>
31 #include <simgear/environment/metar.hxx>
32 #include <simgear/structure/exception.hxx>
33
34 using namespace std;
35
36 // text color
37 #if defined(__linux__) || defined(__sun) || defined(__CYGWIN__) \
38     || defined( __FreeBSD__ ) || defined ( sgi )
39 #       define R "\033[31;1m"           // red
40 #       define G "\033[32;1m"           // green
41 #       define Y "\033[33;1m"           // yellow
42 #       define B "\033[34;1m"           // blue
43 #       define M "\033[35;1m"           // magenta
44 #       define C "\033[36;1m"           // cyan
45 #       define W "\033[37;1m"           // white
46 #       define N "\033[m"               // normal
47 #else
48 #       define R ""
49 #       define G ""
50 #       define Y ""
51 #       define B ""
52 #       define M ""
53 #       define C ""
54 #       define W ""
55 #       define N ""
56 #endif
57
58
59
60 const char *azimuthName(double d)
61 {
62         const char *dir[] = {
63                 "N", "NNE", "NE", "ENE",
64                 "E", "ESE", "SE", "SSE",
65                 "S", "SSW", "SW", "WSW",
66                 "W", "WNW", "NW", "NNW"
67         };
68         d += 11.25;
69         while (d < 0)
70                 d += 360;
71         while (d >= 360)
72                 d -= 360;
73         return dir[int(d / 22.5)];
74 }
75
76
77 // round double to 10^g
78 double rnd(double r, int g = 0)
79 {
80         double f = pow(10.0, g);
81         return f * floor(r / f + 0.5);
82 }
83
84
85 ostream& operator<<(ostream& s, const SGMetarVisibility& v)
86 {
87         ostringstream buf;
88         int m = v.getModifier();
89         const char *mod;
90         if (m == SGMetarVisibility::GREATER_THAN)
91                 mod = ">=";
92         else if (m == SGMetarVisibility::LESS_THAN)
93                 mod = "<";
94         else
95                 mod = "";
96         buf << mod;
97
98         double dist = rnd(v.getVisibility_m(), 1);
99         if (dist < 1000.0)
100                 buf << rnd(dist, 1) << " m";
101         else
102                 buf << rnd(dist / 1000.0, -1) << " km";
103
104         const char *dir = "";
105         int i;
106         if ((i = v.getDirection()) != -1) {
107                 dir = azimuthName(i);
108                 buf << " " << dir;
109         }
110         buf << "\t\t\t\t\t" << mod << rnd(v.getVisibility_sm(), -1) << " US-miles " << dir;
111         return s << buf.str();
112 }
113
114
115 void printReport(SGMetar *m)
116 {
117 #define NaN SGMetarNaN
118         const char *s;
119         char buf[256];
120         double d;
121         int i, lineno;
122
123         if ((i = m->getReportType()) == SGMetar::AUTO)
124                 s = "\t\t(automatically generated)";
125         else if (i == SGMetar::COR)
126                 s = "\t\t(manually corrected)";
127         else if (i == SGMetar::RTD)
128                 s = "\t\t(routine delayed)";
129         else
130                 s = "";
131
132         cout << "METAR Report" << s << endl;
133         cout << "============" << endl;
134         cout << "Airport-Id:\t\t" << m->getId() << endl;
135
136
137         // date/time
138         int year = m->getYear();
139         int month = m->getMonth();
140         cout << "Report time:\t\t" << year << '/' << month << '/' << m->getDay();
141         cout << ' ' << m->getHour() << ':';
142         cout << setw(2) << setfill('0') << m->getMinute() << " UTC" << endl;
143
144
145         // visibility
146         SGMetarVisibility minvis = m->getMinVisibility();
147         SGMetarVisibility maxvis = m->getMaxVisibility();
148         double min = minvis.getVisibility_m();
149         double max = maxvis.getVisibility_m();
150         if (min != NaN) {
151                 if (max != NaN) {
152                         cout << "min. Visibility:\t" << minvis << endl;
153                         cout << "max. Visibility:\t" << maxvis << endl;
154                 } else
155                         cout << "Visibility:\t\t" << minvis << endl;
156         }
157
158
159         // directed visibility
160         const SGMetarVisibility *dirvis = m->getDirVisibility();
161         for (i = 0; i < 8; i++, dirvis++)
162                 if (dirvis->getVisibility_m() != NaN)
163                         cout << "\t\t\t" << *dirvis << endl;
164
165
166         // vertical visibility
167         SGMetarVisibility vertvis = m->getVertVisibility();
168         if ((d = vertvis.getVisibility_ft()) != NaN)
169                 cout << "Vert. visibility:\t" << vertvis << endl;
170         else if (vertvis.getModifier() == SGMetarVisibility::NOGO)
171                 cout << "Vert. visibility:\timpossible to determine" << endl;
172
173
174         // wind
175         d = m->getWindSpeed_kmh();
176         cout << "Wind:\t\t\t";
177         if (d < .1)
178                 cout << "none" << endl;
179         else {
180                 if ((i = m->getWindDir()) == -1)
181                         cout << "from variable directions";
182                 else
183                         cout << "from the " << azimuthName(i) << " (" << i << "°)";
184                 cout << " at " << rnd(d, -1) << " km/h";
185
186                 cout << "\t\t" << rnd(m->getWindSpeed_kt(), -1) << " kt";
187                 cout << " = " << rnd(m->getWindSpeed_mph(), -1) << " mph";
188                 cout << " = " << rnd(m->getWindSpeed_mps(), -1) << " m/s";
189                 cout << endl;
190
191                 if ((d = m->getGustSpeed_kmh()) != NaN) {
192                         cout << "\t\t\twith gusts at " << rnd(d, -1) << " km/h";
193                         cout << "\t\t\t" << rnd(m->getGustSpeed_kt(), -1) << " kt";
194                         cout << " = " << rnd(m->getGustSpeed_mph(), -1) << " mph";
195                         cout << " = " << rnd(m->getGustSpeed_mps(), -1) << " m/s";
196                         cout << endl;
197                 }
198
199                 int from = m->getWindRangeFrom();
200                 int to = m->getWindRangeTo();
201                 if (from != to) {
202                         cout << "\t\t\tvariable from " << azimuthName(from);
203                         cout << " to " << azimuthName(to);
204                         cout << " (" << from << "°--" << to << "°)" << endl;
205                 }
206         }
207
208
209         // temperature/humidity/air pressure
210         if ((d = m->getTemperature_C()) != NaN) {
211                 cout << "Temperature:\t\t" << d << "°C\t\t\t\t\t";
212                 cout << rnd(m->getTemperature_F(), -1) << "°F" << endl;
213
214                 if ((d = m->getDewpoint_C()) != NaN) {
215                         cout << "Dewpoint:\t\t" << d << "°C\t\t\t\t\t";
216                         cout << rnd(m->getDewpoint_F(), -1) << "°F"  << endl;
217                         cout << "Rel. Humidity:\t\t" << rnd(m->getRelHumidity()) << "%" << endl;
218                 }
219         }
220         if ((d = m->getPressure_hPa()) != NaN) {
221                 cout << "Pressure:\t\t" << rnd(d) << " hPa\t\t\t\t";
222                 cout << rnd(m->getPressure_inHg(), -2) << " in. Hg" << endl;
223         }
224
225
226         // weather phenomena
227         vector<string> wv = m->getWeather();
228         vector<string>::iterator weather;
229         for (i = 0, weather = wv.begin(); weather != wv.end(); weather++, i++) {
230                 cout << (i ? ", " : "Weather:\t\t") << weather->c_str();
231         }
232         if (i)
233                 cout << endl;
234
235
236         // cloud layers
237         const char *coverage_string[5] = {
238                 "clear skies", "few clouds", "scattered clouds", "broken clouds", "sky overcast"
239         };
240         vector<SGMetarCloud> cv = m->getClouds();
241         vector<SGMetarCloud>::iterator cloud;
242         for (lineno = 0, cloud = cv.begin(); cloud != cv.end(); cloud++, lineno++) {
243                 cout << (lineno ? "\t\t\t" : "Sky condition:\t\t");
244
245                 if ((i = cloud->getCoverage()) != -1)
246                         cout << coverage_string[i];
247                 if ((d = cloud->getAltitude_ft()) != NaN)
248                         cout << " at " << rnd(d, 1) << " ft";
249                 if ((s = cloud->getTypeLongString()))
250                         cout << " (" << s << ')';
251                 if (d != NaN)
252                         cout << "\t\t\t" << rnd(cloud->getAltitude_m(), 1) << " m";
253                 cout << endl;
254         }
255
256
257         // runways
258         map<string, SGMetarRunway> rm = m->getRunways();
259         map<string, SGMetarRunway>::iterator runway;
260         for (runway = rm.begin(); runway != rm.end(); runway++) {
261                 lineno = 0;
262                 if (!strcmp(runway->first.c_str(), "ALL"))
263                         cout << "All runways:\t\t";
264                 else
265                         cout << "Runway " << runway->first << ":\t\t";
266                 SGMetarRunway rwy = runway->second;
267
268                 // assemble surface string
269                 vector<string> surface;
270                 if ((s = rwy.getDepositString()) && strlen(s))
271                         surface.push_back(s);
272                 if ((s = rwy.getExtentString()) && strlen(s))
273                         surface.push_back(s);
274                 if ((d = rwy.getDepth()) != NaN) {
275                         sprintf(buf, "%.1lf mm", d * 1000.0);
276                         surface.push_back(buf);
277                 }
278                 if ((s = rwy.getFrictionString()) && strlen(s))
279                         surface.push_back(s);
280                 if ((d = rwy.getFriction()) != NaN) {
281                         sprintf(buf, "friction: %.2lf", d);
282                         surface.push_back(buf);
283                 }
284
285                 if (surface.size()) {
286                         vector<string>::iterator rwysurf = surface.begin();
287                         for (i = 0; rwysurf != surface.end(); rwysurf++, i++) {
288                                 if (i)
289                                         cout << ", ";
290                                 cout << *rwysurf;
291                         }
292                         lineno++;
293                 }
294
295                 // assemble visibility string
296                 SGMetarVisibility minvis = rwy.getMinVisibility();
297                 SGMetarVisibility maxvis = rwy.getMaxVisibility();
298                 if ((d = minvis.getVisibility_m()) != NaN) {
299                         if (lineno++)
300                                 cout << endl << "\t\t\t";
301                         cout << minvis;
302                 }
303                 if (maxvis.getVisibility_m() != d) {
304                         cout << endl << "\t\t\t" << maxvis << endl;
305                         lineno++;
306                 }
307
308                 if (rwy.getWindShear()) {
309                         if (lineno++)
310                                 cout << endl << "\t\t\t";
311                         cout << "critical wind shear" << endl;
312                 }
313                 cout << endl;
314         }
315         cout << endl;
316 #undef NaN
317 }
318
319
320 void printArgs(SGMetar *m, double airport_elevation)
321 {
322 #define NaN SGMetarNaN
323         vector<string> args;
324         char buf[256];
325         int i;
326
327         // ICAO id
328         sprintf(buf, "--airport=%s ", m->getId());
329         args.push_back(buf);
330
331         // report time
332         sprintf(buf, "--start-date-gmt=%4d:%02d:%02d:%02d:%02d:00 ",
333                         m->getYear(), m->getMonth(), m->getDay(),
334                         m->getHour(), m->getMinute());
335         args.push_back(buf);
336
337         // cloud layers
338         const char *coverage_string[5] = {
339                 "clear", "few", "scattered", "broken", "overcast"
340         };
341         vector<SGMetarCloud> cv = m->getClouds();
342         vector<SGMetarCloud>::iterator cloud;
343         for (i = 0, cloud = cv.begin(); i < 5; i++) {
344                 int coverage = 0;
345                 double altitude = -99999;
346                 if (cloud != cv.end()) {
347                         coverage = cloud->getCoverage();
348                         altitude = coverage ? cloud->getAltitude_ft() + airport_elevation : -99999;
349                         cloud++;
350                 }
351                 sprintf(buf, "--prop:/environment/clouds/layer[%d]/coverage=%s ", i, coverage_string[coverage]);
352                 args.push_back(buf);
353                 sprintf(buf, "--prop:/environment/clouds/layer[%d]/elevation-ft=%.0lf ", i, altitude);
354                 args.push_back(buf);
355                 sprintf(buf, "--prop:/environment/clouds/layer[%d]/thickness-ft=500 ", i);
356                 args.push_back(buf);
357         }
358
359         // environment (temperature, dewpoint, visibility, pressure)
360         // metar sets don't provide aloft information; we have to
361         // set the same values for all boundary levels
362         int wind_dir = m->getWindDir();
363         double visibility = m->getMinVisibility().getVisibility_m();
364         double dewpoint = m->getDewpoint_C();
365         double temperature = m->getTemperature_C();
366         double pressure = m->getPressure_inHg();
367         double wind_speed = m->getWindSpeed_kt();
368         double elevation = -100;
369         for (i = 0; i < 3; i++, elevation += 2000.0) {
370                 sprintf(buf, "--prop:/environment/config/boundary/entry[%d]/", i);
371                 int pos = strlen(buf);
372
373                 sprintf(&buf[pos], "elevation-ft=%.0lf", elevation);
374                 args.push_back(buf);
375                 sprintf(&buf[pos], "turbulence-norm=%.0lf", 0.0);
376                 args.push_back(buf);
377
378                 if (visibility != NaN) {
379                         sprintf(&buf[pos], "visibility-m=%.0lf", visibility);
380                         args.push_back(buf);
381                 }
382                 if (temperature != NaN) {
383                         sprintf(&buf[pos], "temperature-degc=%.0lf", temperature);
384                         args.push_back(buf);
385                 }
386                 if (dewpoint != NaN) {
387                         sprintf(&buf[pos], "dewpoint-degc=%.0lf", dewpoint);
388                         args.push_back(buf);
389                 }
390                 if (pressure != NaN) {
391                         sprintf(&buf[pos], "pressure-sea-level-inhg=%.0lf", pressure);
392                         args.push_back(buf);
393                 }
394                 if (wind_dir != NaN) {
395                         sprintf(&buf[pos], "wind-from-heading-deg=%d", wind_dir);
396                         args.push_back(buf);
397                 }
398                 if (wind_speed != NaN) {
399                         sprintf(&buf[pos], "wind-speed-kt=%.0lf", wind_speed);
400                         args.push_back(buf);
401                 }
402         }
403
404         // wind dir@speed
405         int range_from = m->getWindRangeFrom();
406         int range_to = m->getWindRangeTo();
407         double gust_speed = m->getGustSpeed_kt();
408         if (wind_speed != NaN && wind_dir != -1) {
409                 strcpy(buf, "--wind=");
410                 if (range_from != -1 && range_to != -1)
411                         sprintf(&buf[strlen(buf)], "%d:%d", range_from, range_to);
412                 else
413                         sprintf(&buf[strlen(buf)], "%d", wind_dir);
414                 sprintf(&buf[strlen(buf)], "@%.0lf", wind_speed);
415                 if (gust_speed != NaN)
416                         sprintf(&buf[strlen(buf)], ":%.0lf", gust_speed);
417                 args.push_back(buf);
418         }
419         
420
421         // output everything
422         //cout << "fgfs" << endl;
423         vector<string>::iterator arg;
424         for (i = 0, arg = args.begin(); arg != args.end(); i++, arg++) {
425                 cout << "\t" << *arg << endl;
426         }
427         cout << endl;
428 #undef NaN
429 }
430
431
432 void getproxy(string& host, string& port)
433 {
434         host = "";
435         port = "80";
436
437         const char *p = getenv("http_proxy");
438         if (!p)
439                 return;
440         while (isspace(*p))
441                 p++;
442         if (!strncmp(p, "http://", 7))
443                 p += 7;
444         if (!*p)
445                 return;
446
447         char s[256], *t;
448         strncpy(s, p, 255);
449         s[255] = '\0';
450
451         for (t = s + strlen(s); t > s; t--)
452                 if (!isspace(t[-1]) && t[-1] != '/')
453                         break;
454         *t = '\0';
455
456         t = strchr(s, ':');
457         if (t) {
458                 *t++ = '\0';
459                 port = t;
460         }
461         host = s;
462 }
463
464
465 void usage()
466 {
467         printf(
468                 "Usage: metar [-v] [-e elevation] [-r|-c] <list of ICAO airport ids or METAR strings>\n"
469                 "       metar -h\n"
470                 "\n"
471                 "       -h|--help            show this help\n"
472                 "       -v|--verbose         verbose output\n"
473                 "       -r|--report          print report (default)\n"
474                 "       -c|--command-line    print command line\n"
475                 "       -e E|--elevation E   set airport elevation to E meters\n"
476                 "                            (added to cloud bases in command line mode)\n"
477                 "Environment:\n"
478                 "       http_proxy           set proxy in the form \"http://host:port/\"\n"
479                 "\n"
480                 "Examples:\n"
481                 "       $ metar ksfo koak\n"
482                 "       $ metar -c ksfo -r ksfo\n"
483                 "       $ metar \"LOWL 161500Z 19004KT 160V240 9999 FEW035 SCT300 29/23 Q1006 NOSIG\"\n"
484                 "       $ fgfs  `metar -e 183 -c loww`\n"
485                 "       $ http_proxy=http://localhost:3128/ metar ksfo\n"
486                 "\n"
487         );
488 }
489
490 int main(int argc, char *argv[])
491 {
492         bool report = true;
493         bool verbose = false;
494         double elevation = 0.0;
495
496         if (argc <= 1) {
497                 usage();
498                 return 0;
499         }
500
501         string proxy_host, proxy_port;
502         getproxy(proxy_host, proxy_port);
503
504         for (int i = 1; i < argc; i++) {
505                 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help"))
506                         usage();
507                 else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))
508                         verbose = true;
509                 else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--report"))
510                         report = true;
511                 else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--command-line"))
512                         report = false;
513                 else if (!strcmp(argv[i], "-e") || !strcmp(argv[i], "--elevation")) {
514                         if (++i >= argc) {
515                                 cerr << "-e option used without elevation" << endl;
516                                 return 1;
517                         }
518                         elevation = strtod(argv[i], 0);
519                 } else {
520                         static bool shown = false;
521                         if (verbose && !shown) {
522                                 cerr << "Proxy host: '" << proxy_host << "'" << endl;
523                                 cerr << "Proxy port: '" << proxy_port << "'" << endl << endl;
524                                 shown = true;
525                         }
526
527                         try {
528                                 SGMetar *m = new SGMetar(argv[i], proxy_host, proxy_port, "", time(0));
529                                 //SGMetar *m = new SGMetar("2004/01/11 01:20\nLOWG 110120Z AUTO VRB01KT 0050 1600N R35/0600 FG M06/M06 Q1019 88//////\n");
530
531                                 if (verbose) {
532                                         cerr << G"INPUT: " << m->getData() << ""N << endl;
533
534                                         const char *unused = m->getUnusedData();
535                                         if (*unused)
536                                                 cerr << R"UNUSED: " << unused << ""N << endl;
537                                 }
538
539                                 if (report)
540                                         printReport(m);
541                                 else
542                                         printArgs(m, elevation);
543
544                                 delete m;
545                         } catch (const sg_io_exception& e) {
546                                 cerr << R"ERROR: " << e.getFormattedMessage().c_str() << ""N << endl << endl;
547                         }
548                 }
549         }
550         return 0;
551 }
552
553