]> git.mxchange.org Git - flightgear.git/blob - src/Main/metar_main.cxx
Added a <solve-weight> subtag of the approach/cruise parameters that can
[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
436
437
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",
448         0
449 };
450
451
452 int main(int argc, char *argv[])
453 {
454         const char **src = metar_list;
455         if (argc > 1)
456                 src = (const char **)&argv[1];
457
458         for (int i = 0; src[i]; i++) {
459                 const char *icao = src[i];
460
461                 try {
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");
464
465                         printf(G"INPUT: %s\n"N, m->getData());
466                         const char *unused = m->getUnusedData();
467                         if (*unused)
468                                 printf(R"UNUSED: %s\n"N, unused);
469
470                         printReport(m);
471                         //printArgs(m, 0.0);
472
473                         delete m;
474                 } catch (const sg_io_exception& e) {
475                         fprintf(stderr, R"ERROR: %s\n\n"N, e.getFormattedMessage().c_str());
476                 }
477         }
478         return 0;
479 }
480
481