Previous: Python API   Up: Madrigal developer's guide   Next: Fortran API

Madrigal C API

 

The madrec library (libmadrec.a) contains a comprehensive set of C-language procedures for working with Cedar Database files (the madrec module), and recently has been upgraded to work at a higher data level with both measured and derived data (the maddata module).

The madrec library (libmadrec.a) contains a comprehensive set of C-language procedures for working with Cedar Database files. Five version of CEDAR file are supported - Madrigal, Blocked Binary, Cbf, Unblocked binary and Ascii. In addition, an entire file may be read into memory for rapid random record access. When opening a file for reading, the madrec package is able to determine the file version automatically. Data, header and catalog records are all supported. The Cedar format itself is big-endian, but Madrec is endian-neutral and works on little endian as well as big endian computers.

The maddata module allows an application writer to ignore the difference between measured and derived parameters - from the maddata level, any file can be assumed to contain its measured data and all possible derived data. The maddata module is also designed to be easily extended to derive new parameters.

The following are suggested lines to add to your Makefile when using the Madrigal C API:

  # Library directory  LIBDIR = $(MADROOT)/lib    # Include directory  INCLUDEDIRS = -I. -I$(MADROOT)/source/madc/include    LDLIBS = -L$(LIBDIR) -lmadrec -lgeo -lm -lnsl        if solaris:    LDFLAGS = -R$(LIBDIR)        if gnu:    LDFLAGS = -Xlinker -R$(LIBDIR)  

madrec and maddata procedures

The madrec library (libmadrec.a) contains a comprehensive set of C-language procedures for working with Cedar Database files (the madrec module), and at a higher data level with both measured and derived data (the maddata module). As of release 2.2, all Madrigal software is built on the madrec C library.

Overview of file-level madrec API

As detailed in the synopsis of madrecOpen, five version of CEDAR file are supported - Madrigal, Blocked Binary, Cbf, Unblocked binary and Ascii. In addition, and entire file may be read into memory for rapid random record access. When opening a file for reading, the madrec package is able to determine the file version automatically. Data, header and catalog records are all supported. The Cedar format itself is big-endian, but Madrec is endian-neutral and works on little endian as well as big endian computers.

All methods in madc that return an array of any sort (char, int, or double) allocate the returned array from the heap. The user of this library is responsible for freeing these arrays when done. Some methods, such as cedarGetGeodetic, also allocate arrays for pointers passed in as arguments. This is the case when the method is designed to return more than one array, as is the case with cedarGetGeodetic. See the documentation of the individual methods for details.

Overview of data level maddata API

The maddata level exposes Madrigal in such a way that the user does not need to worry about whether data is measured in a file or derived. The user needs only deal with the abstact maddata data structure.The struct maddata is intented to provide easy access to madrigal data from a single cedar file that has been combined with derived parameters and has been filtered. The maddata module can also be used without files by passing in data directly needed to calculate other parameters.

The data is organized as follows:


           Maddata           MadparmList
         |         |
         v         v
      Madcycle   Madfilter
         |
         v
      Madrecord

All data in this structure corresponds to Madrigal parameters, and so is referenced by its unique mnemonic, not by Cedar parameter codes. All data is stored as doubles, with special values as defined in cedar.h.

While this module is written in C and not C++; its methods and design are as close as I could get to object-oriented. Every data structure should be instantiated via a create* method and released via destroy*. All other methods take the respective data structure pointer as the first argument. See usage in simpleMaddata.c.

The Madrecord structure defined in this file differs from the Madrec structure defined in madrec.h in that the Madrecord struct does not care about the Cedar file format, or indeed in what way the data is stored. It's basic unit of data is a double, not the 16 bit Int as in the Cedar format.

The derivation engine behind this interface is defined in the private modules madDeriveEngine and madDeriveMethods. Extending maddata simply involves adding new methods (and possibly parameters), as fully explained in madDeriveMethods.h.

See the files simpleMaddata.c and simpleNonfileMaddata.c for example usage, or the online examples.


The madrec module

madrecCreate madrecDestroy madrecOpen madrecClose madrecGetNextRec
madrecPutNextRec madrecRewind madrecGetPreviousRec madrecGetRecByRecno madrecGetRecByKey
madrecGenKeys madrecDeleteKeys madrecPrintKeys madrecCheckFile madrecCopy
madrecGetFileType madrecSetError madrecGetError madrecGetMissing madrecGetNumParms
madrecGetParmsList madrecGetParmLoc madrecGetParmMin madrecGetParmMax madrecHasCatalog
madrecHasHeader madrecGetSortedRecnoList compareCedarIndices cedarGetMadroot cedarGetLtot
cedarGetKrec isDataRecord cedarGetKinst cedarGetKindat cedarGetIbyr
cedarGetIbdt cedarGetIbhm cedarGetIbcs cedarGetIeyr cedarGetIedt
cedarGetIehm cedarGetIecs cedarGetLprol cedarGetJpar cedarGetMpar
cedarGetNrow cedarGetKpar cedarGetWord cedarGetStartTime cedarGetEndTime
cedarGetStartJday cedarGetEndJday cedarGetStartIndex cedarGetEndIndex cedarGet1dParcodes
cedarGet2dParcodes cedarHas1DParcode cedarHas2DParcode cedarGet1dParm cedarGet2dParm
cedarGet2dParmValue cedarGetFlatParm hasData cedarGetParmCodeArray cedarGetParmArray
cedarGetGeodetic cedarGet1dInt cedarGet2dInt cedarGet2dIntValue cedarCreateRecord
cedarCreateCatalogRecord cedarAppendCatalogRecord cedarCreateHeaderRecord cedarAppendHeaderRecord cedarSetKrec
cedarSetKinst cedarSetKindat cedarSetStartTime cedarSetEndTime cedarSet1dParm
cedarSetNorm1dParm cedarSet2dParm cedarSetNorm2dParm cedarSet1dInt cedarSet2dInt
cedarPrintRecord cedarGetInformation cedarPrintProlog cedarReadParCodes cedarGetNumParCodes
cedarGetParCode cedarGetParCodeIndex madGetParMnemIndex isMadparmError getStdMnem
cedarGetParCodeType madGetParMnemType madGetCategoryIndex cedarGetParDescription madGetParDescription
cedarGetParInt16Description madGetParInt16Description cedarGetParScaleFactor madGetParScaleFactor cedarGetNormScaleFactor
madGetNormScaleFactor cedarGetParUnits madGetParUnits cedarGetParMnemonic cedarGetParCodeFromMnemonic
cedarGetParFormat madGetParFormat cedarGetParWidth madGetParWidth cedarHasHtmlDesc
madHasHtmlDesc cedarCheckRecord cedarHexPrintRecord cedarDecimalPrintRecord cedarSetError
cedarGetError cedarTabInt cedarUpdateParmsList cedarGetStationPos cedarGetStationName
searchFilesByDate loadExpFileTable goodDataExists sprod vadd
vsub csconv vctcnv point look
convrt rpcart gdv los2geodetic solarzen_az
solardist shadowheight sunrise_set jday jdater
idmyck dmadptr getKey dinvmadptr madGetDayno

/***********************************************************************
*
* madrecCreate     creates a madrec object
*
*   arguments:
*       None
*
*   returns:
*       pointer to the new madrec object
*
*/

Madrec *
madrecCreate ()
 
 

/***********************************************************************
*
* madrecDestroy    destroys a madrec object
*
*   arguments:
*       madrecp - pointer to the madrec object
*
*   returns
*       0
*
*/

int
madrecDestroy (Madrec *madrecp)
 
 

/***********************************************************************
*
* madrecOpen     opens a madrec data file
*
*   arguments:
*       madrecp - pointer to the madrec object
*       iotype  - file type as described below
*       filnam  - file name
*
*   The following file types (iotype) are supported:
*
*       Open Cedar file for sequential reading:
*             1 - Determine file type automatically
*            10 - Madrigal file
*            11 - Blocked Binary file
*            12 - Cbf file
*            13 - Unblocked Binary file
*            14 - Ascii file
*
*       Create Cedar file for update; discard previous contents if any:
*             2 - Madrigal file
*            20 - Madrigal file
*            21 - Blocked Binary file
*            22 - Cbf file
*            23 - Unblocked Binary file
*            24 - Ascii file
*
*       Create Cedar file in memory for sequential and random read and write. 
*            30 - Determine file type automatically
*            40 - Madrigal file
*            41 - Blocked Binary file
*            42 - Cbf file
*            43 - Unblocked Binary file
*            44 - Ascii file 
*
*       Fast create Cedar file in memory for sequential and random read and write.
*          Does not calculate min and and max parameter values 
*            50 - Determine file type automatically
*            60 - Madrigal file
*            61 - Blocked Binary file
*            62 - Cbf file
*            63 - Unblocked Binary file
*            64 - Ascii file
*
*   returns:
*       0 - File opened successfully
*       1 - Invalid file type (iotype)
*       2 - unable to open data file
*       3 - data file already open
*       4 - input file name too long
*       5 - error writing file to memory
*
*/

int
madrecOpen (Madrec *madrecp, int iotype, char *filnam)
 
 

/***********************************************************************
*
* madrecClose    closes a madrec data file
*
*   arguments:
*       madrecp - pointer to the madrec object
*
*   returns:
*       0 - File closed successfully
*       1 - error closing file
*       2 - file not open
*       3 - error flushing file
*
*/

int
madrecClose (Madrec *madrecp)
 
 

/***********************************************************************
*
* madrecGetNextRec   reads a cedar record and fills a Madrec structure
*                    with the information in the record.
*
*   arguments:
*       madrecp - pointer to the madrec object
*
*   returns:
*       0 - Record read successfully
*       1 - Illegal file type (bad iotype in madrec)
*      -n - Error in CedarIO package
*
*/

int
madrecGetNextRec (Madrec *madrecp)
 
 

/***********************************************************************
*
* madrecPutNextRec   writes a cedar record.
*
*   arguments:
*       madrecp - pointer to the madrec object
*
*   returns:
*       0 - 
*       1 - Illegal file type (bad iotype in madrec)
*      -n - Error in CedarIO package
*
*/

int
madrecPutNextRec (Madrec *madrecp)
 
 

/***********************************************************************
*
* madrecRewind   rewinds a cedar record.
*
*   arguments:
*       madrecp - pointer to the madrec object
*
*   returns:
*       0 - 
*       1 - Illegal file type (bad iotype in madrec)
*      -n - Error in CedarIO package
*
*/

int
madrecRewind (Madrec *madrecp)
 
 

/***********************************************************************
*
* madrecGetPreviousRec   reads an madrigal record and fills a Mad 
*                        structure with the information in the record.
*
*   arguments:
*       madrecp - pointer to the madrec object
*
*   returns:
*       0 - 
*
*/

int
madrecGetPreviousRec(Madrec *madrecp)
 
 

/***********************************************************************
*
* madrecGetRecByRecno   reads a madrigal record and fills a Mad 
*                          structure with the information in the record.
*                          The record number is specified by the second
*                          argument. The first record is recno=0.
*
*   arguments:
*       madrecp - pointer to the madrec object
*       recno   - the record number to get. The first record is recno=0.
*
*   returns:
*       0 - Record read successfully
*      -1 - Specified record not in file
*
*/

int
madrecGetRecByRecno(Madrec *madrecp, int recno)
 
 

/***********************************************************************
*
* madrecGetRecordByKey   reads an madrigal record and fills a Mad 
*                        structure with the information in the record.
*                        The record is the first data record for which key is
*                        greater than or equal to the start key of the
*                        record, and less than the start time of the
*                        following record. Thus, if the specified key
*                        corresponds to a time within a record, the
*                        first such record is returned. Header or catalog
*                        records are never returned.
*                    
*
*   arguments:
*       madrecp - pointer to the madrec object
*       key - time in seconds since 1/1/1950
*
*   returns:
*       0 - if record found
*      -1 - if record not found
*
*/

int
madrecGetRecByKey(Madrec *madrecp, double key)
 
 

/***********************************************************************
*
* madrecGenKeys   Generates madrec key array. All information needed to
*                 access records randomly by key or record number is
*                 saved.
*
*   arguments:
*       madrecp - pointer to the madrec object
*
*   returns:
*       0 - 
*
*/


int
madrecGenKeys (Madrec *madrecp)
 
 

/***********************************************************************
*
* madrecDeleteKeys   Deletes madrec key array.
*
*   arguments:
*       madrecp - pointer to the madrec object
*
*   returns:
*       0 - 
*
*/


int
madrecDeleteKeys (Madrec *madrecp)
 
 

/***********************************************************************
*
* madrecPrintKeys   Prints madrec file key table
*
*   arguments:
*       madrecp - pointer to the madrec object
*
*   returns:
*       0 - 
*
*/


int
madrecPrintKeys (Madrec *madrecp)
 
 

/***********************************************************************
*
* madrecCheckFile    checks the structure of a madrec data file
*
  Block (Physical record) structure:
  
  6720 16bit words (Int16)
  
  word[0]       = Total number of significant words in the block
		  record (all blocks are 13440 bytes long)
    
  word[1]       = Pointer to the first word of the first logical
		  record contained in the block.
  
		  (set to zero if the block doesn't contain
		   any complete logical records i.e. it just
		   contains the last part of a logical record.)
  
  word[2]       = Pointer to the first word of the last logical
		  record contained in block.
  
  word[word[0]-1] = Checksum.
  
  Logical Records:
  
  word[0 - 15]  = Same as NCAR binary logical records.
  word[16]      = Pointer to word 1 of previous logical record.
		   -could be contained in previous block.
		   -Set to zero in the first logical record of the file.
*/

int
madrecCheckFile(Madrec *madrecp)
 
 

/***********************************************************************
*
* madrecCopy   Copies logical record from one madrec object to another
*
*   arguments:
*       madrec1p - pointer to the source madrec object
*       madrec2p - pointer to the destination madrec object
*
*   returns:
*       0 - Record copied successfully
*       1 - Empty source record
*
*/

int
madrecCopy (Madrec *madrec1p, Madrec *madrec2p)
 
 

/***********************************************************************
*
* madrecGetFileType   Gets file type
*
*   arguments:
*       madrecp - pointer to madrec object
*
*/
int
madrecGetFileType (Madrec *madrecp)
 
 

/***********************************************************************
*
* madrecSetError   sets madrec error
*
*   arguments:
*       madrecp - pointer to the madrec object
*
*   returns:
*       0 - 
*
*/


int
madrecSetError (Madrec *madrecp, const char *error)
 
 

/***********************************************************************
*
* madrecGetError   gets last madrec error
*
*   arguments:
*       madrecp - pointer to the madrec object
*
*   returns:
*       error string 
*
*/

char *
madrecGetError (Madrec *madrecp)
 
 

/***********************************************************************
*
* madrecGetMissing   gets missing data value
*
*   arguments:
*       madrecp - pointer to the madrec object
*
*   returns:
*       double precision missing value 
*
*/

double
madrecGetMissing (Madrec *madrecp)
 
 

/***********************************************************************
*
* madrecGetNumParms   gets number of different parameters in file
*
*   arguments:
*       madrecp - pointer to the madrec object
*
*   returns:
*       number of distint parameters
*
*/

int
madrecGetNumParms (Madrec *madrecp)
 
 

/***********************************************************************
*
* madrecGetParmsList   gets list (int array) of different parameters
*                      codes in file
*
*   arguments:
*       madrecp - pointer to the madrec object
*
*   returns:
*       codes of distinct parameter 
*
*  This returned array is a pointer to an internal structure in Madrec;
*  it does not need to be freed by the user.
*
*/

int *
madrecGetParmsList (Madrec *madrecp)
 
 

/***********************************************************************
*
* madrecGetParmLoc   gets location of parameter
*
*   arguments:
*       madrecp - pointer to the madrec object
*
*   returns:
*       parameter location:
*           1 - 1D array
*           2 - 2D array
*           3 - Derived
*
*/

int *
madrecGetParmLoc (Madrec *madrecp)
 
 

/***********************************************************************
*
* madrecGetParmMin   gets minimum value of parameter
*
*   arguments:
*       madrecp - pointer to the madrec object
*
*   returns - Minimum value of parameter in entire file
*
*/

double *
madrecGetParmMin (Madrec *madrecp)
 
 

/***********************************************************************
*
* madrecGetParmMax   gets maximum value of parameter
*
*   arguments:
*       madrecp - pointer to the madrec object
*
*   returns - Maximum value of parameter in entire file
*
*/

double *
madrecGetParmMax (Madrec *madrecp)
 
 

/***********************************************************************
*
* madrecHasCatalog   returns 1 if file has catalog record, 0 otherwise
*
*   arguments:
*       madrecp - pointer to the madrec object
*
*   returns - 1 if file has catalog record, 0 otherwise
*
*/

int madrecHasCatalog(Madrec *madrecp)
 
 

/***********************************************************************
*
* madrecHasHeader   returns 1 if file has header record, 0 otherwise
*
*   arguments:
*       madrecp - pointer to the madrec object
*
*   returns - 1 if file has header record, 0 otherwise
*
*/

int madrecHasHeader(Madrec *madrecp)
 
 

/***********************************************************************
*
* madrecGetSortedRecnoList   gets int array of recno's sorted by start time key
*
*   arguments:
*       madrecp - pointer to the madrec object
*
*   returns - int array of recno's sorted by start time key, length = nrecords
*
*/

int * madrecGetSortedRecnoList (Madrec *madrecp)
 
 

/***********************************************************************
*
* compareCedarIndices   a private method to compare one CedarIndex to another
*
*   arguments:
*       void * cedarIndex1 - void pointer to the first CedarIndex
*       void * cedarIndex2 - void pointer to the second CedarIndex
*
*   returns - if cedarIndex1 before cedarIndex2, return -1
*             if cedarIndex1 same as cedarIndex2, return 0
*             if cedarIndex1 after cedarIndex2, return 1
*
*/
int compareCedarIndices(const void * index1, const void * index2)
 
 

/***********************************************************************
*
* cedarGetMadroot   copies Madroot path into user-supplied character
*                   buffer.
*
*   Simply calls getenv, if not found, uses #define __MAD_ROOT__
*
*   Returns  void
*/
void cedarGetMadroot(char * buf)
 
 

/***********************************************************************
*
* cedarGetLtot   gets length of record
*
*/

int cedarGetLtot (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGetKrec   gets Kind of record
*
*/

int cedarGetKrec (Int16 *cedarp)
 
 

/***********************************************************************
*
* isDataRecord   returns 1 if data record, 0 if catalog or header
*
*/

int isDataRecord(Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGetKinst   gets instrument code for these data
*
*/

int cedarGetKinst (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGetKindat   gets kind-of-data code
*
*/

int cedarGetKindat (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGetIbyr   gets beginning year
*
*/

int cedarGetIbyr (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGetIbdt   gets beginning date (100*month+day)
*
*/

int cedarGetIbdt (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGetIbhm   gets beginning hour and minute (100*hour + minute)
*
*/

int cedarGetIbhm (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGetIbcs   gets beginning centisecond
*
*/

int cedarGetIbcs (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGetIeyr   gets ending year
*
*/

int cedarGetIeyr (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGetIedt   gets ending date (100*month+day)
*
*/

int cedarGetIedt (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGetIehm   gets ending hour and minute (100*hour + minute)
*
*/

int cedarGetIehm (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGetIecs   gets ending centisecond
*
*/

int cedarGetIecs (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGetLprol   gets prolog length
*
*/

int cedarGetLprol (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGetJpar   gets number of single-valued parameters
*
*/

int cedarGetJpar (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGetMpar  gets number of multiple-valued parameters
*
*/

int cedarGetMpar (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGetNrow   gets number of entries for each multiple-valued parameter
*
*/

int cedarGetNrow (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGetKpar  gets number of derived parameters
*
*  Deprecated - use Maddata module for all derived data
*
*/

int cedarGetKpar (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGetWord   gets specified word from cedar record
*
*/

int cedarGetWord (Int16 *cedarp, int word)
 
 

/***********************************************************************
*
* cedarGetStartTime   gets start time of record
*
*/

int cedarGetStartTime (Int16 *cedarp, int *year, int *month, int *day,
                   int *hour, int *minute, int *second, int *centisecond)
 
 

/***********************************************************************
*
* cedarGetEndTime   gets end time of record
*
*/

int cedarGetEndTime (Int16 *cedarp, int *year, int *month, int *day,
                   int *hour, int *minute, int *second, int *centisecond)
 
 

/***********************************************************************
*
* cedarGetStartJday   gets start Julian Day plus fractioanl day
*
*/

double cedarGetStartJday (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGetEndJday   gets end Julian Day plus fractioanl day
*
*/

double cedarGetEndJday (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGetStartIndex   gets start index time of record
*
*/

double cedarGetStartIndex (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGetEndIndex   gets end index time of record
*
*/

double cedarGetEndIndex (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGet1dParcodes  gets 1D parameter codes from a madrigal record
*
*  This methods allocates dynamic memory for the array of ints
*  returned.  The caller of this method is responsible for
*  calling free to release this memory when finished with it.
*
*  If no 1D parameter codes, returns NULL pointer.
*
*/

int * cedarGet1dParcodes(Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGet2dParcodes  gets 2D parameter codes from a madrigal record
*
*  This methods allocates dynamic memory for the array of ints
*  returned.  The caller of this method is responsible for
*  calling free to release this memory when finished with it.
*
*  If no 2D parameter codes, returns NULL pointer.
*/

int * cedarGet2dParcodes(Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarHas1DParcode  returns 1 if cedarp has particular 1D parcode, 0 otherwise.
*
*/
int cedarHas1DParcode(Int16 *cedarp, int parcode)
 
 

/***********************************************************************
*
* cedarHas2DParcode  returns 1 if cedarp has particular 2D parcode, 0 otherwise.
*
*/
int cedarHas2DParcode(Int16 *cedarp, int parcode)
 
 

/***********************************************************************
*
* cedarGet1dParm   gets a scaled 1D parameter from a madrigal record
*
*  If 1D parm does not exist, returns double "missing"
*  If 1D parm = -32767 (missing), returns double "missing"
*  If 1D parm is an error code, and = -32766 (assumed), returns double "assumed"
*  If 1D parm is an error code, and = +32767 (known bad), returns double "knownbad"
*
*  Otherwise, scales value and includes additional increment values 
*  if they exist
*
*  If cedarp is a header or catalog record; warning is printed to std err
*  and returns "missing"
*
*/

double cedarGet1dParm(Int16 *cedarp, int parcode)
 
 

/***********************************************************************
*
* cedarGet2dParm   gets a scaled 2D parameter from a madrigal record
*
*  If 2D parm does not exist, returns array of double "missing"
*  If 2D parm = -32767 (missing), returns double "missing"
*  If 2D parm is an error code, and = -32766 (assumed), returns double "assumed"
*  If 2D parm is an error code, and = +32767 (known bad), returns double "knownbad"
*
*  Otherwise, scales value and includes additional increment values 
*  if they exist
*
*  This methods allocates dynamic memory for the array of doubles
*  returned.  The caller of this method is responsible for
*  calling free to release this memory when finished with it.
*
*  If nrow = 0, returns NULL pointer. 
*
*  If parcode not found, returns array of missing
*
*  If cedarp is a header or catalog record; warning is printed to std err
*  and returns NULL
*/

double * cedarGet2dParm(Int16 *cedarp, int parcode)
 
 

/***********************************************************************
*
* cedarGet2dParmValue   gets a single scaled 2D parameter from a madrigal record
*
*  If 2D parm does not exist, returns double "missing"
*  If 2D parm = -32767 (missing), returns double "missing"
*  If 2D parm is an error code, and = -32766 (assumed), returns double "assumed"
*  If 2D parm is an error code, and = +32767 (known bad), returns double "knownbad"
*  If row is greater than number of 2d rows, returns double "missing"
*
*  Otherwise, scales value and includes additional increment values 
*  if they exist
*
*  This method differs from cedarGet2dParm in that it only returns a 
*  single double from a single row, so no malloc/free is required.
*
*
*  If cedarp is a header or catalog record; warning is printed to std err
*  and returns missing
*/

double cedarGet2dParmValue(Int16 *cedarp, int parcode, int row)
 
 

/***********************************************************************
*
* cedarGetFlatParm   creates a flattened 2D parameter 
*
*  If 1D parmeter exists, copies array of double of length nrow with
*  every value set to the 1D value.  If not, uses cedarGet2dParm.  Note
*  cedarGet2dParm returns all "missing" is parm not found. 
*
*  This methods allocates dynamic memory for the array of doubles
*  returned.  The caller of this method is responsible for
*  calling free to release this memory when finished with it.
*
*  If nrow = 0, returns NULL pointer. 
*
*  If parcode not found, returns array of missing
*/

double * cedarGetFlatParm(Int16 *cedarp, int parcode)
 
 

/***********************************************************************
*
* hasData   determines whether any non-missing data in a double array
*
*  Returns 0 if all data in 2d array of length nrow is missing, 1 
*  otherwise.
*/

int hasData(int nrow, double * parp)
 
 

/***********************************************************************
*
* cedarGetParmCodeArray   gets parameter codes of all parameters 
*                         in specp->pparms which are actually available
*                         from the current record.
*
*  User is responsible for calling free to release the returned
*  array of ints when finished with them.
*
*  Deprecated - use Maddata module instead
*/

int * cedarGetParmCodeArray(Int16 *cedarp, Ffspec *specp, int *nlines) {
 
 

/***********************************************************************
*
* cedarGetParmArray   flattens a subset of a CEDAR file
*
*  If record is rejected, nlinesp will be set to 0; returned
*     double array will be set to random values.
*
*  User is responsible for calling free to release the returned
*  array of doubles when finished with them.
*
*  Deprecated - use Maddata module
*/

double * cedarGetParmArray(Int16 *cedarp, Ffspec *specp, int *nlinesp) 
 
 

/***********************************************************************
*
* cedarGetGeodetic   gets geodetic coordinates from radar coordinates
* 
*  cedarGetGeodetic modifies the three arrays of doubles to return
*  lat, long, and alt.  Length of each array is nrows.  Geodetic
*  coordinates will be calculated in any of the following ways:
*
*     1) az, el, and range - az from azm, az1, or az2, and
*        el from elm, el, or el2
*     2) (altb, alte, or gdalt), gdlat and glon 
*     3) (altb, altav or altb, alte, or gdalt) alone - lat and long assumed
*        to be that of instrument
*     
*     If all three methods fail, all three arrays populated with missing
*
*     All parameters can be either 1d or 2d.   
*      
*  This methods allocates dynamic memory for the array of doubles
*  modified.  The caller of this method is responsible for
*  calling free to release the memory from these 3 arrays when 
*  finished with them.
*
*  If nrow = 0, returns -1. 
*/

int cedarGetGeodetic(Int16 *cedarp, double **gdlatpp, double **glonpp, double **gdaltpp)
 
 

/***********************************************************************
*
* cedarGet1dInt   gets a 1D parameter (unscaled) from a madrigal record
*
*/

Int16 cedarGet1dInt(Int16 *cedarp, int parcode)
 
 

/***********************************************************************
*
* cedarGet2dInt   gets a 2D parameter (unscaled) from a madrigal record
*
*  This method allocates dynamic memory for the array of ints
*  returned.  The caller of this method is responsible for
*  calling free to release this memory when finished with it.
*
*  If nrow = 0, returns NULL pointer. 
*
*  If parcode not found, returns array of missingData
*/

Int16 * cedarGet2dInt(Int16 *cedarp, int parcode)
 
 

/***********************************************************************
*
* cedarGet2dIntValue   gets a single 2D parameter (unscaled) from a madrigal record
*
*  This method differs from cedarGet2dInt in that it only returns
*  a single unscaled Int16 from a particular row.
*
*  If nrow = 0,  or row > number of 2d rows, returns missingData. 
*
*  If parcode not found, returns missingData
*/

Int16 cedarGet2dIntValue(Int16 *cedarp, int parcode, int row)
 
 

/***********************************************************************
*
* cedarCreateRecord  creates a new Cedar record   
*
*   User is responsible for freeing dynamically allocated array
*   when finished with it.
*/

Int16 *cedarCreateRecord(int lprol, int jpar, int mpar, int nrow,
                         int krec, int kinst, int kindat,
                         int year1, int month1, int day1,
                         int hour1, int minute1, int second1, int centisecond1,
                         int year2, int month2, int day2,
                         int hour2, int minute2, int second2, int centisecond2)
 
 

/***********************************************************************
*
* cedarCreateCatalogRecord  creates a new Cedar Catalog record
*
*   This method creates a catalog record with or without the actual text.
*   Users can also append text to this record by calling cedarAppendCatalogRecord
*
*   Inputs: 
*
*       kinst - instrument code from instTab.txt
*       modexp - code describing the mode of the experiment
*       year1, month1, day1, hour1, minute1, second1, centisecond1 - starting time 
*           of experiment
*       year2, month2, day2, hour2, minute2, second2, centisecond2 - starting time 
*           of experiment
*       text - text to append.  See Cedar database format for suggested layout.
*              Must be multiple of 80 characters in length - no line feeds.  May
*              be empty, if user is planning to use cedarAppendCatalogRecord.
*
*   Returns - pointer to Int16 holding newly allocated catalog record. User is 
*   responsible for freeing dynamically allocated array
*   when finished with it.
*/

Int16 *cedarCreateCatalogRecord(int kinst, int modexp,
                                int year1, int month1, int day1,
                                int hour1, int minute1, int second1, int centisecond1,
                                int year2, int month2, int day2,
                                int hour2, int minute2, int second2, int centisecond2,
				char * text)
 
 

/***********************************************************************
*
* cedarAppendCatalogRecord  appends text to an existing Catalog Record
*
*   Users should first create a catalog record by calling cedarCreateCatalogRecord
*
*   Inputs: 
*
*       Int16 *cedarp - pointer to existing catalog record
*       char * text - text to append.  See Cedar database format for suggested layout.
*                     Must be multiple of 80 characters in length - no line feeds. 
*
*   Returns: 0 if success, -1 if failure 
*
*/
int cedarAppendCatalogRecord(Int16 **cedarpp, char * text)
 
 

/***********************************************************************
*
* cedarCreateHeaderRecord  creates a new Cedar Header record
*
*   This method creates a header record with or without the actual text.
*   Users can also append text to this record by calling cedarAppendHeaderRecord
*
*   Inputs: 
*
*       kinst - instrument code from instTab.txt
*       kindat - code describing the kind of data
*       year1, month1, day1, hour1, minute1, second1, centisecond1 - starting time 
*           of experiment
*       year2, month2, day2, hour2, minute2, second2, centisecond2 - starting time 
*           of experiment
*       jpar - number of single-valued parameters in accompanying data records
*       mpar - number of multiple-valued parameters in accompanying data records
*       text - text to append.  See Cedar database format for suggested layout.
*              Must be multiple of 80 characters in length - no line feeds.  May
*              be empty, if user is planning to use cedarAppendHeaderRecord.
*
*   Returns - pointer to Int16 holding newly allocated header record. User is 
*   responsible for freeing dynamically allocated array
*   when finished with it.
*/

Int16 *cedarCreateHeaderRecord(int kinst, int kindat,
                                int year1, int month1, int day1,
                                int hour1, int minute1, int second1, int centisecond1,
                                int year2, int month2, int day2,
                                int hour2, int minute2, int second2, int centisecond2,
				int jpar, int mpar,
				char * text)
 
 

/***********************************************************************
*
* cedarAppendHeaderRecord  appends text to an existing Header Record
*
*   Users should first create a header record by calling cedarCreateHeaderRecord
*
*   Inputs: 
*
*       Int16 *cedarp - pointer to existing header record
*       char * text - text to append.  See Cedar database format for suggested layout.
*                     Must be multiple of 80 characters in length - no line feeds. 
*
*   Returns: 0 if success, -1 if failure 
*
*/
int cedarAppendHeaderRecord(Int16 **cedarpp, char * text)
 
 

/***********************************************************************
*
* cedarSetKrec   sets Kind of record
*
*/

int cedarSetKrec (Int16 *cedarp, int krec)
 
 

/***********************************************************************
*
* cedarSetKinst   sets instrument code for these data
*
*/

int cedarSetKinst (Int16 *cedarp, int kinst)
 
 

/***********************************************************************
*
* cedarSetKindat   sets kind-of-data code
*
*/

int cedarSetKindat (Int16 *cedarp, int kindat)
 
 

/***********************************************************************
*
* cedarSetStartTime   sets start time of record
*
*/

int cedarSetStartTime (Int16 *cedarp, int year, int month, int day,
                       int hour, int minute, int second, int centisecond)
 
 

/***********************************************************************
*
* cedarSetEndTime   sets end time of record
*
*/

int cedarSetEndTime (Int16 *cedarp, int year, int month, int day,
                     int hour, int minute, int second, int centisecond)
 
 

/***********************************************************************
*
* cedarSet1dParm   sets a 1D parameter in a Cedar record
*
*   Inputs:
*       Int16 *cedarp - pointer to existing Cedar record
*       int parcode   - Cedar parmater code
*       double parm   - doubles containing value to set.Special values
*                       may be set by setting values to #defines
*                       missing, assumed, or knownbad
*       int index     - index of which 2d parameter to set
*
*   Returns 1 if failure, 0 if success.  If value out of Int16 range, will set
*   value to missing and return failure.
*/

int cedarSet1dParm(Int16 *cedarp, int parcode, double parm, int index)
 
 

/***********************************************************************
*
* cedarSetNorm1dParm   sets a 1D parameter in a Cedar record with the units
*                      of the standard parameter even if additional increment parameter
*
*   Inputs:
*       Int16 *cedarp - pointer to existing Cedar record
*       int parcode   - Cedar parmater code
*       double parm   - doubles containing value to set.Special values
*                       may be set by setting values to #defines
*                       missing, assumed, or knownbad
*       int index     - index of which 2d parameter to set
*
*   Returns 1 if failure, 0 if success.  If value out of Int16 range, will set
*   value to missing and return failure.
*/

int cedarSetNorm1dParm(Int16 *cedarp, int parcode, double parm, int index)
 
 

/***********************************************************************
*
* cedarSet2dParm   sets all values for a 2D parameter in a cedar record
*
*   Inputs:
*       Int16 *cedarp - pointer to existing Cedar record
*       int parcode   - Cedar parmater code
*       double *parmp - array of doubles containing values to set.  Length
*                       must be nrow.  Special values may be set by setting
*                       values to #defines missing, assumed, or knownbad
*       int index     - index of which 2d parameter to set
*
*   Returns 1 if failure, 0 if success. If any value out of Int16 range, will set
*   value to missing, but all valid values will still be set.  Returns 1 if any
*   out of range data found.
*
*/

int cedarSet2dParm(Int16 *cedarp, int parcode, double *parmp, int index)
 
 

/***********************************************************************
*
* cedarSetNorm2dParm   sets all values for a 2D parameter in a cedar record
*                      with the units as standard parameter even if 
*                      additional increment parameter
*
*   Inputs:
*       Int16 *cedarp - pointer to existing Cedar record
*       int parcode   - Cedar parmater code
*       double *parmp - array of doubles containing values to set.  Length
*                       must be nrow.  Special values may be set by setting
*                       values to #defines missing, assumed, or knownbad
*       int index     - index of which 2d parameter to set
*
*   Returns 1 if failure, 0 if success. If any value out of Int16 range, will set
*   value to missing, but all valid values will still be set.  Returns 1 if any
*   out of range data found.
*
*/

int cedarSetNorm2dParm(Int16 *cedarp, int parcode, double *parmp, int index)
 
 

/***********************************************************************
*
* cedarSet1dInt   puts a 1D parameter (unscaled) into a madrigal record
*
*/

int cedarSet1dInt(Int16 *cedarp, int parcode, Int16 int1d, int index)
 
 

/***********************************************************************
*
* cedarSet2dInt   puts a 2D parameter (unscaled) into a madrigal record
*
*/

int cedarSet2dInt(Int16 *cedarp, int parcode, Int16 *int2dp, int index)
 
 

/***********************************************************************
*
* cedarPrintRecord   prints cedar record
*
*/

int cedarPrintRecord(Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarGetInformation   gets Ascii Information from Catalog or Header record
*
*   inputs:  Int16 * cedarp (pointer to cedar record)
*
*   outputs:  char * (pointer to dynamically allocated string holding
*             ASCII text in catalog or header record, or empty string
*             if no text available.  Will return empty string if called
*             with a data record instead of a header or catalog record).
*             The string will have 81 characters for each line of
*             information - the first 80 characters will be the 80
*             characters in the file with unprintable characters converted
*             to spaces, and the 81st character a newline. After the last
*             line a null character is added to make a valid c string.
*
*  The user is resposible for freeing the returned string when finished
*  with it.
*
*/

char * cedarGetInformation(Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarPrintProlog   prints cedar record prolog
*
*/

int cedarPrintProlog(Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarReadParCodes   reads the following metadata tables:
*
*  1. parcods.tab  (parameter information)
*  2. instTab.txt   (instrument location)
*  3. madCatTab.txt  (parameter category information)
*
*   For the moment hard-coded to the column layout of parcods.tab
*      0-7     Code
*      10-48   Description   Note: DESC_LEN   = 40
*      50-60   Int16Desc     Note: DESC16_LEN = 12
*      62-68   ScaleFactor
*      70-77   Units         Note: UNIT_LEN   =  9
*      81-100  Mnemonic      Note: MNEM_LEN   = 21
*      105-112 Format
*      114-115 Width
*      118-120 CatId
*      122-122 hasDesc  - does this mnemonic have an html description?
*      124-124 hasErrDesc - does this error mnemonic have an html description?
*
*   Returns 0 if successful, non-zero and error set if not successful
*/

int cedarReadParCodes ()
 
 

/***********************************************************************
*
* cedarGetNumParCodes   Gets number of Cedar parameter codes in parcods.tab
*/

int cedarGetNumParCodes ()
 
 

/***********************************************************************
*
* cedarGetParCode   Gets Cedar parameter code from table given its position
*                   in file parcods.tab
*
*
*/

int 
cedarGetParCode (int position)
 
 

/***********************************************************************
*
* cedarGetParCodeIndex   Gets index of Cedar parameter code in table, given its code
*
*   For a pure Madrigal parameter with code 0, will return missing.  Use
*   madGetParMnemIndex instead.  For a negative parcode, will return negitive
*   of index found.  If not found, returns missingData.
*
*   No longer requires that parcods.tab be in order.
*/

int cedarGetParCodeIndex (int parcode)
 
 

/***********************************************************************
*
*  madGetParMnemIndex   Gets index of Madrigal parameter code in table, given its mnemonic
*
*   Returns the index of the specified mnemonic.  Matching is case-insensitive, and
*   ignores whitespace. If not found and begins with "D", will next search with "D"
*   removed, and return the negitive of the index found.  If still not found, 
*   returns missingData.
*
*/
int madGetParMnemIndex (char * mnem)
 
 

/***********************************************************************
*
*  isMadparmError   returns 1 if this is an error parm, 0 if standard, 
*                   -1 if neither
*
*
*/
int isMadparmError(const char * mnem)
 
 

/***********************************************************************
*
*  getStdMnem   converts a str to standard mnemonic form
*
*   Inputs: const char * mnem    - the string containing the mnemonic to be converted
*           char * stdMnem - a string to copy the standard form of the 
*                            mnemonic to.  Allocated by the user.  At most
*                            MNEM_LEN - 1 characters will be copied.  Std form
*                            strips all whitespace and is upper case.
*
*/

void getStdMnem (const char * mnem, char * stdMnem)
 
 

/***********************************************************************
*
* cedarGetParCodeType   Gets type of Cedar parameter code from table, given its code.
*
*   For a pure Madrigal parameter with code 0, will return first found.  Use
*   madGetParMnemType instead.  If not in parcods.tab but in a standard range,
*   as defined by madCatTab.txt will return Cedar values.  If not in parcodes
*   and not in any standard range, will return "Unknown Parameter Type"
*
*   User is responsible for freeing dynamically allocated string
*   when finished with it.
*/

char * cedarGetParCodeType (int parcode)
 
 

/***********************************************************************
*
* madGetParMnemType   Gets type of Madrigal parameter from table, given its mnemonic.
*
*   If mnemonic not in parcods.tab, will return "Unknown Parameter Type"
*
*   User is responsible for freeing dynamically allocated string
*   when finished with it.
*/

char * madGetParMnemType (char * mnem)
 
 

/***********************************************************************
*
* madGetCategoryIndex   Gets the index of a given Category name.
*
*   If category string not found, returns missingData
*   Matching is case and whitespace sensitive
*
*/

int madGetCategoryIndex (char * category)
 
 

/***********************************************************************
*
* cedarGetParDescription   Gets Cedar parameter code description from
* table
*
*   User is responsible for freeing dynamically allocated string
*   when finished with it.
*
*
*/

char * cedarGetParDescription (int parcode)
 
 

/***********************************************************************
*
* madGetParDescription   Gets Madrigal parameter code description from
* table, given mnemonic
*
*   User is responsible for freeing dynamically allocated string
*   when finished with it.
*
*/

char * madGetParDescription (char * mnem)
 
 

/***********************************************************************
*
* cedarGetParInt16Description   Gets Cedar parameter code Int16
*                               description from table
*
*   User is responsible for freeing dynamically allocated string
*   when finished with it.
*/

char * cedarGetParInt16Description (int parcode)
 
 

/***********************************************************************
*
* madGetParInt16Description   Gets Madrigal parameter code Int16
*                               description from table, given mnemonic
*
*/

char * madGetParInt16Description (char * mnem)
 
 

/***********************************************************************
*
* cedarGetParScaleFactor   Gets Cedar parameter scale factor from table
*
*/

double cedarGetParScaleFactor (int parcode)
 
 

/***********************************************************************
*
* madGetParScaleFactor   Gets Madrigal parameter scale factor from table,
*                        given mnemonic
*
*/

double madGetParScaleFactor (char * mnem)
 
 

/***********************************************************************
*
* cedarGetNormScaleFactor   Gets Cedar parameter scale factor, where additional
*                           increment parameters use the same units as main
*                           parameter.  Differs from cedarGetParScaleFactor, which
*                           returns scale factors for additional increment parameters
*                           that may have different units than the main parameter.
*
*/

double cedarGetNormScaleFactor (int parcode)
 
 

/***********************************************************************
*
* madGetNormScaleFactor   Gets Madrigal parameter scale factor, where additional
*                           increment parameters use the same units as main
*                           parameter.  Differs from cedarGetParScaleFactor, which
*                           returns scale factors for additional increment parameters
*                           that may have different units than the main parameter.
*
*/

double madGetNormScaleFactor (char * mnem)
 
 

/***********************************************************************
*
* cedarGetParUnits   Gets Cedar parameter code units from table
*
*   User is responsible for freeing dynamically allocated string
*   when finished with it.
*/

char * cedarGetParUnits (int parcode)
 
 

/***********************************************************************
*
* madGetParUnits   Gets Madrigal parameter code units from table,
*                  given mnemonic
*
*   User is responsible for freeing dynamically allocated string
*   when finished with it.
*/

char * madGetParUnits (char * mnem)
 
 

/***********************************************************************
*
* cedarGetParMnemonic   Gets Cedar parameter code mnemonic from table
*
*   User is responsible for freeing dynamically allocated string
*   when finished with it.  If parcode 0 passed in, first Madrigal
*   parameter found will be returned.
*
*   If unknown parcode passed in, mnemonic is atoi(parcode)
*/

char * cedarGetParMnemonic (int parcode)
 
 

/***********************************************************************
*
* cedarGetParCodeFromMnemonic   Gets Cedar parameter code given mnemonic 
*
*   If mnemonic is integer in form of string, returns that integer
*   If not found, returns missingData
*
*/

int cedarGetParCodeFromMnemonic (char * mnem)
 
 

/***********************************************************************
*
* cedarGetParFormat   Gets Cedar parameter code format from table.
*
*   If not found, returns NULL
*/

char * cedarGetParFormat (int parcode)
 
 

/***********************************************************************
*
* madGetParFormat   Gets Madrigal parameter format from table (given mnemonic)
*
*   If not found, returns NULL
*/

char * madGetParFormat (char * mnem)
 
 

/***********************************************************************
*
* cedarGetParWidth   Gets Cedar parameter field width from table
*
*    If unknown, returns default value of 11
*
*/

int cedarGetParWidth (int parcode)
 
 

/***********************************************************************
*
* madGetParWidth   Gets Madrigal parameter field width from table,
*                  given mnemonic
*
*    If unknown, returns default value of 11
*/

int madGetParWidth (char * mnem)
 
 

/***********************************************************************
*
* cedarHasHtmlDesc   Returns 1 if parameter has entry in Html description
*                    page, 0 if not.  Works also for error codes (< 0)
*
*    If unknown, returns default value of 0
*
*/

int cedarHasHtmlDesc(int parcode)
 
 

/***********************************************************************
*
* madHasHtmlDesc   Returns 1 if mnemonic has entry in Html description
*                    page, 0 if not.  Works also for error mnemonics.
*
*    If unknown, returns default value of 0
*/

int madHasHtmlDesc (char * mnem)
 
 

/***********************************************************************
*
* cedarCheckRecord   checks cedar record for consistency
*
*/

int cedarCheckRecord (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarHexPrintRecord   prints hex version of record
*
*/

int cedarHexPrintRecord (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarDecimalPrintRecord   prints hex version of record
*
*/

int cedarDecimalPrintRecord (Int16 *cedarp)
 
 

/***********************************************************************
*
* cedarSetError   sets cedar error
*
*/

int cedarSetError (const char *error) 
 
 

/***********************************************************************
*
* cedarGetError   gets last cedar error
*
*/

char * cedarGetError ()
 
 

/***********************************************************************
*
* cedarTabInt   linear interpolation routine
*
* tabint interpolates linearly to calculate y(x) from a table
* containing nt independent variable values xt and dependent
* variable values yt. the xt are assumed to be in non-decreasing
* order.
*
*/

double cedarTabInt (int nt, double *xt, double *yt, double x, double badval)
 
 

/***********************************************************************
*
* cedarUpdateParmsList   Updates list of parameters and their minimum
*                        and maximum values
*
*   The first eleven parameters are effectively derived:
*   [0] 10: year, [1] 11: month,  [2] 12: day
*   [3] 13: hour, [4] 14: minute, [5] 15: second, 
*   [6] 16: centisecond [7] 34: uth, [8] 160: gdlat, 
*   [9] 170: glon, [10] 110: gdalt 
*
*   All parameters actually in the file will be listed starting with
*   the 12th.  If any of the above parameters are actually in the file
*   itself, they will appear again.
*
*   Does not include data from 2D rows if all error parameters are
*   missing of knownbad
*
*   Also updates earliestStartTime, latestEndTime, and lists of
*   all kinsts and kindats found in file.
*
*   If header or catalog record, returns 0 immediately without making
*   any changes.
*
*/

int cedarUpdateParmsList(Int16 *cedarp, int *numParmsp,
                         int *parmsListpp[], int *parmLocpp[],
                         double *parmMinpp[], double *parmMaxpp[], int *parmMissing[],
                         int *startJday0, 
                         double * earliestStartTime, double * latestEndTime,
                         int * numKinst, int * kinstArr,
                         int * numKindat, int * kindatArr)
 
 

/***********************************************************************
*
*  cedarGetStationPos  Gets instrument coordinates for a given kinst
*
*      Uses data from metadata/instTab.txt
*
*/

void cedarGetStationPos(int kinst, double * lat, double * lon, double * alt)
 
 

/***********************************************************************
*
*  cedarGetStationName  Gets instrument name for a given kinst
*
*      Uses data from metadata/instTab.txt
*
*/

char * cedarGetStationName(int kinst)
 
 

/***********************************************************************
*
* searchFilesByDate    searches the metadata for all files between
*                      starttime and endtime. 
*
*   arguments: 
*       double starttime: seconds since 1/1/1950.
*       double endtime: seconds since 1/1/1950.
*       int * numFilesFound: pointer to int, set to number of files found
*       char ** fileList: pointer to char pointer to be allocated and
*                         populated with a comma-delimited list of full
*                         paths to files found
*       double ** fileStarttime: pointer to double array to be allocated and
*                                populated with start times of each file found
*                                (number of seconds since 1/1/1950)
*       double ** fileEndtime: pointer to double array to be allocated and
*                              populated with end times of each file found
*                              (number of seconds since 1/1/1950)
*
*       To be found, the file must start after starttime and end before endtime.
*       File must also be a default file.
*
*       User must free fileList, fileStarttime, fileEndtime if numFilesFound > 0
*
*   returns: 0 if success, non-zero and error set if not successful
*
*/
int searchFilesByDate(double starttime,
                      double endtime,
                      int * numFilesFound,
                      char ** fileList,
                      double ** fileStarttime,
                      double ** fileEndtime)
 
 

/***********************************************************************
*
* loadExpFileTable    Loads data from expTab.txt and fileTab.txt into
*                     global data. Private method - do not call directly.
*
*   arguments: None
*
*   returns: 0 if success, non-zero and error set if not successful
*
*   Affects: loads global data that deals with expTab and fileTab
*
*/
int loadExpFileTable()
 
 

/***********************************************************************
*
* goodDataExists    Returns 1 if record contains valid data at 2d parameter
*                   index index2D.  Valid data is when the absolute value
*                   of any error parameter is not missing or knownbad.  
*                   If no error parameters, always returns 1.
*                   0 otherwise. 
*
*   arguments:
*       record pointer to Madrigal record
*       index into 2D parameter values
*
*   returns:
*       1 if 2d index contains valid data,
*       0 if not
*
*/ 
int goodDataExists(Int16 * recordp, int index2D)
 
 

/***********************************************************************
* 
* sprod calculates the scalar product of two vectors a and b,
* sprod = a .dot. b.
*/
double
sprod(double *a, double *b)
 
 

/***********************************************************************
*      
* vadd calculates the sum of two vectors a and b, c = a + b.
*/
int 
vadd(double *a, double *b, double *c)
 
 

/***********************************************************************
*
* vsub calculates the difference of two vectors a and b, c = a - b.
*/
int
vsub(double *a, double *b, double *c)
 
 

/***********************************************************************
*
* csconv converts between cartesian coordinates x,y,z and spherical
* coordinates r,theta,phi. if imode=1, (x,y,z) -> (r,theta,phi).
* if imode=2, (r,theta,phi) -> (x,y,z). theta and phi are in
* degrees.
*/
int
csconv(double *xp, double *yp, double *zp,
       double *rp, double *thetap, double *phip,
       int imode)
 
 

/***********************************************************************
*
* vctcnv converts between the cartesian and spherical coordinate
* representations of a vector field f. (fx,fy,fz) are the
* components of the field at (x,y,z). (fr,ft,fp) are the
* components of the field at (r,theta,phi) in the directions of
* increasing r, increasing theta and increasing phi. if imode=1,
* (fx,fy,fz,x,y,z) -> (fr,ft,fp,r,theta,phi). if imode=2,
* (fr,ft,fp,r,theta,phi) -> (fx,fy,fz,x,y,z). theta and phi are
* in degrees.
*/
int
vctcnv(double *fxp, double *fyp, double *fzp,
       double *xp, double *yp, double *zp,
       double *frp, double *ftp, double *fpp,
       double *rp, double *thetap, double *phip,
       int imode)
 
 

/***********************************************************************
*
* point calculates the position of a point defined by the radar
* line-of sight vector to that point.
* 
* input parameters
*    sr    - distance of station from center of earth (km)
*    slat  - geocentric latitude of station (deg)
*    slon  - longitude of station (deg)
*    az    - radar azimuth (deg)
*    el    - radar elevation (deg)
*    range - radar range (km)
* 
* output parameters
*    pr    - distance from center of earth of observation point (km)
*   glat  - observation point geocentric latitude (deg)
*    glon  - observation point longitude (deg)
*/
int
point(double *srp, double *slatp, double *slonp,
      double *azp, double *elp, double *rangep,
      double *prp, double *glatp, double *glonp)
 
 

/***********************************************************************
*
* look calculates the azimuth, elevation and range from a radar
* of a specified point.
* 
* input parameters
*    sr    - distance of station from center of earth (km)
*    slat  - geocentric latitude of station (deg)
*    slon  - longitude of station (deg)
*    pr    - distance from center of earth of observation point (km)
*    glat  - observation point geocentric latitude (deg)
*    glon  - observation point longitude (deg)
* 
* output parameters
*    az    - radar azimuth (deg)
*    el    - radar elevation (deg)
*    range - radar range (km)
*/
int
look(double *srp, double *slatp, double *slonp,
     double *prp, double *glatp, double *glonp,
     double *azp, double *elp, double *rangep)
 
 

/***********************************************************************
*
* convrt converts between geodetic and geocentric coordinates. the
* reference geoid is that adopted by the iau in 1964. a=6378.16,
* b=6356.7746, f=1/298.25. the equations for conversion from
* geocentric to geodetic are from astron. j., vol 66, 1961, p. 15.
*      i=1   geodetic to geocentric
*      i=2   geocentric to geodetic
*    gdlat   geodetic latitude (degrees)
*    gdalt   altitude above geoid (km)
*    gclat   geocentric latitude (degrees)
*      rkm   geocentric radial distance (km)
*/
int
convrt(int i, double *gdlatp, double *gdaltp,
       double *gclatp, double *rkmp)

 
 

/***********************************************************************
*
* rpcart computes the components (rfx,rfy,rfz) relative to an earth
* centered cartesian coordinate system of the radar line of sight
* vector from a radar with coordinates sr (distance from center
* of earth), slat (geocentric latitude) and slon (longitude). the
* observation point is specified by az (azimuth), el (elevation) and
* range (range). the cartesian coordinates of the observation
* point are returned in (pfx,pfy,pfz).
*    input - sr,slat,slon,az,el,range
*    output - rfx,rfy,rfz,pfx,pfy,pfz
*/
int
rpcart (double *srp,  double *slatp, double *slonp,
        double *azp,  double *elp,   double *rangep,
        double *rfxp, double *rfyp,  double *rfzp,
        double *pfxp, double *pfyp,  double *pfzp)
 
 

/***********************************************************************
*
* gdv converts a vector field f at geodetic latitude gdlat and
* geocentric latitude gclat from a geocentric based representation
* to a geodetic based representation. the geocentric components
* are fr (radial outward), ft (increasing geocentric colatitude,
* e.g. southward) and fp (increasing east longitude). the
* geodetic components are fx (northward, parallel to surface of
* earth), fy (eastward, parallel to surface of earth) and fz
* (downward, perpendicular to surface of earth). fr,ft,fp thus
* correspond to spherical coordinates r,theta,phi, with their
* origin at the center of the earth. x,y,z are the coordinates
* customarily used to describe the three components of the
* geomagnetic field. fp and fy are the same.
*/
int
gdv(double *gdlatp, double *gclatp,
    double *frp, double *ftp, double *fpp,
    double *fxp, double *fyp, double *fzp)
 
 

/***********************************************************************
*
* los2geodetic calculates the position of a point defined by an instrument
* line-of sight vector to that point. This is a convenience routine in
* which the instrument location is specified by its CEDAR instrument code
* and which returns the geodetic coordinates of the point.
* 
* 
* input parameters
*    kinst - instrument code in metadata
*    az    - radar azimuth (deg)
*    el    - radar elevation (deg)
*    range - radar range (km)
* 
* output parameters
*    gdlat - observation point geodetic latitude (deg)
*    glon  - observation point longitude (deg)
*    gdalt - altitude above geoid (km)
*/

int los2geodetic(int kinst, double az, double el, double range,
             double *gdlatp, double *glonp, double *gdaltp)
 
 

/***********************************************************************
*
* solarzen_az calculates the solar zenith and az angles for a given time, gdlat,
*          and glon.
* 
* 
* input parameters
*    double ut    - Universal time in seconds since 1950
*    double gdlat - geodetic latitude in degrees
*    double glon  - geodetic longitude in degrees
* 
* output parameters
*    szen - solar zenith angle (deg, 0=directly overhead)
*    saz  - solar azimuth angle (deg, N=0, E=90)
*    
*
*    Solar zenith angle is calculated at 0 alt, although this changes
*    very little with altitute.  No atmospheric correction is applied.
*    This method uses solpos.c, written by National Renewable Energy
*    Laboratory.
*
*    This method is a modified version of  stest.c found at 
*    http://rredc.nrel.gov/solar/codes_algs/solpos/
*/
void solarzen_az(double ut, double gdlat, double glon, double * szen, double * saz)
 
 

/***********************************************************************
*
* solardist calculates the distance in km from the center of the earth
*          to the center of the sun at time ut.
* 
* 
* input parameters
*    double ut    - Universal time in seconds since 1950
* 
* returns double - distance in km from the center of the earth
*          to the center of the sun at time ut
*
*    This method is taken from "Practical Astronomy with Your
*    Calculator" 2nd edition, Peter Duffett-Smith, p. 80-87.
*/
double solardist(double ut)
 
 

/***********************************************************************
*
* shadowheight calculates the distance directly above any gdlat and glon
*              for a given UT in km at which the earth's shadow terminates.
*              Will be 0.0 on dayside of earth.
* 
* 
* input parameters
*    double ut    - Universal time in seconds since 1950
*    double gdlat - geodetic latitude in degrees
*    double glon  - geodetic longitude in degrees
* 
* returns double - distance directly above any gdlat and glon
*              for a given UT in km at which the earth's shadow terminates.
*              Will be 0.0 on dayside of earth.
*
*    This method uses the results of solarzen and solardist to create a simple
*    cone on a sphere model of the earth's shadow.  Shadow height is defined as
*    the lowest elevation at which any part of the sun can be seen.  No atmospheric
*    bending of light is included. The radius of the earth is calculated at the 
*    tan point of the sun's rays.
*
*    Algorithm:
*
*    Solar Zenith Angle = Z
*    Solar Azimuth      =Az (0 = North, 90 = East)
*
*    Get latitude of tangent point of sun's rays:
*
*      = gdlat + cos(Az)*(Z-90.0)
*
*    Get earthRadius at that point using convrt at gdlat = 0 (sea level)
*
*    ConeHalfAngle = C = atan((sunRadius - earthRadius)/soldist)
*
*
*                               (cos Z tan C + 1 - sin Z)
*    Shadowheight = earthRadius -------------------------
*                                  (sin Z - cos Z tan C)
*
*    Daytime (Shadowheight = 0) if numerator negitive or if
*    Z <= 91.0.
*/
double shadowheight(double ut, double gdlat, double glon)
 
 

/***********************************************************************
*
* sunrise_set calculates the time UT  of ionospheric sunrise
*             and sunset.
* 
* 
* input parameters
*    double ut    - Universal time in seconds since 1950
*    double gdlat - geodetic latitude in degrees
*    double glon  - longitude in degrees
*    double gdalt - geodetic altitude in km
*    double * sunrise - pointer to double allocated by user to be
*                       set to sunrise time UT 
*    double * sunset - pointer to double allocated by user to be
*                       set to sunset time UT 
* 
* returns void
*
*    If either sunrise or sunset not found, set to missing.
*    All times in seconds since 1/1/1950
*
*  Algorithm:
*
*    Depends on shadowheight calculation, so limitations discussed
*    there apply (atmospheric bending of light ignored).
*
*    If glon < 0:
*      solar midnight = UT - glon*24/360
*      solar noon     = UT + 12 - glon*24/360
*
*    If sun is not set at solar midnight (defined by shadowheight > gdalt),
*    or sun not up at solar noon, check if any difference between 0 and 24 UT.
*    If so, find only one of sunrise and sunset as described below, and set
*    the other to missing.  If not, return missing for both, because that point
*    is either in the sun or in the shadow all day.  The user must determine
*    which by comparing shadowheight and gdalt.  Otherwise, seek sunrise
*    between solar midnight and solar noon, slice remaining time in half each
*    guess.  Stop when time step less than 1 minute.
*    
*            If sun is up at 0 UT that day:  Seek sunset between 0.0 and
*    solar midnight as above.
*            Else: Seek sunset between solar noon and 24.0 as above.
*    
*    Else if glon > 0:
*        solar midnight = UT + 24 - glon*24/360
*        solar noon     = UT + 12 - glon*24/360
*    
*    If sun is not set at solar midnight (defined by shadowheight > gdalt),
*    or sun not up at solar noon, check if any difference between 0 and 24 UT.
*    If so, find only one of sunrise and sunset as described below, and set
*    the other to missing.  If not, return missing for both, because that point
*    is either in the sun or in the shadow all day.  The user must determine
*    which by comparing shadowheight and gdalt.  Otherwise, seek sunset
*    between solar noon and solar midnight, slice remaining time in half each
*    guess.  Stop when time step less than 1 minute.
*    
*            If sun is up at 0 UT that day:  Seek sunrise between solar
*    midnight and 24.0 as above.
*    
*            Else: Seek sunrise between 0.0 and solar noon as above.
*
*   Note: day is divided 11 times to ensure greater than one minute resolution.
*/
void sunrise_set(double ut,
                 double gdlat, 
                 double glon,
                 double gdalt,
                 double * sunrise,
                 double * sunset)
 
 

/***********************************************************************
*  jday  - returns Julian day number given day, month, and year
*  
*    Julian day 0 is Nov. 24, -4713
*
*    Returns -1 if illegal year, month, day passed in
*/
int jday(int day, int month, int year)
 
 

/***********************************************************************
*  jdater - sets day, month and year given jdayno
* 
*    Inverse of jday method
*/
    int jdater (int jdayno, int *day, int *month, int *year)
 
 

/***********************************************************************
*
*  idmyck - returns 0 if valid day, month, and year
*
*/
int idmyck(int day, int month, int year)
 
 

/***********************************************************************
*
* dmadptr - returns number of seconds since 1/1/1950 for iyr, imd, ihm, ics
*           as double
*
*    Can retain fractions of a second
*
*    Inputs: iyr - year
*            imd - month/day as integer mmdd
*            ihm - hour/min as integer hhmm
*            ics - centiseconds since last minute
*
*/
double dmadptr(int iyr, int imd, int ihm, int ics)
 
 

/***********************************************************************
*
* getKey - returns number of seconds since 1/1/1950 for year,
*          month, day, hour, minute, second
*
*/
double getKey(int year, int month, int day,
           int hour, int minute, int second)
 
 

/***********************************************************************
*
* dinvmadptr - sets iyr, imd, ihm, ics given dmadptr (number of seconds 
*              as a double since 1/1/1950)
*
*    inverse of dmadptr.  
*
*    Returns 0 if success, 1 if failure (negitive dmadptr)
*    Uses time.h, and constant to shift from 1/1/1970 to 1/1/1950
*/
int dinvmadptr(double dmadptr, int * iyr, int * imd, int * ihm, int * ics)
 
 

/***********************************************************************
*
* madGetDayno - gets day number (1-366) given year, month, and day
*
*
*    Returns -1 if illegal year, month, day passed in
*/
int madGetDayno(int year, int month, int day)
 
 


The maddata module

createMadparmList copyMadparmList destroyMadparmList appendMadparm hasParm
isErrorParm getIndex getMinParm getMaxParm analyzeFileParms
getDerivedParms createMadfilterList destroyMadfilterList appendMadfilter copyMadfilterList
getMadfilterListFromStr createMaddata createNonfileMaddata destroyMaddata appendMadrecParmType
appendMadcycle createMadcycle destroyMadcycle createMadrecord destroyMadrecord
simpleMadrecordPrint simpleMadfilterPrint simpleMaddataPrint classicIsprint printIsprintHeader
getIsprintHeader printIsprintLabel getIsprintLabel classicMadrecordPrint getClassicMadrecordStrings
lookerMadrecordPrint populate1DDataFromStr populate2DDataFromStr

/***********************************************************************
*
* createMadparmList   initializes a new MadparmList
*
*   arguments: None
*
*   returns - pointer to newly created MadparmList.  Use 
*             destroyMadparmList when done
*/
MadparmList * createMadparmList()
 
 

/***********************************************************************
*
* copyMadparmList   copies an existing MadparmList
*
*   arguments: Pointer to existing MadparmList
*
*   returns - pointer to newly copied MadparmList, newly allocated
*             on the heap.  Use destroyMadparmList when done
*             Returns NULL if NULL passed in
*/
MadparmList * copyMadparmList(MadparmList * madparmList)
 
 

/***********************************************************************
*
* destroyMadparmList   releases an existing MadparmList
*
*   arguments: pointer to existing MadparmList
*
*   returns - void
*/
void destroyMadparmList(MadparmList * madParmList)
 
 

/***********************************************************************
*
* appendMadparm   adds a new parameter to the MadparmList
*
*   arguments: MadparmList * madparmList - pointer to existing MadparmList
*              const char * mnem - string containing name
*
*      mnem is copied into newly allocated memory, so user is free to
*      release mnem after this method.  mnem converted to standard form.
*
*   returns - 0 if success, -1 if failure (if mnem too long or unknown)
*/
int appendMadparm(MadparmList * madparmList, const char * mnem)
 
 

/***********************************************************************
*
*  hasParm   returns 1 if madparmList has parameter, 0 otherwise
*
*   arguments: MadparmList * madparmList - pointer to existing MadparmList
*              const char * mnem - string containing name
*
*      comparision is done after coverting mnem to standard form
*
*   returns - 0 if success, -1 if failure (if mnem too long)
*/
int hasParm(MadparmList * madparmList, const char * mnem)
 
 

/***********************************************************************
*
*  isErrorParm   returns 1 parameter at index is error, 0 if standard
*
*   arguments: MadparmList * madparmList - pointer to existing MadparmList
*              int index - index into madparmList
*
*   returns - 1 parameter at index is error, 0 if standard, -1 if
*             index out of bounds
*/
int isErrorParm(MadparmList * madparmList, int index)
 
 

/***********************************************************************
*
*  getIndex   returns index of parameter if found, -1 otherwise
*
*   arguments: MadparmList * madparmList - pointer to existing MadparmList
*              const char * mnem - string containing name
*
*      comparision is done after coverting mnem to standard form
*
*   returns - index of parameter if found, -1 otherwise
*/
int getIndex(MadparmList * madparmList, const char * mnem)
 
 

/***********************************************************************
*
*  getMinParm   returns minimum value of mnem, or missing if unknown
*
*   arguments: MadparmList * madparmList - pointer to existing MadparmList
*              char * mnem - string containing name of parameter
*
*   returns - minimum value of mnem, or missing if unknown or not in list
*/
double getMinParm(MadparmList * madparmList, char * mnem)
 
 

/***********************************************************************
*
*  getMaxParm   returns maximum value of mnem, or missing if unknown
*
*   arguments: MadparmList * madparmList - pointer to existing MadparmList
*              char * mnem - string containing name of parameter
*
*   returns - maximum value of mnem, or missing if unknown or not in list
*/
double getMaxParm(MadparmList * madparmList, char * mnem)
 
 

/***********************************************************************
*
* analyzeFileParms   get 4 lists of parameters from file:
*                     1) all 1D measured parameters
*                     1) all 2D measured parameters
*                     1) all 1D derivable parameters
*                     1) all 2D derivable parameters
*
*   arguments: char * filename - full path to file
*              MadparmList * list1DMeasParms - pointer to MadparmList to be
*                  populated with all 1D measured parameters found in file 
*              MadparmList * list2DMeasParms - pointer to MadparmList to be
*                  populated with all 2D measured parameters found in file 
*              MadparmList * list1DDervParms - pointer to MadparmList to be
*                  populated with all 1D parameters that could be derived
*              MadparmList * list2DDervParms - pointer to MadparmList to be
*                  populated with all 2D parameters that could be derived
*              FILE * errFile   - errFile to write an error messages to
*
*   returns - 0 if success, -1 otherwise
*
*   affects - populates the four input lists.  All four pointers should point
*   to NULL when passed in.  When done with these four lists, user should call
*   destroyMadparmList for each to free memory.
*
*   Note: Since a file may contain more than one type of record, these lists contain
*   parameters from any record that fits into each list.  For example, a certain 1D 
*   parameter is measured in one type of record, but can be derived from another type
*   where its not measured, that parameter would appear in both list1DMeasParms and
*   list1DDervParms.  
*
*   See also method getDerivableParms, which accepts two lists of measured 1D and 
*   measured 2D parameters, and returns two lists of derivable 1D and 
*   derivable 2D parameters.  Since this other method does not analyze a file, it does not
*   have the ambiguities of analyzeFileParms discussed above.
*/
int analyzeFileParms(char * filename, 
                     MadparmList ** list1DMeasParms,
                     MadparmList ** list2DMeasParms,
                     MadparmList ** list1DDervParms,
                     MadparmList ** list2DDervParms,
                     FILE * errFile)
 
 

/***********************************************************************
*
* getDerivedParms   gets a list of derivable parameters given a list of
*                   measured parameters
*
*   arguments: MadparmList * listMeasParms - pointer to MadparmList 
*                  containing measured parameters
*
*   returns - MadparmList * listDervParms - pointer to MadparmList 
*                  containing all parameters that could be derived.
*                  User is responsible for calling destroyMadparmList
*                  when done with this list
*
*/
MadparmList * getDerivedParms(MadparmList * listMeasParms)
 
 

/***********************************************************************
*
* createMadfilterList   initializes a new MadfilterList
*
*   arguments: None
*
*   returns - pointer to newly created MadfilterList.  Use 
*             destroyMadfilterList when done
*/
MadfilterList * createMadfilterList()
 
 

/***********************************************************************
*
* destroyMadfilterList   releases an existing MadfilterList
*
*   arguments: pointer to existing MadfilterList
*
*   returns - void
*/
void destroyMadfilterList(MadfilterList * madFiltList)
 
 

/***********************************************************************
*
* appendMadfilter   adds a new Madfilter to the MadfilterList
*
*   arguments: MadfilterList * madfilt_list - pointer to existing MadfilterList
*              Filter_type filtType - enum used to identify filter types
*              int numRange - number of ranges included - must be greater than 0
*              double * lower - array of lower limits of range - if "missing", no 
*                               lower limit for that range
*              double * upper - array of upper limits of range - if "missing", no 
*                               upper limit for that range
*              char * - madParm1 - Mnemonic of first parameter - cannot be 0 length
*              char * - madParm2 - Mnemonic of second parameter - can be 0 length if SINGLE_FILT
*
*      lower, upper, madParm1 and madParm2 are copied into newly allocated memory, so user is free to
*      release them after this method.  madParm1 and madParm2 converted to standard mnemonic form.
*
*   returns - 0 if success, -1 if failure (if either mnemonic too long, if madfilt_list NULL,
*             or numRange < 1)
*/
int appendMadfilter(MadfilterList * madFiltList,
                    Filter_type filtType, 
                    int numRange,
                    double * lower, 
                    double * upper, 
                    char * madParm1,
                    char * madParm2)
 
 

/***********************************************************************
*
* copyMadfilterList   copies an existing MadfilterList
*
*   arguments: Pointer to existing MadfilterList
*
*   returns - pointer to newly copied MadfilterList, newly allocated
*             on the heap.  Use destroyMadfilterList when done
*             Returns NULL if NULL passed in
*/
MadfilterList * copyMadfilterList(MadfilterList * madfilterList)
 
 

/***********************************************************************
*
* getMadfilterListFromStr   creates a MadfilterList based on an isprint-like command string
*
*   arguments: str - an isprint-like command string
*
*      lower, upper, madParm1 and madParm2 are copied into newly allocated memory, so user is free to
*      release them after this method.  madParm1 and madParm2 converted to standard mnemonic form.
*
*      The filter string is the same string that is used in the new isprint
*      command line.  Filters are separated by spaces.  The allowed filters
*      are:  
*  
*         date1=mm/dd/yyyy  (starting date to be examined. If time1 not given, defaults to 0 UT.)
*            Example: date1=01/20/1998 
* 
*         time1=hh:mm:ss (starting UT time to be examined. If date1 given, is applied to date1.
*                         If not, applies on the first day of the experiment.)
*            Example: time1=13:30:00
* 
*         date2=mm/dd/yyyy (ending date to be examined.  If time2 not given, defaults to 0 UT.)
*            Example: date2=01/21/1998
*
*         time2=hh:mm:ss (ending UT time to be examined - If date2 not given, ignored.)
*            Example: time2=15:45:00
* 
*         In the follow arguments ranges are used.  If any range value is not given, it may be used to 
*         indicate no lower or upper limit (but the comma is always required). Ranges are inclusive
*         of the end points:
* 
*         z=lower alt limit1, upper alt limit1 [or lower alt limit2 , upper alt limit2 ...] (km)
*            Example 1: z=100,500  (This would limit the geodetic altitude to 100 to 500 km.)
*            Example 2: z=100,200or300,400  (This would limit the geodetic altitude to 100 to 200 km
*                                            or 300 to 400 km.)
*            Example 3: z=,200or300,400   (Since the lower limit of the first range is missing, this 
*                                          would limit the geodetic altitude to anything below 200 km 
*                                          or from 300 to 400 km.)
* 
*         az=lower az limit1, upper az limit1 [or lower az limit2 , upper az limit2 ...] (from -180 to 180 degrees)
*            Example 1: az=100,120  (This would limit the azimuth to 100 to 120 degrees.)
*            Example 2: z=-180,-90or90,180  (This would limit the azimuth to between -180 and -90 degrees or
*                                            to between 90 and 180 degrees.  Note this allows a filter to go
*                                            through 180 degrees.)
*  
*         el=lower el limit1, upper el limit1 [or lower el limit2 , upper el limit2 ...] (from 0 to 90) 
*            Example 1: z=0,45  (This would limit the elevation from 0 to 45 degrees.) 
* 
*         plen=lower pl limit1, upper pl limit1 [or lower pl limit2 , upper pl limit2 ...] (pulse len in sec)
*            Example 1: z=,5e-4  (This would limit the pulse length to 5e-4 seconds or less.)
*   
*   
*         Free form filters using any mnemonic, or two mnemonics added, subtracted, multiplied, or divided.
*         Any number of filters may be added: 
*  
*         filter=[mnemonic] or [mnemonic1,[+*-/]mnemonic2] , lower limit1 , upper limit1 [or lower limit2 , upper limit2 ...] 
*            Example 1: filter=ti,500,1000or2000,3000   (Limits the data to points where Ti is between 500 and 1000 degrees
*                                                      or between 2000 and 3000 degrees.  Note that the units are always
*                                                      those of the Cedar standard.)
*            Example 2: filter=gdalt,-,sdwht,0,    (This filter implies "gdalt - sdwht" must be greater than 0.0.  Since
*                                                   sdwht is shadow height - the distance above any point on the earth 
*                                                   where the sun is first visible - this filter implies that only data 
*                                                   in direct sunlight will be displayed.)
*            Example 3: filter=ti,/,Dti,100,   (Limits the data to points where the ratio Ti/dTi is more than 100.)
*   
*         So an full FLTSTR argument might be:
*      
*            "date1=01/20/1998 time1=13:30:00 z=,200or300,400 filter=gdalt,-,sdwht,0, filter=ti,/,Dti,100,"
*
*   returns - MadfilterList if success, NULL if failure
*/
MadfilterList * getMadfilterListFromStr(char * str)
 
 

/***********************************************************************
*
* createMaddata   creates a new Maddata
*
*   arguments:
*
*     char * filename - full path to the file which was basis of data 
*     char * infoStr  - Information string (may be used in outputing formatted data)
*     MadparmList *   madparmList - list of Madrigal parameters desired
*     MadfilterList * madFiltList - list of Madfilters to apply
*     FILE * errFile   - errFile to write an error messages to
*
*
*   returns - pointer to newly created Maddata.  Use 
*             destroyMaddata when done
*
*   Allocates memory to store all data, so all input may be released or changed
*   after this method is called.  Maddata is the main data structure, and is meant 
*   to be the main way to expose Madrigal data from a single cedar file that 
*   applies filtering and calculates derived data.
*
*   Returns NULL if failure.
*/
Maddata * createMaddata(char * filename,
                        char * infoStr,
                        MadparmList * requestParmList,
                        MadfilterList * madfilterList,
                        FILE * errFile)
 
 

/***********************************************************************
*
* createNonfileMaddata   creates a new Maddata using user-supplied data
*                        rather than data from a file
*
*   arguments:
*
*     MadparmList * madparmList - list of Madrigal parameters desired
*     double ut1                - start time of integration period
*     double ut2                - end time of integration period 
*     int kinst                 - kinst id - needed since its in the prolog
*     MadparmList * oneDParms,  - list of 1D parameters for which you plan 
*                                 to provide data - may be 0 length
*     MadparmList * twoDParms,  - list of 2D parameters for which you plan 
*                                  to provide data - may be 0 length
*     int num2Drows             - number of 2D rows - may be zero
*     double * oneDdata         - array of 1D data in order of oneDParms
*     double ** twoDdata        - array of num2Drows double * to 2D data 
*                                 Each double * points to array of doubles of
*                                 length = length of twoDParms
*     FILE * errFile   - errFile to write an error messages to
*
*
*   returns - pointer to newly created Maddata.  Use 
*             destroyMaddata when done.  Will contain only one Madrecord.
*
*   Allocates memory to store all data, so all input may be released or changed
*   after this method is called.  Use this method to calculate Maddata when
*   you want to directly provide measured data, rather than get it from a file.  
*/
Maddata * createNonfileMaddata(MadparmList * requestedParms,
                               double ut1,
                               double ut2,
                               int kinst,
                               MadparmList * oneDParms,
                               MadparmList * twoDParms,
                               int num2Drows,
                               double * oneDdata,
                               double ** twoDdata,
                               FILE * errFile)
 
 

/***********************************************************************
*
* destroyMaddata   releases an existing Maddata
*
*   arguments: pointer to existing Maddata
*
*   returns - void
*/
void destroyMaddata(Maddata * maddata)
 
 

/***********************************************************************
*
* appendMadrecParmType   appends a new MadrecParmType onto maddata
*
*   arguments: 
*
*     Maddata * maddata - pointer to Maddata to append new cycle to
*     MadparmList * parm1DList - the list of 1D parameters in that type
*     MadparmList * parm1DList - the list of 2D parameters in that type
*
*
*   returns - index of new type.  Starts at 0.  If failure,
*             returns -1
*
*   Allocates memory to store all data, so all input may be released or changed
*   after this method is called.
*/
int appendMadrecParmType(Maddata * maddata,
                         MadparmList * parm1DList,
                         MadparmList * parm2DList)
 
 

/***********************************************************************
*
* appendMadcycle   appends a new Madcycle onto maddata
*
*   arguments: 
*
*     Maddata * maddata - pointer to Maddata to append new cycle to
*     int   cycleId     - cycle id (identifies cycle type)
*     char *   cycleDesc - Additional cycle description (may be empty string)
*
*
*   returns - cycle index of new cycle.  Starts at 0.  If failure,
*             returns -1
*
*   Allocates memory to store all data, so all input may be released or changed
*   after this method is called.
*/
int appendMadcycle(Maddata * maddata,
                   int cycleId,
                   char * cycleDesc)
 
 

/***********************************************************************
*
* createMadcycle   creates a new Madcycle
*
*   arguments:
*
*     int   cyclenum  - cycle number 
*     int   cycleId   - cycle id (identifies cycle type)
*     char *   cycleDesc - Additional cycle description (may be empty string)
*
*
*   returns - pointer to newly created Madcycle.  Use 
*             destroyMadcycle when done
*
*   Allocates memory to store all data, so all input may be released or changed
*   after this method is called.  Use appendMadrecord to append a new Madrecord
*/
Madcycle * createMadcycle(int cyclenum,
                          int cycleId,
                          char * cycleDesc)
 
 

/***********************************************************************
*
* destroyMadcycle   releases an existing Madcycle
*
*   will also free all Madrecords in this cycle
*
*   arguments: pointer to existing Madcycle
*
*   returns - void
*/
void destroyMadcycle(Madcycle * madcycle)
 
 

/***********************************************************************
*
* createMadrecord   creates a new Madrecord
*
*   arguments:
*
*   Rec_type  rectype - Record type: HEADER_REC, CATALOG_REC, or DATA_REC.  If DATA_REC,
*                       text will be empty string, no matter what passed in. If not, 
*                       data1Dparms will be null.
*   int numType       - index into maddata.madrecParmTypeList that defines the parm type of 
*                       record (that is, its list of 1D and 2D parameters)
*   char   *  text    - Text of header or catalog record. Empty string if data rec.
*   int num1DParms    - Number of 1D parameters to copy
*   double *  data1Dparms - pointer to array of doubles containing 1D data
*   int  kinst  - instrument id
*   double starttime - start time of record in seconds since 1/1/1950
*   double endtime - end time of record in seconds since 1/1/1950
*
*     Number of 1D parameters and order must correspond to
*     maddata.parm1DList
*
*   returns - pointer to newly created Madrecord.  Use 
*             destroyMadrecord when done
*
*   Allocates memory to store all data, so all input arrays may be released or changed
*   after this method is called.  Use createMadrecord to create a record with just the
*   1D data; then append each 2D row using append2DRow
*/
Madrecord * createMadrecord(Rec_type rectype,
                            int numType,
                            char * text,
                            int num1DParms,
                            double * data1Dparms,
                            int kinst,
                            double starttime,
                            double endtime)
 
 

/***********************************************************************
*
* destroyMadrecord   releases an existing Madrecord
*
*   arguments: pointer to existing Madrecord
*
*   returns - void
*/
void destroyMadrecord(Madrecord * madrecord)
 
 

/***********************************************************************
*
* simpleMadrecordPrint - a simple method that prints all data from one Madrecord
*
*
*   arguments: 
*         Maddata * maddata - pointer to Maddata
*         int cycId - cycle number of Madrecord
*         int recId - record number in cycle of Madrecord to print
*         FILE * fp - file to print to (may be stdout)
*
*   returns - void
*
*    Prints simple version of Madrecord to FILE
*/
void simpleMadrecordPrint(Maddata * maddata,
                          int cycId,
                          int recId,
                          FILE * fp)
 
 

/***********************************************************************
*
* simpleMadfilterPrint - a simple method that prints all data from Maddata
*                        a single Madfilter
*
*   arguments: 
*         Madfilter * madfilter - pointer to Madfilter
*         FILE * fp - file to print to (may be stdout)
*
*   returns - void
*
*    Prints simple version of Madfilter to FILE
*/
void simpleMadfilterPrint(Madfilter * madfilter, int filterNum, FILE * fp)
 
 

/***********************************************************************
*
* simpleMaddataPrint - a simple method that prints all data from Maddata
*
*
*   arguments: 
*         Maddata * maddata - pointer to Maddata
*         FILE * fp - file to print to (may be stdout)
*
*   returns - void
*
*    Prints simple version of Maddata to FILE
*/
void simpleMaddataPrint(Maddata * maddata, FILE * fp)
 
 

/***********************************************************************
*
* classicIsprint - a method that prints all data in standard isprint format
*
*
*   arguments: 
*         Maddata * maddata - pointer to Maddata
*         int displayHeaders - if 1, display headers, if 0, don't
*         int displaySummary - if 1, display summary at top,
*                              if 0, don't
*         int maxCharsPerLine - if 0, no limit, if < ISPRINT_MIN_CHARS_PER_LINE, 
*                               limit line to ISPRINT_MIN_CHARS_PER_LINE, else
*                               limit line to maxCharsPerLine
*         char * missingStr - string to use when data missing
*         char * assumedStr - string to use when error data assumed
*         char * knownBadStr - string to use when error data knownBad
*         FILE * fp - file to print to (may be stdout)
*
*   returns - void
*
*    Prints Maddata in standard isprint format to FILE.  Isprint format
*    does not differentiate between 1 and 2D data; everything is treated
*    as 2D data.  Cycle ignored.  If both displayHeaders and displaySummary == 0,
*    only data will be printed without any labels.
*/
void classicIsprint(Maddata * maddata,
                    int displayHeaders,
                    int displaySummary,
                    int maxCharsPerLine,
                    char * missingStr,
                    char * assumedStr,
                    char * knownBadStr,
                    FILE * fp)
 
 

/***********************************************************************
*
* printIsprintHeader - prints isprint header (time and instrument)
*
*
*   arguments: 
*         Maddata * maddata - pointer to Maddata
*         int cycleNum - cycle number
*         int recNum - record in that cycle
*         FILE * fp - file to print to (may be stdout)
*
*   returns - void
*
*    Prints isprint header line.
*/
void printIsprintHeader(Maddata * maddata, 
                        int cycleNum,
                        int recNum, 
                        FILE * fp)
 
 

/***********************************************************************
*
* getIsprintHeader - returns malloced string containing isprint header (time and instrument)
*
*
*   arguments: 
*         Maddata * maddata - pointer to Maddata
*         int cycleNum - cycle number
*         int recNum - record in that cycle
*
*   returns - char * to malloced string containing isprint header (time and instrument)
*
*    Similiar to printIsprintHeader except returns string instead of printing it.
*    User must free returned string when done with it.
*/
char * getIsprintHeader(Maddata * maddata, 
                        int cycleNum,
                        int recNum)
 
 

/***********************************************************************
*
* printIsprintLabel - prints isprint header
*
*
*   arguments: 
*         Maddata * maddata - pointer to Maddata
*         int maxCharsPerLine - limit line to maxCharsPerLine
*         FILE * fp - file to print to (may be stdout)
*
*   returns - void
*
*    Prints isprint header line.
*/
void printIsprintLabel(Maddata * maddata, 
                        int maxCharsPerLine, 
                        FILE * fp)
 
 

/***********************************************************************
*
* getIsprintLabel - creates malloced strings containing isprint label
*                   and comma-separated mnemonics
*
*
*   arguments: 
*         Maddata * maddata - pointer to Maddata
*         int maxCharsPerLine - limit line to maxCharsPerLine
*         char ** mnemStr - string containing comma-separated requested mnemonics
*         char ** labelStr - string containing mnemonics labels as formatted for isprint
*
*   returns - void
*
*    User must free malloced strings mnemStr and labelStr when done with them.
*/
void getIsprintLabel(Maddata * maddata, 
                     int maxCharsPerLine,
                     char ** mnemStr,
                     char ** labelStr)
 
 

/***********************************************************************
*
* classicMadrecordPrint - prints a single Madrecord in isprint format
*
*
*   arguments: 
*         Maddata * maddata - pointer to Maddata
*         int cycleNum - cycle number
*         int recNum - record in that cycle
*         int displayHeaders - if 1, display headers, if 0, don't
*         int maxCharsPerLine - limit line to maxCharsPerLine
*         char * missingStr - string to use when data missing
*         char * assumedStr - string to use when error data assumed
*         char * knownBadStr - string to use when error data knownBad
*         FILE * fp - file to print to (may be stdout)
*
*   returns - void
*
*    Prints isprint header line.
*/
void classicMadrecordPrint(Maddata * maddata, 
                          int cycleNum, 
                          int recNum, 
                          int displayHeaders,
                          int maxCharsPerLine,
                          char * missingStr,
                          char * assumedStr,
                          char * knownBadStr,
                          FILE * fp)
 
 

/***********************************************************************
*
* getClassicMadrecordStrings - returns strings describing a single Madrecord 
*                              in isprint format
*
*   similar to classicMadrecordPrint except returns data as strings instead
*   of directly fprintf'ing output
*
*
*   arguments: 
*         Maddata * maddata - pointer to Maddata
*         int cycleNum - cycle number
*         int recNum - record in that cycle
*         int maxCharsPerLine - limit line to maxCharsPerLine
*         char * missingStr - string to use when data missing
*         char * assumedStr - string to use when error data assumed
*         char * knownBadStr - string to use when error data knownBad
*         char ** headerStr - string containing header as formatted for isprint
*         char ** mnemStr - string containing comma-separated requested mnemonics
*         char ** labelStr - string containing mnemonics labels as formatted for isprint
*         char ** dataStr - string containing data as formatted for isprint, except
*                          rows separated by commas instead of carriage return.
*
*   The strings mnemStr, headerStr, and dataStr are allocated on the heap, and are
*   the resposiblity of the caller to free when no longer needed.
*
*   returns - void
*/
void getClassicMadrecordStrings(Maddata * maddata, 
                                int cycleNum, 
                                int recNum, 
                                int maxCharsPerLine,
                                char * missingStr,
                                char * assumedStr,
                                char * knownBadStr,
                                char ** headerStr,
                                char ** mnemStr,
                                char ** labelStr,
                                char ** dataStr)
 
 

/***********************************************************************
*
* lookerMadrecordPrint - prints a single Madrecord in looker format
*
*
*   arguments: 
*         Maddata * maddata - pointer to Maddata (assumed to have one record only)
*         char * missingStr - string to use when data missing
*         char * assumedStr - string to use when error data assumed
*         char * knownBadStr - string to use when error data knownBad
*         int printHeaderFlag - if zero, surpress printing header line
*         FILE * fp - file to print to (may be stdout)
*
*   returns - void
*
*/
void lookerMadrecordPrint(Maddata * maddata, 
                          char * missingStr,
                          char * assumedStr,
                          char * knownBadStr,
			  int printHeaderFlag,
                          FILE * fp)
 
 

/***********************************************************************
*
* populate1DDataFromStr - uses a command string to populate a MadparmList
*                         and an array of doubles
*
*   For example, if onedString = "gdalt=100.0 glon=45.0 gdlat=-20.0",
*   oneDParms would have gdalt, glon, and gdlat, and oneDdata = {100.0, 45.0, -20.0}
*
*   arguments: 
*         char * onedString - string that describes 1D data
*         MadparmList ** oneDParms - created list of 1D parameters
*         double ** oneDdata - pointer to array of doubles.  Memory malloc'ed here
*         and must be free'd by the user when done
*
*   returns - 0 if success, -1 if problem
*
*/
int populate1DDataFromStr(char * onedString, 
                          MadparmList ** oneDParms, 
                          double ** oneDdata)
 
 

/***********************************************************************
*
* populate2DDataFromStr - uses a command string to populate a MadparmList
*                         and an array of doubles
*
*   For example, if twodString is:
*
*      "gdlat=45,45,45,45,50,50,50,50 glon=20,20,30,30,20,20,30,30 gdalt=500,600,500,600,500,600,500,600"
*
*   (Note that each parameters must have same number of values or an error is thrown)
*
*   twoDParms would have gdalt, glon, and gdlat, and 
*   twoDdata = { {45.0,45.0,45.0,45.0,50.0,50.0,50.0,50.0},
*                {20.0,20.0,30.0,30.0,20.0,20.0,30.0,30.0},
*                {500.0,600.0,500.0,600.0,500.0,600.0,500.0,600.0}}
*
*   arguments: 
*         char * twodString - string that describes 2D data
*         MadparmList ** twoDParms - created list of 2D parameters
*         double *** twoDdata - pointer to array of arrays of doubles.  Memory malloc'ed here
*         and must be free'd by the user when done
*         int * num2Drows - number of rows found in each 2D parameter
*
*   returns - number of 2D values per parameter if success, -1 if problem
*
*/
int populate2DDataFromStr(char * twodString, 
                          MadparmList ** twoDParms, 
                          double *** twoDdata, 
                          int * num2Drows)
 
 

Private madrec and maddata methods

These methods are private to the madc library, and should not need to be used by application developers. They are documented here for maintenance. They involve low-level routines to access various Cedar files, and also the details of how the madDeriveEngine module works


getNextMadrigalRecord putNextMadrigalRecord getNextCedarAsciiRecord putNextCedarAsciiRecord getNextCedarCbfRecord
getNextCosRecord putNextCedarCbfRecord putNextCosRecord flushCedarCbfRecord endFileCedarCbfRecord
endDataCedarCbfRecord writeCbfControlWord getNextCedarBlockedRecord putNextCedarBlockedRecord flushCedarBlockedRecord
getNextCedarUnblockedRecord putNextCedarUnblockedRecord getMemNextCedarUnblockedRecord putMemNextCedarUnblockedRecord putMemFastNextCedarUnblockedRecord
editMemNextCedarUnblockedRecord cedarFileType isProlog madptr jday1
idmyk1 setCheckSum int encodeBits int
setbit dumpCedarRecord fread16 fwrite16 reorderBytes
createInfoMethod destroyInfoMethod createInfoMultiRowMethod destroyInfoMultiRowMethod createInfoDervFile
destroyInfoDervFile getRecType load1DMeasData load2DMeasData load1DMultiRowData
load1DMultiRowDataNonfile load2DMultiRowData load2DMultiRowDataNonfile evaluateFilter appendMadrecord
updateMadrecordWithMultiRow append2DRow createInfoDerived destroyInfoDerived dispatchMethod
dispatchMultiRowMethod checkIf1DMethodNeeded checkIf2DMethodNeeded make1DMethodNeeded make2DMethodNeeded
checkIfMultiRowMethodNeeded hasOutput

/***********************************************************************
*
* getNextMadrigalRecord  reads a madrigal record
*
* The file should be positioned at the beginning of a record before
* calling this function, and on the first call, the Cedar record
* pointer, *cedarRecord, should be NULL. The pointers at the beginning
* of each block (words 1 and 2) and the checksum (word(blockSize-1) are
* ignored.
*
* Madrec completely ignores the 5 extra control words, words 17-21, in the
* Madrigal format prolog. They are removed when a Madrigal record is read 
* and added when a Madrigal record is written.
* These extra words are fully compliant with the CEDAR binary format in
* the case of data records, since the prolog length is specified in the
* data record.  They are not fully compliant with the CEDAR standard in
* the case of binary catalog and header records, which have fixed prolog
* lengths of 40, of which only the first 12 and 15 respectively are
* significant. The standard requires that the remaining words in the
* prolog must be zero. However, in practice this has often been ignored,
* even at NCAR, and it is unlikely that any CEDAR software actually
* chokes at non-zero words beyond 12 and 15 in catalog and header
* records.
*
*    fp          - File pointer to the Madrigal file
*    cedarRecord - The Cedar record
*    blockSize   - Madrigal file block size. Normally 6720 16-bit integers.
*    sigWords    - Number of signifcant words in the current block
*
*/

int
getNextMadrigalRecord (FILE *fp, Int16 **cedarRecor