/****************************************************************************/
/*                                                                          */
/*                                  GADF.h                                  */
/*                                                                          */
/****************************************************************************/
/****************************************************************************/
/* This is a C language header file that defines the following routines for */
/* reading data from a GADF-format data file into memory and writing data   */
/* from memory into a GADF-format file. The description of the GADF format  */
/* is given in the file GADF.doc in the EISCAT CD.                          */
/*                                                                          */
/*  -------------------  Read data from a GADF-format file  --------------  */
/*                                                                          */
/* long ReadGADF(char *FileName, NetworkPtr Network, char *StartTimeStr,    */
/*               char *EndTimeStr, char *StationList)                       */
/*      FileName        Name of the GADF-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 included.              */
/*                                                                          */
/*      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: FileFormatError  : Data file is not a GADF file              */
/*                                                                          */
/*                                                                          */
/*  ------------------  Write data into a GADF-format file  --------------- */
/*                                                                          */
/* long WriteGADF(char *FileName, NetworkPtr Network, char *StartTimeStr,   */
/*               char *EndTimeStr, char *StationList)                       */
/*      FileName        Name of data file. If NULL or zero length then      */
/*                      stdout is used.                                     */
/*      Network         Pointer to the Network structure whose data is      */
/*                      written into the data file.                         */
/*      StartTimeStr    Start date string YYMMDDHHMM. If NULL then same as  */
/*                      the time of the first record of the first station   */
/*                      in the Network.                                     */
/*      EndTimeStr      End date string YYMMDDHHMM. This record is NOT      */
/*                      included anymore. If NULL then same as the EndTime  */
/*                      of the first station in the Network.                */
/*      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 KEV').  */
/*                                                                          */
/*  The function result indicates how successfull the file writing was:     */
/*              0: OK           : Data written successfully                 */
/*              1: FileError    : Failed to create the given file           */
/*                                                                          */
/*  ----------------------------------------------------------------------- */
/*                                                                          */
/*  NOTE:   These routines are intended to be used only in dealing with     */
/*          EISCAT or IMAGE magnetometer network data files in GADF-format. */
/*          The routines defined here make assumptions about the data       */
/*          content. The routines might work also with other than EISCAT or */
/*          IMAGE data files but this is not guaranteed.                    */
/*                                                                          */
/****************************************************************************/
/****************************************************************************/
/*                            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.05    22.08.2001                          */
/****************************************************************************/
/****************************************************************************/
/*  Version history:                                                        */
/*                                                                          */
/*  1.05 22.08.2001                                                         */
/*      - Added a command which checks the validness of the ASCII header.   */
/*        If there are non-ascii characters then ReadGADF routine stops and */
/*        returns a file error.                                             */
/*  1.04 28.11.2000                                                         */
/*      - Fixed a bug where no data was written if there were less than 180 */
/*        data points in the station data record.                           */
/*  1.03 17.12.1999                                                         */
/*      - Fixed a bug in reading baseline values from a GADF-file.          */
/*        MissingValue was defined in MagnData.h as 9999999 while           */
/*        Missing baseline value in GADF is 999999.                         */
/*  1.02 27.07.1996                                                         */
/*      - Fixed a bug where WriteGADF crashed is there is no data in the    */
/*        Network-structure                                                 */
/*  1.01 15.12.1995                                                         */
/*      - Added missing GADF baseline manipulation. If baseline in GADF     */
/*        file is 999999 then 0 is substituted for the baseline value.      */
/*      - Added manipulation of the E-component. Now E is read into the     */
/*        D-field (= Y-field).                                              */
/*      - Fixed a bug which resulted in wrong field values if the scale     */
/*        factor was less than one (e.g. 1/2). This bug did not occur with  */
/*        the EISCAT CD-ROM.                                                */
/*                                                                          */
/*  1.0  2.2.1995   First official release                                  */
/****************************************************************************/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>


/*--------------------------------------------------------------------------*/
/* Define the offsets of various fields from the start of the GADF block    */
/* See the definition of GADF record for the explanation of various fields. */
/*--------------------------------------------------------------------------*/

#define GADF_RecLength       0
#define GADF_BinLength       2
#define GADF_ASCIILength     4
#define GADF_StationNbr      6
#define GADF_SampleStep      8
#define GADF_SampleCount    10
#define GADF_RefLevel       12
#define GADF_Uncertainty    14
#define GADF_Temperature    16
#define GADF_FilterBreak    18
#define GADF_FilterSlope    20
#define GADF_Reserved       22
#define GADF_RecFlag        24
#define GADF_Scale          25
#define GADF_Source         26
#define GADF_InstrNbr       27
#define GADF_ExtElemCode    28
#define GADF_DataType       29
#define GADF_BaseLineInfo   30
#define GADF_CodeRefLevel   31
#define GADF_StationCode    32
#define GADF_Component      35
#define GADF_PolarDist      36
#define GADF_Longitude      42
#define GADF_InvColatitude  48
#define GADF_DateTime       54
#define GADF_Baseline       66
#define GADF_Data           72

#define GADF_Missing    999999

typedef char *GADFptr;


/*--------------------------------------------------------------------------*/
/* Function prototypes                                                      */
/*--------------------------------------------------------------------------*/

static long CheckASCIIHeader(GADFptr GADF);
static long Get6Digits(char *p);
static double Power(long base,long n);
static long GetShort(char *p);
Time_sec GetGADFTime(GADFptr GADF);
long ReadGADFBlock(FILE *GADFfile, GADFptr GADF);
static void SetStationParamsGADF(StationPtr Station,GADFptr GADF);
static void GetGADFData(StationPtr Station,GADFptr GADF);
long ReadGADF(char *FileName, NetworkPtr Network, char *StartTimeStr,
              char *EndTimeStr, char *StationList);

static void SetShort(char *p,long value);
static void FillConstantData(GADFptr GADF);
static void FillStationInfo(GADFptr GADF,StationPtr Station);
static void FillGADFBlock(GADFptr GADF,StationPtr Station,
                          Time_sec T,char Comp);
void WriteGADFblock(FILE *GADFfile,GADFptr GADF);
long WriteGADF(char *FileName,NetworkPtr Network,char *StartTimeStr,
              char *EndTimeStr,char *StationList);



/*==========================================================================*/
/*              Routines for reading GADF-format files                      */
/*==========================================================================*/

/*--------------------------------------------------------------------------*/
/*  Check that all the characters in the ASCII header are valid.            */
/*--------------------------------------------------------------------------*/


static long CheckASCIIHeader(GADFptr GADF)
{
    char *p;
    long i;
    
    p = GADF+GADF_StationCode;
    for (i=0;i<4;i++) {
        if ((*p < 'A') || (*p > 'Z')) return (1);
        p++;
    }
    
    p = GADF+GADF_PolarDist;
    for (i=0;i<18;i++) {
        if (((*p < '0') || (*p > '9')) && (*p != ' ')) return (1);
        p++;
    }
    
    p = GADF+GADF_DateTime;
    /* For some reason the leading zero in day number is converted into space ??? */
    for (i=0;i<12;i++) {
        if (((*p < '0') || (*p > '9')) && (i != 4)) return (1);
        p++;
    }

    p = GADF+GADF_Baseline;
    for (i=0;i<6;i++) {
        if (((*p < '0') || (*p > '9')) && (*p != ' ') && (*p != '-')) return (1);
        p++;
    }

    return (0);
}


/*--------------------------------------------------------------------------*/
/*  Convert a six digit character string into long variable. This routine   */
/*  works if there are leading zeros or spaces.                             */
/*--------------------------------------------------------------------------*/

static long Get6Digits(char *p)
{
    long value = 0;
    long i;

    for (i=0;i<6;i++) {
        if (*p != ' ') value = 10*value+(*p-0x30);
        p++;
    }
    return value;
}


/*--------------------------------------------------------------------------*/
/*  Compute the power function: base^n, where n is negative or positive.    */
/*--------------------------------------------------------------------------*/

static double Power(long base,long n)
{
    long i;
    
    if (n == 0) return 1.0;
    
    if (n > 0) {
        for (i=1;n>0;--n) i *= base;
        return (double) i;
    }
    
    for (i=1;n<0;++n) i *= base;
    return 1.0/i;
}


/*--------------------------------------------------------------------------*/
/*  Get a two-byte binary number. The order of the bytes is : High-Low      */
/*  We cannot read the binaries with (short) p, where p is defined as       */
/*  (short) *p, since some processors use low-high byte ordering. In the    */
/*  GADF format the ordering is defined as high-low. We could check the     */
/*  type of the processor and then swap the bytes if necessary but that     */
/*  wouldn't be any simpler.                                                */
/*--------------------------------------------------------------------------*/

static long GetShort(char *p)
{
    register long high,low;
    
    high = (signed char) (*p++);
    low  = *p;  
    return((high << 8) | (0x000000FF & low));
}


/*--------------------------------------------------------------------------*/
/*  Get the start time in seconds of a 180-point data block. In a GADF      */
/*  data block the date format is YYMMDDHHMMSS.                             */
/*--------------------------------------------------------------------------*/

Time_sec GetGADFTime(GADFptr GADF)
{
    return (StrToSecs(GADF+GADF_DateTime,12));
}


/*--------------------------------------------------------------------------*/
/* Read one 432 character record from the GADF-format file into GADF block. */
/* Function result is 1 if reading was successful otherwise 0 is returned.  */
/*--------------------------------------------------------------------------*/

long ReadGADFBlock(FILE *GADFfile, GADFptr GADF)
{
    return (fread(GADF,432,1,GADFfile));
}


/*--------------------------------------------------------------------------*/
/*  Fill some fields in the Station_struct by copying them from the GADF    */
/*  block. AddNewStation procedure in the MagnData.h file will set other    */
/*  station parameters.                                                     */
/*--------------------------------------------------------------------------*/

static void SetStationParamsGADF(StationPtr Station,GADFptr GADF)
{
    /* --- Set the three letter station ID --- */
    strncpy(Station->StationID,GADF+GADF_StationCode,3);
    Station->StationID[3] = '\0';

    /* --- Set the coordinates of the station, unit = 0.01 degrees --- */
    Station->Longitude = Get6Digits(GADF+GADF_Longitude)/10;
    Station->Latitude  = 9000-Get6Digits(GADF+GADF_PolarDist)/10;

    /* --- Set StartTime and TimeStep, GetGADFData-routine sets EndTime --- */
    Station->StartTime = GetGADFTime(GADF);
    Station->TimeStep  = GetShort(GADF+GADF_SampleStep);
}



/*--------------------------------------------------------------------------*/
/*  Copy data from 432 character GADF block into proper OneDayBlock in the  */
/*  Station structure.                                                      */
/*--------------------------------------------------------------------------*/

static void GetGADFData(StationPtr Station,GADFptr GADF)
{
    OneDayPtr p;
    long *DataPtr;
    char *q;
    register long i,FieldValue,Baseline;
    double Scale;
    long ScaleParam,Count,Temperature;
    char Component;
    
    /* --- Find the OneDayBlock which has room for new data --- */
    Component = *(GADF+GADF_Component);
    p = Station->FirstDay;
    do {
        switch (Component) {
            case 'H' :
            case 'X' : Count = p->XCount; break;
            case 'D' :
            case 'Y' :
            case 'E' : 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 component markers in the station record --- */
    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 'E' : Station->Components[1] = 'D'; break;
        case 'Z' : Station->Components[2] = 'Z'; break;
    }
    
    /* --- Get the temperature value --- */
    if ((Component == 'X') || (Component == 'H')) {
        Temperature = GetShort(GADF+GADF_Temperature);
        DataPtr = (p->T)+(p->TCount);
        if ((Temperature == 0) || (Temperature == 0x7FFF))
            *DataPtr++ = MissingValue;
        else 
            *DataPtr++ = Temperature;
        p->TCount++;
    }

    /* --- Get the baseline and scalefactor for this block ---- */
    /* Because we use 0.1 nT accuracy in the field values we    */
    /* multiply the scale factor by 10. (see GADF.DOC)          */

    Baseline = Get6Digits(GADF+GADF_Baseline);
    if (Baseline == GADF_Missing) Baseline = 0;
    else Baseline = 10*Baseline;            /* Unit = 0.1 nT */

    ScaleParam = (long) (*(GADF+GADF_Scale));
    if (ScaleParam == 0) Scale = 10.0;
    else {
        if (ScaleParam <= 8) Scale = 10*Power(2,3-ScaleParam);
        else Scale = Power(10,11-ScaleParam);
    }

    /* --- Get the 180 datapoints for the particular component --- */
    switch (Component) {
        case 'H' :
        case 'X' : DataPtr = (p->X)+(p->XCount); break;
        case 'D' :
        case 'E' :
        case 'Y' : DataPtr = (p->Y)+(p->YCount); break;
        case 'Z' : DataPtr = (p->Z)+(p->ZCount); break;
    }
    q = GADF+GADF_Data;
    for (i=0;i<180;i++) {
        FieldValue = GetShort(q);
        if (FieldValue == 0x7FFF) *DataPtr++ = MissingValue;
        else *DataPtr++ = (long) (Scale*FieldValue)+Baseline;
        q += 2;
    }

    /* --- Update counters --- */
    switch (Component) {
        case 'H' :
        case 'X' : p->XCount += 180; break;
        case 'D' :
        case 'E' :
        case 'Y' : p->YCount += 180; break;
        case 'Z' : p->ZCount += 180; break;
    }

    /* --- Set the end time --- */
    Station->EndTime = GetGADFTime(GADF)+180*Station->TimeStep;
}



/*--------------------------------------------------------------------------*/
/*  Here is the routine for reading GADF-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 (432 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 ReadGADF(char *FileName, NetworkPtr Network, char *StartTimeStr,
              char *EndTimeStr, char *StationList)
{
    FILE    *GADFfile;              /* The data file to be processed        */
    char    GADF[432];              /* This is one GADF 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       */
    long    SampleStep;             /* Sampling rate                        */
    
    InitNetwork(Network);           /* Initialize the fields of Network     */

    /* --- Set the start and end times --- */
    if ((StartTimeStr == NULL) || (StartTimeStr[0] == '\0'))
        StartTime = MIN_TIME;
    else StartTime = StrToSecs(StartTimeStr,0);
    
    if ((EndTimeStr == NULL) || (EndTimeStr[0] == '\0'))
        EndTime = MAX_TIME-(Time_sec)86400;
    else EndTime = StrToSecs(EndTimeStr,0);

    /* --- Try to open the file --- */
    if ((FileName == NULL) || (FileName[0] == '\0')) GADFfile = stdin;
    else if ((GADFfile = fopen(FileName, "rb")) == NULL) return(FileError);

    /* --- Read data into one day blocks ---*/ 
    while (ReadGADFBlock(GADFfile,GADF))
    {
        /* Check that some fields at the beginning of the block are valid */
        
        if (GetShort(GADF+GADF_RecLength) != 432)
            return(FileFormatError);
        
        if (CheckASCIIHeader(GADF) != 0) {
            fprintf(stderr,"### Corrupt block ! Station = %.3s Component = %c Date = %.12s\n",
                    GADF+GADF_StationCode, GADF+GADF_Component, GADF+GADF_DateTime);
            return(FileError);
        }
            
        if ((CurrTime=GetGADFTime(GADF)) >= EndTime+(Time_sec)86400) break;
    
        if ((CurrTime >= StartTime) && (CurrTime < EndTime)
            && StationInList(GADF+GADF_StationCode,StationList))
        {
            Station = FindStation(Network,GADF+GADF_StationCode);

            if (Station == NULL) {      /* First occurrence of this station */
                SampleStep = GetShort(GADF+GADF_SampleStep);
                Station = AddNewStation(Network,SampleStep,180*SampleStep);
                if (Station != NULL)
                    SetStationParamsGADF(Station,GADF);
                else        /* Unable to allocate memory for station data   */
                    return(OutOfMemory);
            }
        
            if (OneDayFull(Station,GADF[GADF_Component])) {
                if (AddOneDay(Station))
                    return(OutOfMemory);    /* Unable to allocate memory    */
            }
                        
            GetGADFData(Station,GADF);
        }
    }
    fclose(GADFfile);

    /* --- 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 GADF-format files                      */
/*==========================================================================*/


/*--------------------------------------------------------------------------*/
/*  Set a two-byte binary number. The order of the bytes is : High-Low.     */
/*--------------------------------------------------------------------------*/

static void SetShort(char *p,long value)
{
    *p++ = (char)(value >> 8);
    *p   = (char)(value);
}


/*--------------------------------------------------------------------------*/
/*  Fill those fields in the GADF block that are always constant.           */
/*  This applies to EISCAT and IMAGE networks. In other cases these fields  */
/*  may change.                                                             */
/*--------------------------------------------------------------------------*/

static void FillConstantData(GADFptr GADF)
{
    SetShort(GADF+GADF_RecLength,432);
    SetShort(GADF+GADF_BinLength,32);
    SetShort(GADF+GADF_ASCIILength,40);
    SetShort(GADF+GADF_SampleCount,180);
    SetShort(GADF+GADF_RefLevel,0x7FFF);
    SetShort(GADF+GADF_Uncertainty,0x7FFF);
    SetShort(GADF+GADF_FilterSlope,0x7FFF);
    SetShort(GADF+GADF_Reserved,0x7FFF);
    GADF[GADF_Source]   = (char) 1;
    GADF[GADF_InstrNbr] = (char) 0;
    GADF[GADF_CodeRefLevel] = (char) 0;
}


/*--------------------------------------------------------------------------*/
/*  Fill those fields in the GADF block that describe the station           */
/*--------------------------------------------------------------------------*/

static void FillStationInfo(GADFptr GADF, StationPtr Station)
{
    StationInfoPtr p;
    char DummyStr[10];

    /* --- Write the internal station number --- */
    p = FindStationInfo(Station->StationID);
    SetShort(GADF+GADF_StationNbr,p->GADF_ID);

    /* --- Write the three letter Station code --- */
    memcpy(GADF+GADF_StationCode,Station->StationID,3);
    
    /* --- Write the coordinates of the station --- */
    sprintf(DummyStr,"%6d",10*(9000-Station->Latitude)); /* Northpole dist. */
    memcpy(GADF+GADF_PolarDist,DummyStr,6);
    sprintf(DummyStr,"%6d",10*Station->Longitude);       /* Longitude       */
    memcpy(GADF+GADF_Longitude,DummyStr,6);
    memcpy(GADF+GADF_InvColatitude,"     0",6);     /* Invariant colatitude */
}


/*--------------------------------------------------------------------------*/
/*  Fill the data fields and those fields which have not yet been filled in */
/*  in the GADF block.                                                      */
/*--------------------------------------------------------------------------*/

static void FillGADFBlock(GADFptr GADF,StationPtr Station,
                          Time_sec T,char Comp)
{
    long i,Max,Min,Baseline,Temperature,DataValue;
    char DummyStr[14];
    char *q;
    Time_sec Time;

    /* --- Write the component character --- */
    GADF[GADF_Component] = Comp;
    
    /* --- Write the sample interval --- */
    SetShort(GADF+GADF_SampleStep,Station->TimeStep);
    
    /* --- Write date and time --- */
    SecsToStr(T,DummyStr);
    if (DummyStr[4] == '0')
        DummyStr[4] = ' ';  /* Change leading zero in day field into space  ??? */
    memcpy(GADF+GADF_DateTime,DummyStr,12);

    /* ---------------- Write the scale factor -----------------*/
    /* Here we always write scale factor 0.1 (ScaleParam = 11)  */
    /* This corresponds to 0.1 nT accuracy in field values      */
    /* which is the same as in IAGA-format data files.          */
    
    GADF[GADF_Scale] = (char) 11;

    /* --- Compute and write the baseline value for this block ---  */

    /* Try to use first the baselines that DMI has used for EISCAT  */
    /* stations. If they are not good compute new ones.             */

    switch (Comp) {
        case 'X' : Baseline = 10000; break;
        case 'Y' : Baseline =     0; break;
        case 'Z' : Baseline = 50000; break;
        default  : Baseline =     0;    /* for H and D */
    }
    if ((Comp == 'Z') && (strncmp(Station->StationID,"KAU",3) == 0))
        Baseline = 52000;

    /* Compute the maximum and minimum value of the field */
    FindMaxMin(Station,Comp,T,180*Station->TimeStep,&Max,&Min);
    
    /* Compute new baseline value if there is overflow. */
    /* and round it to the nearest 1000 nT.             */                      
    Baseline *= 10;     /* Use 0.1 nT as a unit */
    if ((abs(Max-Baseline) > 0x7FFF) || (abs(Min-Baseline) > 0x7FFF))
        Baseline = (((Max+Min)/2+5000)/10000)*10000;

    
    /* If there is still overflow give a notification. This should  */
    /* never happen. (Max - Min > 5000 nT).                         */
    if ((abs(Max-Baseline) > 0x7FFF) || (abs(Min-Baseline) > 0x7FFF)) {
        fprintf(stderr,"### Overflow : variations too large: %s %c %s\n",
            Station->StationID,Comp,DummyStr);
        fprintf(stderr,"    Max value: %d\n",Max/10);
        fprintf(stderr,"    Min value: %d\n",Min/10);
    }
    
    if (Max == MissingValue) Baseline = 0;      /* All values missing data  */
    
    sprintf(DummyStr,"%6d",Baseline/10);
    memcpy(GADF+GADF_Baseline,DummyStr,6);
    

    /* --- Write all 180 data values --- */
    q = GADF+GADF_Data;
    Time = T;

    for (i=0;i<180;i++) {
        DataValue = GetDataValue(Station,Time,Comp);
        if (DataValue == MissingValue)
            SetShort(q,0x7FFF);
        else
            SetShort(q,DataValue-Baseline);
        q += 2;
        Time += Station->TimeStep;
    }

    
    
    /* --- Fill temperature and rest of the fields in GADF block --- */

    /* If Max = Min then all field values are missing */
    if (Max == Min) {
        SetShort(GADF+GADF_Temperature,0);
        SetShort(GADF+GADF_FilterBreak,0x7FFF);
        GADF[GADF_RecFlag]      = (char) 1;
        GADF[GADF_ExtElemCode]  = (char) 0;
        GADF[GADF_DataType]     = (char) 0;
        GADF[GADF_BaseLineInfo] = (char) 0;
    } else {
        Temperature = GetDataValue(Station,T,'T');
        if (Temperature == MissingValue) Temperature = 0x7FFF;
        SetShort(GADF+GADF_Temperature,Temperature);
        SetShort(GADF+GADF_FilterBreak,0);
        GADF[GADF_RecFlag]      = (char) 0;
        GADF[GADF_ExtElemCode]  = (char) 0;
        GADF[GADF_DataType]     = (char) 1;
        GADF[GADF_BaseLineInfo] = (char) 0;
        switch (Comp) {
            case 'D' : i = 1; break;
            case 'H' : i = 3; break;
            case 'X' : i = 5; break;
            case 'Y' : i = 6; break;
            case 'Z' : i = 7; break;
        }
        GADF[GADF_ExtElemCode] = (char) i;
    }
}

/*--------------------------------------------------------------------------*/
/*  Write a single 432 character GADF block into given file.                */
/*--------------------------------------------------------------------------*/

void WriteGADFblock(FILE *GADFfile,GADFptr GADF)
{
    fwrite(GADF,432,1,GADFfile);
}

/*--------------------------------------------------------------------------*/
/*  Here is the routine for writing data into a GADF-format file. See the   */
/*  comments at the beginning of this file for more details about the       */
/*  parameters for this routine.                                            */
/*--------------------------------------------------------------------------*/

long WriteGADF(char *FileName,NetworkPtr Network,char *StartTimeStr,
              char *EndTimeStr,char *StationList)
{
    FILE    *GADFfile;              /* The data file to be processed        */
    char    GADF[432];              /* This is one GADF 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       */
    long    TimeStep;               /* Time between successive data points  */
    long    HourCount;              /* Number of hours to be written        */
    long    BlockCount;             /* Number of blocks to be written       */
    long    i,Comp;                 /* Dummy indices                        */
    Time_sec Time2;                 /* Dummy time variable                  */
    Time_struct TimeStruct;         /* Dummy time variable                  */
    char    Component;              /* Magnetic field component             */
    
    /* --- 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 = StrToSecs(StartTimeStr,0);

    if ((EndTimeStr == NULL) || (*EndTimeStr == '\0'))
        EndTime = Network->StationList[0].EndTime;
    else EndTime = StrToSecs(EndTimeStr,0);

    /* --- Try to create the file --- */
    if ((FileName == NULL) || (*FileName == '\0')) GADFfile = stdout;
    else if ((GADFfile = fopen(FileName, "wb")) == NULL) return FileError;
    
    FillConstantData(GADF);     /* Fill the fields independent of stations */

    /* ------------------------- Write the data ----------------------------*/
    /* Records in GADF file are in the order: Day,Station,Component,Hour.   */
    /* So first comes 24 hours for Day #1, Station #1, Component #1.        */
    /* Then 24 hours for Day #1, Station #1, Component #2 etc.              */
    /* 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.                                                */
    /* ---------------------------------------------------------------------*/

    TimeStep = Network->StationList[0].TimeStep;
    CurrTime = StartTime;
    
    while (CurrTime < EndTime) {
        /* --- Check first the number of hours in current day --- */
        if ((EndTime-CurrTime) < (Time_sec) 86400)
            HourCount = (EndTime-CurrTime)/3600; /* Last day ?*/
        else {
            SecsToTm(CurrTime,&TimeStruct);
            HourCount = 24-TimeStruct.tm_hour;
        }
        BlockCount = (((3600*HourCount)/TimeStep)+179)/180; /* 180 points per block */


        /* --- Go through all stations, components and hours --- */
        Station = Network->StationList;
        while (Station != NULL) {
            if (StationInList(Station->StationID,StationList)) {
                FillStationInfo(GADF,Station);
                for (Comp=0;Comp<3;Comp++) {
                    Component = Station->Components[Comp];
                    Time2 = CurrTime;
                    for (i=0;i<BlockCount;i++) {
                        FillGADFBlock(GADF,Station,Time2,Component);
                        WriteGADFblock(GADFfile,GADF);
                        Time2 += 180*TimeStep;
                    }
                }
            }
            Station = Station->Next;
        }
        CurrTime += 3600*HourCount;
    }
    fclose(GADFfile);

    return OK;
}

