mirror of https://github.com/ecmwf/eccodes.git
1145 lines
41 KiB
C++
1145 lines
41 KiB
C++
/*
|
|
* (C) Copyright 2005- ECMWF.
|
|
*
|
|
* This software is licensed under the terms of the Apache Licence Version 2.0
|
|
* which can be obtained at http://www.apache.org/licenses/LICENSE-2.0.
|
|
*
|
|
* In applying this licence, ECMWF does not waive the privileges and immunities granted to it by
|
|
* virtue of its status as an intergovernmental organisation nor does it submit to any jurisdiction.
|
|
*/
|
|
|
|
#include "grib_api_internal.h"
|
|
|
|
// Return the rank of the key using list of keys (For BUFR keys)
|
|
// The argument 'keys' is an input as well as output from each call
|
|
int compute_bufr_key_rank(grib_handle* h, grib_string_list* keys, const char* key)
|
|
{
|
|
grib_string_list* next = keys;
|
|
grib_string_list* prev = keys;
|
|
int theRank = 0;
|
|
size_t size = 0;
|
|
const grib_context* c = h->context;
|
|
DEBUG_ASSERT(h->product_kind == PRODUCT_BUFR);
|
|
|
|
while (next && next->value && strcmp(next->value, key)) {
|
|
prev = next;
|
|
next = next->next;
|
|
}
|
|
if (!next) {
|
|
DEBUG_ASSERT(prev);
|
|
if (prev) {
|
|
prev->next = (grib_string_list*)grib_context_malloc_clear(c, sizeof(grib_string_list));
|
|
next = prev->next;
|
|
}
|
|
}
|
|
DEBUG_ASSERT(next);
|
|
if (!next) return 0;
|
|
|
|
if (!next->value) {
|
|
next->value = strdup(key);
|
|
next->count = 0;
|
|
}
|
|
|
|
next->count++;
|
|
theRank = next->count;
|
|
if (theRank == 1) {
|
|
// If the count is 1 it could mean two things:
|
|
// This is the first instance of the key and there is another one
|
|
// This is the first and only instance of the key
|
|
// So we check if there is a second one of this key,
|
|
// If not, then rank is zero i.e. this is the only instance
|
|
size_t slen = strlen(key) + 5;
|
|
char* s = (char*)grib_context_malloc_clear(c, slen);
|
|
snprintf(s, slen, "#2#%s", key);
|
|
if (grib_get_size(h, s, &size) == GRIB_NOT_FOUND)
|
|
theRank = 0;
|
|
grib_context_free(c, s);
|
|
}
|
|
|
|
return theRank;
|
|
}
|
|
|
|
char** codes_bufr_copy_data_return_copied_keys(grib_handle* hin, grib_handle* hout, size_t* nkeys, int* err)
|
|
{
|
|
bufr_keys_iterator* kiter = NULL;
|
|
char** keys = NULL;
|
|
grib_sarray* k = 0;
|
|
|
|
if (hin == NULL || hout == NULL) {
|
|
*err = GRIB_NULL_HANDLE;
|
|
return NULL;
|
|
}
|
|
|
|
kiter = codes_bufr_data_section_keys_iterator_new(hin);
|
|
if (!kiter)
|
|
return NULL;
|
|
k = grib_sarray_new(hin->context, 50, 10);
|
|
|
|
while (codes_bufr_keys_iterator_next(kiter)) {
|
|
char* name = codes_bufr_keys_iterator_get_name(kiter);
|
|
// if the copy fails we want to keep copying without any errors.
|
|
// This is because the copy can be between structures that are not
|
|
// identical and we want to copy what can be copied and skip what
|
|
// cannot be copied because is not in the output handle
|
|
*err = codes_copy_key(hin, hout, name, 0);
|
|
if (*err == 0) {
|
|
// 'name' will be freed when we call codes_bufr_keys_iterator_delete so copy
|
|
char* copied_name = strdup(name);
|
|
k = grib_sarray_push(hin->context, k, copied_name);
|
|
}
|
|
}
|
|
*nkeys = grib_sarray_used_size(k);
|
|
keys = grib_sarray_get_array(hin->context, k);
|
|
grib_sarray_delete(hin->context, k);
|
|
if (*nkeys > 0) {
|
|
// Do the pack if something was copied
|
|
*err = grib_set_long(hout, "pack", 1);
|
|
}
|
|
codes_bufr_keys_iterator_delete(kiter);
|
|
return keys;
|
|
}
|
|
|
|
int codes_bufr_copy_data(grib_handle* hin, grib_handle* hout)
|
|
{
|
|
bufr_keys_iterator* kiter = NULL;
|
|
int err = 0;
|
|
int nkeys = 0;
|
|
|
|
if (hin == NULL || hout == NULL) {
|
|
return GRIB_NULL_HANDLE;
|
|
}
|
|
|
|
kiter = codes_bufr_data_section_keys_iterator_new(hin);
|
|
if (!kiter)
|
|
return GRIB_INTERNAL_ERROR;
|
|
|
|
while (codes_bufr_keys_iterator_next(kiter)) {
|
|
char* name = codes_bufr_keys_iterator_get_name(kiter);
|
|
// if the copy fails we want to keep copying without any error messages.
|
|
// This is because the copy can be between structures that are not
|
|
// identical and we want to copy what can be copied and skip what
|
|
// cannot be copied because is not in the output handle
|
|
err = codes_copy_key(hin, hout, name, GRIB_TYPE_UNDEFINED);
|
|
if (err == 0)
|
|
nkeys++;
|
|
}
|
|
|
|
if (nkeys > 0) {
|
|
// Do the pack if something was copied
|
|
err = grib_set_long(hout, "pack", 1);
|
|
}
|
|
|
|
codes_bufr_keys_iterator_delete(kiter);
|
|
return err;
|
|
}
|
|
|
|
#define BUFR_SECTION0_LEN 8 // BUFR section 0 is always 8 bytes long
|
|
static int bufr_extract_edition(const void* message, long* edition)
|
|
{
|
|
const long nbits_edition = 8;
|
|
long pos_edition = 7 * 8;
|
|
const unsigned char* pMessage = (const unsigned char*)message;
|
|
|
|
*edition = (long)grib_decode_unsigned_long(pMessage, &pos_edition, nbits_edition);
|
|
return GRIB_SUCCESS;
|
|
}
|
|
// The ECMWF BUFR local use section
|
|
static int bufr_decode_rdb_keys(const void* message, long offset_section2, codes_bufr_header* hdr)
|
|
{
|
|
const unsigned char* pMessage = (const unsigned char*)message;
|
|
long nbits_rdbType = 1 * 8;
|
|
long pos_rdbType = (offset_section2 + 4) * 8;
|
|
long nbits_oldSubtype = 1 * 8;
|
|
long pos_oldSubtype = (offset_section2 + 5) * 8;
|
|
|
|
long nbits_qualityControl = 1 * 8;
|
|
long pos_qualityControl = (offset_section2 + 48) * 8;
|
|
long nbits_newSubtype = 2 * 8;
|
|
long pos_newSubtype = (offset_section2 + 49) * 8;
|
|
long nbits_daLoop = 1 * 8;
|
|
long pos_daLoop = (offset_section2 + 51) * 8;
|
|
|
|
long start = 0;
|
|
const long offset_keyData = offset_section2 + 6;
|
|
const long offset_rdbtime = offset_section2 + 38;
|
|
const long offset_rectime = offset_section2 + 41;
|
|
|
|
unsigned char* p = (unsigned char*)message + offset_keyData;
|
|
|
|
DEBUG_ASSERT(hdr->ecmwfLocalSectionPresent);
|
|
|
|
hdr->rdbType = (long)grib_decode_unsigned_long(pMessage, &pos_rdbType, nbits_rdbType);
|
|
hdr->oldSubtype = (long)grib_decode_unsigned_long(pMessage, &pos_oldSubtype, nbits_oldSubtype);
|
|
|
|
start = 0;
|
|
hdr->localYear = (long)grib_decode_unsigned_long(p, &start, 12);
|
|
hdr->localMonth = (long)grib_decode_unsigned_long(p, &start, 4);
|
|
hdr->localDay = (long)grib_decode_unsigned_long(p, &start, 6);
|
|
hdr->localHour = (long)grib_decode_unsigned_long(p, &start, 5);
|
|
hdr->localMinute = (long)grib_decode_unsigned_long(p, &start, 6);
|
|
hdr->localSecond = (long)grib_decode_unsigned_long(p, &start, 6);
|
|
|
|
// rdbtime
|
|
p = (unsigned char*)message + offset_rdbtime;
|
|
start = 0;
|
|
hdr->rdbtimeDay = (long)grib_decode_unsigned_long(p, &start, 6);
|
|
hdr->rdbtimeHour = (long)grib_decode_unsigned_long(p, &start, 5);
|
|
hdr->rdbtimeMinute = (long)grib_decode_unsigned_long(p, &start, 6);
|
|
hdr->rdbtimeSecond = (long)grib_decode_unsigned_long(p, &start, 6);
|
|
|
|
// rectime
|
|
p = (unsigned char*)message + offset_rectime;
|
|
start = 0;
|
|
hdr->rectimeDay = (long)grib_decode_unsigned_long(p, &start, 6);
|
|
hdr->rectimeHour = (long)grib_decode_unsigned_long(p, &start, 5);
|
|
hdr->rectimeMinute = (long)grib_decode_unsigned_long(p, &start, 6);
|
|
hdr->rectimeSecond = (long)grib_decode_unsigned_long(p, &start, 6);
|
|
hdr->restricted = (long)grib_decode_unsigned_long(p, &start, 1);
|
|
|
|
hdr->qualityControl = (long)grib_decode_unsigned_long(pMessage, &pos_qualityControl, nbits_qualityControl);
|
|
hdr->newSubtype = (long)grib_decode_unsigned_long(pMessage, &pos_newSubtype, nbits_newSubtype);
|
|
hdr->daLoop = (long)grib_decode_unsigned_long(pMessage, &pos_daLoop, nbits_daLoop);
|
|
hdr->rdbSubtype = (hdr->oldSubtype < 255) ? hdr->oldSubtype : hdr->newSubtype;
|
|
|
|
return GRIB_SUCCESS;
|
|
}
|
|
|
|
#define IDENT_LEN 9 // 8 chars plus the final 0 terminator
|
|
|
|
// The ECMWF BUFR local use section
|
|
static int bufr_decode_extra_rdb_keys(const void* message, long offset_section2, codes_bufr_header* hdr)
|
|
{
|
|
bool isSatelliteType = false;
|
|
long start = 0;
|
|
const long offset_keyData = offset_section2 + 6;
|
|
const long offset_keyMore = offset_section2 + 19; // 8 bytes long
|
|
const long offset_keySat = offset_section2 + 27; // 9 bytes long
|
|
|
|
unsigned char* pKeyData = (unsigned char*)message + offset_keyData;
|
|
char* pKeyMore = (char*)message + offset_keyMore;
|
|
|
|
DEBUG_ASSERT(hdr->ecmwfLocalSectionPresent);
|
|
|
|
if (hdr->rdbType == 2 || hdr->rdbType == 3 || hdr->rdbType == 8 || hdr->rdbType == 12 || hdr->rdbType == 30) {
|
|
isSatelliteType = true;
|
|
}
|
|
if (isSatelliteType || hdr->numberOfSubsets > 1) {
|
|
hdr->isSatellite = 1;
|
|
}
|
|
else {
|
|
hdr->isSatellite = 0;
|
|
}
|
|
|
|
if (hdr->isSatellite) {
|
|
unsigned char* pKeyMoreLong = (unsigned char*)message + offset_keyMore; // as an integer
|
|
unsigned char* pKeySat = (unsigned char*)message + offset_keySat;
|
|
unsigned long lValue = 0;
|
|
start = 40;
|
|
lValue = grib_decode_unsigned_long(pKeyData, &start, 26);
|
|
hdr->localLongitude1 = (lValue - 18000000.0) / 100000.0;
|
|
start = 72;
|
|
lValue = grib_decode_unsigned_long(pKeyData, &start, 25);
|
|
hdr->localLatitude1 = (lValue - 9000000.0) / 100000.0;
|
|
start = 0;
|
|
lValue = grib_decode_unsigned_long(pKeyMoreLong, &start, 26);
|
|
hdr->localLongitude2 = (lValue - 18000000.0) / 100000.0;
|
|
start = 32;
|
|
lValue = grib_decode_unsigned_long(pKeyMoreLong, &start, 25);
|
|
hdr->localLatitude2 = (lValue - 9000000.0) / 100000.0;
|
|
|
|
if (hdr->oldSubtype == 255 || hdr->numberOfSubsets > 255 ||
|
|
(hdr->oldSubtype >= 121 && hdr->oldSubtype <= 130) ||
|
|
hdr->oldSubtype == 31) {
|
|
start = 0;
|
|
hdr->localNumberOfObservations = (long)grib_decode_unsigned_long(pKeySat, &start, 16);
|
|
start = 16;
|
|
hdr->satelliteID = (long)grib_decode_unsigned_long(pKeySat, &start, 16);
|
|
}
|
|
else {
|
|
start = 0;
|
|
hdr->localNumberOfObservations = (long)grib_decode_unsigned_long(pKeySat, &start, 8);
|
|
start = 8;
|
|
hdr->satelliteID = (long)grib_decode_unsigned_long(pKeySat, &start, 16);
|
|
}
|
|
}
|
|
else {
|
|
size_t i = 0;
|
|
unsigned long lValue = 0;
|
|
char* pTemp = NULL;
|
|
char temp[IDENT_LEN] = {0,};
|
|
|
|
start = 72;
|
|
lValue = grib_decode_unsigned_long(pKeyData, &start, 25);
|
|
hdr->localLatitude = (lValue - 9000000.0) / 100000.0;
|
|
start = 40;
|
|
lValue = grib_decode_unsigned_long(pKeyData, &start, 26);
|
|
hdr->localLongitude = (lValue - 18000000.0) / 100000.0;
|
|
|
|
// interpret keyMore as a string. Copy to a temporary
|
|
for (i = 0; i < IDENT_LEN - 1; ++i) {
|
|
temp[i] = *pKeyMore++;
|
|
}
|
|
temp[i] = '\0';
|
|
pTemp = temp;
|
|
string_lrtrim(&pTemp, 1, 1); // Trim left and right
|
|
strncpy(hdr->ident, pTemp, IDENT_LEN - 1);
|
|
}
|
|
|
|
return GRIB_SUCCESS;
|
|
}
|
|
|
|
static int bufr_decode_edition3(const void* message, codes_bufr_header* hdr)
|
|
{
|
|
int err = GRIB_SUCCESS;
|
|
const unsigned char* pMessage = (const unsigned char*)message;
|
|
|
|
unsigned long totalLength = 0;
|
|
const long nbits_totalLength = 3 * 8;
|
|
long pos_totalLength = 4 * 8;
|
|
|
|
unsigned long section1Length = 0;
|
|
const long nbits_section1Length = 3 * 8;
|
|
long pos_section1Length = 8 * 8;
|
|
|
|
long nbits_masterTableNumber = 1 * 8;
|
|
long pos_masterTableNumber = 11 * 8;
|
|
|
|
long nbits_bufrHeaderSubCentre = 1 * 8;
|
|
long pos_bufrHeaderSubCentre = 12 * 8;
|
|
|
|
long nbits_bufrHeaderCentre = 1 * 8;
|
|
long pos_bufrHeaderCentre = 13 * 8;
|
|
|
|
long nbits_updateSequenceNumber = 1 * 8;
|
|
long pos_updateSequenceNumber = 14 * 8;
|
|
|
|
long section1Flags = 0;
|
|
long nbits_section1Flags = 1 * 8;
|
|
long pos_section1Flags = 15 * 8;
|
|
|
|
long nbits_dataCategory = 1 * 8;
|
|
long pos_dataCategory = 16 * 8;
|
|
|
|
long nbits_dataSubCategory = 1 * 8;
|
|
long pos_dataSubCategory = 17 * 8;
|
|
|
|
long nbits_masterTablesVersionNumber = 1 * 8;
|
|
long pos_masterTablesVersionNumber = 18 * 8;
|
|
|
|
long nbits_localTablesVersionNumber = 1 * 8;
|
|
long pos_localTablesVersionNumber = 19 * 8;
|
|
|
|
const long typicalCentury = 21; // This century
|
|
long typicalYearOfCentury = 0;
|
|
long nbits_typicalYearOfCentury = 1 * 8;
|
|
long pos_typicalYearOfCentury = 20 * 8;
|
|
|
|
long nbits_typicalMonth = 1 * 8;
|
|
long pos_typicalMonth = 21 * 8;
|
|
|
|
long nbits_typicalDay = 1 * 8;
|
|
long pos_typicalDay = 22 * 8;
|
|
|
|
long nbits_typicalHour = 1 * 8;
|
|
long pos_typicalHour = 23 * 8;
|
|
|
|
long nbits_typicalMinute = 1 * 8;
|
|
long pos_typicalMinute = 24 * 8;
|
|
|
|
long section2Length = 0;
|
|
long offset_section2 = 0;
|
|
long offset_section3 = 0;
|
|
long nbits_numberOfSubsets = 2 * 8;
|
|
long pos_numberOfSubsets = 0; //depends on offset_section3
|
|
|
|
unsigned long section3Flags;
|
|
long nbits_section3Flags = 1 * 8;
|
|
long pos_section3Flags = 0; //depends on offset_section3
|
|
|
|
totalLength = grib_decode_unsigned_long(pMessage, &pos_totalLength, nbits_totalLength);
|
|
if (totalLength != hdr->message_size) {
|
|
return GRIB_WRONG_LENGTH;
|
|
}
|
|
section1Length = grib_decode_unsigned_long(pMessage, &pos_section1Length, nbits_section1Length);
|
|
hdr->masterTableNumber = (long)grib_decode_unsigned_long(pMessage, &pos_masterTableNumber, nbits_masterTableNumber);
|
|
hdr->bufrHeaderSubCentre = (long)grib_decode_unsigned_long(pMessage, &pos_bufrHeaderSubCentre, nbits_bufrHeaderSubCentre);
|
|
hdr->bufrHeaderCentre = (long)grib_decode_unsigned_long(pMessage, &pos_bufrHeaderCentre, nbits_bufrHeaderCentre);
|
|
hdr->updateSequenceNumber = (long)grib_decode_unsigned_long(pMessage, &pos_updateSequenceNumber, nbits_updateSequenceNumber);
|
|
section1Flags = (long)grib_decode_unsigned_long(pMessage, &pos_section1Flags, nbits_section1Flags);
|
|
hdr->dataCategory = (long)grib_decode_unsigned_long(pMessage, &pos_dataCategory, nbits_dataCategory);
|
|
hdr->dataSubCategory = (long)grib_decode_unsigned_long(pMessage, &pos_dataSubCategory, nbits_dataSubCategory);
|
|
hdr->masterTablesVersionNumber = (long)grib_decode_unsigned_long(
|
|
pMessage, &pos_masterTablesVersionNumber, nbits_masterTablesVersionNumber);
|
|
hdr->localTablesVersionNumber = (long)grib_decode_unsigned_long(pMessage, &pos_localTablesVersionNumber, nbits_localTablesVersionNumber);
|
|
typicalYearOfCentury = (long)grib_decode_unsigned_long(pMessage, &pos_typicalYearOfCentury, nbits_typicalYearOfCentury);
|
|
hdr->typicalYear = (typicalCentury - 1) * 100 + typicalYearOfCentury;
|
|
hdr->typicalMonth = (long)grib_decode_unsigned_long(pMessage, &pos_typicalMonth, nbits_typicalMonth);
|
|
hdr->typicalDay = (long)grib_decode_unsigned_long(pMessage, &pos_typicalDay, nbits_typicalDay);
|
|
hdr->typicalHour = (long)grib_decode_unsigned_long(pMessage, &pos_typicalHour, nbits_typicalHour);
|
|
hdr->typicalMinute = (long)grib_decode_unsigned_long(pMessage, &pos_typicalMinute, nbits_typicalMinute);
|
|
hdr->typicalSecond = 0;
|
|
hdr->typicalDate = hdr->typicalYear * 10000 + hdr->typicalMonth * 100 + hdr->typicalDay;
|
|
hdr->typicalTime = hdr->typicalHour * 10000 + hdr->typicalMinute * 100 + hdr->typicalSecond;
|
|
|
|
offset_section2 = BUFR_SECTION0_LEN + section1Length; //bytes
|
|
section2Length = 0;
|
|
hdr->localSectionPresent = (section1Flags != 0);
|
|
if (hdr->localSectionPresent) {
|
|
long pos_section2Length;
|
|
const long nbits_section2Length = 3 * 8;
|
|
pos_section2Length = offset_section2 * 8;
|
|
|
|
section2Length = grib_decode_unsigned_long(pMessage, &pos_section2Length, nbits_section2Length);
|
|
|
|
if (hdr->bufrHeaderCentre == 98) {
|
|
hdr->ecmwfLocalSectionPresent = 1;
|
|
err = bufr_decode_rdb_keys(message, offset_section2, hdr);
|
|
}
|
|
}
|
|
|
|
offset_section3 = BUFR_SECTION0_LEN + section1Length + section2Length; //bytes
|
|
pos_numberOfSubsets = (offset_section3 + 4) * 8;
|
|
hdr->numberOfSubsets = grib_decode_unsigned_long(pMessage, &pos_numberOfSubsets, nbits_numberOfSubsets);
|
|
|
|
pos_section3Flags = (offset_section3 + 6) * 8;
|
|
section3Flags = grib_decode_unsigned_long(pMessage, &pos_section3Flags, nbits_section3Flags);
|
|
hdr->observedData = (section3Flags & 1 << 7) ? 1 : 0;
|
|
hdr->compressedData = (section3Flags & 1 << 6) ? 1 : 0;
|
|
|
|
if (hdr->ecmwfLocalSectionPresent && hdr->bufrHeaderCentre == 98 && section2Length == 52) {
|
|
err = bufr_decode_extra_rdb_keys(message, offset_section2, hdr);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int bufr_decode_edition4(const void* message, codes_bufr_header* hdr)
|
|
{
|
|
int err = GRIB_SUCCESS;
|
|
const unsigned char* pMessage = (const unsigned char*)message;
|
|
|
|
unsigned long totalLength = 0;
|
|
const long nbits_totalLength = 3 * 8;
|
|
long pos_totalLength = 4 * 8;
|
|
|
|
unsigned long section1Length;
|
|
const long nbits_section1Length = 3 * 8;
|
|
long pos_section1Length = 8 * 8;
|
|
|
|
long nbits_masterTableNumber = 1 * 8;
|
|
long pos_masterTableNumber = 11 * 8;
|
|
|
|
long nbits_bufrHeaderCentre = 2 * 8;
|
|
long pos_bufrHeaderCentre = 12 * 8;
|
|
|
|
long nbits_bufrHeaderSubCentre = 2 * 8;
|
|
long pos_bufrHeaderSubCentre = 14 * 8;
|
|
|
|
long nbits_updateSequenceNumber = 1 * 8;
|
|
long pos_updateSequenceNumber = 16 * 8;
|
|
|
|
long section1Flags = 0;
|
|
long nbits_section1Flags = 1 * 8;
|
|
long pos_section1Flags = 17 * 8;
|
|
|
|
long nbits_dataCategory = 1 * 8;
|
|
long pos_dataCategory = 18 * 8;
|
|
|
|
long nbits_internationalDataSubCategory = 1 * 8;
|
|
long pos_internationalDataSubCategory = 19 * 8;
|
|
|
|
long nbits_dataSubCategory = 1 * 8;
|
|
long pos_dataSubCategory = 20 * 8;
|
|
|
|
long nbits_masterTablesVersionNumber = 1 * 8;
|
|
long pos_masterTablesVersionNumber = 21 * 8;
|
|
|
|
long nbits_localTablesVersionNumber = 1 * 8;
|
|
long pos_localTablesVersionNumber = 22 * 8;
|
|
|
|
long typicalYear2 = 0; // corrected
|
|
long nbits_typicalYear = 2 * 8;
|
|
long pos_typicalYear = 23 * 8;
|
|
|
|
long nbits_typicalMonth = 1 * 8;
|
|
long pos_typicalMonth = 25 * 8;
|
|
|
|
long nbits_typicalDay = 1 * 8;
|
|
long pos_typicalDay = 26 * 8;
|
|
|
|
long nbits_typicalHour = 1 * 8;
|
|
long pos_typicalHour = 27 * 8;
|
|
|
|
long nbits_typicalMinute = 1 * 8;
|
|
long pos_typicalMinute = 28 * 8;
|
|
|
|
long nbits_typicalSecond = 1 * 8;
|
|
long pos_typicalSecond = 29 * 8;
|
|
|
|
unsigned long section2Length = 0;
|
|
long offset_section2 = 0;
|
|
long offset_section3 = 0;
|
|
long nbits_numberOfSubsets = 2 * 8;
|
|
long pos_numberOfSubsets = 0; //depends on offset_section3
|
|
|
|
unsigned long section3Flags;
|
|
long nbits_section3Flags = 1 * 8;
|
|
long pos_section3Flags = 0; //depends on offset_section3
|
|
|
|
totalLength = grib_decode_unsigned_long(pMessage, &pos_totalLength, nbits_totalLength);
|
|
if (totalLength != hdr->message_size) {
|
|
return GRIB_WRONG_LENGTH;
|
|
}
|
|
section1Length = grib_decode_unsigned_long(pMessage, &pos_section1Length, nbits_section1Length);
|
|
hdr->masterTableNumber = (long)grib_decode_unsigned_long(pMessage, &pos_masterTableNumber, nbits_masterTableNumber);
|
|
hdr->bufrHeaderCentre = (long)grib_decode_unsigned_long(pMessage, &pos_bufrHeaderCentre, nbits_bufrHeaderCentre);
|
|
hdr->bufrHeaderSubCentre = (long)grib_decode_unsigned_long(pMessage, &pos_bufrHeaderSubCentre, nbits_bufrHeaderSubCentre);
|
|
hdr->updateSequenceNumber = (long)grib_decode_unsigned_long(pMessage, &pos_updateSequenceNumber, nbits_updateSequenceNumber);
|
|
section1Flags = (long)grib_decode_unsigned_long(pMessage, &pos_section1Flags, nbits_section1Flags);
|
|
hdr->dataCategory = (long)grib_decode_unsigned_long(pMessage, &pos_dataCategory, nbits_dataCategory);
|
|
hdr->internationalDataSubCategory = (long)grib_decode_unsigned_long(pMessage, &pos_internationalDataSubCategory, nbits_internationalDataSubCategory);
|
|
hdr->dataSubCategory = (long)grib_decode_unsigned_long(pMessage, &pos_dataSubCategory, nbits_dataSubCategory);
|
|
hdr->masterTablesVersionNumber = (long)grib_decode_unsigned_long(pMessage, &pos_masterTablesVersionNumber, nbits_masterTablesVersionNumber);
|
|
hdr->localTablesVersionNumber = (long)grib_decode_unsigned_long(pMessage, &pos_localTablesVersionNumber, nbits_localTablesVersionNumber);
|
|
|
|
hdr->typicalYear = (long)grib_decode_unsigned_long(pMessage, &pos_typicalYear, nbits_typicalYear);
|
|
typicalYear2 = hdr->typicalYear < 100 ? 2000 + hdr->typicalYear : hdr->typicalYear; //ECC-556
|
|
hdr->typicalMonth = (long)grib_decode_unsigned_long(pMessage, &pos_typicalMonth, nbits_typicalMonth);
|
|
hdr->typicalDay = (long)grib_decode_unsigned_long(pMessage, &pos_typicalDay, nbits_typicalDay);
|
|
hdr->typicalHour = (long)grib_decode_unsigned_long(pMessage, &pos_typicalHour, nbits_typicalHour);
|
|
hdr->typicalMinute = (long)grib_decode_unsigned_long(pMessage, &pos_typicalMinute, nbits_typicalMinute);
|
|
hdr->typicalSecond = (long)grib_decode_unsigned_long(pMessage, &pos_typicalSecond, nbits_typicalSecond);
|
|
hdr->typicalDate = typicalYear2 * 10000 + hdr->typicalMonth * 100 + hdr->typicalDay;
|
|
hdr->typicalTime = hdr->typicalHour * 10000 + hdr->typicalMinute * 100 + hdr->typicalSecond;
|
|
|
|
offset_section2 = BUFR_SECTION0_LEN + section1Length; //bytes
|
|
section2Length = 0;
|
|
hdr->localSectionPresent = (section1Flags != 0);
|
|
if (hdr->localSectionPresent) {
|
|
long pos_section2Length;
|
|
const long nbits_section2Length = 3 * 8;
|
|
pos_section2Length = offset_section2 * 8;
|
|
|
|
section2Length = grib_decode_unsigned_long(pMessage, &pos_section2Length, nbits_section2Length);
|
|
|
|
if (hdr->bufrHeaderCentre == 98) {
|
|
hdr->ecmwfLocalSectionPresent = 1;
|
|
err = bufr_decode_rdb_keys(message, offset_section2, hdr);
|
|
}
|
|
}
|
|
|
|
offset_section3 = BUFR_SECTION0_LEN + section1Length + section2Length; //bytes
|
|
pos_numberOfSubsets = (offset_section3 + 4) * 8;
|
|
hdr->numberOfSubsets = grib_decode_unsigned_long(pMessage, &pos_numberOfSubsets, nbits_numberOfSubsets);
|
|
|
|
pos_section3Flags = (offset_section3 + 6) * 8;
|
|
section3Flags = grib_decode_unsigned_long(pMessage, &pos_section3Flags, nbits_section3Flags);
|
|
hdr->observedData = (section3Flags & 1 << 7) ? 1 : 0;
|
|
hdr->compressedData = (section3Flags & 1 << 6) ? 1 : 0;
|
|
|
|
if (hdr->ecmwfLocalSectionPresent && hdr->bufrHeaderCentre == 98 && section2Length == 52) {
|
|
err = bufr_decode_extra_rdb_keys(message, offset_section2, hdr);
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int bufr_decode_header(grib_context* c, const void* message, off_t offset, size_t size, codes_bufr_header* hdr)
|
|
{
|
|
int err = GRIB_SUCCESS;
|
|
|
|
hdr->message_offset = (unsigned long)offset;
|
|
hdr->message_size = (unsigned long)size;
|
|
|
|
err = bufr_extract_edition(message, &hdr->edition);
|
|
if (err) return err;
|
|
|
|
if (hdr->edition == 3) {
|
|
err = bufr_decode_edition3(message, hdr);
|
|
}
|
|
else if (hdr->edition == 4) {
|
|
err = bufr_decode_edition4(message, hdr);
|
|
}
|
|
else {
|
|
grib_context_log(c, GRIB_LOG_ERROR, "Unsupported BUFR edition: %ld", hdr->edition);
|
|
err = GRIB_DECODING_ERROR;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
|
|
static int count_bufr_messages(grib_context* c, FILE* f, int* n, int strict_mode)
|
|
{
|
|
int err = 0;
|
|
void* mesg = NULL;
|
|
size_t size = 0;
|
|
off_t offset = 0;
|
|
int done = 0;
|
|
|
|
*n = 0;
|
|
if (!c)
|
|
c = grib_context_get_default();
|
|
|
|
while (!done) {
|
|
mesg = wmo_read_bufr_from_file_malloc(f, 0, &size, &offset, &err);
|
|
//printf("Count so far=%d, mesg=%x, err=%d (%s)\n", *n, mesg, err, grib_get_error_message(err));
|
|
if (!mesg) {
|
|
if (err == GRIB_END_OF_FILE || err == GRIB_PREMATURE_END_OF_FILE) {
|
|
done = 1; // reached the end
|
|
break;
|
|
}
|
|
if (strict_mode)
|
|
return GRIB_DECODING_ERROR;
|
|
}
|
|
if (mesg && !err) {
|
|
grib_context_free(c, mesg);
|
|
}
|
|
(*n)++;
|
|
if (*n >= INT_MAX/100) {
|
|
grib_context_log(c, GRIB_LOG_ERROR, "Limit reached: looped %d times without finding a valid BUFR message", *n);
|
|
done = 1;
|
|
err = GRIB_INTERNAL_ERROR;
|
|
}
|
|
}
|
|
(void)done;
|
|
rewind(f);
|
|
if (err == GRIB_END_OF_FILE)
|
|
err = GRIB_SUCCESS;
|
|
return err;
|
|
}
|
|
|
|
int codes_bufr_extract_headers_malloc(grib_context* c, const char* filename, codes_bufr_header** result, int* num_messages, int strict_mode)
|
|
{
|
|
int err = 0, i = 0;
|
|
FILE* fp = NULL;
|
|
void* mesg = NULL;
|
|
size_t size = 0;
|
|
off_t offset = 0;
|
|
|
|
if (!c)
|
|
c = grib_context_get_default();
|
|
if (path_is_directory(filename)) {
|
|
grib_context_log(c, GRIB_LOG_ERROR, "%s: \"%s\" is a directory", __func__, filename);
|
|
return GRIB_IO_PROBLEM;
|
|
}
|
|
fp = fopen(filename, "rb");
|
|
if (!fp) {
|
|
grib_context_log(c, GRIB_LOG_ERROR, "%s: Unable to read file \"%s\"", __func__, filename);
|
|
perror(filename);
|
|
return GRIB_IO_PROBLEM;
|
|
}
|
|
err = count_bufr_messages(c, fp, num_messages, strict_mode);
|
|
if (err) {
|
|
grib_context_log(c, GRIB_LOG_ERROR, "%s: Unable to count BUFR messages in file \"%s\"", __func__, filename);
|
|
fclose(fp);
|
|
return err;
|
|
}
|
|
|
|
size = *num_messages;
|
|
if (size == 0) {
|
|
grib_context_log(c, GRIB_LOG_ERROR, "%s: No BUFR messages in file \"%s\"", __func__, filename);
|
|
return GRIB_INVALID_MESSAGE;
|
|
}
|
|
*result = (codes_bufr_header*)calloc(size, sizeof(codes_bufr_header));
|
|
if (!*result) {
|
|
fclose(fp);
|
|
return GRIB_OUT_OF_MEMORY;
|
|
}
|
|
i = 0;
|
|
while (err != GRIB_END_OF_FILE) {
|
|
if (i >= *num_messages)
|
|
break;
|
|
mesg = wmo_read_bufr_from_file_malloc(fp, 0, &size, &offset, &err);
|
|
if (mesg != NULL && err == 0) {
|
|
int err2 = bufr_decode_header(c, mesg, offset, size, &(*result)[i]);
|
|
if (err2) {
|
|
fclose(fp);
|
|
return err2;
|
|
}
|
|
grib_context_free(c, mesg);
|
|
}
|
|
if (mesg && err) {
|
|
if (strict_mode) {
|
|
fclose(fp);
|
|
grib_context_free(c, mesg);
|
|
return GRIB_DECODING_ERROR;
|
|
}
|
|
}
|
|
if (!mesg) {
|
|
if (err != GRIB_END_OF_FILE && err != GRIB_PREMATURE_END_OF_FILE) {
|
|
// An error occurred
|
|
grib_context_log(c, GRIB_LOG_ERROR, "%s: Unable to read BUFR message", __func__);
|
|
if (strict_mode) {
|
|
fclose(fp);
|
|
return GRIB_DECODING_ERROR;
|
|
}
|
|
}
|
|
}
|
|
++i;
|
|
}
|
|
|
|
fclose(fp);
|
|
return GRIB_SUCCESS;
|
|
}
|
|
|
|
static const char* codes_bufr_header_get_centre_name(long edition, long centre_code)
|
|
{
|
|
(void)edition;
|
|
switch (centre_code) {
|
|
case 1:
|
|
return "ammc";
|
|
case 4:
|
|
return "rums";
|
|
case 7:
|
|
return "kwbc";
|
|
case 24:
|
|
return "fapr";
|
|
case 28:
|
|
return "vabb";
|
|
case 29:
|
|
return "dems";
|
|
case 34:
|
|
return "rjtd";
|
|
case 38:
|
|
return "babj";
|
|
case 40:
|
|
return "rksl";
|
|
case 41:
|
|
return "sabm";
|
|
case 46:
|
|
return "sbsj";
|
|
case 54:
|
|
return "cwao";
|
|
case 58:
|
|
return "fnmo";
|
|
case 69:
|
|
return "nzkl";
|
|
case 74:
|
|
return "egrr";
|
|
case 78:
|
|
return "edzw";
|
|
case 80:
|
|
return "cnmc";
|
|
case 82:
|
|
return "eswi";
|
|
case 84:
|
|
return "lfpw";
|
|
case 85:
|
|
return "lfpw";
|
|
case 86:
|
|
return "efkl";
|
|
case 88:
|
|
return "enmi";
|
|
case 94:
|
|
return "ekmi";
|
|
case 98:
|
|
return "ecmf";
|
|
case 173:
|
|
return "nasa";
|
|
case 195:
|
|
return "wiix";
|
|
case 204:
|
|
return "niwa";
|
|
case 213:
|
|
return "birk";
|
|
case 214:
|
|
return "lemm";
|
|
case 215:
|
|
return "lssw";
|
|
case 218:
|
|
return "habp";
|
|
case 224:
|
|
return "lowm";
|
|
case 227:
|
|
return "ebum";
|
|
case 233:
|
|
return "eidb";
|
|
case 235:
|
|
return "ingv";
|
|
case 239:
|
|
return "crfc";
|
|
case 244:
|
|
return "vuwien";
|
|
case 245:
|
|
return "knmi";
|
|
case 246:
|
|
return "ifmk";
|
|
case 247:
|
|
return "hadc";
|
|
case 250:
|
|
return "cosmo";
|
|
case 252:
|
|
return "mpim";
|
|
case 254:
|
|
return "eums";
|
|
case 255:
|
|
return "consensus";
|
|
case 291:
|
|
return "anso";
|
|
case 292:
|
|
return "ufz";
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
#if defined(BUFR_PROCESS_CODE_TABLE)
|
|
// TODO(masn): Not efficient as it opens the code table every time
|
|
static char* codes_bufr_header_get_centre_name(long edition, long centre_code)
|
|
{
|
|
char full_path[2014] = {0,};
|
|
char line[1024];
|
|
FILE *f = NULL;
|
|
const char* defs_path = grib_definition_path(NULL);
|
|
|
|
if (edition == 3) snprintf(full_path, 2014, "%s/common/c-1.table", defs_path);
|
|
else if (edition == 4) snprintf(full_path, 2014, "%s/common/c-11.table", defs_path);
|
|
else return NULL;
|
|
|
|
f = codes_fopen(full_path, "r");
|
|
if (!f) return NULL;
|
|
|
|
while(fgets(line,sizeof(line)-1,f)) {
|
|
char* p = line;
|
|
int code = 0;
|
|
char abbreviation[32] = {0,};
|
|
char* q = abbreviation;
|
|
|
|
line[strlen(line)-1] = 0;
|
|
|
|
while(*p != '\0' && isspace(*p)) p++;
|
|
|
|
if(*p == '#')
|
|
continue;
|
|
|
|
while(*p != '\0' && isspace(*p)) p++;
|
|
|
|
if( *p =='\0' ) continue;
|
|
|
|
Assert(isdigit(*p));
|
|
while(*p != '\0') {
|
|
if(isspace(*p)) break;
|
|
code *= 10;
|
|
code += *p - '0';
|
|
p++;
|
|
}
|
|
|
|
while(*p != '\0' && isspace(*p)) p++;
|
|
|
|
while(*p != '\0') {
|
|
if(isspace(*p)) break;
|
|
*q++ = *p++;
|
|
}
|
|
*q = 0;
|
|
if (code == centre_code) {
|
|
fclose(f);
|
|
return strdup(abbreviation);
|
|
}
|
|
}
|
|
fclose(f);
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
int codes_bufr_header_get_string(codes_bufr_header* bh, const char* key, char* val, size_t* len)
|
|
{
|
|
static const char* NOT_FOUND = "not_found";
|
|
bool isEcmwfLocal = false;
|
|
Assert(bh);
|
|
Assert(key);
|
|
*len = strlen(NOT_FOUND); // By default
|
|
|
|
isEcmwfLocal = (bh->ecmwfLocalSectionPresent == 1);
|
|
Assert(!(isEcmwfLocal && bh->bufrHeaderCentre != 98));
|
|
Assert(!(bh->ecmwfLocalSectionPresent && !bh->localSectionPresent));
|
|
|
|
if (strcmp(key, "message_offset") == 0)
|
|
*len = snprintf(val, 32, "%lu", bh->message_offset);
|
|
else if (strcmp(key, "offset") == 0)
|
|
*len = snprintf(val, 32, "%lu", bh->message_offset);
|
|
else if (strcmp(key, "message_size") == 0)
|
|
*len = snprintf(val, 32, "%lu", bh->message_size);
|
|
else if (strcmp(key, "totalLength") == 0)
|
|
*len = snprintf(val, 32, "%lu", bh->message_size);
|
|
else if (strcmp(key, "edition") == 0)
|
|
*len = snprintf(val, 32, "%ld", bh->edition);
|
|
else if (strcmp(key, "masterTableNumber") == 0)
|
|
*len = snprintf(val, 32, "%ld", bh->masterTableNumber);
|
|
else if (strcmp(key, "bufrHeaderSubCentre") == 0)
|
|
*len = snprintf(val, 32, "%ld", bh->bufrHeaderSubCentre);
|
|
else if (strcmp(key, "bufrHeaderCentre") == 0)
|
|
*len = snprintf(val, 32, "%ld", bh->bufrHeaderCentre);
|
|
|
|
else if (strcmp(key, "centre") == 0) {
|
|
const char* centre_str = codes_bufr_header_get_centre_name(bh->edition, bh->bufrHeaderCentre);
|
|
if (centre_str)
|
|
*len = snprintf(val, 32, "%s", centre_str);
|
|
else
|
|
*len = snprintf(val, 32, "%ld", bh->bufrHeaderCentre);
|
|
}
|
|
|
|
else if (strcmp(key, "updateSequenceNumber") == 0)
|
|
*len = snprintf(val, 32, "%ld", bh->updateSequenceNumber);
|
|
else if (strcmp(key, "dataCategory") == 0)
|
|
*len = snprintf(val, 32, "%ld", bh->dataCategory);
|
|
else if (strcmp(key, "dataSubCategory") == 0)
|
|
*len = snprintf(val, 32, "%ld", bh->dataSubCategory);
|
|
else if (strcmp(key, "masterTablesVersionNumber") == 0)
|
|
*len = snprintf(val, 32, "%ld", bh->masterTablesVersionNumber);
|
|
else if (strcmp(key, "localTablesVersionNumber") == 0)
|
|
*len = snprintf(val, 32, "%ld", bh->localTablesVersionNumber);
|
|
else if (strcmp(key, "typicalYear") == 0)
|
|
*len = snprintf(val, 32, "%ld", bh->typicalYear);
|
|
else if (strcmp(key, "typicalMonth") == 0)
|
|
*len = snprintf(val, 32, "%ld", bh->typicalMonth);
|
|
else if (strcmp(key, "typicalDay") == 0)
|
|
*len = snprintf(val, 32, "%ld", bh->typicalDay);
|
|
else if (strcmp(key, "typicalHour") == 0)
|
|
*len = snprintf(val, 32, "%ld", bh->typicalHour);
|
|
else if (strcmp(key, "typicalMinute") == 0)
|
|
*len = snprintf(val, 32, "%ld", bh->typicalMinute);
|
|
else if (strcmp(key, "typicalSecond") == 0)
|
|
*len = snprintf(val, 32, "%ld", bh->typicalSecond);
|
|
else if (strcmp(key, "typicalDate") == 0)
|
|
*len = snprintf(val, 32, "%06ld", bh->typicalDate);
|
|
else if (strcmp(key, "typicalTime") == 0)
|
|
*len = snprintf(val, 32, "%06ld", bh->typicalTime);
|
|
else if (strcmp(key, "internationalDataSubCategory") == 0)
|
|
*len = snprintf(val, 32, "%ld", bh->internationalDataSubCategory);
|
|
else if (strcmp(key, "localSectionPresent") == 0)
|
|
*len = snprintf(val, 32, "%ld", bh->localSectionPresent);
|
|
else if (strcmp(key, "ecmwfLocalSectionPresent") == 0)
|
|
*len = snprintf(val, 32, "%ld", bh->ecmwfLocalSectionPresent);
|
|
|
|
// Local ECMWF keys. Can be absent so must return NOT_FOUND
|
|
else if (strcmp(key, "rdbType") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->rdbType);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "oldSubtype") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->oldSubtype);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "ident") == 0) {
|
|
if (!isEcmwfLocal || strlen(bh->ident) == 0)
|
|
strcpy(val, NOT_FOUND);
|
|
else
|
|
*len = snprintf(val, 32, "%s", bh->ident);
|
|
}
|
|
else if (strcmp(key, "localYear") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->localYear);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "localMonth") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->localMonth);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "localDay") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->localDay);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "localHour") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->localHour);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "localMinute") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->localMinute);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "localSecond") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->localSecond);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "rdbtimeDay") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->rdbtimeDay);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "rdbtimeHour") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->rdbtimeHour);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "rdbtimeMinute") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->rdbtimeMinute);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "rdbtimeSecond") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->rdbtimeSecond);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "rectimeDay") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->rectimeDay);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "rectimeHour") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->rectimeHour);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "rectimeMinute") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->rectimeMinute);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "rectimeSecond") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->rectimeSecond);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "restricted") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->restricted);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "isSatellite") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->isSatellite);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "localLongitude1") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%g", bh->localLongitude1);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "localLatitude1") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%g", bh->localLatitude1);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "localLongitude2") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%g", bh->localLongitude2);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "localLatitude2") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%g", bh->localLatitude2);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "localLatitude") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%g", bh->localLatitude);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "localLongitude") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%g", bh->localLongitude);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "qualityControl") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->qualityControl);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "newSubtype") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->newSubtype);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "rdbSubtype") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->rdbSubtype);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "daLoop") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->daLoop);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "localNumberOfObservations") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->localNumberOfObservations);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
else if (strcmp(key, "satelliteID") == 0) {
|
|
if (isEcmwfLocal)
|
|
*len = snprintf(val, 32, "%ld", bh->satelliteID);
|
|
else
|
|
strcpy(val, NOT_FOUND);
|
|
}
|
|
|
|
else if (strcmp(key, "numberOfSubsets") == 0)
|
|
*len = snprintf(val, 32, "%lu", bh->numberOfSubsets);
|
|
else if (strcmp(key, "observedData") == 0)
|
|
*len = snprintf(val, 32, "%ld", bh->observedData);
|
|
else if (strcmp(key, "compressedData") == 0)
|
|
*len = snprintf(val, 32, "%ld", bh->compressedData);
|
|
else
|
|
return GRIB_NOT_FOUND;
|
|
|
|
return GRIB_SUCCESS;
|
|
}
|
|
|
|
// Returns 1 if the BUFR key is in the header and 0 if it is in the data section
|
|
int codes_bufr_key_is_header(const grib_handle* h, const char* key, int* err)
|
|
{
|
|
const grib_accessor* acc = grib_find_accessor(h, key);
|
|
if (!acc) {
|
|
*err = GRIB_NOT_FOUND;
|
|
return 0;
|
|
}
|
|
*err = GRIB_SUCCESS;
|
|
return ((acc->flags & GRIB_ACCESSOR_FLAG_BUFR_DATA) == 0);
|
|
}
|
|
|
|
// Returns 1 if the BUFR key is a coordinate descriptor
|
|
int codes_bufr_key_is_coordinate(const grib_handle* h, const char* key, int* err)
|
|
{
|
|
const grib_accessor* acc = grib_find_accessor(h, key);
|
|
if (!acc) {
|
|
*err = GRIB_NOT_FOUND;
|
|
return 0;
|
|
}
|
|
*err = GRIB_SUCCESS;
|
|
return ((acc->flags & GRIB_ACCESSOR_FLAG_BUFR_COORD) != 0);
|
|
}
|
|
|
|
int codes_bufr_key_exclude_from_dump(const char* key)
|
|
{
|
|
if (strstr(key, "percentConfidence->percentConfidence->percentConfidence->percentConfidence->percentConfidence")) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|