/****************************************************************************/ /* */ /* WDC.h */ /* */ /****************************************************************************/ /****************************************************************************/ /* This is a C language header file that defines the following routines */ /* for reading and writing WDC-format data files. */ /* */ /* ------------------- Read data from a WDC-format file -------------- */ /* */ /* long ReadWDC(char *FileName,NetworkPtr Network,char *StartTimeStr, */ /* char *EndTimeStr,char *StationList) */ /* FileName Name of the WDC-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 file (String delimited with '\0'). */ /* String format : YYMMDDHH. 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 YYMMDDHH. If nil or zero length */ /* then data is read until end of file. */ /* StationList List of stations included in the data. The stations */ /* must be specified with three letter codes (e.g. */ /* SOR) and must be separeted with a space in the list */ /* (e.g. 'SOR MAS KEV'). If nil or zero length then */ /* all stations in the file are be included. */ /* */ /* ------------------- Write data into a WDC-format file -------------- */ /* */ /* long WriteWDC(char *FileName,NetworkPtr Network,char *StartTimeStr, */ /* char *EndTimeStr,char *StationList) */ /* FileName Name of the WDC file. IF NULL or zero length then */ /* stdout is used. */ /* Network A pointer to the Network structure whose data is to */ /* be written into a file. */ /* StartTime Start date string YYMMDDHHMM. If NULL then same as */ /* the time of the first record of the first station */ /* in the Network data structure. */ /* EndTime End date string YYMMDDHHMM. This record is NOT */ /* included anymore. If NULL then data till the end of */ /* available data in Network structure will be written */ /* StationList String containing the 3-letter ID's of the stations */ /* to be included. If NULL or 0-length then all the */ /* stations will be written. The stations must be */ /* separated by a space in the list (e.g. 'SOR MUO'). */ /****************************************************************************/ /****************************************************************************/ /* Lasse Hakkinen */ /* Finnish Meteorological Institute */ /* Department of Geophysics */ /* P.O.Box 503 */ /* FIN-00101, Helsinki, Finland */ /* e-mail: Lasse.Hakkinen@fmi.fi */ /* phone : (+358)-9-19294634 */ /* fax : (+358)-9-19294603 */ /* */ /* version 1.03 12.2.1998 */ /****************************************************************************/ /****************************************************************************/ /* Version history: */ /* 1.03 12.02.1998 */ /* - If WDC file is in DHZ-format then the unit of D is now 0.1 arcmin */ /* Previously the D values were read in with 0.01 arcmin accuracy. */ /* Also when writing WDC files with DHZ representation it is now */ /* assumed that in memory D is in units of 0.1 arcmin. */ /* 1.02 27.07.1996 */ /* - Fixed a bug where WriteWDC crashed is there is no data in the */ /* Network-structure */ /* 1.01 5.1.1996 */ /* - If WDC-file contained data for four components (e.g. F,X,Y,Z) */ /* program crashed. This was fixed by skipping all blocks which */ /* contain F component. So only three components are read. */ /* */ /* 1.0 13.11.1995 First release */ /****************************************************************************/ #include #include #include #define WDCstep 60 /* One-minute values. No other possibilities ! */ #define MissingWDC 99999 /* Marker for missing data value (usually !) */ /*--------------------------------------------------------------------------*/ /* Define the offsets of various fields from the start of the WDC block. */ /* See the definition of IAGA record for the explanation of various fields. */ /*--------------------------------------------------------------------------*/ #define WDC_PolarDist 0 #define WDC_Longitude 6 #define WDC_Date 12 #define WDC_Component 18 #define WDC_Hour 19 #define WDC_Station 21 #define WDC_Data 34 #define CR 0x0D #define NL 0x0A /*--------------------------------------------------------------------------*/ /* Function prototypes */ /*--------------------------------------------------------------------------*/ static Time_sec GetWDCTime(char *Buffer); static int ReadWDCBlock(FILE *WDCfile,char *Buff); static void SetStationParamsWDC(StationPtr Station,char *Buff); static long GetWDCFieldValue(char *p); static void GetWDCData(StationPtr Station, char *Buff); long ReadWDC(char *FileName,NetworkPtr Network,char *StartTimeStr, char *EndTimeStr,char *StationList); static void WriteWDCBlock(FILE *DataFile,StationPtr Station,Time_sec Time, char Component); long WriteWDC(char *FileName,NetworkPtr Network,char *StartTimeStr, char *EndTimeStr,char *StationList); /*==========================================================================*/ /* Routines for reading WDC-format files */ /*==========================================================================*/ /*--------------------------------------------------------------------------*/ /* Get the start time in seconds of a 400-character data block. In the */ /* WDC file the date is in format YYMMDDXHH. Where X is the component */ /* character (is there any logic here ?). */ /*--------------------------------------------------------------------------*/ static Time_sec GetWDCTime(char *Buffer) { char DateStr[20] = "20"; // Assume century = 20 long YY; YY = TwoCharNumber(Buffer+WDC_Date); // WDC data line does not contain century if (YY > 70) strncpy(DateStr, "19",2); // Century = 19 strncpy(DateStr+2,Buffer+WDC_Date,6); strncpy(DateStr+8,Buffer+WDC_Hour,2); DateStr[10] = 0; /* String terminator */ return (StrToSecsC(DateStr,8)); } /*--------------------------------------------------------------------------*/ /* Read one block from the WDC-format file into the Buffer. This routine */ /* works also for 401 or 402 character blocks (i.e. terminated with */ /* linefeed and/or carriage returns). */ /* The routine will return 1 if the block was succesfully read, otherwise */ /* 0 is returned. */ /*--------------------------------------------------------------------------*/ static int ReadWDCBlock(FILE *WDCfile,char *Buffer) { int c; /* Handle the first character */ if ((c = getc(WDCfile)) == EOF) return(0); if (c < 32) { /* Linefeed or carriage return */ if ((c = getc(WDCfile)) == EOF) return(0); if (c < 32) { /* Linefeed or carriage return */ if ((c = getc(WDCfile)) == EOF) return(0); } } *Buffer = c; /* Put the first char into Buffer */ /* read the rest of the block */ return(fread(Buffer+1,399,1,WDCfile)); } /*--------------------------------------------------------------------------*/ /* Get the 6 character number from the Buffer. We could also use sscanf, */ /* but this is considerably faster because no checkings are made. */ /*--------------------------------------------------------------------------*/ static long GetWDCFieldValue(char *p) { int k; int m = 0; long value = 0; long negative = 0; /* scan blankos */ while (*p == ' ') { p++; m++; } for (k=m;k<6;k++) { if (*p == '-') negative = 1; else { if ((*p >= '0') && (*p <= '9')) value = 10*value+(*p-'0'); else { return(MissingValue); /* Some illegal characters */ } } p++; } if (negative) value = -value; if ((value == MissingWDC) || (value == 999999)) return(MissingValue); return (value); } /*--------------------------------------------------------------------------*/ /* Fill some fields in the Station_struct by copying them from the WDC */ /* buffer. AddNewStation procedure in the MagnData.h file will set other */ /* station parameters. */ /*--------------------------------------------------------------------------*/ static void SetStationParamsWDC(StationPtr Station,char *Buff) { long dummy; /* Set the station ID */ strncpy(Station->StationID,Buff+WDC_Station,3); Station->StationID[3] = '\0'; /* Set the location of the station */ Station->Longitude = GetWDCFieldValue(Buff+WDC_Longitude)/10; Station->Latitude = 9000 - GetWDCFieldValue(Buff+WDC_PolarDist)/10; /* Set the StartTime and TimeStep, GetWDCData-routine will set EndTime */ Station->StartTime = GetWDCTime(Buff); Station->TimeStep = WDCstep; } /*--------------------------------------------------------------------------*/ /* Copy data from the 400 character block into the proper OneDayBlock. */ /*--------------------------------------------------------------------------*/ static void GetWDCData(StationPtr Station, char *Buff) { OneDay_struct *p; long *DataPtr,*TPtr; long i,Count,value; char *q; char Component; /*** Find the OneDayBlock which has room for new data ***/ Component = *(Buff+WDC_Component); if (Component == 'E') Component = 'X'; /* Put AE index into X */ if (Component == 'L') Component = 'Y'; /* Put AL index into Y */ if (Component == 'U') Component = 'Z'; /* Put AU index into Z */ p = Station->FirstDay; do { switch (Component) { case 'H' : case 'X' : Count = p->XCount; break; case 'D' : case 'Y' : Count = p->YCount; break; case 'Z' : Count = p->ZCount; break; } if (Count < 86400L/(Station->TimeStep)) break; /* still room */ p=p->NextDay; /* day full was full, check the next one */ } while (1); /* Set the temperature value */ if ((Component == 'H') || (Component == 'X')) { TPtr = (p->T)+(p->TCount); *TPtr = MissingValue; /* There is no temperature value in WDC */ p->TCount++; } /* Set the pointers and update counters */ switch (Component) { case 'H' : case 'X' : DataPtr = (p->X)+(p->XCount); p->XCount += 60; break; case 'D' : case 'Y' : DataPtr = (p->Y)+(p->YCount); p->YCount += 60; break; case 'Z' : DataPtr = (p->Z)+(p->ZCount); p->ZCount += 60; break; } /* Copy the data */ q = Buff+WDC_Data; for (i=0;i<60;i++) { value = GetWDCFieldValue(q); if (Component == 'D') /* Unit = 0.1 arcmin */ *DataPtr++ = value; else { if (value == MissingValue) *DataPtr++ = MissingValue; else *DataPtr++ = 10*value; /* Convert into 0.1 nT */ } q += 6; } /* Set the last time */ Station->EndTime = GetWDCTime(Buff)+60*Station->TimeStep; /* Set the component characters in the Station->Components */ switch (Component) { case 'H' : Station->Components[0] = 'H'; break; case 'X' : Station->Components[0] = 'X'; break; case 'D' : Station->Components[1] = 'D'; break; case 'Y' : Station->Components[1] = 'Y'; break; case 'Z' : Station->Components[2] = 'Z'; break; } } /*--------------------------------------------------------------------------*/ /* Here is the routine for reading WDC-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. */ /* */ /* The rounite works by reading the data file one block (400 bytes) at a */ /* time and adds that data in the particular stations data block. First */ /* for each new station that is encountered space for one day is allocated.*/ /* If the day becomes full a new one day block is allocated. At the end of */ /* the routine all one day blocks are combined into a single data block. */ /*--------------------------------------------------------------------------*/ long ReadWDC(char *FileName,NetworkPtr Network,char *StartTimeStr, char *EndTimeStr,char *StationList) { FILE *WDCfile; /* The data file to be processed */ char Buffer[400]; /* Buffer for one WDC data block */ Time_sec CurrTime; /* 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 */ StationPtr Station; /* Pointer to the current station */ 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-(Time_sec)86400; else EndTime = StrToSecsC(EndTimeStr,0); /* --- Try to open the file --- */ if ((FileName == NULL) || (*FileName == '\0')) WDCfile = stdin; else if ((WDCfile = fopen(FileName, "r")) == NULL) return FileError; /* --- Read the data into one day blocks ---*/ while (ReadWDCBlock(WDCfile,Buffer) && /* 24 hours for each component */ ((CurrTime=GetWDCTime(Buffer)) < EndTime+(Time_sec)86400)) { /* Change AE,AL,AU and AO index names so that they are not read as individual stations */ if ((Buffer[WDC_Station] == 'A') && (Buffer[WDC_Station+2] == ' ')) { Buffer[WDC_Station+1] = 'E'; } if ((CurrTime >= StartTime) && (CurrTime < EndTime) && StationInList(Buffer+WDC_Station,StationList) && (Buffer[WDC_Component] != 'F') && (Buffer[WDC_Component] != 'G') && (Buffer[WDC_Component] != 'O')) /* skip F and G components and AO index */ { Station = FindStation(Network,Buffer+WDC_Station); if (Station == NULL) { /* First occurrence of this station */ Station = AddNewStation(Network,WDCstep,60*WDCstep); if (Station != NULL) SetStationParamsWDC(Station,Buffer); else /* Unable to allocate memory for station data */ return(OutOfMemory); } if (OneDayFull(Station,Buffer[WDC_Component])) { if (AddOneDay(Station)) return(OutOfMemory); /* Unable to allocate memory */ } GetWDCData(Station,Buffer); } } fclose(WDCfile); /* --- 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 WDC-format files */ /*==========================================================================*/ /*--------------------------------------------------------------------------*/ /* Write one 400 character block for given station and for given time */ /* into specified datafile. */ /*--------------------------------------------------------------------------*/ static void WriteWDCBlock(FILE *DataFile,StationPtr Station,Time_sec Time, char Component) { long j,Ave; char DummyStr[20]; Time_sec T = Time; /*** Write station info and time fields ***/ fprintf(DataFile,"%6d%6d", 10*(9000-Station->Latitude),10*Station->Longitude); SecsToStrC(Time,DummyStr); // YYYYMMDD fprintf(DataFile,"%.6s",DummyStr+2); // Don't write century fprintf(DataFile,"%c",Component); fprintf(DataFile,"%.2s",DummyStr+8); fprintf(DataFile,"%s",Station->StationID); fprintf(DataFile,"20D"); /* Mysterious origin character */ fprintf(DataFile," "); /* 7 spaces */ /*** Write field values ***/ for (j=0;j<60;j++) { Ave = ComputeAverage(Station,Component,T,60); if (Ave == MissingValue) Ave = MissingWDC; if ((Component == 'D') || (Ave == MissingWDC)) /* Unit = 0.1 arcmin */ fprintf(DataFile,"% 6d",Ave); else fprintf(DataFile,"% 6d",RoundFloat(Ave/10.0)); /* Unit = 1 nT */ T += 60; } /*** Write hour average ***/ Ave = ComputeAverage(Station,Component,T-3600,3600); if (Ave != MissingValue) fprintf(DataFile,"% 6d",RoundFloat(Ave/10.0)); else fprintf(DataFile,"% 6d",MissingWDC); /*** The block end characters ***/ fprintf(DataFile,"%c%c",CR,NL); } /*--------------------------------------------------------------------------*/ /* Here is the routine for writing data into an WDC-format file. See the */ /* comments at the beginning of this file for more details about the */ /* parameters for this routine. */ /*--------------------------------------------------------------------------*/ long WriteWDC(char *FileName,NetworkPtr Network,char *StartTimeStr, char *EndTimeStr,char *StationList) { FILE *WDCfile; /* The data file to be processed */ Time_sec Time; /* Time of the current data block */ Time_sec Time2; /* Dummy time variable */ Time_sec StartTime; /* Time of first record to be read */ Time_sec EndTime; /* Time of record not read anymore */ long HourCount; /* Length of data block to be written */ long Hour,Comp; /* Dummy indices */ StationPtr Station; /* Pointer to the current station */ Time_struct TimeStruct; /* --- 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 = Network->StationList[0].StartTime; else StartTime = StrToSecsC(StartTimeStr,0); if ((EndTimeStr == NULL) || (*EndTimeStr == '\0')) EndTime = Network->StationList[0].EndTime; else EndTime = StrToSecsC(EndTimeStr,0); /* --- Try to create the file --- */ if ((FileName == NULL) || (*FileName == '\0')) WDCfile = stdout; else if ((WDCfile = fopen(FileName, "w")) == NULL) return FileError; /* ------------------- Write the data ------------------*/ /* Data is written in full day (00UT-24UT) blocks. If */ /* start hour is not midnight (00UT) then data for all */ /* components and for all stations till 24UT on the */ /* first day will be written. After that full days. */ /* -----------------------------------------------------*/ Time = StartTime; while (Time < EndTime) { if ((EndTime-Time)<86400) HourCount = (EndTime-Time)/3600; /* Last day ? */ else { SecsToTm(Time,&TimeStruct); HourCount = 24-TimeStruct.tm_hour; } Station = Network->StationList; while (Station != NULL) { if (StationInList(Station->StationID,StationList)) { for (Comp=0;Comp<3;Comp++) { Time2 = Time; for (Hour=0;HourComponents[Comp]); Time2 += 3600; } } } Station = Station->Next; } Time += HourCount*3600; } fclose(WDCfile); return OK; }