]> git.mxchange.org Git - flightgear.git/blob - src/Main/metar_main.cxx
616edc85b962adc2fced73b7d3fd56b319c2ec8f
[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 std::ostringstream;
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 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);
59
60
61 const char *azimuthName(double d)
62 {
63         const char *dir[] = {
64                 "N", "NNE", "NE", "ENE",
65                 "E", "ESE", "SE", "SSE",
66                 "S", "SSW", "SW", "WSW",
67                 "W", "WNW", "NW", "NNW"
68         };
69         d += 11.25;
70         while (d < 0)
71                 d += 360;
72         while (d >= 360)
73                 d -= 360;
74         return dir[int(d / 22.5)];
75 }
76
77
78 // round double to 10^g
79 double rnd(double r, int g = 0)
80 {
81         double f = pow(10.0, g);
82         return f * rint(r / f);
83 }
84
85
86 ostream& operator<<(ostream& s, SGMetarVisibility& v)
87 {
88         ostringstream buf;
89         int m = v.getModifier();
90         const char *mod;
91         if (m == SGMetarVisibility::GREATER_THAN)
92                 mod = ">=";
93         else if (m == SGMetarVisibility::LESS_THAN)
94                 mod = "<";
95         else
96                 mod = "";
97         buf << mod;
98
99         double dist = rnd(v.getVisibility_m(), 1);
100         if (dist < 1000.0)
101                 buf << rnd(dist, 1) << " m";
102         else
103                 buf << rnd(dist / 1000.0, -1) << " km";
104
105         const char *dir = "";
106         int i;
107         if ((i = v.getDirection()) != -1) {
108                 dir = azimuthName(i);
109                 buf << " " << dir;
110         }
111         buf << "\t\t\t\t\t" << mod << rnd(v.getVisibility_sm(), -1) << " US-miles " << dir;
112         return s << buf.str();
113 }
114
115
116 void printReport(SGMetar *m)
117 {
118 #define NaN SGMetarNaN
119         const char *s;
120         char buf[256];
121         double d;
122         int i, lineno;
123
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)";
130         else
131                 s = "";
132
133         cout << "METAR Report" << s << endl;
134         cout << "============" << endl;
135         cout << "Airport-Id:\t\t" << m->getId() << endl;
136
137
138         // date/time
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;
146
147
148         // visibility
149         SGMetarVisibility minvis = m->getMinVisibility();
150         SGMetarVisibility maxvis = m->getMaxVisibility();
151         double min = minvis.getVisibility_m();
152         double max = maxvis.getVisibility_m();
153         if (min != NaN) {
154                 if (max != NaN) {
155                         cout << "min. Visibility:\t" << minvis << endl;
156                         cout << "max. Visibility:\t" << maxvis << endl;
157                 } else
158                         cout << "Visibility:\t\t" << minvis << endl;
159         }
160
161
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;
167
168
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;
175
176
177         // wind
178         d = m->getWindSpeed_kmh();
179         cout << "Wind:\t\t\t";
180         if (d < .1)
181                 cout << "none" << endl;
182         else {
183                 if ((i = m->getWindDir()) == -1)
184                         cout << "from variable directions";
185                 else
186                         cout << "from the " << azimuthName(i) << " (" << i << "°)";
187                 cout << " at " << rnd(d, -1) << " km/h";
188
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";
192                 cout << endl;
193
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";
199                         cout << endl;
200                 }
201
202                 int from = m->getWindRangeFrom();
203                 int to = m->getWindRangeTo();
204                 if (from != to) {
205                         cout << "\t\t\tvariable from " << azimuthName(from);
206                         cout << " to " << azimuthName(to);
207                         cout << " (" << from << "°--" << to << "°)" << endl;
208                 }
209         }
210
211
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;
216
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;
221                 }
222         }
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;
226         }
227
228
229         // weather phenomena
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();
234         }
235         if (i)
236                 cout << endl;
237
238
239         // cloud layers
240         const char *coverage_string[5] = {
241                 "clear skies", "few clouds", "scattered clouds", "broken clouds", "sky overcast"
242         };
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");
247
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 << ')';
254                 if (d != NaN)
255                         cout << "\t\t\t" << rnd(cloud->getAltitude_m(), 1) << " m";
256                 cout << endl;
257         }
258
259
260         // runways
261         map<string, SGMetarRunway> rm = m->getRunways();
262         map<string, SGMetarRunway>::iterator runway;
263         for (runway = rm.begin(); runway != rm.end(); runway++) {
264                 lineno = 0;
265                 if (!strcmp(runway->first.c_str(), "ALL"))
266                         cout << "All runways:\t\t";
267                 else
268                         cout << "Runway " << runway->first << ":\t\t";
269                 SGMetarRunway rwy = runway->second;
270
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);
280                 }
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);
286                 }
287
288                 if (surface.size()) {
289                         vector<string>::iterator rwysurf = surface.begin();
290                         for (i = 0; rwysurf != surface.end(); rwysurf++, i++) {
291                                 if (i)
292                                         cout << ", ";
293                                 cout << *rwysurf;
294                         }
295                         lineno++;
296                 }
297
298                 // assemble visibility string
299                 SGMetarVisibility minvis = rwy.getMinVisibility();
300                 SGMetarVisibility maxvis = rwy.getMaxVisibility();
301                 if ((d = minvis.getVisibility_m()) != NaN) {
302                         if (lineno++)
303                                 cout << endl << "\t\t\t";
304                         cout << minvis;
305                 }
306                 if (maxvis.getVisibility_m() != d) {
307                         cout << endl << "\t\t\t" << maxvis << endl;
308                         lineno++;
309                 }
310
311                 if (rwy.getWindShear()) {
312                         if (lineno++)
313                                 cout << endl << "\t\t\t";
314                         cout << "critical wind shear" << endl;
315                 }
316                 cout << endl;
317         }
318         cout << endl;
319 #undef NaN
320 }
321
322
323 void printArgs(SGMetar *m, double airport_elevation)
324 {
325 #define NaN SGMetarNaN
326         vector<string> args;
327         char buf[256];
328         int i;
329
330         // ICAO id
331         sprintf(buf, "--airport=%s ", m->getId());
332         args.push_back(buf);
333
334         // report time
335         sprintf(buf, "--start-date-gmt=%4d:%02d:%02d:%02d:%02d:00 ",
336                         m->getYear(), m->getMonth(), m->getDay(),
337                         m->getHour(), m->getMinute());
338         args.push_back(buf);
339
340         // cloud layers
341         const char *coverage_string[5] = {
342                 "clear", "few", "scattered", "broken", "overcast"
343         };
344         vector<SGMetarCloud> cv = m->getClouds();
345         vector<SGMetarCloud>::iterator cloud;
346         for (i = 0, cloud = cv.begin(); i < 5; i++) {
347                 int coverage = 0;
348                 double altitude = -99999;
349                 if (cloud != cv.end()) {
350                         coverage = cloud->getCoverage();
351                         altitude = coverage ? cloud->getAltitude_ft() + airport_elevation : -99999;
352                         cloud++;
353                 }
354                 sprintf(buf, "--prop:/environment/clouds/layer[%d]/coverage=%s ", i, coverage_string[coverage]);
355                 args.push_back(buf);
356                 sprintf(buf, "--prop:/environment/clouds/layer[%d]/elevation-ft=%.0lf ", i, altitude);
357                 args.push_back(buf);
358                 sprintf(buf, "--prop:/environment/clouds/layer[%d]/thickness-ft=500 ", i);
359                 args.push_back(buf);
360         }
361
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);
375
376                 sprintf(&buf[pos], "elevation-ft=%.0lf", elevation);
377                 args.push_back(buf);
378                 sprintf(&buf[pos], "turbulence-norm=%.0lf", 0.0);
379                 args.push_back(buf);
380
381                 if (visibility != NaN) {
382                         sprintf(&buf[pos], "visibility-m=%.0lf", visibility);
383                         args.push_back(buf);
384                 }
385                 if (temperature != NaN) {
386                         sprintf(&buf[pos], "temperature-degc=%.0lf", temperature);
387                         args.push_back(buf);
388                 }
389                 if (dewpoint != NaN) {
390                         sprintf(&buf[pos], "dewpoint-degc=%.0lf", dewpoint);
391                         args.push_back(buf);
392                 }
393                 if (pressure != NaN) {
394                         sprintf(&buf[pos], "pressure-sea-level-inhg=%.0lf", pressure);
395                         args.push_back(buf);
396                 }
397                 if (wind_dir != NaN) {
398                         sprintf(&buf[pos], "wind-from-heading-deg=%d", wind_dir);
399                         args.push_back(buf);
400                 }
401                 if (wind_speed != NaN) {
402                         sprintf(&buf[pos], "wind-speed-kt=%.0lf", wind_speed);
403                         args.push_back(buf);
404                 }
405         }
406
407         // wind dir@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);
415                 else
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);
420                 args.push_back(buf);
421         }
422         
423
424         // output everything
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;
429         }
430         cout << endl;
431 #undef NaN
432 }
433
434
435 int main(int argc, char *argv[])
436 {
437         if (argc <= 1) {
438                 fprintf(stderr,
439                         "Usage:   metar <list of ICAO airport ids>\n"
440                         "Example: metar ksfo koak\n"
441                 );
442                 return 0;
443         }
444
445         for (int i = 1; i < argc; i++) {
446                 try {
447                         SGMetar *m = new SGMetar(argv[i]);
448                         //SGMetar *m = new SGMetar("2004/01/11 01:20\nLOWG 110120Z AUTO VRB01KT 0050 1600N R35/0600 FG M06/M06 Q1019 88//////\n");
449
450                         printf(G"INPUT: %s\n"N, m->getData());
451                         const char *unused = m->getUnusedData();
452                         if (*unused)
453                                 printf(R"UNUSED: %s\n"N, unused);
454
455                         printReport(m);
456                         //printArgs(m, 0.0);
457
458                         delete m;
459                 } catch (const sg_io_exception& e) {
460                         fprintf(stderr, R"ERROR: %s\n\n"N, e.getFormattedMessage().c_str());
461                 }
462         }
463         return 0;
464 }
465
466