From 63a8209a839fcc142ab86ef16f323bb54c37521a Mon Sep 17 00:00:00 2001 From: James Turner Date: Fri, 21 Oct 2011 09:35:37 +0100 Subject: [PATCH] Remove using std:: from the metar header, remove HTTP support, add very basic unit-test harness. --- simgear/environment/CMakeLists.txt | 9 +++ simgear/environment/metar.cxx | 112 +++-------------------------- simgear/environment/metar.hxx | 70 ++++++++---------- simgear/environment/test_metar.cxx | 68 ++++++++++++++++++ 4 files changed, 118 insertions(+), 141 deletions(-) create mode 100644 simgear/environment/test_metar.cxx diff --git a/simgear/environment/CMakeLists.txt b/simgear/environment/CMakeLists.txt index 5a4dce1e..c59f98a7 100644 --- a/simgear/environment/CMakeLists.txt +++ b/simgear/environment/CMakeLists.txt @@ -5,3 +5,12 @@ set(HEADERS metar.hxx precipitation.hxx) 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 diff --git a/simgear/environment/metar.cxx b/simgear/environment/metar.cxx index c15c4a72..75109b75 100644 --- a/simgear/environment/metar.cxx +++ b/simgear/environment/metar.cxx @@ -32,7 +32,6 @@ #include #include -#include #include #include @@ -40,32 +39,28 @@ #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), @@ -87,16 +82,10 @@ SGMetar::SGMetar(const string& m, const string& proxy, const string& port, _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; @@ -169,85 +158,6 @@ void SGMetar::useCurrentDate() _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. diff --git a/simgear/environment/metar.hxx b/simgear/environment/metar.hxx index e501a0c4..26e8523e 100644 --- a/simgear/environment/metar.hxx +++ b/simgear/environment/metar.hxx @@ -29,18 +29,12 @@ #include -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; @@ -48,7 +42,7 @@ class SGMetarVisibility { friend class SGMetar; public: SGMetarVisibility() : - _distance(NaN), + _distance(SGMetarNaN), _direction(-1), _modifier(EQUALS), _tendency(NONE) {} @@ -70,8 +64,8 @@ public: 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; } @@ -93,8 +87,8 @@ public: _deposit_string(0), _extent(-1), _extent_string(0), - _depth(NaN), - _friction(NaN), + _depth(SGMetarNaN), + _friction(SGMetarNaN), _friction_string(0), _comment(0), _wind_shear(false) {} @@ -146,14 +140,14 @@ public: 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; } @@ -167,8 +161,7 @@ protected: 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 { @@ -189,8 +182,8 @@ public: Weather() { intensity = NIL; vincinity = false; } Intensity intensity; bool vincinity; - vector descriptions; - vector phenomena; + std::vector descriptions; + std::vector phenomena; }; inline const char *getData() const { return _data; } @@ -206,14 +199,14 @@ public: 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; } @@ -224,11 +217,11 @@ public: 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; } @@ -237,13 +230,13 @@ public: double getRelHumidity() const; - inline const vector& getClouds() const { return _clouds; } - inline const map& getRunways() const { return _runways; } - inline const vector& getWeather() const { return _weather; } - inline const vector getWeather2() const { return _weather2; } + inline const std::vector& getClouds() const { return _clouds; } + inline const std::map& getRunways() const { return _runways; } + inline const std::vector& getWeather() const { return _weather; } + inline const std::vector getWeather2() const { return _weather2; } protected: - string _url; + std::string _url; int _grpcount; bool _x_proxy; char *_data; @@ -267,15 +260,15 @@ protected: int _hail; int _snow; bool _cavok; - vector _weather2; + std::vector _weather2; SGMetarVisibility _min_visibility; SGMetarVisibility _max_visibility; SGMetarVisibility _vert_visibility; SGMetarVisibility _dir_visibility[8]; - vector _clouds; - map _runways; - vector _weather; + std::vector _clouds; + std::map _runways; + std::vector _weather; bool scanPreambleDate(); bool scanPreambleTime(); @@ -303,10 +296,7 @@ protected: 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 diff --git a/simgear/environment/test_metar.cxx b/simgear/environment/test_metar.cxx new file mode 100644 index 00000000..bcdc7725 --- /dev/null +++ b/simgear/environment/test_metar.cxx @@ -0,0 +1,68 @@ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include +#include +#include + +#ifdef _MSC_VER +# define random rand +#endif + +#include +#include + +#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; +}