/***************************************************************************** FILE: load_ctd.c USAGE: load_ctd This program loads CTD data from WOCE/NODC/WHOI format ASCII files into a CODAS database. The must contain the following information: DATABASE NAME - the name to use for the CODAS database to be created PRODUCER DEFINITION FILE NAME FORMAT - NODC or WHOI or WOCE 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 ASCII FILE NAME(S) - the name of the ASCII files containing the CTD data . (they must use NODC or WHOI format) . . 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. *****************************************************************************/ #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_DATA; /* no. of pressure, temp, oxygen, salinity values to be added */ int N_PRESSURE; /* no. of pressure values to store in database */ SHORT pressure[NMAX]; FLOAT data[NMAX]; FILE_NAME_TYPE dbname, prdname, ascname; char FORMAT[5], 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[], char *profile_comment); int get_temperature(FILE *fpasc, int *nvalues, FLOAT temp[], FLOAT *anc_max_pressure); int get_salinity(FILE *fpasc, int *nvalues, FLOAT sal[]); int get_oxygen(FILE *fpasc, int *nvalues, FLOAT oxy[]); int convert_time(USHORT ztime, 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; long filepos; FILE *fpasc; CONFIGURATION_1_TYPE conf; ANCILLARY_1_TYPE anc; DMSH_POSITION_TYPE position[2]; YMDHMS_TIME_TYPE prftime; char profile_comment[40]; static PARAMETER_LIST_ENTRY_TYPE ctdcnt[] = { {" DATABASE_NAME:" , " %79s", dbname , TYPE_STRING}, {" DEFINITION_FILE:" , " %79s", prdname , TYPE_STRING}, {" FORMAT:" , " %4s" , FORMAT , 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}, {" ASCII_FILE(S):" , NULL , NULL , 0}, {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"); if ( stricmp(FORMAT, "NODC") && stricmp(FORMAT, "WHOI") && stricmp(FORMAT, "WOCE") ) { printf("\n ERROR: Invalid value for FORMAT flag %s\n\n", FORMAT); return 1; } /* --> 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 CAST ---------------------------------------------------*/ while (fscanf(fpcnt, " %79s", ascname) == 1) { 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 (get_header(fpasc, &conf, &anc, &prftime, position, profile_comment) == 0) { 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 = ftell(fpasc); if (get_temperature(fpasc, &n, data, &(anc.max_pressure))) 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 (error_found(fseek(fpasc, filepos, 0L), "seek error")) goto close; if (get_oxygen(fpasc, &n, data)) goto close; if (dbaddf_cnf(OXYGEN, data, n, "adding oxygen")) goto close; */ if (dbadd_cnf(ANCILLARY_1, (char *) &anc, sizeof(ANCILLARY_1_TYPE), "adding ancillary")) goto close; if (stricmp(FORMAT, "WOCE") == 0) if (dbadd_cnf(PROFILE_COMMENTS, (char *) profile_comment, sizeof(profile_comment), "adding profile comments")) 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); } else { printf("\n ERROR: Scanning header information\n\n"); goto close; } } /* end while not end-of-control-file loop */ close: printf("\n\n"); DBENDBLK(&ierr); error_found(ierr, "DBENDBLK"); dbclose_cnf(); return 0; } int get_header(FILE *fpasc, CONFIGURATION_1_TYPE *conf, ANCILLARY_1_TYPE *anc, YMDHMS_TIME_TYPE *time, DMSH_POSITION_TYPE position[], char profile_comment[]) { char header_line[85], lon_dir, lat_dir, ccast[6], clatdeg[3], clondeg[4], cdepth[6]; USHORT stime; int status, lon_degrees, lat_degrees; float lat_minutes, lon_minutes; LONG hundredths; set_byte((UBYTE *) header_line, '\0', sizeof(header_line)); set_byte((UBYTE *) profile_comment, '\0', 40); if (stricmp(FORMAT, "WOCE") == 0) { if (fgets(header_line, 85, fpasc) == NULL) return(EOF); status = sscanf(header_line, "%5c%5hu%5c%2hd%2hd%2hd %3c%2c%f%c%3c%f%c%2hd%2hd%5c%30c", conf->cruise_id, &(anc->station), ccast, &(time->day), &(time->month), &(time->year), anc->unused, clatdeg, &lat_minutes, &lat_dir, clondeg, &lon_minutes, &lon_dir, &(time->hour), &(time->minute), cdepth, profile_comment); if (error_found( (status != 17), "scanning header line")) return(1); ccast[5] = clatdeg[2] = clondeg[3] = cdepth[5] = '\0'; anc->cast_num = (USHORT) atoi(ccast); anc->ocean_depth = (FLOAT) atof(cdepth); time->second = 0; time->year = yr4digit(time->year); anc->end_time = BADUSHORT; anc->inst = BADSHORT; anc->aver = anc->rate = BADFLOAT; hundredths = (atoi(clondeg) * 360000L) + (long) (lon_minutes * 6000L); if (lon_dir == 'W') hundredths *= -1.0; HUNPOS(&position[0], &hundredths); if (error_found(invalid_position(&position[0], 0), "invalid longitude")) return(1); hundredths = (atoi(clatdeg) * 360000L) + (long) (lat_minutes * 6000L); if (lat_dir == 'S') hundredths *= -1.0; HUNPOS(&position[1], &hundredths); if (error_found(invalid_position(&position[1], 0), "invalid latitude")) return(1); if (stricmp(SHIP_NAME, "none")) strncpy(conf->ship_name, SHIP_NAME, 11); return(0); } status = fscanf(fpasc, " SHIP %11s CRUIS %11s STAT: %hu C#: %hu DATE %2hu-%2hu-%2hu", conf->ship_name, conf->cruise_id, &(anc->station), &(anc->cast_num), &(time->year), &(time->month), &(time->day)); switch(status) { case 7: break; case EOF: return(EOF); default: printf("\n ERROR: scanning ship & cruise name, station & cast no., date"); return(1); } if (stricmp(SHIP_NAME, "none")) strncpy(conf->ship_name, SHIP_NAME, 11); if (stricmp(FORMAT, "NODC") == 0) { if (error_found( (fscanf(fpasc, " STIME: %huZ ETIME: %huZ LAT %d%f LNG %d%f", &stime, &(anc->end_time), &lat_degrees, &lat_minutes, &lon_degrees, &lon_minutes) != 6), "scanning ASCII file for time & position") ) return(1); } else { if (error_found((fscanf(fpasc, " TIME: %hu Z LAT %d%f LG %d%f", &stime, &lat_degrees, &lat_minutes, &lon_degrees, &lon_minutes) != 5), "scanning ASCII file for time & position")) return(1); /* WHOI format ASCII files do not have end time */ anc->end_time = BADUSHORT; } time->year = yr4digit(time->year); if (error_found(convert_time(stime, time), "converting time")) return(1); if ((lat_degrees < 0) || (lat_minutes < 0.0)) { hundredths = - (abs_val(lat_degrees * 360000L) + (long) (abs_val(lat_minutes * 6000L))); } else hundredths = (lat_degrees * 360000L) + (long) (lat_minutes * 6000L); HUNPOS(&position[1], &hundredths); if (error_found(invalid_position(&position[1], 1), "invalid latitude")) return(1); hundredths = (lon_degrees * 360000L) + (long) (lon_minutes * 6000L); HUNPOS(&position[0], &hundredths); if (error_found(invalid_position(&position[0], 0), "invalid longitude")) return(1); if (error_found((fscanf(fpasc, " MAX. PRS= %f DB DEPTH= %f M AVER %f INST %hd RATE %fHZ", &(anc->max_pressure), &(anc->ocean_depth), &(anc->aver), &(anc->inst), &(anc->rate)) != 5), "scanning max. pressure, depth, aver, inst, & rate")) return(1); if (anc->ocean_depth < 0) anc->ocean_depth = BADFLOAT; /* BAD coded as -99 */ if (stricmp(FORMAT, "NODC") == 0) { if (error_found( (fscanf(fpasc, " DATA %d", &N_DATA) != 1), "scanning no. of data points" )) return(1); } if (error_found((fgets(header_line, 80, fpasc) == NULL), "scanning ASCII file header")) return(1); /* read till end of line */ if (stricmp(FORMAT, "WHOI") == 0) if (error_found((fgets(header_line, 80, fpasc) == NULL), "scanning ASCII file header")) return(1); /* read OBS/FMT line */ if (error_found((fgets(header_line, 80, fpasc) == NULL), "scanning column titles line")) return(1); return(0); } int get_temperature(FILE *fpasc, int *nvalues, FLOAT temp[], FLOAT *anc_max_pressure) { int i, j = -1; FLOAT t, p = 0.0; 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; if (stricmp(FORMAT, "WOCE") == 0) { while (fgets(line, 80, fpasc) != NULL) { if (sscanf(line, " %f %f %*f %*f", &p, &t) < 2) { printf("\n ERROR: Scanning temperature for n = %d", *nvalues+1); return(1); } if (p <= MAX_PRESSURE) /* store only up to MAX_PRESSURE */ { if (t >= 0) { j = ((int) p - START_PRESSURE) / PRESSURE_INCREMENT; /* may be missing data at initial pressures */ temp[j] = t; /* BADs coded as negative values */ } } } *nvalues = j + 1; *anc_max_pressure = p; /* from last data line in file */ return(0); } if (stricmp(FORMAT, "WHOI") == 0) { /*----------------------------------------------- SCAN AVAILABLE PROFILE DATA FROM ASCII FILE -----------------------------------------------*/ while (fgets(line, 80, fpasc) != NULL) { if (sscanf(line, " %f %f %*f %*f", &p, &t) < 2) { printf("\n ERROR: Scanning temperature for n = %d", *nvalues+1); return(1); } if (p > MAX_PRESSURE) break; if (t >= 0) /* BADs coded as negative values */ { j = ((int) p - START_PRESSURE) / PRESSURE_INCREMENT; /* may be missing data at initial pressures */ temp[j] = t; } } /* end while not eof loop */ *nvalues = j + 1; } else /* NODC */ { for (i = 0, p = START_PRESSURE; i < N_DATA && p < MAX_PRESSURE; i++) { if (fscanf(fpasc, " %f %f %*f %*f", &p, &t) < 2) { printf("\n ERROR: Scanning temperature for n = %d", i+1); return(1); } j = ((int) p - START_PRESSURE) / PRESSURE_INCREMENT; /* may be missing data at initial pressures */ if (t >= 0) /* BADs coded as negative values */ temp[j] = t; } *nvalues = j + 1; } return(0); } int get_salinity(FILE *fpasc, int *nvalues, FLOAT sal[]) { int i, j = -1; FLOAT s, p = 0.0; char line[80], scan_format[20]; /*---------------------------------------------------------- INITIALIZE ARRAYS TO BADFLOAT IN CASE OF MISSING DATA, PARTICULARLY FOR INITIAL PRESSURE INTERVALS ----------------------------------------------------------*/ for (i = 0; i < N_PRESSURE; i++) sal[i] = BADFLOAT; if (stricmp(FORMAT, "NODC") == 0) { for (i = 0, p = START_PRESSURE; i < N_DATA && p < MAX_PRESSURE; i++) { if (fscanf(fpasc, " %f %*f %f %*f", &p, &s) < 2) { printf("\n ERROR: Scanning salinity data for n = %d", i+1); return(1); } j = ((int) p - START_PRESSURE) / PRESSURE_INCREMENT; /* may have missing data at initial pressures */ if (s >= 0) sal[j] = s; } *nvalues = j + 1; return(0); } if (stricmp(FORMAT, "WHOI") == 0) strcpy(scan_format, " %f %*f %f %*f"); else strcpy(scan_format, " %f %*f %*f %f"); while (fgets(line, 80, fpasc) != NULL) { if (sscanf(line, scan_format, &p, &s) < 2) { printf("\n ERROR: Scanning salinity for n = %d", *nvalues+1); return(1); } if (p > MAX_PRESSURE) break; if (s >= 0) { j = ((int) p - START_PRESSURE) / PRESSURE_INCREMENT; /* may be missing data at initial pressures */ sal[j] = s; /* BADs coded as negative values */ } } *nvalues = j + 1; return(0); } int get_oxygen(FILE *fpasc, int *nvalues, FLOAT oxy[]) { int i, j = -1; FLOAT o, p = 0.0; char line[80], scan_format[30]; /*---------------------------------------------------------- INITIALIZE ARRAYS TO BADFLOAT IN CASE OF MISSING DATA, PARTICULARLY FOR INITIAL PRESSURE INTERVALS ----------------------------------------------------------*/ for (i = 0; i < N_PRESSURE; i++) oxy[i] = BADFLOAT; if (stricmp(FORMAT, "NODC") == 0) { /*----------------------------------------------- SCAN AVAILABLE PROFILE DATA FROM ASCII FILE -----------------------------------------------*/ for (i = 0, p = START_PRESSURE; i < N_DATA && p < MAX_PRESSURE; i++) { if (fscanf(fpasc, " %f %*f %*f %f", &p, &o) < 2) { printf("\n ERROR: Scanning oxygen data for n = %d", i+1); return(1); } j = ((int) p - START_PRESSURE) / PRESSURE_INCREMENT; /* may have missing data at initial pressures */ if (o >= 0) oxy[j] = o; } *nvalues = j + 1; return(0); } if (stricmp(FORMAT, "WHOI") == 0) strcpy(scan_format, " %f %*f %*f %f"); else strcpy(scan_format, " %f %*f %*f %*f %*f %f"); while (fgets(line, 80, fpasc) != NULL) { if (sscanf(line, scan_format, &p, &o) < 2) { printf("\n ERROR: Scanning oxygen for n = %d", *nvalues+1); return(1); } if (p > MAX_PRESSURE) break; if (o >= 0) { j = ((int) p - START_PRESSURE) / PRESSURE_INCREMENT; /* may be missing data at initial pressures */ oxy[j] = o; /* BADs coded as negative values */ } } *nvalues = j + 1; return(0); } int convert_time(USHORT ztime, YMDHMS_TIME_TYPE *time) { time->hour = ztime / 100; time->minute = ztime % 100; time->second = 0; if (error_found(invalid_time(time), "invalid time ")) { print_ymdhms_time(stdout, time); return(1); } return(0); } #if PROTOTYPE_ALLOWED int dbaddf_cnf(int type, FLOAT data[], unsigned int n, char *msg) #else int dbaddf_cnf(type, data, n, msg) int type; FLOAT data[]; unsigned int n; char *msg; #endif { 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); }