aboutsummaryrefslogblamecommitdiffstats
path: root/my-evolution/metar.c
blob: 00b16518be76539c3e125217ca9e0290a1b70437 (plain) (tree)
1
2
3
4
5
6
7





                                                                   
                   




















                                                 
                                                                                     


                                                           
                                                                                       



                                                         
                                                                                      

                                                  
                                                                                               






















































                                                                                      





















































































































































































































































































































































































                                            





                                      
                       















                                                                                              
                                   
 






                                                                          




































































































































































































































































































                                                                                                       

                                         


                            



                                         










                                                                




























                                                                         





















































































































                                                                                             





















































                                                 
/*
 * 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) ? _("\260F") : _("\260C"))

#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";
}