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