/* ** File: grib2to1.c ** ** Author: Bob Dattore ** NCAR/DSS ** dattore@ucar.edu ** (303) 497-1825 ** ** Purpose: to provide a simple C program for converting from GRIB2 to GRIB1 ** ** This program has been tested on Sun/Solaris and Dell/Windows XP systems. ** ** Revision History: ** 20 Feb 2008 - first version; will convert analyses and forecasts ** on a regular latitude/longitude grid ** 31 Mar 2008 - added code for Lambert conformal grids (GDS template ** 3.30), statistically processed data (PDS template 4.8), and some ** NCEP-specific parameter mappings ** 03 Apr 2008 - bug fixes ** 17 Apr 2008 - change reference value packing so that it works on ** little-endian machines ** 16 Jul 2008 - added more GRIB2 to GRIB1 parameter mappings, added ** PDS packing for GRIB2 Product Definition Template 4.2 ** 22 Jul 2008 - patch for NCEP max/min temperature grids ** ** Contact Bob Dattore at dattore@ucar.edu to get conversions for other products ** and grid definitions added. ** ** You will need to download the GRIB2 decoder: ** http://dss.ucar.edu/libraries/grib/c_routines/unpackgrib2.c ** It must be in the same directory as this program. ** ** You will also need to link with the JasPer library, which needs the JPEG-6b ** library. ** You can get the JasPer code at ** http://www.ece.uvic.ca/~mdadams/jasper/ ** You can get the installation code for libjpeg.a at ** http://www.ijg.org/files/jpegsrc.v6b.tar.gz ** ** Example compile command: ** % cc -o grib2to1 -I -L grib2to1.c -ljasper ** where is the directory path of the jasper header ** files ** is the directory path of the jasper library ** ** If the compiler complains about the "pow" function being an undefined ** symbol, include the math library in the compile (-lm) ** ** To use the program: ** % grib2to1 */ #include #include #include "unpackgrib2.c" /* setBits sets the contents of the various GRIB octets ** buf is the GRIB buffer as a stream of bytes ** src is the value of the octet(s) to set ** off is the offset in BITS from the beginning of the buffer to the beginning ** of the octet(s) to be packed ** bits is the number of BITS to pack - will be a multiple of 8 since GRIB ** octets are 8 bits long */ void setBits(unsigned char *buf,int src,size_t off,size_t bits) { unsigned char bmask,left,right; int smask; size_t buf_size=sizeof(unsigned char)*8,src_size=sizeof(int)*8; size_t wskip,bskip,lclear,rclear,more; size_t n; // no work to do if (bits == 0) return; if (bits > src_size) { fprintf(stderr,"Error: packing %d bits from a %d-bit field\n",bits,src_size); exit(1); } else { // create masks to use when right-shifting (necessary because different // compilers do different things when right-shifting a signed bit-field) bmask=1; for (n=1; n < buf_size; n++) { bmask<<=1; bmask++; } smask=1; for (n=1; n < src_size; n++) { smask<<=1; smask++; } // get number of words and bits to skip before packing begins wskip=off/buf_size; bskip=off % buf_size; lclear=bskip+bits; rclear=buf_size-bskip; left= (rclear != buf_size) ? (buf[wskip]&(bmask<>more)&~(smask<<(bits-more))); // clear the next (or part of the next) word and pack those bits while (more > buf_size) { more-=buf_size; buf[++wskip]=(src>>more)&~(smask<<(src_size-more)); } wskip++; more=buf_size-more; right= (more != buf_size) ? (buf[wskip]&~(bmask< src_size) ? src&~(bmask<md.lvl2_type != 255 && grid->md.lvl1_type != grid->md.lvl2_type) { fprintf(stderr,"Unable to indicate a layer bounded by different level types %d and %d in GRIB1\n",grid->md.lvl1_type,grid->md.lvl2_type); exit(1); } *level1=*level2=0; switch (grid->md.lvl1_type) { case 1: *level_type=1; break; case 2: *level_type=2; break; case 3: *level_type=3; break; case 4: *level_type=4; break; case 5: *level_type=5; break; case 6: *level_type=6; break; case 7: *level_type=7; break; case 8: *level_type=8; break; case 9: *level_type=9; break; case 20: *level_type=20; break; case 100: if (grid->md.lvl2_type == 255) { *level_type=100; *level1=grid->md.lvl1/100.; } else { *level_type=101; *level1=grid->md.lvl1/1000.; *level2=grid->md.lvl2/1000.; } break; case 101: *level_type=102; break; case 102: if (grid->md.lvl2_type == 255) { *level_type=103; *level1=grid->md.lvl1; } else { *level_type=104; *level1=grid->md.lvl1/100.; *level2=grid->md.lvl2/100.; } break; case 103: if (grid->md.lvl2_type == 255) { *level_type=105; *level1=grid->md.lvl1; } else { *level_type=106; *level1=grid->md.lvl1/100.; *level2=grid->md.lvl2/100.; } break; case 104: if (grid->md.lvl2_type == 255) { *level_type=107; *level1=grid->md.lvl1*10000.; } else { *level_type=108; *level1=grid->md.lvl1*100.; *level2=grid->md.lvl2*100.; } break; case 105: *level1=grid->md.lvl1; if (grid->md.lvl2_type == 255) *level_type=109; else { *level_type=110; *level2=grid->md.lvl2; } break; case 106: *level1=grid->md.lvl1*100.; if (grid->md.lvl2_type == 255) *level_type=111; else { *level_type=112; *level2=grid->md.lvl2*100.; } break; case 107: if (grid->md.lvl2_type == 255) { *level_type=113; *level1=grid->md.lvl1; } else { *level_type=114; *level1=475.-grid->md.lvl1; *level2=475.-grid->md.lvl2; } break; case 108: *level1=grid->md.lvl1/100.; if (grid->md.lvl2_type == 255) *level_type=115; else { *level_type=116; *level2=grid->md.lvl2/100.; } break; case 109: *level_type=117; *level1=grid->md.lvl1*1000000000.; break; case 111: if (grid->md.lvl2_type == 255) { *level_type=119; *level1=grid->md.lvl1*10000.; } else { *level_type=120; *level1=grid->md.lvl1*100.; *level2=grid->md.lvl2*100.; } break; case 117: fprintf(stderr,"There is no GRIB1 level code for 'Mixed layer depth'\n"); exit(1); case 160: *level_type=160; *level1=grid->md.lvl1; break; case 200: switch (center) { case 7: *level_type=200; break; } break; } } int mapStatisticalEndTime(GRIBMessage *msg,GRIB2Grid *grid) { switch (grid->md.time_unit) { case 0: return (grid->md.stat_proc.etime/100 % 100)-(msg->time/100 % 100); case 1: return (grid->md.stat_proc.etime/10000-msg->time/10000); case 2: return (grid->md.stat_proc.edy-msg->dy); case 3: return (grid->md.stat_proc.emo-msg->mo); case 4: return (grid->md.stat_proc.eyr-msg->yr); default: fprintf(stderr,"Unable to map end time with units %d to GRIB1\n",grid->md.time_unit); exit(1); } } void mapTimeRange(GRIBMessage *msg,GRIB2Grid *grid,int *p1,int *p2,int *t_range,int *n_avg,int *n_missing,int center) { size_t n; switch (grid->md.pds_templ_num) { case 0: case 2: *t_range=0; *p1=grid->md.fcst_time; *p2=0; *n_avg=*n_missing=0; break; case 8: case 12: if (grid->md.stat_proc.num_ranges > 1) { fprintf(stderr,"Unable to map multiple statistical processes to GRIB1\n"); exit(1); } for (n=0; n < grid->md.stat_proc.num_ranges; n++) { switch (grid->md.stat_proc.proc_code[n]) { case 0: case 1: case 4: switch (grid->md.stat_proc.proc_code[n]) { /* average */ case 0: *t_range=3; break; /* accumulation */ case 1: *t_range=4; break; /* difference */ case 4: *t_range=5; break; } *p1=grid->md.fcst_time; *p2=mapStatisticalEndTime(msg,grid); if (grid->md.stat_proc.incr_length[n] == 0) *n_avg=0; else { fprintf(stderr,"Unable to map discrete processing to GRIB1\n"); exit(1); } break; // maximum case 2: // minimum case 3: *t_range=2; *p1=grid->md.fcst_time; *p2=mapStatisticalEndTime(msg,grid); if (grid->md.stat_proc.incr_length[n] == 0) *n_avg=0; else { fprintf(stderr,"Unable to map discrete processing to GRIB1\n"); exit(1); } break; default: // patch for NCEP grids if (grid->md.stat_proc.proc_code[n] == 255 && center == 7) { if (msg->disc == 0) { if (grid->md.param_cat == 0) { switch (grid->md.param_num) { case 4: case 5: *t_range=2; *p1=grid->md.fcst_time; *p2=mapStatisticalEndTime(msg,grid); if (grid->md.stat_proc.incr_length[n] == 0) *n_avg=0; else { fprintf(stderr,"Unable to map discrete processing to GRIB1\n"); exit(1); } break; } } } } else { fprintf(stderr,"Unable to map statistical process %d to GRIB1\n",grid->md.stat_proc.proc_code[n]); exit(1); } } } *n_missing=grid->md.stat_proc.nmiss; break; default: fprintf(stderr,"Unable to map time range for Product Definition Template %d into GRIB1\n",grid->md.pds_templ_num); exit(1); } } void packPDS(GRIBMessage *msg,int grid_number,unsigned char *grib1_buffer,size_t *offset) { int level_type,level1,level2,p1,p2,t_range,n_avg,n_missing,D; static short warned_ensemble=0; // length of the PDS setBits(grib1_buffer,28,*offset,24); // GRIB1 tables version number setBits(grib1_buffer,3,*offset+24,8); // originating center ID setBits(grib1_buffer,msg->center_id,*offset+32,8); // generating process ID setBits(grib1_buffer,msg->grids[grid_number].md.gen_proc,*offset+40,8); // grid definition catalog number - set to 255 because GDS is to be included setBits(grib1_buffer,255,*offset+48,8); // flag if (msg->grids[grid_number].md.bitmap == NULL) setBits(grib1_buffer,0x80,*offset+56,8); else setBits(grib1_buffer,0xc0,*offset+56,8); // parameter code setBits(grib1_buffer,mapParameterData(msg->center_id,msg->disc,msg->grids[grid_number].md.param_cat,msg->grids[grid_number].md.param_num),*offset+64,8); mapLevelData(&msg->grids[grid_number],&level_type,&level1,&level2,msg->center_id); // level type code setBits(grib1_buffer,level_type,*offset+72,8); if (msg->grids[grid_number].md.lvl2_type == 255) setBits(grib1_buffer,level1,*offset+80,16); else { setBits(grib1_buffer,level1,*offset+80,8); setBits(grib1_buffer,level2,*offset+88,8); } // year of century setBits(grib1_buffer,(msg->yr % 100),*offset+96,8); // month setBits(grib1_buffer,msg->mo,*offset+104,8); // day setBits(grib1_buffer,msg->dy,*offset+112,8); // hour setBits(grib1_buffer,msg->time/10000,*offset+120,8); // minute setBits(grib1_buffer,(msg->time/100 % 100),*offset+128,8); // second if (msg->md.time_unit == 13) fprintf(stderr,"Unable to indicate 'Second' for time unit in GRIB1\n"); else setBits(grib1_buffer,msg->md.time_unit,*offset+136,8); mapTimeRange(msg,&msg->grids[grid_number],&p1,&p2,&t_range,&n_avg,&n_missing,msg->center_id); if (t_range == 10) setBits(grib1_buffer,p1,*offset+144,16); else { setBits(grib1_buffer,p1,*offset+144,8); setBits(grib1_buffer,p2,*offset+152,8); } setBits(grib1_buffer,t_range,*offset+160,8); // century of year setBits(grib1_buffer,(msg->yr/100)+1,*offset+192,8); // originating sub-center ID setBits(grib1_buffer,msg->sub_center_id,*offset+200,8); // decimal scale factor D=msg->md.D; if (D < 0) D=-D+0x8000; setBits(grib1_buffer,D,*offset+208,16); (*offset)+=224; if (msg->md.derived_fcst_code >= 0) { // length of the PDS setBits(grib1_buffer,42,*offset-224,24); setBits(grib1_buffer,msg->md.derived_fcst_code,*offset+96,8); setBits(grib1_buffer,msg->md.nfcst_in_ensemble,*offset+104,8); (*offset)+=112; if (warned_ensemble == 0) { fprintf(stderr,"Notice: the 'Derived forecast code' and the 'Number of forecasts in ensemble'\n"); fprintf(stderr,"from Product Definition Template 4.2 and/or Product Definition Template 4.12\n"); fprintf(stderr,"have been packed in octets 41 and 42 of the GRIB1 Product Definition Section\n"); warned_ensemble=1; } } } void packGDS(GRIBMessage *msg,int grid_number,unsigned char *grib1_buffer,size_t *offset) { int rescomp=0,sign,value; // NV setBits(grib1_buffer,255,*offset+24,8); // PV setBits(grib1_buffer,255,*offset+32,8); switch (msg->md.gds_templ_num) { case 0: // length of the GDS setBits(grib1_buffer,32,*offset,24); // data representation setBits(grib1_buffer,0,*offset+40,8); // Ni setBits(grib1_buffer,msg->md.nx,*offset+48,16); // Nj setBits(grib1_buffer,msg->md.ny,*offset+64,16); // first latitude value=msg->md.slat*1000.; if (value < 0.) { value=-value; setBits(grib1_buffer,1,*offset+80,1); setBits(grib1_buffer,value,*offset+81,23); } else setBits(grib1_buffer,value,*offset+80,24); // first longitude value=msg->md.slon*1000.; if (value < 0.) { value=-value; setBits(grib1_buffer,1,*offset+104,1); setBits(grib1_buffer,value,*offset+105,23); } else setBits(grib1_buffer,value,*offset+104,24); // resolution and component flags if (msg->md.rescomp&0x20 == 0x20) rescomp|=0x80; if (msg->md.earth_shape == 2) rescomp|=0x40; if (msg->md.rescomp&0x8 == 0x8) rescomp|=0x8; setBits(grib1_buffer,rescomp,*offset+128,8); // last latitude value=msg->md.lats.elat*1000.; if (value < 0.) { value=-value; setBits(grib1_buffer,1,*offset+136,1); setBits(grib1_buffer,value,*offset+137,23); } else setBits(grib1_buffer,value,*offset+136,24); // last longitude value=msg->md.lons.elon*1000.; if (value < 0.) { value=-value; setBits(grib1_buffer,1,*offset+160,1); setBits(grib1_buffer,value,*offset+161,23); } else setBits(grib1_buffer,value,*offset+160,24); // Di increment value=msg->md.xinc.loinc*1000.; if (value < 0.) { value=-value; setBits(grib1_buffer,1,*offset+184,1); setBits(grib1_buffer,value,*offset+185,15); } else setBits(grib1_buffer,value,*offset+184,16); // Dj increment value=msg->md.yinc.lainc*1000.; if (value < 0.) { value=-value; setBits(grib1_buffer,1,*offset+200,1); setBits(grib1_buffer,value,*offset+201,15); } else setBits(grib1_buffer,value,*offset+200,16); // scanning mode setBits(grib1_buffer,msg->md.scan_mode,*offset+216,8); // reserved setBits(grib1_buffer,0,*offset+224,32); (*offset)+=256; break; case 30: // length of the GDS setBits(grib1_buffer,42,*offset,24); // data representation setBits(grib1_buffer,3,*offset+40,8); // Nx setBits(grib1_buffer,msg->md.nx,*offset+48,16); // Ny setBits(grib1_buffer,msg->md.ny,*offset+64,16); // first latitude value=msg->md.slat*1000.; if (value < 0.) { value=-value; setBits(grib1_buffer,1,*offset+80,1); setBits(grib1_buffer,value,*offset+81,23); } else setBits(grib1_buffer,value,*offset+80,24); // first longitude value=msg->md.slon*1000.; if (value < 0.) { value=-value; setBits(grib1_buffer,1,*offset+104,1); setBits(grib1_buffer,value,*offset+105,23); } else setBits(grib1_buffer,value,*offset+104,24); // resolution and component flags if (msg->md.rescomp&0x20 == 0x20) rescomp|=0x80; if (msg->md.earth_shape == 2) rescomp|=0x40; if (msg->md.rescomp&0x8 == 0x8) rescomp|=0x8; setBits(grib1_buffer,rescomp,*offset+128,8); // LoV value=msg->md.lons.lov*1000.; if (value < 0.) { value=-value; setBits(grib1_buffer,1,*offset+136,1); setBits(grib1_buffer,value,*offset+137,23); } else setBits(grib1_buffer,value,*offset+136,24); // Dx value=msg->md.xinc.dxinc+0.5; setBits(grib1_buffer,value,*offset+160,24); // Dy value=msg->md.yinc.dyinc+0.5; setBits(grib1_buffer,value,*offset+184,24); // projection center flag setBits(grib1_buffer,msg->md.proj_flag,*offset+208,8); // scanning mode setBits(grib1_buffer,msg->md.scan_mode,*offset+216,8); // latin1 value=msg->md.latin1*1000.; if (value < 0.) { value=-value; setBits(grib1_buffer,1,*offset+224,1); setBits(grib1_buffer,value,*offset+225,23); } else setBits(grib1_buffer,value,*offset+224,24); // latin2 value=msg->md.latin2*1000.; if (value < 0.) { value=-value; setBits(grib1_buffer,1,*offset+248,1); setBits(grib1_buffer,value,*offset+249,23); } else setBits(grib1_buffer,value,*offset+248,24); // latitude of southern pole of projection value=msg->md.splat*1000.; if (value < 0.) { value=-value; setBits(grib1_buffer,1,*offset+272,1); setBits(grib1_buffer,value,*offset+273,23); } else setBits(grib1_buffer,value,*offset+272,24); // longitude of southern pole of projection value=msg->md.splon*1000.; if (value < 0.) { value=-value; setBits(grib1_buffer,1,*offset+296,1); setBits(grib1_buffer,value,*offset+297,23); } else setBits(grib1_buffer,value,*offset+296,24); // reserved setBits(grib1_buffer,0,*offset+320,16); (*offset)+=336; break; default: fprintf(stderr,"Unable to map Grid Definition Template %d into GRIB1\n",msg->md.gds_templ_num); exit(1); } } void packBMS(GRIBMessage *msg,int grid_number,unsigned char *grib1_buffer,size_t *offset,int num_points) { int length=6+(num_points+7)/8; int ub=8-(num_points % 8); size_t n,off; // length of the BMS setBits(grib1_buffer,length,*offset,24); // unused bits at end of section setBits(grib1_buffer,ub,*offset+24,8); // table reference setBits(grib1_buffer,0,*offset+32,16); // the bitmap off=*offset+48; for (n=0; n < num_points; n++) setBits(grib1_buffer,msg->grids[grid_number].md.bitmap[n],off++,1); (*offset)+=length*8; } int ieee2ibm(double ieee) { int ibm_real=0; unsigned char *ir=(unsigned char *)&ibm_real; int sign=0,fr=0; int exp=64; const double full=0xffffff; size_t size=sizeof(size_t)*8,off=0; if (ieee != 0.) { if (ieee < 0.) { sign=1; ieee=-ieee; } ieee/=pow(2.,-24.); while (exp > 0 && ieee < full) { ieee*=16.; exp--; } while (ieee > full) { ieee/=16.; exp++; } fr=ieee+0.5; if (size > 32) { off=size-32; setBits(ir,0,0,off); } setBits(ir,sign,off,1); setBits(ir,exp,off+1,7); setBits(ir,fr,off+8,24); } return ibm_real; } void packBDS(GRIBMessage *msg,int grid_number,unsigned char *grib1_buffer,size_t *offset,int *pvals,int num_to_pack,int pack_width) { int length=11+(num_to_pack*pack_width+7)/8; size_t m,off; int E,ibm_rep; // length of the BDS setBits(grib1_buffer,length,*offset,24); // flag setBits(grib1_buffer,0,*offset+24,4); // unused bits setBits(grib1_buffer,(length-11)*8-(num_to_pack*pack_width),*offset+28,4); // scale factor E E=msg->grids[grid_number].md.E; if (E < 0) E=-E+0x8000; setBits(grib1_buffer,E,*offset+32,16); // Reference value ibm_rep=ieee2ibm(msg->grids[grid_number].md.R*pow(10.,msg->grids[grid_number].md.D)); memcpy(&grib1_buffer[(*offset+48)/8],&ibm_rep,4); // width in bits of each packed value setBits(grib1_buffer,pack_width,*offset+80,8); // packed data values off=*offset+88; for (m=0; m < num_to_pack; m++) { setBits(grib1_buffer,pvals[m],off,pack_width); off+=pack_width; } } int main(int argc,char **argv) { GRIBMessage grib_msg; FILE *fp,*ofp; size_t nmsg=0,ngrid=0; int status; int length,max_length=0,num_points,num_to_pack,pack_width,*pvals,max_pack; unsigned char *grib1_buffer=NULL,dum[3]; char *head="GRIB",*tail="7777"; size_t n,m,offset,cnt; grib_msg.buffer=NULL; grib_msg.grids=NULL; if (argc != 3) { fprintf(stderr,"usage: %s GRIB2_file_name GRIB1_file_name\n",argv[0]); exit(1); } fp=fopen(argv[1],"rb"); ofp=fopen(argv[2],"wb"); while ( (status=unpackgrib2(fp,&grib_msg)) == 0) { fprintf(stderr,"nmsg=%d\n",nmsg); nmsg++; for (n=0; n < grib_msg.num_grids; n++) { // calculate the octet length of the GRIB1 grid (minus the Indicator and End // Sections, which are both fixed in length switch (grib_msg.md.pds_templ_num) { case 0: case 8: length=28; break; case 2: case 12: length=42; break; default: fprintf(stderr,"Unable to map Product Definition Template %d into GRIB1\n",grib_msg.md.gds_templ_num); exit(1); } switch (grib_msg.md.gds_templ_num) { case 0: length+=32; num_points=grib_msg.md.nx*grib_msg.md.ny; break; case 30: length+=42; num_points=grib_msg.md.nx*grib_msg.md.ny; break; default: fprintf(stderr,"Unable to map Grid Definition Template %d into GRIB1\n",grib_msg.md.gds_templ_num); exit(1); } if (grib_msg.grids[n].md.bitmap != NULL) { length+=6+(num_points+7)/8; num_to_pack=0; for (m=0; m < num_points; m++) { if (grib_msg.grids[n].md.bitmap[m] == 1) num_to_pack++; } } else num_to_pack=num_points; pvals=(int *)malloc(sizeof(int)*num_to_pack); max_pack=0; cnt=0; for (m=0; m < num_points; m++) { if (grib_msg.grids[n].gridpoints[m] != GRIB_MISSING_VALUE) { //printf("%f %f\n",grib_msg.grids[n].md.R,grib_msg.grids[n].gridpoints[m]); pvals[cnt]=(grib_msg.grids[n].gridpoints[m]-grib_msg.grids[n].md.R)*pow(10.,grib_msg.grids[n].md.D)/pow(2.,grib_msg.grids[n].md.E); if (pvals[cnt] > max_pack) max_pack=pvals[cnt]; cnt++; } } pack_width=1; while (pow(2.,pack_width)-1 < max_pack) pack_width++; length+=11+(num_to_pack*pack_width+7)/8; // allocate enough memory for the GRIB1 buffer if (length > max_length) { if (grib1_buffer != NULL) free(grib1_buffer); grib1_buffer=(unsigned char *)malloc(length*sizeof(unsigned char)); max_length=length; } offset=0; // pack the Product Definition Section packPDS(&grib_msg,n,grib1_buffer,&offset); // pack the Grid Definition Section packGDS(&grib_msg,n,grib1_buffer,&offset); // pack the Bitmap Section, if it exists if (grib_msg.grids[n].md.bitmap != NULL) packBMS(&grib_msg,n,grib1_buffer,&offset,num_points); // pack the Binary Data Section packBDS(&grib_msg,n,grib1_buffer,&offset,pvals,num_to_pack,pack_width); free(pvals); // output the GRIB1 grid fwrite(head,1,4,ofp); setBits(dum,length+12,0,24); fwrite(dum,1,3,ofp); dum[0]=1; fwrite(dum,1,1,ofp); fwrite(grib1_buffer,1,length,ofp); fwrite(tail,1,4,ofp); ngrid++; } } if (status != -1) printf("Read error after %d messages\n",nmsg); printf("Number of GRIB1 grids written to output: %d\n",ngrid); fclose(fp); fclose(ofp); }