/****************************************************************************/ /* */ /* IAGA_to_TEXT.c */ /* */ /****************************************************************************/ /****************************************************************************/ /* This is a filter program that reads a IAGA format data file and writes */ /* out the same data in text columns. */ /* */ /* Usage: */ /* IAGA_to_TEXT [-s] [-e | -h] [-o] [-c] [-f] [-a] [-v | -g] [-y] [-z] [-p]*/ /* [<] IAGAFile > TEXTFile */ /* [-s YYYYMMDDHH] Time of the first record included. If missing */ /* then start of file is assumed. */ /* [-e YYYYMMDDHH] Time of the record not included anymore. If */ /* missing then end of file is assumed. */ /* [-h HH] Number of hours included. Either -e or -h can */ /* be specified, not both. */ /* [-o 'Station list'] List of stations delimited by aposthropes */ /* If missing then all stations included. */ /* Stations are identified by three-letter code */ /* and separated by exactly one space. */ /* [-c 'CompList'] Components to be written. Possible values are */ /* tXYZT (=time,X,Y,Z,Temperature). Default value */ /* is tXYZ (so temperature is not written.). Time */ /* is written in the format YYYY MM DD HH MM SS. */ /* See -f option for other time formats. */ /* [-f TimeFormat] If time is printed then set the time format. */ /* Possible values: */ /* ISO8601 : 2015-10-11T12:45:00Z */ /* [-a AverPeriod] Don't write all data values but averages over */ /* AverPeriod. */ /* [-n AverageMode] Defines how averages are computed. S = start, */ /* M = middle, E = end. Default = S */ /* [-v] Write headers (= Station name + Component) */ /* If missing then no headers are written */ /* [-g] Write the geographic coordinates of stations */ /* into the first line. This can be used only if */ /* -c option is not given (=tXYZ) and then the */ /* format of the line is: */ /* 0 0 0 0 0 0 lat_1 lon_1 0 ... lat_N lon_N 0. */ /* [-y] Write the year string with two digits. Default */ /* value is 4 digits (century included). */ /* [-z] For FMI internal use only. This option writes */ /* the time in Finnish local time. The times */ /* defined in -s and -e options must be, however, */ /* in UT. */ /* [-p] Prevents usage-text from appearing. Useful when */ /* reading data from standard input. */ /* IAGAFile Name of IAGA-format file. */ /* TEXTFile Name of TEXT 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.12 13.01.2020 */ /****************************************************************************/ /****************************************************************************/ /* Version history: */ /* */ /* 1.12 13.01.2020 Date strings must be YYYYMMDD (four digits for year) */ /* Replaced StrToSecs -> StrToSecsC and */ /* SecsToStr -> SecsToStrC. */ /* 1.11 11.08.2017 Fixed a segmentation fault if there were no stations in */ /* the NETWORK structure after ReadIAGA function. */ /* 1.10 10.11.2015 Added -f option. */ /* 1.09 09.11.2015 Added -n option. */ /* 1.08 15.03.2006 Added -z option. */ /* 1.07 12.05.2005 Fixed a bug in IAGA.h where the data was incorrectly */ /* read if there were missing data blocks in the IAGA file.*/ /* 1.06 14.11.2002 Added -g option. */ /* 1.05 19.01.2000 Added -y option and changed the default year string */ /* format to YYYY. Also added the checking of magnetic */ /* components. */ /* 1.04 09.09.1999 Fixed a Y2K bug in NewTime.h file which resulted in */ /* incorrect year in date strings if year >= 2000. */ /* 1.03 19.11.1998 Added the invisible -m option. */ /* 1.02 22.10.1998 Internal modification due to change of the MissingValue */ /* data marker. */ /* 1.01 16.10.1998 Fixed a bug where some data was not read if StartTime */ /* given in the command line was not exactly same as the */ /* start time of an IAGA block. */ /* 1.0 15.01.1997 First official release */ /****************************************************************************/ #include #include #include #include "Usage.h" #include "MagnData.h" #include "IAGA.h" char *version = "1.12"; char *date = "13.01.2020"; #define HeaderLineCount 4 #define UsageLineCount 35 char *HeaderText[HeaderLineCount] = { "**************************************************************************", "This program reads an IAGA-format magnetometer data file and writes out ", "the data for given components and stations in columnar format (TEXT). ", "**************************************************************************", }; char *UsageText[UsageLineCount] = { " [-s] [-e | -h] [-o] [-c] [-a] [-v | -g] [-y] [<] IAGAFile > TEXTFile", " [-s YYYYMMDDHH] Time of the first record included. If missing ", " then start of file is assumed. ", " [-e YYYYMMDDHH] Time of the record not included anymore. If ", " missing then end of file is assumed. ", " [-h HH] Number of hours included. Either -e or -h can ", " be specified, not both. ", " [-o 'Station list'] List of stations delimited by aposthropes ", " If missing then all stations included. ", " Stations are identified by three-letter code ", " and separated by exactly one space. ", " [-c 'CompList'] List of components to be printed. Possible ", " values for the components are t,X,Y,Z,T. Here ", " t is time and T is temperature. The format of ", " time is YYYY MM DD HH MM SS. Default value for ", " CompList is tXYZ (temperature is not written). ", " [-f TimeFormat] If time is printed then set the time format. ", " Possible values: ", " ISO8601 : e.g. 2015-10-11T12:45:00Z ", " [-a AverPeriod] Write data values as averages over AverPeriod. ", " [-n AverageMode] Defines how averages are computed. S = start, ", " M = middle, E = end. Default = S. ", " [-v] Write headers (= Station names + components). ", " If missing then no headers are written. ", " [-g] Write the geographic coordinates of the stations", " into the first line. This can be used only if ", " the -c option is not defined (= -tXYZ, default).", " The format of the first line is then: ", " 0 0 0 0 0 0 lat_1 lon_1 0 ... lat_N lon_N 0 ", " [-y] Write year string with two digits. Default value", " is four (century included). ", " [-p] Prevents usage text from appearing. Useful ", " when reading from standard input. ", " IAGAFile Name of IAGA-format file. ", " TEXTFile Name of ASCII file. ", }; /*--------------------------------------------------------------------------*/ /* Write headers (= Station name + component). */ /*--------------------------------------------------------------------------*/ void WriteHeaders(NetworkPtr NETWORK, char *CompStr, long Year2option) { long i,CompCount; StationPtr s; CompCount = strlen(CompStr); /* Write time */ if (CompStr[0] == 't') { if (Year2option == 1) printf("YY MM DD HH MM SS "); else printf("YYYY MM DD HH MM SS "); CompCount--; } /* Write station names and components */ s = NETWORK->StationList; while (s != NULL) { for (i = strlen(CompStr)-CompCount; i < strlen(CompStr); i++) printf(" %s %c ",s->StationID,CompStr[i]); s = s->Next; } printf("\n"); } /*--------------------------------------------------------------------------*/ /* Write horizontal line. */ /*--------------------------------------------------------------------------*/ void WriteHorLine(NetworkPtr NETWORK, char *CompStr, long Year2option) { long i,CompCount; CompCount = strlen(CompStr); /* Write a dashed line */ if (CompStr[0] == 't') { if (Year2option == 1) printf("--------------------"); else printf("----------------------"); CompCount--; } for (i = 0; i < CompCount*(NETWORK->StationCount);i++) printf("--------"); printf("\n"); } /*--------------------------------------------------------------------------*/ /* Write geographic locations of the stations in format: */ /* 0 0 0 0 0 0 lat_1 lon_1 0 ... lat_N lon_N 0 */ /*--------------------------------------------------------------------------*/ void WriteLocations(NetworkPtr NETWORK, char *CompStr, long Year2option) { long i,CompCount; StationPtr s; CompCount = strlen(CompStr); /* Write time */ if (CompStr[0] == 't') { if (Year2option == 1) printf(" 0 0 0 0 0 0 "); else printf(" 0 0 0 0 0 0 "); CompCount--; } /* Write station locations */ s = NETWORK->StationList; while (s != NULL) { printf(" %5.2f %5.2f 0 ",(s->Latitude)/100.0,(s->Longitude)/100.0); s = s->Next; } printf("\n"); } /*--------------------------------------------------------------------------*/ /* Write headers in a special format used by Ari. This is defined by the */ /* invisible option -m. */ /*--------------------------------------------------------------------------*/ void WriteAriHeaders(NetworkPtr NETWORK, char *CompStr) { long i,CompCount; StationPtr s; char DateStr[20]; long First = 1; CompCount = strlen(CompStr); if (CompStr[0] == 't') { CompCount--; } /* Write first comment line including station names and components */ printf("%% "); s = NETWORK->StationList; while (s != NULL) { for (i = strlen(CompStr)-CompCount; i < strlen(CompStr); i++) printf(" %s %c ",s->StationID,CompStr[i]); s = s->Next; } printf("\n"); /* Write time */ s = NETWORK->StationList; // SecsToStr(s->StartTime,DateStr); // if (s->StartTime < YEAR2000) // printf("19%"); // else // printf("20"); // // printf("%.2s %.2s %.2s",DateStr,DateStr+2,DateStr+4); SecsToStrC(s->StartTime,DateStr); printf("%.4s %.2s %.2s",DateStr,DateStr+4,DateStr+6); while (s != NULL) { for (i = strlen(CompStr)-CompCount; i < strlen(CompStr); i++) { if (First) { printf(" 0"); First = 0; } else printf(" 0"); } s = s->Next; } printf("\n"); } /*--------------------------------------------------------------------------*/ /* Find if summer time is in effect in Finland at the specified time. */ /* Returns 1 if yes and 0 if not. */ /* Summer time in Finland starts on last sunday in March and ends on last */ /* sunday on October. */ /*--------------------------------------------------------------------------*/ long SummerTime(Time_sec T) { Time_struct SummerStart = {0,0,1,31, 2, 0}; /* {secs,mins,...,years} */ Time_struct SummerEnd = {0,0,1,31, 9, 0}; /* {secs,mins,...,years} */ Time_struct CurrentTime; Time_struct RefSunday = {0,0,0, 4, 0,81}; Time_sec SummerStartSec,SummerEndSec,RefSundaySec; SecsToTm(T,&CurrentTime); SummerStart.tm_year = CurrentTime.tm_year; SummerEnd.tm_year = CurrentTime.tm_year; SummerStartSec = TmToSecs(&SummerStart); SummerEndSec = TmToSecs(&SummerEnd); RefSundaySec = TmToSecs(&RefSunday); SummerStartSec -= 86400*(((SummerStartSec - RefSundaySec)/86400) % 7); SummerEndSec -= 86400*(((SummerEndSec - RefSundaySec)/86400) % 7); if ((T > SummerStartSec) && (T < SummerEndSec)) return 1; else return 0; } /*--------------------------------------------------------------------------*/ /* The main procedure */ /*--------------------------------------------------------------------------*/ int main(int argc, char *argv[]) { long params; long status = 0; long FileCount = 0; long HourCount = 0; long AvePeriod = 0; long i,Ave; char FileName[100] = ""; char StartTimeStr[20] = ""; char EndTimeStr[20] = ""; char StationStr[200] = ""; char ComponentStr[20] = ""; char TimeFormat[20] = ""; StationPtr s; Time_sec t,EndTime,CurrTime; long FirstComp; long HeaderFlag = 0; long AriOption = 0; /* A special formatting option used by Ari */ long LocationFlag = 0; /* Flag for writing geographic locations */ long Year2option = 0; /* Print year with two digits, default = 4 */ char TimeStr[20]; long Finnish = 0; /* Use Finnish local time. -z option */ char c; long found; long time_offset = 0; /* Offset used when computing different averages */ char Time_ave_mode[3] = "S"; /* S = start, M = middle, E = end */ Network_struct NETWORK; /*==========================*/ /* Analyse the command line */ /*==========================*/ if (argc == 1) { /* No arguments, show the usage text */ PrintUsage(argv[0],0,HeaderLineCount,UsageLineCount); return OK; } for (params = 1; params < argc; params++) { if (*argv[params] != '-') { strcpy(FileName,argv[params]); FileCount++; } else { switch (*(argv[params]+1)) { case 's' : strcpy(StartTimeStr,argv[++params]); break; case 'e' : strcpy(EndTimeStr,argv[++params]); break; case 'h' : HourCount = atol(argv[++params]); break; case 'o' : strcpy(StationStr,argv[++params]); break; case 'c' : strcpy(ComponentStr,argv[++params]); break; case 'f' : strcpy(TimeFormat,argv[++params]); break; case 'a' : AvePeriod = atol(argv[++params]); break; case 'n' : strcpy(Time_ave_mode,argv[++params]); break; case 'v' : HeaderFlag = 1; break; case 'm' : AriOption = 1; break; case 'g' : LocationFlag = 1; break; case 'y' : Year2option = 1; break; case 'z' : Finnish = 1; break; case 'p' : /* Do nothing */ break; default : fprintf(stderr,"\n### %s: \"%s\" is not an option.\n\n", argv[0], argv[params]); PrintUsage(argv[0],1,HeaderLineCount,UsageLineCount); return FAIL; break; } } } /*====================================*/ /* Check the values of the parameters */ /*====================================*/ if (FileCount > 1) { PrintUsage(argv[0],1,HeaderLineCount,UsageLineCount); return FAIL; } if ((HourCount != 0) && (strlen(EndTimeStr) > 0)) { PrintUsage(argv[0],1,HeaderLineCount,UsageLineCount); return FAIL; } if ((HourCount != 0) && (strlen(StartTimeStr) == 0)) { PrintUsage(argv[0],1,HeaderLineCount,UsageLineCount); return FAIL; } if ((LocationFlag == 1) && (strlen(ComponentStr) > 0)) { PrintUsage(argv[0],1,HeaderLineCount,UsageLineCount); return FAIL; } if (HourCount != 0) { SecsToStrC(StrToSecsC(StartTimeStr,0)+3600*HourCount,EndTimeStr); } if ((strlen(Time_ave_mode) != 1) || (Time_ave_mode[0] != 'S' && Time_ave_mode[0] != 'M' && Time_ave_mode[0] != 'E')) { PrintUsage(argv[0],1,HeaderLineCount,UsageLineCount); return FAIL; } if (strlen(TimeFormat) > 0) { if (strcmp(TimeFormat, "ISO8601") != 0) { fprintf(stderr,"Time format illegal (-f option). Currently only ISO8601 is possible\n"); PrintUsage(argv[0],1,HeaderLineCount,UsageLineCount); return FAIL; } } /*====================================================*/ /* Read the data from an IAGA format file into memory */ /*====================================================*/ status = ReadIAGA(FileName,&NETWORK,StartTimeStr,EndTimeStr,StationStr); if (status != 0) { if (status == FileError) fprintf(stderr,"Error in reading file %s\n",FileName); if (status == OutOfMemory) fprintf(stderr,"Out of memory while reading file %s\n",FileName); if (status == FileFormatError) fprintf(stderr,"%s is not an IAGA format file\n",FileName); return FAIL; } if (NETWORK.StationList == NULL) return OK; // No stations, don't write anything /*===============================*/ /* Check the magnetic components */ /*===============================*/ if (strlen(ComponentStr) == 0) { *ComponentStr = 't'; strcpy(ComponentStr+1,NETWORK.StationList->Components); } else { for (i=0;iComponents, c) == NULL) { if (c != 'H' && c != 'D' && c != 'F') { fprintf(stderr,"Component %c not found in data file %s\n", c,FileName); return FAIL; } } } } /*=========================================================*/ /* If components are XYZ and user requests H, D or F */ /* then compute HDF for all stations */ /*=========================================================*/ found = 0; for (i=0; iComponents,"XYZ",3) == 0) && (found == 1)) { Compute_HDF(s); } s = s->Next; } /*==========================================*/ /* Write the data in columns into stdout */ /*==========================================*/ if (AriOption) WriteAriHeaders(&NETWORK,ComponentStr); else { if (HeaderFlag) WriteHeaders(&NETWORK,ComponentStr,Year2option); if (LocationFlag) WriteLocations(&NETWORK,ComponentStr,Year2option); if (HeaderFlag) WriteHorLine(&NETWORK,ComponentStr,Year2option); } if (AvePeriod == 0) AvePeriod = NETWORK.StationList->TimeStep; switch (Time_ave_mode[0]) { case 'S' : time_offset = 0; break; case 'M' : time_offset = NETWORK.StationList->TimeStep *((int) (AvePeriod/(2*NETWORK.StationList->TimeStep))); break; case 'E' : time_offset = NETWORK.StationList->TimeStep *((int) (AvePeriod/NETWORK.StationList->TimeStep)); break; } if (strlen(StartTimeStr) == 0) t = NETWORK.StationList->StartTime; else t = StrToSecsC(StartTimeStr,0); if (strlen(EndTimeStr) == 0) EndTime = NETWORK.StationList->EndTime; else EndTime = StrToSecsC(EndTimeStr,0); while (t < EndTime) { FirstComp = 0; if (AriOption) { SecsToStrC(t,TimeStr); printf("%.2s %.2s %.2s",TimeStr+8,TimeStr+10,TimeStr+12); FirstComp++; } else if (ComponentStr[0] == 't') { /* Write time */ CurrTime = t; if (Finnish) { /* Display time in Finnish time */ CurrTime += (2 + SummerTime(CurrTime))*3600; } // SecsToStr(CurrTime,TimeStr); // if (Year2option == 0) { /* Write century */ // if (CurrTime >= YEAR2000) // printf("20"); // else // printf("19"); // } // if (strlen(TimeFormat) == 0) // printf("%.2s %.2s %.2s %.2s %.2s %.2s ",TimeStr,TimeStr+2,TimeStr+4,TimeStr+6,TimeStr+8,TimeStr+10); // else // if (strcmp(TimeFormat, "ISO8601") == 0) { // printf("%.2s-%.2s-%.2sT%.2s:%.2s:%.2s",TimeStr,TimeStr+2,TimeStr+4,TimeStr+6,TimeStr+8,TimeStr+10); // if (Finnish) // printf(" "); // else // printf("Z "); // } SecsToStrC(CurrTime,TimeStr); if (strcmp(TimeFormat, "ISO8601") == 0) { printf("%.4s-%.2s-%.2sT%.2s:%.2s:%.2s",TimeStr,TimeStr+4,TimeStr+6,TimeStr+8,TimeStr+10,TimeStr+12); if (Finnish) printf(" "); else printf("Z "); } else { if (Year2option == 0) { /* Write century */ printf("%.2s",TimeStr); } if (strlen(TimeFormat) == 0) printf("%.2s %.2s %.2s %.2s %.2s %.2s ",TimeStr+2,TimeStr+4,TimeStr+6,TimeStr+8,TimeStr+10,TimeStr+12); } FirstComp++; } s = NETWORK.StationList; /* go through the stations */ while (s != NULL) { for (i=FirstComp;iNext; } printf("\n"); t += AvePeriod; } FreeNetwork(&NETWORK); return OK; }