/***************************************************************************** * FindGRIBMsg() -- Review 12/2002 * * Arthur Taylor / MDL * * PURPOSE * Jumps through a GRIB2 file looking for a specific message. Currently * that message is determined by msgNum which is in the range of 1..n. * In the future we may be searching based on projection or date. * * ARGUMENTS * fp = The current GRIB2 file to look through. (Input) * msgNum = Which message to look for. (Input) * offset = Where in the file the message starts (this is before the * wmo ASCII part if there is one.) (Output) * curMsg = The current # of messages we have looked through. (In/Out) * * FILES/DATABASES: * An already opened "GRIB2" File * * RETURNS: int (could use errSprintf()) * 0 = OK * -1 = Problems reading Section 0. * -2 = Ran out of file. * * HISTORY * 11/2002 Arthur Taylor (MDL/RSIS): Created. * 12/2002 (TK,AC,TB,&MS): Code Review. * 6/2003 Matthew T. Kallio ([email protected]): * "wmo" dimension increased to WMO_HEADER_LEN + 1 (for '\0' char) * 8/2003 AAT: Removed dependence on offset and fileLen. * * NOTES ***************************************************************************** */ int FindGRIBMsg (DataSource &fp, int msgNum, sInt4 *offset, int *curMsg) { int cnt; /* The current message we are looking at. */ char *buff; /* Holds the info between records. */ uInt4 buffLen; /* Length of info between records. */ sInt4 sect0[SECT0LEN_WORD]; /* Holds the current Section 0. */ uInt4 gribLen; /* Length of the current GRIB message. */ int version; /* Which version of GRIB is in this message. */ int c; /* Determine if end of the file without fileLen. */ sInt4 jump; /* How far to jump to get to past GRIB message. */ cnt = *curMsg + 1; buff = NULL; buffLen = 0; while ((c = fp.DataSourceFgetc()) != EOF) { fp.DataSourceUngetc(c); if (cnt >= msgNum) { /* 12/1/2004 version 1.63 forgot to free buff */ free (buff); *curMsg = cnt; return 0; } /* Read section 0 to find gribLen and wmoLen. */ if (ReadSECT0 (fp, &buff, &buffLen, GRIB_LIMIT, sect0, &gribLen, &version) < 0) { preErrSprintf ("Inside FindGRIBMsg\n"); free (buff); return -1; } myAssert ((version == 1) || (version == 2) || (version == -1)); /* Continue on to the next grib message. */ if ((version == 1) || (version == -1)) { jump = gribLen - 8; } else { jump = gribLen - 16; } fp.DataSourceFseek(jump, SEEK_CUR); *offset = *offset + gribLen + buffLen; cnt++; } free (buff); *curMsg = cnt - 1; /* Return -2 since we reached the end of file. This may not be an error * (multiple file option). */ return -2; /* errSprintf ("ERROR: Ran out of file looking for msgNum %d.\n", msgNum); errSprintf (" Current msgNum %d\n", cnt); */ }
/***************************************************************************** * GRIB2SectJump() -- * * Arthur Taylor / MDL * * PURPOSE * To jump past a GRIB2 section. Reads in secLen and checks that the * section is valid. * * ARGUMENTS * fp = Opened file pointing to the section in question. (Input/Output) * gribLen = The total length of the grib message. (Input) * sect = Which section we think we are reading. * If it is -1, then set it to the section the file says we are * reading (useful for optional sect 2)) (Input/Output). * secLen = The length of this section (Output) * * FILES/DATABASES: * An already opened GRIB2 file pointer, already at section in question. * * RETURNS: int (could use errSprintf()) * 0 = Ok. * -1 = Ran out of file. * -2 = Section was miss-labeled. * * HISTORY * 3/2003 Arthur Taylor (MDL/RSIS): Created. * 8/2003 AAT: Removed dependence on curTot, which was used to compute if * the file should be large enough for the fseek, but didn't check * if it actually was. * * NOTES * May want to put this in degrib2.c ***************************************************************************** */ static int GRIB2SectJump (DataSource &fp, CPL_UNUSED sInt4 gribLen, sChar *sect, uInt4 *secLen) { char sectNum; /* Validates that we are on the correct section. */ int c; /* Check that the fseek is still inside the file. */ if (FREAD_BIG (secLen, sizeof (sInt4), 1, fp) != 1) { if (*sect != -1) { errSprintf ("ERROR: Ran out of file in Section %d\n", *sect); } else { errSprintf ("ERROR: Ran out of file in GRIB2SectSkip\n"); } return -1; } if (fp.DataSourceFread (§Num, sizeof (char), 1) != 1) { if (*sect != -1) { errSprintf ("ERROR: Ran out of file in Section %d\n", *sect); } else { errSprintf ("ERROR: Ran out of file in GRIB2SectSkip\n"); } return -1; } if (*sect == -1) { *sect = sectNum; } else if (sectNum != *sect) { errSprintf ("ERROR: Section %d mislabeled\n", *sect); return -2; } /* Since fseek does not give an error if we jump outside the file, we test * it by using fgetc / ungetc. */ fp.DataSourceFseek (*secLen - 5, SEEK_CUR); if ((c = fp.DataSourceFgetc()) == EOF) { errSprintf ("ERROR: Ran out of file in Section %d\n", *sect); return -1; } else { fp.DataSourceUngetc(c); } return 0; }
/***************************************************************************** * GRIB2Inventory() -- Review 12/2002 * * Arthur Taylor / MDL * * PURPOSE * Fills out an inventory structure for each GRIB message in a GRIB file, * without calling the FORTRAN routines to unpack the message. It returns * the number of messages it found, or a negative number signifying an error. * * ARGUMENTS * filename = File to do the inventory of. (Input) * Inv = The resultant array of inventories. (Output) * LenInv = Length of the Array Inv (Output) * numMsg = # of messages to inventory (0 = all, 1 = just first) (In) * msgNum = MsgNum to start with, MsgNum of last message (Input/Output) * * FILES/DATABASES: * Opens a GRIB2 file for reading given its filename. * * RETURNS: int (could use errSprintf()) * +# = number of GRIB2 messages in the file. * -1 = Problems opening file for read. * -2 = Problems in section 0 * -3 = Ran out of file. * -4 = Problems Reading in section 1 * -5 = Problems Reading in section 2 or 3 * -6 = Problems Reading in section 3 * -7 = Problems Reading in section 4 * -8 = Problems Parsing section 4. * -9 = Problems Reading in section 5 * -10 = Problems Reading in section 6 * -11 = Problems Reading in section 7 * -12 = Problems inventory'ing a GRIB1 record * -13 = Problems inventory'ing a TDLP record * * HISTORY * 9/2002 Arthur Taylor (MDL/RSIS): Created. * 11/2002 AAT: Revised. * 12/2002 (TK,AC,TB,&MS): Code Review. * 3/2003 AAT: Corrected some satellite type mistakes. * 3/2003 AAT: Implemented multiple grid inventories in the same GRIB2 * message. * 4/2003 AAT: Started adding GRIB1 support * 6/2003 Matthew T. Kallio ([email protected]): * "wmo" dimension increased to WMO_HEADER_LEN + 1 (for '\0' char) * 7/2003 AAT: Added numMsg so we can quickly find the reference time for * a file by inventorying just the first message. * 8/2003 AAT: Adjusted use of GRIB_LIMIT to only affect the first message * after we know we have a GRIB file, we don't want "trailing" bytes * to break the program. * 8/2003 AAT: switched fileLen to only be computed for an error message. * 8/2003 AAT: curTot no longer serves a purpose. * 5/2004 AAT: Added a check for section number 2..8 for the repeated * section (otherwise error) * 10/2004 AAT: Added ability to inventory TDLP records. * * NOTES ***************************************************************************** */ int GRIB2Inventory (DataSource &fp, inventoryType **Inv, uInt4 *LenInv, int numMsg, int *MsgNum) { //FileDataSource fp (filename); /* The opened GRIB2 file. */ sInt4 offset = 0; /* Where we are in the file. */ sInt4 msgNum; /* Which GRIB2 message we are on. */ uInt4 gribLen; /* Length of the current GRIB message. */ uInt4 secLen; /* Length of current section. */ sChar sectNum; /* Which section we are reading. */ char *buff; /* Holds the info between records. */ uInt4 buffLen; /* Length of info between records. */ sInt4 sect0[SECT0LEN_WORD]; /* Holds the current Section 0. */ char *buffer = NULL; /* Holds a given section. */ uInt4 bufferLen = 0; /* Size of buffer. */ inventoryType *inv; /* Local ptr to Inv to reduce ptr confusion. */ inventoryType *lastInv; /* Used to point to last inventory record when * there are multiple grids in the same message. */ wordType word; /* Used to parse the prodType out of Sect 0. */ int ans; /* The return error code of ReadSect0. */ char *msg; /* Used to pop messages off the error Stack. */ int version; /* Which version of GRIB is in this message. */ uChar prodType; /* Which GRIB2 type of product, 0 is meteo, 1 is * hydro, 2 is land, 3 is space, 10 is oceanographic. */ int grib_limit; /* How many bytes to look for before the first "GRIB" * in the file. If not found, is not a GRIB file. */ int c; /* Determine if end of the file without fileLen. */ sInt4 fileLen; /* Length of the GRIB2 file. */ unsigned short int center, subcenter; /* Who produced it. */ // char *ptr; /* used to find the file extension. */ grib_limit = GRIB_LIMIT; /* if (filename != NULL) { //if ((fp = fopen (filename, "rb")) == NULL) { // errSprintf ("ERROR: Problems opening %s for read.", filename); // return -1; //} //fp = FileDataSource(filename); ptr = strrchr (filename, '.'); if (ptr != NULL) { if (strcmp (ptr, ".tar") == 0) { grib_limit = 5000; } } } else { //fp = stdin; // TODO!! } */ msgNum = *MsgNum; buff = NULL; buffLen = 0; while ((c = fp.DataSourceFgetc()) != EOF) { fp.DataSourceUngetc(c); // ungetc (c, fp); /* msgNum++ done first so any error messages range from 1..n, instead * of 0.. n-1. Note msgNum should end up as n not (n-1) */ msgNum++; /* Used when testing inventory of large TDLPack files. */ /* #ifdef DEBUG myAssert (msgNum < 32500L); if (msgNum % 10 == 0) { printf ("%ld :: %f\n", msgNum, clock () / (double) CLOCKS_PER_SEC); } #endif */ /* Make it so the second, third, etc messages have no limit to finding * the "GRIB" keyword. */ if (msgNum > 1) { grib_limit = -1; } /* Read in the wmo header and sect0. */ if (ReadSECT0 (fp, &buff, &buffLen, grib_limit, sect0, &gribLen, &version) < 0) { if (msgNum == 1) { /* Handle case where we couldn't find 'GRIB' in the message. */ preErrSprintf ("Inside GRIB2Inventory, Message # %d\n", msgNum); free (buffer); free (buff); //fclose (fp); return -2; } else { /* Handle case where there are trailing bytes. */ msg = errSprintf (NULL); printf ("Warning: Inside GRIB2Inventory, Message # %d\n", msgNum); printf ("%s", msg); free (msg); /* find out how big the file is. */ fp.DataSourceFseek (0L, SEEK_END); fileLen = static_cast<int>(fp.DataSourceFtell()); /* fseek (fp, 0L, SEEK_SET); */ printf ("There were %d trailing bytes in the file.\n", fileLen - offset); free (buffer); free (buff); //fclose (fp); return msgNum; } } /* Make room for this GRIB message in the inventory list. */ *LenInv = *LenInv + 1; *Inv = (inventoryType *) realloc ((void *) *Inv, *LenInv * sizeof (inventoryType)); inv = *Inv + (*LenInv - 1); /* Start parsing the message. */ inv->GribVersion = version; inv->msgNum = msgNum; inv->subgNum = 0; inv->start = offset; inv->element = NULL; inv->comment = NULL; inv->unitName = NULL; inv->shortFstLevel = NULL; inv->longFstLevel = NULL; if (version == 1) { if (GRIB1_Inventory (fp, gribLen, inv) != 0) { preErrSprintf ("Inside GRIB2Inventory \n"); free (buffer); free (buff); //fclose (fp); return -12; } } else if (version == -1) { if (TDLP_Inventory (fp, gribLen, inv) != 0) { preErrSprintf ("Inside GRIB2Inventory \n"); free (buffer); free (buff); //fclose (fp); return -13; } } else { word.li = sect0[1]; prodType = word.buffer[2]; /* Read section 1 into buffer. */ sectNum = 1; if (GRIB2SectToBuffer (fp, gribLen, §Num, &secLen, &bufferLen, &buffer) != 0) { errSprintf ("ERROR: Problems with section 1\n"); free (buffer); free (buff); //fclose (fp); return -4; } /* Parse the interesting data out of sect 1. */ InventoryParseTime (buffer + 13 - 5, &(inv->refTime)); MEMCPY_BIG (¢er, buffer + 6 - 5, sizeof (short int)); MEMCPY_BIG (&subcenter, buffer + 8 - 5, sizeof (short int)); sectNum = 2; do { /* Look at sections 2 to 7 */ if ((ans = GRIB2Inventory2to7 (sectNum, fp, gribLen, &bufferLen, &buffer, inv, prodType, center, subcenter)) != 0) { //fclose (fp); free (buffer); free (buff); return ans; } /* Try to read section 8. If it is "7777" = 926365495 regardless * of endian'ness then we have a simple message, otherwise it is * complex, and we need to read more. */ if (FREAD_BIG (&secLen, sizeof (sInt4), 1, fp) != 1) { errSprintf ("ERROR: Ran out of file looking for Sect 8.\n"); free (buffer); free (buff); // fclose (fp); return -4; } if (secLen == 926365495L) { sectNum = 8; } else { if (fp.DataSourceFread (§Num, sizeof (char), 1) != 1) { errSprintf ("ERROR: Ran out of file looking for " "subMessage.\n"); free (buffer); free (buff); //fclose (fp); return -4; } if ((sectNum < 2) || (sectNum > 7)) { errSprintf ("ERROR (GRIB2Inventory): Couldn't find the end" " of message\n"); errSprintf ("and it doesn't appear to repeat sections.\n"); errSprintf ("so it is probably an ASCII / binary bug\n"); free (buffer); free (buff); //fclose (fp); return -4; } fp.DataSourceFseek (-5, SEEK_CUR); /* Make room for the next part of this GRIB message in the * inventory list. This is for when we have sub-grids. */ *LenInv = *LenInv + 1; *Inv = (inventoryType *) realloc ((void *) *Inv, *LenInv * sizeof (inventoryType)); inv = *Inv + (*LenInv - 1); lastInv = *Inv + (*LenInv - 2); inv->GribVersion = version; inv->msgNum = msgNum; inv->subgNum = lastInv->subgNum + 1; inv->start = offset; inv->element = NULL; inv->comment = NULL; inv->unitName = NULL; inv->shortFstLevel = NULL; inv->longFstLevel = NULL; word.li = sect0[1]; prodType = word.buffer[2]; inv->refTime = lastInv->refTime; } } while (sectNum != 8); } /* added to inventory either first msgNum messages, or all messages */ if (numMsg == msgNum) { break; } /* Continue on to the next GRIB2 message. */ if (version == -1) { /* TDLPack uses 4 bytes for FORTRAN record size, then another 8 * bytes for the size of the record (so FORTRAN can see it), then * the data rounded up to an 8 byte boundary, then a trailing 4 * bytes for a final FORTRAN record size. However it only stores * in_ the gribLen the non-rounded amount, so we need to take care * of the rounding, and the trailing 4 bytes here. */ offset += buffLen + ((sInt4) ceil (gribLen / 8.0)) * 8 + 4; } else { offset += buffLen + gribLen; } fp.DataSourceFseek (offset, SEEK_SET); } free (buffer); free (buff); //fclose (fp); *MsgNum = msgNum; return msgNum; }