set(SOURCES metar.cxx precipitation.cxx)
simgear_component(environment environment "${SOURCES}" "${HEADERS}")
+
+add_executable(test_metar test_metar.cxx)
+target_link_libraries(test_metar
+ sgenvironment sgstructure sgmisc sgdebug
+ ${CMAKE_THREAD_LIBS_INIT}
+ ${ZLIB_LIBRARY}
+ ${RT_LIBRARY})
+
+add_test(metar ${EXECUTABLE_OUTPUT_PATH}/test_metar)
\ No newline at end of file
#include <time.h>
#include <cstring>
-#include <simgear/io/sg_socket.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/structure/exception.hxx>
#define NaN SGMetarNaN
+using std::string;
+using std::map;
+using std::vector;
+
/**
- * The constructor takes a Metar string, or a four-letter ICAO code. In the
- * latter case the metar string is downloaded from
- * http://weather.noaa.gov/pub/data/observations/metar/stations/.
+ * The constructor takes a Metar string
* The constructor throws sg_io_exceptions on failure. The "METAR"
* keyword has no effect (apart from incrementing the group counter
* @a grpcount) and can be left away. A keyword "SPECI" is
* likewise accepted.
*
* @param m ICAO station id or metar string
- * @param proxy proxy host (optional; default: "")
- * @param port proxy port (optional; default: "80")
- * @param auth proxy authorization information (optional; default: "")
*
* @par Examples:
* @code
* SGMetar *m = new SGMetar("METAR KSFO 061656Z 19004KT 9SM SCT100 OVC200 08/03 A3013");
* double t = m->getTemperature_F();
* delete m;
- *
- * SGMetar n("KSFO", "proxy.provider.foo", "3128", "proxy-password");
- * double d = n.getDewpoint_C();
+
* @endcode
*/
-SGMetar::SGMetar(const string& m, const string& proxy, const string& port,
- const string& auth, const time_t time) :
+SGMetar::SGMetar(const string& m) :
_grpcount(0),
_x_proxy(false),
_year(-1),
_snow(false),
_cavok(false)
{
- if (m.length() == 4 && isalnum(m[0]) && isalnum(m[1]) && isalnum(m[2]) && isalnum(m[3])) {
- for (int i = 0; i < 4; i++)
- _icao[i] = toupper(m[i]);
- _icao[4] = '\0';
- _data = loadData(_icao, proxy, port, auth, time);
- } else {
- _data = new char[m.length() + 2]; // make room for " \0"
- strcpy(_data, m.c_str());
- _url = _data;
- }
+ _data = new char[m.length() + 2]; // make room for " \0"
+ strcpy(_data, m.c_str());
+ _url = _data;
+
normalizeData();
_m = _data;
_month = now.tm_mon + 1;
}
-
-/**
- * If called with "KSFO" loads data from
- * @code
- * http://weather.noaa.gov/pub/data/observations/metar/stations/KSFO.TXT.
- * @endcode
- * Throws sg_io_exception on failure. Gives up after waiting longer than 10 seconds.
- *
- * @param id four-letter ICAO Metar station code, e.g. "KSFO".
- * @param proxy proxy host (optional; default: "")
- * @param port proxy port (optional; default: "80")
- * @param auth proxy authorization information (optional; default: "")
- * @return pointer to Metar data string, allocated by new char[].
- * @see rfc2068.txt for proxy spec ("Proxy-Authorization")
- */
-char *SGMetar::loadData(const char *id, const string& proxy, const string& port,
- const string& auth, time_t time)
-{
- const int buflen = 512;
- char buf[2 * buflen];
-
- string metar_server = "weather.noaa.gov";
- string host = proxy.empty() ? metar_server : proxy;
- string path = "/pub/data/observations/metar/stations/";
-
- path += string(id) + ".TXT";
- _url = "http://" + metar_server + path;
-
- SGSocket *sock = new SGSocket(host, port.empty() ? "80" : port, "tcp");
- sock->set_timeout(10000);
- if (!sock->open(SG_IO_OUT)) {
- delete sock;
- throw sg_io_exception("cannot connect to ", sg_location(host));
- }
-
- string get = "GET ";
- if (!proxy.empty())
- get += "http://" + metar_server;
-
- sprintf(buf, "%ld", time);
- get += path + " HTTP/1.0\015\012X-Time: " + buf + "\015\012";
- get += "Host: " + metar_server + "\015\012";
-
- if (!auth.empty())
- get += "Proxy-Authorization: " + auth + "\015\012";
-
- get += "\015\012";
- sock->writestring(get.c_str());
-
- int i;
-
- // skip HTTP header
- while ((i = sock->readline(buf, buflen))) {
- if (i <= 2 && isspace(buf[0]) && (!buf[1] || isspace(buf[1])))
- break;
- if (!strncmp(buf, "X-MetarProxy: ", 13))
- _x_proxy = true;
- }
- if (i) {
- i = sock->readline(buf, buflen);
- if (i)
- sock->readline(&buf[i], buflen);
- }
-
- sock->close();
- delete sock;
-
- char *b = buf;
- scanBoundary(&b);
- if (*b == '<')
- throw sg_io_exception("no metar data available from ",
- sg_location(_url));
-
- char *metar = new char[strlen(b) + 2]; // make room for " \0"
- strcpy(metar, b);
- return metar;
-}
-
-
/**
* Replace any number of subsequent spaces by just one space, and add
* a trailing space. This makes scanning for things like "ALL RWY" easier.
#include <simgear/constants.h>
-using std::vector;
-using std::map;
-using std::string;
-
-const double SGMetarNaN = -1E20;
-#define NaN SGMetarNaN
-
struct Token {
const char *id;
const char *text;
};
+const double SGMetarNaN = -1E20;
class SGMetar;
friend class SGMetar;
public:
SGMetarVisibility() :
- _distance(NaN),
+ _distance(SGMetarNaN),
_direction(-1),
_modifier(EQUALS),
_tendency(NONE) {}
void set(double dist, int dir = -1, int mod = -1, int tend = -1);
inline double getVisibility_m() const { return _distance; }
- inline double getVisibility_ft() const { return _distance == NaN ? NaN : _distance * SG_METER_TO_FEET; }
- inline double getVisibility_sm() const { return _distance == NaN ? NaN : _distance * SG_METER_TO_SM; }
+ inline double getVisibility_ft() const { return _distance == SGMetarNaN ? SGMetarNaN : _distance * SG_METER_TO_FEET; }
+ inline double getVisibility_sm() const { return _distance == SGMetarNaN ? SGMetarNaN : _distance * SG_METER_TO_SM; }
inline int getDirection() const { return _direction; }
inline int getModifier() const { return _modifier; }
inline int getTendency() const { return _tendency; }
_deposit_string(0),
_extent(-1),
_extent_string(0),
- _depth(NaN),
- _friction(NaN),
+ _depth(SGMetarNaN),
+ _friction(SGMetarNaN),
_friction_string(0),
_comment(0),
_wind_shear(false) {}
static const char * COVERAGE_BROKEN_STRING;
static const char * COVERAGE_OVERCAST_STRING;
- SGMetarCloud() : _coverage(COVERAGE_NIL), _altitude(NaN), _type(0), _type_long(0) {}
+ SGMetarCloud() : _coverage(COVERAGE_NIL), _altitude(SGMetarNaN), _type(0), _type_long(0) {}
void set(double alt, Coverage cov = COVERAGE_NIL );
inline Coverage getCoverage() const { return _coverage; }
static Coverage getCoverage( const std::string & coverage );
inline double getAltitude_m() const { return _altitude; }
- inline double getAltitude_ft() const { return _altitude == NaN ? NaN : _altitude * SG_METER_TO_FEET; }
+ inline double getAltitude_ft() const { return _altitude == SGMetarNaN ? SGMetarNaN : _altitude * SG_METER_TO_FEET; }
inline const char *getTypeString() const { return _type; }
inline const char *getTypeLongString() const { return _type_long; }
class SGMetar {
public:
- SGMetar(const string& m, const string& proxy = "", const string& port = "",
- const string &auth = "", const time_t time = 0);
+ SGMetar(const std::string& m);
~SGMetar();
enum ReportType {
Weather() { intensity = NIL; vincinity = false; }
Intensity intensity;
bool vincinity;
- vector<string> descriptions;
- vector<string> phenomena;
+ std::vector<std::string> descriptions;
+ std::vector<std::string> phenomena;
};
inline const char *getData() const { return _data; }
inline int getWindDir() const { return _wind_dir; }
inline double getWindSpeed_mps() const { return _wind_speed; }
- inline double getWindSpeed_kmh() const { return _wind_speed == NaN ? NaN : _wind_speed * SG_MPS_TO_KMH; }
- inline double getWindSpeed_kt() const { return _wind_speed == NaN ? NaN : _wind_speed * SG_MPS_TO_KT; }
- inline double getWindSpeed_mph() const { return _wind_speed == NaN ? NaN : _wind_speed * SG_MPS_TO_MPH; }
+ inline double getWindSpeed_kmh() const { return _wind_speed == SGMetarNaN ? SGMetarNaN : _wind_speed * SG_MPS_TO_KMH; }
+ inline double getWindSpeed_kt() const { return _wind_speed == SGMetarNaN ? SGMetarNaN : _wind_speed * SG_MPS_TO_KT; }
+ inline double getWindSpeed_mph() const { return _wind_speed == SGMetarNaN ? SGMetarNaN : _wind_speed * SG_MPS_TO_MPH; }
inline double getGustSpeed_mps() const { return _gust_speed; }
- inline double getGustSpeed_kmh() const { return _gust_speed == NaN ? NaN : _gust_speed * SG_MPS_TO_KMH; }
- inline double getGustSpeed_kt() const { return _gust_speed == NaN ? NaN : _gust_speed * SG_MPS_TO_KT; }
- inline double getGustSpeed_mph() const { return _gust_speed == NaN ? NaN : _gust_speed * SG_MPS_TO_MPH; }
+ inline double getGustSpeed_kmh() const { return _gust_speed == SGMetarNaN ? SGMetarNaN : _gust_speed * SG_MPS_TO_KMH; }
+ inline double getGustSpeed_kt() const { return _gust_speed == SGMetarNaN ? SGMetarNaN : _gust_speed * SG_MPS_TO_KT; }
+ inline double getGustSpeed_mph() const { return _gust_speed == SGMetarNaN ? SGMetarNaN : _gust_speed * SG_MPS_TO_MPH; }
inline int getWindRangeFrom() const { return _wind_range_from; }
inline int getWindRangeTo() const { return _wind_range_to; }
inline const SGMetarVisibility *getDirVisibility() const { return _dir_visibility; }
inline double getTemperature_C() const { return _temp; }
- inline double getTemperature_F() const { return _temp == NaN ? NaN : 1.8 * _temp + 32; }
+ inline double getTemperature_F() const { return _temp == SGMetarNaN ? SGMetarNaN : 1.8 * _temp + 32; }
inline double getDewpoint_C() const { return _dewp; }
- inline double getDewpoint_F() const { return _dewp == NaN ? NaN : 1.8 * _dewp + 32; }
- inline double getPressure_hPa() const { return _pressure == NaN ? NaN : _pressure / 100; }
- inline double getPressure_inHg() const { return _pressure == NaN ? NaN : _pressure * SG_PA_TO_INHG; }
+ inline double getDewpoint_F() const { return _dewp == SGMetarNaN ? SGMetarNaN : 1.8 * _dewp + 32; }
+ inline double getPressure_hPa() const { return _pressure == SGMetarNaN ? SGMetarNaN : _pressure / 100; }
+ inline double getPressure_inHg() const { return _pressure == SGMetarNaN ? SGMetarNaN : _pressure * SG_PA_TO_INHG; }
inline int getRain() const { return _rain; }
inline int getHail() const { return _hail; }
double getRelHumidity() const;
- inline const vector<SGMetarCloud>& getClouds() const { return _clouds; }
- inline const map<string, SGMetarRunway>& getRunways() const { return _runways; }
- inline const vector<string>& getWeather() const { return _weather; }
- inline const vector<struct Weather> getWeather2() const { return _weather2; }
+ inline const std::vector<SGMetarCloud>& getClouds() const { return _clouds; }
+ inline const std::map<std::string, SGMetarRunway>& getRunways() const { return _runways; }
+ inline const std::vector<std::string>& getWeather() const { return _weather; }
+ inline const std::vector<struct Weather> getWeather2() const { return _weather2; }
protected:
- string _url;
+ std::string _url;
int _grpcount;
bool _x_proxy;
char *_data;
int _hail;
int _snow;
bool _cavok;
- vector<struct Weather> _weather2;
+ std::vector<struct Weather> _weather2;
SGMetarVisibility _min_visibility;
SGMetarVisibility _max_visibility;
SGMetarVisibility _vert_visibility;
SGMetarVisibility _dir_visibility[8];
- vector<SGMetarCloud> _clouds;
- map<string, SGMetarRunway> _runways;
- vector<string> _weather;
+ std::vector<SGMetarCloud> _clouds;
+ std::map<std::string, SGMetarRunway> _runways;
+ std::vector<std::string> _weather;
bool scanPreambleDate();
bool scanPreambleTime();
int scanNumber(char **str, int *num, int min, int max = 0);
bool scanBoundary(char **str);
const struct Token *scanToken(char **str, const struct Token *list);
- char *loadData(const char *id, const string& proxy, const string& port,
- const string &auth, time_t time);
void normalizeData();
};
-#undef NaN
#endif // _METAR_HXX
--- /dev/null
+
+#ifdef HAVE_CONFIG_H
+# include <simgear_config.h>
+#endif
+
+#include <simgear/compiler.h>
+
+#include <iostream>
+#include <cstdlib>
+#include <cstdio>
+
+#ifdef _MSC_VER
+# define random rand
+#endif
+
+#include <simgear/misc/sg_dir.hxx>
+#include <simgear/structure/exception.hxx>
+
+#include "metar.hxx"
+
+using std::cout;
+using std::cerr;
+using std::endl;
+using std::string;
+
+#define COMPARE(a, b) \
+ if ((a) != (b)) { \
+ cerr << "failed:" << #a << " != " << #b << endl; \
+ cerr << "\tgot:" << a << endl; \
+ exit(1); \
+ }
+
+#define VERIFY(a) \
+ if (!(a)) { \
+ cerr << "failed:" << #a << endl; \
+ exit(1); \
+ }
+
+
+void test_basic()
+{
+ SGMetar m1("2011/10/20 11:25 EHAM 201125Z 27012KT 240V300 9999 VCSH FEW025CB SCT048 10/05 Q1025 TEMPO VRB03KT");
+ COMPARE(m1.getYear(), 2011);
+ COMPARE(m1.getMonth(), 10);
+ COMPARE(m1.getDay(), 20);
+ COMPARE(m1.getHour(), 11);
+ COMPARE(m1.getMinute(), 25);
+ COMPARE(m1.getReportType(), -1); // should default to NIL?
+
+ COMPARE(m1.getWindDir(), 270);
+ COMPARE(m1.getWindSpeed_kt(), 12);
+
+ COMPARE(m1.getTemperature_C(), 10);
+ COMPARE(m1.getDewpoint_C(), 5);
+ COMPARE(m1.getPressure_hPa(), 1025);
+}
+
+int main(int argc, char* argv[])
+{
+ try {
+ test_basic();
+ } catch (sg_exception& e) {
+ cerr << "got exception:" << e.getMessage() << endl;
+ return -1;
+ }
+
+ return 0;
+}