FlightGear next
metar_main.cxx
Go to the documentation of this file.
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#include <cstdio>
30
31#include <simgear/environment/metar.hxx>
32#include <simgear/structure/exception.hxx>
33
34#include <simgear/io/HTTPClient.hxx>
35#include <simgear/io/HTTPMemoryRequest.hxx>
36#include <simgear/io/raw_socket.hxx>
37#include <simgear/timing/timestamp.hxx>
38#include <simgear/misc/strutils.hxx>
39
40
41using namespace std;
42using namespace simgear;
43
44// text color
45#if defined(__linux__) || defined(__sun) || defined(__CYGWIN__) || defined( __FreeBSD__ ) || defined( __OpenBSD__ ) || defined ( sgi )
46# define R "\033[31;1m" // red
47# define G "\033[32;1m" // green
48# define Y "\033[33;1m" // yellow
49# define B "\033[34;1m" // blue
50# define M "\033[35;1m" // magenta
51# define C "\033[36;1m" // cyan
52# define W "\033[37;1m" // white
53# define N "\033[m" // normal
54#else
55# define R ""
56# define G ""
57# define Y ""
58# define B ""
59# define M ""
60# define C ""
61# define W ""
62# define N ""
63#endif
64
65
66
67const char *azimuthName(double d)
68{
69 const char *dir[] = {
70 "N", "NNE", "NE", "ENE",
71 "E", "ESE", "SE", "SSE",
72 "S", "SSW", "SW", "WSW",
73 "W", "WNW", "NW", "NNW"
74 };
75 d += 11.25;
76 while (d < 0)
77 d += 360;
78 while (d >= 360)
79 d -= 360;
80 return dir[int(d / 22.5)];
81}
82
83
84// round double to 10^g
85double rnd(double r, int g = 0)
86{
87 double f = pow(10.0, g);
88 return f * floor(r / f + 0.5);
89}
90
91
92ostream& operator<<(ostream& s, const SGMetarVisibility& v)
93{
94 ostringstream buf;
95 int m = v.getModifier();
96 const char *mod;
97 if (m == SGMetarVisibility::GREATER_THAN)
98 mod = ">=";
99 else if (m == SGMetarVisibility::LESS_THAN)
100 mod = "<";
101 else
102 mod = "";
103 buf << mod;
104
105 double dist = rnd(v.getVisibility_m(), 1);
106 if (dist < 1000.0)
107 buf << rnd(dist, 1) << " m";
108 else
109 buf << rnd(dist / 1000.0, -1) << " km";
110
111 const char *dir = "";
112 int i;
113 if ((i = v.getDirection()) != -1) {
114 dir = azimuthName(i);
115 buf << " " << dir;
116 }
117 buf << "\t\t\t\t\t" << mod << rnd(v.getVisibility_sm(), -1) << " US-miles " << dir;
118 return s << buf.str();
119}
120
121
122void printReport(SGMetar *m)
123{
124 cout << m->getDescription(0);
125}
126
127
128void printArgs(SGMetar *m, double airport_elevation)
129{
130#define NaN SGMetarNaN
131 vector<string> args;
132 char buf[256];
133 int i;
134
135 // ICAO id
136 snprintf(buf, 256, "--airport=%s ", m->getId());
137 args.push_back(buf);
138
139 // report time
140 snprintf(buf, 256, "--start-date-gmt=%4d:%02d:%02d:%02d:%02d:00 ",
141 m->getYear(), m->getMonth(), m->getDay(),
142 m->getHour(), m->getMinute());
143 args.push_back(buf);
144
145 // cloud layers
146 const char *coverage_string[5] = {
147 "clear", "few", "scattered", "broken", "overcast"
148 };
149 vector<SGMetarCloud> cv = m->getClouds();
150 vector<SGMetarCloud>::iterator cloud;
151 for (i = 0, cloud = cv.begin(); i < 5; i++) {
152 int coverage = 0;
153 double altitude = -99999;
154 if (cloud != cv.end()) {
155 coverage = cloud->getCoverage();
156 altitude = coverage ? cloud->getAltitude_ft() + airport_elevation : -99999;
157 cloud++;
158 }
159 snprintf(buf, 256, "--prop:/environment/clouds/layer[%d]/coverage=%s ", i, coverage_string[coverage]);
160 args.push_back(buf);
161 snprintf(buf, 256, "--prop:/environment/clouds/layer[%d]/elevation-ft=%.0lf ", i, altitude);
162 args.push_back(buf);
163 snprintf(buf, 256, "--prop:/environment/clouds/layer[%d]/thickness-ft=500 ", i);
164 args.push_back(buf);
165 }
166
167 // environment (temperature, dewpoint, visibility, pressure)
168 // metar sets don't provide aloft information; we have to
169 // set the same values for all boundary levels
170 int wind_dir = m->getWindDir();
171 double visibility = m->getMinVisibility().getVisibility_m();
172 double dewpoint = m->getDewpoint_C();
173 double temperature = m->getTemperature_C();
174 double pressure = m->getPressure_inHg();
175 double wind_speed = m->getWindSpeed_kt();
176 double elevation = -100;
177 for (i = 0; i < 3; i++, elevation += 2000.0) {
178 snprintf(buf, 256, "--prop:/environment/config/boundary/entry[%d]/", i);
179 int pos = strlen(buf);
180 const int rem = 256 - pos;
181
182 snprintf(&buf[pos], rem, "elevation-ft=%.0lf", elevation);
183 args.push_back(buf);
184 snprintf(&buf[pos], rem, "turbulence-norm=%.0lf", 0.0);
185 args.push_back(buf);
186
187 if (visibility != NaN) {
188 snprintf(&buf[pos], rem, "visibility-m=%.0lf", visibility);
189 args.push_back(buf);
190 }
191 if (temperature != NaN) {
192 snprintf(&buf[pos], rem, "temperature-degc=%.0lf", temperature);
193 args.push_back(buf);
194 }
195 if (dewpoint != NaN) {
196 snprintf(&buf[pos], rem, "dewpoint-degc=%.0lf", dewpoint);
197 args.push_back(buf);
198 }
199 if (pressure != NaN) {
200 snprintf(&buf[pos], rem, "pressure-sea-level-inhg=%.0lf", pressure);
201 args.push_back(buf);
202 }
203 if (wind_dir != NaN) {
204 snprintf(&buf[pos], rem, "wind-from-heading-deg=%d", wind_dir);
205 args.push_back(buf);
206 }
207 if (wind_speed != NaN) {
208 snprintf(&buf[pos], rem, "wind-speed-kt=%.0lf", wind_speed);
209 args.push_back(buf);
210 }
211 }
212
213 // wind dir@speed
214 int range_from = m->getWindRangeFrom();
215 int range_to = m->getWindRangeTo();
216 double gust_speed = m->getGustSpeed_kt();
217 if (wind_speed != NaN && wind_dir != -1) {
218 std::ostringstream os;
219 os << "--wind=";
220
221 if (range_from != -1 && range_to != -1) {
222 os << range_from << ":" << range_to;
223 } else {
224 os << wind_dir;
225 }
226
227 os << std::setw(3) << "@" << wind_speed;
228 if (gust_speed != NaN)
229 os << ":" << gust_speed;
230 args.push_back(os.str());
231 }
232
233
234 // output everything
235 //cout << "fgfs" << endl;
236 vector<string>::iterator arg;
237 for (i = 0, arg = args.begin(); arg != args.end(); i++, arg++) {
238 cout << "\t" << *arg << endl;
239 }
240 cout << endl;
241#undef NaN
242}
243
244
245void getproxy(string& host, string& port)
246{
247 host = "";
248 port = "80";
249
250 const char *p = getenv("http_proxy");
251 if (!p)
252 return;
253 while (isspace(*p))
254 p++;
255 if (!strncmp(p, "http://", 7))
256 p += 7;
257 if (!*p)
258 return;
259
260 char s[256], *t;
261 strncpy(s, p, 255);
262 s[255] = '\0';
263
264 for (t = s + strlen(s); t > s; t--)
265 if (!isspace(t[-1]) && t[-1] != '/')
266 break;
267 *t = '\0';
268
269 t = strchr(s, ':');
270 if (t) {
271 *t++ = '\0';
272 port = t;
273 }
274 host = s;
275}
276
277
278void usage()
279{
280 printf(
281 "Usage: metar [-v] [-e elevation] [-r|-c] <list of ICAO airport ids or METAR strings>\n"
282 " metar -h\n"
283 "\n"
284 " -h|--help show this help\n"
285 " -v|--verbose verbose output\n"
286 " -r|--report print report (default)\n"
287 " -c|--command-line print command line\n"
288 " -e E|--elevation E set airport elevation to E meters\n"
289 " (added to cloud bases in command line mode)\n"
290 " -s|--string <METAR string>\n"
291 "Environment:\n"
292 " http_proxy set proxy in the form \"http://host:port/\"\n"
293 "\n"
294 "Examples:\n"
295 " $ metar ksfo koak\n"
296 " $ metar -c ksfo -r ksfo\n"
297 " $ metar -s \"LOWL 161500Z 19004KT 160V240 9999 FEW035 SCT300 29/23 Q1006 NOSIG\"\n"
298 " $ fgfs `metar -e 183 -c loww`\n"
299 " $ http_proxy=http://localhost:3128/ metar ksfo\n"
300 "\n"
301 );
302}
303
304int main(int argc, char *argv[])
305{
306 bool report = true;
307 bool verbose = false;
308 double elevation = 0.0;
309
310 if (argc <= 1) {
311 usage();
312 return 0;
313 }
314
315 string proxy_host, proxy_port;
316 getproxy(proxy_host, proxy_port);
317
318 Socket::initSockets();
319
320 HTTP::Client http;
321 http.setProxy(proxy_host, atoi(proxy_port.c_str()));
322
323 for (int i = 1; i < argc; i++) {
324 if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help"))
325 usage();
326 else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))
327 verbose = true;
328 else if (!strcmp(argv[i], "-r") || !strcmp(argv[i], "--report"))
329 report = true;
330 else if (!strcmp(argv[i], "-c") || !strcmp(argv[i], "--command-line"))
331 report = false;
332 else if (!strcmp(argv[i], "-e") || !strcmp(argv[i], "--elevation")) {
333 if (++i >= argc) {
334 cerr << "-e option used without elevation" << endl;
335 return 1;
336 }
337 elevation = strtod(argv[i], 0);
338 }
339 else if (!strcmp(argv[i], "-s") || !strcmp(argv[i], "--string")) {
340 if (++i >= argc) {
341 cerr << "-s option used with out string\n";
342 return 1;
343 }
344 const char* string = argv[i];
345 SGMetar metar(string);
346 printReport(&metar);
347 }
348 else {
349 static bool shown = false;
350 if (verbose && !shown) {
351 cerr << "Proxy host: '" << proxy_host << "'" << endl;
352 cerr << "Proxy port: '" << proxy_port << "'" << endl << endl;
353 shown = true;
354 }
355
356 try
357 {
358 static const std::string NOAA_BASE_URL =
359 "https://tgftp.nws.noaa.gov/data/observations/metar/stations/";
360 HTTP::MemoryRequest* mr = new HTTP::MemoryRequest
361 (
362 NOAA_BASE_URL + strutils::uppercase(argv[i]) + ".TXT"
363 );
364 HTTP::Request_ptr own(mr);
365 http.makeRequest(mr);
366
367 // spin until the request completes, fails or times out
368 SGTimeStamp start(SGTimeStamp::now());
369 while (start.elapsedMSec() < 8000) {
370 http.update();
371 if( mr->isComplete() )
372 break;
373 SGTimeStamp::sleepForMSec(1);
374 }
375
376 if( !mr->isComplete() )
377 throw sg_io_exception("metar download timed out");
378 if( mr->responseCode() != 200 )
379 {
380 std::cerr << "metar download failed: "
381 << mr->url()
382 << " (" << mr->responseCode()
383 << " " << mr->responseReason() << ")"
384 << std::endl;
385 throw sg_io_exception("metar download failed");
386 }
387
388 SGMetar *m = new SGMetar(mr->responseBody());
389
390 //SGMetar *m = new SGMetar("2004/01/11 01:20\nLOWG 110120Z AUTO VRB01KT 0050 1600N R35/0600 FG M06/M06 Q1019 88//////\n");
391
392 if (verbose) {
393 cerr << G "INPUT: " << m->getDataString() << "" N << endl;
394
395 const auto unused = m->getUnparsedData();
396 if (!unused.empty())
397 cerr << R "UNUSED: " << unused << "" N << endl;
398 }
399
400 if (report)
401 printReport(m);
402 else
403 printArgs(m, elevation);
404
405 delete m;
406 } catch (const sg_io_exception& e) {
407 cerr << R "ERROR: " << e.getFormattedMessage().c_str() << "" N << endl << endl;
408 }
409 }
410 }
411 return 0;
412}
413
414
double altitude
Definition ADA.cxx:46
#define p(x)
#define i(x)
int main()
#define N
void usage()
const char * azimuthName(double d)
ostream & operator<<(ostream &s, const SGMetarVisibility &v)
void printArgs(SGMetar *m, double airport_elevation)
#define R
void getproxy(string &host, string &port)
#define NaN
#define G
double rnd(double r, int g=0)
void printReport(SGMetar *m)
static int atoi(const string &str)
Definition options.cxx:113
void report(Airplane *a)