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;
435 int main(int argc, char *argv[])
439 "Usage: metar <list of ICAO airport ids>\n"
440 "Example: metar ksfo koak\n"
445 for (int i = 1; i < argc; i++) {
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");
450 printf(G"INPUT: %s\n"N, m->getData());
451 const char *unused = m->getUnusedData();
453 printf(R"UNUSED: %s\n"N, unused);
459 } catch (const sg_io_exception& e) {
460 fprintf(stderr, R"ERROR: %s\n\n"N, e.getFormattedMessage().c_str());