/* @(#) File name: genpro.c Release: 1.1 Date: 4/7/94, 11:00:58 */ /**************************************************************************** PROGRAM: genpro PURPOSE/DESCRIPTION: Program genpro reads a pair of GENPRO formatted header and data files from the command line arguments, parses the header file, reads the data from data file, and prints out the values of a selected number of variables to the standard output device. GENPRO formatted files are provided by NCAR. This program intends to be generic enough to read all GENPRO formatted file. So far, it has been tested on the FIRE CI1, FIRE CI2, and FIRE CI3 data files successfully. NOTE: It is found out that FIRE CI2 Kingair data set has a padding of 4 bytes after every three logical records, and FIRE CI3 Electra data set has a padding of 4 bytes after every logical record. That's why when compiling this program, a definition for the C preprocessor is needed for different FIRE genpro formatted data sets. For FIRE CI2 Kingair data set, add "-DF2_KINGAIR" in the compilation command. For FIRE CI3 Electra data set, add "-DF3_ELECTRA" in the compilation command. There is no need to add a preprocessor parameter for FIRE CI1 data sets, and FIRE CI2 Sabreliner data set. AUTHOR: Meng-chun Lin, Mail stop 157B, EOSDIS LANGLEY DAAC, Hampton, VA; (804)864-8657. e-mail: M.LIN@LaRC.NASA.GOV. ALGORITHM: None. INVOCATION: genpro header_file data_file WHERE is the executable file name. is the GENPRO formatted header file, which contains information to decode the binary data file. is the corresponding binary data file. FILE/RECORD REFERENCES: Two files are required as the command line arguments. The first one is the genpro header file, the second one is the binary data file. EXTERNAL ROUTINES: None. NOTES: 1. Since this program uses dynamic memory allocation, the calculation of the array index described in the header seciton is not applicable. 2. Because the huge amount of data, only one logical record worth of data are kept in the memory. Therefore, if data are to be printed out, the printf statement should be added (or modified) at the end of each logical record before the data is overwritten by the following records. *****************************************************************************/ #ifdef sccs static char sccsid[] = "File: genpro.c Release: 1.1 Date: 4/7/94, 11:00:58" #endif /* System include files. */ #include #include #include #include #include #include /* Constant MAXPATHLEN is defined here. */ #include #include /*** The following constants and the data structure genpro should be defined *** in the fci2_sw.h include file. But because this program is to be used *** by FIRE CI1, FIRE CI2, FIRE CI2 genpro formmated data sets, they are *** included in this program to avoid the multiple inclusions. ***/ #define RECSIZE 80 #define HD_KEYWORD "BEGINHD" #define VAR_KEYWORD "/VARIABLES" #define VAR_ID "APPVAR" #define VAR_DESC "ORDVAR = TITLE" #define DESC_ID "LETVAR = " /************************************************************************* Units (UNITS), Sample Rate(SAMPLE), Output Rate (RATE), Bit length of each data value (BITS), First bit location of each variable (FSTBIT), # of bits between two sequential data values for the same variable (SKIP). **************************************************************************/ #define URB_KEYWORD "ORDVAR = UNITS, SAMPLE, RATE, BITS, FSTBIT, SKIP" /************************************************************************* Conversion code used by GENPRO (CONKEY), Scaling factor (FACTOR), Scaling algorithm selection (SCLKEY), Value of the scaling term (TERM). ***************************************************************************/ #define CONV_KEYS "ORDVAR = CONKEY, SCLKEY, TERM, FACTOR" /*** Macro int2real() converts the integer value to floating point value. *** where x = integer value to be converted to real, f = factor, *** t = term ***/ #define int2real(x,f,t) (double)((double)x/f - t) /*** Macro cmin() and cmax() returns the minimum and maximum of the *** two values respectively. ***/ #define cmin(a, b) ((a >= b) ? b : a) #define cmax(a, b) ((a >= b) ? a : b) /*** Output rate of the variable is not used, hence, it is not incorporated *** into the 'genpro' structure. ***/ typedef struct genpro { char name[12]; char unit [12]; short sample_rate; short bit_num; int start_bit; short skip; short scale_key; unsigned int integer; double term; double factor; double *value; } genpro; extern int str_trim(); extern void interrupt_cat(); main (argc, argv) int argc; char *argv[]; { char filename[MAXPATHLEN+1]; FILE *fptr; int cnt, i, j, k; genpro *hd, *phd; if (argc != 3) { fprintf (stderr, "Usage: genpro header_file data_file.\n"); exit (-1); } (void) signal (SIGINT, interrupt_cat); if (signal (SIGINT, SIG_IGN) != SIG_IGN) signal (SIGINT, interrupt_cat); (void) signal (SIGTERM, interrupt_cat); strcpy (filename, argv[1]); /*** Parse header and construct the data structure. ***/ if ((cnt = genpro_header (filename, RECSIZE, &hd)) < 0) exit (-1); fprintf (stdout, "Total of %d variables processed.\n", cnt); /*** Decode the binary data based on the information in the header *** data structure. ***/ strcpy (filename, argv[2]); if ((k = data_file (filename, hd, cnt)) > 0) fprintf (stdout, "Total of %d records processed.\n", k); /*** Release the dynamically allocated memory. ***/ for (i = 0, phd = hd; i < cnt; i++, phd = hd+i) { if (phd->value != (double *)NULL) free (phd->value); } free (hd); exit (0); } /**************************************************************************** FUNCTION: data_file () PURPOSE/DESCRIPTION: Function data_file reads the data values from the specified data file for all variables. It extracts the data and converts them from the positive integer values to real numbers based on the sample rate, and scaling factors of each variable, then prints out the values of a selected number of variables as it reads through each logical record. See NOTES 2. for explanations of not printing all values. Because this routine is intended to be used to read data from FIRE CI1, FIRE CI2, and FIRE CI3 data sets, and because data sets generated at three phases of FIRE experiment have different padding schemes in the data, the conditional statements such as "ifdef F2_KINGAIR", and "ifdef F3_ELECTRA" are needed to ensure this function a generic one. INVOCATION: return_value = data_file (file_name, hd, total); WHERE - I is the file name of the GENPRO formatted data file to be read. - I is the base of the data structure that stores the header information for all variables. - I is the total number of variables (parameters) obtained from parsing the corresponding header file. - O returns the number of logical records read, or returns -1 when the specified data file can not be opened. FILE/RECORD REFERENCES: The genpro binary data file is named ciX_aircraft_flighttime. For example, ci2_kng_911109_11 to means FIRE CI2 Kingair flight starts at 11 o'clock on Nov. 9, 1991. When there are more than one flight starting at the same time, the first flight has '.1' at the end of the file name, the second flight has '.2' at the end of the file name. EXTERNAL ROUTINES: None. NOTES: 1. This function assumes all data values are in unsigned integers. 2. Because of the huge amount of data in the file, only values of the selected 7 variables (1st, 2nd, 3rd, 4th, 5th, 6th, and 7th variables, in that order) are printed out. The printf statement works as a demonstration of printing out values of the desired variables. *****************************************************************************/ int data_file (file_name, hd, total) char *file_name; genpro *hd; int total; { genpro *phd; double real, min_lat, min_lon, max_lat, max_lon; FILE *fptr; int i, j, k=0, ret=1, l; unsigned int integer; if ((fptr = fopen (file_name, "r")) == NULL) { fprintf (stderr,"ERROR:Can't open data file:\t %s\n", file_name); return (-1); } /** Initialize latitude range, and longitude range variables to ** be (-90.0, 90.0) and (-180.0, 180.0) for the (maximu, minimum) ** value, respectively. **/ min_lat = 90.0; max_lat = -90.0; min_lon = 180.0; max_lon = -180.0; /*** Print out the values of all variables read if the DEBUG *** preprocessor option is set. Print column header before data. ***/ #ifdef DEBUG printf("%20s %15s %15s Values\n","Variable Name","Term","Factor"); #endif /*** In general, the variables are sampled at 1HZ per second (per genpro *** cycle), but some variables have more than one sample per genpro cycle. *** That's why there are two 'for' loops in the following lines. ***/ while (feof (fptr) == 0) { for (phd = hd, i = 0; i < total; i++, phd++) { for (j = 0; j < phd->sample_rate; j++) { ret = fread (&integer, sizeof (unsigned int), 1, fptr); /** Convert to floating point value with the term and factor. **/ if (ret == 0) break; phd->integer = integer; real = int2real(integer, phd->factor, phd->term); *(phd->value+j) = real; #ifdef DEBUG printf("%-20s %15.4lf %15.4lf; %lf\n", phd->name, phd->term, phd->factor, *(phd->value+j)); #endif /**** Use ALAT, and ALON as latitude and longitude values, repectively. ***/ if (strcmp (phd->name, "ALAT") == 0) { if ((real > 90.0) || (real < -90.0)) printf("\t?? Latutude = %lf, record #%d\n", real, k+1); else { min_lat = cmin (min_lat, real); max_lat = cmax (max_lat, real); } } if (strcmp (phd->name, "ALON") == 0) { if ((real > 180.0) || (real < -180.0)) printf("\t?? Longitude = %lf, record #%d\n", real, k+1); else { min_lon = cmin (min_lon, real); max_lon = cmax (max_lon, real); } } } } if (ret != 0) { k++; #ifdef F2_KINGAIR if ((k % 3 == 0) && (k > 0)) fread ((char *)&integer, sizeof (int), 1, fptr); #endif #ifdef F3_ELECTRA fread ((char *)&integer, sizeof (int), 1, fptr); #endif /*** NOTE: The following printf statement serves as an example of writing *** out the values of the selected variables in a record. Users can *** modify the printf statement to print values of the desired variables. *** This printf only occur when the DEBUG flag is not set. ***/ #ifndef DEBUG printf ("%6.3lf %6.3lf %6.3lf %8.3lf %8.3lf %8.3lf %8.3lf\n", *(hd->value), *(hd+1)->value, *(hd+2)->value, *(hd+3)->value, *(hd+4)->value, *(hd+5)->value, *(hd+6)->value); #endif } } printf ("File Name: %s\n", file_name); printf ("(Min, Max) Latitude = (%10.3lf, %10.3lf)\n", min_lat, max_lat); printf ("(Min, Max) Longitude = (%10.3lf, %10.3lf)\n", min_lon, max_lon); fclose (fptr); return (k); } /**************************************************************************** FUNCTION: genpro_header () PURPOSE/DESCRIPTION: Function genpro_header parses the header file specified in the command line argument. It reads the header file line by line and extracts the variables and the information related to the processing of these variables. The information includes units, sample rate, bits, 1st bit position, the in-record offset (skip), scaling term and factor of variables. Since the header file is in ASCII format, users can view the detailed descriptions for the variables with Unix "more" command. INVOCATION: return_value = genpro_header (file_name, rec_size, &phd); WHERE - I is the file name of the genpro header file to be parsed. - I is the logical record size for the header file, i.e., 80-byte ASCII data per logical record. - I, O points to the base of the dynamically allocated space which stores the variables' information needed to decode binary data values. - O returns the number of variables (parameters) read from the header file. A -1 is returned if the header file can not be opened. FILE/RECORD REFERENCES: A GENPRO header file is in ASCII format (WITHOUT line breaks) and has the naming convention of ciX_aircraft_flighttime.hdr. For example, ci2_sab_911117_19.hdr is a FIRE CI2 Sabreliner header file. EXTERNAL ROUTINES: None. NOTES: Some variables may have sampling rate higher than one per genpro cycle. This is handled by allocating "sample rate" times size of floating point value space to store the data. *****************************************************************************/ int genpro_header (file_name, rec_size, phd) char *file_name; int rec_size; genpro **phd; { char *buf, *s, *ret, unuse[10], name[16], holder[20], month[10], *get_fld(); char hr_str[4], min_str[4], sec_str[4]; FILE *fptr; genpro *dat; int cnt, outrate, day, year, conv_key, line = 0; if ((fptr = fopen (file_name, "r")) == NULL) { fprintf (stderr,"ERROR:Can't open FIRE header file:\t %s\n", file_name); return (-1); } /* Genpro header file is in ASCII format. */ buf = (char *) calloc (rec_size+1, sizeof (char)); /* The following while loop searches the project date (PRDATE), project time * (PRTIME), beginning flight time (BEGSNP), and ending flight time (ENDSNP) * keyword then prints out these time values. */ while ((fgets (buf, rec_size+1, fptr) != NULL) && ((s=strstr (buf, "PRDATE")) == (char *)NULL)) ; sscanf (&(buf[12]), "%2d%3s%3s%3s%2d", &day, unuse, month, unuse, &year); printf ("Research Flight Date = %s %d, %d\n", month, day, year); while ((fgets (buf, rec_size+1, fptr) != NULL) && ((s=strstr (buf, "PRTIME")) == (char *)NULL)) ; sscanf (&(buf[12]), "%3s%3s%3s%3s%3s", hr_str, unuse, min_str, unuse, sec_str); printf ("Research Flight Time = %s %s %s\n", hr_str, min_str, sec_str); while ((fgets (buf, rec_size+1, fptr) != NULL) && ((s=strstr (buf, "BEGSNP")) == (char *)NULL)) ; printf ("Flight Start Time (Hour, Min, Sec) = %s", &(buf[10])); while ((fgets (buf, rec_size+1, fptr) != NULL) && ((s=strstr (buf, "ENDSNP")) == (char *)NULL)) ; printf ("Flight End Time (Hour, Min, Sec) = %s\n", &(buf[10])); /* The following while loop skip all lines until the variables * definition section keyword is encountered. */ while ((fgets (buf, rec_size+1, fptr) != NULL) && ((s=strstr (buf, VAR_KEYWORD)) == (char *)NULL)) ; s = (char *)NULL; /* Find the 1st line of variables definitions. */ while (fgets (buf, rec_size+1, fptr) != NULL) { if (str_trim (buf, 0) == 0) continue; if ((s=strstr (buf, VAR_ID)) != (char *)NULL) break; } while (s != (char *) NULL) { /* Start the variables definition sect. */ if (fgets (buf, rec_size+1, fptr) == NULL) break; if (str_trim (buf, 0) == 0) continue; line++; s = strstr (buf, VAR_ID); } /*** Allocate memory with the estimate max. # of records, i.e., the lines *** of variables mulplied by 6 variables per line. Final total # of *** variables may be less than (lines * 6) because there may be less *** less than 6 variables in the last line. ***/ *phd = (genpro *) calloc (line*6, sizeof (struct genpro)); dat = *phd; /* For now, skip the description section */ if (strstr(buf, VAR_DESC) != (char *)NULL) {/* Begin description sect. */ do { ret = fgets (buf, rec_size+1, fptr); if (str_trim (buf, 0) == 0) continue; } while ((ret != (char *)NULL) && ((s=strstr (buf, URB_KEYWORD)) == (char *)NULL)); } /* Otherwise, it's an error */ /*** Process Units, sample rate, output rate, bits, 1st bit position, *** and the in-record offset (skip) of variables ***/ for (cnt=0; (fgets (buf, rec_size+1, fptr) != NULL); ) { if (str_trim (buf, 0) == 0) continue; if ((s=strstr (buf, CONV_KEYS)) != (char *)NULL) break; ret = &(buf[strlen (DESC_ID) + 1]); ret = get_fld (ret, dat->unit); ret = get_fld (ret, holder); /* Sample Rate */ dat->sample_rate = atoi (holder); ret = get_fld (ret, holder); /* Output Rate */ ret = get_fld (ret, holder); /* Bit number */ dat->bit_num = atoi (holder); ret = get_fld (ret, holder); /* Start bit position */ dat->start_bit = atoi (holder); ret = get_fld (ret, holder); /* in-record offset */ dat->skip = atoi (holder); ret = get_fld (ret, holder); /* unused string: %FOR */ ret = get_fld (ret, dat->name); /* Variable name */ dat->value = (double *) calloc (dat->sample_rate, sizeof (double)); dat++; cnt++; } /*** Process conversion key, scaling algorithm, term and factor of *** variables. The conversion key value is not stored in the memory. ***/ for (cnt=0, dat=*phd; (fgets (buf, rec_size+1, fptr) != NULL); ) { if (str_trim (buf, 0) == 0) continue; if ((s=strstr (buf, "ENDHD")) != (char *)NULL) break; ret = &(buf[strlen (DESC_ID) + 1]); ret = get_fld (ret, holder); /* unused: conversion key */ ret = get_fld (ret, holder); /* Scaling algorithm */ dat->scale_key = atoi (holder); ret = get_fld (ret, holder); /* Term */ sscanf (holder, "%lf", &(dat->term)); ret = get_fld (ret, holder); /* Factor */ sscanf (holder, "%lf", &(dat->factor)); ret = get_fld (ret, holder); /* unused string: %FOR */ ret = get_fld (ret, name); /* unused string: %FOR */ if (strcmp (dat->name, name) != 0) printf ("Name inconformace %s\n", dat->name); dat->value = (double *) calloc (dat->sample_rate, sizeof (double)); dat++; cnt++; } free (buf); fclose (fptr); return (cnt); } /********************************************************************** FUNCTION: get_fld () PURPOSE/DESCRIPTION: Function GET_FLD parses the field data from the input string. It skips the field separator (a comma) in the string in header file, and returns the field data (in character string) to the calling routine. This routine can skip the leading and trailing blanks in a field. INVOCATION: return_string = get_fld (str, s) WHERE is the pointer to a character string in the header file. is the pointer to a buffer which will store the field value in character string. returns the updated pointer to the beginning of next field. FILE/RECORD REFERENCES: None. EXTERNAL ROUTINES: None. NOTES: This is a support function to the function GENPRO_HEADER for the purpose of extracting individual field from a string. ***********************************************************************/ char *get_fld (str, s) char *str; char *s; { char delimiters[256]; int len, num; if (strlen(str) <= 0) { *s = '\0'; *str = '\0'; return (str); } num = sscanf (str, "%[ , ]", delimiters); if (num == 1) { len = strlen (delimiters); str = str+len; } if (strlen(str) <= 0) { *s = '\0'; *str = '\0'; } else { sscanf (str, "%[^,]", s); str = str+strlen (s); } str_trim (s, 0); return (str); }