/****************************************************************************/ /* */ /* IAGA2002.h */ /* */ /****************************************************************************/ /****************************************************************************/ /* This is a C language header file that defines the following routines */ /* for reading IAGA2002 format data files into memory and writing data from */ /* memory into IAGA2002 format data files. The description of IAGA2002 */ /* format is given at http://www.ngdc.noaa.gov/IAGA/vdat/iagaformat.html */ /* */ /* ---------------- Read data from an IAGA2002-format file ------------- */ /* */ /* long ReadIAGA2002(char *FileName,NetworkPtr Network,char *StartTimeStr, */ /* char *EndTimeStr,char *StationID) */ /* FileName Name of the IAGA2002-file containing the data. */ /* If FileName is nil or zero length then standard */ /* input is used. */ /* Network Structure into which the data is read. */ /* Network_struct is defined in file MagnData.h. */ /* StartTimeStr String defining the first time to be read from */ /* the data file (String delimited with '\0'). */ /* String format : YYYYMMDDHH. If HH is missing then */ /* 00 is assumed. If StartTimeStr is nil or zero */ /* length then start of file is assumed. */ /* EndTimeStr String defining the time not to be read anymore. */ /* Format as before YYYYMMDDHH. If nil or zero length */ /* then data is read until end of file. */ /* StationID Three letter station ID of the station. IAGA2002 */ /* format allows only one station per file (!). */ /* If nil or zero length then all data in the file */ /* is read. If the given StationID does not match the */ /* identifier in the data file then no data is read. */ /* */ /* The function result indicates how successfull the file reading was: */ /* 0: OK : File read successfully */ /* 1: FileError : Failed to read the given file */ /* 2: OutOfMemory : Memory allocation failed during reading */ /* 3: FormatError : File to be read is not IAGA file */ /* */ /* */ /****************************************************************************/ /****************************************************************************/ /* Lasse Hakkinen */ /* Finnish Meteorological Institute */ /* Geophysical Research Division */ /* P.O.Box 503 */ /* FIN-00101, Helsinki, Finland */ /* e-mail: Lasse.Hakkinen@fmi.fi */ /* phone : (+358)-9-19294634 */ /* fax : (+358)-9-19294603 */ /* */ /* version 1.01 03.01.2020 */ /****************************************************************************/ /****************************************************************************/ /* Version history: */ /* */ /* 1.01 03.01.2020 Date strings must be YYYYMMDD (four digits for year) */ /* Replaced StrToSecs -> StrToSecsC and */ /* SecsToStr -> SecsToStrC. */ /* 1.0 27.05.2005 First official release */ /****************************************************************************/ #include #include #include /*--------------------------------------------------------------------------*/ /* Define the offsets of various fields from the start of the IAGA2002 line */ /* See the definition of IAGA2002 format for the explanation of various */ /* fields. */ /*--------------------------------------------------------------------------*/ #define IAGA2002_LineLength 70 #define IAGA2002_EndChar '|' #define IAGA2002_Description 24 #define IAGA2002_DateYear 0 #define IAGA2002_DateMonth 5 #define IAGA2002_DateDay 8 #define IAGA2002_DateHour 11 #define IAGA2002_DateMinute 14 #define IAGA2002_DateSecond 17 #define IAGA2002_DateMilliSec 20 #define IAGA2002_Data 30 #define IAGA2002MissingValue 99999 #define IAGA2002NotObserved 88888 /*--------------------------------------------------------------------------*/ /* Function prototypes */ /*--------------------------------------------------------------------------*/ static long GetPositiveInteger(char *p); Time_sec GetIAGA2002Time(char *Buffer,long TimeStep); Time_sec GetIAGA2002TimeStep(char *Line); int ReadIAGA2002Line(FILE *IAGAfile,char *Buff); static long GetFloatNumber(char *p); static void SetStationParamsIAGA2002(StationPtr Station,Time_sec T,char* ID, long Latitude, long Longitude, char *Comps); static void GetIAGA2002Data(StationPtr Station, Time_sec T, char *Buff, char *Components); long ReadIAGA2002(char *FileName,NetworkPtr Network,char *StartTimeStr, char *EndTimeStr,char *StationList); long Greenland_relatime_flag = 0; /*==========================================================================*/ /* Routines for reading IAGA-format files */ /*==========================================================================*/ /*--------------------------------------------------------------------------*/ /* Get positive integer value from the Buffer. We could also use sscanf */ /* but this is considerably faster because no checkings are made. */ /*--------------------------------------------------------------------------*/ static long GetPositiveInteger(char *p) { long value; while (*p == ' ') p++; /* scan leading spaces */ value = (*p++ -'0'); /* Go through the digits */ while ((*p >= '0') && (*p <= '9')) value = 10*value+(*p++ - '0'); return (value); } /*--------------------------------------------------------------------------*/ /* Get the time in seconds of a single data line. */ /* In IAGA2002 file the date is in format YYYY-MM-DD HH:MM:SS.XXX */ /*--------------------------------------------------------------------------*/ Time_sec GetIAGA2002Time(char *Line, long TimeStep) { Time_struct MyTime; MyTime.tm_year = GetPositiveInteger(Line+IAGA2002_DateYear)-1900; MyTime.tm_mon = GetPositiveInteger(Line+IAGA2002_DateMonth)-1; MyTime.tm_mday = GetPositiveInteger(Line+IAGA2002_DateDay); MyTime.tm_hour = GetPositiveInteger(Line+IAGA2002_DateHour); MyTime.tm_min = GetPositiveInteger(Line+IAGA2002_DateMinute); MyTime.tm_sec = GetPositiveInteger(Line+IAGA2002_DateSecond); if ((TimeStep == 60) && (MyTime.tm_sec == 0)) { // 1-minute data and secs = 0 return (TmToSecs(&MyTime)); // Assume that given time is the starting point of recording interval ??? } else return (TmToSecs(&MyTime) - TimeStep/2); // Assume that given time is the middle point of recording interval ??? May change !!! } /*--------------------------------------------------------------------------*/ /* Get the time in seconds of a single data line. */ /* In IAGA2002 file the date is in format YYYY-MM-DD HH:MM:SS.XXX */ /*--------------------------------------------------------------------------*/ Time_sec GetIAGA2002TimeStep(char *Line) { if (strstr(Line+IAGA2002_Description,"1-second") != NULL) return 1; if (strstr(Line+IAGA2002_Description,"1-Second") != NULL) return 1; if (strstr(Line+IAGA2002_Description,"1 sec") != NULL) return 1; if (strstr(Line+IAGA2002_Description,"10 second") != NULL) return 10; if (strstr(Line+IAGA2002_Description,"10-second") != NULL) return 10; if (strstr(Line+IAGA2002_Description,"20-second") != NULL) return 20; if (strstr(Line+IAGA2002_Description,"1-minute") != NULL) return 60; if (strstr(Line+IAGA2002_Description,"1 min") != NULL) return 60; if (strstr(Line+IAGA2002_Description,"1-Minute") != NULL) return 60; if (strstr(Line+IAGA2002_Description,"1-hour") != NULL) return 3600; if (strstr(Line+IAGA2002_Description,"1 hour") != NULL) return 3600; if (strstr(Line+IAGA2002_Description,"1-Hour") != NULL) return 3600; return 0; } /*--------------------------------------------------------------------------*/ /* Read one line from the IAGA2002 file into buffer. */ /* The routine will return 1 if the block was succesfully read, otherwise */ /* 0 is returned. */ /*--------------------------------------------------------------------------*/ int ReadIAGA2002Line(FILE *IAGAfile,char *Buff) { register char *ChrPtr = Buff; register int c; while (((c = getc(IAGAfile)) != EOF) && (c != '\n')) *ChrPtr++ = c; *ChrPtr = '\0'; /* End of string marker */ if (c == EOF) return (0); /* End of file */ return (1); } /*--------------------------------------------------------------------------*/ /* Get a floating point number from the Buffer. The number may contain */ /* either 1 or 2 decimals. The value is multiplied by 100. This convention */ /* implies the use of 0.01 nT accuracy in field values. */ /*--------------------------------------------------------------------------*/ static long GetFloatNumber(char *p) { long value; long negative; while (*p == ' ') p++; /* scan leading spaces */ negative = (*p == '-'); /* check the sign */ if (negative) p++; value = (*p++ - '0'); /* get the integer part */ while (*p != '.') value = 10*value+(*p++ - '0'); if ((value == IAGA2002MissingValue) || (value == IAGA2002NotObserved)) return MissingValue; p++; /* get the first decimal */ value = 10*value+(*p++ -'0'); if ((*p >= '0') && (*p <= '9')) {/* get the second decimal */ value = 10*value+(*p++ -'0'); if ((*p >= '5') && (*p <= '9')) /* if there is third */ value++; /* decimal then round */ } else value *= 10; if (negative) return(-value); /* return the result */ return (value); } /*--------------------------------------------------------------------------*/ /* Fill some fields in the Station_struct by copying them from the IAGA */ /* buffer. AddNewStation procedure in the MagnData.h file will set other */ /* station parameters. */ /*--------------------------------------------------------------------------*/ static void SetStationParamsIAGA2002(StationPtr Station,Time_sec T,char* ID, long Latitude, long Longitude, char *Comps) { /* Set the station ID */ strncpy(Station->StationID,ID,3); Station->StationID[3] = '\0'; /* Set the location of the station */ Station->Latitude = Latitude; Station->Longitude = Longitude; /* Set the StartTime, GetIAGAData-routine will set EndTime */ Station->StartTime = T; /* Set the component markers */ if (strncmp(Comps,"DHZ",3) == 0) strcpy(Station->Components,"HDZ"); if (strncmp(Comps,"HDZ",3) == 0) strcpy(Station->Components,"HDZ"); if (strncmp(Comps,"XYZ",3) == 0) strcpy(Station->Components,"XYZ"); } /*--------------------------------------------------------------------------*/ /* Copy the data from the IAGA2002 line into OneDayBlock. */ /*--------------------------------------------------------------------------*/ static void GetIAGA2002Data(StationPtr Station, Time_sec T, char *Buff, char *Components) { OneDay_struct *p; long *XPtr,*YPtr,*ZPtr,*TPtr; long i; char *q; /* Find the last OneDayBlock */ for (p = Station->FirstDay; p->NextDay != NULL; p = p->NextDay); /* Set the temperature value */ TPtr = (p->T)+(p->TCount); *TPtr = MissingValue; p->TCount++; /* Set the pointers within the OneDayBlock */ XPtr = (p->X)+(p->XCount); YPtr = (p->Y)+(p->YCount); ZPtr = (p->Z)+(p->ZCount); /* Copy the data */ q = Buff+IAGA2002_Data; for (i = 0; i < 3; i++) { while (*q != ' ') q++; while (*q == ' ') q++; switch (Components[i]) { case 'X': case 'H': *XPtr++ = GetFloatNumber(q); break; case 'Y': case 'D': *YPtr++ = GetFloatNumber(q); break; case 'Z': *ZPtr++ = GetFloatNumber(q); break; } } p->XCount++; p->YCount++; p->ZCount++; /* Set the last time */ Station->EndTime = T+Station->TimeStep; } /*--------------------------------------------------------------------------*/ /* Here is the routine for reading IAGA2000-format data file into memory. */ /* The data is read into a Network structure (see MagnData.h for details). */ /* See the comments at the beginning of this file for more details about */ /* the parameters for this routine. */ /*--------------------------------------------------------------------------*/ long ReadIAGA2002(char *FileName,NetworkPtr Network,char *StartTimeStr, char *EndTimeStr,char *StationList) { FILE *IAGAfile; /* The data file to be processed */ char Line[100]; /* Current data line */ Time_sec Time; /* Time of the current data block */ Time_sec StartTime; /* Time of first record to be read */ Time_sec EndTime; /* Time of record not read anymore */ Time_sec TimeStep = 0; /* Sampling rate in seconds */ StationPtr Station = NULL; /* Pointer to the current station */ char ID[10]; /* Station ID */ long Latitude, Longitude; /* Station coordinates */ char Components[10]; /* Station data components */ InitNetwork(Network); /* Initialize the fields of Network */ /* --- Set the start and end times --- */ if ((StartTimeStr == NULL) || (*StartTimeStr == '\0')) StartTime = MIN_TIME; else StartTime = StrToSecsC(StartTimeStr,0); if ((EndTimeStr == NULL) || (*EndTimeStr == '\0')) EndTime = MAX_TIME; else EndTime = StrToSecsC(EndTimeStr,0); /* --- Try to open the file --- */ if ((FileName == NULL) || (*FileName == '\0')) IAGAfile = stdin; else if ((IAGAfile = fopen(FileName, "r")) == NULL) return FileError; /* --- Read the mandatory header ( >= 12 lines) --- */ /* Check that this is a IAGA-2002 format file */ /* The first line should be the format line */ ReadIAGA2002Line(IAGAfile,Line); if (strncmp(Line," Format IAGA-2002",33)) { return(FileFormatError); } /* Read the other header lines containing info about station */ ReadIAGA2002Line(IAGAfile,Line); while (*Line == ' ') { if (strncmp(Line+1,"IAGA Code ",23) == 0) strncpy(ID,Line+IAGA2002_Description,3); if (strncmp(Line+1,"IAGA CODE ",23) == 0) strncpy(ID,Line+IAGA2002_Description,3); if (strncmp(Line+1,"Geodetic Latitude ",23) == 0) Latitude = GetFloatNumber(Line+IAGA2002_Description); if (strncmp(Line+1,"Geodetic Longitude ",23) == 0) Longitude = GetFloatNumber(Line+IAGA2002_Description); if (strncmp(Line+1,"Reported ",23) == 0) strncpy(Components,Line+IAGA2002_Description,3); if (strncmp(Line+1,"Data Interval Type ",23) == 0) TimeStep = GetIAGA2002TimeStep(Line); if (strncmp(Line+1,"Data Interval Unknown",30) == 0) TimeStep = 60; ReadIAGA2002Line(IAGAfile,Line); } if (TimeStep == 0) { fprintf(stderr,"Unsupported time step %d\n",TimeStep); return(FileFormatError); } if (!StationInList(ID,StationList)) { return(StationNotFound); } /* Now the last read line is the mandatory data header record */ /* containing text 'DATE TIME DOY .... ' . */ /* The next line is the first data line. */ /* --- Read the data into one day blocks --- */ while (ReadIAGA2002Line(IAGAfile,Line)) { if ((Time = GetIAGA2002Time(Line, TimeStep)) >= EndTime) break; if (Time >= StartTime) { if (Station == NULL) { /* First occurrence of this station */ Station = AddNewStation(Network,TimeStep,TimeStep); if (Station != NULL) SetStationParamsIAGA2002(Station,Time,ID,Latitude,Longitude,Components); else /* Unable to allocate memory for station data */ return(OutOfMemory); } if (OneDayFull(Station,'Z')) { if (AddOneDay(Station)) return(OutOfMemory); /* Failed to allocate memory */ } GetIAGA2002Data(Station,Time,Line,Components); } } fclose(IAGAfile); /* --- Combine one day blocks into one large block ---*/ Station = Network->StationList; while (Station != NULL) { if (CombineData(Station) == OutOfMemory) return(OutOfMemory); Station = Station->Next; } return OK; } /*==========================================================================*/ /* Routines for writing IAGA2002-format files */ /*==========================================================================*/ // --------------------------------------------------------------------- // Write the IAGA 2002 header. This header is read from a file which is // located in the directory 'IAGA2002' under the image bin directory. // Function returns -1 if the title line starting with 'DATE' is included // in the header file. // --------------------------------------------------------------------- long WriteIAGA2002header(FILE *Datafile, char *Header_file, char *StationID, long TimeStep) { FILE *header; /* The header file to be processed */ char Line[100]; /* Current data line */ char DummyStr[100] = ""; int i; int TitleLine = 0; /* Flag indicating whether the 'DATE' line is included in the header file */ char HeaderFile[150] = "/proj/image/bin/IAGA2002/"; if ((Header_file == NULL) || (*Header_file == '\0')) { strcat(HeaderFile, StationID); sprintf(DummyStr,"%d", TimeStep); strcat(HeaderFile, DummyStr); // fprintf(stderr, "%s %s\n", HeaderFile, DummyStr); } else strcpy(HeaderFile, Header_file); if ((header = fopen(HeaderFile, "r")) == NULL) return FileError; while (ReadIAGA2002Line(header,Line)) { if (strncmp(Line+1,"Data Interval Type ",23) == 0) { // Update the sampling period if (TimeStep < 60) sprintf(DummyStr,"%d-Second ", TimeStep); else if (TimeStep < 3600) sprintf(DummyStr,"%d-Minute ", TimeStep/60); else sprintf(DummyStr,"%d-Hour ", TimeStep/3600); // Update the sampling period in the "Data Interval Type" line for (i=0;iComponents, "HDZ",3) == 0) { X = GetDataValue(Station,Time,'H'); Y = GetDataValue(Station,Time,'D'); Z = GetDataValue(Station,Time,'Z'); } else { X = GetDataValue(Station,Time,'X'); Y = GetDataValue(Station,Time,'Y'); Z = GetDataValue(Station,Time,'Z'); } if (X == MissingValue) X = 100*IAGA2002MissingValue; if (Y == MissingValue) Y = 100*IAGA2002MissingValue; if (Z == MissingValue) Z = 100*IAGA2002MissingValue; fprintf(Datafile,"%9.2f %9.2f %9.2f %9.2f\n", X/100.0, Y/100.0, Z/100.0, (float)IAGA2002NotObserved); } /*--------------------------------------------------------------------------*/ /* Here is the routine for writing data into an IAGA2002-format file. See */ /* the comments at the beginning of this file for more details about the */ /* parameters for this routine. */ /*--------------------------------------------------------------------------*/ long WriteIAGA2002(char *FileName, NetworkPtr Network, char *StartTimeStr, char *EndTimeStr, StationPtr Station, char *Header_file) { FILE *IAGAfile; /* The data file to be processed */ Time_sec Time; /* Time of the current data block */ Time_sec StartTime; /* Time of first record to be written */ Time_sec EndTime; /* Time of record not written anymore */ long status; /* --- Check there is some data in the Network --- */ if (Network->StationList == NULL) return FAIL; /* --- Set the start and end times --- */ if ((StartTimeStr == NULL) || (*StartTimeStr == '\0')) StartTime = Station->StartTime; else StartTime = StrToSecsC(StartTimeStr,0); if ((EndTimeStr == NULL) || (*EndTimeStr == '\0')) EndTime = Station->EndTime; else EndTime = StrToSecsC(EndTimeStr,0); /* --- Try to create the file --- */ if ((FileName == NULL) || (*FileName == '\0')) IAGAfile = stdout; else if ((IAGAfile = fopen(FileName, "w")) == NULL) return FileError; /* --- Write the IAGA2002 header --- */ status = WriteIAGA2002header(IAGAfile, Header_file, Station->StationID, Station->TimeStep); if (status > 0) return status; if (status == 0) { // DATE line is not included in the header file /* Write the title line */ fprintf(IAGAfile, "DATE TIME DOY "); fprintf(IAGAfile, "%s%c ", Station->StationID, Station->Components[0]); fprintf(IAGAfile, "%s%c ", Station->StationID, Station->Components[1]); fprintf(IAGAfile, "%s%c ", Station->StationID, Station->Components[2]); fprintf(IAGAfile, "%sF |\n", Station->StationID); } /* --- Write the data ---*/ Time = StartTime; while (Time < EndTime) { WriteIAGA2002line(IAGAfile, Station, Time); Time += Station->TimeStep; } fclose(IAGAfile); return OK; }