/*****************************************************************************
 * metaname.c
 *
 * DESCRIPTION
 *    This file contains the code necessary to parse the GRIB2 product
 * definition information into human readable text.  In addition to the
 * tables in the GRIB2 specs, it also attempts to handle local table
 * definitions that NCEP and NDFD have developed.
 *
 * HISTORY
 *    1/2004 Arthur Taylor (MDL / RSIS): Created.
 *
 * NOTES
 *****************************************************************************
 */
#include <string.h>
#include <stdlib.h>
#include "meta.h"
#include "metaname.h"
#include "myerror.h"
#include "myassert.h"
#include "myutil.h"

#include "cpl_port.h"

const char *centerLookup (unsigned short int center)
{
   /* see:
    * http://www.wmo.ch/web/www/WMOCodes/Operational/CommonTables/BUFRCommon-2005feb.pdf
    * http://www.wmo.int/web/www/WMOCodes/Operational/CommonTables/BUFRCommon-2005nov.pdf
    * also see:
    * http://www.nco.ncep.noaa.gov/pmb/docs/on388/table0.html 
    * I typed this in on 11/2/2006 */
/* *INDENT-OFF* */
   static const struct {
      unsigned short int num;
      const char *name;
   } Center[] = {
      {0, "WMO Secretariat"},
      {1, "Melbourne"},
      {2, "Melbourne"},
      {3, ") Melbourne"},
      {4, "Moscow"},
      {5, "Moscow"},
      {6, ") Moscow"},
      {7, "US-NCEP"},
      {8, "US-NWSTG"},
      {9, "US-Other"},
      {10, "Cairo"},
      {11, ") Cairo"},
      {12, "Dakar"},
      {13, ") Dakar"},
      {14, "Nairobi"},
      {15, ") Nairobi"},
      {16, "Casablanca"},
      {17, "Tunis"},
      {18, "Tunis Casablanca"},
      {19, ") Tunis Casablanca"},
      {20, "Las Palmas"},
      {21, "Algiers"},
      {22, "ACMAD"},
      {23, "Mozambique"},
      {24, "Pretoria"},
      {25, "La R\xE9" "union"},
      {26, "Khabarovsk"},
      {27, ") Khabarovsk"},
      {28, "New Delhi"},
      {29, ") New Delhi"},
      {30, "Novosibirsk"},
      {31, ") Novosibirsk"},
      {32, "Tashkent"},
      {33, "Jeddah"},
      {34, "Tokyo"},
      {35, ") Tokyo"},
      {36, "Bangkok"},
      {37, "Ulan Bator"},
      {38, "Beijing"},
      {39, ") Beijing"},
      {40, "Seoul"},
      {41, "Buenos Aires"},
      {42, ") Buenos Aires"},
      {43, "Brasilia"},
      {44, ") Brasilia"},
      {45, "Santiago"},
      {46, "Brazilian Space Agency"},
      {47, "Colombia"},
      {48, "Ecuador"},
      {49, "Peru"},
      {50, "Venezuela"},
      {51, "Miami"},
      {52, "Miami-NHC"},
      {53, "Montreal"},
      {54, ") Montreal"},
      {55, "San Francisco"},
      {56, "ARINC Centre"},
      {57, "US-Air Force Weather"},
      {58, "US-Fleet Meteorology and Oceanography"},
      {59, "US-FSL"},
      {60, "US-NCAR"},
      {61, "US-Service ARGOS"},
      {62, "US-Naval Oceanographic Office"},
/*      {63, "Reserved for another centre in Region IV"},*/
      {64, "Honolulu"},
      {65, "Darwin"},
      {66, ") Darwin"},
      {67, "Melbourne"},
/*      {68, "Reserved"},*/
      {69, "Wellington"},
      {70, ") Wellington"},
      {71, "Nadi"},
      {72, "Singapore"},
      {73, "Malaysia"},
      {74, "UK-Met-Exeter"},
      {75, ") UK-Met-Exeter"},
      {76, "Moscow"},
/*      {77, "Reserved"},*/
      {78, "Offenbach"},
      {79, ") Offenbach"},
      {80, "Rome"},
      {81, ") Rome"},
      {82, "Norrk\xF6" "ping"},
      {83, ") Norrk\xF6" "ping"},
      {84, "Toulouse"},
      {85, "Toulouse"},
      {86, "Helsinki"},
      {87, "Belgrade"},
      {88, "Oslo"},
      {89, "Prague"},
      {90, "Episkopi"},
      {91, "Ankara"},
      {92, "Frankfurt/Main"},
      {93, "London"},
      {94, "Copenhagen"},
      {95, "Rota"},
      {96, "Athens"},
      {97, "ESA-European Space Agency"},
      {98, "ECMWF"},
      {99, "DeBilt"},
      {100, "Brazzaville"},
      {101, "Abidjan"},
      {102, "Libyan Arab Jamahiriya"},
      {103, "Madagascar"},
      {104, "Mauritius"},
      {105, "Niger"},
      {106, "Seychelles"},
      {107, "Uganda"},
      {108, "Tanzania"},
      {109, "Zimbabwe"},
      {110, "Hong-Kong, China"},
      {111, "Afghanistan"},
      {112, "Bahrain"},
      {113, "Bangladesh"},
      {114, "Bhutan"},
      {115, "Cambodia"},
      {116, "Democratic People's Republic of Korea"},
      {117, "Islamic Republic of Iran"},
      {118, "Iraq"},
      {119, "Kazakhstan"},
      {120, "Kuwait"},
      {121, "Kyrgyz Republic"},
      {122, "Lao People's Democratic Republic"},
      {123, "Macao, China"},
      {124, "Maldives"},
      {125, "Myanmar"},
      {126, "Nepal"},
      {127, "Oman"},
      {128, "Pakistan"},
      {129, "Qatar"},
      {130, "Republic of Yemen"},
      {131, "Sri Lanka"},
      {132, "Tajikistan"},
      {133, "Turkmenistan"},
      {134, "United Arab Emirates"},
      {135, "Uzbekistan"},
      {136, "Socialist Republic of Viet Nam"},
/*      {137, "Reserved"},*/
/*      {138, "Reserved"},*/
/*      {139, "Reserved"},*/
      {140, "Bolivia"},
      {141, "Guyana"},
      {142, "Paraguay"},
      {143, "Suriname"},
      {144, "Uruguay"},
      {145, "French Guyana"},
      {146, "Brazilian Navy Hydrographic Centre"},
/*      {147, "Reserved"},*/
/*      {148, "Reserved"},*/
/*      {149, "Reserved"},*/
      {150, "Antigua and Barbuda"},
      {151, "Bahamas"},
      {152, "Barbados"},
      {153, "Belize"},
      {154, "British Caribbean Territories"},
      {155, "San Jose"},
      {156, "Cuba"},
      {157, "Dominica"},
      {158, "Dominican Republic"},
      {159, "El Salvador"},
      {160, "US-NESDIS"},
      {161, "US-OAR"},
      {162, "Guatemala"},
      {163, "Haiti"},
      {164, "Honduras"},
      {165, "Jamaica"},
      {166, "Mexico"},
      {167, "Netherlands Antilles and Aruba"},
      {168, "Nicaragua"},
      {169, "Panama"},
      {170, "Saint Lucia NMC"},
      {171, "Trinidad and Tobago"},
      {172, "French Departments"},
/*      {173, "Reserved"},*/
/*      {174, "Reserved"},*/
/*      {175, "Reserved"},*/
/*      {176, "Reserved"},*/
/*      {177, "Reserved"},*/
/*      {178, "Reserved"},*/
/*      {179, "Reserved"},*/
/*      {180, "Reserved"},*/
/*      {181, "Reserved"},*/
/*      {182, "Reserved"},*/
/*      {183, "Reserved"},*/
/*      {184, "Reserved"},*/
/*      {185, "Reserved"},*/
/*      {186, "Reserved"},*/
/*      {187, "Reserved"},*/
/*      {188, "Reserved"},*/
/*      {189, "Reserved"},*/
      {190, "Cook Islands"},
      {191, "French Polynesia"},
      {192, "Tonga"},
      {193, "Vanuatu"},
      {194, "Brunei"},
      {195, "Indonesia"},
      {196, "Kiribati"},
      {197, "Federated States of Micronesia"},
      {198, "New Caledonia"},
      {199, "Niue"},
      {200, "Papua New Guinea"},
      {201, "Philippines"},
      {202, "Samoa"},
      {203, "Solomon Islands"},
/*      {204, "Reserved"},*/
/*      {205, "Reserved"},*/
/*      {206, "Reserved"},*/
/*      {207, "Reserved"},*/
/*      {208, "Reserved"},*/
/*      {209, "Reserved"},*/
      {210, "Frascati (ESA/ESRIN)"},
      {211, "Lanion"},
      {212, "Lisboa"},
      {213, "Reykiavik"},
      {214, "Madrid"},
      {215, "Z\xFC" "rich"},
      {216, "Service ARGOS Toulouse"},
      {217, "Bratislava"},
      {218, "Budapest"},
      {219, "Ljubljana"},
      {220, "Warsaw"},
      {221, "Zagreb"},
      {222, "Albania"},
      {223, "Armenia"},
      {224, "Austria"},
      {225, "Azerbaijan"},
      {226, "Belarus"},
      {227, "Belgium"},
      {228, "Bosnia and Herzegovina"},
      {229, "Bulgaria"},
      {230, "Cyprus"},
      {231, "Estonia"},
      {232, "Georgia"},
      {233, "Dublin"},
      {234, "Israel"},
      {235, "Jordan"},
      {236, "Latvia"},
      {237, "Lebanon"},
      {238, "Lithuania"},
      {239, "Luxembourg"},
      {240, "Malta"},
      {241, "Monaco"},
      {242, "Romania"},
      {243, "Syrian Arab Republic"},
      {244, "The former Yugoslav Republic of Macedonia"},
      {245, "Ukraine"},
      {246, "Republic of Moldova"},
/*      {247, "Reserved"},*/
/*      {248, "Reserved"},*/
/*      {249, "Reserved"},*/
/*      {250, "Reserved"},*/
/*      {251, "Reserved"},*/
/*      {252, "Reserved"},*/
/*      {253, "Reserved"},*/
      {254, "EUMETSAT Operation Centre"},
/*      {255, "Reserved"},*/
      {256, "Angola"},
      {257, "Benin"},
      {258, "Botswana"},
      {259, "Burkina Faso"},
      {260, "Burundi"},
      {261, "Cameroon"},
      {262, "Cape Verde"},
      {263, "Central African republic"},
      {264, "Chad"},
      {265, "Comoros"},
      {266, "Democratic Republic of the Congo"},
      {267, "Djibouti"},
      {268, "Eritrea"},
      {269, "Ethiopia"},
      {270, "Gabon"},
      {271, "Gambia"},
      {272, "Ghana"},
      {273, "Guinea"},
      {274, "Guinea Bissau"},
      {275, "Lesotho"},
      {276, "Liberia"},
      {277, "Malawi"},
      {278, "Mali"},
      {279, "Mauritania"},
      {280, "Namibia"},
      {281, "Nigeria"},
      {282, "Rwanda"},
      {283, "Sao Tome and Principe"},
      {284, "Sierra Leone"},
      {285, "Somalia"},
      {286, "Sudan"},
      {287, "Swaziland"},
      {288, "Togo"},
      {289, "Zambia"}
   };
/* *INDENT-ON* */
   int numCenter = sizeof (Center) / sizeof (Center[0]);
   int i;

   for (i = 0; i < numCenter; i++) {
      if (Center[i].num == center) {
         return Center[i].name;
      }
   }
   return NULL;
}

const char *subCenterLookup(unsigned short int center,
                            unsigned short int subcenter)
{
/* *INDENT-OFF* */
/* see:
 * http://www.wmo.ch/web/www/WMOCodes/Operational/CommonTables/
 *        BUFRCommon-2005feb.pdf as of 10/12/2005
 * http://www.nco.ncep.noaa.gov/pmb/docs/on388/tablec.html as of 11/9/2005
 * http://www.ecmwf.int/publications/manuals/libraries/gribex/
 *        localGRIBUsage.html as of 4/5/2006
 */
   static const struct {
      unsigned short int center, subcenter;
      const char *name;
   } SubCenter[] = {
      {7, 1, "NCEP Re-Analysis Project"}, {7, 2, "NCEP Ensemble Products"},
      {7, 3, "NCEP Central Operations"},
      {7, 4, "Environmental Modeling Center"},
      {7, 5, "Hydrometeorological Prediction Center"},
      {7, 6, "Ocean Prediction Center"}, {7, 7, "Climate Prediction Center"},
      {7, 8, "Aviation Weather Center"}, {7, 9, "Storm Prediction Center"},
      {7, 10, "Tropical Prediction Center"},
      {7, 11, "Techniques Development Laboratory"},
      {7, 12, "NESDIS Office of Research and Applications"},
      {7, 13, "FAA"},
      {7, 14, "Meteorological Development Laboratory (MDL)"},
      {7, 15, "North American Regional Reanalysis (NARR) Project"},
      {7, 16, "Space Environment Center"},
      {8, 0, "National Digital Forecast Database"},
/*      {8, GRIB2MISSING_2, "National Digital Forecast Database"},*/
      {161, 1, "Great Lakes Environmental Research Laboratory"},
      {161, 2, "Forecast Systems Laboratory"},
      {74, 1, "Shanwick Oceanic Area Control Centre"},
      {74, 2, "Fucino"}, {74, 3, "Gatineau"}, {74, 4, "Maspalomas"},
      {74, 5, "ESA ERS Central Facility"}, {74, 6, "Prince Albert"},
      {74, 7, "West Freugh"}, {74, 13, "Tromso"},
      {74, 21, "Agenzia Spaziale Italiana (Italy)"},
      {74, 22, "Centre National de la Recherche Scientifique (France)"},
      {74, 23, "GeoForschungsZentrum (Germany)"},
      {74, 24, "Geodetic Observatory Pecny (Czech Republic)"},
      {74, 25, "Institut d'Estudis Espacials de Catalunya (Spain)"},
      {74, 26, "Swiss Federal Office of Topography"},
      {74, 27, "Nordic Commission of Geodesy (Norway)"},
      {74, 28, "Nordic Commission of Geodesy (Sweden)"},
      {74, 29, "Institute de Geodesie National (France)"},
      {74, 30, "Bundesamt f\xFC" "r Kartographie und Geod\xE4" "sie (Germany)"},
      {74, 31, "Institute of Engineering Satellite Surveying and Geodesy (U.K.)"},
      {254, 10, "Tromso (Norway)"}, {254, 10, "Maspalomas (Spain)"},
      {254, 30, "Kangerlussuaq (Greenland)"}, {254, 40, "Edmonton (Canada)"},
      {254, 50, "Bedford (Canada)"}, {254, 60, "Gander (Canada)"},
      {254, 70, "Monterey (USA)"}, {254, 80, "Wallops Island (USA)"},
      {254, 90, "Gilmor Creek (USA)"}, {254, 100, "Athens (Greece)"},
      {98, 231, "CNRM, Meteo France Climate Centre (HIRETYCS)"},
      {98, 232, "MPI, Max Planck Institute Climate Centre (HIRETYCS)"},
      {98, 233, "UKMO Climate Centre (HIRETYCS)"},
      {98, 234, "ECMWF (DEMETER)"},
      {98, 235, "INGV-CNR (Bologna, Italy)(DEMETER)"},
      {98, 236, "LODYC (Paris, France)(DEMETER)"},
      {98, 237, "DMI (Copenhagen, Denmark)(DEMETER)"},
      {98, 238, "INM (Madrid, Spain)(DEMETER)"},
      {98, 239, "CERFACS (Toulouse, France)(DEMETER)"},
      {98, 240, "ECMWF (PROVOST)"},
      {98, 241, "Meteo France (PROVOST)"},
      {98, 242, "EDF (PROVOST)"},
      {98, 243, "UKMO (PROVOST)"},
      {98, 244, "Biometeorology group, University of Veterinary Medicine, Vienna (ELDAS)"},
   };
/* *INDENT-ON* */
   int numSubCenter = sizeof (SubCenter) / sizeof (SubCenter[0]);
   int i;

   for (i = 0; i < numSubCenter; i++) {
      if ((SubCenter[i].center == center) &&
          (SubCenter[i].subcenter == subcenter)) {
         return SubCenter[i].name;
      }
   }
   return NULL;
}

const char *processLookup (unsigned short int center, unsigned char process)
{
   /* see: http://www.nco.ncep.noaa.gov/pmb/docs/on388/tablea.html I typed
    * this in on 10/12/2005 */
/* *INDENT-OFF* */
   static const struct {
      unsigned short int center;
      unsigned char process;
      const char *name;
   } Process[] = {
      {7, 2, "Ultra Violet Index Model"},
      {7, 3, "NCEP/ARL Transport and Dispersion Model"},
      {7, 4, "NCEP/ARL Smoke Model"},
      {7, 5, "Satellite Derived Precipitation and temperatures, from IR"},
      {7, 10, "Global Wind-Wave Forecast Model"},
      {7, 19, "Limited-area Fine Mesh (LFM) analysis"},
      {7, 25, "Snow Cover Analysis"},
      {7, 30, "Forecaster generated field"},
      {7, 31, "Value added post processed field"},
      {7, 39, "Nested Grid forecast Model (NGM)"},
      {7, 42, "Global Optimum Interpolation Analysis (GOI) from GFS model"},
      {7, 43, "Global Optimum Interpolation Analysis (GOI) from 'Final' run"},
      {7, 44, "Sea Surface Temperature Analysis"},
      {7, 45, "Coastal Ocean Circulation Model"},
      {7, 46, "HYCOM - Global"},
      {7, 47, "HYCOM - North Pacific basin"},
      {7, 48, "HYCOM - North Atlantic basin"},
      {7, 49, "Ozone Analysis from TIROS Observations"},
      {7, 52, "Ozone Analysis from Nimbus 7 Observations"},
      {7, 53, "LFM-Fourth Order Forecast Model"},
      {7, 64, "Regional Optimum Interpolation Analysis (ROI)"},
      {7, 68, "80 wave triangular, 18-layer Spectral model from GFS model"},
      {7, 69, "80 wave triangular, 18 layer Spectral model from 'Medium Range Forecast' run"},
      {7, 70, "Quasi-Lagrangian Hurricane Model (QLM)"},
      {7, 73, "Fog Forecast model - Ocean Prod. Center"},
      {7, 74, "Gulf of Mexico Wind/Wave"},
      {7, 75, "Gulf of Alaska Wind/Wave"},
      {7, 76, "Bias corrected Medium Range Forecast"},
      {7, 77, "126 wave triangular, 28 layer Spectral model from GFS model"},
      {7, 78, "126 wave triangular, 28 layer Spectral model from 'Medium Range Forecast' run"},
      {7, 79, "Backup from the previous run"},
      {7, 80, "62 wave triangular, 28 layer Spectral model from 'Medium Range Forecast' run"},
      {7, 81, "Spectral Statistical Interpolation (SSI) analysis from GFS model"},
      {7, 82, "Spectral Statistical Interpolation (SSI) analysis from 'Final' run."},
      {7, 84, "MESO ETA Model (currently 12 km)"},
      {7, 86, "RUC Model from FSL (isentropic; scale: 60km at 40N)"},
      {7, 87, "CAC Ensemble Forecasts from Spectral (ENSMB)"},
      {7, 88, "NOAA Wave Watch III (NWW3) Ocean Wave Model"},
      {7, 89, "Non-hydrostatic Meso Model (NMM) Currently 8 km)"},
      {7, 90, "62 wave triangular, 28 layer spectral model extension of the 'Medium Range Forecast' run"},
      {7, 91, "62 wave triangular, 28 layer spectral model extension of the GFS model"},
      {7, 92, "62 wave triangular, 28 layer spectral model run from the 'Medium Range Forecast' final analysis"},
      {7, 93, "62 wave triangular, 28 layer spectral model run from the T62 GDAS analysis of the 'Medium Range Forecast' run"},
      {7, 94, "T170/L42 Global Spectral Model from MRF run"},
      {7, 95, "T126/L42 Global Spectral Model from MRF run"},
      {7, 96, "Global Forecast System Model"},
      {7, 98, "Climate Forecast System Model"},
      {7, 100, "RUC Surface Analysis (scale: 60km at 40N)"},
      {7, 101, "RUC Surface Analysis (scale: 40km at 40N)"},
      {7, 105, "RUC Model from FSL (isentropic; scale: 20km at 40N)"},
      {7, 110, "ETA Model - 15km version"},
      {7, 111, "Eta model, generic resolution"},
      {7, 112, "WRF-NMM (Nondydrostatic Mesoscale Model) model, generic resolution"},
      {7, 113, "Products from NCEP SREF processing"},
      {7, 115, "Downscaled GFS from Eta eXtension"},
      {7, 116, "WRF-EM (Eulerian Mass-core) model, generic resolution "},
      {7, 120, "Ice Concentration Analysis"},
      {7, 121, "Western North Atlantic Regional Wave Model"},
      {7, 122, "Alaska Waters Regional Wave Model"},
      {7, 123, "North Atlantic Hurricane Wave Model"},
      {7, 124, "Eastern North Pacific Regional Wave Model"},
      {7, 125, "North Pacific Hurricane Wave Model"},
      {7, 126, "Sea Ice Forecast Model"},
      {7, 127, "Lake Ice Forecast Model"},
      {7, 128, "Global Ocean Forecast Model"},
      {7, 129, "Global Ocean Data Analysis System (GODAS)"},
      {7, 130, "Merge of fields from the RUC, Eta, and Spectral Model"},
      {7, 131, "Great Lakes Wave Model"},
      {7, 140, "North American Regional Reanalysis (NARR)"},
      {7, 141, "Land Data Assimilation and Forecast System"},
      {7, 150, "NWS River Forecast System (NWSRFS)"},
      {7, 151, "NWS Flash Flood Guidance System (NWSFFGS)"},
      {7, 152, "WSR-88D Stage II Precipitation Analysis"},
      {7, 153, "WSR-88D Stage III Precipitation Analysis"},
      {7, 180, "Quantitative Precipitation Forecast"},
      {7, 181, "River Forecast Center Quantitative Precipitation Forecast mosaic"},
      {7, 182, "River Forecast Center Quantitative Precipitation estimate mosaic"},
      {7, 183, "NDFD product generated by NCEP/HPC"},
      {7, 190, "National Convective Weather Diagnostic"},
      {7, 191, "Current Icing Potential automated product"},
      {7, 192, "Analysis product from NCEP/AWC"},
      {7, 193, "Forecast product from NCEP/AWC"},
      {7, 195, "Climate Data Assimilation System 2 (CDAS2)"},
      {7, 196, "Climate Data Assimilation System 2 (CDAS2)"},
      {7, 197, "Climate Data Assimilation System (CDAS)"},
      {7, 198, "Climate Data Assimilation System (CDAS)"},
      {7, 200, "CPC Manual Forecast Product"},
      {7, 201, "CPC Automated Product"},
      {7, 210, "EPA Air Quality Forecast"},
      {7, 211, "EPA Air Quality Forecast"},
      {7, 220, "NCEP/OPC automated product"}
   };
/* *INDENT-ON* */
   int numProcess = sizeof (Process) / sizeof (Process[0]);
   int i;

   for (i = 0; i < numProcess; i++) {
      if ((Process[i].center == center) && (Process[i].process == process)) {
         return Process[i].name;
      }
   }
   return NULL;
}

typedef struct {
   const char *name, *comment, *unit;
   unit_convert convert;
} GRIB2ParmTable;

typedef struct {
   int prodType, cat, subcat;
   const char *name, *comment, *unit;
   unit_convert convert;
} GRIB2LocalTable;

typedef struct {
    const char *GRIB2name, *NDFDname;
} NDFD_AbrevOverideTable;

/* *INDENT-OFF* */
/* Updated based on:
 * http://www.wmo.ch/web/www/WMOCodes/Operational/GRIB2/FM92-GRIB2-2005nov.pdf
 * 1/3/2006
 */
/* GRIB2 Code table 4.2 : 0.0 */
static const GRIB2ParmTable MeteoTemp[] = {
   /* 0 */ {"TMP", "Temperature", "K", UC_K2F},  /* Need NDFD override. T */
   /* 1 */ {"VTMP", "Virtual temperature", "K", UC_K2F},
   /* 2 */ {"POT", "Potential temperature", "K", UC_K2F},
   /* 3 */ {"EPOT", "Pseudo-adiabatic potential temperature", "K", UC_K2F},
   /* 4 */ {"TMAX", "Maximum Temperature", "K", UC_K2F}, /* Need NDFD override MaxT */
   /* 5 */ {"TMIN", "Minimum Temperature", "K", UC_K2F}, /* Need NDFD override MinT */
   /* 6 */ {"DPT", "Dew point temperature", "K", UC_K2F}, /* Need NDFD override Td */
   /* 7 */ {"DEPR", "Dew point depression", "K", UC_K2F},
   /* 8 */ {"LAPR", "Lapse rate", "K/m", UC_NONE},
   /* 9 */ {"TMPA", "Temperature anomaly", "K", UC_K2F},
   /* 10 */ {"LHTFL", "Latent heat net flux", "W/(m^2)", UC_NONE},
   /* 11 */ {"SHTFL", "Sensible heat net flux", "W/(m^2)", UC_NONE},
            /* NDFD */
   /* 12 */ {"HeatIndex", "Heat index", "K", UC_K2F},
            /* NDFD */
   /* 13 */ {"WCI", "Wind chill factor", "K", UC_K2F},
   /* 14 */ {"", "Minimum dew point depression", "K", UC_K2F},
   /* 15 */ {"VPTMP", "Virtual potential temperature", "K", UC_K2F},
/* 16 */    {"SNOHF", "Snow phase change heat flux", "W/m^2", UC_NONE},
};

/* GRIB2 Code table 4.2 : 0.1 */
/* NCEP added "Water" to items 22, 24, 25 */
static const GRIB2ParmTable MeteoMoist[] = {
   /* 0 */ {"SPFH", "Specific humidity", "kg/kg", UC_NONE},
   /* 1 */ {"RH", "Relative Humidity", "%", UC_NONE},
   /* 2 */ {"MIXR", "Humidity mixing ratio", "kg/kg", UC_NONE},
   /* 3 */ {"PWAT", "Precipitable water", "kg/(m^2)", UC_NONE},
   /* 4 */ {"VAPP", "Vapor Pressure", "Pa", UC_NONE},
   /* 5 */ {"SATD", "Saturation deficit", "Pa", UC_NONE},
   /* 6 */ {"EVP", "Evaporation", "kg/(m^2)", UC_NONE},
   /* 7 */ {"PRATE", "Precipitation rate", "kg/(m^2 s)", UC_NONE},
   /* 8 */ {"APCP", "Total precipitation", "kg/(m^2)", UC_InchWater}, /* Need NDFD override QPF */
   /* 9 */ {"NCPCP", "Large scale precipitation", "kg/(m^2)", UC_NONE},
   /* 10 */ {"ACPCP", "Convective precipitation", "kg/(m^2)", UC_NONE},
   /* 11 */ {"SNOD", "Snow depth", "m", UC_M2Inch}, /* Need NDFD override SnowDepth ? */
   /* 12 */ {"SRWEQ", "Snowfall rate water equivalent", "kg/(m^2 s)", UC_NONE},
   /* 13 */ {"WEASD", "Water equivalent of accumulated snow depth",
             "kg/(m^2)", UC_NONE},
   /* 14 */ {"SNOC", "Convective snow", "kg/(m^2)", UC_NONE},
   /* 15 */ {"SNOL", "Large scale snow", "kg/(m^2)", UC_NONE},
   /* 16 */ {"SNOM", "Snow melt", "kg/(m^2)", UC_NONE},
   /* 17 */ {"SNOAG", "Snow age", "day", UC_NONE},
   /* 18 */ {"", "Absolute humidity", "kg/(m^3)", UC_NONE},
   /* 19 */ {"", "Precipitation type", "(1 Rain, 2 Thunderstorm, "
             "3 Freezing Rain, 4 Mixed/ice, 5 snow, 255 missing)", UC_NONE},
   /* 20 */ {"", "Integrated liquid water", "kg/(m^2)", UC_NONE},
   /* 21 */ {"TCOND", "Condensate", "kg/kg", UC_NONE},
/* CLWMR Did not make it to tables yet should be "-" */
   /* 22 */ {"CLWMR", "Cloud Water Mixing Ratio", "kg/kg", UC_NONE},
   /* 23 */ {"ICMR", "Ice water mixing ratio", "kg/kg", UC_NONE},   /* ICMR? */
   /* 24 */ {"RWMR", "Rain Water Mixing Ratio", "kg/kg", UC_NONE},
   /* 25 */ {"SNMR", "Snow Water Mixing Ratio", "kg/kg", UC_NONE},
   /* 26 */ {"MCONV", "Horizontal moisture convergence", "kg/(kg s)", UC_NONE},
   /* 27 */ {"", "Maximum relative humidity", "%", UC_NONE},
   /* 28 */ {"", "Maximum absolute humidity", "kg/(m^3)", UC_NONE},
            /* NDFD */
   /* 29 */ {"ASNOW", "Total snowfall", "m", UC_M2Inch},
   /* 30 */ {"", "Precipitable water category", "(undefined)", UC_NONE},
   /* 31 */ {"", "Hail", "m", UC_NONE},
   /* 32 */ {"", "Graupel (snow pellets)", "kg/kg", UC_NONE},
/* 33 */    {"CRAIN", "Categorical rain", "0=no, 1=yes", UC_NONE},
/* 34 */    {"CFRZR", "Categorical freezing rain", "0=no, 1=yes", UC_NONE},
/* 35 */    {"CICEP", "Categorical ice pellets", "0=no, 1=yes", UC_NONE},
/* 36 */    {"CSNOW", "Categorical snow", "0=no, 1=yes", UC_NONE},
/* 37 */    {"CPRAT", "Convective precipitation rate", "kg/(m^2*s)", UC_NONE},
/* 38 */    {"MCONV", "Horizontal moisture divergence", "kg/(kg*s)", UC_NONE},
/* 39 */    {"CPOFP", "Percent frozen precipitation", "%", UC_NONE},
/* 40 */    {"PEVAP", "Potential evaporation", "kg/m^2", UC_NONE},
/* 41 */    {"PEVPR", "Potential evaporation rate", "W/m^2", UC_NONE},
/* 42 */    {"SNOWC", "Snow Cover", "%", UC_NONE},
/* 43 */    {"FRAIN", "Rain fraction of total cloud water", "-", UC_NONE},
/* 44 */    {"RIME", "Rime factor", "-", UC_NONE},
/* 45 */    {"TCOLR", "Total column integrated rain", "kg/m^2", UC_NONE},
/* 46 */    {"TCOLS", "Total column integrated snow", "kg/m^2", UC_NONE},
};

/* GRIB2 Code table 4.2 : 0.2 */
static const GRIB2ParmTable MeteoMoment[] = {
   /* 0 */ {"WDIR", "Wind direction (from which blowing)", "deg true",
            UC_NONE}, /* Need NDFD override WindDir */
   /* 1 */ {"WIND", "Wind speed", "m/s", UC_MS2Knots}, /* Need NDFD override WindSpd */
   /* 2 */ {"UGRD", "u-component of wind", "m/s", UC_NONE},
   /* 3 */ {"VGRD", "v-component of wind", "m/s", UC_NONE},
   /* 4 */ {"STRM", "Stream function", "(m^2)/s", UC_NONE},
   /* 5 */ {"VPOT", "Velocity potential", "(m^2)/s", UC_NONE},
   /* 6 */ {"MNTSF", "Montgomery stream function", "(m^2)/(s^2)", UC_NONE},
   /* 7 */ {"SGCVV", "Sigma coordinate vertical velocity", "1/s", UC_NONE},
   /* 8 */ {"VVEL", "Vertical velocity (pressure)", "Pa/s", UC_NONE}, /* NCEP override WEL?  */
   /* 9 */ {"DZDT", "Verical velocity (geometric)", "m/s", UC_NONE},
   /* 10 */ {"ABSV", "Absolute vorticity", "1/s", UC_NONE},
   /* 11 */ {"ABSD", "Absolute divergence", "1/s", UC_NONE},
   /* 12 */ {"RELV", "Relative vorticity", "1/s", UC_NONE},
   /* 13 */ {"RELD", "Relative divergence", "1/s", UC_NONE},
   /* 14 */ {"PV", "Potential vorticity", "K(m^2)/(kg s)", UC_NONE},
   /* 15 */ {"VUCSH", "Vertical u-component shear", "1/s", UC_NONE},
   /* 16 */ {"VVCSH", "Vertical v-component shear", "1/s", UC_NONE},
   /* 17 */ {"UFLX", "Momentum flux; u component", "N/(m^2)", UC_NONE},
   /* 18 */ {"VFLX", "Momentum flux; v component", "N/(m^2)", UC_NONE},
   /* 19 */ {"WMIXE", "Wind mixing energy", "J", UC_NONE},
   /* 20 */ {"BLYDP", "Boundary layer dissipation", "W/(m^2)", UC_NONE},
   /* 21 */ {"", "Maximum wind speed", "m/s", UC_NONE},
   /* 22 */ {"GUST", "Wind speed (gust)", "m/s", UC_MS2Knots},  /* GUST? */
   /* 23 */ {"", "u-component of wind (gust)", "m/s", UC_NONE},
   /* 24 */ {"", "v-component of wind (gust)", "m/s", UC_NONE},
/* 25 */    {"VWSH", "Vertical speed shear", "1/s", UC_NONE},
/* 26 */    {"MFLX", "Horizontal momentum flux", "N/(m^2)", UC_NONE},
/* 27 */    {"USTM", "U-component storm motion", "m/s", UC_NONE},
/* 28 */    {"VSTM", "V-component storm motion", "m/s", UC_NONE},
/* 29 */    {"CD", "Drag coefficient", "-", UC_NONE},
/* 30 */    {"FRICV", "Frictional velocity", "m/s", UC_NONE},
};

/* GRIB2 Code table 4.2 : 0.3 */
static const GRIB2ParmTable MeteoMass[] = {
   /* 0 */ {"PRES", "Pressure", "Pa", UC_NONE},
   /* 1 */ {"PRMSL", "Pressure reduced to MSL", "Pa", UC_NONE},
   /* 2 */ {"PTEND", "Pressure tendency", "Pa/s", UC_NONE},
   /* 3 */ {"ICAHT", "ICAO Standard Atmosphere Reference Height", "m", UC_NONE},
   /* 4 */ {"GP", "Geopotential", "(m^2)/(s^2)", UC_NONE},
   /* 5 */ {"HGT", "Geopotential height", "gpm", UC_NONE},
   /* 6 */ {"DIST", "Geometric Height", "m", UC_NONE},
   /* 7 */ {"HSTDV", "Standard deviation of height", "m", UC_NONE},
   /* 8 */ {"PRESA", "Pressure anomaly", "Pa", UC_NONE},
   /* 9 */ {"GPA", "Geopotential height anomaly", "gpm", UC_NONE},
   /* 10 */ {"DEN", "Density", "kg/(m^3)", UC_NONE},
   /* 11 */ {"", "Altimeter setting", "Pa", UC_NONE},
   /* 12 */ {"", "Thickness", "m", UC_NONE},
   /* 13 */ {"", "Pressure altitude", "m", UC_NONE},
   /* 14 */ {"", "Density altitude", "m", UC_NONE},
/* 15 */    {"5WAVH", "5-wave geopotential height", "gpm", UC_NONE},
/* 16 */    {"U-GWD", "Zonal flux of gravity wave stress", "N/(m^2)", UC_NONE},
/* 17 */    {"V-GWD", "Meridional flux of gravity wave stress", "N/(m^2)", UC_NONE},
/* 18 */    {"HPBL", "Planetary boundary layer height", "m", UC_NONE},
/* 19 */    {"5WAVA", "5-Wave geopotential height anomaly", "gpm", UC_NONE},
};

/* GRIB2 Code table 4.2 : 0.4 */
static const GRIB2ParmTable MeteoShortRadiate[] = {
   /* 0 */ {"NSWRS", "Net short-wave radiation flux (surface)", "W/(m^2)", UC_NONE},
   /* 1 */ {"NSWRT", "Net short-wave radiation flux (top of atmosphere)",
            "W/(m^2)", UC_NONE},
   /* 2 */ {"SWAVR", "Short wave radiation flux", "W/(m^2)", UC_NONE},
   /* 3 */ {"GRAD", "Global radiation flux", "W/(m^2)", UC_NONE},
   /* 4 */ {"BRTMP", "Brightness temperature", "K", UC_NONE},
   /* 5 */ {"LWRAD", "Radiance (with respect to wave number)", "W/(m sr)", UC_NONE},
   /* 6 */ {"SWRAD", "Radiance (with respect to wave length)", "W/(m^3 sr)", UC_NONE},
/* 7 */    {"DSWRF", "Downward short-wave radiation flux", "W/(m^2)", UC_NONE},
/* 8 */    {"USWRF", "Upward short-wave radiation flux", "W/(m^2)", UC_NONE},
};

/* GRIB2 Code table 4.2 : 0.5 */
static const GRIB2ParmTable MeteoLongRadiate[] = {
   /* 0 */ {"NLWRS", "Net long wave radiation flux (surface)", "W/(m^2)", UC_NONE},
   /* 1 */ {"NLWRT", "Net long wave radiation flux (top of atmosphere)",
            "W/(m^2)", UC_NONE},
   /* 2 */ {"LWAVR", "Long wave radiation flux", "W/(m^2)", UC_NONE},
/* 3 */    {"DLWRF", "Downward Long-Wave Rad. Flux", "W/(m^2)", UC_NONE},
/* 4 */    {"ULWRF", "Upward Long-Wave Rad. Flux", "W/(m^2)", UC_NONE},
};

/* GRIB2 Code table 4.2 : 0.6 */
static const GRIB2ParmTable MeteoCloud[] = {
   /* 0 */ {"CICE", "Cloud Ice", "kg/(m^2)", UC_NONE},
   /* 1 */ {"TCDC", "Total cloud cover", "%", UC_NONE}, /* Need NDFD override Sky */
   /* 2 */ {"CDCON", "Convective cloud cover", "%", UC_NONE},
   /* 3 */ {"LCDC", "Low cloud cover", "%", UC_NONE},
   /* 4 */ {"MCDC", "Medium cloud cover", "%", UC_NONE},
   /* 5 */ {"HCDC", "High cloud cover", "%", UC_NONE},
   /* 6 */ {"CWAT", "Cloud water", "kg/(m^2)", UC_NONE},
   /* 7 */ {"", "Cloud amount", "%", UC_NONE},
   /* 8 */ {"", "Cloud type", "(0 clear, 1 Cumulonimbus, 2 Stratus, "
            "3 Stratocumulus, 4 Cumulus, 5 Altostratus, 6 Nimbostratus, "
            "7 Altocumulus, 8 Cirrostratus, 9 Cirrocumulus, 10 Cirrus, "
            "11 Cumulonimbus (fog), 12 Stratus (fog), 13 Stratocumulus (fog),"
            " 14 Cumulus (fog), 15 Altostratus (fog), 16 Nimbostratus (fog), "
            "17 Altocumulus (fog), 18 Cirrostratus (fog), "
            "19 Cirrocumulus (fog), 20 Cirrus (fog), 191 unknown, "
            "255 missing)", UC_NONE},
   /* 9 */ {"", "Thunderstorm maximum tops", "m", UC_NONE},
   /* 10 */ {"", "Thunderstorm coverage", "(0 none, 1 isolated (1%-2%), "
             "2 few (3%-15%), 3 scattered (16%-45%), 4 numerous (> 45%), "
             "255 missing)", UC_NONE},
   /* 11 */ {"", "Cloud base", "m", UC_NONE},
   /* 12 */ {"", "Cloud top", "m", UC_NONE},
   /* 13 */ {"", "Ceiling", "m", UC_NONE},
/* 14 */    {"CDLYR", "Non-convective cloud cover", "%", UC_NONE},
/* 15 */    {"CWORK", "Cloud work function", "J/kg", UC_NONE},
/* 16 */    {"CUEFI", "Convective cloud efficiency", "-", UC_NONE},
/* 17 */    {"TCOND", "Total condensate", "kg/kg", UC_NONE},
/* 18 */    {"TCOLW", "Total column-integrated cloud water", "kg/(m^2)", UC_NONE},
/* 19 */    {"TCOLI", "Total column-integrated cloud ice", "kg/(m^2)", UC_NONE},
/* 20 */    {"TCOLC", "Total column-integrated condensate", "kg/(m^2)", UC_NONE},
/* 21 */    {"FICE", "Ice fraction of total condensate", "-", UC_NONE},
};

/* GRIB2 Code table 4.2 : 0.7 */
/* NCEP capitalized items 6, 7, 8 */
static const GRIB2ParmTable MeteoStability[] = {
   /* 0 */ {"PLI", "Parcel lifted index (to 500 hPa)", "K", UC_NONE},
   /* 1 */ {"BLI", "Best lifted index (to 500 hPa)", "K", UC_NONE},
   /* 2 */ {"KX", "K index", "K", UC_NONE},
   /* 3 */ {"", "KO index", "K", UC_NONE},
   /* 4 */ {"", "Total totals index", "K", UC_NONE},
   /* 5 */ {"SX", "Sweat index", "numeric", UC_NONE},
   /* 6 */ {"CAPE", "Convective available potential energy", "J/kg", UC_NONE},
   /* 7 */ {"CIN", "Convective inhibition", "J/kg", UC_NONE},
   /* 8 */ {"HLCY", "Storm relative helicity", "J/kg", UC_NONE},
   /* 9 */ {"", "Energy helicity index", "numeric", UC_NONE},
// TODO: Spelling fifted.
/* 10 */   {"LFTX", "Surface fifted index", "K", UC_NONE},
/* 11 */   {"4LFTX", "Best (4-layer) lifted index", "K", UC_NONE},
/* 12 */   {"RI", "Richardson number", "-", UC_NONE},
};

/* GRIB2 Code table 4.2 : 0.13 */
static const GRIB2ParmTable MeteoAerosols[] = {
   /* 0 */ {"", "Aerosol type", "(0 Aerosol not present, 1 Aerosol present, "
            "255 missing)", UC_NONE},
};

/* GRIB2 Code table 4.2 : 0.14 */
static const GRIB2ParmTable MeteoGases[] = {
   /* 0 */ {"TOZNE", "Total ozone", "Dobson", UC_NONE},
/* 1 */    {"O3MR", "Ozone Mixing Ratio", "kg/kg", UC_NONE},
};

/* GRIB2 Code table 4.2 : 0.15 */
static const GRIB2ParmTable MeteoRadar[] = {
   /* 0 */ {"", "Base spectrum width", "m/s", UC_NONE},
   /* 1 */ {"", "Base reflectivity", "dB", UC_NONE},
   /* 2 */ {"", "Base radial velocity", "m/s", UC_NONE},
   /* 3 */ {"", "Vertically-integrated liquid", "kg/m", UC_NONE},
   /* 4 */ {"", "Layer-maximum base reflectivity", "dB", UC_NONE},
   /* 5 */ {"", "Precipitation", "kg/(m^2)", UC_NONE},
   /* 6 */ {"RDSP1", "Radar spectra (1)", "-", UC_NONE},
   /* 7 */ {"RDSP2", "Radar spectra (2)", "-", UC_NONE},
   /* 8 */ {"RDSP3", "Radar spectra (3)", "-", UC_NONE},
};

/* GRIB2 Code table 4.2 : 0.18 */
static const GRIB2ParmTable MeteoNuclear[] = {
   /* 0 */ {"", "Air concentration of Caesium 137", "Bq/(m^3)", UC_NONE},
   /* 1 */ {"", "Air concentration of Iodine 131", "Bq/(m^3)", UC_NONE},
   /* 2 */ {"", "Air concentration of radioactive pollutant", "Bq/(m^3)", UC_NONE},
   /* 3 */ {"", "Ground deposition of Caesium 137", "Bq/(m^2)", UC_NONE},
   /* 4 */ {"", "Ground deposition of Iodine 131", "Bq/(m^2)", UC_NONE},
   /* 5 */ {"", "Ground deposition of radioactive pollutant", "Bq/(m^2)", UC_NONE},
   /* 6 */ {"", "Time-integrated air concentration of caesium pollutant",
            "(Bq s)/(m^3)", UC_NONE},
   /* 7 */ {"", "Time-integrated air concentration of iodine pollutant",
            "(Bq s)/(m^3)", UC_NONE},
   /* 8 */ {"", "Time-integrated air concentration of radioactive pollutant",
            "(Bq s)/(m^3)", UC_NONE},
};

/* GRIB2 Code table 4.2 : 0.19 */
/* NCEP capitalized items 11 */
static const GRIB2ParmTable MeteoAtmos[] = {
   /* 0 */ {"VIS", "Visibility", "m", UC_NONE},
   /* 1 */ {"ALBDO", "Albedo", "%", UC_NONE},
   /* 2 */ {"TSTM", "Thunderstorm probability", "%", UC_NONE},
   /* 3 */ {"MIXHT", "mixed layer depth", "m", UC_NONE},
   /* 4 */ {"", "Volcanic ash", "(0 not present, 1 present, 255 missing)", UC_NONE},
   /* 5 */ {"", "Icing top", "m", UC_NONE},
   /* 6 */ {"", "Icing base", "m", UC_NONE},
   /* 7 */ {"", "Icing", "(0 None, 1 Light, 2 Moderate, 3 Severe, "
            "255 missing)", UC_NONE},
   /* 8 */ {"", "Turbulance top", "m", UC_NONE},
   /* 9 */ {"", "Turbulence base", "m", UC_NONE},
   /* 10 */ {"", "Turbulance", "(0 None(smooth), 1 Light, 2 Moderate, "
             "3 Severe, 4 Extreme, 255 missing)", UC_NONE},
   /* 11 */ {"TKE", "Turbulent Kinetic Energy", "J/kg", UC_NONE},
   /* 12 */ {"", "Planetary boundary layer regime", "(0 Reserved, 1 Stable, "
             "2 Mechanically driven turbulence, 3 Forced convection, "
             "4 Free convection, 255 missing)", UC_NONE},
   /* 13 */ {"", "Contrail intensity", "(0 Contrail not present, "
             "1 Contrail present, 255 missing)", UC_NONE},
   /* 14 */ {"", "Contrail engine type", "(0 Low bypass, 1 High bypass, "
             "2 Non bypass, 255 missing)", UC_NONE},
   /* 15 */ {"", "Contrail top", "m", UC_NONE},
   /* 16 */ {"", "Contrail base", "m", UC_NONE},
/* 17 */    {"MXSALB", "Maximum snow albedo", "%", UC_NONE},
/* 18 */    {"SNFALB", "Snow free albedo", "%", UC_NONE},
};

/* GRIB2 Code table 4.2 : 0.253 or 0.190 (Document is inconsistent.) */
static const GRIB2ParmTable MeteoText[] = {
   /* 0 */ {"", "Arbitrary text string", "CCITTIA5", UC_NONE},
};

static const GRIB2ParmTable MeteoMisc[] = {
   /* 0 */ {"TSEC", "Seconds prior to initial reference time (defined in Section"
            " 1)", "s", UC_NONE},
};

/* GRIB2 Code table 4.2 : 1.0 */
static const GRIB2ParmTable HydroBasic[] = {
   /* 0 */ {"", "Flash flood guidance", "kg/(m^2)", UC_NONE},
   /* 1 */ {"", "Flash flood runoff", "kg/(m^2)", UC_NONE},
   /* 2 */ {"", "Remotely sensed snow cover", "(50 no-snow/no-cloud, "
            "100 Clouds, 250 Snow, 255 missing)", UC_NONE},
   /* 3 */ {"", "Elevation of snow covered terrain", "(0-90 elevation in "
            "increments of 100m, 254 clouds, 255 missing)", UC_NONE},
   /* 4 */ {"", "Snow water equivalent percent of normal", "%", UC_NONE},
/* 5 */    {"BGRUN", "Baseflow-groundwater runoff", "kg/(m^2)", UC_NONE},
/* 6 */    {"SSRUN", "Storm surface runoff", "kg/(m^2)", UC_NONE},
};

/* GRIB2 Code table 4.2 : 1.1 */
static const GRIB2ParmTable HydroProb[] = {
   /* 0 */ {"", "Conditional percent precipitation amount fractile for an "
            "overall period", "kg/(m^2)", UC_NONE},
   /* 1 */ {"", "Percent precipitation in a sub-period of an overall period",
            "%", UC_NONE},
   /* 2 */ {"PoP", "Probability of 0.01 inch of precipitation", "%", UC_NONE},
};

/* GRIB2 Code table 4.2 : 2.0 */
static const GRIB2ParmTable LandVeg[] = {
   /* 0 */ {"LAND", "Land cover (1=land; 2=sea)", "Proportion", UC_NONE},
   /* 1 */ {"SFCR", "Surface roughness", "m", UC_NONE}, /*NCEP override SFRC? */
   /* 2 */ {"TSOIL", "Soil temperature", "K", UC_NONE},
   /* 3 */ {"SOILM", "Soil moisture content", "kg/(m^2)", UC_NONE},
   /* 4 */ {"VEG", "Vegetation", "%", UC_NONE},
   /* 5 */ {"WATR", "Water runoff", "kg/(m^2)", UC_NONE},
   /* 6 */ {"", "Evapotranspiration", "1/(kg^2 s)", UC_NONE},
   /* 7 */ {"", "Model terrain height", "m", UC_NONE},
   /* 8 */ {"", "Land use", "(1 Urban land, 2 agriculture, 3 Range Land, "
            "4 Deciduous forest, 5 Coniferous forest, 6 Forest/wetland, "
            "7 Water, 8 Wetlands, 9 Desert, 10 Tundra, 11 Ice, "
            "12 Tropical forest, 13 Savannah)", UC_NONE},
/* 9 */    {"SOILW", "Volumetric soil moisture content", "fraction", UC_NONE},
/* 10 */   {"GFLUX", "Ground heat flux", "W/(m^2)", UC_NONE},
/* 11 */   {"MSTAV", "Moisture availability", "%", UC_NONE},
/* 12 */   {"SFEXC", "Exchange coefficient", "(kg/(m^3))(m/s)", UC_NONE},
/* 13 */   {"CNWAT", "Plant canopy surface water", "kg/(m^2)", UC_NONE},
/* 14 */   {"BMIXL", "Blackadar's mixing length scale", "m", UC_NONE},
/* 15 */   {"CCOND", "Canopy conductance", "m/s", UC_NONE},
/* 16 */   {"RSMIN", "Minimal stomatal resistance", "s/m", UC_NONE},
/* 17 */   {"WILT", "Wilting point", "fraction", UC_NONE},
/* 18 */   {"RCS", "Solar parameter in canopy conductance", "fraction", UC_NONE},
/* 19 */   {"RCT", "Temperature parameter in canopy conductance", "fraction", UC_NONE},
/* 20 */   {"RCSOL", "Soil moisture parameter in canopy conductance", "fraction", UC_NONE},
/* 21 */   {"RCQ", "Humidity parameter in canopy conductance", "fraction", UC_NONE},
};

/* GRIB2 Code table 4.2 : 2.3 */
/* NCEP changed 0 to be "Soil type (as in Zobler)" I ignored them */
static const GRIB2ParmTable LandSoil[] = {
   /* 0 */ {"SOTYP", "Soil type", "(1 Sand, 2 Loamy sand, 3 Sandy loam, "
            "4 Silt loam, 5 Organic (redefined), 6 Sandy clay loam, "
            "7 Silt clay loam, 8 Clay loam, 9 Sandy clay, 10 Silty clay, "
            "11 Clay)", UC_NONE},
   /* 1 */ {"", "Upper layer soil temperature", "K", UC_NONE},
   /* 2 */ {"", "Upper layer soil moisture", "kg/(m^3)", UC_NONE},
   /* 3 */ {"", "Lower layer soil moisture", "kg/(m^3)", UC_NONE},
   /* 4 */ {"", "Bottom layer soil temperature", "K", UC_NONE},
/* 5 */ {"SOILL", "Liquid volumetric soil moisture (non-frozen)", "fraction", UC_NONE},
/* 6 */ {"RLYRS", "Number of soil layers in root zone", "-", UC_NONE},
/* 7 */ {"SMREF", "Transpiration stress-onset (soil moisture)", "fraction", UC_NONE},
/* 8 */ {"SMDRY", "Direct evaporation cease (soil moisture)", "fraction", UC_NONE},
/* 9 */ {"POROS", "Soil porosity", "fraction", UC_NONE},
};

/* GRIB2 Code table 4.2 : 3.0 */
static const GRIB2ParmTable SpaceImage[] = {
   /* 0 */ {"", "Scaled radiance", "numeric", UC_NONE},
   /* 1 */ {"", "Scaled albedo", "numeric", UC_NONE},
   /* 2 */ {"", "Scaled brightness temperature", "numeric", UC_NONE},
   /* 3 */ {"", "Scaled precipitable water", "numeric", UC_NONE},
   /* 4 */ {"", "Scaled lifted index", "numeric", UC_NONE},
   /* 5 */ {"", "Scaled cloud top pressure", "numeric", UC_NONE},
   /* 6 */ {"", "Scaled skin temperature", "numeric", UC_NONE},
   /* 7 */ {"", "Cloud mask", "(0 clear over water, 1 clear over land, "
            "2 cloud)", UC_NONE},
/* 8 */ {"", "Pixel scene type", "(0 No scene, 1 needle, 2 broad-leafed, "
         "3 Deciduous needle, 4 Deciduous broad-leafed, 5 Deciduous mixed, "
         "6 Closed shrub, 7 Open shrub, 8 Woody savannah, 9 Savannah, "
         "10 Grassland, 11 wetland, 12 Cropland, 13 Urban, 14 crops, "
         "15 snow, 16 Desert, 17 Water, 18 Tundra, 97 Snow on land, "
         "98 Snow on water, 99 Sun-glint, 100 General cloud, "
         "101 (fog, Stratus), 102 Stratocumulus, 103 Low cloud, "
         "104 Nimbotratus, 105 Altostratus, 106 Medium cloud, 107 Cumulus, "
         "108 Cirrus, 109 High cloud, 110 Unknown cloud)", UC_NONE},
};

/* GRIB2 Code table 4.2 : 3.1 */
static const GRIB2ParmTable SpaceQuantitative[] = {
   /* 0 */ {"", "Estimated precipitation", "kg/(m^2)", UC_NONE},
/* 1 */ {"", "Instantaneous rain rate", "kg/(m^2*s)", UC_NONE},
/* 2 */ {"", "Cloud top height", "kg/(m^2*s)", UC_NONE},
/* 3 */ {"", "Cloud top height quality indicator", "(0 Nominal cloud top "
         "height quality, 1 Fog in segment, 2 Poor quality height estimation "
         "3 Fog in segment and poor quality height estimation)", UC_NONE},
/* 4 */ {"", "Estimated u component of wind", "m/s", UC_NONE},
/* 5 */ {"", "Estimated v component of wind", "m/s", UC_NONE},
};

/* GRIB2 Code table 4.2 : 10.0 */
static const GRIB2ParmTable OceanWaves[] = {
   /* 0 */ {"WVSP1", "Wave spectra (1)", "-", UC_NONE},
   /* 1 */ {"WVSP2", "Wave spectra (2)", "-", UC_NONE},
   /* 2 */ {"WVSP3", "Wave spectra (3)", "-", UC_NONE},
   /* 3 */ {"HTSGW", "Significant height of combined wind waves and swell", "m", UC_NONE},
   /* 4 */ {"WVDIR", "Direction of wind waves", "Degree true", UC_NONE},
   /* 5 */ {"WVHGT", "Significant height of wind waves", "m", UC_M2Feet}, /* NDFD override needed WaveHeight */
   /* 6 */ {"WVPER", "Mean period of wind waves", "s", UC_NONE},
   /* 7 */ {"SWDIR", "Direction of swell waves", "Degree true", UC_NONE},
   /* 8 */ {"SWELL", "Significant height of swell waves", "m", UC_NONE},
   /* 9 */ {"SWPER", "Mean period of swell waves", "s", UC_NONE},
   /* 10 */ {"DIRPW", "Primary wave direction", "Degree true", UC_NONE},
   /* 11 */ {"PERPW", "Primary wave mean period", "s", UC_NONE},
   /* 12 */ {"DIRSW", "Secondary wave direction", "Degree true", UC_NONE},
   /* 13 */ {"PERSW", "Secondary wave mean period", "s", UC_NONE},
};

/* GRIB2 Code table 4.2 : 10.1 */
static const GRIB2ParmTable OceanCurrents[] = {
   /* 0 */ {"DIRC", "Current direction", "Degree true", UC_NONE},
   /* 1 */ {"SPC", "Current speed", "m/s", UC_NONE},
   /* 2 */ {"UOGRD", "u-component of current", "m/s", UC_NONE},
   /* 3 */ {"VOGRD", "v-component of current", "m/s", UC_NONE},
};

/* GRIB2 Code table 4.2 : 10.2 */
static const GRIB2ParmTable OceanIce[] = {
   /* 0 */ {"ICEC", "Ice cover", "Proportion", UC_NONE},
   /* 1 */ {"ICETK", "Ice thinkness", "m", UC_NONE},
   /* 2 */ {"DICED", "Direction of ice drift", "Degree true", UC_NONE},
   /* 3 */ {"SICED", "Speed of ice drift", "m/s", UC_NONE},
   /* 4 */ {"UICE", "u-component of ice drift", "m/s", UC_NONE},
   /* 5 */ {"VICE", "v-component of ice drift", "m/s", UC_NONE},
   /* 6 */ {"ICEG", "Ice growth rate", "m/s", UC_NONE},
   /* 7 */ {"ICED", "Ice divergence", "1/s", UC_NONE},
};

/* GRIB2 Code table 4.2 : 10.3 */
static const GRIB2ParmTable OceanSurface[] = {
   /* 0 */ {"WTMP", "Water temperature", "K", UC_NONE},
   /* 1 */ {"DSLM", "Deviation of sea level from mean", "m", UC_NONE},
};

#ifdef unused
/* GRIB2 Code table 4.2 : 10.4 */
static const GRIB2ParmTable OceanSubSurface[] = {
   /* 0 */ {"MTHD", "Main thermocline depth", "m", UC_NONE},
   /* 1 */ {"MTHA", "Main thermocline anomaly", "m", UC_NONE},
   /* 2 */ {"TTHDP", "Transient thermocline depth", "m", UC_NONE},
   /* 3 */ {"SALTY", "Salinity", "kg/kg", UC_NONE},
};
#endif

/* *INDENT-ON* */

/*****************************************************************************
 * Choose_GRIB2ParmTable() --
 *
 * Arthur Taylor / MDL
 *
 * PURPOSE
 *   Chooses the correct Parameter table depending on what is in the GRIB2
 * message's "Product Definition Section".
 *
 * ARGUMENTS
 * prodType = The product type (meteo, hydro, land, space, ocean, etc) (In)
 *      cat = The category inside the product (Input)
 * tableLen = The length of the returned table (Output)
 *
 * FILES/DATABASES: None
 *
 * RETURNS: ParmTable (appropriate parameter table.)
 *
 * HISTORY
 *   1/2004 Arthur Taylor (MDL/RSIS): Created
 *
 * NOTES
 *****************************************************************************
 */
static const GRIB2ParmTable *Choose_GRIB2ParmTable (int prodType, int cat,
                                              size_t *tableLen)
{
   enum { METEO_TEMP = 0, METEO_MOIST = 1, METEO_MOMENT = 2, METEO_MASS = 3,
      METEO_SW_RAD = 4, METEO_LW_RAD = 5, METEO_CLOUD = 6,
      METEO_THERMO_INDEX = 7, METEO_KINEMATIC_INDEX = 8, METEO_TEMP_PROB = 9,
      METEO_MOISTURE_PROB = 10, METEO_MOMENT_PROB = 11, METEO_MASS_PROB = 12,
      METEO_AEROSOL = 13, METEO_GAS = 14, METEO_RADAR = 15,
      METEO_RADAR_IMAGERY = 16, METEO_ELECTRO = 17, METEO_NUCLEAR = 18,
      METEO_ATMOS = 19, METEO_CCITT = 190, METEO_MISC = 191,
      METEO_CCITT2 = 253
   };
   enum { HYDRO_BASIC = 0, HYDRO_PROB = 1 };
   enum { LAND_VEG = 0, LAND_SOIL = 3 };
   enum { SPACE_IMAGE = 0, SPACE_QUANTIT = 1 };
   enum { OCEAN_WAVES = 0, OCEAN_CURRENTS = 1, OCEAN_ICE = 2, OCEAN_SURF = 3,
      OCEAN_SUBSURF = 4
   };

   switch (prodType) {
      case 0:          /* Meteo type. */
         switch (cat) {
            case METEO_TEMP:
               *tableLen = sizeof (MeteoTemp) / sizeof (GRIB2ParmTable);
               return &MeteoTemp[0];
            case METEO_MOIST:
               *tableLen = sizeof (MeteoMoist) / sizeof (GRIB2ParmTable);
               return &MeteoMoist[0];
            case METEO_MOMENT:
               *tableLen = sizeof (MeteoMoment) / sizeof (GRIB2ParmTable);
               return &MeteoMoment[0];
            case METEO_MASS:
               *tableLen = sizeof (MeteoMass) / sizeof (GRIB2ParmTable);
               return &MeteoMass[0];
            case METEO_SW_RAD:
               *tableLen = (sizeof (MeteoShortRadiate) /
                            sizeof (GRIB2ParmTable));
               return &MeteoShortRadiate[0];
            case METEO_LW_RAD:
               *tableLen = (sizeof (MeteoLongRadiate) /
                            sizeof (GRIB2ParmTable));
               return &MeteoLongRadiate[0];
            case METEO_CLOUD:
               *tableLen = sizeof (MeteoCloud) / sizeof (GRIB2ParmTable);
               return &MeteoCloud[0];
            case METEO_THERMO_INDEX:
               *tableLen = sizeof (MeteoStability) / sizeof (GRIB2ParmTable);
               return &MeteoStability[0];
            case METEO_KINEMATIC_INDEX:
            case METEO_TEMP_PROB:
            case METEO_MOISTURE_PROB:
            case METEO_MOMENT_PROB:
            case METEO_MASS_PROB:
               *tableLen = 0;
               return NULL;
            case METEO_AEROSOL:
               *tableLen = sizeof (MeteoAerosols) / sizeof (GRIB2ParmTable);
               return &MeteoAerosols[0];
            case METEO_GAS:
               *tableLen = sizeof (MeteoGases) / sizeof (GRIB2ParmTable);
               return &MeteoGases[0];
            case METEO_RADAR:
               *tableLen = sizeof (MeteoRadar) / sizeof (GRIB2ParmTable);
               return &MeteoRadar[0];
            case METEO_RADAR_IMAGERY:
            case METEO_ELECTRO:
               *tableLen = 0;
               return NULL;
            case METEO_NUCLEAR:
               *tableLen = sizeof (MeteoNuclear) / sizeof (GRIB2ParmTable);
               return &MeteoNuclear[0];
            case METEO_ATMOS:
               *tableLen = sizeof (MeteoAtmos) / sizeof (GRIB2ParmTable);
               return &MeteoAtmos[0];
            case METEO_CCITT:
            case METEO_CCITT2:
               *tableLen = sizeof (MeteoText) / sizeof (GRIB2ParmTable);
               return &MeteoText[0];
            case METEO_MISC:
               *tableLen = sizeof (MeteoMisc) / sizeof (GRIB2ParmTable);
               return &MeteoMisc[0];
            default:
               *tableLen = 0;
               return NULL;
         }
      case 1:          /* Hydro type. */
         switch (cat) {
            case HYDRO_BASIC:
               *tableLen = sizeof (HydroBasic) / sizeof (GRIB2ParmTable);
               return &HydroBasic[0];
            case HYDRO_PROB:
               *tableLen = sizeof (HydroProb) / sizeof (GRIB2ParmTable);
               return &HydroProb[0];
            default:
               *tableLen = 0;
               return NULL;
         }
      case 2:          /* Land type. */
         switch (cat) {
            case LAND_VEG:
               *tableLen = sizeof (LandVeg) / sizeof (GRIB2ParmTable);
               return &LandVeg[0];
            case LAND_SOIL:
               *tableLen = sizeof (LandSoil) / sizeof (GRIB2ParmTable);
               return &LandSoil[0];
            default:
               *tableLen = 0;
               return NULL;
         }
      case 3:          /* Space type. */
         switch (cat) {
            case SPACE_IMAGE:
               *tableLen = sizeof (SpaceImage) / sizeof (GRIB2ParmTable);
               return &SpaceImage[0];
            case SPACE_QUANTIT:
               *tableLen = (sizeof (SpaceQuantitative) /
                            sizeof (GRIB2ParmTable));
               return &SpaceQuantitative[0];
            default:
               *tableLen = 0;
               return NULL;
         }
      case 10:         /* ocean type. */
         switch (cat) {
            case OCEAN_WAVES:
               *tableLen = sizeof (OceanWaves) / sizeof (GRIB2ParmTable);
               return &OceanWaves[0];
            case OCEAN_CURRENTS:
               *tableLen = sizeof (OceanCurrents) / sizeof (GRIB2ParmTable);
               return &OceanCurrents[0];
            case OCEAN_ICE:
               *tableLen = sizeof (OceanIce) / sizeof (GRIB2ParmTable);
               return &OceanIce[0];
            case OCEAN_SURF:
               *tableLen = sizeof (OceanSurface) / sizeof (GRIB2ParmTable);
               return &OceanSurface[0];
            default:
               *tableLen = 0;
               return NULL;
         }
      default:
         *tableLen = 0;
         return NULL;
   }
}

/* *INDENT-OFF* */
static const NDFD_AbrevOverideTable NDFD_Overide[] = {
   /*  0 */ {"TMP", "T"},
   /*  1 */ {"TMAX", "MaxT"},
   /*  2 */ {"TMIN", "MinT"},
   /*  3 */ {"DPT", "Td"},
   /*  4 */ {"APCP", "QPF"},
   /* Don't need SNOD for now. */
   /*  5 */ /* {"SNOD", "SnowDepth"}, */
   /*  6 */ {"WDIR", "WindDir"},
   /*  7 */ {"WIND", "WindSpd"},
   /*  8 */ {"TCDC", "Sky"},
   /*  9 */ {"WVHGT", "WaveHeight"},
   /* 10 */ {"ASNOW", "SnowAmt"},
   /* 11 */ {"GUST", "WindGust"},
};

static const GRIB2LocalTable NDFD_LclTable[] = {
   /* 0 */ {0, 1, 192, "Wx", "Weather string", "-", UC_NONE},
   /* 1 */ {0, 0, 193, "ApparentT", "Apparent Temperature", "K", UC_K2F},
   /* 2 */ {0, 14, 192, "O3MR", "Ozone Mixing Ratio", "kg/kg", UC_NONE},
   /* 3 */ {0, 14, 193, "OZCON", "Ozone Concentration", "PPB", UC_NONE},
   /* grandfather'ed in a NDFD choice for POP. */
   /* 4 */ {0, 10, 8, "PoP12", "Prob of 0.01 In. of Precip", "%", UC_NONE},
           {0, 13, 194, "smokes", "Surface level smoke from fires",
            "log10(\xB5" "g/m^3)", UC_LOG10},
           {0, 13, 195, "smokec", "Average vertical column smoke from fires",
            "log10(\xB5" "g/m^3)", UC_LOG10},
   /* Arthur Added this to both NDFD and NCEP local tables. (5/1/2006) */
           {10, 3, 192, "Surge", "Hurricane Storm Surge", "m", UC_M2Feet},
           {10, 3, 193, "ETSurge", "Extra Tropical Storm Surge", "m", UC_M2Feet},
};

static const GRIB2LocalTable HPC_LclTable[] = {
   /* 0 */ {0, 1, 192, "HPC-Wx", "HPC Code", "-", UC_NONE},
};

/*
Updated this table last on 12/29/2005
Based on:
http://www.nco.ncep.noaa.gov/pmb/docs/grib2/GRIB2_parmeter_conversion_table.html
Better source is:
http://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_table4-1.shtml
For abreviations see:
http://www.nco.ncep.noaa.gov/pmb/docs/on388/table2.html

Updated again on 2/14/2006
Updated again on 3/15/2006
*/
static const GRIB2LocalTable NCEP_LclTable[] = {
   /*  0 */ {0, 0, 192, "SNOHF", "Snow Phase Change Heat Flux", "W/(m^2)", UC_NONE},
            {0, 0, 193, "TTRAD", "Temperature tendency by all radiation", "K/s", UC_NONE},

   /*  1 */ {0, 1, 192, "CRAIN", "Categorical Rain", "(0 no; 1 yes)", UC_NONE},
   /*  2 */ {0, 1, 193, "CFRZR", "Categorical Freezing Rain", "(0 no; 1 yes)", UC_NONE},
   /*  3 */ {0, 1, 194, "CICEP", "Categorical Ice Pellets", "(0 no; 1 yes)", UC_NONE},
   /*  4 */ {0, 1, 195, "CSNOW", "Categorical Snow", "(0 no; 1 yes)", UC_NONE},
   /*  5 */ {0, 1, 196, "CPRAT", "Convective Precipitation Rate", "kg/(m^2*s)", UC_NONE},
   /*  6 */ {0, 1, 197, "MCONV", "Horizontal Moisture Divergence", "kg/(kg*s)", UC_NONE},
/* Following was grandfathered in... Should use: 1, 1, 193 */
   /*  7 */ {0, 1, 198, "CPOFP", "Percent Frozen Precipitation", "%", UC_NONE},
   /*  8 */ {0, 1, 199, "PEVAP", "Potential Evaporation", "kg/(m^2)", UC_NONE},
   /*  9 */ {0, 1, 200, "PEVPR", "Potential Evaporation Rate", "W/(m^2)", UC_NONE},
   /* 10 */ {0, 1, 201, "SNOWC", "Snow Cover", "%", UC_NONE},
   /* 11 */ {0, 1, 202, "FRAIN", "Rain Fraction of Total Liquid Water", "-", UC_NONE},
/* FRIME -> RIME 12/29/2005 */
   /* 12 */ {0, 1, 203, "RIME", "Rime Factor", "-", UC_NONE},
   /* 13 */ {0, 1, 204, "TCOLR", "Total Column Integrated Rain", "kg/(m^2)", UC_NONE},
   /* 14 */ {0, 1, 205, "TCOLS", "Total Column Integrated Snow", "kg/(m^2)", UC_NONE},
            {0, 1, 206, "TIPD", "Total Icing Potential Diagnostic", "-", UC_NONE},
            {0, 1, 207, "NCIP", "Number concentration for ice particles", "-", UC_NONE},
            {0, 1, 208, "SNOT", "Snow temperature", "K", UC_NONE},

   /* 15 */ {0, 2, 192, "VWSH", "Vertical speed sheer", "1/s", UC_NONE},
   /* 16 */ {0, 2, 193, "MFLX", "Horizontal Momentum Flux", "N/(m^2)", UC_NONE},
   /* 17 */ {0, 2, 194, "USTM", "U-Component Storm Motion", "m/s", UC_NONE},
   /* 18 */ {0, 2, 195, "VSTM", "V-Component Storm Motion", "m/s", UC_NONE},
   /* 19 */ {0, 2, 196, "CD", "Drag Coefficient", "-", UC_NONE},
   /* 20 */ {0, 2, 197, "FRICV", "Frictional Velocity", "m/s", UC_NONE},

   /* 21 */ {0, 3, 192, "MSLET", "Mean Sea Level Pressure (Eta Reduction)", "Pa", UC_NONE},
   /* 22 */ {0, 3, 193, "5WAVH", "5-Wave Geopotential Height", "gpm", UC_NONE},
   /* 23 */ {0, 3, 194, "U-GWD", "Zonal Flux of Gravity Wave Stress", "N/(m^2)", UC_NONE},
   /* 24 */ {0, 3, 195, "V-GWD", "Meridional Flux of Gravity Wave Stress", "N/(m^2)", UC_NONE},
   /* 25 */ {0, 3, 196, "HPBL", "Planetary Boundary Layer Height", "m", UC_NONE},
   /* 26 */ {0, 3, 197, "5WAVA", "5-Wave Geopotential Height Anomaly", "gpm", UC_NONE},
            {0, 3, 198, "MSLMA", "Mean Sea Level Pressure (MAPS System Reduction)", "Pa", UC_NONE},
            {0, 3, 199, "TSLSA", "3-hr pressure tendency (Std. Atmos. Reduction)", "Pa/s", UC_NONE},
            {0, 3, 200, "PLPL", "Pressure of level from which parcel was lifted", "Pa", UC_NONE},

   /* 27 */ {0, 4, 192, "DSWRF", "Downward Short-Wave Rad. Flux", "W/(m^2)", UC_NONE},
   /* 28 */ {0, 4, 193, "USWRF", "Upward Short-Wave Rad. Flux", "W/(m^2)", UC_NONE},
            {0, 4, 194, "DUVB", "UV-B downward solar flux", "W/(m^2)", UC_NONE},
            {0, 4, 195, "CDUVB", "Clear sky UV-B downward solar flux", "W/(m^2)", UC_NONE},

   /* 29 */ {0, 5, 192, "DLWRF", "Downward Long-Wave Rad. Flux", "W/(m^2)", UC_NONE},
   /* 30 */ {0, 5, 193, "ULWRF", "Upward Long-Wave Rad. Flux", "W/(m^2)", UC_NONE},

   /* 31 */ {0, 6, 192, "CDLYR", "Non-Convective Cloud Cover", "%", UC_NONE},
   /* 32 */ {0, 6, 193, "CWORK", "Cloud Work Function", "J/kg", UC_NONE},
   /* 33 */ {0, 6, 194, "CUEFI", "Convective Cloud Efficiency", "-", UC_NONE},
   /* 34 */ {0, 6, 195, "TCOND", "Total Condensate", "kg/kg", UC_NONE},
   /* 35 */ {0, 6, 196, "TCOLW", "Total Column-Integrated Cloud Water", "kg/(m^2)", UC_NONE},
   /* 36 */ {0, 6, 197, "TCOLI", "Total Column-Integrated Cloud Ice", "kg/(m^2)", UC_NONE},
   /* 37 */ {0, 6, 198, "TCOLC", "Total Column-Integrated Condensate", "kg/(m^2)", UC_NONE},
   /* 38 */ {0, 6, 199, "FICE", "Ice fraction of total condensate", "-", UC_NONE},

   /* 39 */ {0, 7, 192, "LFTX", "Surface Lifted Index", "K", UC_NONE},
   /* 40 */ {0, 7, 193, "4LFTX", "Best (4 layer) Lifted Index", "K", UC_NONE},
   /* 41 */ {0, 7, 194, "RI", "Richardson Number", "-", UC_NONE},

            {0, 13, 192, "PMTC", "Particulate matter (coarse)", "\xB5" "g/m^3", UC_NONE},
            {0, 13, 193, "PMTF", "Particulate matter (fine)", "\xB5" "g/m^3", UC_NONE},
            {0, 13, 194, "LPMTF", "Particulate matter (fine)",
             "log10(\xB5" "g/m^3)", UC_LOG10},
            {0, 13, 195, "LIPMF", "Integrated column particulate matter "
             "(fine)", "log10(\xB5" "g/m^3)", UC_LOG10},

   /* 42 */ {0, 14, 192, "O3MR", "Ozone Mixing Ratio", "kg/kg", UC_NONE},
   /* 43 */ {0, 14, 193, "OZCON", "Ozone Concentration", "PPB", UC_NONE},
   /* 44 */ {0, 14, 194, "OZCAT", "Categorical Ozone Concentration", "-", UC_NONE},

            {0, 16, 192, "REFZR", "Derived radar reflectivity backscatter from rain", "mm^6/m^3", UC_NONE},
            {0, 16, 193, "REFZI", "Derived radar reflectivity backscatter from ice", "mm^6/m^3", UC_NONE},
            {0, 16, 194, "REFZC", "Derived radar reflectivity backscatter from parameterized convection", "mm^6/m^3", UC_NONE},
            {0, 16, 195, "REFD", "Derived radar reflectivity", "dB", UC_NONE},
            {0, 16, 196, "REFC", "Maximum / Composite radar reflectivity", "dB", UC_NONE},

            {0, 17, 192, "LTNG", "Lightning", "-", UC_NONE},

   /* 45 */ {0, 19, 192, "MXSALB", "Maximum Snow Albedo", "%", UC_NONE},
   /* 46 */ {0, 19, 193, "SNFALB", "Snow-Free Albedo", "%", UC_NONE},
            {0, 19, 194, "", "Slight risk convective outlook", "categorical", UC_NONE},
            {0, 19, 195, "", "Moderate risk convective outlook", "categorical", UC_NONE},
            {0, 19, 196, "", "High risk convective outlook", "categorical", UC_NONE},
            {0, 19, 197, "", "Tornado probability", "%", UC_NONE},
            {0, 19, 198, "", "Hail probability", "%", UC_NONE},
            {0, 19, 199, "", "Wind probability", "%", UC_NONE},
            {0, 19, 200, "", "Significant Tornado probability", "%", UC_NONE},
            {0, 19, 201, "", "Significant Hail probability", "%", UC_NONE},
            {0, 19, 202, "", "Significant Wind probability", "%", UC_NONE},
            {0, 19, 203, "TSTMC", "Categorical Thunderstorm", "0=no, 1=yes", UC_NONE},
            {0, 19, 204, "MIXLY", "Number of mixed layers next to surface", "integer", UC_NONE},

   /* 47 */ {0, 191, 192, "NLAT", "Latitude (-90 to 90)", "deg", UC_NONE},
   /* 48 */ {0, 191, 193, "ELON", "East Longitude (0 to 360)", "deg", UC_NONE},
   /* 49 */ {0, 191, 194, "TSEC", "Seconds prior to initial reference time", "s", UC_NONE},

   /* 50 */ {1, 0, 192, "BGRUN", "Baseflow-Groundwater Runoff", "kg/(m^2)", UC_NONE},
   /* 51 */ {1, 0, 193, "SSRUN", "Storm Surface Runoff", "kg/(m^2)", UC_NONE},

            {1, 1, 192, "CPOZP", "Probability of Freezing Precipitation", "%", UC_NONE},
            {1, 1, 193, "CPOFP", "Probability of Frozen Precipitation", "%", UC_NONE},
            {1, 1, 194, "PPFFG", "Probability of precipitation exceeding flash flood guidance values", "%", UC_NONE},

   /* 52 */ {2, 0, 192, "SOILW", "Volumetric Soil Moisture Content", "Fraction", UC_NONE},
   /* 53 */ {2, 0, 193, "GFLUX", "Ground Heat Flux", "W/(m^2)", UC_NONE},
   /* 54 */ {2, 0, 194, "MSTAV", "Moisture Availability", "%", UC_NONE},
   /* 55 */ {2, 0, 195, "SFEXC", "Exchange Coefficient", "(kg/(m^3))(m/s)", UC_NONE},
   /* 56 */ {2, 0, 196, "CNWAT", "Plant Canopy Surface Water", "kg/(m^2)", UC_NONE},
   /* 57 */ {2, 0, 197, "BMIXL", "Blackadar's Mixing Length Scale", "m", UC_NONE},
   /* 58 */ {2, 0, 198, "VGTYP", "Vegetation Type", "0..13", UC_NONE},
   /* 59 */ {2, 0, 199, "CCOND", "Canopy Conductance", "m/s", UC_NONE},
   /* 60 */ {2, 0, 200, "RSMIN", "Minimal Stomatal Resistance", "s/m", UC_NONE},
   /* 61 */ {2, 0, 201, "WILT", "Wilting Point", "Fraction", UC_NONE},
   /* 62 */ {2, 0, 202, "RCS", "Solar parameter in canopy conductance", "Fraction", UC_NONE},
   /* 63 */ {2, 0, 203, "RCT", "Temperature parameter in canopy conductance", "Fraction", UC_NONE},
   /* 64 */ {2, 0, 204, "RCQ", "Humidity parameter in canopy conductance", "Fraction", UC_NONE},
   /* 65 */ {2, 0, 205, "RCSOL", "Soil moisture parameter in canopy conductance", "Fraction", UC_NONE},
            {2, 0, 206, "RDRIP", "Rate of water dropping from canopy to ground", "unknown", UC_NONE},
            {2, 0, 207, "ICWAT", "Ice-free water surface", "%", UC_NONE},

   /* 66 */ {2, 3, 192, "SOILL", "Liquid Volumetric Soil Moisture (non Frozen)", "Proportion", UC_NONE},
   /* 67 */ {2, 3, 193, "RLYRS", "Number of Soil Layers in Root Zone", "-", UC_NONE},
   /* 68 */ {2, 3, 194, "SLTYP", "Surface Slope Type", "Index", UC_NONE},
   /* 69 */ {2, 3, 195, "SMREF", "Transpiration Stress-onset (soil moisture)", "Proportion", UC_NONE},
   /* 70 */ {2, 3, 196, "SMDRY", "Direct Evaporation Cease (soil moisture)", "Proportion", UC_NONE},
   /* 71 */ {2, 3, 197, "POROS", "Soil Porosity", "Proportion", UC_NONE},

/* ScatEstUWind -> USCT, ScatEstVWind -> VSCT as of 7/5/2006 (pre 1.80) */
   /* 72 */ {3, 1, 192, "USCT", "Scatterometer Estimated U Wind", "m/s", UC_NONE},
   /* 73 */ {3, 1, 193, "VSCT", "Scatterometer Estimated V Wind", "m/s", UC_NONE},

   /* Arthur Added this to both NDFD and NCEP local tables. (5/1/2006) */
           {10, 3, 192, "SURGE", "Hurricane Storm Surge", "m", UC_M2Feet},
           {10, 3, 193, "ETSRG", "Extra Tropical Storm Surge", "m", UC_M2Feet},
};
/* *INDENT-ON* */

int IsData_NDFD (unsigned short int center, unsigned short int subcenter)
{
   return ((center == 8) &&
           ((subcenter == GRIB2MISSING_u2) || (subcenter == 0)));
}

int IsData_MOS (unsigned short int center, unsigned short int subcenter)
{
   return ((center == 7) && (subcenter == 14));
}

/*****************************************************************************
 * Choose_LocalParmTable() --
 *
 * Arthur Taylor / MDL
 *
 * PURPOSE
 *   Chooses the local parameter table for a given center/subcenter.
 * Typically this is called after the default Choose_ParmTable was tried,
 * since it consists of all the local specs, and one has to linearly walk
 * through the table.
 *
 * ARGUMENTS
 *    center = The center that created the data. (Input)
 * subcenter = The subcenter that created the data. (Input)
 *  tableLen = The length of the returned table (Output)
 *
 * FILES/DATABASES: None
 *
 * RETURNS: LocalParmTable (appropriate parameter table.)
 *
 * HISTORY
 *   1/2004 Arthur Taylor (MDL/RSIS): Created
 *
 * NOTES
 *****************************************************************************
 */
static const GRIB2LocalTable *Choose_LocalParmTable (unsigned short int center,
                                               unsigned short int subcenter,
                                               size_t *tableLen)
{
   switch (center) {
      case 7:          /* NWS NCEP */
         /* Check if all subcenters of NCEP use the same local table. */
/*
         *tableLen = sizeof (NCEP_LclTable) / sizeof (GRIB2LocalTable);
         return &NCEP_LclTable[0];
*/
         switch (subcenter) {
            case 5:    /* Have HPC use NDFD table. */
               *tableLen = sizeof (HPC_LclTable) / sizeof (GRIB2LocalTable);
               return &HPC_LclTable[0];
            default:
               *tableLen = sizeof (NCEP_LclTable) / sizeof (GRIB2LocalTable);
               return &NCEP_LclTable[0];
/*
               *tableLen = 0;
               return NULL;
*/
         }
      case 8:          /* NWS Telecomunications gateway */
         switch (subcenter) {
            case GRIB2MISSING_u2: /* NDFD */
            case 0:    /* NDFD */
               *tableLen = sizeof (NDFD_LclTable) / sizeof (GRIB2LocalTable);
               return &NDFD_LclTable[0];
            default:
               *tableLen = 0;
               return NULL;
         }
      default:
         *tableLen = 0;
         return NULL;
   }
}

/*****************************************************************************
 * ParseElemName() --
 *
 * Arthur Taylor / MDL
 *
 * PURPOSE
 *   Converts a prodType, template, category and subcategory quadruple to the
 * ASCII string abbreviation of that variable.
 *   For example: 0, 0, 0, 0, = "T" for temperature.
 *
 * ARGUMENTS
 *    center = The center that created the data. (Input)
 * subcenter = The subcenter that created the data. (Input)
 *  prodType = The GRIB2, section 0 product type. (Input)
 *   templat = The GRIB2 section 4 template number. (Input)
 *       cat = The GRIB2 section 4 "General category of Product." (Input)
 *    subcat = The GRIB2 section 4 "Specific subcategory of Product". (Input)
 *   lenTime = The length of time over which statistics are done
 *             (see template 4.8). (Input)
 *     genID = The Generating process ID (used for GFS MOS) (Input)
 *  probType = For Probability templates (Input)
 * lowerProb = Lower Limit for probability templates. (Input)
 * upperProb = Upper Limit for probability templates. (Input)
 *      name = Short name for the data set (T, MaxT, etc) (Output)
 *   comment = Long form of the name (Temperature, etc) (Output)
 *      unit = What unit this variable is originally in (Output)
 *
 * FILES/DATABASES: None
 *
 * RETURNS: void
 *
 * HISTORY
 *   1/2004 Arthur Taylor (MDL/RSIS): Re-Created.
 *   6/2004 AAT: Added deltTime (because of Ozone issues).
 *   8/2004 AAT: Adjusted so template 9 gets units of % and no convert.
 *   3/2005 AAT: ReWrote to handle template 5, 9 and MOS.
 *   9/2005 AAT: Added code to handle MOS PoP06 vs MOS PoP12.
 *
 * NOTES
 *****************************************************************************
 */
/* Deal with probability templates 2/16/2006 */
static void ElemNameProb (uShort2 center, uShort2 subcenter, int prodType,
                          CPL_UNUSED int templat,
                          uChar cat, uChar subcat, sInt4 lenTime,
                          uChar timeIncrType,
                          CPL_UNUSED uChar genID,
                          uChar probType,
                          double lowerProb, double upperProb, char **name,
                          char **comment, char **unit, int *convert)
{
   const GRIB2ParmTable *table;
   const GRIB2LocalTable *local;
   size_t tableLen;
   size_t i;
   char f_isNdfd = IsData_NDFD (center, subcenter);
   char f_isMos = IsData_MOS (center, subcenter);

   *unit = (char *) malloc (strlen ("[%]") + 1);
   strcpy (*unit, "[%]");

   if (f_isNdfd || f_isMos) {
      /* Deal with NDFD/MOS handling of Prob Precip_Tot -> PoP12 */
      if ((prodType == 0) && (cat == 1) && (subcat == 8)) {
         myAssert (probType == 1);
         if (lenTime > 0) {
            mallocSprintf (name, "PoP%02d", lenTime);
            mallocSprintf (comment, "%02d hr Prob of Precip > 0.01 "
                           "In. [%%]", lenTime);
         } else {
            *name = (char *) malloc (strlen ("PoP") + 1);
            strcpy (*name, "PoP");
            *comment =
                  (char *) malloc (strlen ("Prob of Precip > 0.01 In. [%]") +
                                   1);
            strcpy (*comment, "Prob of Precip > 0.01 In. [%]");
         }
         *convert = UC_NONE;
         return;
      }
      /* 
       * Deal with NDFD handling of Prob. Wind speeds.
       * There are different solutions for naming the Prob. Wind fields
       * AAT(Mine): ProbSurge5c
       */
      if ((prodType == 10) && (cat == 3) && (subcat == 192)) {
         myAssert (probType == 1);
         myAssert (lenTime > 0);
         if (timeIncrType == 2) {
            /* Incremental */
            mallocSprintf (name, "ProbSurge%02di",
                           (int) ((upperProb / 0.3048) + .5));
         } else {
            /* Cumulative */
            myAssert (timeIncrType == 192);
            mallocSprintf (name, "ProbSurge%02dc",
                           (int) ((upperProb / 0.3048) + .5));
         }
         mallocSprintf (comment, "%02d hr Prob of Hurricane Storm Surge > %g "
                        "m [%%]", lenTime, upperProb);
         *convert = UC_NONE;
         return;
      }
   }
   if (f_isNdfd) {
      /* 
       * Deal with NDFD handling of Prob. Wind speeds.
       * There are different solutions for naming the Prob. Wind fields
       * Tim Boyer: TCWindSpdIncr34 TCWindSpdIncr50 TCWindSpdIncr64
       *            TCWindSpdCumu34 TCWindSpdCumu50 TCWindSpdCumu64
       * Dave Ruth: tcwspdabv34i tcwspdabv50i tcwspdabv64i
       *            tcwspdabv34c tcwspdabv50c tcwspdabv64c
       * AAT(Mine): ProbWindSpd34c ProbWindSpd50c ProbWindSpd64c
       *            ProbWindSpd34i ProbWindSpd50i ProbWindSpd64i
       */
      if ((prodType == 0) && (cat == 2) && (subcat == 1)) {
         myAssert (probType == 1);
         myAssert (lenTime > 0);
         if (timeIncrType == 2) {
            /* Incremental */
            mallocSprintf (name, "ProbWindSpd%02di",
                           (int) ((upperProb * 3600. / 1852.) + .5));
         } else {
            /* Cumulative */
            myAssert (timeIncrType == 192);
            mallocSprintf (name, "ProbWindSpd%02dc",
                           (int) ((upperProb * 3600. / 1852.) + .5));
         }
         mallocSprintf (comment, "%02d hr Prob of Wind speed > %g m/s [%%]",
                        lenTime, upperProb);
         *convert = UC_NONE;
         return;
      }
   }

   /* Generic tables. */
   table = Choose_GRIB2ParmTable (prodType, cat, &tableLen);
   if (table != NULL) {
      if (subcat < tableLen) {
         /* Check for NDFD over-rides. */
         /* The NDFD over-rides for probability templates have already been
          * handled. */
         if (lenTime > 0) {
            mallocSprintf (name, "Prob%s%02d", table[subcat].name, lenTime);
            mallocSprintf (comment, "%02d hr Prob of %s ", lenTime,
                           table[subcat].comment);
         } else {
            mallocSprintf (name, "Prob%s", table[subcat].name);
            mallocSprintf (comment, "Prob of %s ", table[subcat].comment);
         }
         if (probType == 0) {
            reallocSprintf (comment, "< %g %s [%%]", lowerProb,
                            table[subcat].unit);
         } else if (probType == 1) {
            reallocSprintf (comment, "> %g %s [%%]", upperProb,
                            table[subcat].unit);
         } else if (probType == 2) {
            reallocSprintf (comment, ">= %g, < %g %s [%%]", lowerProb,
                            upperProb, table[subcat].unit);
         } else if (probType == 3) {
            reallocSprintf (comment, "> %g %s [%%]", lowerProb,
                            table[subcat].unit);
         } else if (probType == 4) {
            reallocSprintf (comment, "< %g %s [%%]", upperProb,
                            table[subcat].unit);
         } else {
            reallocSprintf (comment, "%s [%%]", table[subcat].unit);
         }
         *convert = UC_NONE;
         return;
      }
   }

   /* Local use tables. */
   local = Choose_LocalParmTable (center, subcenter, &tableLen);
   if (local != NULL) {
      for (i = 0; i < tableLen; i++) {
         if ((prodType == local[i].prodType) && (cat == local[i].cat) &&
             (subcat == local[i].subcat)) {
            if (lenTime > 0) {
               mallocSprintf (name, "Prob%s%02d", local[i].name, lenTime);
               mallocSprintf (comment, "%02d hr Prob of %s ", lenTime,
                              local[i].comment);
            } else {
               mallocSprintf (name, "Prob%s", local[i].name);
               mallocSprintf (comment, "Prob of %s ", local[i].comment);
            }
            if (probType == 0) {
               reallocSprintf (comment, "< %g %s [%%]", lowerProb,
                               local[i].unit);
            } else if (probType == 1) {
               reallocSprintf (comment, "> %g %s [%%]", upperProb,
                               local[i].unit);
            } else if (probType == 2) {
               reallocSprintf (comment, ">= %g, < %g %s [%%]", lowerProb,
                               upperProb, local[i].unit);
            } else if (probType == 3) {
               reallocSprintf (comment, "> %g %s [%%]", lowerProb,
                               local[i].unit);
            } else if (probType == 4) {
               reallocSprintf (comment, "< %g %s [%%]", upperProb,
                               local[i].unit);
            } else {
               reallocSprintf (comment, "%s [%%]", local[i].unit);
            }
            *convert = UC_NONE;
            return;
         }
      }
   }

   *name = (char *) malloc (strlen ("ProbUnknown") + 1);
   strcpy (*name, "ProbUnknown");
   mallocSprintf (comment, "Prob of (prodType %d, cat %d, subcat %d) [-]",
                  prodType, cat, subcat);
   *convert = UC_NONE;
   return;
}

/* Deal with percentile templates 5/1/2006 */
static void ElemNamePerc (uShort2 center, uShort2 subcenter, int prodType,
                          CPL_UNUSED int templat,
                          uChar cat, uChar subcat, sInt4 lenTime,
                          sChar percentile, char **name, char **comment,
                          char **unit, int *convert)
{
   const GRIB2ParmTable *table;
   const GRIB2LocalTable *local;
   size_t tableLen;
   size_t i;

   /* Generic tables. */
   table = Choose_GRIB2ParmTable (prodType, cat, &tableLen);
   if (table != NULL) {
      if (subcat < tableLen) {
         /* Check for NDFD over-rides. */
         if (IsData_NDFD (center, subcenter) ||
             IsData_MOS (center, subcenter)) {
            for (i = 0; i < (sizeof (NDFD_Overide) /
                             sizeof (NDFD_AbrevOverideTable)); i++) {
               if (strcmp (NDFD_Overide[i].GRIB2name, table[subcat].name) ==
                   0) {
                  mallocSprintf (name, "%s%02d", NDFD_Overide[i].NDFDname,
                                 percentile);
                  if (lenTime > 0) {
                     mallocSprintf (comment, "%02d hr %s Percentile(%d) [%s]",
                                    lenTime, table[subcat].comment,
                                    percentile, table[subcat].unit);
                  } else {
                     mallocSprintf (comment, "%s Percentile(%d) [%s]",
                                    table[subcat].comment, percentile,
                                    table[subcat].unit);
                  }
                  mallocSprintf (unit, "[%s]", table[subcat].unit);
                  *convert = table[subcat].convert;
                  return;
               }
            }
         }
         mallocSprintf (name, "%s%02d", table[subcat].name, percentile);
         if (lenTime > 0) {
            mallocSprintf (comment, "%02d hr %s Percentile(%d) [%s]",
                           lenTime, table[subcat].comment, percentile,
                           table[subcat].unit);
         } else {
            mallocSprintf (comment, "%s Percentile(%d) [%s]",
                           table[subcat].comment, percentile,
                           table[subcat].unit);
         }
         mallocSprintf (unit, "[%s]", table[subcat].unit);
         *convert = table[subcat].convert;
         return;
      }
   }

   /* Local use tables. */
   local = Choose_LocalParmTable (center, subcenter, &tableLen);
   if (local != NULL) {
      for (i = 0; i < tableLen; i++) {
         if ((prodType == local[i].prodType) && (cat == local[i].cat) &&
             (subcat == local[i].subcat)) {
            mallocSprintf (name, "%s%02d", local[i].name, percentile);
            if (lenTime > 0) {
               mallocSprintf (comment, "%02d hr %s Percentile(%d) [%s]",
                              lenTime, local[i].comment, percentile,
                              local[i].unit);
            } else {
               mallocSprintf (comment, "%s Percentile(%d) [%s]",
                              local[i].comment, percentile, local[i].unit);
            }
            mallocSprintf (unit, "[%s]", local[i].unit);
            *convert = local[i].convert;
            return;
         }
      }
   }

   *name = (char *) malloc (strlen ("unknown") + 1);
   strcpy (*name, "unknown");
   mallocSprintf (comment, "(prodType %d, cat %d, subcat %d) [-]", prodType,
                  cat, subcat);
   *unit = (char *) malloc (strlen ("[-]") + 1);
   strcpy (*unit, "[-]");
   *convert = UC_NONE;
   return;
}

/* Deal with non-prob templates 2/16/2006 */
static void ElemNameNorm (uShort2 center, uShort2 subcenter, int prodType,
                          int templat, uChar cat, uChar subcat, sInt4 lenTime,
                          CPL_UNUSED uChar timeIncrType,
                          CPL_UNUSED uChar genID,
                          CPL_UNUSED uChar probType,
                          CPL_UNUSED double lowerProb,
                          CPL_UNUSED double upperProb,
                          char **name,
                          char **comment, char **unit, int *convert)
{
   const GRIB2ParmTable *table;
   const GRIB2LocalTable *local;
   size_t tableLen;
   size_t i;
   sChar f_accum;

   /* Check for over-ride case for ozone.  Originally just for NDFD, but I
    * think it is useful for ozone data that originated elsewhere. */
   if ((prodType == 0) && (templat == 8) && (cat == 14) && (subcat == 193)) {
      if (lenTime > 0) {
         mallocSprintf (name, "Ozone%02d", lenTime);
         mallocSprintf (comment, "%d hr Average Ozone Concentration "
                        "[PPB]", lenTime);
      } else {
         *name = (char *) malloc (strlen ("AVGOZCON") + 1);
         strcpy (*name, "AVGOZCON");
         *comment =
               (char *) malloc (strlen ("Average Ozone Concentration [PPB]") +
                                1);
         strcpy (*comment, "Average Ozone Concentration [PPB]");
      }
      *unit = (char *) malloc (strlen ("[PPB]") + 1);
      strcpy (*unit, "[PPB]");
      *convert = UC_NONE;
      return;
   }

   /* Generic tables. */
   table = Choose_GRIB2ParmTable (prodType, cat, &tableLen);
   if (table != NULL) {
      if (subcat < tableLen) {
         /* Check for NDFD over-rides. */
         if (IsData_NDFD (center, subcenter) ||
             IsData_MOS (center, subcenter)) {
            for (i = 0; i < (sizeof (NDFD_Overide) /
                             sizeof (NDFD_AbrevOverideTable)); i++) {
               if (strcmp (NDFD_Overide[i].GRIB2name, table[subcat].name) ==
                   0) {
                  *name =
                        (char *) malloc (strlen (NDFD_Overide[i].NDFDname) +
                                         1);
                  strcpy (*name, NDFD_Overide[i].NDFDname);
                  mallocSprintf (comment, "%s [%s]", table[subcat].comment,
                                 table[subcat].unit);
                  mallocSprintf (unit, "[%s]", table[subcat].unit);
                  *convert = table[subcat].convert;
                  return;
               }
            }
         }
         /* Allow hydrologic PoP, thunderstorm probability (TSTM), or APCP to 
          * have lenTime labels. */
         f_accum = (((prodType == 1) && (cat == 1) && (subcat == 2)) ||
                    ((prodType == 0) && (cat == 19) && (subcat == 2)) ||
                    ((prodType == 0) && (cat == 1) && (subcat == 8)) ||
                    ((prodType == 0) && (cat == 19) && (subcat == 203)));
         if (f_accum && (lenTime > 0)) {
            mallocSprintf (name, "%s%02d", table[subcat].name, lenTime);
            mallocSprintf (comment, "%02d hr %s [%s]", lenTime,
                           table[subcat].comment, table[subcat].unit);
         } else {
            *name = (char *) malloc (strlen (table[subcat].name) + 1);
            strcpy (*name, table[subcat].name);
            mallocSprintf (comment, "%s [%s]", table[subcat].comment,
                           table[subcat].unit);
         }
         mallocSprintf (unit, "[%s]", table[subcat].unit);
         *convert = table[subcat].convert;
         return;
      }
   }

   /* Local use tables. */
   local = Choose_LocalParmTable (center, subcenter, &tableLen);
   if (local != NULL) {
      for (i = 0; i < tableLen; i++) {
         if ((prodType == local[i].prodType) && (cat == local[i].cat) &&
             (subcat == local[i].subcat)) {
            /* Allow specific products with non-zero lenTime to reflect that. 
             */
            f_accum = 0;
            if (f_accum && (lenTime > 0)) {
               mallocSprintf (name, "%s%02d", local[i].name, lenTime);
               mallocSprintf (comment, "%02d hr %s [%s]", lenTime,
                              local[i].comment, local[i].unit);
            } else {
               *name = (char *) malloc (strlen (local[i].name) + 1);
               strcpy (*name, local[i].name);
               mallocSprintf (comment, "%s [%s]", local[i].comment,
                              local[i].unit);
            }
            mallocSprintf (unit, "[%s]", local[i].unit);
            *convert = local[i].convert;
            return;
         }
      }
   }

   *name = (char *) malloc (strlen ("unknown") + 1);
   strcpy (*name, "unknown");
   mallocSprintf (comment, "(prodType %d, cat %d, subcat %d) [-]", prodType,
                  cat, subcat);
   *unit = (char *) malloc (strlen ("[-]") + 1);
   strcpy (*unit, "[-]");
   *convert = UC_NONE;
   return;
}

void ParseElemName (uShort2 center, uShort2 subcenter, int prodType,
                    int templat, int cat, int subcat, sInt4 lenTime,
                    uChar timeIncrType, uChar genID, uChar probType,
                    double lowerProb, double upperProb, char **name,
                    char **comment, char **unit, int *convert,
                    sChar percentile)
{
   myAssert (*name == NULL);
   myAssert (*comment == NULL);
   myAssert (*unit == NULL);

   /* Check if this is Probability data */
   if ((templat == GS4_PROBABIL_TIME) || (templat == GS4_PROBABIL_PNT)) {
      ElemNameProb (center, subcenter, prodType, templat, cat, subcat,
                    lenTime, timeIncrType, genID, probType, lowerProb,
                    upperProb, name, comment, unit, convert);
   } else if (templat == GS4_PERCENTILE) {
      ElemNamePerc (center, subcenter, prodType, templat, cat, subcat,
                    lenTime, percentile, name, comment, unit, convert);
   } else {
      ElemNameNorm (center, subcenter, prodType, templat, cat, subcat,
                    lenTime, timeIncrType, genID, probType, lowerProb,
                    upperProb, name, comment, unit, convert);
   }
}

/*****************************************************************************
 * ParseElemName2() -- Review 12/2002
 *
 * Arthur Taylor / MDL
 *
 * PURPOSE
 *   Converts a prodType, template, category and subcategory quadruple to the
 * ASCII string abbreviation of that variable.
 *   For example: 0, 0, 0, 0, = "T" for temperature.
 *
 * ARGUMENTS
 * prodType = The GRIB2, section 0 product type. (Input)
 *  templat = The GRIB2 section 4 template number. (Input)
 *      cat = The GRIB2 section 4 "General category of Product." (Input)
 *   subcat = The GRIB2 section 4 "Specific subcategory of Product". (Input)
 *     name = Where to store the result (assumed already allocated to at
 *            least 15 bytes) (Output)
 *  comment = Extra info about variable (assumed already allocated to at
 *            least 100 bytes) (Output)
 *     unit = What unit this variable is in. (assumed already allocated to at
 *            least 20 bytes) (Output)
 *
 * FILES/DATABASES: None
 *
 * RETURNS: char *
 *   Same as 'strcpy', i.e. it returns name.
 *
 * HISTORY
 *   9/2002 Arthur Taylor (MDL/RSIS): Created.
 *  11/2002 AAT: Added MOIST_TOT_SNOW (and switched MOIST_SNOWAMT to
 *               SnowDepth)
 *  12/2002 (TK,AC,TB,&MS): Code Review.
 *   2/2003 AAT: moved from degrib.c to metaparse.c
 *              (Reason: primarily for Sect2 Parsing)
 *              (renamed from ElementName to ParseElemName)
 *   4/2003 AAT: Added the comment as a return element.(see GRIB2 discipline)
 *   6/2003 AAT: Added the unit as a return element.
 *   6/2003 AAT: Added Wave Height.
 *
 * NOTES
 *   Similar to GRIB1_Table2LookUp... May want to take this and the unit
 * stuff and combine them into a module.
 *****************************************************************************
 */
/*
static void ParseElemName2 (int prodType, int templat, int cat, int subcat,
                            char *name, char *comment, char *unit)
{
   if (prodType == 0) {
      if (cat == CAT_TEMP) { * 0 *
         switch (subcat) {
            case TEMP_TEMP: * 0 *
               strcpy (comment, "Temperature [K]");
               strcpy (name, "T");
               strcpy (unit, "[K]");
               return;
            case TEMP_MAXT: * 4 *
               strcpy (comment, "Maximum temperature [K]");
               strcpy (name, "MaxT");
               strcpy (unit, "[K]");
               return;
            case TEMP_MINT: * 5 *
               strcpy (comment, "Minimum temperature [K]");
               strcpy (name, "MinT");
               strcpy (unit, "[K]");
               return;
            case TEMP_DEW_TEMP: * 6 *
               strcpy (comment, "Dew point temperature [K]");
               strcpy (name, "Td");
               strcpy (unit, "[K]");
               return;
            case TEMP_WINDCHILL: * 13 *
               strcpy (comment, "Wind chill factor [K]");
               strcpy (name, "WCI");
               strcpy (unit, "[K]");
               return;
            case TEMP_HEAT: * 12 *
               strcpy (comment, "Heat index [K]");
               strcpy (name, "HeatIndex");
               strcpy (unit, "[K]");
               return;
         }
      } else if (cat == CAT_MOIST) { * 1 *
         switch (subcat) {
            case MOIST_REL_HUMID: * 1 *
               strcpy (comment, "Relative Humidity [%]");
               strcpy (name, "RH");
               strcpy (unit, "[%]");
               return;
            case MOIST_PRECIP_TOT: * 8 *
               if (templat == GS4_PROBABIL_TIME) { * template number 9 implies prob. *
                  strcpy (comment, "Prob of 0.01 In. of Precip [%]");
                  strcpy (name, "PoP12");
                  strcpy (unit, "[%]");
                  return;
               } else {
                  strcpy (comment, "Total precipitation [kg/(m^2)]");
                  strcpy (name, "QPF");
                  strcpy (unit, "[kg/(m^2)]");
                  return;
               }
            case MOIST_SNOWAMT: * 11 *
               strcpy (comment, "Snow Depth [m]");
               strcpy (name, "SnowDepth");
               strcpy (unit, "[m]");
               return;
            case MOIST_TOT_SNOW: * 29 *
               strcpy (comment, "Total snowfall [m]");
               strcpy (name, "SnowAmt");
               strcpy (unit, "[m]");
               return;
            case 192:  * local use moisture. *
               strcpy (comment, "Weather (local use moisture) [-]");
               strcpy (name, "Wx");
               strcpy (unit, "[-]");
               return;
         }
      } else if (cat == CAT_MOMENT) { * 2 *
         switch (subcat) {
            case MOMENT_WINDDIR: * 0 *
               strcpy (comment, "Wind direction (from which blowing) "
                       "[deg true]");
               strcpy (name, "WindDir");
               strcpy (unit, "[deg true]");
               return;
            case MOMENT_WINDSPD: * 1 *
               strcpy (comment, "Wind speed [m/s]");
               strcpy (name, "WindSpd");
               strcpy (unit, "[m/s]");
               return;
         }
      } else if (cat == CAT_CLOUD) { * 6 *
         switch (subcat) {
            case CLOUD_COVER: * 1 *
               strcpy (comment, "Total cloud cover [%]");
               strcpy (name, "Sky");
               strcpy (unit, "[%]");
               return;
         }
      } else if (cat == CAT_MOISTURE_PROB) { * 10 *
         if (subcat == 8) { * grandfather'ed in. *
            strcpy (comment, "Prob of 0.01 In. of Precip [%]");
            strcpy (name, "PoP12");
            strcpy (unit, "[%]");
            return;
         }
      }
   } else if (prodType == 10) {
      if (cat == OCEAN_CAT_WAVES) { * 0 *
         if (subcat == OCEAN_WAVE_SIG_HT_WV) { * 5 *
            strcpy (comment, "Significant height of wind waves [m]");
            strcpy (name, "WaveHeight");
            strcpy (unit, "[m]");
            return;
         }
      }
   }
   strcpy (name, "");
   strcpy (comment, "unknown");
   strcpy (unit, "[-]");
   return;
}
*/

/*****************************************************************************
 * ComputeUnit() --
 *
 * Arthur Taylor / MDL
 *
 * PURPOSE
 *   Sets m, and b for equation y = mx + b, where x is in the unit
 * specified by GRIB2, and y is the one specified by f_unit.  The default
 * is m = 1, b = 0.
 *
 * Currently:
 *   For f_unit = 1 (English) we return Fahrenheit, knots, and inches for
 * temperature, wind speed, and amount of snow or rain.  The original units
 * are Kelvin, m/s, kg/m**2.
 *   For f_unit = 2 (metric) we return Celsius instead of Kelvin.
 *
 * ARGUMENTS
 *  convert = The enumerated type describing the type of conversion. (Input)
 * origName = Original unit name (needed for log10 option) (Input)
 *   f_unit = What type of unit to return (see above) (Input).
 *    unitM = M in equation y = m x + b (Output)
 *    unitB = B in equation y = m x + b (Output)
 *     name = Where to store the result (assumed already allocated to at
 *           least 15 bytes) (Output)
 *
 * FILES/DATABASES: None
 *
 * RETURNS: int
 *   0 if we set M and B, 1 if we used defaults.
 *
 * HISTORY
 *   1/2004 Arthur Taylor (MDL/RSIS): Re-Created.
 *
 * NOTES
 *****************************************************************************
 */
int ComputeUnit (int convert, char *origName, sChar f_unit, double *unitM,
                 double *unitB, char *name)
{
   switch (convert) {
      case UC_NONE:
         break;
      case UC_K2F:     /* Convert from Kelvin to F or C. */
         if (f_unit == 1) {
            strcpy (name, "[F]");
            *unitM = 9. / 5.;
            /* 32 - (9/5 * 273.15) = 32 - 491.67 = -459.67. */
            *unitB = -459.67;
            return 0;
         } else if (f_unit == 2) {
            strcpy (name, "[C]");
            *unitM = 1;
            *unitB = -273.15;
            return 0;
         }
         break;
      case UC_InchWater: /* Convert from kg/(m^2) to inches water. */
         if (f_unit == 1) {
            strcpy (name, "[inch]");
            /* 
             * kg/m**2 / density of water (1000 kg/m**3)
             * 1/1000 m * 1/2.54 in/cm * 100 cm/m = 1/25.4 inches
             */
            *unitM = 1. / 25.4;
            *unitB = 0;
            return 0;
         }
         break;
      case UC_M2Feet:  /* Convert from meters to feet. */
         if (f_unit == 1) {
            /* 1 (m) * (100cm/m) * (inch/2.54cm) * (ft/12inch) = X (ft) */
            strcpy (name, "[feet]");
            *unitM = 100. / 30.48;
            *unitB = 0;
            return 0;
         }
         break;
      case UC_M2Inch:  /* Convert from meters to inches. */
         if (f_unit == 1) {
            strcpy (name, "[inch]");
            *unitM = 100. / 2.54; /* inch / m */
            *unitB = 0;
            return 0;
         }
         break;
         /* NCEP goes with a convention of 1 nm = 1853.248 m.
          * http://www.sizes.com/units/mile_USnautical.htm Shows that on
          * 7/1/1954 US Department of Commerce switched to 1 nm = 1852 m
          * (International standard.) */
      case UC_MS2Knots: /* Convert from m/s to knots. */
         if (f_unit == 1) {
            strcpy (name, "[knots]");
            *unitM = 3600. / 1852.; /* knot / m s**-1 */
            *unitB = 0;
            return 0;
         }
         break;
      case UC_LOG10:   /* convert from log10 (x) to x */
         if ((f_unit == 1) || (f_unit == 2)) {
            origName[strlen (origName) - 2] = '\0';
            if (strlen (origName) > 21)
               origName[21] = '\0';
            snprintf (name, 15, "[%s]", origName + 7);
            *unitM = -10; /* M = -10 => take 10^(x) */
            *unitB = 0;
            return 0;
         }
         break;
   }
   /* Default case is for the unit in the GRIB2 document. */
   strcpy (name, "[GRIB2 unit]");
   *unitM = 1;
   *unitB = 0;
   return 1;
}

/*****************************************************************************
 * ComputeUnit2() --
 *
 * Arthur Taylor / MDL
 *
 * PURPOSE
 *   Sets m, and b for equation y = mx + b, where x is in the unit
 * specified by GRIB2, and y is the one specified by f_unit.  The default
 * is m = 1, b = 0.
 *
 * Currently:
 *   For f_unit = 1 (English) we return Fahrenheit, knots, and inches for
 * temperature, wind speed, and amount of snow or rain.  The original units
 * are Kelvin, m/s, kg/m**2.
 *   For f_unit = 2 (metric) we return Celsius instead of Kelvin.
 *
 * ARGUMENTS
 * prodType = The GRIB2, section 0 product type. (Input)
 *  templat = The GRIB2 section 4 template number. (Input)
 *      cat = The GRIB2 section 4 "General category of Product." (Input)
 *   subcat = The GRIB2 section 4 "Specific subcategory of Product". (Input)
 *   f_unit = What type of unit to return (see above) (Input).
 *    unitM = M in equation y = m x + b (Output)
 *    unitB = B in equation y = m x + b (Output)
 *     name = Where to store the result (assumed already allocated to at
 *            least 15 bytes) (Output)
 *
 * FILES/DATABASES: None
 *
 * RETURNS: int
 *   0 if we set M and B, 1 if we used defaults.
 *
 * HISTORY
 *  11/2002 Arthur Taylor (MDL/RSIS): Created.
 *
 * NOTES
 *****************************************************************************
 */
/*
static int ComputeUnit2 (int prodType, int templat, int cat, int subcat,
                         sChar f_unit, double *unitM, double *unitB,
                         char *name)
{
   if (prodType == 0) {
      switch (cat) {
         case CAT_TEMP:
            * subcat 8 is K/m, 10, 11 is W/m**2 *
            if ((subcat < 16) && (subcat != 8) &&
                (subcat != 10) && (subcat != 11)) {
               if (f_unit == 1) {
                  strcpy (name, "[F]");
                  *unitM = 9. / 5.;
                  * 32 - (9/5 * 273.15) = 32 - 491.67 = -459.67. *
                  *unitB = -459.67;
                  return 0;
               } else if (f_unit == 2) {
                  strcpy (name, "[C]");
                  *unitM = 1;
                  *unitB = -273.15;
                  return 0;
               }
            }
            break;
         case CAT_MOIST:
            if (subcat == MOIST_PRECIP_TOT) {
               if (templat != 9) { * template number != 9 implies QPF. *
                  if (f_unit == 1) {
                     strcpy (name, "[inch]");
                     *
                      * kg/m**2 / density of water (1000 kg/m**3)
                      * 1/1000 m * 1/2.54 in/cm * 100 cm/m = 1/25.4 inches
                      *
                     *unitM = 1. / 25.4;
                     *unitB = 0;
                     return 0;
                  }
               }
            }
            if ((subcat == MOIST_SNOWAMT) || (subcat == MOIST_TOT_SNOW)) {
               if (f_unit == 1) {
                  strcpy (name, "[inch]");
                  *unitM = 100. / 2.54; * inch / m *
                  *unitB = 0;
                  return 0;
               }
            }
            break;
         case CAT_MOMENT:
            if (subcat == MOMENT_WINDSPD) {
               if (f_unit == 1) {
                  strcpy (name, "[knots]");
                  *unitM = 3600. / 1852.; * knot / m s**-1 *
                  *unitB = 0;
                  return 0;
               }
            }
            break;
      }
   } else if (prodType == 10) {
      if (cat == OCEAN_CAT_WAVES) { * 0 *
         if (subcat == OCEAN_WAVE_SIG_HT_WV) { * 5 *
            if (f_unit == 1) {
               * 1 (m) * (100cm/m) * (inch/2.54cm) * (ft/12inch) = X (ft) *
               strcpy (name, "[feet]");
               *unitM = 100. / 30.48;
               *unitB = 0;
               return 0;
            }
         }
      }
   }
   * Default case is for the unit in the GRIB2 document. *
   strcpy (name, "[GRIB2 unit]");
   *unitM = 1;
   *unitB = 0;
   return 1;
}
*/

/* GRIB2 Code Table 4.5 */
/* *INDENT-OFF* */
static const GRIB2SurfTable Surface[] = {
   /* 0 */ {"RESERVED", "Reserved", "-"},
   /* 1 */ {"SFC", "Ground or water surface", "-"},
   /* 2 */ {"CBL", "Cloud base level", "-"},
   /* 3 */ {"CTL", "Level of cloud tops", "-"},
   /* 4 */ {"0DEG", "Level of 0 degree C isotherm", "-"},
   /* 5 */ {"ADCL", "Level of adiabatic condensation lifted from the surface", "-"},
   /* 6 */ {"MWSL", "Maximum wind level", "-"},
   /* 7 */ {"TRO", "Tropopause", "-"},
   /* 8 */ {"NTAT", "Nominal top of atmosphere", "-"},
   /* 9 */ {"SEAB", "Sea bottom", "-"},
   /* 10: 10-19 */ {"RESERVED", "Reserved", "-"},
   /* 11: 20 */ {"TMPL", "Isothermal level", "K"},
   /* 12: 21-99 */ {"RESERVED", "Reserved", "-"},
   /* 13: 100 */ {"ISBL", "Isobaric surface", "Pa"},
   /* 14: 101 */ {"MSL", "Mean sea level", "-"},
   /* 15: 102 */ {"GPML", "Specific altitude above mean sea level", "m"},
   /* 16: 103 */ {"HTGL", "Specified height level above ground", "m"},
   /* 17: 104 */ {"SIGL", "Sigma level", "'sigma' value"},
   /* 18: 105 */ {"HYBL", "Hybrid level", "-"},
   /* 19: 106 */ {"DBLL", "Depth below land surface", "m"},
   /* 20: 107 */ {"THEL", "Isentropic (theta) level", "K"},
   /* 21: 108 */ {"SPDL", "Level at specified pressure difference from ground to level", "Pa"},
   /* 22: 109 */ {"PVL", "Potential vorticity surface", "(K m^2)/(kg s)"},
   /* 23: 110 */ {"RESERVED", "Reserved", "-"},
   /* 24: 111 */ {"EtaL", "Eta* level", "-"},
   /* 25: 112-116 */ {"RESERVED", "Reserved", "-"},
   /* 26: 117 */ {"unknown", "Mixed layer depth", "m"}, /* unknown abbrev */
   /* 27: 118-159 */ {"RESERVED", "Reserved", "-"},
   /* 28: 160 */ {"DBSL", "Depth below sea level", "m"},
   /* 29: 161-191 */ {"RESERVED", "Reserved", "-"},
   /* 30: 192-254 */ {"RESERVED", "Reserved Local use", "-"},
   /* 31: 255 */ {"MISSING", "Missing", "-"},
};

typedef struct {
   int index;
   GRIB2SurfTable surface;
} GRIB2LocalSurface;

/* based on http://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_table4-5.shtml
 * updated last on 3/14/2006 */
static const GRIB2LocalSurface NCEP_Surface[] = {
   {200, {"EATM", "Entire atmosphere (considered as a single layer)", "-"}},
   {201, {"EOCN", "Entire ocean (considered as a single layer)", "-"}},
   {204, {"HTFL", "Highest tropospheric freezing level", "-"}},
   {206, {"GCBL", "Grid scale cloud bottom level", "-"}},
   {207, {"GCTL", "Grid scale cloud top level", "-"}},
   {209, {"BCBL", "Boundary layer cloud bottom level", "-"}},
   {210, {"BCTL", "Boundary layer cloud top level", "-"}},
   {211, {"BCY", "Boundary layer cloud level", "-"}},
   {212, {"LCBL", "Low cloud bottom level", "-"}},
   {213, {"LCTL", "Low cloud top level", "-"}},
   {214, {"LCY", "Low cloud level", "-"}},
   {215, {"CEIL", "Cloud ceiling", "-"}},
   {222, {"MCBL", "Middle cloud bottom level", "-"}},
   {223, {"MCTL", "Middle cloud top level", "-"}},
   {224, {"MCY", "Middle cloud level", "-"}},
   {232, {"HCBL", "High cloud bottom level", "-"}},
   {233, {"HCTL", "High cloud top level", "-"}},
   {234, {"HCY", "High cloud level", "-"}},
   {235, {"OITL", "Ocean Isotherm Level (1/10 deg C)", "-"}},
   {236, {"OLYR", "Layer between two depths below ocean surface", "-"}},
   {237, {"OBML", "Bottom of Ocean Mixed Layer (m)", "-"}},
   {238, {"OBIL", "Bottom of Ocean Isothermal Layer (m)", "-"}},
   {242, {"CCBL", "Convective cloud bottom level", "-"}},
   {243, {"CCTL", "Convective cloud top level", "-"}},
   {244, {"CCY", "Convective cloud level", "-"}},
   {245, {"LLTW", "Lowest level of the wet bulb zero", "-"}},
   {246, {"MTHE", "Maximum equivalent potential temperature level", "-"}},
   {247, {"EHLT", "Equilibrium level", "-"}},
   {248, {"SCBL", "Shallow convective cloud bottom level", "-"}},
   {249, {"SCTL", "Shallow convective cloud top level", "-"}},
   {251, {"DCBL", "Deep convective cloud bottom level", "-"}},
   {252, {"DCTL", "Deep convective cloud top level", "-"}},
   {253, {"LBLSW", "Lowest bottom level of supercooled liquid water layer", "-"}},
   {254, {"HTLSW", "Highest top level of supercooled liquid water layer", "-"}},
};
/* *INDENT-ON* */

/*****************************************************************************
 * Table45Index() --
 *
 * Arthur Taylor / MDL
 *
 * PURPOSE
 *   To figure out the entry in the "Surface" table (used for Code Table 4.5)
 *
 * ARGUMENTS
 *          i = The original index to look up. (Input)
 * f_reserved = If the index is a "reserved" index (Output)
 *
 * FILES/DATABASES: None
 *
 * RETURNS: GRIB2SurfTable
 *
 * HISTORY
 *   9/2002 Arthur Taylor (MDL/RSIS): Created.
 *  12/2004 Arthur Taylor (RSIS): Modified to return SurfaceTable.
 *
 * NOTES
 *****************************************************************************
 */
GRIB2SurfTable Table45Index (int i,
                             int *f_reserved,
                             uShort2 center,
                             CPL_UNUSED uShort2 subcenter)
{
   size_t j;

   *f_reserved = 1;
   if ((i > 255) || (i < 0)) {
#ifdef DEBUG
      printf ("Surface index is out of 0..255 range?\n");
#endif
      return Surface[0];
   }
   if (i == 255)
      return Surface[31];
   if (i > 191) {
      if (center == 7) {
         for (j = 0; j < sizeof (NCEP_Surface) / sizeof (NCEP_Surface[0]);
              j++) {
            if (i == NCEP_Surface[j].index) {
               *f_reserved = 0;
               return (NCEP_Surface[j].surface);
            }
         }
      }
      return Surface[30];
   }
   if (i > 160)
      return Surface[29];
   if (i == 160) {
      *f_reserved = 0;
      return Surface[28];
   }
   if (i > 117)
      return Surface[27];
   if (i == 117) {
      *f_reserved = 0;
      return Surface[26];
   }
   if (i > 111)
      return Surface[25];
   if (i == 111) {
      *f_reserved = 0;
      return Surface[i - 87];
   }
   if (i == 110)
      return Surface[i - 87];
   if (i > 99) {
      *f_reserved = 0;
      return Surface[i - 87];
   }
   if (i > 20)
      return Surface[12];
   if (i == 20) {
      *f_reserved = 0;
      return Surface[11];
   }
   if (i > 9)
      return Surface[10];
   if (i > 0) {
      *f_reserved = 0;
      return Surface[i];
   }
   return Surface[0];
}

void ParseLevelName (unsigned short int center, unsigned short int subcenter,
                     uChar surfType, double value, sChar f_sndValue,
                     double sndValue, char **shortLevelName,
                     char **longLevelName)
{
   int f_reserved;
   char valBuff[512];
   char sndBuff[512];
   GRIB2SurfTable surf = Table45Index (surfType, &f_reserved, center,
                                       subcenter);

   /* Check if index is defined... 191 is undefined. */
   free (*shortLevelName);
   *shortLevelName = NULL;
   free (*longLevelName);
   *longLevelName = NULL;
   snprintf (valBuff, sizeof(valBuff), "%f", value);
   strTrimRight (valBuff, '0');
   if (valBuff[strlen (valBuff) - 1] == '.') {
      valBuff[strlen (valBuff) - 1] = '\0';
   }
   if (f_sndValue) {
      snprintf (sndBuff, sizeof(sndBuff), "%f", sndValue);
      strTrimRight (sndBuff, '0');
      if (sndBuff[strlen (sndBuff) - 1] == '.') {
         sndBuff[strlen (sndBuff) - 1] = '\0';
      }
      if (f_reserved) {
         reallocSprintf (shortLevelName, "%s-%s-%s(%d)", valBuff, sndBuff,
                         surf.name, surfType);
         reallocSprintf (longLevelName, "%s-%s[%s] %s(%d) (%s)", valBuff,
                         sndBuff, surf.unit, surf.name, surfType,
                         surf.comment);
      } else {
         reallocSprintf (shortLevelName, "%s-%s-%s", valBuff, sndBuff,
                         surf.name);
         reallocSprintf (longLevelName, "%s-%s[%s] %s=\"%s\"", valBuff,
                         sndBuff, surf.unit, surf.name, surf.comment);
      }
   } else {
      if (f_reserved) {
         reallocSprintf (shortLevelName, "%s-%s(%d)", valBuff, surf.name,
                         surfType);
         reallocSprintf (longLevelName, "%s[%s] %s(%d) (%s)", valBuff,
                         surf.unit, surf.name, surfType, surf.comment);
      } else {
         reallocSprintf (shortLevelName, "%s-%s", valBuff, surf.name);
         reallocSprintf (longLevelName, "%s[%s] %s=\"%s\"", valBuff,
                         surf.unit, surf.name, surf.comment);
      }
   }
}
