1017 lines
24 KiB
C
1017 lines
24 KiB
C
/*
|
|
* metar.c: Metar decoding routines.
|
|
*
|
|
* Originally written by Papadimitriou Spiros <spapadim+@cs.cmu.ed>
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <glib.h>
|
|
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <regex.h>
|
|
#include <math.h>
|
|
|
|
#include <libgnome/gnome-defs.h>
|
|
#include <libgnome/gnome-i18n.h>
|
|
|
|
#include "e-summary.h"
|
|
#include "weather.h"
|
|
|
|
#include "metar.h"
|
|
|
|
static regex_t metar_re[RE_NUM];
|
|
|
|
/* Unit conversions and names */
|
|
|
|
#define TEMP_F_TO_C(f) (((f) - 32.0) * 0.555556)
|
|
#define TEMP_C_TO_F(c) (((c) * 1.8) + 32.0)
|
|
#define TEMP_UNIT_STR(units) (((units) == UNITS_IMPERIAL) ? _(" F") : _(" C"))
|
|
|
|
#define WINDSPEED_KNOTS_TO_KPH(knots) ((knots) * 1.851965)
|
|
#define WINDSPEED_KPH_TO_KNOTS(kph) ((kph) * 0.539967)
|
|
#define WINDSPEED_UNIT_STR(units) (((units) == UNITS_IMPERIAL) ? _("knots") : _("kph"))
|
|
|
|
#define PRESSURE_INCH_TO_MM(inch) ((inch) * 25.4)
|
|
#define PRESSURE_MM_TO_INCH(mm) ((mm) * 0.03937)
|
|
#define PRESSURE_MBAR_TO_INCH(mbar) ((mbar) * 0.02963742)
|
|
#define PRESSURE_UNIT_STR(units) (((units) == UNITS_IMPERIAL) ? _("inHg") : _("mmHg"))
|
|
#define VISIBILITY_SM_TO_KM(sm) ((sm) * 1.609344)
|
|
#define VISIBILITY_KM_TO_SM(km) ((km) * 0.621371)
|
|
#define VISIBILITY_UNIT_STR(units) (((units) == UNITS_IMPERIAL) ? _("miles") : _("kilometers"))
|
|
|
|
static const char *sky_str[] = {
|
|
N_("Clear sky"),
|
|
N_("Broken clouds"),
|
|
N_("Scattered clouds"),
|
|
N_("Few clouds"),
|
|
N_("Overcast")
|
|
};
|
|
|
|
const char *
|
|
weather_sky_string (Weather *w)
|
|
{
|
|
if (w->sky < 0 ||
|
|
w->sky >= (sizeof (sky_str) / sizeof (char *))) {
|
|
return _("Invalid");
|
|
}
|
|
|
|
return _(sky_str[(int)w->sky]);
|
|
}
|
|
|
|
static const char *wind_direction_str[] = {
|
|
N_("Variable"),
|
|
N_("North"), N_("North - NorthEast"), N_("Northeast"), N_("East - NorthEast"),
|
|
N_("East"), N_("East - Southeast"), N_("Southeast"), N_("South - Southeast"),
|
|
N_("South"), N_("South - Southwest"), N_("Southwest"), N_("West - Southwest"),
|
|
N_("West"), N_("West - Northwest"), N_("Northwest"), N_("North - Northwest")};
|
|
|
|
const char *
|
|
weather_wind_direction_string (Weather *w)
|
|
{
|
|
if (w->wind < 0 ||
|
|
w->wind >= (sizeof (wind_direction_str) / sizeof (char *))) {
|
|
return _("Invalid");
|
|
}
|
|
|
|
return _(wind_direction_str[(int)w->wind]);
|
|
}
|
|
|
|
/*
|
|
* Even though tedious, I switched to a 2D array for weather condition
|
|
* strings, in order to facilitate internationalization, esp. for languages
|
|
* with genders.
|
|
*
|
|
* I tried to come up with logical names for most phenomena, but I'm no
|
|
* meteorologist, so there will undoubtedly be some stupid mistakes.
|
|
* However, combinations that did not seem plausible (eg. I cannot imagine
|
|
* what a "light tornado" may be like ;-) were filled in with "??". If this
|
|
* ever comes up in the weather conditions field, let me know...
|
|
*/
|
|
|
|
/*
|
|
* Note, magic numbers, when you change the size here, make sure to change
|
|
* the below function so that new values are recognized
|
|
*/
|
|
static const gchar *conditions_str[24][13] = {
|
|
/* NONE
|
|
VICINITY
|
|
LIGHT
|
|
MODERATE
|
|
HEAVY
|
|
SHALLOW
|
|
PATCHES
|
|
PARTIAL
|
|
THUNDERSTORM
|
|
BLOWING
|
|
SHOWERS
|
|
DRIFTING
|
|
FREEZING
|
|
*/
|
|
/* NONE */ {
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
"",
|
|
},
|
|
/* DRIZZLE */ {
|
|
N_("Drizzle"),
|
|
N_("Drizzle in the vicinity"),
|
|
N_("Light drizzle"),
|
|
N_("Moderate drizzle"),
|
|
N_("Heavy drizzle"),
|
|
N_("Shallow drizzle"),
|
|
N_("Patches of drizzle"),
|
|
N_("Partial drizzle"),
|
|
N_("Thunderstorm"),
|
|
N_("Windy drizzle"),
|
|
N_("Showers"),
|
|
N_("Drifting drizzle"),
|
|
N_("Freezing drizzle")
|
|
},
|
|
/* RAIN */ {
|
|
N_("Rain"),
|
|
N_("Rain in the vicinity"),
|
|
N_("Light rain"),
|
|
N_("Moderate rain"),
|
|
N_("Heavy rain"),
|
|
N_("Shallow rain"),
|
|
N_("Patches of rain"),
|
|
N_("Partial rainfall"),
|
|
N_("Thunderstorm"),
|
|
N_("Blowing rainfall"),
|
|
N_("Rain showers"),
|
|
N_("Drifting rain"),
|
|
N_("Freezing rain")
|
|
},
|
|
/* SNOW */ {
|
|
N_("Snow"),
|
|
N_("Snow in the vicinity"),
|
|
N_("Light snow"),
|
|
N_("Moderate snow"),
|
|
N_("Heavy snow"),
|
|
N_("Shallow snow"),
|
|
N_("Patches of snow"),
|
|
N_("Partial snowfall"),
|
|
N_("Snowstorm"),
|
|
N_("Blowing snowfall"),
|
|
N_("Snow showers"),
|
|
N_("Drifting snow"),
|
|
N_("Freezing snow")
|
|
},
|
|
/* SNOW_GRAINS */ {
|
|
N_("Snow grains"),
|
|
N_("Snow grains in the vicinity"),
|
|
N_("Light snow grains"),
|
|
N_("Moderate snow grains"),
|
|
N_("Heavy snow grains"),
|
|
N_("Shallow snow grains"),
|
|
N_("Patches of snow grains"),
|
|
N_("Partial snow grains"),
|
|
N_("Snowstorm"),
|
|
N_("Blowing snow grains"),
|
|
N_("Snow grain showers"),
|
|
N_("Drifting snow grains"),
|
|
N_("Freezing snow grains")
|
|
},
|
|
/* ICE_CRYSTALS */ {
|
|
N_("Ice crystals"),
|
|
N_("Ice crystals in the vicinity"),
|
|
N_("Few ice crystals"),
|
|
N_("Moderate ice crystals"),
|
|
N_("Heavy ice crystals"),
|
|
"??",
|
|
N_("Patches of ice crystals"),
|
|
N_("Partial ice crystals"),
|
|
N_("Ice crystal storm"),
|
|
N_("Blowing ice crystals"),
|
|
N_("Showers of ice crystals"),
|
|
N_("Drifting ice crystals"),
|
|
N_("Freezing ice crystals")
|
|
},
|
|
/* ICE_PELLETS */ {
|
|
N_("Ice pellets"),
|
|
N_("Ice pellets in the vicinity"),
|
|
N_("Few ice pellets"),
|
|
N_("Moderate ice pellets"),
|
|
N_("Heavy ice pellets"),
|
|
N_("Shallow ice pellets"),
|
|
N_("Patches of ice pellets"),
|
|
N_("Partial ice pellets"),
|
|
N_("Ice pellet storm"),
|
|
N_("Blowing ice pellets"),
|
|
N_("Showers of ice pellets"),
|
|
N_("Drifting ice pellets"),
|
|
N_("Freezing ice pellets")
|
|
},
|
|
/* HAIL */ {
|
|
N_("Hail"),
|
|
N_("Hail in the vicinity"),
|
|
N_("Light hail"),
|
|
N_("Moderate hail"),
|
|
N_("Heavy hail"),
|
|
N_("Shallow hail"),
|
|
N_("Patches of hail"),
|
|
N_("Partial hail"),
|
|
N_("Hailstorm"),
|
|
N_("Blowing hail"),
|
|
N_("Hail showers"),
|
|
N_("Drifting hail"),
|
|
N_("Freezing hail")
|
|
},
|
|
/* SMALL_HAIL */ {
|
|
N_("Small hail"),
|
|
N_("Small hail in the vicinity"),
|
|
N_("Light hail"),
|
|
N_("Moderate small hail"),
|
|
N_("Heavy small hail"),
|
|
N_("Shallow small hail"),
|
|
N_("Patches of small hail"),
|
|
N_("Partial small hail"),
|
|
N_("Small hailstorm"),
|
|
N_("Blowing small hail"),
|
|
N_("Showers of small hail"),
|
|
N_("Drifting small hail"),
|
|
N_("Freezing small hail")
|
|
},
|
|
/* PRECIPITATION */ {
|
|
N_("Unknown precipitation"),
|
|
N_("Precipitation in the vicinity"),
|
|
N_("Light precipitation"),
|
|
N_("Moderate precipitation"),
|
|
N_("Heavy precipitation"),
|
|
N_("Shallow precipitation"),
|
|
N_("Patches of precipitation"),
|
|
N_("Partial precipitation"),
|
|
N_("Unknown thunderstorm"),
|
|
N_("Blowing precipitation"),
|
|
N_("Showers, type unknown"),
|
|
N_("Drifting precipitation"),
|
|
N_("Freezing precipitation")
|
|
},
|
|
/* MIST */ {
|
|
N_("Mist"),
|
|
N_("Mist in the vicinity"),
|
|
N_("Light mist"),
|
|
N_("Moderate mist"),
|
|
N_("Thick mist"),
|
|
N_("Shallow mist"),
|
|
N_("Patches of mist"),
|
|
N_("Partial mist"),
|
|
"??",
|
|
N_("Mist with wind"),
|
|
"??",
|
|
N_("Drifting mist"),
|
|
N_("Freezing mist")
|
|
},
|
|
/* FOG */ {
|
|
N_("Fog"),
|
|
N_("Fog in the vicinity"),
|
|
N_("Light fog"),
|
|
N_("Moderate fog"),
|
|
N_("Thick fog"),
|
|
N_("Shallow fog"),
|
|
N_("Patches of fog"),
|
|
N_("Partial fog"),
|
|
"??",
|
|
N_("Fog with wind"),
|
|
"??",
|
|
N_("Drifting fog"),
|
|
N_("Freezing fog")
|
|
},
|
|
/* SMOKE */ {
|
|
N_("Smoke"),
|
|
N_("Smoke in the vicinity"),
|
|
N_("Thin smoke"),
|
|
N_("Moderate smoke"),
|
|
N_("Thick smoke"),
|
|
N_("Shallow smoke"),
|
|
N_("Patches of smoke"),
|
|
N_("Partial smoke"),
|
|
N_("Thunderous smoke"),
|
|
N_("Smoke with wind"),
|
|
"??",
|
|
N_("Drifting smoke"),
|
|
"??"
|
|
},
|
|
/* VOLCANIC_ASH */ {
|
|
N_("Volcanic ash"),
|
|
N_("Volcanic ash in the vicinity"),
|
|
"??",
|
|
N_("Moderate volcanic ash"),
|
|
N_("Thick volcanic ash"),
|
|
N_("Shallow volcanic ash"),
|
|
N_("Patches of volcanic ash"),
|
|
N_("Partial volcanic ash"),
|
|
N_("Thunderous volcanic ash"),
|
|
N_("Blowing volcanic ash"),
|
|
N_("Showers of volcanic ash"),
|
|
N_("Drifting volcanic ash"),
|
|
N_("Freezing volcanic ash")
|
|
},
|
|
/* SAND */ {
|
|
N_("Sand"),
|
|
N_("Sand in the vicinity"),
|
|
N_("Light sand"),
|
|
N_("Moderate sand"),
|
|
N_("Heavy sand"),
|
|
"??",
|
|
N_("Patches of sand"),
|
|
N_("Partial sand"),
|
|
"??",
|
|
N_("Blowing sand"),
|
|
"",
|
|
N_("Drifting sand"),
|
|
"??"
|
|
},
|
|
/* HAZE */ {
|
|
N_("Haze"),
|
|
N_("Haze in the vicinity"),
|
|
N_("Light haze"),
|
|
N_("Moderate haze"),
|
|
N_("Thick haze"),
|
|
N_("Shallow haze"),
|
|
N_("Patches of haze"),
|
|
N_("Partial haze"),
|
|
"??",
|
|
N_("Haze with wind"),
|
|
"??",
|
|
N_("Drifting haze"),
|
|
N_("Freezing haze")
|
|
},
|
|
/* SPRAY */ {
|
|
N_("Spray"),
|
|
N_("Spray in the vicinity"),
|
|
N_("Light spray"),
|
|
N_("Moderate spray"),
|
|
N_("Heavy spray"),
|
|
N_("Shallow spray"),
|
|
N_("Patches of spray"),
|
|
N_("Partial spray"),
|
|
"??",
|
|
N_("Blowing spray"),
|
|
"??",
|
|
N_("Drifting spray"),
|
|
N_("Freezing spray")
|
|
},
|
|
/* DUST */ {
|
|
N_("Dust"),
|
|
N_("Dust in the vicinity"),
|
|
N_("Light dust"),
|
|
N_("Moderate dust"),
|
|
N_("Heavy dust"),
|
|
"??",
|
|
N_("Patches of dust"),
|
|
N_("Partial dust"),
|
|
"??",
|
|
N_("Blowing dust"),
|
|
"??",
|
|
N_("Drifting dust"),
|
|
"??"
|
|
},
|
|
/* SQUALL */ {
|
|
N_("Squall"),
|
|
N_("Squall in the vicinity"),
|
|
N_("Light squall"),
|
|
N_("Moderate squall"),
|
|
N_("Heavy squall"),
|
|
"??",
|
|
"??",
|
|
N_("Partial squall"),
|
|
N_("Thunderous squall"),
|
|
N_("Blowing squall"),
|
|
"??",
|
|
N_("Drifting squall"),
|
|
N_("Freezing squall")
|
|
},
|
|
/* SANDSTORM */ {
|
|
N_("Sandstorm"),
|
|
N_("Sandstorm in the vicinity"),
|
|
N_("Light standstorm"),
|
|
N_("Moderate sandstorm"),
|
|
N_("Heavy sandstorm"),
|
|
N_("Shallow sandstorm"),
|
|
"??",
|
|
N_("Partial sandstorm"),
|
|
N_("Thunderous sandstorm"),
|
|
N_("Blowing sandstorm"),
|
|
"??",
|
|
N_("Drifting sandstorm"),
|
|
N_("Freezing sandstorm")
|
|
},
|
|
/* DUSTSTORM */ {
|
|
N_("Duststorm"),
|
|
N_("Duststorm in the vicinity"),
|
|
N_("Light duststorm"),
|
|
N_("Moderate duststorm"),
|
|
N_("Heavy duststorm"),
|
|
N_("Shallow duststorm"),
|
|
"??",
|
|
N_("Partial duststorm"),
|
|
N_("Thunderous duststorm"),
|
|
N_("Blowing duststorm"),
|
|
"??",
|
|
N_("Drifting duststorm"),
|
|
N_("Freezing duststorm")
|
|
},
|
|
/* FUNNEL_CLOUD */ {
|
|
N_("Funnel cloud"),
|
|
N_("Funnel cloud in the vicinity"),
|
|
N_("Light funnel cloud"),
|
|
N_("Moderate funnel cloud"),
|
|
N_("Thick funnel cloud"),
|
|
N_("Shallow funnel cloud"),
|
|
N_("Patches of funnel clouds"),
|
|
N_("Partial funnel clouds"),
|
|
"??",
|
|
N_("Funnel cloud w/ wind"),
|
|
"??",
|
|
N_("Drifting funnel cloud"),
|
|
"??"
|
|
},
|
|
/* TORNADO */ {
|
|
N_("Tornado"),
|
|
N_("Tornado in the vicinity"),
|
|
"??",
|
|
N_("Moderate tornado"),
|
|
N_("Raging tornado"),
|
|
"??",
|
|
"??",
|
|
N_("Partial tornado"),
|
|
N_("Thunderous tornado"),
|
|
N_("Tornado"),
|
|
"??",
|
|
N_("Drifting tornado"),
|
|
N_("Freezing tornado")
|
|
},
|
|
/* DUST_WHIRLS */ {
|
|
N_("Dust whirls"),
|
|
N_("Dust whirls in the vicinity"),
|
|
N_("Light dust whirls"),
|
|
N_("Moderate dust whirls"),
|
|
N_("Heavy dust whirls"),
|
|
N_("Shallow dust whirls"),
|
|
N_("Patches of dust whirls"),
|
|
N_("Partial dust whirls"),
|
|
"??",
|
|
N_("Blowing dust whirls"),
|
|
"??",
|
|
N_("Drifting dust whirls"),
|
|
"??"
|
|
}
|
|
};
|
|
|
|
const char *
|
|
weather_conditions_string (Weather *w)
|
|
{
|
|
if (!w->cond.significant) {
|
|
return " ";
|
|
} else {
|
|
if (w->cond.phenomenon >= 0 &&
|
|
w->cond.phenomenon < 24 &&
|
|
w->cond.qualifier >= 0 &&
|
|
w->cond.qualifier < 13) {
|
|
return _(conditions_str[(int)w->cond.phenomenon][(int)w->cond.qualifier]);
|
|
} else {
|
|
return _("Invalid");
|
|
}
|
|
}
|
|
}
|
|
|
|
char *
|
|
weather_temp_string (Weather *w)
|
|
{
|
|
char *temp;
|
|
ESummaryWeatherUnits units;
|
|
|
|
if (w->summary->preferences == NULL) {
|
|
units = UNITS_METRIC;
|
|
} else {
|
|
units = w->summary->preferences->units;
|
|
}
|
|
|
|
temp = g_strdup_printf ("%.1f%s", w->temp, TEMP_UNIT_STR (units));
|
|
return temp;
|
|
}
|
|
|
|
void
|
|
metar_init_re (void)
|
|
{
|
|
static gboolean initialized = FALSE;
|
|
if (initialized)
|
|
return;
|
|
initialized = TRUE;
|
|
|
|
regcomp(&metar_re[TIME_RE], TIME_RE_STR, REG_EXTENDED);
|
|
regcomp(&metar_re[WIND_RE], WIND_RE_STR, REG_EXTENDED);
|
|
regcomp(&metar_re[VIS_RE], VIS_RE_STR, REG_EXTENDED);
|
|
regcomp(&metar_re[CLOUD_RE], CLOUD_RE_STR, REG_EXTENDED);
|
|
regcomp(&metar_re[TEMP_RE], TEMP_RE_STR, REG_EXTENDED);
|
|
regcomp(&metar_re[PRES_RE], PRES_RE_STR, REG_EXTENDED);
|
|
regcomp(&metar_re[COND_RE], COND_RE_STR, REG_EXTENDED);
|
|
}
|
|
|
|
static inline gint
|
|
days_in_month (gint month,
|
|
gint year)
|
|
{
|
|
if (month == 1)
|
|
return ((year % 4) == 0) ? 29 : 28;
|
|
else if (((month <= 6) && (month % 2 == 0)) || ((month >=7) && (month % 2 != 0)))
|
|
return 31;
|
|
else
|
|
return 30;
|
|
}
|
|
|
|
/* FIX - there *must* be a simpler, less stupid way to do this!... */
|
|
static time_t
|
|
make_time (gint date,
|
|
gint hour,
|
|
gint min)
|
|
{
|
|
struct tm *tm;
|
|
struct tm tms;
|
|
time_t now;
|
|
gint loc_mday, loc_hour, gm_mday, gm_hour;
|
|
gint hour_diff; /* local time = UTC - hour_diff */
|
|
gint is_dst;
|
|
|
|
now = time(NULL);
|
|
|
|
tm = gmtime(&now);
|
|
gm_mday = tm->tm_mday;
|
|
gm_hour = tm->tm_hour;
|
|
memcpy(&tms, tm, sizeof(struct tm));
|
|
|
|
tm = localtime(&now);
|
|
loc_mday = tm->tm_mday;
|
|
loc_hour = tm->tm_hour;
|
|
is_dst = tm->tm_isdst;
|
|
|
|
/* Estimate timezone */
|
|
if (gm_mday == loc_mday)
|
|
hour_diff = gm_hour - loc_hour;
|
|
else
|
|
if ((gm_mday == loc_mday + 1) || ((gm_mday == 1) && (loc_mday >= 27)))
|
|
hour_diff = gm_hour + (24 - loc_hour);
|
|
else
|
|
hour_diff = -((24 - gm_hour) + loc_hour);
|
|
|
|
/* Make time */
|
|
tms.tm_min = min;
|
|
tms.tm_sec = 0;
|
|
tms.tm_hour = hour - hour_diff;
|
|
tms.tm_mday = date;
|
|
tms.tm_isdst = is_dst;
|
|
if (tms.tm_hour < 0) {
|
|
tms.tm_hour += 24;
|
|
--tms.tm_mday;
|
|
if (tms.tm_mday < 1) {
|
|
--tms.tm_mon;
|
|
if (tms.tm_mon < 0) {
|
|
tms.tm_mon = 11;
|
|
--tms.tm_year;
|
|
}
|
|
tms.tm_mday = days_in_month(tms.tm_mon, tms.tm_year + 1900);
|
|
}
|
|
} else if (tms.tm_hour > 23) {
|
|
tms.tm_hour -= 24;
|
|
++tms.tm_mday;
|
|
if (tms.tm_mday > days_in_month(tms.tm_mon, tms.tm_year + 1900)) {
|
|
++tms.tm_mon;
|
|
if (tms.tm_mon > 11) {
|
|
tms.tm_mon = 0;
|
|
++tms.tm_year;
|
|
}
|
|
tms.tm_mday = 1;
|
|
}
|
|
}
|
|
|
|
return mktime(&tms);
|
|
}
|
|
|
|
gboolean
|
|
metar_tok_time (char *token,
|
|
Weather *w)
|
|
{
|
|
char sday[3], shr[3], smin[3];
|
|
int day, hour, min;
|
|
|
|
if (regexec (&metar_re[TIME_RE], token, 0, NULL, 0) == REG_NOMATCH) {
|
|
return FALSE;
|
|
}
|
|
|
|
strncpy(sday, token, 2);
|
|
sday[2] = 0;
|
|
day = atoi (sday);
|
|
|
|
strncpy (shr, token + 2, 2);
|
|
shr[2] = 0;
|
|
hour = atoi (shr);
|
|
|
|
strncpy (smin, token + 4, 2);
|
|
smin[2] = 0;
|
|
min = atoi (smin);
|
|
|
|
w->update = make_time (day, hour, min);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#define CONST_DIGITS "0123456789"
|
|
|
|
gboolean
|
|
metar_tok_wind (gchar *tokp,
|
|
Weather *w)
|
|
{
|
|
char sdir[4], sspd[4], sgust[4];
|
|
int dir, spd, gust = -1;
|
|
char *gustp;
|
|
|
|
if (regexec(&metar_re[WIND_RE], tokp, 0, NULL, 0) == REG_NOMATCH)
|
|
return FALSE;
|
|
|
|
strncpy(sdir, tokp, 3);
|
|
sdir[3] = 0;
|
|
dir = (!strcmp(sdir, "VRB")) ? -1 : atoi(sdir);
|
|
|
|
memset(sspd, 0, sizeof(sspd));
|
|
strncpy(sspd, tokp+3, strspn(tokp+3, CONST_DIGITS));
|
|
spd = atoi(sspd);
|
|
|
|
gustp = strchr(tokp, 'G');
|
|
if (gustp) {
|
|
memset(sgust, 0, sizeof(sgust));
|
|
strncpy(sgust, gustp+1, strspn(gustp+1, CONST_DIGITS));
|
|
gust = atoi(sgust);
|
|
}
|
|
|
|
if ((349 <= dir) && (dir <= 11))
|
|
w->wind = WIND_N;
|
|
else if ((12 <= dir) && (dir <= 33))
|
|
w->wind = WIND_NNE;
|
|
else if ((34 <= dir) && (dir <= 56))
|
|
w->wind = WIND_NE;
|
|
else if ((57 <= dir) && (dir <= 78))
|
|
w->wind = WIND_ENE;
|
|
else if ((79 <= dir) && (dir <= 101))
|
|
w->wind = WIND_E;
|
|
else if ((102 <= dir) && (dir <= 123))
|
|
w->wind = WIND_ESE;
|
|
else if ((124 <= dir) && (dir <= 146))
|
|
w->wind = WIND_SE;
|
|
else if ((147 <= dir) && (dir <= 168))
|
|
w->wind = WIND_SSE;
|
|
else if ((169 <= dir) && (dir <= 191))
|
|
w->wind = WIND_S;
|
|
else if ((192 <= dir) && (dir <= 213))
|
|
w->wind = WIND_SSW;
|
|
else if ((214 <= dir) && (dir <= 236))
|
|
w->wind = WIND_SW;
|
|
else if ((247 <= dir) && (dir <= 258))
|
|
w->wind = WIND_WSW;
|
|
else if ((259 <= dir) && (dir <= 281))
|
|
w->wind = WIND_W;
|
|
else if ((282 <= dir) && (dir <= 303))
|
|
w->wind = WIND_WNW;
|
|
else if ((304 <= dir) && (dir <= 326))
|
|
w->wind = WIND_NW;
|
|
else if ((327 <= dir) && (dir <= 348))
|
|
w->wind = WIND_NNW;
|
|
|
|
w->windspeed = (ESummaryWeatherWindSpeed)spd;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
metar_tok_vis (gchar *tokp,
|
|
Weather *w)
|
|
{
|
|
char *pfrac, *pend;
|
|
char sval[4];
|
|
int val;
|
|
|
|
if (regexec(&metar_re[VIS_RE], tokp, 0, NULL, 0) == REG_NOMATCH)
|
|
return FALSE;
|
|
|
|
pfrac = strchr(tokp, '/');
|
|
pend = strstr(tokp, "SM");
|
|
memset(sval, 0, sizeof(sval));
|
|
|
|
if (pfrac) {
|
|
strncpy(sval, pfrac + 1, pend - pfrac - 1);
|
|
val = atoi(sval);
|
|
w->visibility = (*tokp == 'M') ? 0.001 : (1.0 / ((ESummaryWeatherVisibility)val));
|
|
} else {
|
|
strncpy(sval, tokp, pend - tokp);
|
|
val = atoi(sval);
|
|
w->visibility = (ESummaryWeatherVisibility)val;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
metar_tok_cloud (gchar *tokp,
|
|
Weather *w)
|
|
{
|
|
char stype[4], salt[4];
|
|
int alt = -1;
|
|
|
|
if (regexec(&metar_re[CLOUD_RE], tokp, 0, NULL, 0) == REG_NOMATCH)
|
|
return FALSE;
|
|
|
|
strncpy(stype, tokp, 3);
|
|
stype[3] = 0;
|
|
if (strlen(tokp) == 6) {
|
|
strncpy(salt, tokp+3, 3);
|
|
salt[3] = 0;
|
|
alt = atoi(salt); /* Altitude - currently unused */
|
|
}
|
|
|
|
if (!strcmp(stype, "CLR")) {
|
|
w->sky = SKY_CLEAR;
|
|
} else if (!strcmp(stype, "BKN")) {
|
|
w->sky = SKY_BROKEN;
|
|
} else if (!strcmp(stype, "SCT")) {
|
|
w->sky = SKY_SCATTERED;
|
|
} else if (!strcmp(stype, "FEW")) {
|
|
w->sky = SKY_FEW;
|
|
} else if (!strcmp(stype, "OVC")) {
|
|
w->sky = SKY_OVERCAST;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
metar_tok_pres (gchar *tokp,
|
|
Weather *w)
|
|
{
|
|
if (regexec(&metar_re[PRES_RE], tokp, 0, NULL, 0) == REG_NOMATCH)
|
|
return FALSE;
|
|
|
|
if (*tokp == 'A') {
|
|
char sintg[3], sfract[3];
|
|
int intg, fract;
|
|
|
|
strncpy(sintg, tokp+1, 2);
|
|
sintg[2] = 0;
|
|
intg = atoi(sintg);
|
|
|
|
strncpy(sfract, tokp+3, 2);
|
|
sfract[2] = 0;
|
|
fract = atoi(sfract);
|
|
|
|
w->pressure = (ESummaryWeatherPressure)intg + (((ESummaryWeatherPressure)fract)/100.0);
|
|
} else { /* *tokp == 'Q' */
|
|
gchar spres[5];
|
|
gint pres;
|
|
|
|
strncpy(spres, tokp+1, 4);
|
|
spres[4] = 0;
|
|
pres = atoi(spres);
|
|
|
|
w->pressure = PRESSURE_MBAR_TO_INCH((ESummaryWeatherPressure)pres);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Relative humidity computation - thanks to <Olof.Oberg@modopaper.modogroup.com> */
|
|
|
|
|
|
static inline gint
|
|
calc_humidity(gdouble temp,
|
|
gdouble dewp,
|
|
ESummaryWeatherUnits units)
|
|
{
|
|
gdouble esat, esurf;
|
|
|
|
if (units == UNITS_IMPERIAL) {
|
|
temp = TEMP_F_TO_C(temp);
|
|
dewp = TEMP_F_TO_C(dewp);
|
|
}
|
|
|
|
esat = 6.11 * pow(10.0, (7.5 * temp) / (237.7 + temp));
|
|
esurf = 6.11 * pow(10.0, (7.5 * dewp) / (237.7 + dewp));
|
|
|
|
return (gint)((esurf/esat) * 100.0);
|
|
}
|
|
|
|
gboolean
|
|
metar_tok_temp (gchar *tokp,
|
|
Weather *w)
|
|
{
|
|
ESummaryWeatherUnits units;
|
|
gchar *ptemp, *pdew, *psep;
|
|
|
|
if (regexec(&metar_re[TEMP_RE], tokp, 0, NULL, 0) == REG_NOMATCH)
|
|
return FALSE;
|
|
|
|
if (w->summary->preferences == NULL) {
|
|
units = UNITS_METRIC;
|
|
} else {
|
|
units = w->summary->preferences->units;
|
|
}
|
|
|
|
psep = strchr(tokp, '/');
|
|
*psep = 0;
|
|
ptemp = tokp;
|
|
pdew = psep + 1;
|
|
|
|
if (units == UNITS_IMPERIAL) {
|
|
w->temp = (*ptemp == 'M') ? TEMP_C_TO_F(-atoi(ptemp+1)) :
|
|
TEMP_C_TO_F(atoi(ptemp));
|
|
w->dew = (*pdew == 'M') ? TEMP_C_TO_F(-atoi(pdew+1)) :
|
|
TEMP_C_TO_F(atoi(pdew));
|
|
} else {
|
|
w->temp = (*ptemp == 'M') ? -atoi(ptemp+1) : atoi(ptemp);
|
|
w->dew = (*pdew == 'M') ? -atoi(pdew+1) : atoi (pdew);
|
|
}
|
|
|
|
w->humidity = calc_humidity(w->temp, w->dew, units);
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
metar_tok_cond (gchar *tokp,
|
|
Weather *w)
|
|
{
|
|
char squal[3], sphen[4];
|
|
char *pphen;
|
|
|
|
if (regexec(&metar_re[COND_RE], tokp, 0, NULL, 0) == REG_NOMATCH)
|
|
return FALSE;
|
|
|
|
if ((strlen(tokp) > 3) && ((*tokp == '+') || (*tokp == '-')))
|
|
++tokp; /* FIX */
|
|
|
|
if ((*tokp == '+') || (*tokp == '-'))
|
|
pphen = tokp + 1;
|
|
else if (strlen(tokp) < 4)
|
|
pphen = tokp;
|
|
else
|
|
pphen = tokp + 2;
|
|
|
|
memset(squal, 0, sizeof(squal));
|
|
strncpy(squal, tokp, pphen - tokp);
|
|
squal[pphen - tokp] = 0;
|
|
|
|
memset(sphen, 0, sizeof(sphen));
|
|
strncpy(sphen, pphen, sizeof(sphen));
|
|
sphen[sizeof(sphen)-1] = '\0';
|
|
|
|
/* Defaults */
|
|
w->cond.qualifier = QUALIFIER_NONE;
|
|
w->cond.phenomenon = PHENOMENON_NONE;
|
|
w->cond.significant = FALSE;
|
|
|
|
if (!strcmp(squal, "")) {
|
|
w->cond.qualifier = QUALIFIER_MODERATE;
|
|
} else if (!strcmp(squal, "-")) {
|
|
w->cond.qualifier = QUALIFIER_LIGHT;
|
|
} else if (!strcmp(squal, "+")) {
|
|
w->cond.qualifier = QUALIFIER_HEAVY;
|
|
} else if (!strcmp(squal, "VC")) {
|
|
w->cond.qualifier = QUALIFIER_VICINITY;
|
|
} else if (!strcmp(squal, "MI")) {
|
|
w->cond.qualifier = QUALIFIER_SHALLOW;
|
|
} else if (!strcmp(squal, "BC")) {
|
|
w->cond.qualifier = QUALIFIER_PATCHES;
|
|
} else if (!strcmp(squal, "PR")) {
|
|
w->cond.qualifier = QUALIFIER_PARTIAL;
|
|
} else if (!strcmp(squal, "TS")) {
|
|
w->cond.qualifier = QUALIFIER_THUNDERSTORM;
|
|
} else if (!strcmp(squal, "BL")) {
|
|
w->cond.qualifier = QUALIFIER_BLOWING;
|
|
} else if (!strcmp(squal, "SH")) {
|
|
w->cond.qualifier = QUALIFIER_SHOWERS;
|
|
} else if (!strcmp(squal, "DR")) {
|
|
w->cond.qualifier = QUALIFIER_DRIFTING;
|
|
} else if (!strcmp(squal, "FZ")) {
|
|
w->cond.qualifier = QUALIFIER_FREEZING;
|
|
} else {
|
|
g_return_val_if_fail(FALSE, FALSE);
|
|
}
|
|
|
|
if (!strcmp(sphen, "DZ")) {
|
|
w->cond.phenomenon = PHENOMENON_DRIZZLE;
|
|
} else if (!strcmp(sphen, "RA")) {
|
|
w->cond.phenomenon = PHENOMENON_RAIN;
|
|
} else if (!strcmp(sphen, "SN")) {
|
|
w->cond.phenomenon = PHENOMENON_SNOW;
|
|
} else if (!strcmp(sphen, "SG")) {
|
|
w->cond.phenomenon = PHENOMENON_SNOW_GRAINS;
|
|
} else if (!strcmp(sphen, "IC")) {
|
|
w->cond.phenomenon = PHENOMENON_ICE_CRYSTALS;
|
|
} else if (!strcmp(sphen, "PE")) {
|
|
w->cond.phenomenon = PHENOMENON_ICE_PELLETS;
|
|
} else if (!strcmp(sphen, "GR")) {
|
|
w->cond.phenomenon = PHENOMENON_HAIL;
|
|
} else if (!strcmp(sphen, "GS")) {
|
|
w->cond.phenomenon = PHENOMENON_SMALL_HAIL;
|
|
} else if (!strcmp(sphen, "UP")) {
|
|
w->cond.phenomenon = PHENOMENON_UNKNOWN_PRECIPITATION;
|
|
} else if (!strcmp(sphen, "BR")) {
|
|
w->cond.phenomenon = PHENOMENON_MIST;
|
|
} else if (!strcmp(sphen, "FG")) {
|
|
w->cond.phenomenon = PHENOMENON_FOG;
|
|
} else if (!strcmp(sphen, "FU")) {
|
|
w->cond.phenomenon = PHENOMENON_SMOKE;
|
|
} else if (!strcmp(sphen, "VA")) {
|
|
w->cond.phenomenon = PHENOMENON_VOLCANIC_ASH;
|
|
} else if (!strcmp(sphen, "SA")) {
|
|
w->cond.phenomenon = PHENOMENON_SAND;
|
|
} else if (!strcmp(sphen, "HZ")) {
|
|
w->cond.phenomenon = PHENOMENON_HAZE;
|
|
} else if (!strcmp(sphen, "PY")) {
|
|
w->cond.phenomenon = PHENOMENON_SPRAY;
|
|
} else if (!strcmp(sphen, "DU")) {
|
|
w->cond.phenomenon = PHENOMENON_DUST;
|
|
} else if (!strcmp(sphen, "SQ")) {
|
|
w->cond.phenomenon = PHENOMENON_SQUALL;
|
|
} else if (!strcmp(sphen, "SS")) {
|
|
w->cond.phenomenon = PHENOMENON_SANDSTORM;
|
|
} else if (!strcmp(sphen, "DS")) {
|
|
w->cond.phenomenon = PHENOMENON_DUSTSTORM;
|
|
} else if (!strcmp(sphen, "PO")) {
|
|
w->cond.phenomenon = PHENOMENON_DUST_WHIRLS;
|
|
} else if (!strcmp(sphen, "+FC")) {
|
|
w->cond.phenomenon = PHENOMENON_TORNADO;
|
|
} else if (!strcmp(sphen, "FC")) {
|
|
w->cond.phenomenon = PHENOMENON_FUNNEL_CLOUD;
|
|
} else {
|
|
g_return_val_if_fail(FALSE, FALSE);
|
|
}
|
|
|
|
if ((w->cond.qualifier != QUALIFIER_NONE) || (w->cond.phenomenon != PHENOMENON_NONE))
|
|
w->cond.significant = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
const char *
|
|
icon_from_weather (Weather *w)
|
|
{
|
|
ESummaryWeatherConditions cond = w->cond;
|
|
ESummaryWeatherSky sky = w->sky;
|
|
|
|
switch (cond.phenomenon) {
|
|
case PHENOMENON_DRIZZLE:
|
|
case PHENOMENON_RAIN:
|
|
case PHENOMENON_UNKNOWN_PRECIPITATION:
|
|
case PHENOMENON_HAIL:
|
|
case PHENOMENON_SMALL_HAIL:
|
|
return "myweather-rain.png";
|
|
case PHENOMENON_SNOW:
|
|
case PHENOMENON_SNOW_GRAINS:
|
|
case PHENOMENON_ICE_PELLETS:
|
|
case PHENOMENON_ICE_CRYSTALS:
|
|
return "myweather-snow.png";
|
|
case PHENOMENON_TORNADO:
|
|
case PHENOMENON_SQUALL:
|
|
return "myweather-storm.png";
|
|
case PHENOMENON_MIST:
|
|
case PHENOMENON_FOG:
|
|
case PHENOMENON_SMOKE:
|
|
case PHENOMENON_VOLCANIC_ASH:
|
|
case PHENOMENON_SAND:
|
|
case PHENOMENON_HAZE:
|
|
case PHENOMENON_SPRAY:
|
|
case PHENOMENON_DUST:
|
|
case PHENOMENON_SANDSTORM:
|
|
case PHENOMENON_DUSTSTORM:
|
|
case PHENOMENON_FUNNEL_CLOUD:
|
|
case PHENOMENON_DUST_WHIRLS:
|
|
return "myweather-fog.png";
|
|
default:
|
|
break;
|
|
}
|
|
|
|
switch (sky) {
|
|
case SKY_CLEAR:
|
|
return "myweather-sun.png";
|
|
case SKY_BROKEN:
|
|
case SKY_SCATTERED:
|
|
case SKY_FEW:
|
|
return "myweather-suncloud.png";
|
|
case SKY_OVERCAST:
|
|
return "myweather-clouds.png";
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return "es-weather.png";
|
|
}
|