/***************************************************************************** FILE: LOAD_ODF.C USAGE: LOAD_ODF This program loads CTD data from ODF ASCII files into a CODAS database. Unlike load_ctd where the header information are prepended to the data files, this program reads the header information from a single table file, one line per cast, and then reads the CTD data from separate data files, one per cast, each named after the station number. Below is a sample of the header information read in: 131 1 150893 ROS 3 0.0 N 178 59.9 E 0218 5481 132 1 150893 ROS 2 45.1 N 179 0.0 E 0719 5482 133 1 150893 ROS 2 30.0 N 179 0.0 E 1239 4840 The must contain the following information: DATABASE NAME - the name to use for the CODAS database to be created PRODUCER DEFINITION FILE NAME SHIP NAME - maximum of 11 characters or "none" to use default NODC code START_PRESSURE - smallest possible pressure value (usually 0 or 1) MAX_PRESSURE - largest pressure value to store in database PRESSURE_INCREMENT - the pressure interval in decibars LIST_FILE - table of header information, one line per cast DATA_FILE_ROOT - path/root specification for data files, one per cast All the data are loaded into one block file unless the OPTIMUM_PROFILES_PER_BLOCK figure has been exceeded, in which case a new block file is started. EF 99/04/15 quick change from 3 to 4 digits in the station number part of the ctd data file names; the right way to do it would be to switch from parameter to option input, and then make it an option. *****************************************************************************/ #include "geninc.h" #include "data_id.h" #include "ioserv.h" /* error_found() */ #include "use_db.h" /* db*_cnf() */ #define NMAX 3000 /* maximum array size -- adjust if needed */ int MAX_PRESSURE; /* largest pressure value to use in loading pressure var. */ int START_PRESSURE; /* smallest possible pressure value (usually 0 or 1) */ int PRESSURE_INCREMENT; /* set in control file to pressure interval used */ int N_PRESSURE; /* no. of pressure values to store in database */ SHORT pressure[NMAX]; FLOAT data[NMAX]; FILE_NAME_TYPE dbname, prdname, listname, ascroot, ascfn, ascname; char SHIP_NAME[12]; typedef struct { CHAR ship_name[12]; CHAR cruise_id[12]; CHAR unused[24]; } CONFIGURATION_1_TYPE; typedef struct { USHORT station; USHORT cast_num; USHORT end_time; /* station end-time (hour & minute only) */ SHORT inst; FLOAT max_pressure; FLOAT aver; FLOAT rate; FLOAT ocean_depth; CHAR unused[20]; } ANCILLARY_1_TYPE; int get_header(FILE *fpasc, CONFIGURATION_1_TYPE *conf, ANCILLARY_1_TYPE *anc, YMDHMS_TIME_TYPE *prftime, DMSH_POSITION_TYPE position[]); int get_temperature(FILE *fpasc, int *nvalues, FLOAT temp[]); int get_salinity(FILE *fpasc, int *nvalues, FLOAT sal[]); int convert_time(long ztime, long dmy, YMDHMS_TIME_TYPE *time); int dbaddf_cnf(int type, FLOAT data[], unsigned int n, char *msg); int main(int argc, char *argv[]) { FILE *fpcnt; int dbid = 1, memory_mode = 0, ierr, iprf, n, i, status; long filepos; FILE *fpasc, *fplist; CONFIGURATION_1_TYPE conf; ANCILLARY_1_TYPE anc; DMSH_POSITION_TYPE position[2]; YMDHMS_TIME_TYPE prftime; static PARAMETER_LIST_ENTRY_TYPE ctdcnt[] = { {" DATABASE_NAME:" , " %79s", dbname , TYPE_STRING}, {" DEFINITION_FILE:" , " %79s", prdname , TYPE_STRING}, {" SHIP_NAME:" , " %11s", SHIP_NAME , TYPE_STRING}, {" START_PRESSURE:" , " %d" , &START_PRESSURE , TYPE_INT}, {" MAX_PRESSURE:" , " %d" , &MAX_PRESSURE , TYPE_INT}, {" PRESSURE_INCREMENT:", " %d" , &PRESSURE_INCREMENT, TYPE_INT}, {" LIST_FILE:" , " %79s", listname , TYPE_STRING}, {" DATA_FILE_ROOT:" , " %79s", ascroot , TYPE_STRING}, {NULL , NULL , NULL , 0} }; fpcnt = get_fpcnt(argc, argv); if (get_parameters(fpcnt, ctdcnt, NULL)) return 1; dbname[79] = prdname[79] = '\0'; print_parameters(stdout, ctdcnt, NULL, "\n"); /* --> INITIALIZATION OF STRUCTURES FOR UNUSED FIELDS */ set_byte((UBYTE *) &conf, '\0', sizeof(CONFIGURATION_1_TYPE)); set_byte((UBYTE *) &anc, '\0', sizeof(ANCILLARY_1_TYPE)); /*--------------------------------------------------------------- NOTE: pressures are initialized by equation rather than read from the file because there have been instances of missing data toward the surface. ---------------------------------------------------------------*/ N_PRESSURE = (MAX_PRESSURE - START_PRESSURE + PRESSURE_INCREMENT) / PRESSURE_INCREMENT; if (error_found( (N_PRESSURE > NMAX), "array limit exceeded\n\n")) return 1; for (i = 0; i < N_PRESSURE; i++) pressure[i] = START_PRESSURE + i * PRESSURE_INCREMENT; /* --> CREATE DATABASE */ DBCREATE(&dbid, dbname, prdname, &memory_mode, &ierr); if (error_found(ierr, "DBCREATE\n\n")) return 1; iprf = 0; /*------------------------------------------------------ NOTE THAT THERE IS ONE ASCII FILE FOR EACH STATION ------------------------------------------------------*/ if ((fplist = fopen(listname, "r")) == NULL) { printf("\n ERROR: Cannot open list file %s\n\n", listname); goto close; } while ( (status = get_header(fplist, &conf, &anc, &prftime, position)) == 0 ) { strncpy(ascname, ascroot, 79); sprintf(ascfn, "%04d.%02d", anc.station, anc.cast_num); strncat(ascname, ascfn, 79); ascname[79] = '\0'; if ((fpasc = fopen(ascname, "r")) == NULL) { printf("\n ERROR: Cannot open ASCII file %s\n\n", ascname); goto close; } printf("\n %s ", ascname); if (iprf == 0) { /*---------------- CREATE BLOCK ----------------*/ DBNEWBLK(&ierr); if (error_found(ierr, "DBNEWBLK")) goto close; if (dbadd_cnf(CONFIGURATION_1, (char *) &conf, sizeof(CONFIGURATION_1_TYPE), "adding configuration")) goto close; if (dbadd_cnf(P, (char *) pressure, N_PRESSURE * sizeof(SHORT), "adding pressure")) goto close; } /*------------------ CREATE PROFILE ------------------*/ DBNEWPRF(&prftime, &ierr); if (error_found(ierr, "DBNEWPRF")) goto close; printf(" ...Profile %3d => ", iprf); print_ymdhms_time(stdout, &prftime); if (dbadd_cnf(POSITION, (char *) position, sizeof(position), "adding position")) goto close; /*---------------------------------------------------------------- NOTE: The data in the ASCII file are read in a column at a time, rather than a line at a time, to cover the case where the number of observations is so large as to cause a memory shortage. Hence, to conserve memory, only one data array is used to read all the variables. ----------------------------------------------------------------*/ filepos = 0L; if (get_temperature(fpasc, &n, data)) goto close; if (dbaddf_cnf(TEMPERATURE, data, n, "adding temperature")) goto close; if (error_found(fseek(fpasc, filepos, 0L), "seek error")) goto close; if (get_salinity(fpasc, &n, data)) goto close; if (dbaddf_cnf(SALINITY, data, n, "adding salinity")) goto close; if (dbadd_cnf(ANCILLARY_1, (char *) &anc, sizeof(ANCILLARY_1_TYPE), "adding ancillary")) goto close; DBENDPRF(&ierr); if (error_found(ierr, "DBENDPRF")) goto close; iprf++; if (iprf == OPTIMUM_PROFILES_PER_BLOCK) { DBENDBLK(&ierr); if (error_found(ierr, "DBENDBLK")) goto close; iprf = 0; } fclose(fpasc); } close: printf("\n\n"); DBENDBLK(&ierr); error_found(ierr, "DBENDBLK"); dbclose_cnf(); return 0; } /* Note that the header comes from the list file, not the "ascii file" which has only the data. */ int get_header(FILE *fpasc, CONFIGURATION_1_TYPE *conf, ANCILLARY_1_TYPE *anc, YMDHMS_TIME_TYPE *time, DMSH_POSITION_TYPE position[]) { long stime; long dmy; int status; int lat_degrees, lon_degrees; double lat_minutes, lon_minutes; char lat_NS, lon_EW; LONG hundredths; anc->end_time = BADUSHORT; anc->inst = BADSHORT; anc->max_pressure = BADFLOAT; anc->aver = BADFLOAT; anc->rate = BADFLOAT; printf("get_header "); status = fscanf(fpasc, "%hu %hu %ld ROS %d %lf %c %d %lf %c %ld %f", &(anc->station), &(anc->cast_num), &dmy, &lat_degrees, &lat_minutes, &lat_NS, &lon_degrees, &lon_minutes, &lon_EW, &stime, &(anc->ocean_depth)); if (status == 0) { printf("End of list file.\n"); return(1); /* Normal end. */ } printf(" status = %d, station = %d\n", status, anc->station); if (error_found(status != 11, "bad line in header table?")) return(1); if (error_found(convert_time(stime, dmy, time), "converting time")) return(1); hundredths = (lat_degrees * 360000) + (LONG) (lat_minutes * 6000.0); if (lat_NS == 'S') hundredths = - hundredths; HUNPOS(&position[1], &hundredths); if (error_found(invalid_position(&position[1], 1), "invalid latitude")) return(1); hundredths = (lon_degrees * 360000) + (LONG) (lon_minutes * 6000.0); if (lon_EW == 'W') hundredths = - hundredths; HUNPOS(&position[0], &hundredths); if (error_found(invalid_position(&position[0], 0), "invalid longitude")) return(1); if (anc->ocean_depth < 0) anc->ocean_depth = BADFLOAT; /* BAD coded as -99 */ return(0); } int get_temperature(FILE *fpasc, int *nvalues, FLOAT temp[]) { int i, j, jrem; FLOAT t, p; char line[80]; /*---------------------------------------------------------- INITIALIZE ARRAY TO BADFLOAT IN CASE OF MISSING DATA, PARTICULARLY FOR INITIAL PRESSURE INTERVALS ----------------------------------------------------------*/ for (i = 0; i < N_PRESSURE; i++) temp[i] = BADFLOAT; /*----------------------------------------------- SCAN AVAILABLE PROFILE DATA FROM ASCII FILE -----------------------------------------------*/ j = 0; while (fgets(line, 80, fpasc) != NULL && j < N_PRESSURE) { if (sscanf(line, " %f %f %*f", &p, &t) < 2) { printf("\n ERROR: Scanning temperature for n = %d", *nvalues+1); return(1); } j = ((int) p - START_PRESSURE) / PRESSURE_INCREMENT; /* may have missing data */ jrem = ((int) p - (int) START_PRESSURE) % (int) PRESSURE_INCREMENT; /* may have missing data */ if (jrem == 0 && j < N_PRESSURE && t >= -98) temp[j] = t; /* BADs coded as negative values */ } /* end while not eof loop */ if (j == N_PRESSURE) *nvalues = j; else *nvalues = j + 1; return(0); } int get_salinity(FILE *fpasc, int *nvalues, FLOAT sal[]) { int i, j, jrem; FLOAT s, p; char line[80]; /*---------------------------------------------------------- INITIALIZE ARRAYS TO BADFLOAT IN CASE OF MISSING DATA, PARTICULARLY FOR INITIAL PRESSURE INTERVALS ----------------------------------------------------------*/ for (i = 0; i < N_PRESSURE; i++) sal[i] = BADFLOAT; /*----------------------------------------------- SCAN AVAILABLE PROFILE DATA FROM ASCII FILE -----------------------------------------------*/ j = 0; while (fgets(line, 80, fpasc) != NULL && j < N_PRESSURE) { if (sscanf(line, " %f %*f %f", &p, &s) < 2) { printf("\n ERROR: Scanning salinity data for n = %d", *nvalues+1); return(1); } j = ((int) p - START_PRESSURE) / PRESSURE_INCREMENT; /* may have missing data at initial pressures */ jrem = ((int) p - (int) START_PRESSURE) % (int) PRESSURE_INCREMENT; /* may have missing data */ if (jrem == 0 && j < N_PRESSURE && s >= 0) sal[j] = s; } if (j == N_PRESSURE) *nvalues = j; else *nvalues = j + 1; return(0); } int convert_time(long ztime, long dmy, YMDHMS_TIME_TYPE *time) { int dm; time->year = yr4digit( (int)(dmy % 100) ); dm = dmy / 100; time->month = dm % 100; time->day = dm / 100; time->hour = ztime / 100; time->minute = ztime % 100; time->second = 0; print_ymdhms_time(stdout, time); if (error_found(invalid_time(time), "invalid time ")) { print_ymdhms_time(stdout, time); return(1); } return(0); } int dbaddf_cnf(int type, FLOAT data[], unsigned int n, char *msg) { int ierr; unsigned int nbad = 0; char badmsg[40]; DBADD_F(&type, data, &n, &nbad, &ierr); if (error_found(ierr, msg)) return(ierr); strcpy(badmsg, "bad values "); strcat(badmsg, msg); error_found(nbad, badmsg); return(nbad); }