/* $Id$ */ /*****************************************************************************/ /* Copyright 2013 by the University of Alabama in Huntsville. */ /* All rights reserved. */ /*****************************************************************************/ /*****************************************************************************/ /* Name: gpmnrlrtifld2nc.c - Convert NRL binary data to NetCDF */ /* */ /* Usage: gpmnrlrtifld2nc filename */ /* filename - Input file name */ /* */ /* Description: */ /* Convert the given NRL binary data file to NetCDF. The file consists */ /* of one quarter-degree grid. There are 480 rows of 1440 columns. */ /* The ULC is 59.875, -179.875. The data are short integers, but in */ /* big-endian order, so we have to swap bytes. */ /* */ /* Missing data values are negative. */ /* */ /* The output file is created in the current directory and named the */ /* same as the input file, suffixing it with ".nc": */ /* */ /* yyyymmddhh00.1.geo (binary data file) */ /* yyyymmddhh00.1.geo.nc (NetCDF file) */ /* */ /* This file contains the following routines: */ /* */ /* main - Main program */ /* */ /* Notes: */ /* In order to make "missing data" and "no rain" transparent, we set */ /* all "missing" values to 0 and set the fill value to 0. */ /* */ /* Revision history: */ /* 2013-04-26 BEB Written */ /*****************************************************************************/ #include #include #include #include #include #include #include #include #include #include /* Constants */ #define TITLE "NRL Real-time Rain Rate Data" #define ULLAT 59.875 /* Upper-left corner latitude */ #define ULLON -179.875 /* Upper-left corner longitude */ #define LRLAT -60.125 /* Lower-right corner latitude */ #define LRLON 180.125 /* Lower-right corner longitude */ #define GRIDSPACING 0.25 /* Grid spacing in degrees */ #define GRIDW ((int)((LRLON-ULLON)/GRIDSPACING)) /* Grid width in pixels */ #define GRIDH ((int)((ULLAT-LRLAT)/GRIDSPACING)) /* Grid height in pixels */ #define MISSING 0.0 /* Missing data value (a LIE!) */ /* Macros */ #define SWAPS(x) (((x)>>8)|(((x)&0xFF)<<8)) /* Globals */ static int DEBUGGING; /* Debugging switch */ static float Lats[GRIDH]; /* Latitude values */ static float Lons[GRIDW]; /* Longitude values */ static float Rain[GRIDH][GRIDW]; /* Rain rate values */ /* Data from file name */ static char *Inppath; /* Input file's path, if any */ static char *Inpname; /* Input file's name */ static int Yyyy; /* Year from file name */ static int Mo; /* Month from file name */ static int Dd; /* Day from file name */ static int Hh; /* Hour from file name */ /* Local functions */ static int initialize(int argc, char *argv[]); static int getdata(); static int putdata(); /*---------------------------------------------------------------------------*/ /* Main program */ /*---------------------------------------------------------------------------*/ int main(int argc, char *argv[]) { /* Initialize for operations */ if (initialize(argc, argv)) return (1); /* Read the file */ if (getdata()) return (1); /* Write the output file */ if (putdata()) return (1); /* Looks good from here */ return (0); } /*---------------------------------------------------------------------------*/ /* Name: initialize - Initialize for operations */ /* */ /* Usage: int result = initialize(int argc, char *argv[]); */ /* argc - Count of command-line arguments */ /* argv - Array of command-line arguments */ /* result - Non-zero on errors */ /* */ /* Description: */ /* Initialize for operations by setting up globals, parsing command */ /* line arguments, etc. */ /* */ /* Notes: */ /* None. */ /* */ /* Revision history: */ /* 2013-04-26 BEB Written */ /*---------------------------------------------------------------------------*/ static int initialize(int argc, char *argv[]) { char *inpfile; /* Input file name */ char *p; /* Local pointer */ char cmd[1024]; /* Local command line */ int i; /* Local index */ /* Parse off the options, if any */ DEBUGGING = 0; inpfile = NULL; for (i = 1; i < argc; i++) { /* Check for the debug flag */ if (strcmp(argv[i], "-d") == 0) { DEBUGGING = 1; continue; } /* Anything else should be the file name */ if (inpfile == NULL) { inpfile = argv[i]; continue; } /* What's this? */ printf("**Unknown argument or duplicate file name '%s' found\n", argv[i]); return (1); } /* We have to have a file name */ if (inpfile == NULL) { printf("**Usage is %s [-d] filename\n", argv[0]); return (1); } /* Break up the file name to path and name */ if ((p = strrchr(inpfile, '/')) != NULL) { *p = '\0'; Inppath = inpfile; Inpname = p + 1; } else { Inppath = "."; Inpname = inpfile; } /* Now parse the input file name into globals */ if (sscanf(Inpname, "%4d%2d%2d%2d.1.geo", &Yyyy, &Mo, &Dd, &Hh) != 4) { printf("**Error, file name '%s' is malformed\n", Inpname); return (1); } /* Looks OK from here */ return (0); } /*---------------------------------------------------------------------------*/ /* Name: getdata - Fetch (or synthesize) file data */ /* */ /* Usage: int result = getdata(); */ /* result - Non-zero on errors */ /* */ /* Description: */ /* Read the input file into globals. Return 1 on errors. */ /* */ /* Notes: */ /* None. */ /* */ /* Revision history: */ /* 2013-04-26 BEB Written */ /*---------------------------------------------------------------------------*/ static int getdata() { char inpfile[1024]; /* Input file name with path */ int ifd; /* Input file handle */ int row; /* Current row index */ int col; /* Current column index */ int i; /* Local index */ short rain[GRIDH][GRIDW]; /* Local rain data */ if (DEBUGGING) printf(">>Reading data\n"); /* Initialize the latitude array (these are fixed values) */ for (row = 0; row < GRIDH; row++) Lats[row] = ULLAT - row * GRIDSPACING; /* Initialize the longitude array (these are fixed values) */ for (col = 0; col < GRIDW; col++) Lons[col] = ULLON + col * GRIDSPACING; /* Open the file, which is binary */ sprintf(inpfile, "%s/%s", Inppath, Inpname); if (DEBUGGING) printf(">>Opening input file %s\n", inpfile); if ((ifd = open(inpfile, O_RDONLY)) <= 0) { printf("**Unable to open '%s' for reading: %s\n", inpfile, strerror(errno)); return (1); } /* Read in the array of data values */ if (DEBUGGING) printf(">>Reading data array\n"); if (read(ifd, rain, sizeof(rain)) != sizeof(rain)) { printf("**Error reading data array from '%s'\n", inpfile); return (1); } /* Swap bytes, convert the values to floats and unscale them, fixing up "missing" values */ for (row = 0; row < GRIDH; row++) { for (col = 0; col < GRIDW; col++) { Rain[row][col] = (float) ((short) SWAPS(rain[row][col])) / 100.0; if (Rain[row][col] < 0.0) Rain[row][col] = 0.0; } } /* Looks good from here */ close(ifd); return (0); } /*---------------------------------------------------------------------------*/ /* Name: putdata - Create the output file */ /* */ /* Usage: int result = putdata(); */ /* result - Non-zero on errors */ /* */ /* Description: */ /* Create the output NetCDF file from the globals. */ /* */ /* Notes: */ /* None. */ /* */ /* Revision history: */ /* 2013-04-26 BEB Written */ /*---------------------------------------------------------------------------*/ static int putdata() { int outncid; /* Output file handle */ int status; /* Return from NC calls */ int timedim; /* Time dimension handle */ int londim; /* Longitude dimension handle */ int latdim; /* Latitude dimension handle */ int timeid; /* Time variable handle */ int latid; /* Latitude variable handle */ int lonid; /* Longitude variable handle */ int rainid; /* RainRate variable handle */ int timeval; /* Time vector value (always 0) */ int dims[3]; /* Dimension-id vector */ int i; /* Local index */ float fill; /* Fill value */ char outname[64]; /* Output file name (only) */ char host[256]; /* Host name (for provenance) */ char cwd[1024]; /* Current working directory */ char history[1024]; /* History attribute value */ char timeunits[40]; /* "seconds since yyyy-mm-dd hh:mm:ssZ" */ char *value; /* Temporary attribute value */ char *p; /* Local pointer */ char *env; /* Environment derived from cwd */ time_t now; /* Current date/time stamp */ struct tm *today; /* Current date/time */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Create the output file */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Compute the output file name from the input file name */ sprintf(outname, "%04d%02d%02d%02d00.1.geo.nc", Yyyy, Mo, Dd, Hh); if (DEBUGGING) printf(">>Creating output file '%s'\n", outname); /* Attempt to create the file */ if ((status = nc_create(outname, NC_NETCDF4, &outncid)) != NC_NOERR) { printf("**Error %d creating '%s': %s\n", status, outname, nc_strerror(status)); return (1); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Define the global attributes */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Define standard attributes */ if (DEBUGGING) printf(">>Defining global attribute 'title': %s\n", TITLE); value = TITLE; if ((status = nc_put_att(outncid, NC_GLOBAL, "title", NC_STRING, 1, &value)) != NC_NOERR) { printf("**Error %d creating global attribute 'title': %s\n", status, nc_strerror(status)); return (1); } /* Provenance is a little trickier */ time(&now); today = gmtime(&now); gethostname(host, sizeof(host)); if ((p = strchr(host, '.')) != NULL) *p = '\0'; getcwd(cwd, sizeof(cwd)); if (strstr(cwd, "dev") != NULL) env = "dev"; else if (strstr(cwd, "test") != NULL) env = "test"; else env = "ops"; sprintf(history, "Converted from ASCII file %s on host %s in the %s environment on %04d-%02d-%02dT%02d:%02d:%02dZ", Inpname, host, env, today->tm_year + 1900, today->tm_mon + 1, today->tm_mday, today->tm_hour, today->tm_min, today->tm_sec); if (DEBUGGING) printf(">>Defining global attribute 'history': %s\n", history); value = history; if ((status = nc_put_att(outncid, NC_GLOBAL, "history", NC_STRING, 1, &value)) != NC_NOERR) { printf("**Error %d creating global attribute 'history': %s\n", status, nc_strerror(status)); return (1); } /* Define the dimensions */ if (DEBUGGING) printf(">>Defining dimension 'latitude' as %d\n", GRIDH); if ((status = nc_def_dim(outncid, "latitude", GRIDH, &latdim)) != NC_NOERR) { printf("**Error %d creating dimension 'latitude': %s\n", status, nc_strerror(status)); return (1); } if (DEBUGGING) printf(">>Defining dimension 'longitude' as %d\n", GRIDW); if ((status = nc_def_dim(outncid, "longitude", GRIDW, &londim)) != NC_NOERR) { printf("**Error %d creating dimension 'longitude': %s\n", status, nc_strerror(status)); return (1); } if (DEBUGGING) printf(">>Defining dimension 'time' as 1\n"); if ((status = nc_def_dim(outncid, "time", 1, &timedim)) != NC_NOERR) { printf("**Error %d creating dimension 'time': %s\n", status, nc_strerror(status)); return (1); } /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Define the variables and their attributes */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Time vector (one value) ----------------------------------------------- */ if (DEBUGGING) printf(">>Defining variable 'time'\n"); if ((status = nc_def_var(outncid, "time", NC_INT, 1, (const int *) &timedim, &timeid)) != NC_NOERR) { printf("**Error %d creating variable 'time': %s\n", status, nc_strerror(status)); return (1); } /* Attributes */ if (DEBUGGING) printf(">>Defining attribute 'standard_name'\n"); value = "time"; if ((status = nc_put_att(outncid, timeid, "standard_name", NC_STRING, 1, &value)) != NC_NOERR) { printf("**Error %d creating attribute 'standard_name': %s\n", status, nc_strerror(status)); return (1); } if (DEBUGGING) printf(">>Defining attribute 'long_name'\n"); value = "time of measurement"; if ((status = nc_put_att(outncid, timeid, "long_name", NC_STRING, 1, &value)) != NC_NOERR) { printf("**Error %d creating attribute 'long_name': %s\n", status, nc_strerror(status)); return (1); } if (DEBUGGING) printf(">>Defining attribute 'units'\n"); sprintf(timeunits, "minutes since %04d-%02d-%02d %02d:00:00Z", Yyyy, Mo, Dd, Hh); if (DEBUGGING) printf(">>Defining attribute 'units' as '%s'\n", timeunits); value = timeunits; if ((status = nc_put_att(outncid, timeid, "units", NC_STRING, 1, &value)) != NC_NOERR) { printf("**Error %d creating attribute 'units': %s\n", status, nc_strerror(status)); return (1); } /* Latitude vector -------------------------------------------------------- */ if (DEBUGGING) printf(">>Defining variable 'latitude'\n"); if ((status = nc_def_var(outncid, "latitude", NC_FLOAT, 1, (const int *) &latdim, &latid)) != NC_NOERR) { printf("**Error %d creating variable 'latitude': %s\n", status, nc_strerror(status)); return (1); } if (DEBUGGING) printf(">>Defining attribute 'standard_name'\n"); value = "latitude"; if ((status = nc_put_att(outncid, latid, "standard_name", NC_STRING, 1, &value)) != NC_NOERR) { printf("**Error %d creating attribute 'standard_name': %s\n", status, nc_strerror(status)); return (1); } if (DEBUGGING) printf(">>Defining attribute 'long_name'\n"); value = "latitude of the observation"; if ((status = nc_put_att(outncid, latid, "long_name", NC_STRING, 1, &value)) != NC_NOERR) { printf("**Error %d creating attribute 'long_name': %s\n", status, nc_strerror(status)); return (1); } if (DEBUGGING) printf(">>Defining attribute 'units'\n"); value = "degrees_north"; if ((status = nc_put_att(outncid, latid, "units", NC_STRING, 1, &value)) != NC_NOERR) { printf("**Error %d creating attribute 'units': %s\n", status, nc_strerror(status)); return (1); } /* Longitude vector ------------------------------------------------------- */ if (DEBUGGING) printf(">>Defining variable 'longitude'\n"); if ((status = nc_def_var(outncid, "longitude", NC_FLOAT, 1, (const int *) &londim, &lonid)) != NC_NOERR) { printf("**Error %d creating variable 'longitude': %s\n", status, nc_strerror(status)); return (1); } if (DEBUGGING) printf(">>Defining attribute 'standard_name'\n"); value = "longitude"; if ((status = nc_put_att(outncid, lonid, "standard_name", NC_STRING, 1, &value)) != NC_NOERR) { printf("**Error %d creating attribute 'standard_name': %s\n", status, nc_strerror(status)); return (1); } if (DEBUGGING) printf(">>Defining attribute 'long_name'\n"); value = "longitude of the observation"; if ((status = nc_put_att(outncid, lonid, "long_name", NC_STRING, 1, &value)) != NC_NOERR) { printf("**Error %d creating attribute 'long_name': %s\n", status, nc_strerror(status)); return (1); } if (DEBUGGING) printf(">>Defining attribute 'units'\n"); value = "degrees_east"; if ((status = nc_put_att(outncid, lonid, "units", NC_STRING, 1, &value)) != NC_NOERR) { printf("**Error %d creating attribute 'units': %s\n", status, nc_strerror(status)); return (1); } /* Rain rate variable ----------------------------------------------------- */ if (DEBUGGING) printf(">>Defining variable 'RainRate'\n"); dims[0] = timedim; dims[1] = latdim; dims[2] = londim; if ((status = nc_def_var(outncid, "RainRate", NC_FLOAT, 3, (const int *) dims, &rainid)) != NC_NOERR) { printf("**Error %d creating variable 'RainRate': %s\n", status, nc_strerror(status)); return (1); } /* Set the compression */ if ((status = nc_def_var_deflate(outncid, rainid, 0, 1, 5)) != NC_NOERR) { printf("**Error %d setting deflate for 'RainRate': %s\n", status, nc_strerror(status)); return (1); } /* Define attributes */ if (DEBUGGING) printf(">>Defining attribute 'long_name'\n"); value = "rainfall rate in mm/hr"; if ((status = nc_put_att(outncid, rainid, "long_name", NC_STRING, 1, &value)) != NC_NOERR) { printf("**Error %d creating attribute 'long_name': %s\n", status, nc_strerror(status)); return (1); } if (DEBUGGING) printf(">>Defining attribute 'standard_name'\n"); value = "rainfall_rate"; if ((status = nc_put_att(outncid, rainid, "standard_name", NC_STRING, 1, &value)) != NC_NOERR) { printf("**Error %d creating attribute 'standard_name': %s\n", status, nc_strerror(status)); return (1); } if (DEBUGGING) printf(">>Defining attribute 'units'\n"); value = "mm/hr"; if ((status = nc_put_att(outncid, rainid, "units", NC_STRING, 1, &value)) != NC_NOERR) { printf("**Error %d creating attribute 'units': %s\n", status, nc_strerror(status)); return (1); } if (DEBUGGING) printf(">>Defining attribute '_FillValue'\n"); fill = MISSING; if ((status = nc_put_att(outncid, rainid, "_FillValue", NC_FLOAT, 1, &fill)) != NC_NOERR) { printf("**Error %d creating attribute '_FillValue': %s\n", status, nc_strerror(status)); return (1); } if (DEBUGGING) printf(">>Defining attribute 'comment'\n"); value = "All data values < 0 have been set to 0, and _FillValue has been set to 0 to enable transparency when visualized"; if ((status = nc_put_att(outncid, rainid, "comment", NC_STRING, 1, &value)) != NC_NOERR) { printf("**Error %d creating attribute 'comment': %s\n", status, nc_strerror(status)); return (1); } /* Done with definitions */ nc_enddef(outncid); /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* Now write the variables */ /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ /* The time "vector" is one value. This is a Michael McEniry cheat: */ /* The time values have to be in some single value, so a string like */ /* "2012-04-15T19:15:30Z" isn't acceptable. So, we define (in "units" */ /* above) the time BASE to be the actual time, and we put the offset in */ /* the time "vector" as zero and 30 minutes. */ timeval = 0; /* Write the time "vector" */ if (DEBUGGING) printf(">>Writing time variable\n"); if ((status = nc_put_var(outncid, timeid, &timeval)) != NC_NOERR) { printf("**Error %d writing time variable: %s\n", status, nc_strerror(status)); return (1); } /* Write the latitude vector */ if (DEBUGGING) printf(">>Writing latitude variable\n"); if ((status = nc_put_var(outncid, latid, Lats)) != NC_NOERR) { printf("**Error %d writing latitude variable: %s\n", status, nc_strerror(status)); return (1); } /* Write the longitude vector */ if (DEBUGGING) printf(">>Writing longitude variable\n"); if ((status = nc_put_var(outncid, lonid, Lons)) != NC_NOERR) { printf("**Error %d writing longitude variable: %s\n", status, nc_strerror(status)); return (1); } /* Write the Rain rate array */ if (DEBUGGING) printf(">>Writing RainRate variable\n"); if ((status = nc_put_var(outncid, rainid, Rain)) != NC_NOERR) { printf("**Error %d writing RainRate variable: %s\n", status, nc_strerror(status)); return (1); } /* All done */ (void) nc_close(outncid); return (0); }