/*******************************************************************************
 * JMMC project ( http://www.jmmc.fr ) - Copyright (C) CNRS.
 ******************************************************************************/
/**
 * @file
 * Functions to read/write IO-FITS file
 */
#define _POSIX_SOURCE 1

/* 
 *   System Headers
 */

#include "fitsio.h"
#include <string.h>
#include <math.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>

/* 
   Local Headers
*/
#include "amdlib.h"
#include "amdlibProtected.h"

/* Local function declaration */
static amdlibCOMPL_STAT amdlibWriteOiArray(fitsfile        *filePtr,
                                           amdlibOI_ARRAY  *array,
                                           amdlibERROR_MSG errMsg);
static amdlibCOMPL_STAT amdlibReadOiArray(fitsfile       *filePtr,
                                          amdlibOI_ARRAY *array,
                                          amdlibERROR_MSG errMsg );

static amdlibCOMPL_STAT amdlibWriteOiTarget(fitsfile        *filePtr,
                                            amdlibOI_TARGET *target,
                                            amdlibERROR_MSG errMsg);
static amdlibCOMPL_STAT amdlibReadOiTarget(fitsfile        *filePtr, 
                                           amdlibOI_TARGET *target, 
                                           amdlibERROR_MSG errMsg);

static amdlibCOMPL_STAT amdlibWriteOiWavelength(fitsfile         *filePtr,
                                                char             *insName,
                                                amdlibWAVELENGTH *wave, 
                                                amdlibERROR_MSG  errMsg);
static amdlibCOMPL_STAT amdlibReadOiWavelength(fitsfile          *filePtr,
                                               amdlibWAVELENGTH  *wave,
                                               amdlibERROR_MSG   errMsg);

static amdlibCOMPL_STAT amdlibWriteOiVis(fitsfile        *filePtr, 
                                         char            *insName,
                                         char            *arrName,
                                         amdlibVIS       *vis, 
                                         amdlibERROR_MSG errMsg);
static amdlibCOMPL_STAT amdlibReadOiVis(fitsfile        *filePtr,
                                        amdlibVIS       *vis,
                                        int             nbBases,
                                        amdlibERROR_MSG errMsg);

static amdlibCOMPL_STAT amdlibWriteOiVis2(fitsfile        *filePtr, 
                                          char            *insName,
                                          char            *arrName,
                                          amdlibVIS2      *vis2, 
                                          amdlibERROR_MSG errMsg);
static amdlibCOMPL_STAT amdlibReadOiVis2(fitsfile        *filePtr,
                                         amdlibVIS2      *vis2,
                                         int             nbBases,
                                         amdlibERROR_MSG errMsg);

static amdlibCOMPL_STAT amdlibWriteOiVis3(fitsfile        *filePtr, 
                                          char            *insName,
                                          char            *arrName,
                                          amdlibVIS3      *vis3, 
                                          amdlibERROR_MSG errMsg);
static amdlibCOMPL_STAT amdlibReadOiVis3(fitsfile        *filePtr,
                                         amdlibVIS3      *vis3,
                                         amdlibERROR_MSG errMsg);

static amdlibCOMPL_STAT amdlibWriteAmberData(fitsfile         *filePtr,
                                             char             *insName,
                                             amdlibPHOTOMETRY *photometry,
                                             amdlibVIS        *vis,
                                             amdlibPISTON     *pst,
                                             amdlibWAVELENGTH *wave, 
                                             amdlibERROR_MSG  errMsg);
static amdlibCOMPL_STAT amdlibReadAmberData(fitsfile         *filePtr,
                                            amdlibPHOTOMETRY *photometry,
                                            amdlibVIS        *vis,
                                            amdlibPISTON     *pst,
                                            int              nbBases,
                                            amdlibERROR_MSG  errMsg);


/*
 * Public functions 
 */
/**
 * Write OI-FITS file.
 *
 * This function create OI-FITS file containing the OI_ARRAY, OI_TARGET, OI_VIS,
 * OI_VIS2, OI_T3 and OI_WAVELENGTH binary tables defined in the IAU standard,
 * and the AMBER_DATA binary table which is specific to AMBER. The AMBER_DATA
 * binary table contains photometry and piston data.
 *
 * @note
 * If the file exists, it is overwritten.
 *
 * @param filename name of the OI-FITS file to create
 * @param insCfg array containing keywords of the primary header.
 * @param array structure containing information of OI_ARRAY binary table 
 * @param target structure containing information of OI_TARGET binary table 
 * @param photometry structure containing photometry which is stored in
 * AMBER_DATA binary table.
 * @param vis structure containing information of OI_VIS binary table 
 * @param vis2 structure containing information of OI_VIS2 binary table 
 * @param vis3 structure containing information of OI_T3 binary table 
 * @param wave structure containing information of OI_WAVELENGTH binary table 
 * @param pst structure containing piston which is stored in AMBER_DATA binary
 * table.
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibSaveOiFile(const char           *filename,
                                  amdlibINS_CFG        *insCfg, 
                                  amdlibOI_ARRAY       *array,
                                  amdlibOI_TARGET      *target,
                                  amdlibPHOTOMETRY     *photometry,
                                  amdlibVIS            *vis,
                                  amdlibVIS2           *vis2,
                                  amdlibVIS3           *vis3,
                                  amdlibWAVELENGTH     *wave,
                                  amdlibPISTON         *pst,
                                  amdlibSPECTRUM       *spectrum,
                                  amdlibERROR_MSG      errMsg)
{
    int         status = 0;
    fitsfile    *filePtr;
    int         i;
#ifndef ESO_CPL_PIPELINE_VARIANT
    struct stat statBuf;
    time_t      timeSecs;
    struct tm   *timeNow;
    char        strTime[amdlibKEYW_VAL_LEN+1];
#endif
    char        fitsioMsg[256];
    char        insName[amdlibKEYW_VAL_LEN+1];
    char        arrName[amdlibKEYW_VAL_LEN+1];
    char        version[32];

    amdlibLogTrace("amdlibSaveOiFile()");

#ifndef ESO_CPL_PIPELINE_VARIANT
    /* 
     * amdlib will create the file on its own
     */
    /* First remove previous IO file (if any) */
    if (stat(filename, &statBuf) == 0)
    {
        if (remove(filename) != 0)
        {
            amdlibSetErrMsg("Could not overwrite file %s", filename); 
            return amdlibFAILURE;
        }
    }

    /* Create new FITS file */
    if (fits_create_file(&filePtr, filename, &status))
    {
        amdlibReturnFitsError("opening file");
    }
#else
    /* 
     *  The ESO pipeline will create that file and prepare the header
     *  with DMD compliant keywords. Hence amdlib shall just append to it
     */ 

    /* Append to FITS file */
    if (fits_open_file(&filePtr, filename, READWRITE, &status))
    {
        printf("%s->fits_open_file cannot open in append mode\n", filename  );
        amdlibReturnFitsError("opening file");
    }
    else
    {
        printf("%s->fits_open_file append OK\n", filename);
    }
#endif    

    /* Build a uniq INSNAME using P2VM id (if possible), else use default */
    sprintf(insName, "AMBER");

    /* Retrieve ARRNAME if possible, else use default.*/
    if (array != NULL)
    {
        strncpy(arrName, array->arrayName, amdlibKEYW_VAL_LEN+1);
    }
    else
    {
        sprintf(arrName, "UNKNOWN");
    }    

    /* Write OI_ARRAY binary table */
    if (array != NULL)
    {
        if (amdlibWriteOiArray(filePtr, array,  errMsg) != amdlibSUCCESS)
        {
            fits_close_file(filePtr, &status);
            return amdlibFAILURE;
        }
    }

    /* Write OI_TARGET binary table */
    if (target != NULL)
    {
        if (amdlibWriteOiTarget(filePtr, target, errMsg) != amdlibSUCCESS)
        {
            fits_close_file(filePtr, &status);
            return amdlibFAILURE;
        }
    }

    /* Write OI_WAVELENGTH binary table */
    if (wave != NULL)
    {
        if (amdlibWriteOiWavelength(filePtr, insName, 
                                    wave, errMsg) != amdlibSUCCESS)
        {
            fits_close_file(filePtr, &status);
            return amdlibFAILURE;
        }
    }

    /* Write OI_VIS binary table */
    if (vis != NULL)
    {
        if (amdlibWriteOiVis(filePtr, insName, arrName, 
                             vis, errMsg) != amdlibSUCCESS)
        {
            fits_close_file(filePtr, &status);
            return amdlibFAILURE;
        }
    }

    /* Write OI_VIS2 binary table */
    if (vis2 != NULL)
    {    
        if (amdlibWriteOiVis2(filePtr, insName, arrName, 
                              vis2, errMsg) != amdlibSUCCESS)
        {
            fits_close_file(filePtr, &status);
            return amdlibFAILURE;
        }
    }
    /* Write OI_T3 binary table */
    if (vis3 != NULL)
    {
        if (amdlibWriteOiVis3(filePtr, insName, arrName, 
                              vis3, errMsg) != amdlibSUCCESS)
        {
            fits_close_file(filePtr, &status);
            return amdlibFAILURE;
        }
    }

    /* Write AMBER_DATA binary table */
    if ((photometry != NULL) && (vis != NULL) && (pst != NULL))
    {
        if (vis->nbFrames != 1)
        {
            if (amdlibWriteAmberData(filePtr, insName, 
                                     photometry, vis, pst, wave,
                                     errMsg) != amdlibSUCCESS)
            {
                fits_close_file(filePtr, &status);
                return amdlibFAILURE;
            }
        }
    }

    /* Write AMBER_SPECTRUM binary table */
    if (spectrum != NULL)
    {
        if (amdlibWriteAmberSpectrum(filePtr, wave, spectrum,
                                     errMsg) != amdlibSUCCESS)
        {
            fits_close_file(filePtr, &status);
            return amdlibFAILURE;
        }
    }

    /* Move to main header */
    amdlibLogTrace("Move to main header");
    if (fits_movabs_hdu(filePtr, 1, 0, &status) != 0)
    {
        amdlibGetFitsError("main header");
        fits_close_file(filePtr, &status);
        return amdlibFAILURE;
    }    

#ifndef ESO_CPL_PIPELINE_VARIANT
    /* Complete main header */
    if (insCfg != NULL)
    {
        /* Add DATE */
        amdlibLogTrace("Add DATE");
        timeSecs = time(NULL);
        timeNow = gmtime(&timeSecs);
        strftime(strTime, sizeof(strTime), "%Y-%m-%dT%H:%M:%S", timeNow);
        if (fits_write_key(filePtr, TSTRING, "DATE", strTime,
                           "Date this file was written", &status) != 0)
        {
            amdlibGetFitsError("completing main header");
            fits_close_file(filePtr, &status);
            return amdlibFAILURE;
        }

        /* Add other keywords */
        amdlibLogTrace("Add other keywords");
        for (i = 0; i < insCfg->nbKeywords; i++)
        {
	  char keywLine[200];

	   if ((strstr(insCfg->keywords[i].name, "SIMPLE") == NULL) &&
                (strstr(insCfg->keywords[i].name, "BITPIX") == NULL) &&
                (strstr(insCfg->keywords[i].name, "NAXIS ") == NULL) &&
                (strstr(insCfg->keywords[i].name, "EXTEND") == NULL) &&
                (strstr(insCfg->keywords[i].name, "DATE  ") == NULL))
            {
                sprintf((char*)keywLine, "%s=%s/%s", insCfg->keywords[i].name,
                        insCfg->keywords[i].value, 
                        insCfg->keywords[i].comment);

                if (fits_write_record(filePtr, keywLine, &status) != 0)
                {
                    amdlibGetFitsError("completing main header");
                    fits_close_file(filePtr, &status);
                    return amdlibFAILURE;
                }
            }
        }
    }
#else
    /* Complete main header */
    if (insCfg != NULL)
    {
        /* Add QC keywords  - spectral shifts */
        amdlibLogTrace("Add QC keywords  - spectral shifts");
        for (i = 0; i < insCfg->nbKeywords; i++)
        {
	    char keywLine[200];
            if ((strstr(insCfg->keywords[i].name, 
                        "HIERARCH ESO QC P1 ") != NULL) ||
                (strstr(insCfg->keywords[i].name, 
                        "HIERARCH ESO QC P2 ") != NULL) ||
                (strstr(insCfg->keywords[i].name, 
                        "HIERARCH ESO QC P3 ") != NULL)) 
            {
                sprintf((char*)keywLine, "%s=%s/%s", insCfg->keywords[i].name,
                        insCfg->keywords[i].value, 
                        insCfg->keywords[i].comment);
                if (fits_write_record(filePtr, keywLine, &status) != 0)
                {
                    amdlibGetFitsError("completing main header");
                    fits_close_file(filePtr, &status);
                    return amdlibFAILURE;
                }
            }
        }
    }
#endif
    
    /* Add amdlib version */
    amdlibLogTrace("Add amdlib version");
    amdlibGetVersion(version);
    if (fits_update_key(filePtr, TSTRING, "HIERARCH ESO OCS DRS VERSION", 
                        version,
                        "Data Reduction SW version", &status) != 0)
    {
        amdlibGetFitsError("HIERARCH ESO OCS DRS VERSION");
        fits_close_file(filePtr, &status);
        return amdlibFAILURE;
    }

    /* Close FITS file */
    amdlibLogTrace("Close FITS file");
    if (fits_close_file(filePtr, &status)) 
    {
        amdlibReturnFitsError("closing file");
    }

    amdlibLogTrace("amdlibSaveOiFile done");
    return amdlibSUCCESS;
}

/**
 * Read OI-FITS file.
 *
 * This function reads OI-FITS file containing the OI_ARRAY, OI_TARGET, OI_VIS,
 * OI_VIS2, OI_T3 and OI_WAVELENGTH binary tables defined in the IAU standard,
 * and the AMBER_DATA binary table which is specific to AMBER. The AMBER_DATA
 * binary table contains photometry and piston data.
 *
 * @note
 * If the file exists, it is overwritten.
 *
 * @param filename name of the OI-FITS file to read
 * @param insCfg array which will contain keywords of the primary header.
 * @param array structure which will contain information of OI_ARRAY binary
 * table 
 * @param target structure which will contain information of OI_TARGET binary
 * table 
 * @param photometry structure which will contain photometry which is stored in
 * AMBER_DATA binary table.
 * @param vis structure which will contain information of OI_VIS binary table 
 * @param vis2 structure which will contain information of OI_VIS2 binary table 
 * @param vis3 structure which will contain information of OI_T3 binary table 
 * @param wave structure which will contain information of OI_WAVELENGTH binary
 * table 
 * @param pst structure which will contain piston which is stored in AMBER_DATA
 * binary table.
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
  */
amdlibCOMPL_STAT amdlibLoadOiFile(const char           *filename,
                                  amdlibINS_CFG        *insCfg,  
                                  amdlibOI_ARRAY       *array,
                                  amdlibOI_TARGET      *target,
                                  amdlibPHOTOMETRY     *photometry,
                                  amdlibVIS            *vis,
                                  amdlibVIS2           *vis2,
                                  amdlibVIS3           *vis3,
                                  amdlibWAVELENGTH     *wave,
                                  amdlibPISTON         *pst,
                                  amdlibSPECTRUM       *spectrum,
                                  amdlibERROR_MSG      errMsg)
{
    int status = 0;
    fitsfile *filePtr;
    char fitsioMsg[256];
    int nbBases, i;
    int nbTels;
    int keysExist = 0;
    int moreKeys = 0;
    amdlibKEYW_LINE record;

    amdlibLogTrace("amdlibLoadOiFile()");

    /* Open FITS file */
    if (fits_open_file(&filePtr, filename, READONLY, &status))
    {
        amdlibGetFitsError("Opening file");
        return amdlibFAILURE;
    }    
    /* Get the number of keywords in HDU */
    if (insCfg != NULL)
    {
        keysExist=0;
        moreKeys=0;
        if (fits_get_hdrspace(filePtr, &keysExist, &moreKeys, &status) != 0)
        {
            status = 0;
        }
        /* For each keyword */
        for (i = 1; i <= keysExist; i++)
        {
            /* Read keyword line #i */
            if (fits_read_record(filePtr, i, record, &status) != 0)
            {
                status = 0;
            }
            else
            {
                /* If it is not a simple comment */
                if (strstr(record, "COMMENT ") == NULL)
                {
                    /* Store keyword */
                    if (amdlibAddInsCfgKeyword(insCfg,
                                               record, errMsg) == amdlibFAILURE)
                    {
                        return amdlibFAILURE;
                    }
                }
            }
        }

        /* Get instrument mode */
        char insMode[amdlibKEYW_NAME_LEN+1];
        if (amdlibGetInsCfgKeyword(insCfg, "HIERARCH ESO INS MODE",
                                   insMode, errMsg) != amdlibSUCCESS)
        {
            return amdlibFAILURE;
        }
        if (strstr(insMode, "Low") != NULL) 
        {
            amdlibLogWarning("Differential phases and visibilities are not yet "
                             "validated in low resolution mode.");
            amdlibLogWarningDetail("Must be used carrefully");
            
        }
    }
    /* Read OI_ARRAY binary table */
    if (array != NULL)
    {
        if (amdlibReadOiArray(filePtr, array, errMsg) != amdlibSUCCESS)
        {
            fits_close_file(filePtr, &status);
            amdlibLogWarning("No OI_ARRAY table");
        }
    }
 
    /* Read OI_TARGET binary table */
    if (target != NULL)
    {
        if (amdlibReadOiTarget(filePtr, target, errMsg) != amdlibSUCCESS)
        {
            fits_close_file(filePtr, &status);
            amdlibLogError("No OI_TARGET table: non conformant OIFITS");
            return amdlibFAILURE; 
        }
    }

    /* Read OI_WAVELENGTH binary table */
    if (wave != NULL)
    {
        if (amdlibReadOiWavelength(filePtr, wave, errMsg) != amdlibSUCCESS)
        {
            fits_close_file(filePtr, &status);
            amdlibLogError("No OI_WAVELENGTH table: non conformant OIFITS");
            return amdlibFAILURE; 
        }
    }
   
    /* Read OI_T3 binary table */
    if (vis3 != NULL)
    {
        if (amdlibReadOiVis3(filePtr, vis3, errMsg) != amdlibSUCCESS)
        {
            fits_close_file(filePtr, &status);
            amdlibLogTest("No OI_T3 table");
        }
    }

    /* If there is no structure for phase closure, assuming there is only one
     * baseline */
    if (vis3->thisPtr != vis3)
    {
        nbBases = 1;
        nbTels  = 2;
    }
    else
    {
        nbBases = 3;
        nbTels  = 3;
    }

    /* Read OI_VIS binary table */
    if (vis != NULL)
    {
        if (amdlibReadOiVis(filePtr, vis, nbBases, errMsg) != amdlibSUCCESS)
        {
            fits_close_file(filePtr, &status);
	        amdlibLogWarning("No OI_VIS table");
        }
    }

    /* Read OI_VIS2 binary table */
    if (vis2 != NULL)
    {
        if (amdlibReadOiVis2(filePtr, vis2, nbBases, errMsg) != amdlibSUCCESS)
        {
            fits_close_file(filePtr, &status);
	        amdlibLogWarning("No OI_VIS2 table");
        }
    }

    /* Read AMBER_DATA binary table */
    if (photometry != NULL)
    {
        if (amdlibReadAmberData(filePtr, photometry, vis,
                                pst, nbBases, errMsg) != amdlibSUCCESS)
        {
	        amdlibLogTest("No AMBER_DATA table");
        }
    }

    /* Read AMBER_SPECTRUM binary table */
    if (spectrum != NULL)
    {
        if (amdlibReadAmberSpectrum(filePtr, nbTels,
                                    spectrum, errMsg) != amdlibSUCCESS)
        {
	        amdlibLogTest("No AMBER_SPECTRUM table");
        }
    }
    
    /* Close FITS file */
    if (fits_close_file(filePtr, &status)) 
    {
        amdlibReturnFitsError("closing file");
    }

    return amdlibSUCCESS;
}

/*
 * Protected functions
 */

/*
 * Local functions
 */
/**
 * Write OI_ARRAY table in OI-FITS file
 *
 * This function writes the OI_ARRAY binary table defined in the IAU standard
 * in the OI-FITS file given as parameter.
 *
 * @param filePtr pointer on the file to be created
 * @param array OI_ARRAY produced
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibWriteOiArray(fitsfile        *filePtr,
                                    amdlibOI_ARRAY  *array,
                                    amdlibERROR_MSG errMsg)
{
    const int  tfields = 5;
    char       *ttype[] = {"TEL_NAME", "STA_NAME", "STA_INDEX", "DIAMETER", 
        "STAXYZ"};
    char       *tform[] = {"8A", "8A", "I", "E", "3D"};
    char       *tunit[] = {"\0", "\0", "\0", "m", "m"};
    char       extname[] = "OI_ARRAY";
    int        status = 0;
    int        iRow;
    char       fitsioMsg[256];
    int        revision = amdlib_OI_REVISION;
    char       *str;

    amdlibLogTrace("amdlibWriteOiArray()");

    /* Algorithm */

    /* Create binary table */
    if (fits_create_tbl(filePtr, BINARY_TBL, 1, tfields, ttype, tform, tunit,
                       extname, &status)) 
    {
        amdlibReturnFitsError("Creating Binary Table");
    }

    /* Write revision number */
    if (fits_write_key(filePtr, TINT, "OI_REVN", &revision,
                       "Revision number of the table definition", &status))
    {
        amdlibReturnFitsError("OI_REVN");
    }

    /* Write array name */
    if (fits_write_key(filePtr, TSTRING, "ARRNAME", array->arrayName,
                       "Array name", &status))
    {
        amdlibReturnFitsError("ARRNAME");
    }

    /* Write coordinate frame */
    if (fits_write_key(filePtr, TSTRING, "FRAME", array->coordinateFrame,
                       "Coordinate frame", &status))
    {
        amdlibReturnFitsError("FRAME");
    }

    /* Write array center x coordinate */
    if (fits_write_key(filePtr, TDOUBLE, "ARRAYX", 
                       &array->arrayCenterCoordinates[0],
                       "Array centre x coordinate", &status))
    {
        amdlibReturnFitsError("ARRAYX");
    }

    if (fits_write_key_unit(filePtr, "ARRAYX", "m", &status))
    {
        amdlibReturnFitsError("ARRAYX");
    }

    /* Write array center y coordinate */
    if (fits_write_key(filePtr, TDOUBLE, "ARRAYY", 
                       &array->arrayCenterCoordinates[1],
                       "Array centre y coordinate", &status))
    {
        amdlibReturnFitsError("ARRAYY");
    }

    if (fits_write_key_unit(filePtr, "ARRAYY", "m", &status))
    {
        amdlibReturnFitsError("ARRAYY");
    }

    /* Write array center z coordinate */
    if (fits_write_key(filePtr, TDOUBLE, "ARRAYZ", 
                       &array->arrayCenterCoordinates[2],
                       "Array centre z coordinate", &status)) 
    {
        amdlibReturnFitsError("ARRAYZ");
    }

    if (fits_write_key_unit(filePtr, "ARRAYZ", "m", &status)) 
    {
        amdlibReturnFitsError("ARRAYZ");
    }

    /* Write information on instrument */
    for (iRow = 1; iRow <= array->nbStations; iRow++) 
    {
        str = array->element[iRow-1].telescopeName;
        if (fits_write_col(filePtr, TSTRING, 1, iRow, 1, 1, &str, &status))
        {
            amdlibReturnFitsError("telescopeName");
        }
        str = array->element[iRow-1].stationName;
        if (fits_write_col(filePtr, TSTRING, 2, iRow, 1, 1, &str, &status))
        {
            amdlibReturnFitsError("stationName");
        }
        if (fits_write_col(filePtr, TINT, 3, iRow, 1, 1, 
                           &array->element[iRow-1].stationIndex, &status))
        {
            amdlibReturnFitsError("stationIndex");
        }
        if (fits_write_col(filePtr, TDOUBLE, 4, iRow, 1, 1, 
                           &array->element[iRow-1].elementDiameter, &status))
        {
            amdlibReturnFitsError("diameter");
        }
        if (fits_write_col(filePtr, TDOUBLE, 5, iRow, 1, 3, 
                           array->element[iRow-1].stationCoordinates, &status))
        {
            amdlibReturnFitsError("staXYZ");
        }
    }
    return amdlibSUCCESS;
}


/**
 * Write OI_TARGET table in OI-FITS file.
 *
 * This function writes the OI_TARGET binary table defined in the IAU standard
 * in the OI-FITS file given as parameter.
 *
 * @param filePtr pointer on the file to be created.
 * @param target OI_TARGET produced.
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibWriteOiTarget(fitsfile        *filePtr,
                                     amdlibOI_TARGET *target,
                                     amdlibERROR_MSG errMsg)
{
    const int  tfields = 17;
    char       *ttype[] = {"TARGET_ID", "TARGET", "RAEP0", "DECEP0", "EQUINOX",
        "RA_ERR", "DEC_ERR", "SYSVEL", "VELTYP", "VELDEF", "PMRA", "PMDEC", 
        "PMRA_ERR", "PMDEC_ERR", "PARALLAX", "PARA_ERR", "SPECTYP"};
    char       *tform[] = {"I", "16A", "D", "D", "E", "D", "D", "D", "8A", 
        "8A", "D", "D", "D", "D", "E", "E", "16A"};
    char       *tunit[] = {"\0", "\0", "deg", "deg", "year", "deg", "deg", 
        "m/s", "\0", "\0", "deg/year", "deg/year", "deg/year", "deg/year",
        "deg", "deg", "\0"};
    char       extname[] = "OI_TARGET";
    char       fitsioMsg[256];
    int        status = 0;
    int        revision = amdlib_OI_REVISION;
    int        iRow;
    char       *str;

    amdlibLogTrace("amdlibWriteOiTarget()");

    /* Create binary table */
    if (fits_create_tbl(filePtr, BINARY_TBL, 1, tfields, ttype, tform, tunit,
                       extname, &status)) 
    {
        amdlibReturnFitsError("Creating Binary Table");
    }

    /* Write revision number of the table definition */
    if (fits_write_key(filePtr, TINT, "OI_REVN", &revision,
                       "Revision number of the table definition", &status))
    {
        amdlibReturnFitsError("OI_REVN"); 
    }

    for (iRow = 1; iRow <= target->nbTargets; iRow++) 
    {
        /* Write target identity */
        if (fits_write_col(filePtr, TINT, 1, iRow, 1, 1, 
                          &target->element[iRow-1].targetId, &status))
        {
            amdlibReturnFitsError("targetId");
        }

        str = target->element[iRow-1].targetName;
    
        /* Write target name */
        if (fits_write_col(filePtr, TSTRING, 2, iRow, 1, 1, &str, &status))
        {
            amdlibReturnFitsError("targetName");
        }

        /* Write right ascension at mean equinox */
        if (fits_write_col(filePtr, TDOUBLE, 3, iRow, 1, 1,
                           &target->element[iRow-1].raEp0, &status))
        {
            amdlibReturnFitsError("raEp0");
        }
        /* Write declinaison at mean equinox */
        if (fits_write_col(filePtr, TDOUBLE, 4, iRow, 1, 1,
                           &target->element[iRow-1].decEp0, &status))
        {
            amdlibReturnFitsError("decEp0");
        }
        /* Write equinox of raEp0 and decEp0 */
        if (fits_write_col(filePtr, TDOUBLE, 5, iRow, 1, 1,
                           &target->element[iRow-1].equinox, &status))
        {
            amdlibReturnFitsError("equinox");
        }
        /* Write error in apparent RA */
        if (fits_write_col(filePtr, TDOUBLE, 6, iRow, 1, 1, 
                           &target->element[iRow-1].raErr, &status))
        {
            amdlibReturnFitsError("raErr");
        }
        /* Write error in apparent DEC */
        if (fits_write_col(filePtr, TDOUBLE, 7, iRow, 1, 1,
                           &target->element[iRow-1].decErr, &status))
        {
            amdlibReturnFitsError("decErr");
        }
        /* Write systemic radial velocity */
        if (fits_write_col(filePtr, TDOUBLE, 8, iRow, 1, 1,
                           &target->element[iRow-1].sysVel, &status))
        {
            amdlibReturnFitsError("sysVel");
        }

        str = target->element[iRow-1].velTyp;
        /* Write velocity type of sysVel */
        if (fits_write_col(filePtr, TSTRING, 9, iRow, 1, 1, &str, &status))
        {
            amdlibReturnFitsError("velTyp");
        }

        str = target->element[iRow-1].velDef;
        /* Write definition of radial velocity : "OPTICAL" or "RADIO" */
        if (fits_write_col(filePtr, TSTRING, 10, iRow, 1, 1, &str, &status))
        {
            amdlibReturnFitsError("velDef");
        }

        /* Write proper motion in RA */
        if (fits_write_col(filePtr, TDOUBLE, 11, iRow, 1, 1, 
                           &target->element[iRow-1].pmRa, &status))
        {
            amdlibReturnFitsError("pmRa");
        }
        /* Write proper motion in DEC */
        if (fits_write_col(filePtr, TDOUBLE, 12, iRow, 1, 1, 
                           &target->element[iRow-1].pmDec, &status))
        {
            amdlibReturnFitsError("pmDec");
        }
        /* Write error on pmRa */
        if (fits_write_col(filePtr, TDOUBLE, 13, iRow, 1, 1, 
                           &target->element[iRow-1].pmRaErr, &status))
        {
            amdlibReturnFitsError("pmRaErr");
        }
        /* Write error on pmDec */
        if (fits_write_col(filePtr, TDOUBLE, 14, iRow, 1, 1, 
                           &target->element[iRow-1].pmDecErr, &status))
        {
            amdlibReturnFitsError("pmDecErr");
        }
        /* Write parallax value */
        if (fits_write_col(filePtr, TDOUBLE, 15, iRow, 1, 1, 
                           &target->element[iRow-1].parallax, &status))
        {
            amdlibReturnFitsError("parallax");
        }
        /* Write error in parallax value */
        if (fits_write_col(filePtr, TDOUBLE, 16, iRow, 1, 1, 
                           &target->element[iRow-1].paraErr, &status))
        {
            amdlibReturnFitsError("paraErr");
        }
        /* Write spectral type */
        str = target->element[iRow-1].specTyp;
        if (fits_write_col(filePtr, TSTRING, 17, iRow, 1, 1, &str, &status))
        {
            amdlibReturnFitsError("specTyp");
        }
    }
    return amdlibSUCCESS;
}


/**
 * Write OI_WAVELENGTH table in OI-FITS file.
 *
 * This function writes the OI_WAVELENGTH binary table defined in the IAU 
 * standard in the OI-FITS file given as parameter.
 *
 * @param filePtr pointer on the file to be created.
 * @param insName name of the instrument.
 * @param wave OI_WAVELENGTH produced.
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibWriteOiWavelength(fitsfile         *filePtr,
                                         char             *insName,
                                         amdlibWAVELENGTH *wave,
                                         amdlibERROR_MSG  errMsg)
{
    int        status = 0;
    char       fitsioMsg[256];
    const int  tfields = 2;
    char       *ttype[] = {"EFF_WAVE", "EFF_BAND"};
    char       *tform[] = {"E", "E"};
    char       *tunit[] = {"m", "m"};
    char       extname[] = "OI_WAVELENGTH";
    int        revision = amdlib_OI_REVISION;
    int        i;
    amdlibDOUBLE      waveInM, bwInM;

    amdlibLogTrace("amdlibWriteOiWavelength()");

    /* Create binary table */
    if (fits_create_tbl (filePtr, BINARY_TBL, 1, tfields, ttype, tform, tunit,
                        extname, &status)) 
    {
        amdlibReturnFitsError("Creating Binary Table");
    }

    /* Write revision number of the table definition */
    if (fits_write_key (filePtr, TINT, "OI_REVN", &revision,
                       "Revision number of the table definition", &status)) 
    {
        amdlibReturnFitsError("OI_REVN");
    }

    /* Write spectral setup unique identifier */
    if (fits_write_key (filePtr, TSTRING, "INSNAME", insName,
                       "Instrument name", &status)) 
    {
        amdlibReturnFitsError("INSNAME");
    }

    for (i = 1; i <= wave->nbWlen; i ++)
    {
        waveInM = (wave->wlen[i-1]) * amdlibNM_TO_M; 
        bwInM = (wave->bandwidth[i-1]) * amdlibNM_TO_M; 
        /* Write EFFective_WAVElengh table */
        if (fits_write_col (filePtr, TDOUBLE, 1, i, 1, 1, &waveInM, &status)) 
        {
            amdlibReturnFitsError("EFFective_WAVElengh");
        }

        /* Write EFFective_BANDwidth table */
        if (fits_write_col (filePtr, TDOUBLE, 2, i, 1, 1, &bwInM, &status)) 
        {
            amdlibReturnFitsError("EFFective_BANDwidth");
        }
    }

    return amdlibSUCCESS;
}

#define amdlibWriteOiVis_FREEALL()  free(visError); free(flag); free(convertToDeg);

/**
 * Write OI_VIS table in OI-FITS file
 *
 * This function writes the OI_VIS binary table defined in the IAU 
 * standard in the OI-FITS file given as parameter.
 *
 * @param filePtr pointer on the file to be created
 * @param insName name of the detector
 * @param arrName array name 
 * @param vis OI_VIS produced
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibWriteOiVis(fitsfile        *filePtr, 
                                  char            *insName,
                                  char            *arrName,
                                  amdlibVIS       *vis, 
                                  amdlibERROR_MSG errMsg)

{
    /* Local Declarations */

    int           nbWlen = vis->nbWlen;
    int           lVis;
    double        *convertToDeg;
    amdlibCOMPLEX *visError;
    unsigned char *flag;
    amdlibDOUBLE amdlibBval=amdlibBLANKING_VALUE;

    int           status = 0;
    char          fitsioMsg[256];
    const int     tfields = 14;
    char          *ttype[] = {"TARGET_ID", "TIME", "MJD", "INT_TIME",
        "VISDATA", "VISERR", "VISAMP", "VISAMPERR", "VISPHI", "VISPHIERR",
        "UCOORD", "VCOORD", "STA_INDEX", "FLAG"};
    char          *Tform[] = {"I", "D", "D", "D", "?C", "?C", "?D", "?D", 
        "?D", "?D", "1D", "1D", "2I", "?L"};
    char          *tform[tfields];
    char          *tunit[] = {"\0", "s", "day", "s", "\0", "\0", "\0", "\0", 
        "deg", "deg", "m", "m", "\0", "\0"};
    char          extname[] = "OI_VIS";
    int           revision = amdlib_OI_REVISION;
    char          tmp[16];
    int           iFrame = 0, iBase = 0, iVis = 0, i = 0;
    double        expTime;

    amdlibLogTrace("amdlibWriteOiVis()");

    /* Algorithm */
    if (vis->thisPtr != vis) 
    {
        amdlibSetErrMsg("Unitialized vis structure");
        return amdlibFAILURE; 
    }

    if (vis->nbFrames < 1)
    {
        return amdlibSUCCESS;
    }

    /* Create table structure: Make up Tform: substitute nbWlen for '?' */
    for (i = 0; i < tfields; i++) 
    {
        if (Tform[i][0] == '?')
        {
            sprintf(tmp, "%d%s", nbWlen, &Tform[i][1]);
            tform[i] = calloc((strlen(tmp)+1), sizeof(char));
            strcpy(tform[i], tmp);
        }
        else
        {
            tform[i] = calloc((strlen(Tform[i])+1), sizeof(char));
            strcpy(tform[i], Tform[i]);
        }
    }
    if (fits_create_tbl(filePtr, BINARY_TBL, 0, tfields, ttype, 
                       tform, tunit, extname, &status)) 
    {
        amdlibReturnFitsError("BINARY_TBL");
    }

    for (i = 0; i < tfields; i++)
    {
        free(tform[i]);
    }

    /* Write revision number of the table definition */
    if (fits_write_key(filePtr, TINT, "OI_REVN", &revision, 
                      "Revision number of the table definition", &status))
    {
        amdlibReturnFitsError("OI_REVN");
    }

    /* Give date */
    if (fits_write_key(filePtr, TSTRING, "DATE-OBS", vis->dateObs,
                      "UTC start date of observations", &status))
    {
        amdlibReturnFitsError("DATE-OBS");
    }

    /* Write names of array and detector */
    if (fits_write_key (filePtr, TSTRING, "ARRNAME", arrName,
                       "Array Name", &status))
    {
        amdlibReturnFitsError("ARRNAME");
    }
    if (fits_write_key(filePtr, TSTRING, "INSNAME", insName, 
                      "Instrument name", &status))
    {
        amdlibReturnFitsError("INSNAME");
    }
    
    visError  = calloc(nbWlen, sizeof(amdlibCOMPLEX));
    convertToDeg  = calloc(nbWlen, sizeof(double));
    flag = calloc(nbWlen, sizeof(unsigned char));

    /* Write columns */
    for (iFrame = 0; iFrame < vis->nbFrames; iFrame++) 
    {
        for (iBase = 0; iBase < vis->nbBases; iBase++)
        {
            iVis++;

            /* Write TARGET_IDentity as an index into IO_TARGET table */
            if (fits_write_col(filePtr, TINT, 1, iVis, 1, 1, 
                              &(vis->table[iVis-1].targetId), &status)) 
            {
                amdlibWriteOiVis_FREEALL();
                amdlibReturnFitsError("TARGET_IDentity");
            }

            /* Write utc TIME of observation */
            if (fits_write_col(filePtr, TDOUBLE, 2, iVis, 1, 1,
                              &(vis->table[iVis-1].time), &status))
            {
                amdlibWriteOiVis_FREEALL();
                amdlibReturnFitsError("TIME");
            }

            /* Write observation date in ModifiedJulianDay */
            if (fits_write_col(filePtr, TDOUBLE, 3, iVis, 1, 1,
                              &(vis->table[iVis-1].dateObsMJD), &status))
            {
                amdlibWriteOiVis_FREEALL();
                amdlibReturnFitsError("ModifiedJulianDay");
            }

            /* Write INTegration_TIME (seconds) */
            expTime = (double)vis->table[iVis-1].expTime;
            if (fits_write_col(filePtr, TDOUBLE, 4, iVis, 1, 1,
                              &expTime, &status))
            {
                amdlibWriteOiVis_FREEALL();
                amdlibReturnFitsError("INTegration_TIME");
            }

            /* Writing ComPleXVISibility (no unit) */
            if (fits_write_col(filePtr,TDBLCOMPLEX, 5, iVis, 1, nbWlen,
                              (double*)vis->table[iVis-1].vis, &status))
            {
                amdlibWriteOiVis_FREEALL();
                amdlibReturnFitsError("ComPleXVISibility");
            }

            /* Write ComPleXVISibilityERRor (no unit) */
            /* Need to write Error, NOT sigma2 */
            for (lVis = 0; lVis < nbWlen; lVis++)
            {
                visError[lVis].re = 
                    amdlibSignedSqrt(vis->table[iVis-1].sigma2Vis[lVis].re);
                visError[lVis].im = 
                    amdlibSignedSqrt(vis->table[iVis-1].sigma2Vis[lVis].im);
            }
          
            if (fits_write_col(filePtr,TDBLCOMPLEX,  6, iVis, 1,
                              nbWlen, (double *)visError, &status)) 
            { 
                amdlibWriteOiVis_FREEALL();
                amdlibReturnFitsError("ComPleXVISibilityERRor"); 
            }

            /* Write VISibilityAMPlitude (no unit) */
            if (fits_write_col(filePtr, TDOUBLE, 7, iVis, 1, nbWlen,
                               vis->table[iVis-1].diffVisAmp, &status))
            {
                amdlibWriteOiVis_FREEALL();
                amdlibReturnFitsError("VISibilityAMPlitude");
            }

            /* Write VISibilityAMPlitudeERRor (no unit) */
            if (fits_write_col(filePtr, TDOUBLE, 8, iVis, 1, nbWlen,
                               vis->table[iVis-1].diffVisAmpErr, &status))
            {
                amdlibWriteOiVis_FREEALL();
                amdlibReturnFitsError("VISibilityAMPlitudeERRor");
            }

            /* Write VISibilityPHI (degrees) */
            for(lVis = 0; lVis < nbWlen; lVis++)
            {
                if(vis->table[iVis-1].diffVisPhi[lVis]!=amdlibBLANKING_VALUE)
                {
                    convertToDeg[lVis] = 180.0 / M_PI * 
                    vis->table[iVis-1].diffVisPhi[lVis];
                }
                else
                {
                    convertToDeg[lVis] = amdlibBLANKING_VALUE;
                }
            }

            if (fits_write_colnull(filePtr, TDOUBLE, 9, iVis, 1, nbWlen,
                               convertToDeg, &amdlibBval, &status))
            {
                amdlibWriteOiVis_FREEALL();
                amdlibReturnFitsError("VISibilityPHI");
            }

            /* Write VISibilityPHIERRor (degrees) */
            for(lVis = 0; lVis < nbWlen; lVis++)
            {
                if(vis->table[iVis-1].diffVisPhiErr[lVis]!=amdlibBLANKING_VALUE)
                {
                    convertToDeg[lVis] = 180.0 / M_PI * 
                    vis->table[iVis-1].diffVisPhiErr[lVis];
                }
                else
                {
                    convertToDeg[lVis] = amdlibBLANKING_VALUE;
                }
            }
            if (fits_write_colnull(filePtr, TDOUBLE, 10, iVis, 1, nbWlen,
                               convertToDeg, &amdlibBval, &status))
            {
                amdlibWriteOiVis_FREEALL();
                amdlibReturnFitsError("VISibilityPHIERRor");
            }
            /* Write UCOORDinate (meters) */
            if (fits_write_col(filePtr, TDOUBLE, 11, iVis, 1, 1,
                              &(vis->table[iVis-1].uCoord), &status))
            {
                amdlibWriteOiVis_FREEALL();
                amdlibReturnFitsError("UCOORDinate");
            }

            /* Write VCOORDinates (meters) */
            if (fits_write_col(filePtr, TDOUBLE, 12, iVis, 1, 1,
                              &(vis->table[iVis-1].vCoord), &status))
            {
                amdlibWriteOiVis_FREEALL();
                amdlibReturnFitsError("VCOORDinates");
            }

            /* Write STAtion_INDEX contributing to the data */
            if (fits_write_col(filePtr, TINT, 13, iVis, 1, 2,
                              vis->table[iVis-1].stationIndex, &status))
            {
                amdlibWriteOiVis_FREEALL();
                amdlibReturnFitsError("STAtion_INDEX");
            }
            
            /* Write flag as unsigned char -- does not work with booleans */
            for (lVis = 0; lVis < nbWlen; lVis++)
            {
                flag[lVis] = vis->table[iVis-1].flag[lVis];
            }
          
            if (fits_write_col(filePtr, TLOGICAL, 14, iVis, 1, nbWlen, 
                               flag, &status))
            {
                amdlibWriteOiVis_FREEALL();
                amdlibReturnFitsError("FLAG");
            }
        }
    }
    /* free allocated space at end of job */
    amdlibWriteOiVis_FREEALL();
    return amdlibSUCCESS;
}
#undef   amdlibWriteOiVis_FREEALL

#define amdlibWriteOiVis2_FREEALL()  free(flag);
/**
 * Write OI_VIS2 table in OI-FITS file
 *
 * This function writes the OI_VIS2 binary table defined in the IAU 
 * standard in the OI-FITS file given as parameter.
 *
 * @param filePtr pointer on the file to be created
 * @param insName name of the detector
 * @param arrName array name 
 * @param vis2 OI_VIS2 produced
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibWriteOiVis2( fitsfile        *filePtr,
                                    char            *insName,
                                    char            *arrName,
                                    amdlibVIS2      *vis2,
                                    amdlibERROR_MSG errMsg)
{
    int        nbWlen = vis2->nbWlen;
    int        lVis;
    int        status = 0;
    char       fitsioMsg[256];
    const int  tfields = 10;
    char       *ttype[] = {"TARGET_ID", "TIME", "MJD", "INT_TIME", "VIS2DATA", 
        "VIS2ERR", "UCOORD", "VCOORD", "STA_INDEX", "FLAG"};
    char       *Tform[] = {"I", "D", "D", "D", "?D", "?D", "1D", "1D", "2I", 
        "?L"};
    char       *tform[tfields];
    char       *tunit[] = {"\0", "s", "day", "s", "\0", "\0", "m", "m", "\0",
        "\0"};
    char       extname[] = "OI_VIS2";
    int        revision = amdlib_OI_REVISION;
    int        iFrame = 0, iBase = 0, iVis = 0, i = 0;
    char       tmp[16];
    double     expTime;
    unsigned char *flag;

    amdlibLogTrace("amdlibWriteOiVis2()");
 
    if (vis2->thisPtr != vis2)
    {
        amdlibSetErrMsg("Unitialized vis2 structure");
        return amdlibFAILURE; 
    }
    if (vis2->nbFrames < 1)
    {
        return amdlibSUCCESS;
    }

    /* Create table structure. Make up Tform: substitute nbWlen for '?' */
    for (i = 0; i < tfields; i++) 
    {
        if (Tform[i][0] == '?') 
        {
            sprintf(tmp, "%d%s", nbWlen, &Tform[i][1]);
            tform[i] = calloc((strlen(tmp)+1), sizeof(char));
            strcpy(tform[i], tmp);
        }
        else
        {
            tform[i] = calloc((strlen(Tform[i])+1), sizeof(char));
            strcpy(tform[i], Tform[i]);
        }
    }

    if (fits_create_tbl (filePtr, BINARY_TBL, 0, tfields,
                         ttype, tform, tunit, extname, &status))
    {
        amdlibReturnFitsError("BINARY_TBL");
    }

    for (i = 0; i < tfields; i++)
    {
        free(tform[i]);
    }

    /* Write revision number of the table definition */
    if (fits_write_key (filePtr, TINT, "OI_REVN", &revision,
                        "Revision number of the table definition", &status))
    {
        amdlibReturnFitsError("OI_REVN");
    }

    /* Give date */
    if (fits_write_key(filePtr, TSTRING,"DATE-OBS", vis2->dateObs,
                       "UTC start date of observations", &status))
    {
        amdlibReturnFitsError("DATE-OBS");
    }

    /* Write array and detector names */
    if (fits_write_key (filePtr, TSTRING, "ARRNAME", arrName,
                        "Array Name", &status))
    {
        amdlibReturnFitsError("ARRNAME");
    }
    if (fits_write_key(filePtr, TSTRING, "INSNAME", insName, 
                       "Instrument name", &status))
    {
        amdlibReturnFitsError("INSNAME");
    }

    flag = calloc(nbWlen, sizeof(unsigned char));

    /* Write columns */
    for (iFrame = 0; iFrame < vis2->nbFrames; iFrame++) 
    {
        for (iBase = 0; iBase < vis2->nbBases; iBase++)
        {
            iVis++;
            /* Write TARGET_IDentity */
            if (fits_write_col (filePtr, TINT, 1, iVis, 1, 1,
                                &(vis2->table[iVis-1].targetId), &status))
            {
	       amdlibWriteOiVis2_FREEALL();
               amdlibReturnFitsError("TARGET_IDentity");
            }

            /* Write time */
            if (fits_write_col (filePtr, TDOUBLE, 2, iVis, 1, 1,
                                &(vis2->table[iVis-1].time), &status))
            {
                amdlibWriteOiVis2_FREEALL();
                amdlibReturnFitsError("Time");
            }
            if (fits_write_col (filePtr, TDOUBLE, 3, iVis, 1, 1,
                                &(vis2->table[iVis-1].dateObsMJD), &status))
            {
                amdlibWriteOiVis2_FREEALL();
                amdlibReturnFitsError("dateObsMJD");
            }
            expTime = (double)vis2->table[iVis-1].expTime;
            if (fits_write_col (filePtr, TDOUBLE, 4, iVis, 1, 1,
                                &expTime, &status))
            {
                amdlibWriteOiVis2_FREEALL();
                amdlibReturnFitsError("expTime");
            }

            /* Write squared visibility */
            if (fits_write_col(filePtr, TDOUBLE, 5, iVis, 1, nbWlen,
                               vis2->table[iVis-1].vis2, &status))
            {
                amdlibWriteOiVis2_FREEALL();
                amdlibReturnFitsError("vis2");
            }
            /* Write error in the squared visibility */
            if (fits_write_col(filePtr, TDOUBLE, 6, iVis, 1, nbWlen,
                               vis2->table[iVis-1].vis2Error, &status))
            {
                amdlibWriteOiVis2_FREEALL();
                amdlibReturnFitsError("vis2Error");
            }

            /* Write U and V coordinates on the data */
            if (fits_write_col (filePtr, TDOUBLE, 7, iVis, 1, 1,
                                &(vis2->table[iVis-1].uCoord), &status))
            {
                amdlibWriteOiVis2_FREEALL();
                amdlibReturnFitsError("uCoord");
            }
            if (fits_write_col (filePtr, TDOUBLE, 8, iVis, 1, 1,
                                &(vis2->table[iVis-1].vCoord), &status))
            {
                amdlibWriteOiVis2_FREEALL();
                amdlibReturnFitsError("vCoord");
            }

            /* Write station index */
            if (fits_write_col (filePtr, TINT, 9, iVis, 1, 2,
                                vis2->table[iVis-1].stationIndex, &status))
            {
                amdlibWriteOiVis2_FREEALL();
                amdlibReturnFitsError("stationIndex");
            }
            /* Write flag as unsigned char -- does not work with booleans */
            for (lVis = 0; lVis < nbWlen; lVis++)
            {
                flag[lVis] = vis2->table[iVis-1].flag[lVis];
            }
            if (fits_write_col(filePtr, TLOGICAL, 10, iVis, 1, nbWlen, 
                               flag, &status))
            {
                amdlibWriteOiVis2_FREEALL();
                amdlibReturnFitsError("FLAG");
            }

        }
    }
    /* free allocated space at end of job */
    amdlibWriteOiVis2_FREEALL();
    return amdlibSUCCESS;
}

#define amdlibWriteOiVis3_FREEALL() free(flag); free(convertToDeg);
/**
 * Write OI_T3 table in OI-FITS file.
 *
 * This function writes the OI_T3 binary table defined in the IAU 
 * standard in the OI-FITS file given as parameter.
 *
 * @param filePtr pointer on the file to be created.
 * @param insName name of the detector.
 * @param arrName array name.
 * @param vis3 OI_T3 produced.
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibWriteOiVis3(fitsfile        *filePtr,
                                   char            *insName,
                                   char            *arrName,
                                   amdlibVIS3      *vis3,
                                   amdlibERROR_MSG errMsg)
{
    int        nbWlen =vis3->nbWlen;
    int        lVis;
    int        status = 0;
    char       fitsioMsg[256];
    const int  tfields = 14;
    char       *ttype[] = {"TARGET_ID", "TIME", "MJD", "INT_TIME", "T3AMP", 
        "T3AMPERR", "T3PHI", "T3PHIERR", "U1COORD", "V1COORD", "U2COORD", 
        "V2COORD", "STA_INDEX", "FLAG"};
    char       *Tform[] = {"I", "D", "D", "D", "?D", "?D", "?D", "?D", "1D", 
        "1D", "1D", "1D", "3I", "?L"};
    char       *tform[tfields];
    char       *tunit[] = {"\0", "s", "day", "s", "\0", "\0", "deg", "deg",
        "m", "m", "m", "m", "\0", "\0"};
    char       extname[] = "OI_T3";
    int        revision = amdlib_OI_REVISION;
    int        i;
    char       tmp[16];
    double     expTime;
    double    *convertToDeg;
    unsigned char *flag;
    amdlibDOUBLE amdlibBval=amdlibBLANKING_VALUE;
    
    int        iFrame = 0, iClos = 0, iT3 = 0;

    amdlibLogTrace("amdlibWriteOiVis3()");

    /*Vis3 may not be initialized (only 1 baseline), this is not a defect*/
    if (vis3->thisPtr != vis3)
    {
        return amdlibSUCCESS;
    }
    /*If initialized but empty, do nothing gracefully*/
    if (vis3->nbFrames < 1)
    {
        return amdlibSUCCESS;
    }

    /* Create table structure. Make up Tform: substitute nbWlen for '?' */
    for (i = 0; i < tfields; i++) 
    {
        if (Tform[i][0] == '?') 
        {
            sprintf(tmp, "%d%s", nbWlen, &Tform[i][1]);
            tform[i] = calloc((strlen(tmp)+1), sizeof(char));
            strcpy(tform[i], tmp);
        }
        else
        {
            tform[i] = calloc((strlen(Tform[i])+1), sizeof(char));
            strcpy(tform[i], Tform[i]);
        }
    }
    if (fits_create_tbl(filePtr, BINARY_TBL, 0, tfields, ttype, tform, tunit,
                        extname, &status)) 
    {
        amdlibReturnFitsError("Creating Binary Table");
    }

    for (i = 0; i < tfields; i++) 
    {
        free(tform[i]);
    }

    /* Write revision number of the table definition */
    if (fits_write_key (filePtr, TINT, "OI_REVN", &revision,
                        "Revision number of the table definition", &status))
    {
        amdlibReturnFitsError("OI_REVN");
    }

    /* Give date */
    if (fits_write_key(filePtr, TSTRING,"DATE-OBS", vis3->dateObs,
                       "UTC start date of observations", &status))
    {
        amdlibReturnFitsError("DATE-OBS");
    }

    /* Write names of detector and array */
    if (fits_write_key (filePtr, TSTRING, "ARRNAME", arrName,
                        "Array Name", &status))
    {
        amdlibReturnFitsError("ARRNAME");
    }
    if (fits_write_key(filePtr, TSTRING, "INSNAME", insName, 
                       "Instrument name", &status))
    {
        amdlibReturnFitsError("INSNAME");
    }

    convertToDeg  = calloc(nbWlen, sizeof(double));
    flag = calloc(nbWlen, sizeof(unsigned char));

    /* Write columns */
    for (iFrame = 0; iFrame < vis3->nbFrames; iFrame++) 
    {
        for (iClos = 0; iClos < vis3->nbClosures; iClos++)
        {            
            /* Write dtarget identity */ 
            if (fits_write_col(filePtr, TINT, 1, iT3+1, 1, 1,
                               &(vis3->table[iT3].targetId), &status))
            {
                amdlibWriteOiVis3_FREEALL();
                amdlibReturnFitsError("TARGET_ID");
            }

            /* Write time */
            if (fits_write_col(filePtr, TDOUBLE, 2, iT3+1, 1, 1,
                               &(vis3->table[iT3].time), &status))
            {
                amdlibWriteOiVis3_FREEALL();
                amdlibReturnFitsError("TIME");
            }
            if (fits_write_col(filePtr, TDOUBLE, 3, iT3+1, 1, 1,
                               &(vis3->table[iT3].dateObsMJD), &status))
            {
                amdlibWriteOiVis3_FREEALL();
                amdlibReturnFitsError("DATEOBS");
            }
            expTime = (double)vis3->table[iT3].expTime;
            if (fits_write_col(filePtr, TDOUBLE, 4, iT3+1, 1, 1,
                               &expTime, &status))
            {
                amdlibWriteOiVis3_FREEALL();
                amdlibReturnFitsError("EXPTIME");
            }

            /* Write visibility amplitude and its associated error */
            if (fits_write_col(filePtr, TDOUBLE, 5, iT3+1, 1, nbWlen,
                               vis3->table[iT3].vis3Amplitude, &status))
            {
                amdlibWriteOiVis3_FREEALL();
                amdlibReturnFitsError("VIS3AMP");
            }
            if (fits_write_col(filePtr, TDOUBLE, 6, iT3+1, 1, nbWlen,
                               vis3->table[iT3].vis3AmplitudeError,
                               &status))
            {
                amdlibWriteOiVis3_FREEALL();
                amdlibReturnFitsError("VIS3AMPERR");
            }

            /* Write visibility phase and its associated error,
             * converted in degrees */
            for (lVis = 0; lVis < nbWlen; lVis++)
            {
                if(vis3->table[iT3].vis3Phi[lVis]!=amdlibBLANKING_VALUE)
                {
                    convertToDeg[lVis] = 180.0 / M_PI * 
                    vis3->table[iT3].vis3Phi[lVis];
                }
                else
                {
                    convertToDeg[lVis] =amdlibBLANKING_VALUE;
                }
                
            }
            if (fits_write_colnull(filePtr, TDOUBLE, 7, iT3+1, 1, nbWlen,
                               convertToDeg, &amdlibBval, &status))
            {
                amdlibWriteOiVis3_FREEALL();
                amdlibReturnFitsError("VIS3PHI");
            }
            for(lVis = 0; lVis < nbWlen; lVis++)
            {
                if(vis3->table[iT3].vis3PhiError[lVis]!=amdlibBLANKING_VALUE)
                {
                    convertToDeg[lVis] = 180.0 / M_PI * 
                    vis3->table[iT3].vis3PhiError[lVis];
                }
                else
                {
                    convertToDeg[lVis] =amdlibBLANKING_VALUE;
                }
            }
            if (fits_write_colnull(filePtr, TDOUBLE, 8, iT3+1, 1, nbWlen,
                               convertToDeg, &amdlibBval, &status))
            {
                amdlibWriteOiVis3_FREEALL();
                amdlibReturnFitsError("VIS3PHIERR");
            }

            /* Write U and V coordinates of the data */
            if (fits_write_col(filePtr, TDOUBLE, 9, iT3+1, 1, 1,
                               &(vis3->table[iT3].u1Coord), &status))
            {
                amdlibWriteOiVis3_FREEALL();
                amdlibReturnFitsError("U1COORD");
            }
            if (fits_write_col(filePtr, TDOUBLE, 10, iT3+1, 1, 1,
                               &(vis3->table[iT3].v1Coord), &status))
            {
                amdlibWriteOiVis3_FREEALL();
                amdlibReturnFitsError("V1COORD");
            }
            if (fits_write_col(filePtr, TDOUBLE, 11, iT3+1, 1, 1,
                               &(vis3->table[iT3].u2Coord), &status))
            {
                amdlibWriteOiVis3_FREEALL();
                amdlibReturnFitsError("U2COORD");
            }
            if (fits_write_col(filePtr, TDOUBLE, 12, iT3+1, 1, 1,
                               &(vis3->table[iT3].v2Coord), &status))
            {
                amdlibWriteOiVis3_FREEALL();
                amdlibReturnFitsError("V2COORD");
            }

            /* Write station index */
            if (fits_write_col(filePtr, TINT, 13, iT3+1, 1, 3,
                               vis3->table[iT3].stationIndex, &status))
            {
                amdlibWriteOiVis3_FREEALL();
                amdlibReturnFitsError("STATINDEX");
            }
            
            /* Write flag as unsigned char -- does not work with booleans */
            for (lVis = 0; lVis < nbWlen; lVis++)
            {
                flag[lVis] = vis3->table[iT3].flag[lVis];
            }
            if (fits_write_col(filePtr, TLOGICAL, 14, iT3+1, 1, nbWlen, 
                               flag, &status))
            {
                amdlibWriteOiVis3_FREEALL();
                amdlibReturnFitsError("FLAG");
            }

            iT3++;

        }
    }
    amdlibWriteOiVis3_FREEALL();

    return amdlibSUCCESS;
}

#define amdlibWriteAmberData_FREEALL() free(errTempVal);          \
    free(tempBandNumber); 
/**
 * Write AMBER_DATA table in OI-FITS file.
 *
 * This function writes the AMBER_DATA binary table in the OI-FITS file 
 * given as parameter.
 *
 * @param filePtr pointer on the file to be created.
 * @param insName name of the detector.
 * @param photometry photometry data.
 * @param vis AMBER_DATA produced.
 * @param pst piston data.
 * @param wave structure containing the wavelengths.
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibWriteAmberData(fitsfile         *filePtr,
                                      char             *insName,
                                      amdlibPHOTOMETRY *photometry,
                                      amdlibVIS        *vis,
                                      amdlibPISTON     *pst,
                                      amdlibWAVELENGTH *wave, 
                                      amdlibERROR_MSG  errMsg)
{
    int        nbWlen = photometry->nbWlen;
    int        status = 0;
    char       fitsioMsg[256];
    const int  tfields = 13;
    amdlibDOUBLE amdlibBval=amdlibBLANKING_VALUE;
    char       *Ttype[] = 
    {
        "TARGET_ID", "TIME", "MJD", "INT_TIME", 
        "FLUX_SUM", "FLUX_SUM_CORRECTION", "FLUX_RATIO", 
        "FLUX_RATIO_CORRECTION", "FLUX_PRODUCT", 
        "OPD", "OPD_ERR", "FRINGE_SNR", "STA_INDEX"
    };
    
    char       *Tform[] = {"I", "D", "D", "D", "?D", "?D", "?D", "?D", "?D", 
        "?E", "?E", "?D", "2I"};
    
    char       *Tunit[] = {"\0", "s", "day", "s", "e-", "e-", "\0", "\0",
        "e-^2", "m", "m", "\0", "\0"};
    
    char       *ttype[tfields];
    char       *tform[tfields];
    char       *tunit[tfields];
    
    char       extname[] = "AMBER_DATA";
    int        revision = amdlib_OI_REVISION;
    int        colNum;
    int        i;
    int        lVis;
    char       tmp[16];
    int        iFrame = 0, iBase = 0, iVis = 0;
    double     *errTempVal;
    int        *tempBandNumber;
    int        band, iBand;
    int        nbBands = 0;
    amdlibBAND_DESC *bandDesc;
    amdlibDOUBLE      bound;
    char       keyName[amdlibKEYW_NAME_LEN+1];
    double     expTime;
    amdlibDOUBLE      pistonTmpVal[amdlibNB_BANDS];
    amdlibDOUBLE      pstErrTmpVal[amdlibNB_BANDS];
    double     frgTmpVal[amdlibNB_BANDS];

    amdlibLogTrace("amdlibWriteAmberData()");

    if (vis->thisPtr != vis) 
    {
        amdlibSetErrMsg("Unitialized vis structure");
        return amdlibFAILURE; 
    }

    /*If initialized but empty, do nothing gracefully*/
    if (vis->nbFrames < 1)
    {
        return amdlibSUCCESS;
    }
    
    if (photometry->thisPtr != photometry) 
    {
        amdlibSetErrMsg("Unitialized photometry structure");
        return amdlibFAILURE; 
    }
    /*If initialized but empty, do nothing gracefully*/
    if (photometry->nbFrames < 1)
    {
        return amdlibSUCCESS;
    }

    if (pst->thisPtr != pst) 
    {
        amdlibSetErrMsg("Unitialized piston structure");
        return amdlibFAILURE; 
    }
    /*If initialized but empty, do nothing gracefully*/
    if (pst->nbFrames < 1)
    {
        return amdlibSUCCESS;
    }

    /* Create table structure. Make up Tform: substitute nbWlen or
     * nbBands for '?' */
    /* (informational) check number of bands */
    nbBands=0;
    for (band = amdlibJ_BAND; band <= amdlibK_BAND; band++)
    {
        if (pst->bandFlag[band] == amdlibTRUE)
        {
            nbBands++;
        }
    }    
    for (i = 0; i < tfields; i++) 
    {
        ttype[i] = calloc(strlen(Ttype[i])+1, sizeof(char));
        strcpy(ttype[i],Ttype[i]);
        tunit[i] = calloc(strlen(Tunit[i])+1, sizeof(char));
        strcpy(tunit[i],Tunit[i]);
        
        if (Tform[i][0] == '?') 
        {
            if ((i == 9) || (i == 10) || (i == 11))
            {
                sprintf(tmp, "%d%s", nbBands, &Tform[i][1]);
            }
            else
            {
                sprintf(tmp, "%d%s", nbWlen, &Tform[i][1]);
            }
            tform[i] = calloc((strlen(tmp)+1), sizeof(char));
            strcpy(tform[i], tmp);
        }
        else
        {
            tform[i] = calloc((strlen(Tform[i])+1), sizeof(char));
            strcpy(tform[i], Tform[i]);
        }
    }

    if (fits_create_tbl(filePtr, BINARY_TBL, 0, tfields, ttype, tform, tunit,
                        extname, &status)) 
    {
        amdlibReturnFitsError("Creating Binary Table");
    }

    for (i = 0; i < tfields; i++)
    {
        free(ttype[i]);
        free(tform[i]);
        free(tunit[i]);
    }

    /* Give date */
    if (fits_write_key(filePtr, TSTRING, "DATE-OBS", vis->dateObs,
                       "UTC start date of observations", &status))
    {
        amdlibReturnFitsError("DATE-OBS");
    }

    /* Write detector name */
    if (fits_write_key (filePtr, TSTRING, "INSNAME", insName,
                        "Instrument name", &status))
    {
        amdlibReturnFitsError("INSNAME");
    }

    /* Write revision number of the table definition */
    if (fits_write_key (filePtr, TINT, "AMB_REVN", &revision,
                        "Revision number of the table definition", &status))
    {
        amdlibReturnFitsError("AMB_REVN");
    }

    /* Write information on spectral bands. */
    if (fits_write_key (filePtr, TINT, "HIERARCH ESO QC NBBANDS", 
                        &nbBands, "Number of spectral bands", &status))
    {
        amdlibReturnFitsError("HIERARCH ESO QC NBBANDS");
    }

    iBand = 1;
    for (band = amdlibJ_BAND; band <= amdlibK_BAND; band++)
    {
        if (pst->bandFlag[band] == amdlibTRUE)
        {
            bandDesc = amdlibGetBandDescription(band);
            sprintf(keyName, "HIERARCH ESO QC BAND%d NAME", iBand);
            if (fits_write_key(filePtr, TSTRING, keyName, bandDesc->name, 
                               "Name of spectral band", &status))
            {
                amdlibReturnFitsError(keyName);
            }
            memset(keyName, '\0', (amdlibKEYW_NAME_LEN+1) * sizeof(char));
            sprintf(keyName, "HIERARCH ESO QC BAND%d LOWBOUND", iBand);
            bound = bandDesc->lowerBound/1000;
            if (fits_write_key(filePtr, TDOUBLE, keyName, &bound,
                               "Lower bound of spectral band", &status))
            {
                amdlibReturnFitsError(keyName);
            }
            memset(keyName, '\0', (amdlibKEYW_NAME_LEN+1) * sizeof(char));
            sprintf(keyName, "HIERARCH ESO QC BAND%d UPBOUND", iBand);
            bound = bandDesc->upperBound/1000;
            if (fits_write_key(filePtr, TDOUBLE, keyName, &bound, 
                               "Upper bound of spectral band", &status))
            {
                amdlibReturnFitsError(keyName);
            }
            iBand++;
        }

    }

    errTempVal = calloc(nbWlen, sizeof(double));
    tempBandNumber = calloc(nbWlen, sizeof(int));
    /* Retrieve Band number for each Wlen */
    for (lVis = 0; lVis < nbWlen; lVis++)
    {
        tempBandNumber[lVis] = amdlibGetBand(wave->wlen[lVis]);
    }

    /* Write columns */
    for (iFrame = 0; iFrame < vis->nbFrames; iFrame++) 
    {
        for (iBase = 0; iBase < vis->nbBases; iBase++)
        {
            colNum = 1;
            iVis++;
            /* Write target identity */
            if (fits_write_col(filePtr, TINT, colNum, iVis, 1, 1,
                               &(vis->table[iVis-1].targetId), &status))
            {
                amdlibWriteAmberData_FREEALL();
                amdlibReturnFitsError("Target Id");
            }
            colNum++;

            /* Write time */
            if (fits_write_col(filePtr, TDOUBLE, colNum, iVis, 1, 1,
                               &(vis->table[iVis-1].time), &status))
            {
                amdlibWriteAmberData_FREEALL();
                amdlibReturnFitsError("Time");
            }
            colNum++;
            
            if (fits_write_col(filePtr, TDOUBLE, colNum, iVis, 1, 1,
                               &(vis->table[iVis-1].dateObsMJD), &status))
            {
                amdlibWriteAmberData_FREEALL();
                amdlibReturnFitsError("dateObsMJD");
            }
            colNum++;

            expTime = (double)vis->table[iVis-1].expTime;
            if (fits_write_col(filePtr, TDOUBLE, 4, iVis, 1, 1,
                               &expTime, &status))
            {
                amdlibWriteAmberData_FREEALL();
                amdlibReturnFitsError("expTime");
            }
            colNum++;
            
            /* Write information on photometry */
            /* Write fluxSumPiPj (number of electrons collected in the spectral
             * channel) and associated error */
            if (fits_write_colnull(filePtr, TDOUBLE, colNum, iVis, 1, nbWlen,
                               photometry->table[iVis-1].fluxSumPiPj,
				   &amdlibBval, &status))
            {
                amdlibWriteAmberData_FREEALL();
                amdlibReturnFitsError("baseFluxSum");
            }
            colNum++;
            
            for (lVis = 0; lVis < nbWlen; lVis++)
            {
                if (!(photometry->table[iVis-1].sigma2FluxSumPiPj[lVis] == 
                         amdlibBLANKING_VALUE))
                {
                    errTempVal[lVis] = 
                        sqrt(photometry->table[iVis-1].sigma2FluxSumPiPj[lVis]);
                }
                else 
                {
                    errTempVal[lVis] = amdlibBLANKING_VALUE;
                }
            }

            if (fits_write_colnull(filePtr, TDOUBLE, colNum, iVis, 1, nbWlen,
                               errTempVal, &amdlibBval, &status))
            {
                amdlibWriteAmberData_FREEALL();
                amdlibReturnFitsError("baseFluxSumCorrection");
            }
            colNum++;

            /* Write flux ratio in the spectral channel and associated error */
            if (fits_write_colnull(filePtr, TDOUBLE, colNum, iVis, 1, nbWlen,
                               photometry->table[iVis-1].fluxRatPiPj,
				   &amdlibBval, &status))
            {
                amdlibWriteAmberData_FREEALL();
                amdlibReturnFitsError("baseFluxRatio");
            }
            colNum++;

            for (lVis = 0; lVis < nbWlen; lVis++)
            {
                if (!(photometry->table[iVis-1].sigma2FluxRatPiPj[lVis] == 
                         amdlibBLANKING_VALUE)) 
                {
                    errTempVal[lVis]=
                        sqrt(photometry->table[iVis-1].sigma2FluxRatPiPj[lVis]);
                }
                else 
                {
                    errTempVal[lVis] = amdlibBLANKING_VALUE;
                }
            }
            if (fits_write_colnull(filePtr, TDOUBLE, colNum, iVis, 1, nbWlen,
                               errTempVal, &amdlibBval, &status))
            {
                amdlibWriteAmberData_FREEALL();
                amdlibReturnFitsError("baseFluxRatioCorrection");
            }
            colNum++;
            
            /* Write flux product */
            if (fits_write_colnull(filePtr, TDOUBLE, colNum, iVis, 1, nbWlen,
                               photometry->table[iVis-1].PiMultPj, 
				   &amdlibBval, &status))
            {
                amdlibWriteAmberData_FREEALL();
                amdlibReturnFitsError("baseFluxProduct");
            }
            colNum++;
            
            /* Write piston value and associated error */       
            int i = 0;
            for (band = amdlibJ_BAND; band <= amdlibK_BAND; band++)
            {
                if (pst->bandFlag[band] == amdlibTRUE )
                {
                    if (!amdlibCompareDouble(pst->pistonOPDArray[band][iVis-1],amdlibBLANKING_VALUE))
                    {
                        pistonTmpVal[i] = pst->pistonOPDArray[band][iVis-1] * 
                        amdlibNM_TO_M;
                        pstErrTmpVal[i] = pst->sigmaPistonArray[band][iVis-1] * 
                        amdlibNM_TO_M;
                    }
                    else
                    {
                        pistonTmpVal[i] = amdlibBLANKING_VALUE;
                        pstErrTmpVal[i] = amdlibBLANKING_VALUE;
                    }
                    frgTmpVal[i] = vis->table[iVis-1].frgContrastSnrArray[band];
                    i++;
                }
            }
            if (fits_write_colnull(filePtr, TDOUBLE, colNum, iVis, 1, nbBands,
                               pistonTmpVal, &amdlibBval, &status))
            {
                amdlibWriteAmberData_FREEALL();
                amdlibReturnFitsError("piston");
            }
            colNum++;
            
            if (fits_write_colnull(filePtr, TDOUBLE, colNum, iVis, 1, nbBands,
                               pstErrTmpVal, &amdlibBval, &status))
            {
                amdlibWriteAmberData_FREEALL();
                amdlibReturnFitsError("pistonErr");
            }
            colNum++;

            /* Write fringe contrast SNR */       
            if (fits_write_col(filePtr, TDOUBLE, colNum, iVis, 1, nbBands,
                               frgTmpVal, &status))
            {
                amdlibWriteAmberData_FREEALL();
                amdlibReturnFitsError("frgContrastSnr");
            }
            colNum++;
            
            /* Write station idexes corresponding to the baseline */       
            if (fits_write_col(filePtr, TINT, colNum, iVis, 1, 2,
                               vis->table[iVis-1].stationIndex, &status))
            {
                amdlibWriteAmberData_FREEALL();
                amdlibReturnFitsError("stationIndex");
            }
            colNum++;
        }
    }
    amdlibWriteAmberData_FREEALL();
    return amdlibSUCCESS;
}
#undef   amdlibWriteAmberData_FREEALL

/**
 * Read OI_WAVELENGTH table in OI-FITS file
 *
 * This function reads the OI_WAVELENGTH binary table defined in the IAU 
 * standard in the OI-FITS file given as parameter.
 *
 * @param filePtr pointer on the file to be read
 * @param wave OI_WAVELENGTH produced
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibReadOiWavelength(fitsfile             *filePtr,
                                        amdlibWAVELENGTH     *wave,
                                        amdlibERROR_MSG      errMsg)
{
    int        status = 0;
    char       fitsioMsg[256];
    char       comment[amdlibKEYW_CMT_LEN+1], insName[amdlibKEYW_VAL_LEN+1];
    amdlibDOUBLE      null_amdlibDOUBLE = 0.0;
    const int  revision = amdlib_OI_REVISION;
    int        dataRevision;
    int        colnum, anynull, i;
    long       nbWlen;

    amdlibLogTrace("amdlibReadOiWavelength()");

    /* Go to OI_WAVELENGTH value of EXTNAME keyword */
    if (fits_movnam_hdu(filePtr, BINARY_TBL, "OI_WAVELENGTH", 0, &status))
    {
        amdlibReturnFitsError("No OI_WAVELENGTH table");
    }

    /* Read detector name */
    if (fits_read_key(filePtr, TSTRING, "INSNAME", insName,
                      comment, &status))
    {
        amdlibReturnFitsError("No INSNAME keyword found");
    }

    /* Read revision number of the table definition */
    if (fits_read_key(filePtr, TINT, "OI_REVN", &dataRevision, 
                      comment, &status))
    {
        amdlibReturnFitsError("No OI_REVN keyword found");
    }

    if (dataRevision > revision) 
    {
        amdlibSetErrMsg("Unable to read this OI revision of the data");
        return amdlibFAILURE; 
    }

    /* Get number of channels */
    if (fits_get_num_rows(filePtr, &nbWlen, &status))
    {
        amdlibReturnFitsError("Getting the number of spectral channels");
    }

    /* Allocate memory wavelength */
    if (amdlibAllocateWavelength(wave, nbWlen, errMsg) != amdlibSUCCESS)
    {
        return amdlibFAILURE;
    }

    wave->nbWlen = nbWlen;

    /* Read columns */
    /* Read effective wavelength of each channel */
    if (fits_get_colnum(filePtr, CASEINSEN, "EFF_WAVE", &colnum, &status))
    {
        amdlibReturnFitsError("number of cols of EFF_WAVE");
    }
    if (fits_read_col(filePtr, TDOUBLE, colnum, 1, 1, wave->nbWlen, &null_amdlibDOUBLE,
                       wave->wlen, &anynull, &status))
    {
        amdlibReturnFitsError("reading the col EFF_WAVE");
    }

    /* Read effective bandpass of each channel */
    if (fits_get_colnum(filePtr, CASEINSEN, "EFF_BAND", &colnum, &status))
    {
        amdlibReturnFitsError("number of cols of EFF_BAND");
    }
    if (fits_read_col(filePtr, TDOUBLE, colnum, 1, 1, wave->nbWlen,
                      &null_amdlibDOUBLE, wave->bandwidth, &anynull, &status))
    {
        amdlibReturnFitsError("reading the col EFF_BAND");
    }
    /* Convert wavelengths and bandpass into meters */
    for (i=0; i < wave->nbWlen; i++)
    {
        wave->wlen[i] /= amdlibNM_TO_M;
        wave->bandwidth[i] /= amdlibNM_TO_M;
    }

    return amdlibSUCCESS;
}

/**
 * Read OI_VIS table in OI-FITS file
 *
 * This function reads the OI_VIS binary table defined in the IAU 
 * standard in the OI-FITS file given as parameter.
 *
 * @param filePtr pointer on the file to be read
 * @param vis OI_VIS produced
 * @param nbBases input parameter giving the number of baselines.  
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibReadOiVis(fitsfile        *filePtr,
                                 amdlibVIS       *vis,
                                 int             nbBases,
                                 amdlibERROR_MSG errMsg)
{
    char          comment[amdlibKEYW_CMT_LEN+1];
    int           nullint = 0;
    double        nulldouble = 0;
    amdlibDOUBLE amdlibBval=amdlibBLANKING_VALUE;
    amdlibBOOLEAN nulllogical = amdlibFALSE;
    const int     revision = amdlib_OI_REVISION;
    int           dataRevision;
    int           iFrame, colnum, anynull;
    int           status = 0; 
    char          fitsioMsg[256];
    long          nbRows, nbWlen, nbFrames;
    char          insName[amdlibKEYW_VAL_LEN+1], arrName[amdlibKEYW_VAL_LEN+1];
    char          dateObsMJD[amdlibKEYW_VAL_LEN+1];
    char          flagColSize[amdlibKEYW_VAL_LEN+1];
    unsigned char *flag;
    int lVis;

    amdlibLogTrace("amdlibReadOiVis()");

    /* Read the OI table */
    if (fits_movnam_hdu(filePtr, BINARY_TBL, "OI_VIS", 0, &status))
    {
        amdlibReturnFitsError("OI_VIS");
    }

    /* Read revision number of the table definition */
    if (fits_read_key(filePtr, TINT, "OI_REVN", &dataRevision,
                      comment, &status))
    {
        amdlibReturnFitsError("OI_REVN");
    }
    if (dataRevision > revision) 
    {
        amdlibSetErrMsg("Unable to read this OI revision of the data");
        return amdlibFAILURE;
    }

    /* Read observation date */
    if (fits_read_key(filePtr, TSTRING, "DATE-OBS", dateObsMJD,
                      comment, &status))
    {
        amdlibLogWarning("No DATE-OBS keyword !");
        status = 0;
    }

    /* Read names of array and detector */
    if (fits_read_key(filePtr, TSTRING, "ARRNAME", arrName,
                      comment, &status))
    {
        amdlibLogWarning("No ARRNAME keyword !");
        status = 0;
    }
    if (fits_read_key(filePtr, TSTRING, "INSNAME", insName,
                      comment, &status))
    {
        amdlibLogWarning("No INSNAME keyword !");
        status = 0;
    }

    /* Get flag column dimension */
    if (fits_read_key(filePtr, TSTRING, "TFORM14", flagColSize,
                      comment, &status))
    {
        amdlibLogWarning("No TFORM14 keyword !");
        status = 0;
    }

    /* Get number of rows */
    if (fits_get_num_rows(filePtr, &nbRows, &status))
    {
        amdlibReturnFitsError("nbRows");
    }

    /* Get value for nbWlen */
    /* Format specifies same repeat count for VIS* columns */
    if (fits_get_colnum(filePtr, CASEINSEN, "VISDATA", &colnum, &status))
    {
        amdlibReturnFitsError("VISDATA");
    }

    if (fits_get_coltype(filePtr, colnum, NULL, &nbWlen, NULL, &status))
    {
        amdlibReturnFitsError("VISDATA");
    }

    nbFrames = nbRows / nbBases;

    /* Allocate memory vis */
    if (amdlibAllocateVis(vis, nbFrames, nbBases, nbWlen) != amdlibSUCCESS)
    {
        amdlibReturnFitsError("Could not allocate memory for VIS");
    }

    strcpy(vis->dateObs, dateObsMJD);
    
    for (iFrame=1; iFrame <= nbRows; iFrame++) 
    {
        int j;
        double im, re;
        
        /* Read target identity */
        if (fits_get_colnum(filePtr, CASEINSEN, "TARGET_ID", &colnum, &status))
        {
            amdlibReturnFitsError("TARGET_ID");
        }
        if (fits_read_col(filePtr, TINT, colnum, iFrame, 1, 1, &nullint,
                          &vis->table[iFrame-1].targetId, &anynull, &status))
        {
            amdlibReturnFitsError("TARGET_ID");
        }

        /* Read time tag for the exposure */
        if (fits_get_colnum(filePtr, CASEINSEN, "TIME", &colnum, &status))
        {
            amdlibReturnFitsError("TIME");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, 1,
                          &nulldouble, &vis->table[iFrame-1].time,
                          &anynull, &status))
        {
            amdlibReturnFitsError("TIME");
        }

        /* Read observation date */
        if (fits_get_colnum(filePtr, CASEINSEN, "MJD", &colnum, &status))
        {
            amdlibReturnFitsError("MJD");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, 1,
                          &nulldouble, &vis->table[iFrame-1].dateObsMJD,
                          &anynull, &status))
        {
            amdlibReturnFitsError("MJD");
        }

        /* Read integration time */
        if (fits_get_colnum(filePtr, CASEINSEN, "INT_TIME", &colnum, &status))
        {
            amdlibReturnFitsError("INT_TIME");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, 1,
                          &nulldouble, &vis->table[iFrame-1].expTime,
                          &anynull, &status))
        {
            amdlibReturnFitsError("INT_TIME");
        }

        /* Read visibility and its associated error */
        if (fits_get_colnum(filePtr, CASEINSEN, "VISDATA", &colnum, &status))
        {
            amdlibReturnFitsError("VISDATA");
        }
        if (fits_read_col(filePtr, TDBLCOMPLEX, colnum, iFrame, 1, nbWlen,
                           &amdlibBval, vis->table[iFrame-1].vis,
                           &anynull, &status))
        {
            amdlibReturnFitsError("VISDATA");
        }

        if (fits_get_colnum(filePtr, CASEINSEN, "VISERR", &colnum, &status))
        {
            amdlibReturnFitsError("VISERR");
        }
        if (fits_read_col(filePtr, TDBLCOMPLEX, colnum, iFrame, 1, nbWlen,
                          &amdlibBval, vis->table[iFrame-1].sigma2Vis,
                          &anynull, &status))
        {
            amdlibReturnFitsError("VISERR");
        }
        /* Rectify read value */
        for (j=0; j < nbWlen; j++)
        {
            re = vis->table[iFrame-1].sigma2Vis[j].re;
            im = vis->table[iFrame-1].sigma2Vis[j].im;
            vis->table[iFrame-1].sigma2Vis[j].re *= re;
            vis->table[iFrame-1].sigma2Vis[j].im *= im;
        }

        if (fits_get_colnum(filePtr, CASEINSEN, "VISAMP", &colnum, &status))
        {
            amdlibReturnFitsError("VISAMP");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, nbWlen,
                          &amdlibBval, vis->table[iFrame-1].diffVisAmp,
                          &anynull, &status))
        {
            amdlibReturnFitsError("VISAMP");
        }

        if (fits_get_colnum(filePtr, CASEINSEN, "VISPHI", &colnum, &status))
        {
            amdlibReturnFitsError("VISPHI");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, nbWlen,
                          &amdlibBval, vis->table[iFrame-1].diffVisPhi,
                          &anynull, &status))
        {
            amdlibReturnFitsError("VISPHI");
        }

        if (fits_get_colnum(filePtr, CASEINSEN, "VISAMPERR", &colnum, &status))
        {
            amdlibReturnFitsError("VISAMPERR");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, nbWlen,
                          &amdlibBval, vis->table[iFrame-1].diffVisAmpErr,
                          &anynull, &status))
        {
            amdlibReturnFitsError("VISAMPERR");
        }

        if (fits_get_colnum(filePtr, CASEINSEN, "VISPHIERR", &colnum, &status))
        {
            amdlibReturnFitsError("VISPHIERR");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, nbWlen,
                          &amdlibBval, vis->table[iFrame-1].diffVisPhiErr,
                          &anynull, &status))
        {
            amdlibReturnFitsError("VISPHIERR");
        }

        /* Rectify read value */
        for (j=0; j < nbWlen; j++)
        {
            vis->table[iFrame-1].diffVisPhi[j] = M_PI / 180.0 *
                vis->table[iFrame-1].diffVisPhi[j];
            vis->table[iFrame-1].diffVisPhiErr[j] = M_PI / 180.0 *
                vis->table[iFrame-1].diffVisPhiErr[j];
        }
        
        /* Read validity flags */
        if (fits_get_colnum(filePtr, CASEINSEN, "FLAG", &colnum, &status))
        {
            amdlibReturnFitsError("FLAG");
        }
        if (strncmp(flagColSize, "0L", 2) != 0 )
        {
            flag = calloc(nbWlen, sizeof(unsigned char));
            if (fits_read_col(filePtr, TLOGICAL, colnum, iFrame, 1, nbWlen,
                              &nulllogical, flag,
                              &anynull, &status))
            {
                free(flag);
                amdlibReturnFitsError("FLAG");
            }
            for (lVis = 0; lVis < nbWlen; lVis++)
            {
                vis->table[iFrame-1].flag[lVis]=flag[lVis];
            }
            free(flag);
        }

        /* Read U and V coordinates of the data */
        if (fits_get_colnum(filePtr, CASEINSEN, "UCOORD", &colnum, &status))
        {
            amdlibReturnFitsError("UCOORD");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, 1,
                           &nulldouble, &vis->table[iFrame-1].uCoord,
                           &anynull, &status))
        {
            amdlibReturnFitsError("UCOORD");
        }

        if (fits_get_colnum(filePtr, CASEINSEN, "VCOORD", &colnum, &status))
        {
            amdlibReturnFitsError("VCOORD");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, 1,
                           &nulldouble, &vis->table[iFrame-1].vCoord,
                           &anynull, &status))
        {
            amdlibReturnFitsError("VCOORD");
        }

        /* Read station index */
        if (fits_get_colnum(filePtr, CASEINSEN, "STA_INDEX", &colnum, &status))
        {
            amdlibReturnFitsError("STA_INDEX");
        }
        if (fits_read_col(filePtr, TINT, colnum, iFrame, 1, 2, &nullint,
                           vis->table[iFrame-1].stationIndex,
                           &anynull, &status))
        {
            amdlibReturnFitsError("STA_INDEX");
        }

    }

    return amdlibSUCCESS;
}

/**
 * Read OI_VIS2 table in OI-FITS file.
 *
 * This function reads the OI_VIS2 binary table defined in the IAU 
 * standard in the OI-FITS file given as parameter.
 *
 * @param filePtr pointer on the file to be created.
 * @param vis2 OI_VIS2 produced.
 * @param nbBases input parameter giving the number of baselines.
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibReadOiVis2(fitsfile        *filePtr,
                                  amdlibVIS2      *vis2,
                                  int             nbBases,
                                  amdlibERROR_MSG errMsg)
{
    char          comment[amdlibKEYW_CMT_LEN+1];
    char          insName[amdlibKEYW_VAL_LEN+1], arrName[amdlibKEYW_VAL_LEN+1];
    char          dateObsMJD[amdlibKEYW_VAL_LEN+1];
    int           nullint = 0;
    double        nulldouble = 0;
    amdlibDOUBLE amdlibBval=amdlibBLANKING_VALUE;
    amdlibBOOLEAN nulllogical = amdlibFALSE;
    const int     revision = amdlib_OI_REVISION;
    int           dataRevision;
    int           iFrame, colnum, anynull;
    long          nbRows, nbWlen, nbFrames;
    int           status = 0; 
    char          fitsioMsg[256];
    char          flagColSize[amdlibKEYW_VAL_LEN+1];
    unsigned char *flag;
    int lVis;
    
    amdlibLogTrace("amdlibReadOiVis2()");

    /* Read the OI table */
    if (fits_movnam_hdu(filePtr, BINARY_TBL, "OI_VIS2", 0, &status))
    {
        amdlibReturnFitsError("OI_VIS2");
    }

    /* Read revision number of the table definition */
    if (fits_read_key(filePtr, TINT, "OI_REVN", &dataRevision,
                      comment, &status))
    {
        amdlibReturnFitsError("OI_REVN");
    }
    if (dataRevision > revision) 
    {
        amdlibSetErrMsg("Unable to read this OI revision of the data");
        return amdlibFAILURE;
    }

    /* Read observation date */
    if (fits_read_key(filePtr, TSTRING, "DATE-OBS", dateObsMJD,
                      comment, &status))
    {
        amdlibLogWarning("No DATE-OBS keyword !");
        status = 0;
    }

    /* Read names of array and detector */
    if (fits_read_key(filePtr, TSTRING, "ARRNAME", arrName,
                      comment, &status))
    {
        amdlibLogWarning("No ARRNAME keyword !");
        status = 0;
    }
    if (fits_read_key(filePtr, TSTRING, "INSNAME", insName,
                      comment, &status))
    {
        amdlibLogWarning("No INSNAME keyword !");
        status = 0;
    }

    /* Get flag column dimension */
    if (fits_read_key(filePtr, TSTRING, "TFORM10", flagColSize,
                      comment, &status))
    {
        amdlibLogWarning("No TFORM10 keyword !");
        status = 0;
    }

    /* Get number of rows */
    if (fits_get_num_rows(filePtr, &nbRows, &status))
    {
        amdlibReturnFitsError("nbRows");
    }

    /* Get value for nbWlen. Format specifies same repeat count for VIS2DATA
     * & VIS2ERR columns */
    if (fits_get_colnum(filePtr, CASEINSEN, "VIS2DATA", &colnum, &status))
    {
        amdlibReturnFitsError("VIS2DATA");
    }

    if (fits_get_coltype(filePtr, colnum, NULL, &nbWlen, NULL, &status))
    {
        amdlibReturnFitsError("VIS2DATA");
    }

    nbFrames = nbRows / nbBases;

    /* Allocate memory vis */
    if (amdlibAllocateVis2(vis2, nbFrames, nbBases, nbWlen) != amdlibSUCCESS)
    {
        amdlibSetErrMsg("Could not allocate memory for VIS2");
        return amdlibFAILURE;
    }

    strcpy(vis2->dateObs, dateObsMJD);

    for (iFrame = 1; iFrame <= nbRows; iFrame++) 
    {
        /* Read target identity */
        if (fits_get_colnum(filePtr, CASEINSEN, "TARGET_ID", &colnum, &status))
        {
            amdlibReturnFitsError("TARGET_ID");
        }
        if (fits_read_col(filePtr, TINT, colnum, iFrame, 1, 1, &nullint,
                          &vis2->table[iFrame-1].targetId, &anynull, &status))
        {
            amdlibReturnFitsError("TARGET_ID");
        }

        /* Read time tag for the exposure */
        if (fits_get_colnum(filePtr, CASEINSEN, "TIME", &colnum, &status))
        {
            amdlibReturnFitsError("TIME");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, 1,
                          &nulldouble, &vis2->table[iFrame-1].time,
                          &anynull, &status))
        {
            amdlibReturnFitsError("TIME");
        }

        /* Read observation date */
        if (fits_get_colnum(filePtr, CASEINSEN, "MJD", &colnum, &status))
        {
            amdlibReturnFitsError("MJD");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, 1,
                          &nulldouble, &vis2->table[iFrame-1].dateObsMJD,
                          &anynull, &status))
        {
            amdlibReturnFitsError("MJD");
        }

        /* Read integration time */
        if (fits_get_colnum(filePtr, CASEINSEN, "INT_TIME", &colnum, &status))
        {
            amdlibReturnFitsError("INT_TIME");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, 1,
                          &nulldouble, &vis2->table[iFrame-1].expTime,
                          &anynull, &status))
        {
            amdlibReturnFitsError("INT_TIME");
        }

        /* Read visibility and its associated error */
        if (fits_get_colnum(filePtr, CASEINSEN, "VIS2DATA", &colnum, &status))
        {
            amdlibReturnFitsError("VIS2DATA");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, nbWlen,
                          &amdlibBval, vis2->table[iFrame-1].vis2,
                          &anynull, &status))
        {
            amdlibReturnFitsError("VIS2DATA");
        }

        if (fits_get_colnum(filePtr, CASEINSEN, "VIS2ERR", &colnum, &status))
        {
            amdlibReturnFitsError("VIS2ERR");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, nbWlen,
                          &amdlibBval, vis2->table[iFrame-1].vis2Error,
                          &anynull, &status))
        {
            amdlibReturnFitsError("VIS2ERR");
        }

        /* Read validity flags */
        if (fits_get_colnum(filePtr, CASEINSEN, "FLAG", &colnum, &status))
        {
            amdlibReturnFitsError("FLAG");
        }
        if (strncmp(flagColSize, "0L", 2) != 0 )
        {
            flag = calloc(nbWlen, sizeof(unsigned char));
            if (fits_read_col(filePtr, TLOGICAL, colnum, iFrame, 1, nbWlen,
                              &nulllogical, flag,
                              &anynull, &status))
            {
                free(flag);
                amdlibReturnFitsError("FLAG");
            }
             for (lVis = 0; lVis < nbWlen; lVis++)
            {
                vis2->table[iFrame-1].flag[lVis]=flag[lVis];
            }
            free(flag);
       }
        
        /* Read U and V coordinates of the data */
        if (fits_get_colnum(filePtr, CASEINSEN, "UCOORD", &colnum, &status))
        {
            amdlibReturnFitsError("UCOORD");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, 1,
                          &nulldouble, &vis2->table[iFrame-1].uCoord,
                          &anynull, &status))
        {
            amdlibReturnFitsError("UCOORD");
        }

        if (fits_get_colnum(filePtr, CASEINSEN, "VCOORD", &colnum, &status))
        {
            amdlibReturnFitsError("VCOORD");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, 1,
                          &nulldouble, &vis2->table[iFrame-1].vCoord,
                          &anynull, &status))
        {
            amdlibReturnFitsError("VCOORD");
        }

        /* Read station index */
        if (fits_get_colnum(filePtr, CASEINSEN, "STA_INDEX", &colnum, &status))
        {
            amdlibReturnFitsError("STA_INDEX");
        }
        if (fits_read_col(filePtr, TINT, colnum, iFrame, 1, 2, &nullint,
                          vis2->table[iFrame-1].stationIndex, 
                          &anynull, &status))
        {
            amdlibReturnFitsError("STA_INDEX");
        }
    }

    return amdlibSUCCESS;
}

/**
 * Read OI_T3 table in OI-FITS file.
 *
 * This function reads the OI_T3 binary table defined in the IAU 
 * standard in the OI-FITS file given as parameter.
 *
 * @param filePtr pointer on the file to be read.
 * @param vis3 OI_T3 produced.
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibReadOiVis3(fitsfile        *filePtr,
                                  amdlibVIS3      *vis3,
                                  amdlibERROR_MSG errMsg)
{
    char          comment[amdlibKEYW_CMT_LEN+1];
    char          insName[amdlibKEYW_VAL_LEN+1], arrName[amdlibKEYW_VAL_LEN+1];
    char          dateObsMJD[amdlibKEYW_VAL_LEN+1];
    int           nullint = 0;
    double        nulldouble = 0;
    amdlibDOUBLE amdlibBval=amdlibBLANKING_VALUE;
    amdlibBOOLEAN nulllogical = amdlibFALSE;
    const int     revision = amdlib_OI_REVISION;
    int           dataRevision;
    long          nbRows, nbWlen, nbFrames, nbClosures;
    int           status = 0; 
    char          fitsioMsg[256];
    int           iFrame, colnum, anynull, j;
    char          flagColSize[amdlibKEYW_VAL_LEN+1];
    unsigned char *flag;
    int lVis;

    amdlibLogTrace("amdlibReadOiVis3()");

    /* Read the OI table */
    if (fits_movnam_hdu(filePtr, BINARY_TBL, "OI_T3", 0, &status))
    {
        amdlibLogWarning("No OI_T3 table, this is 2T data");
        return amdlibSUCCESS;
    }    

    /* Read revision number of the table definition */
    if (fits_read_key(filePtr, TINT, "OI_REVN", &dataRevision,
                       comment, &status))
    {
        amdlibReturnFitsError("OI_REVN");
    }
    if (dataRevision > revision) 
    {
        amdlibSetErrMsg("Data too recent to be read with this routine");
        return amdlibFAILURE;
    }

    /* Read observation date */
    if (fits_read_key(filePtr, TSTRING, "DATE-OBS", dateObsMJD,
                      comment, &status))
    {
        amdlibLogWarning("no DATE-OBS keyword");
        status = 0;
    }

    /* Read names of array and detector */
    if (fits_read_key(filePtr, TSTRING, "ARRNAME", arrName,
                      comment, &status))
    {
        amdlibLogWarning("no ARRNAME keyword");
        status = 0;
    }
    if (fits_read_key(filePtr, TSTRING, "INSNAME", insName,
                      comment, &status))
    {
        amdlibLogWarning("no INSNAME keyword");
        status = 0;
    }

    /* Get flag column dimension */
    if (fits_read_key(filePtr, TSTRING, "TFORM14", flagColSize,
                      comment, &status))
    {
        amdlibLogWarning("no TFORM10 keyword");
        status = 0;
    }
    
    /* Get number of rows & allocate storage */
    if (fits_get_num_rows(filePtr, &nbRows, &status))
    {
        amdlibReturnFitsError("getting number of rows");
    }

    /* Get value for nbWlen. Format specifies same repeat count for T3AMP
     * & T3AMPERR columns */
    if (fits_get_colnum(filePtr, CASEINSEN, "T3AMP", &colnum, &status))
    {
        amdlibReturnFitsError("T3AMP");
    }
    if (fits_get_coltype(filePtr, colnum, NULL, &nbWlen, NULL, &status))
    {
        amdlibReturnFitsError("T3AMP");
    }

    nbFrames = nbRows;
    nbClosures = 1;

    /* Allocate memory vis */
    if (amdlibAllocateVis3(vis3, nbFrames, nbClosures, nbWlen) != amdlibSUCCESS)
    {
        amdlibSetErrMsg("Could not allocate memory for VIS3");
        return amdlibFAILURE;
    }

    strcpy(vis3->dateObs, dateObsMJD);
    
    /* Read rows */
    for (iFrame = 1; iFrame <= nbRows; iFrame++) 
    {
        /* Read target identity */
        if (fits_get_colnum(filePtr, CASEINSEN, "TARGET_ID", &colnum, &status))
        {
            amdlibReturnFitsError("TARGET_ID");
        }
        if (fits_read_col(filePtr, TINT, colnum, iFrame, 1, 1,
                          &nullint, &vis3->table[iFrame-1].targetId,
                          &anynull, &status))
        {
            amdlibReturnFitsError("reading TARGET_ID");
        }

        /* Read time tag for the exposure */
        if (fits_get_colnum(filePtr, CASEINSEN, "TIME", &colnum, &status))
        {
            amdlibReturnFitsError("TIME");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, 1,
                          &nulldouble, &vis3->table[iFrame-1].time,
                          &anynull, &status))
        {
            amdlibReturnFitsError("reading TIME");
        }

        /* Read observation date */
        if (fits_get_colnum(filePtr, CASEINSEN, "MJD", &colnum, &status))
        {
            amdlibReturnFitsError("MJD");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, 1,
                          &nulldouble, &vis3->table[iFrame-1].dateObsMJD,
                          &anynull, &status))
        {
            amdlibReturnFitsError("reading MJD");
        }

        /* Read total integration time for this measured visibility */
        if (fits_get_colnum(filePtr, CASEINSEN, "INT_TIME", &colnum, &status))
        {
            amdlibReturnFitsError("INT_TIME");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, 1,
                          &nulldouble, &vis3->table[iFrame-1].expTime,
                          &anynull, &status))
        {
            amdlibReturnFitsError("reading INT_TIME");
        }

        /* Read visibility amplitude and its associated error */
        if (fits_get_colnum(filePtr, CASEINSEN, "T3AMP", &colnum, &status))
        {
            amdlibReturnFitsError("T3AMP");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, nbWlen,
                          &amdlibBval, vis3->table[iFrame-1].vis3Amplitude,
                          &anynull, &status))
        {
            amdlibReturnFitsError("T3AMP");
        }

        if (fits_get_colnum(filePtr, CASEINSEN, "T3AMPERR", &colnum,
                             &status))
        {
            amdlibReturnFitsError("T3AMPERR");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1,
                          nbWlen, &amdlibBval,
                          vis3->table[iFrame-1].vis3AmplitudeError,
                          &anynull, &status))
        {
            amdlibReturnFitsError("T3AMPERR");
        }

        /* Read visibility phase and its associated error */
        if (fits_get_colnum(filePtr, CASEINSEN, "T3PHI", &colnum, &status))
        {
            amdlibReturnFitsError("T3PHI");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, nbWlen,
                          &amdlibBval, vis3->table[iFrame-1].vis3Phi,
                          &anynull, &status))
        {
            amdlibReturnFitsError("T3PHI");
        }

        if (fits_get_colnum(filePtr, CASEINSEN, "T3PHIERR", &colnum, &status))
        {
            amdlibReturnFitsError("T3PHIERR");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, nbWlen,
                          &amdlibBval, vis3->table[iFrame-1].vis3PhiError,
                          &anynull, &status))
        {
            amdlibReturnFitsError("T3PHIERR");
        }

        /* Rectify read value */
        for (j=0; j < nbWlen; j++)
        {
            vis3->table[iFrame-1].vis3Phi[j] = M_PI / 180.0 *
                vis3->table[iFrame-1].vis3Phi[j];
            vis3->table[iFrame-1].vis3PhiError[j] = M_PI / 180.0 *
                vis3->table[iFrame-1].vis3PhiError[j];
        }

        /* Read validity flags */
        if (fits_get_colnum(filePtr, CASEINSEN, "FLAG", &colnum, &status))
        {
            amdlibReturnFitsError("FLAG");
        }
        if (strncmp(flagColSize, "0L", 2) != 0 )
        {
            flag = calloc(nbWlen, sizeof(unsigned char));
            if (fits_read_col(filePtr, TLOGICAL, colnum, iFrame, 1, nbWlen,
                              &nulllogical, flag,
                              &anynull, &status))
            {
                free(flag);
                amdlibReturnFitsError("FLAG");
            }
             for (lVis = 0; lVis < nbWlen; lVis++)
            {
                vis3->table[iFrame-1].flag[lVis]=flag[lVis];
            }
            free(flag);
       }

        /* Read U and V coordinates of the data */
        if (fits_get_colnum(filePtr, CASEINSEN, "U1COORD", &colnum, &status))
        {
            amdlibReturnFitsError("U1COORD");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, 1,
                          &nulldouble, &vis3->table[iFrame-1].u1Coord,
                          &anynull, &status))
        {
            amdlibReturnFitsError("U1COORD");
        }

        if (fits_get_colnum(filePtr, CASEINSEN, "V1COORD", &colnum, &status))
        {
            amdlibReturnFitsError("V1COORD");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, 1,
                          &nulldouble, &vis3->table[iFrame-1].v1Coord,
                          &anynull, &status))
        {
            amdlibReturnFitsError("V1COORD");
        }

        if (fits_get_colnum(filePtr, CASEINSEN, "U2COORD", &colnum, &status))
        {
            amdlibReturnFitsError("U2COORD");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, 1,
                          &nulldouble, &vis3->table[iFrame-1].u2Coord,
                          &anynull, &status))
        {
            amdlibReturnFitsError("U2COORD");
        }

        if (fits_get_colnum(filePtr, CASEINSEN, "V2COORD", &colnum, &status))
        {
            amdlibReturnFitsError("V2COORD");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, 1,
                          &nulldouble, &vis3->table[iFrame-1].v2Coord,
                          &anynull, &status))
        {
            amdlibReturnFitsError("V2COORD");
        }

        /* Read station index */
        if (fits_get_colnum(filePtr, CASEINSEN, "STA_INDEX", &colnum, &status))
        {
            amdlibReturnFitsError("STA_INDEX");
        }
        if (fits_read_col(filePtr, TINT, colnum, iFrame, 1, 3,
                          &nullint, vis3->table[iFrame-1].stationIndex,
                          &anynull, &status))
        {
            amdlibReturnFitsError("STA_INDEX");
        }
    }

    return amdlibSUCCESS;
}

/**
 * Read AMBER_DATA table in OI-FITS file.
 *
 * This function reads the AMBER_DATA binary table in the OI-FITS file 
 * given as parameter.
 *
 * @param filePtr pointer on the file to be created.
 * @param photometry photometry data.
 * @param vis AMBER_DATA produced.
 * @param pst piston data.
 * @param nbBases input parameter giving the number of baselines.
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibReadAmberData(fitsfile         *filePtr,
                                     amdlibPHOTOMETRY *photometry,
                                     amdlibVIS        *vis,
                                     amdlibPISTON     *pst,
                                     int              nbBases,
                                     amdlibERROR_MSG  errMsg)
{

    char       comment[amdlibKEYW_CMT_LEN+1];
    char       keywName[amdlibKEYW_NAME_LEN+1];
    char       keywVal[amdlibKEYW_VAL_LEN+1];
    const int  revision = amdlib_OI_REVISION;
    int        dataRevision;
    int        iFrame, colnum, anynull;
    int        status = 0;
    char       fitsioMsg[256];
    long       nbRows; /* Warning: notifies number of rows in bin table !! */
    long       nbWlen, nbFrames;
    char       insName[amdlibKEYW_VAL_LEN+1], dateObsMJD[amdlibKEYW_VAL_LEN+1];
    int        lVis;
    
    int        newData = 0;
    int        bandInfo = 0;
    double     nulldouble = 0;
    amdlibBAND band = amdlibUNKNOWN_BAND;
    amdlibDOUBLE      *wlen;
    amdlibDOUBLE      *pistonOPD, *sigmaPiston;
    double     *frgContrastSnr;
    int        nbBands, iBand;
    amdlibDOUBLE amdlibBval=amdlibBLANKING_VALUE;

    amdlibLogTrace("amdlibReadAmberData()");

    /* Suppose vis structure has already been allocated */
    if (vis->thisPtr != vis) 
    {
        amdlibSetErrMsg("Unitialized vis structure");
        return amdlibFAILURE;
    }    

    /* For compatibility : read the SPECTRUM column in the OI_VIS table 
     * if it exists and fill the fluxSumPiPj value with it */    
    if (fits_movnam_hdu (filePtr, BINARY_TBL, "OI_VIS", 0, &status))
    {
        amdlibReturnFitsError("OI_VIS");
    }

    /* Get value for nbWlen */
    /* Format specifies same repeat count for VIS* columns */
    if (fits_get_colnum(filePtr, CASEINSEN, "SPECTRUM", &colnum, &status))
    {
        newData = 1;
    }
    else
    {
        amdlibLogWarning("Found a SPECTRUM column in this OI_VIS table : "
                         "this is OLD data, please consider recomputing your "
                         "data with the last data processing version !");
        if (fits_get_coltype(filePtr, colnum, NULL, &nbWlen, NULL, &status))
        {
            amdlibReturnFitsError("nbWlen");
        }

        /* Get number of rows */
        if (fits_get_num_rows(filePtr, &nbRows, &status))
        {
            amdlibReturnFitsError("Getting nbRows");
        }

        nbFrames = nbRows / nbBases;

        /* Allocate photometry data structure */
        if (amdlibAllocatePhotometry(photometry, nbFrames, nbBases, 
                                      nbWlen) != amdlibSUCCESS)
        {
            amdlibSetErrMsg("Could not allocate memory for photometry");
            return amdlibFAILURE;
        }

        for (iFrame=1; iFrame <= nbRows; iFrame++) 
        {
            /* Read spectrum */
            if (fits_get_colnum(filePtr, CASEINSEN, "SPECTRUM", 
                                &colnum, &status))
            {
                amdlibReturnFitsError("SPECTRUM");
            }
            if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, nbWlen,
                              &amdlibBval, 
                              photometry->table[iFrame-1].fluxSumPiPj,
                              &anynull, &status))
            {
                amdlibReturnFitsError("reading spectrum");
            }
        }
    }

    status = 0;

    /* Read the AMBER_DATA table */
    if (fits_movnam_hdu(filePtr, BINARY_TBL, "AMBER_DATA", 0, &status))
    {
        amdlibReturnFitsError("AMBER_DATA");
    }

    /* Read revision number of the table definition */
    if (fits_read_key(filePtr, TINT, "AMB_REVN", &dataRevision,
                     comment, &status))
    {
        amdlibLogWarning("no AMB_REVN keyword ! Setting it to current revision"
                         " (could cause strange behaviour !)");
        status = 0;
        dataRevision = revision;
    }
    if (revision > dataRevision) 
    {
        amdlibSetErrMsg("Unable to read this AMBER revision of the data");
    }

    /* Read observation date */
    if (fits_read_key(filePtr, TSTRING, "DATE-OBS", dateObsMJD,
                      comment, &status))
    {
        amdlibLogWarning("no DATE-OBS keyword !");
        status = 0;
    }

    /* Read detector name */
    if (fits_read_key(filePtr, TSTRING, "INSNAME", insName,
                      comment, &status))
    {
        amdlibLogWarning("no INSNAME keyword !");
        status = 0;
    }

    if (newData == 1)
    {
        /* Get number of wavelengths */
        if (fits_get_colnum(filePtr, CASEINSEN, "*FLUX_SUM", 
                            &colnum, &status))
        {
            /* Try old colunm name */
            status = 0;
            if (fits_get_colnum(filePtr, CASEINSEN, "FLUX_PI_MORE_PJ", 
                                &colnum, &status))
            {
                amdlibReturnFitsError("FLUX_SUM");
            }
        }

        if (fits_get_coltype(filePtr, colnum, NULL, &nbWlen, NULL, &status))
        {
            amdlibReturnFitsError("FLUX_SUM");
        }
    }

    /* Get number of rows */
    if (fits_get_num_rows(filePtr, &nbRows, &status))
    {
        amdlibReturnFitsError("Getting nbRows");
    }

    nbFrames = nbRows / nbBases;

    /* Allocate memory for Piston */
    if (amdlibAllocatePiston(pst, nbFrames, nbBases) != amdlibSUCCESS)
    {
        amdlibSetErrMsg("Could not allocate memory for piston");
        return amdlibFAILURE;
    }
    
    /* Check if data contain explicit information about band */
    /* OCS keywords has been changed to QC keywords ; first check OCS keywords
     * (for backward-compatibility), then try QC keyword */
    char *keywCatg = "OCS";
    if (fits_read_key(filePtr, TINT, "HIERARCH ESO OCS NBBANDS", &nbBands, 
                        comment, &status))
    {
        status = 0;
        keywCatg = "QC";
    }
    if ((strcmp(keywCatg,  "QC") == 0) &&
        (fits_read_key(filePtr, TINT, "HIERARCH ESO QC NBBANDS", &nbBands, 
                       comment, &status)))
    {
        /* It is an old data, where there is necessary only one spectral band
         * per file */
        status = 0;
        nbBands = 1;
        /* Find current band */
        if (fits_movnam_hdu(filePtr, BINARY_TBL, "OI_WAVELENGTH", 0, &status))
        {
            amdlibReturnFitsError("unable to treat information");
        }

        if (fits_get_num_rows(filePtr, &nbWlen, &status))
        {
            amdlibReturnFitsError("number of cols of EFF_WAVE");  
        }
        wlen = calloc(nbWlen, sizeof(amdlibDOUBLE));
        if (fits_get_colnum(filePtr, CASEINSEN, "EFF_WAVE", &colnum, &status))
        {
            amdlibReturnFitsError("number of cols of EFF_WAVE");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, 1, 1, nbWlen, 
                          &nulldouble, wlen, &anynull, &status))
        {
            amdlibReturnFitsError("reading the col EFF_WAVE");
        }

        band = amdlibGetBand(wlen[0] / amdlibNM_TO_M);
        free(wlen);

        /* Read the AMBER_DATA table */
        if (fits_movnam_hdu(filePtr, BINARY_TBL, "AMBER_DATA", 0, &status))
        {
            amdlibReturnFitsError("AMBER_DATA");
        }
    }
    else
    {
        bandInfo = 1;
        
        /* There is at least one band described in the header */
        for (iBand=1; iBand <= nbBands; iBand++)
        {
            sprintf(keywName, "HIERARCH ESO %s BAND%d NAME", keywCatg, iBand);
            if (fits_read_key(filePtr, TSTRING, keywName, keywVal, 
                        comment, &status))
            {
                amdlibReturnFitsError("Getting band name");
            }
            else
            {
                if (strstr(keywVal, "J") != NULL)
                {
                    pst->bandFlag[amdlibJ_BAND] = amdlibTRUE;
                }
                else if (strstr(keywVal, "H") != NULL)
                {
                    pst->bandFlag[amdlibH_BAND] = amdlibTRUE;
                }
                else if (strstr(keywVal, "K") != NULL)
                {
                    pst->bandFlag[amdlibK_BAND] = amdlibTRUE;
                }
                else
                {
                    amdlibSetErrMsg("Invalid band name '%s'", keywVal);
                    return amdlibFAILURE;
                }
            }
        }
    }
    
    if (newData == 1)
    {
        /* Allocate photometry data structure */
        if ( amdlibAllocatePhotometry(photometry, nbFrames, 
                                      nbBases, nbWlen) != amdlibSUCCESS)
        {
            amdlibSetErrMsg("Could not allocate memory for photometry");
            return amdlibFAILURE;
        }
    }

    pistonOPD = calloc(nbBands, sizeof(amdlibDOUBLE));
    sigmaPiston = calloc(nbBands, sizeof(amdlibDOUBLE));
    frgContrastSnr = calloc(nbBands, sizeof(double));
    /* Read rows */
    for (iFrame=1; iFrame <= nbRows; iFrame++) 
    {
        if (bandInfo == 0)
        {
            pst->bandFlag[band] = amdlibTRUE;
            vis->table[iFrame-1].bandFlag[band] = amdlibTRUE;            
            pistonOPD = &pst->pistonOPDArray[band][iFrame-1];
            sigmaPiston = &pst->sigmaPistonArray[band][iFrame-1];
            frgContrastSnr = &vis->table[iFrame-1].frgContrastSnrArray[band];
            
            if (fits_get_colnum(filePtr, CASEINSEN, "PISTON_OPD", &colnum,
                                &status) != 0)
            {
                amdlibReturnFitsError("PISTON_OPD");            
            }
            if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, 1,
                              &amdlibBval, pistonOPD,
                              &anynull, &status))
            {
                amdlibReturnFitsError("PISTON_OPD");
            }
            if (fits_get_colnum(filePtr, CASEINSEN, "PISTON_ERR", &colnum, 
                                &status))
            {
                amdlibReturnFitsError("PISTON_ERR");
            }
            if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, 1,
                              &amdlibBval, sigmaPiston,
                              &anynull, &status))
            {
                amdlibReturnFitsError("PISTON_ERR");
            }
            /* Convert pistonOPD and sigmaPiston into meters */
            if (!amdlibCompareDouble(*pistonOPD,amdlibBLANKING_VALUE))
            {
                *pistonOPD /= amdlibNM_TO_M;
                *sigmaPiston /= amdlibNM_TO_M;
            }

            /* Read frgContrastSnr */
            if (fits_get_colnum(filePtr, CASEINSEN, "FRINGE_CONTRAST_SNR",
                                &colnum, &status))
            {
                amdlibReturnFitsError("FRINGE_CONTRAST_SNR");
            }
            if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, 1,
                              &amdlibBval, frgContrastSnr,
                              &anynull, &status))
            {
                amdlibReturnFitsError("FRINGE_CONTRAST_SNR");
            }
 
            pst->pistonOPD[iFrame-1] = *pistonOPD;
            pst->sigmaPiston[iFrame-1] = *sigmaPiston;
            vis->table[iFrame-1].frgContrastSnr = *frgContrastSnr;
        }
        else
        {
            for (iBand = amdlibJ_BAND; iBand <= amdlibK_BAND; iBand ++)
            {
                vis->table[iFrame-1].bandFlag[iBand] = pst->bandFlag[iBand];
            }    
            /* Read piston and its associated error */
            if (fits_get_colnum(filePtr, CASEINSEN, "*OPD", &colnum,
                                &status) != 0)
            {
                /* Try old colunm name */
                status = 0;
                if (fits_get_colnum(filePtr, CASEINSEN, "PISTON_OPD", 
                                    &colnum, &status))
                {
                    amdlibReturnFitsError("OPD");
                }
            }
            if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, nbBands,
                              &amdlibBval, pistonOPD,
                              &anynull, &status))
            {
                amdlibReturnFitsError("OPD");
            }

            if (fits_get_colnum(filePtr, CASEINSEN, "*OPD_ERR", &colnum,
                                &status))
            {
                /* Try old colunm name */
                status = 0;
                if (fits_get_colnum(filePtr, CASEINSEN, "PISTON_ERR", 
                                    &colnum, &status))
                {
                    amdlibReturnFitsError("OPD_ERR");
                }
            }
            if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, nbBands,
                              &amdlibBval, sigmaPiston,
                              &anynull, &status))
            {
                amdlibReturnFitsError("OPD_ERR");
            }
            /* Read frgContrastSnr */
            if (fits_get_colnum(filePtr, CASEINSEN, "*FRINGE_SNR",
                                &colnum, &status))
            {
                /* Try old colunm name */
                status = 0;
                if (fits_get_colnum(filePtr, CASEINSEN, "FRINGE_CONTRAST_SNR", 
                                    &colnum, &status))
                {
                    amdlibReturnFitsError("FRINGE_SNR");
                }
            }
            if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, nbBands,
                              &amdlibBval, frgContrastSnr,
                              &anynull, &status))
            {
                amdlibReturnFitsError("FRINGE_SNR");
            }

            iBand = 0;
            for (band = amdlibJ_BAND; band <= amdlibK_BAND; band++)
            {
                 if (pst->bandFlag[band] == amdlibTRUE)
                {
                    /* Copy pistonOPD, sigmaPiston and fringeContrastSnr 
                     * & convert pistonOPD and sigmaPiston into meters */
                    if (!amdlibCompareDouble(pistonOPD[iBand],amdlibBLANKING_VALUE))
                    {
                        pst->pistonOPDArray[band][iFrame-1] = 
                        pistonOPD[iBand] / amdlibNM_TO_M;
                        pst->sigmaPistonArray[band][iFrame-1] =
                        sigmaPiston[iBand] / amdlibNM_TO_M;
                    }
                    else
                    {
                        pst->pistonOPDArray[band][iFrame-1] = pistonOPD[iBand];
                        pst->sigmaPistonArray[band][iFrame-1] = sigmaPiston[iBand];
                    }
                    vis->table[iFrame-1].frgContrastSnrArray[band] =
                    frgContrastSnr[iBand];
                    iBand++;

                    pst->pistonOPD[iFrame-1] = 
                        pst->pistonOPDArray[band][iFrame-1];
                    pst->sigmaPiston[iFrame-1] =
                        pst->sigmaPistonArray[band][iFrame-1];
                    vis->table[iFrame-1].frgContrastSnr =
                        vis->table[iFrame-1].frgContrastSnrArray[band];
                }
            }
        }

        if (newData == 1)
        {
            /* Read fluxSumPiPj (number of electrons collected in the spectral 
             * channel) and associated error */
            if (fits_get_colnum(filePtr, CASEINSEN, "*FLUX_SUM", 
                                &colnum, &status))
            {
                /* Try old colunm name */
                status = 0;
                if (fits_get_colnum(filePtr, CASEINSEN, "FLUX_PI_MORE_PJ", 
                                    &colnum, &status))
                {
                    amdlibReturnFitsError("FLUX_SUM");
                }
            }
            if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, nbWlen,
                              &amdlibBval, 
                              photometry->table[iFrame-1].fluxSumPiPj,
                              &anynull, &status))
            {
                amdlibReturnFitsError("FLUX_SUM");
            }

            if (fits_get_colnum(filePtr, CASEINSEN, "*FLUX_SUM_CORRECTION", 
                                &colnum, &status))
            {
                /* Try old colunm name */
                status = 0;
                if (fits_get_colnum(filePtr, CASEINSEN, "FLUX_PI_MORE_PJ_ERR", 
                                    &colnum, &status))
                {
                    amdlibReturnFitsError("FLUX_SUM_CORRECTION");
                }
            }
            if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, nbWlen,
                              &amdlibBval, 
                              photometry->table[iFrame-1].sigma2FluxSumPiPj,
                              &anynull, &status))
            {
                amdlibReturnFitsError("FLUX_SUM_CORRECTION");
            }

            /* Read fluxRatPiPj (flux ratio in the spectral channel) and
             * associated error */
            if (fits_get_colnum(filePtr, CASEINSEN, "*FLUX_RATIO",
                                &colnum, &status))
            {
                /* Try old colunm name */
                status = 0;
                if (fits_get_colnum(filePtr, CASEINSEN, "FLUX_RATIO_PI_PJ", 
                                    &colnum, &status))
                {
                    amdlibReturnFitsError("FLUX_RATIO");
                }
            }
            if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, nbWlen,
                              &amdlibBval, 
                              photometry->table[iFrame-1].fluxRatPiPj,
                              &anynull, &status))
            {
                amdlibReturnFitsError("FLUX_RATIO");
            }

            if (fits_get_colnum(filePtr, CASEINSEN,"*FLUX_RATIO_CORRECTION",
                                &colnum, &status))
            {
                /* Try old colunm name */
                status = 0;
                if (fits_get_colnum(filePtr, CASEINSEN, "FLUX_RATIO_PI_PJ_ERR", 
                                    &colnum, &status))
                {
                    amdlibReturnFitsError("FLUX_RATIO_CORRECTION");
                }
            }
            if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, nbWlen,
                              &amdlibBval, 
                              photometry->table[iFrame-1].sigma2FluxRatPiPj,
                              &anynull, &status))
            {
                amdlibReturnFitsError("FLUX_RATIO_CORRECTION");
            }

            /* Correct values of errors */
            for (lVis = 0; lVis < nbWlen; lVis++)
            {
                if (!amdlibCompareDouble(photometry->table[iFrame-1].sigma2FluxSumPiPj[lVis],amdlibBLANKING_VALUE))
                {
                    photometry->table[iFrame-1].sigma2FluxSumPiPj[lVis] = 
                    amdlibPow2(photometry->table[iFrame-1].sigma2FluxSumPiPj[lVis]);
                
                    photometry->table[iFrame-1].sigma2FluxRatPiPj[lVis] = 
                    amdlibPow2(photometry->table[iFrame-1].sigma2FluxRatPiPj[lVis]);
                }
            }
            
            /* Read PiMultPj (photometric correction on the mks) */
            if (fits_get_colnum(filePtr, CASEINSEN, "*FLUX_PRODUCT", 
                                &colnum, &status))
            {
                /* Try old colunm name */
                status = 0;
                if (fits_get_colnum(filePtr, CASEINSEN, "PI_MULTIPLIED_BY_PJ", 
                                    &colnum, &status))
                {
                    amdlibReturnFitsError("FLUX_PRODUCT");
                }
            }
            if (fits_read_col(filePtr, TDOUBLE, colnum, iFrame, 1, nbWlen,
                              &amdlibBval, photometry->table[iFrame-1].PiMultPj,
                              &anynull, &status))
            {
                amdlibReturnFitsError("FLUX_PRODUCT");
            }
        }
    }
    free(pistonOPD);
    free(sigmaPiston);
    free(frgContrastSnr);
    
    return amdlibSUCCESS;
}

/**
 * Read OI_ARRAY table in OI-FITS file
 *
 * This function reads the OI_ARRAY binary table defined in the IAU standard
 * in the OI-FITS file given as parameter.
 *
 * @param filePtr pointer on the file to be read.
 * @param array OI_ARRAY produced.
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibReadOiArray(fitsfile        *filePtr,
                                   amdlibOI_ARRAY  *array,
                                   amdlibERROR_MSG errMsg )
{
    int        status = 0;
    char       fitsioMsg[256];
    char       comment[amdlibKEYW_CMT_LEN+1], name[amdlibKEYW_VAL_LEN+1];
    char       *p;
    char       nullstring[] = "NULL";
    int        nullint = 0;
    double     nulldouble = 0.0;
    const int  revision = amdlib_OI_REVISION;
    int        dataRevision;
    int        irow, colnum, anynull;
    long       repeat;

    amdlibLogTrace("amdlibReadOiArray()");

    /* Read the OI_ARRAY table */
    if (fits_movnam_hdu(filePtr, BINARY_TBL, "OI_ARRAY", 0, &status))
    {
        amdlibLogWarning("No OI_ARRAY table, please consider recomputing "
                         "your data !");
        return amdlibSUCCESS;
    }

    /* Read array name */
    if (fits_read_key(filePtr, TSTRING, "ARRNAME", name, comment, &status))
    {
        amdlibReturnFitsError("ARRNAME");
    }

    /* Read revision number of the table definition */
    if (fits_read_key(filePtr, TINT, "OI_REVN", &dataRevision, comment, 
                      &status))
    {
        amdlibReturnFitsError("OI_REVN");
    }
    if (dataRevision > revision) 
    {
        amdlibSetErrMsg("Unable to read this revision of the OI_ARRAY data");
        return amdlibFAILURE;
    }

    /* Get number of rows */
    if (fits_get_num_rows(filePtr, &repeat, &status))
    {
        amdlibReturnFitsError("repeat");
    }

    /* Allocate structure array */
    if (amdlibAllocateOiArray(array, repeat, errMsg) != amdlibSUCCESS)
    {
        return amdlibFAILURE;
    }

    array->nbStations = repeat;
    strcpy(array->arrayName, name);

    /* Read coordinate frame */
    if (fits_read_key(filePtr, TSTRING, "FRAME", array->coordinateFrame,
                     comment, &status))
    {
        amdlibReturnFitsError("FRAME");
    }

    /* Read array center x, y and z coordinates */
    if (fits_read_key(filePtr, TDOUBLE, "ARRAYX", 
                      &array->arrayCenterCoordinates[0], comment, &status))
    {
        amdlibReturnFitsError("ARRAYX");
    }
    if (fits_read_key(filePtr, TDOUBLE, "ARRAYY", 
                      &array->arrayCenterCoordinates[1], comment, &status))
    {
        amdlibReturnFitsError("ARRAYY");
    }
    if (fits_read_key(filePtr, TDOUBLE, "ARRAYZ", 
                      &array->arrayCenterCoordinates[2], comment, &status))
    {
        amdlibReturnFitsError("ARRAYZ");
    }

    /* Read rows */
    for (irow=1; irow <= array->nbStations; irow++) 
    {
        /* Read telescope name */
        if (fits_get_colnum(filePtr, CASEINSEN, "TEL_NAME", &colnum, &status))
        {
            amdlibReturnFitsError("TEL_NAME");
        }
        p = array->element[irow-1].telescopeName;
        if (fits_read_col(filePtr, TSTRING, colnum, irow, 1, 1,
                         nullstring, &p, &anynull, &status))
        {
            amdlibReturnFitsError("TEL_NAME");
        }

        /* Read station name */
        if (fits_get_colnum(filePtr, CASEINSEN, "STA_NAME", &colnum, &status))
        {
            amdlibReturnFitsError("STA_NAME");
        }
        p = array->element[irow-1].stationName;
        if (fits_read_col(filePtr, TSTRING, colnum, irow, 1, 1,
                         nullstring, &p, &anynull, &status))
        {
            amdlibReturnFitsError("STA_NAME");
        }

        /* Read station index */
        if (fits_get_colnum(filePtr, CASEINSEN, "STA_INDEX", &colnum, &status))
        {
            amdlibReturnFitsError("STA_INDEX");
        }
        if (fits_read_col(filePtr, TINT, colnum, irow, 1, 1, &nullint,
                         &array->element[irow-1].stationIndex, 
                         &anynull, &status))
        {
            amdlibReturnFitsError("STA_INDEX");
        }

        /* Read diameter (metres) */
        if (fits_get_colnum(filePtr, CASEINSEN, "DIAMETER", &colnum, &status))
        {
            amdlibReturnFitsError("DIAMETER");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, irow, 1, 1,
                          &nulldouble, &array->element[irow-1].elementDiameter,
                          &anynull, &status))
        {
            amdlibReturnFitsError("DIAMETER");
        }

        /* Read station coordinates */
        if (fits_get_colnum(filePtr, CASEINSEN, "STAXYZ", &colnum,
                            &status))
        {
            amdlibReturnFitsError("STAXYZ");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, irow, 1, 3,
                          &nulldouble, 
                          &array->element[irow-1].stationCoordinates,
                          &anynull, &status)) 
        {
            amdlibReturnFitsError("STAXYZ");
        }

    }

    return amdlibSUCCESS;

}

/**
 * Read OI_TARGET table in OI-FITS file.
 *
 * This function reads the OI_TARGET binary table defined in the IAU standard
 * in the OI-FITS file given as parameter.
 *
 * @param filePtr pointer on the file to be read.
 * @param target OI_TARGET produced.
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
 */
amdlibCOMPL_STAT amdlibReadOiTarget(fitsfile        *filePtr, 
                                    amdlibOI_TARGET *target, 
                                    amdlibERROR_MSG  errMsg )
{
    char       fitsioMsg[256];
    char       comment[amdlibKEYW_CMT_LEN+1];
    int        status = 0;
    char       *p;
    char       nullstring[] = "NULL";
    int        nullint = 0;
    double     nulldouble = 0.0;
    const int  revision = amdlib_OI_REVISION;
    int        dataRevision;
    int        irow, colnum, anynull;
    long       repeat;

    amdlibLogTrace("amdlibReadOiTarget()");

    /* Read the OI_TARGET table */
    if (fits_movnam_hdu(filePtr, BINARY_TBL, "OI_TARGET", 0, &status))
    {
        amdlibLogWarning("No OI_TARGET table, please consider recomputing "
                         "your data !");
        return amdlibSUCCESS;
    }

    /* Read revision number of the table definition */
    if (fits_read_key(filePtr, TINT, "OI_REVN", &dataRevision,
                      comment, &status))
    {
        amdlibReturnFitsError("OI_REVN");
    }
    if (dataRevision > revision) 
    {
        amdlibSetErrMsg("Unable to read this revision of the OI_TARGET data");
        return amdlibFAILURE;
    }

    /* Get number of rows */
    if (fits_get_num_rows(filePtr, &repeat, &status))
    {
        amdlibReturnFitsError("repeat");
    }

    /* Allocate structure */
    if (amdlibAllocateOiTarget(target, repeat) != amdlibSUCCESS)
    {
        amdlibReturnFitsError("Could not allocate memory for TARGET");
    }

    target->nbTargets = repeat;

    /* Now read rows */
    for (irow=1; irow <= target->nbTargets; irow++) 
    {
        /* Read target identity */
        if (fits_get_colnum(filePtr, CASEINSEN, "TARGET_ID", &colnum, &status))
        {
            amdlibReturnFitsError("TARGET_ID");
        }
        if (fits_read_col(filePtr, TINT, colnum, irow, 1, 1, &nullint,
                          &target->element[irow-1].targetId, &anynull, &status))
        {
            amdlibReturnFitsError("TARGET_ID");
        }

        /* Read target name */
        if (fits_get_colnum(filePtr, CASEINSEN, "TARGET", &colnum, &status))
        {
            amdlibReturnFitsError("TARGET");
        }
        p = target->element[irow-1].targetName;
        if (fits_read_col(filePtr, TSTRING, colnum, irow, 1, 1,
                          nullstring, &p, &anynull, &status))
        {
            amdlibReturnFitsError("TARGET Name");
        }

        /* Read right ascension at mean equinox */
        if (fits_get_colnum(filePtr, CASEINSEN, "RAEP0", &colnum, &status))
        {
            amdlibReturnFitsError("RAEP0");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, irow, 1, 1,
                          &nulldouble, &target->element[irow-1].raEp0,
                          &anynull, &status))
        {
            amdlibReturnFitsError("RAEP0");
        }

        /* Read declination at mean equinox */
        if (fits_get_colnum(filePtr, CASEINSEN, "DECEP0", &colnum, &status))
        {
            amdlibReturnFitsError("DECEP0");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, irow, 1, 1,
                          &nulldouble, &target->element[irow-1].decEp0,
                          &anynull, &status))
        {
            amdlibReturnFitsError("DECEP0");
        }

        /* Read equinox of RAEP0 and DECEP0 */
        if (fits_get_colnum(filePtr, CASEINSEN, "EQUINOX", &colnum, &status))
        {
            amdlibReturnFitsError("EQUINOX");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, irow, 1, 1,
                          &nulldouble, &target->element[irow-1].equinox,
                          &anynull, &status))
        {
            amdlibReturnFitsError("EQUINOX");
        }

        /* Read errors associated to RAEP0 and DECEP0 */
        if (fits_get_colnum(filePtr, CASEINSEN, "RA_ERR", &colnum, &status))
        {
            amdlibReturnFitsError("RA_ERR");        
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, irow, 1, 1,
                          &nulldouble, &target->element[irow-1].raErr,
                          &anynull, &status))
        {
            amdlibReturnFitsError("RA_ERR");
        }
        if (fits_get_colnum(filePtr, CASEINSEN, "DEC_ERR", &colnum, &status))
        {
            amdlibReturnFitsError("DEC_ERR");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, irow, 1, 1,
                          &nulldouble, &target->element[irow-1].decErr,
                          &anynull, &status))
        {
            amdlibReturnFitsError("DEC_ERR");
        }

        /* Read systemic radial velocity */
        if (fits_get_colnum(filePtr, CASEINSEN, "SYSVEL", &colnum, &status))
        {
            amdlibReturnFitsError("SYSVEL");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, irow, 1, 1,
                          &nulldouble, &target->element[irow-1].sysVel,
                          &anynull, &status))
        {
            amdlibReturnFitsError("SYSVEL");
        }

        /* Read velocity type */
        if (fits_get_colnum(filePtr, CASEINSEN, "VELTYP", &colnum, &status))
        {
            amdlibReturnFitsError("VELTYP");
        }
        p = target->element[irow-1].velTyp;
        if (fits_read_col(filePtr, TSTRING, colnum, irow, 1, 1,
                          nullstring, &p, &anynull, &status))
        {
            amdlibReturnFitsError("VELTYP");
        }

        /* Read definition of radial velocity */
        if (fits_get_colnum(filePtr, CASEINSEN, "VELDEF", &colnum, &status))
        {
            amdlibReturnFitsError("VELDEF");
        }
        p = target->element[irow-1].velDef;
        if (fits_read_col(filePtr, TSTRING, colnum, irow, 1, 1,
                          nullstring, &p, &anynull, &status))
        {
            amdlibReturnFitsError("VELDEF");
        }

        /* Read proper motion in RA */
        if (fits_get_colnum(filePtr, CASEINSEN, "PMRA", &colnum, &status))
        {
            amdlibReturnFitsError("PMRA");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, irow, 1, 1,
                          &nulldouble, &target->element[irow-1].pmRa,
                          &anynull, &status))
        {
            amdlibReturnFitsError("PMRA");
        }

        /* Read proper motion in declination */
        if (fits_get_colnum(filePtr, CASEINSEN, "PMDEC", &colnum, &status))
        {
            amdlibReturnFitsError("PMDEC");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, irow, 1, 1,
                          &nulldouble, &target->element[irow-1].pmDec,
                          &anynull, &status))
        {
            amdlibReturnFitsError("PMDEC");
        }

        /* Read errors on PMRA and PMDEC */
        if (fits_get_colnum(filePtr, CASEINSEN, "PMRA_ERR", &colnum, &status))
        {
            amdlibReturnFitsError("PMRA_ERR");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, irow, 1, 1,
                          &nulldouble, &target->element[irow-1].pmRaErr,
                          &anynull, &status))
        {
            amdlibReturnFitsError("PMRA_ERR");
        }
        if (fits_get_colnum(filePtr, CASEINSEN, "PMDEC_ERR", &colnum, &status))
        {
            amdlibReturnFitsError("PMDEC_ERR");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, irow, 1, 1,
                          &nulldouble, &target->element[irow-1].pmDecErr,
                          &anynull, &status))
        {
            amdlibReturnFitsError("PMDEC_ERR");
        }

        /* Read parallax value and associated error */
        if (fits_get_colnum(filePtr, CASEINSEN, "PARALLAX", &colnum, &status))
        {
            amdlibReturnFitsError("PARALLAX");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, irow, 1, 1,
                          &nulldouble, &target->element[irow-1].parallax,
                          &anynull, &status))
        {
            amdlibReturnFitsError("PARALLAX");
        }
        if (fits_get_colnum(filePtr, CASEINSEN, "PARA_ERR", &colnum, &status))
        {
            amdlibReturnFitsError("PARA_ERR");
        }
        if (fits_read_col(filePtr, TDOUBLE, colnum, irow, 1, 1,
                          &nulldouble, &target->element[irow-1].paraErr,
                          &anynull, &status))
        {
            amdlibReturnFitsError("PARA_ERR");
        }

        /* Read spectral type */
        if (fits_get_colnum(filePtr, CASEINSEN, "SPECTYP", &colnum, &status))
        {
            amdlibReturnFitsError("SPECTYP");
        }
        p = target->element[irow-1].specTyp;
        if (fits_read_col(filePtr, TSTRING, colnum, irow, 1, 1,
                          nullstring, &p, &anynull, &status))
        {
            amdlibReturnFitsError("specTyp");
        }
    }
    return amdlibSUCCESS;
}

