/* This holds the definition functions that access the granules info list */ #include /* Public variable definition */ /* local macro definitions */ /* Generate any missing granules for one time slot. */ /* Define this as a means to reuse code. */ #define GENERATE_MISSING_GRANULES \ { \ /* If foundproducts == total_nproducts, no need to continue. */ \ /* look through the granules_found array and geogranule_found. \ * Make fill granules for each missing product. \ */ \ for (j=0; j < nproducts; j++){ \ if (!granules_found[j]){ \ fill_granule = make_fill_granule(products_list[j], next_slot_btime, timeg); \ if (NULL==fill_granule){ \ NAGG_ERROR("failure to make fill granule"); \ ret_code=FAIL; \ goto done; \ }; \ granules_selected[got_granules++] = fill_granule; \ foundproducts++; \ filled_granules++; \ } \ } \ /* make fill granule for geolocation if needed */ \ if (geoproduct&&!geogranule_found){ \ fill_granule = make_fill_granule(geoproduct, next_slot_btime, timeg); \ if (NULL==fill_granule){ \ NAGG_ERROR("failure to make fill granule"); \ ret_code=FAIL; \ goto done; \ }; \ granules_selected[got_granules++] = fill_granule; \ foundproducts++; \ filled_granules++; \ } \ } /* Local variables definitions */ /* These used by compose_output_fname */ static NPPFileName_t newfilename; static NPPFileName_t newgeofilename; /* Fill granule array and assoicated variables */ static granule_t *fill_granule_array = NULL; /* pointer to array */ static int array_size; /* size of array */ static int array_index; /* index of available granule. 0-based */ /* local protocols */ static int granule_compar(const void *granule1, const void *granule2); static int granprod_compar(const void *granule1, const void *granule2); static int add_tmp_granule(granule_p_t granule_info_p[], int number_of_granules, char * granule_ID, char * product_ID, char * granule_version, int granule_version_num); static int mod_tmp_granule(granule_p_t granule_info_p[], int index, char * granule_ID, char * product_ID, char * granule_version, int granule_version_num); static int init_fill_granules_array(int size); static granule_p_t make_fill_granule(char *geoproduct, iet_t start_time, iet_t duration); static char *iet2utc(iet_t iet_time); /* function definitions */ /* Modify a granules. For debug purpose. * Return SUCCEED if succeed; otherwise FAIL * * Assign given values to the granule_ID, product_ID, granule version, granule version number * of the granule in granule_info_p[index]. Assume index is always within bound. */ static int mod_tmp_granule(granule_p_t granule_info_p[], int index, char * granule_ID, char * product_ID, char * granule_version, int granule_version_num) { granule_p_t oldp; assert(granule_ID!=NULL); assert(product_ID!=NULL); assert(granule_version!=NULL); assert(granule_info_p!=NULL); assert(index>=0); oldp = granule_info_p[index]; HDstrcpy(oldp->granule_id , granule_ID); HDstrcpy(oldp->product_name , product_ID); /* HDstrcpy(oldp->granule_version , granule_version);*/ oldp->granule_version_number = granule_version_num; return SUCCEED; } /* Add some temporary granules. For debug purpose. * Return SUCCEED if succeed; otherwise FAIL * * Duplicate the last granule, then assign granule_ID, product_ID, * granule version, granule version number with the given values. */ static int add_tmp_granule(granule_p_t granule_info_p[], int number_of_granules, char * granule_ID, char * product_ID, char * granule_version, int granule_version_num) { granule_p_t newp, oldp; assert(granule_ID!=NULL); assert(product_ID!=NULL); assert(granule_version!=NULL); assert(granule_info_p!=NULL); assert(number_of_granules>0); oldp = granule_info_p[number_of_granules-1]; if (NULL==(newp=(granule_p_t)HDmalloc(sizeof(granule_t)))){ NAGG_ERROR("add_tmp_granule(): malloc failed\n"); return(FAIL); }; HDmemcpy(newp, oldp, sizeof(granule_t)); HDstrcpy(newp->granule_id , granule_ID); HDstrcpy(newp->product_name , product_ID); HDstrcpy(newp->granule_version , granule_version); newp->granule_version_number = granule_version_num; granule_info_p[number_of_granules] = newp; return SUCCEED; } /* Compare granule1 and granule2. * Return -1 if granule1 < granule2 * 0 if granule1 = granule2 * 1 if granule1 > granule2 * Compare first the Granule ID (ascend), then Product ID (ascend), then Granule version (descend). */ static int granule_compar(const void *granule1, const void *granule2) { int return_val; granule_p_t pt1, pt2; assert(granule1!=NULL); assert(granule2!=NULL); pt1= * (granule_p_t const *) granule1; pt2= * (granule_p_t const *) granule2; #ifdef DEBUG printf("enter granule_compar:\n"); printf("granule1=%x, granule2=%x\n", granule1, granule2); printf("pt1=%x, %s, %s, %d\n", pt1, pt1->granule_id, pt1->product_name, pt1->granule_version_number); printf("pt2=%x, %s, %s, %d\n", pt2, pt2->granule_id, pt2->product_name, pt2->granule_version_number); #endif /* compare Granule ID which is a string (ascend) */ if (0!=(return_val=HDstrcmp((pt1->granule_id), (pt2->granule_id)))){ /* found difference. return result */ #ifdef DEBUG printf("granule ID different return_val=%d\n", return_val); #endif goto done; }; /* compare Product_ID which is a string (ascend) */ if (0!=(return_val=HDstrcmp(pt1->product_name, pt2->product_name))){ /* found difference. return result */ #ifdef DEBUG printf("product ID different return_val=%d\n", return_val); #endif goto done; }; /* compare Granule version number (descend) */ return_val=(pt2->granule_version_number - pt1->granule_version_number); #ifdef DEBUG printf("version cmp value=%d\n", return_val); #endif done: #ifdef DEBUG printf("leaving granule_compar with %d\n", return_val); #endif return return_val; } /* Compare granule1 and granule2 by granule ID and product ID only. * Note this does not compare the granule version values. * Return -1 if granule1 < granule2 * 0 if granule1 = granule2 * 1 if granule1 > granule2 * Compare first the Granule ID, then Product ID. */ static int granprod_compar(const void *granule1, const void *granule2) { int return_val; granule_p_t pt1, pt2; assert(granule1!=NULL); assert(granule2!=NULL); pt1= * (granule_p_t const *) granule1; pt2= * (granule_p_t const *) granule2; #ifdef DEBUG printf("enter granprod_compar:\n"); printf("granule1=%x, granule2=%x\n", granule1, granule2); printf("pt1=%x, %s, %s, %d\n", pt1, pt1->granule_id, pt1->product_name, pt1->granule_version_number); printf("pt2=%x, %s, %s, %d\n", pt2, pt2->granule_id, pt2->product_name, pt2->granule_version_number); #endif /* compare Granule ID which is a string */ if (0!=(return_val=HDstrcmp((pt1->granule_id), (pt2->granule_id)))){ /* found difference. return result */ #ifdef DEBUG printf("granule ID different return_val=%d\n", return_val); #endif goto done; }; /* compare Product_ID which is a string */ return_val=HDstrcmp(pt1->product_name, pt2->product_name); #ifdef DEBUG printf("product ID cmp vale=%d\n", return_val); #endif done: #ifdef DEBUG printf("leaving granprod_compar with %d\n", return_val); #endif return return_val; } /* sort the granules info list by Granule ID, Product ID, Granule version. * Return void */ void nagg_sort_granules(granule_p_t granule_info_p[], int number_of_granules) { /* arugment check */ assert(granule_info_p!=NULL); assert(number_of_granules>0); HDqsort(&granule_info_p[0], number_of_granules, sizeof(granule_p_t), granule_compar); } /* Compare PID1 and PID2 which are char*. * Return -1 if PID1 < PID2 * 0 if PID1 = PID2 * 1 if PID1 > PID2 */ static int char_compar(const char **PID1, const char **PID2) { int return_val; assert(PID1!=NULL); assert(PID2!=NULL); /* compare Product_ID which is a string */ return_val=HDstrcmp(*PID1, *PID2); done: return return_val; } /* Sort the PID_list with nPID_list elements. * Return void. */ void nagg_sort_PID(char* PID_list[], int nPID_list) { /* arugment check */ assert(PID_list!=NULL); assert(nPID_list>0); HDqsort(&PID_list[0], nPID_list, sizeof(char*), char_compar); } #define HDcpyFileTimeFromGranTime(ft, gt) \ HDmemcpy(ft, gt, 6); HDmemcpy(ft+6, gt+7, 1) /* Compose the output file name. * Parameters: * selected_granules: IN: The table of all granules. * number_of_granules: IN: number of granules in selected_granules. * products_list: IN: The list of all products requested. * nproducts: IN: Number of products in the list. * geoproduct IN: Geolocation product name if needed. * ngranulesperfile IN: Number of granules to be writtern to the output file. * outfileformat IN: Output file format, PACKAGED or UNPACKAGED. * createtime: OUT: creation time string is returned via it. * _noutfiles: OUT: number of output files names generated. * output_fname OUT: Composed new output file name is returned via it. * geo_fname OUT: Composed new Geo-file name is returned via it. * NULL if not requested (geoproduct==NULL); * Return code: * SUCCEED if succeed; otherwise FAIL */ /* Algorithm * Two output formats: Packaged and Unpackaged. * Packaged: all granules, product and Geolocation granules are written to the * same file. * Unpackaged: granules of each product and Geolocation are written to separate * files. * Geolocation granules are always written unless Geoproduct is NULL which * corresponds to the case of "-g no". * * file name convension * -...-__d_t_e \ * _b_c__.h5 * It can be divided into two parts, Head and Tail. * The Head consists of the hyphen separated DPID list; * The Tail consists of the rest of the file name; * The two is jointed by an underscore. * The Head is generated according to the output format, products list and if * geoproduct is given. * The Tail is generated according to information from the selected granules. */ int compose_output_fname(granule_p_t selected_granules[], int number_of_granules, char **products_list, int nproducts, char *geoproduct, int ngranulesperfile, outfileformat_t outfileformat, int *_noutfiles, NPPFileName_t output_fname[], char **geo_fname, char *creationdate) { nppfileinfo_t newfile; NPPFileName_t head, tail; granule_p_t firstgranule_p=NULL, lastgranule_p; char buf[64]; const char *const_pt; int i; int total_products; /* Sensor plus possible Geolocation */ int noutfiles=0; /* number of output file names generated. */ int ret_code=SUCCEED; const char *outdir; /* hold the sorted list of PID, +1 for the extra Geolocation PID */ char *PID_sorted[NAGG_Product_list_max+1]; int nPID_sorted; /* find the following among the granules that are to be written for this * file. * **Assume exactly (nproducts+1)*ngranulesperfile granules are available. * **If fewer, will inspect the last granule available. * * spacecraft: retrieve from 1st granule. * Start_date: retrieve from 1st granule. * Start_time: retrieve from 1st granule. * Orbit_number: retrieve from 1st granule. * Stop_time: retrieve from last granule. * Creation_date: calculate by machine. * Orgin: set to origin_arg. * Daomain: set to domain_arg. */ /* sanity check */ assert(selected_granules!=NULL); assert(number_of_granules>0); assert(products_list!=NULL); assert(nproducts>0 || geoproduct); assert(ngranulesperfile>0); assert(nproducts number_of_granules){ /* Not enough. Use the last granule. */ i = number_of_granules-1; }else{ i = total_products*ngranulesperfile-1; }; lastgranule_p = selected_granules[i]; /* Gather information to generate the Tail. */ /* get spacecraft from 1st granule */ newfile.spacecraft = "npp"; /*not available now. set to "npp" */ /* get Start_date, Start_time and orbilastgranule_p number from 1st granule. * Format of time in granules is: HHMMSS.ssssssZ. * Format of time in file is: HHMMSSs * copy the first 6 bytes and the 8th byte. */ newfile.Start_date = firstgranule_p->beginning_date; HDcpyFileTimeFromGranTime(newfile.Start_time,firstgranule_p->beginning_time); if (firstgranule_p->orbit_number > 99999){ NAGG_ERROR("Orbit number is too big"); ret_code=FAIL; goto done; } sprintf(buf, "%05ull", firstgranule_p->orbit_number); HDmemcpy(newfile.Orbit_number, buf, 5); /* get Stop_time from last granule for this file */ HDcpyFileTimeFromGranTime(newfile.Stop_time, lastgranule_p->ending_time); /* CalculaTe Creation_date (current date/time in UTC)*/ { struct timeval now; struct tm *tm; HDgettimeofday(&now, NULL); if (NULL==(tm=HDgmtime(&(now.tv_sec)))){ NAGG_ERROR("can't get current time for creation time"); ret_code=FAIL; goto done; } /* user %06ld for tv_usec which is a long */ sprintf(buf, "%04d%02d%02d%02d%02d%02d%06ld", 1900+tm->tm_year, 1+tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, now.tv_usec); HDmemcpy(newfile.Creation_date, buf, Creation_date_size); } /* Origin set to origin_arg */ newfile.Origin = origin_arg; /* Domain set to domain_arg */ newfile.Domain = domain_arg; /********************* * Compose the tail. * *********************/ sprintf (tail, "%.*s_d%.*s_t%.*s_e%.*s_b%.*s_c%.*s_%.*s_%.*s.h5", SPACECRAFT_size, newfile.spacecraft, Data_date_size, newfile.Start_date, Data_time_size, newfile.Start_time, Data_time_size, newfile.Stop_time, Orbit_number_size, newfile.Orbit_number, Creation_date_size, newfile.Creation_date, Origin_size, newfile.Origin, Domain_size, newfile.Domain ); /*********************** * Compose the Header. * ***********************/ /* get the outdir value and verfiy it will fit within the buffer space. */ /* Further improvement: should only do this once per execution. */ if (outdir = get_outdir()){ int tmp_len; /* Verify the outdir is not too big for the output_fname. */ /* Just calculate * outdir + / + nproducts*(productID + '-') + Optional Geoproduct * will all fit in the Header (DPID_size+1)*(DPID_NUM_MAX+1). */ tmp_len = HDstrlen(outdir) + 1; /* outdir + '/' */ #ifdef DEBUG printf("debug: outdir tmp_len=%d\n", tmp_len); printf("debug: nproducts=%d, DPID_size+1=%d\n", nproducts, DPID_size+1); #endif if (outfileformat == PACKAGED){ /* add all products and the optional geoproduct */ tmp_len += (nproducts + (geoproduct ? 1:0))*(DPID_size+1); }else{ /* each unpackaged file holds exactly one product */ tmp_len += DPID_size+1; } #ifdef DEBUG printf("debug: tmp_len=%d\n", tmp_len); printf("debug: DPID_NUM_MAX+1=%d\n", DPID_NUM_MAX+1); #endif if (tmp_len > (DPID_size+1)*(DPID_NUM_MAX+1)){ /* outdir name is too long. */ NAGG_ERROR("compose_output_fname(): output directory name is too long; use a shorter one.\n"); ret_code=FAIL; goto done; } } if (outfileformat == PACKAGED){ /* Packaged format: all products in one file */ char *pt; /* Sort the product-ID list. * First copy the products list to the sort list. * Add the Geolocation PID if given. * Sort the list. * Future improvement: should only need to sort it once since the * header stays constant. */ /* Copy products list to the sorted list */ HDmemcpy(PID_sorted, products_list, nproducts*sizeof(char *)); nPID_sorted = nproducts; /* Add the Geolocation PID if given. */ if (geoproduct){ PID_sorted[nPID_sorted++] = geoproduct; } /* sanity check */ if (nPID_sorted <= 0){ NAGG_ERROR("compose_output_fname(): no sensor nor geoproduct?!!\n"); ret_code=FAIL; goto done; } /* Sort the PID list */ nagg_sort_PID(&PID_sorted, nPID_sorted); /* Compose the head first. */ pt = &output_fname[0]; /* add the optional output directory plus a '/' */ if (outdir){ sprintf(pt, "%s/", outdir); pt += HDstrlen(pt); }; /* copy first product. */ HDstrcpy(pt, PID_sorted[0]); pt += HDstrlen(pt); /* then the remaining products with "-" separator */ for (i=1; i" here because * the option validate should have caught it earlier. */ if (geoproduct){ sprintf (newgeofilename, "%s%s%s_%s", (outdir ? outdir : ""), (outdir ? "/" : ""), geoproduct, tail); *geo_fname = (char *)newgeofilename; }else{ *geo_fname = NULL; } } /* return creation date */ HDmemcpy(creationdate, newfile.Creation_date, Creation_date_size); *(creationdate+Creation_date_size) = '\0'; /* update return values */ *_noutfiles = noutfiles; #ifdef DEBUG printf("Does it look like this like\n"); printf("%s\n", "REDRO_npp_d20030125_t0702533_e0711257_b00014_c20111025170507190780_unkn_ada.h5"); printf("newfilename=\n%s\n", newfilename); printf("newgeofilename=\n%s\n", newgeofilename); #endif done: return ret_code; } /* Purpose: * To select granules from the given granule_info table that matches one of the * products in the given products list or the geolocation product according to * the given number of granules per file. It returns a list of selected * granules, including fill granules, to be written to the output file(s). * * Returns SUCCEED if success; FAIL otherwise. * * Parameters: * * products list and number of granules per file (ngranulesperfile). * It returns total_granules_file. */ /* An NPP file, either a single file (packaged format) or a set of files * (unpackaged format), contains a bucket of granules, which correspond to the * number of granules given. A bucket contains the same number of slots so that * each slot holds one granule. * * Bucket alignment Algorithm. * 2.1. Nagg algorithm in the calculation of bucket alignment * Let N be the number of granules requested by the nagg user to reaggregate * the NPP product files. * Let Tg be the duration of the first selected granule. (This value is * different for different products and is defined in the product_table.) * Then Tbucket = N*Tg seconds. * * Let An be the n-th bucket since epoch. * Let Asn and Aen be the starting and ending time of An. * Let Gs be the beginning time of the first selected granule. * Then * An = floor(Gs/Tbucket ) * Asn = An*(Tbucket ) * Aen = As + Tbucket * * 2.2. How Nagg adds fill granules to produced files * 2.2.1. First produced file * For the first file, if the starting time of the first selected granule * is bigger than Asn, no fill granules are added before copying existing * granules to the new file. This will produce a partial file. * 2.2.2. second to (n-1)-th files * N existing granules per product requested are copied to each of the * new files, insert fill granules in place of any missing granules. * 2.2.3. Last (n-th) file * Remaining granules per product requested are copied to the last file. * If the ending time of the last granule is less than the ending time of the * last bucket, no fill granules are added. This will produce a partial file. * * Supporting multiple products and optional Geolocation Product: * Have an array of granules_found and a variable of geogranule_found. * At the beginning of each time step, clear granules_found and * geogranule_found. * Whenever a granule, fitting current time step, is selected, look it up into * products_list and geoproduct if applicable. When found, mark the * corresponding granules_found array or the geogranule_found variable. * When no more granules can be selected to fit the current time step, go * through the granules_found array or the geogranule_found variable, make a * fill granule for every expected granule that is not found in this time step. * Repeat this till no more granules to select or all needed granules for this * bucket are selected. */ int select_granules(granule_p_t granule_info[], int *_gindex, char **products_list, int nproducts, int total_nproducts, char *geoproduct, granule_p_t granules_selected[], int ngranulesperfile, int *_granules_remain, int *_total_granules_file) { static notcalled=1; /* has not been called. Need to initialize. */ int granules_remain = *_granules_remain; int gindex = *_gindex; int need_granules, got_granules=0; int i, j, matched; int got_products=0; int ret_code=SUCCEED; static granule_p_t last_granule=NULL; /* need to remember it from */ /* previous call so that it can */ /* detect duplicated granules. */ granule_p_t next_granule; granule_p_t fill_granule; int granules_filtered=0; char *product_id; iet_t time0=Time_First_Ascend; /* Time0: beginning of time. */ static iet_t timeg=0; /* Tg: duration of one granule (ms). 0: not set. */ static iet_t timebucket; /* one bucket time (ms) */ int An; /* bucket index */ iet_t Asn; /* bucket begin time (ms) */ static iet_t next_slot_btime; /* next granule slot begin time (ms) */ int filled_granules = 0; int before_real_granules=FALSE; int granules_found[NAGG_Product_list_max], geogranule_found; int islots, foundproducts; /* sanity check */ assert(granule_info!=NULL); assert(products_list!=NULL); assert(nproducts>0 || geoproduct); assert(granule_info!=NULL); assert(ngranulesperfile>0); need_granules = ngranulesperfile*(total_nproducts); /* initial fill granules array in case we need to make fill granules. */ /* Should need at most of fill granules. */ if (FAIL==init_fill_granules_array(need_granules)){ NAGG_ERROR("could not initialize fill granules array"); ret_code=FAIL; goto done; }; islots=0; while (islots < ngranulesperfile && granules_remain > 0){ foundproducts=0; /* reset the granules_found array and Geogranule_found variable */ HDmemzero(granules_found, sizeof(granules_found)); geogranule_found = 0; while (foundproducts < total_nproducts && granules_remain > 0){ next_granule = granule_info[gindex++]; granules_remain--; /* is it one of the products requested? */ product_id=next_granule->product_id; #ifdef DEBUG printf("product_id=%s, products_list[0]=%s, geoproduct=%s\n", product_id, products_list[0], geoproduct); #endif /* check if this granules is one of the products or geoproducts */ matched = 0; /* initial to no */ for (i=0; igranule_start_time_IET-time0)/timebucket; Asn = An*timebucket + time0; /* set Asn as the next granule slot begin time. */ next_slot_btime = Asn; #ifdef DEBUG printf("select_granule: Time0=%.1f, Calculated timeg=%.1f, timebucket=%.1f\n" "An=%d, Asn=%.1f, next_slot_btime=%.1f\n", micros2s(time0), micros2s(timeg), micros2s(timebucket), An, micros2s(Asn), micros2s(next_slot_btime)); #endif /* Since we are not generating leading fill granules of the first * file, we need to move next_slot_btime forward to near the begin * time of first real granule. This is done by finding all the * empty slots leading to the first real granule. Then move up the * next_slot_btime to the slot that will contain the first real * granule. * Also move up islots by the same increment. */ i = (next_granule->granule_start_time_IET - next_slot_btime)/timeg; next_slot_btime += i*timeg; islots += i; #ifdef DEBUG printf("select_granule: Calculated An=%d, Asn=%.1f, " "next_slot_btime=%.1f, next->start_time=%.1f\n", An, micros2s(Asn), micros2s(next_slot_btime), micros2s(next_granule->granule_start_time_IET)); #endif }; /* if (notcalled) */ if (last_granule){ if (granprod_compar(&next_granule, &last_granule)==0){ /* found duplicated granule. Skip it */ #ifdef DEBUG printf("select_granule: duplicated granule found\n"); #endif continue; } } /* If we have not got any granules yet in this run and the begin time * of the next granule is more than the bucket time away, we will end * up with file that contains only fill granules. In that case, we * don't want to generate such a file. Therefore, we would need to * move forward the next_slot_btime so that we can get some real * granules. */ if (got_granules == 0){ if ((next_granule->granule_start_time_IET - next_slot_btime) > timebucket) { next_slot_btime += ((next_granule->granule_start_time_IET - next_slot_btime)/timebucket)*timebucket; } } /* If the next granule begin time is more than Timeg from the previous * granule endtime, it means no more granules will match the current time slot. * Proceed to check if all product granules of this time slot have been * found and issue filled granules for any missing ones. */ if (next_granule->granule_start_time_IET > (next_slot_btime + timeg)) { GENERATE_MISSING_GRANULES; /* push the granule back */ gindex--; granules_remain++; } else { /* Mark which product is found */ /* check if this granules is one of the products or geoproducts */ matched = 0; /* initial to no */ for (i=0; i 0)*/ /* check why it exits the above loop */ if (foundproducts == total_nproducts){ /* Normal case: got all products for this time slot. */ islots++; /* Increment both islot and next_slot_btime and repeat again. */ next_slot_btime += timeg; }else{ /* No more granules to select */ if (foundproducts > 0){ /* A partial slot. Generate filled granules. */ GENERATE_MISSING_GRANULES; islots++; }; break; /* quit the outer loop. */ }; }; /* while (islots < ngranulesperfile && granules_remain > 0) */ done: #ifdef DEBUG printf("select_granule: filled_granules=%d\n", filled_granules); #endif *_total_granules_file = got_granules; *_granules_remain = granules_remain; *_gindex = gindex; return ret_code; } /* Initalize fill granules array. * Allocate memory for it if this is the first time. * If array has been allocated, verify its size is not less than . * Return SUCCEED if succeed; otherwise FAIL. */ static int init_fill_granules_array(int size){ int ret_code = SUCCEED; /* Allocate memory for fill_granule_array if this is the first time. */ if (!fill_granule_array){ if ((fill_granule_array=HDcalloc(size, sizeof(granule_t)))==NULL){ NAGG_ERROR("Out of memory when allocating fill granules"); ret_code=FAIL; goto done; }; array_size = size; }; /* Verify size is sufficient. If not, report error because it should */ /* not happen. May consider realloc if it makes sense. */ if (array_size < size){ NAGG_ERROR("fill array size is less than requested size"); ret_code=FAIL; goto done; }; /* reset index to the beginning */ array_index = 0; done: if (ret_code != SUCCEED){ /* error encountered. cleanup things */ if (fill_granule_array){ HDfree(fill_granule_array); fill_granule_array=NULL; } }; return(ret_code); } /* Create a fill granule. * The following fields are assigned with these values. The other fields have * undefined values and should not be used. * Granule ID is "N/A" * Product_id is the given product ID * Product_name is the product name deduced from product ID * start_time_IET is start time * end_time_IET is start time + duration * beginning_date is converted from start_time_IET * beginning_time is converted from start_time_IET * ending_time is converted from end_time_IET */ static granule_p_t make_fill_granule(char *product, iet_t start_time, iet_t duration) { granule_p_t ret_code; granule_p_t granule_p; char *UTC; /* sanity check */ if (!product || start_time <=0 || duration <= 0){ NAGG_ERROR("bad argument for make_fill_granule"); ret_code=NULL; goto done; } /* check if any more fill granule is available */ if (array_index < array_size){ granule_p = &fill_granule_array[array_index]; array_index++; }else{ NAGG_ERROR("no more fill granule available"); ret_code=NULL; goto done; } /* Add in appropriate values */ HDstrncpy(granule_p->product_id, product, DPID_size); /* Need not do error checking since we know product_name must be defined * by now and its size always fits in product_name. */ HDstrcpy(granule_p->product_name, get_product_sname_by_id(product)); /* use strcpy because I know the lengths of both src and dest strings */ HDstrcpy(granule_p->granule_id, "N/A"); granule_p->granule_start_time_IET = start_time; granule_p->granule_end_time_IET = start_time + duration; /* convert start_time_IET to beginning date and time */ UTC = iet2utc(granule_p->granule_start_time_IET); if (NULL==UTC){ NAGG_ERROR("failed to convert start_time to beginning date"); ret_code=NULL; goto done; } sprintf(granule_p->beginning_date, "%8.8s", UTC); sprintf(granule_p->beginning_time, "%13.13sZ", UTC+9); /* convert end_time_IET to ending time */ UTC = iet2utc(granule_p->granule_end_time_IET); if (NULL==UTC){ NAGG_ERROR("failed to convert end_time to ending time"); ret_code=NULL; goto done; } sprintf(granule_p->ending_time, "%13.13sZ", UTC+9); /* all done */ ret_code=granule_p; done: return(ret_code); } /* Convert IET time (in microsecons) to UTC format. * Return value: * If succeeded, return a char string that points to the UTC format; * otherwise, return NULL. * Note that the return value points to a statically allocated string which * might be overwritten by subsequent calls to iet2utc. */ #define IET_UNIX_diff 378691234L; /* Difference in seconds between IET */ /* and Unix time */ #define UTC_SIZE 22 /* YYYYMMDD.HHMMSS.ssssss */ static char utc_string[UTC_SIZE+1]; static char *iet2utc(iet_t iet_time) { time_t unixtime; struct tm *t; unixtime = iet_time/million_microsec - IET_UNIX_diff; t = gmtime(&unixtime); sprintf(utc_string, "%4d%02d%02d.%02d%02d%02d.%06d", t->tm_year+1900, t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, iet_time-(iet_time/million_microsec)*million_microsec); #ifdef DEBUG printf("utc_string=%s\n", utc_string); #endif return(utc_string); }