/*******************************************************************************
 * JMMC project ( http://www.jmmc.fr ) - Copyright (C) CNRS.
 ******************************************************************************/
/**
 * @file
 * Functions to convert raw data to science data 
 */

#define _POSIX_SOURCE 1

/* 
 * System Headers
 */
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "fitsio.h"

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

/* Local functions */
amdlibCOMPL_STAT amdlibComputeBaselines(amdlibISS_INFO *iss, int nbBases, 
                                        double duration);
double amdlibSexagToRad(double value, int code);
static void amdlibInitScienceData(amdlibSCIENCE_DATA *scienceData);
static amdlibCOMPL_STAT amdlibGetSpecChannels(
                                        amdlibRAW_DATA     *rawData,
                                        amdlibSCIENCE_DATA *scienceData,
                                        int                globalStartOffset,
                                        int                globalEndOffset,
                                        amdlibERROR_MSG    errMsg);
static amdlibCOMPL_STAT amdlibGetScienceData(
                                        amdlibRAW_DATA       *rawData,
                                        amdlibWAVEDATA       *waveData,
                                        amdlibSCIENCE_DATA   *scienceData,
                                        int                  globalStartOffset,
                                        int                  globalEndOffset,
                                        amdlibBOOLEAN        sumData,
                                        amdlibERROR_MSG      errMsg);
static amdlibCOMPL_STAT amdlibDiscardBadChannelsIntf(
                                                    amdlibDOUBLE           *intf,
                                                    amdlibDOUBLE           *sigma2Intf,
                                                    int             intfWidth,
                                                    int             *channelNo,
                                                    int             nbChannels);
static amdlibCOMPL_STAT amdlibDiscardBadChannelsPhoto(
                                                amdlibDOUBLE           *photo,
                                                amdlibDOUBLE           *sigma2Photo,
                                                int             *channelNo,
                                                int             nbChannels);
static amdlibCOMPL_STAT amdlibDiscardBadChannelsNo(int             *channelNo,
                                                   int             *nbChannels);
/* 
 * Public functions 
 */
/**
 * Release memory allocated to store science data
 *
 * @param scienceData structure where science data is stored
 */
void amdlibReleaseScienceData(amdlibSCIENCE_DATA  *scienceData)
{
    amdlibLogTrace("amdlibReleaseScienceData()"); 

    amdlibFreeScienceData(scienceData);
    memset(scienceData, '\0', sizeof(amdlibSCIENCE_DATA));    
}

#define amdlibCODE_RA (int)24
#define amdlibCODE_DEC (int)180
/**
 * Convert raw data to science data.
 *
 * This function extracts from raw data the science data taking into account the
 * offsets between photometric channels and interferometric channel. This
 * consists in :
 *      \li extracting spectrum of the photometric channels; i.e. sum all
 *          pixels along X-axis, 
 *      \li shifting photometric spectrum of the relative offset given in the
 *          waveData structure,
 *      \li extracting the interferometric channel,
 *      \li removing unusable channels; i.e. channels for which there is missing
 *          information either photometric or interferometric data,
 *      \li computing flux ratio between photometric channels.
 *
 * @param rawData structure where raw data is stored
 * @param waveData structure containing spectral dispersion table and relative
 * offsets between photometric channels and interferometric channel. If a null
 * pointer is given, the waveData structure from raw data is used.
 * @param scienceData structure where science data is stored
 * @param sumData if true, resulting science data is average of all frames
 * contained in raw data.
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned.
  */
amdlibCOMPL_STAT amdlibRawData2ScienceData(amdlibRAW_DATA     *rawData,
                                           amdlibWAVEDATA     *waveData,
                                           amdlibSCIENCE_DATA *scienceData,
                                           amdlibBOOLEAN      sumData,
                                           amdlibERROR_MSG    errMsg)
{
    amdlibDOUBLE downOffset, upOffset;
    int offset, nbPhotoChannels;
    int i;
    int didNotInheritWaveData=0;
    char keyName[amdlibKEYW_NAME_LEN+1];
    char keyVal[amdlibKEYW_NAME_LEN+1];
    int beam, nbTel, nbBases;
    double ra,dec;
    double duration;
    int unableToGetBaseInfo=0;
    
    amdlibLogTrace("amdlibRawData2ScienceData()");
    
    /* If not given, use the wave data of the raw data. */
    if (waveData == NULL)
    {
        waveData = &rawData->waveData;
        didNotInheritWaveData=1;
    }
    /* Check raw data structure contains data */
    if (rawData->dataLoaded == amdlibFALSE)
    {
        amdlibSetErrMsg("The raw data structure does not "
                        "contain data. Check call to amdlibLoadRawData()");
        return amdlibFAILURE;
    }

    /* Check that raw data did not only contain detector data, but that it also
     * contained ICS status and wave data which are needed to convert raw data
     * to science data */
    if (rawData->detDataOnly == amdlibTRUE && didNotInheritWaveData)
    {
        amdlibSetErrMsg("The raw data file contains "
                        "neither ICS header nor wave data binary table");
        return amdlibFAILURE;
    }

    /* Check number of columns. The raw data must at least contains 4 columns:
     * interferometric channel and 2 photometric channels */
    if (rawData->nbCols < 4)
    {
        amdlibSetErrMsg("The raw data does not contain interferometric data");
        return amdlibFAILURE;
    }

    /* If science data structure is not initialized, do it */
    if (scienceData->thisPtr != scienceData)
    {
        /* Initialise data structure */
        amdlibInitScienceData(scienceData);
    }
    /* Else */
    else
    {
        /* Free allocated memory */
        amdlibFreeScienceData(scienceData);
    }
    /* End if */

    /* Determine the number of photometric channels */
    if (rawData->nbCols == 4)
    {
        nbPhotoChannels = 2;
        nbTel = 2;
        nbBases = 1;
    }
    else
    {
        nbPhotoChannels = 3;
        nbTel = 3;
        nbBases = 3;
    }
    /* If spectral calibration has not been done, returns error */
    for (offset = 0; offset < nbPhotoChannels; offset++)
    {
        if (waveData->photoOffset[offset] == amdlibOFFSETY_NOT_CALIBRATED)
        {
            amdlibSetErrMsg("Spectral calibration has not been done for this "
                            "raw data");
            return amdlibFAILURE;
        }
    }

    /* Determine the first and last spectral channels taking into account the
     * offsets between photometric channels and interferometric channel.
     * Remove spectral channels not having entries in interferometric channel
     * and photometric channels */
    downOffset = 0.0;
    upOffset = 0.0;
    for (offset = 0; offset < nbPhotoChannels; offset++)
    {
        downOffset = amdlibMin(downOffset, waveData->photoOffset[offset]);
        upOffset = amdlibMax(upOffset, waveData->photoOffset[offset]);
    }
    downOffset = floor(downOffset);  /* Get the largest integer not greater
                                        than down offset */
    upOffset = ceil(upOffset);   /* Get the smallest integer not less than*/
    amdlibLogTest("Interf to Photo total offset : down = %f - up = %f",
                  downOffset, upOffset);

    /* Set position and dimension of the columns */
    scienceData->col[amdlibPHOTO1_CHANNEL].startPixel = 
        rawData->region[1].corner[0];
    scienceData->col[amdlibPHOTO1_CHANNEL].nbPixels = 
        rawData->region[1].dimAxis[0];
    scienceData->col[amdlibPHOTO2_CHANNEL].startPixel = 
        rawData->region[2].corner[0];
    scienceData->col[amdlibPHOTO2_CHANNEL].nbPixels = 
        rawData->region[2].dimAxis[0];
    if (rawData->nbCols == 5)
    {
        scienceData->col[amdlibPHOTO3_CHANNEL].startPixel = 
            rawData->region[4].corner[0];
        scienceData->col[amdlibPHOTO3_CHANNEL].nbPixels = 
            rawData->region[4].dimAxis[0];
    }
    scienceData->col[amdlibINTERF_CHANNEL].startPixel = 
        rawData->region[3].corner[0];
    scienceData->col[amdlibINTERF_CHANNEL].nbPixels = 
        rawData->region[3].dimAxis[0];

    /* Store station indexes, coordinates, and other useful info */
    memset(scienceData->issInfo.stationIndex, '\0', amdlibNB_TEL * sizeof(int));
    for (i=0; i < 3; i++) 
    {
        memset(scienceData->issInfo.stationCoordinates[i], '\0', 
               amdlibNB_TEL *sizeof(double));
    }
    if (amdlibGetInsCfgKeyword(&(rawData->insCfg),"HIERARCH ESO ISS REF RA",
                               keyVal, errMsg) == amdlibSUCCESS)

    {
        sscanf(keyVal, "%lf", &ra);
        /*Convert Ra to radians (ISS.REF.RA seems to be HHMMSS.SS*/
        scienceData->issInfo.ra=amdlibSexagToRad(ra,amdlibCODE_RA);
    }
    else
    {
        unableToGetBaseInfo=1;
    }
    
    if (amdlibGetInsCfgKeyword(&(rawData->insCfg),"HIERARCH ESO ISS REF DEC" ,
                               keyVal, errMsg) == amdlibSUCCESS)
    { 
        sscanf(keyVal, "%lf", &dec);
        /*Convert Dec to radians*/
        scienceData->issInfo.dec=amdlibSexagToRad(dec,amdlibCODE_DEC);
    } else
    {
        unableToGetBaseInfo=1;
    }
    
    if (amdlibGetInsCfgKeyword(&(rawData->insCfg),"HIERARCH ESO ISS GEOLAT" ,
                               keyVal, errMsg) == amdlibSUCCESS)
    { 
        sscanf(keyVal, "%lf", &(scienceData->issInfo.geoLat));
        /*Convert geoLat (degrees) to radians*/
        scienceData->issInfo.geoLat=scienceData->issInfo.geoLat*M_PI/180.0;
    }
    else
    {
        unableToGetBaseInfo=1;
    }

    if (amdlibGetInsCfgKeyword(&(rawData->insCfg), "LST     ",
                               keyVal, errMsg) == amdlibSUCCESS)
    { 
        sscanf(keyVal, "%lf", &(scienceData->issInfo.lst));
        /* Lst is in seconds of TIME. convert to Radians */
        scienceData->issInfo.lst=scienceData->issInfo.lst*M_PI/3600.0/12.0;
    }
    else
    {
        unableToGetBaseInfo=1;
    }
    
    for (beam=0; beam < nbTel; beam++)
    {
        sprintf(keyName, "HIERARCH ESO ISS CONF STATION%d", beam+1);
        if (amdlibGetInsCfgKeyword(&(rawData->insCfg), keyName,
                                   keyVal, errMsg) != amdlibSUCCESS)
        {
#if 0
            amdlibLogWarning("HIERARCH ESO ISS CONF STATION%d keyword missing",
                             beam+1);
            amdlibLogWarningDetail("Impossible to associate beam%d to any tel",
                                   beam+1);
#endif
        }
        for (i=0; i < rawData->arrayGeometry.nbStations; i++)
        {
            if (strstr(keyVal, 
                       rawData->arrayGeometry.element[i].stationName) != 0)
            {
                scienceData->issInfo.stationIndex[beam] = 
                    rawData->arrayGeometry.element[i].stationIndex;
                break;
            }
        }
        char *axis[3] = { "X", "Y", "Z" };
        for (i=0; i < 3; i++)
        {
            sprintf(keyName, "HIERARCH ESO ISS CONF T%d%1s", beam+1,axis[i]);
            if (amdlibGetInsCfgKeyword(&(rawData->insCfg), keyName,
                                       keyVal, errMsg) == amdlibSUCCESS)
            {
                sscanf(keyVal, "%lf", 
                       &(scienceData->issInfo.stationCoordinates[i][beam]));
            }
            else  
            {
                unableToGetBaseInfo=1;
            }
        }
    }
    
    /* Compute baseline position at begin and end of the observation 
     * and their respective angles. We Shall NOT trust the PBL and PBLA values 
     * given by ISS. Instead we update them! */
    memset(scienceData->issInfo.projectedBaseStart, '\0', 
           amdlibNBASELINE * sizeof(double)); 
    memset(scienceData->issInfo.projectedBaseEnd, '\0', 
           amdlibNBASELINE * sizeof(double));     
    memset(scienceData->issInfo.projectedAngleStart, '\0', 
           amdlibNBASELINE * sizeof(double));     
    memset(scienceData->issInfo.projectedAngleEnd, '\0', 
           amdlibNBASELINE * sizeof(double));     
    if (unableToGetBaseInfo == 1) 
    {
        /* Print warning, only if it is not a calibration file */
        if (rawData->frameType == amdlibUNKNOWN_FRAME)
        {
            amdlibLogWarning("Unable to retrieve necessary information to "
                             "recompute baselines");
        }
        unableToGetBaseInfo=0;
        for (i = 0; i < nbBases; i++)
        {
            int tel1;
            int tel2;
            if (nbBases == 1)
            {
                tel1 = 1;
                tel2 = 2;
            }
            else
            {
                tel1 = amdlibMin((i % nbBases) + 1, ((i+1) % nbBases) + 1);
                tel2 = amdlibMax((i % nbBases) + 1, ((i+1) % nbBases) + 1);
            }
            
            char pos[amdlibKEYW_VAL_LEN+1];
            
            sprintf(keyName, "HIERARCH ESO ISS PBL%d%d START", tel1, tel2);
            if (amdlibGetInsCfgKeyword(&(rawData->insCfg), keyName,
                                       pos, errMsg) == amdlibSUCCESS)
            {
                sscanf(pos, "%lf", 
                       &(scienceData->issInfo.projectedBaseStart[i]));
            } else unableToGetBaseInfo=1;
            
            sprintf(keyName, "HIERARCH ESO ISS PBL%d%d END", tel1, tel2);
            if (amdlibGetInsCfgKeyword(&(rawData->insCfg), keyName,
                                       pos, errMsg) == amdlibSUCCESS)
            {
                sscanf(pos, "%lf", &(scienceData->issInfo.projectedBaseEnd[i]));
            } else unableToGetBaseInfo=1;
            
            sprintf(keyName, "HIERARCH ESO ISS PBLA%d%d START", tel1, tel2);
            if (amdlibGetInsCfgKeyword(&(rawData->insCfg), keyName,
                                       pos, errMsg) == amdlibSUCCESS)
            {
                sscanf(pos, "%lf", 
                       &(scienceData->issInfo.projectedAngleStart[i]));
            } else unableToGetBaseInfo=1;
            
            sprintf(keyName, "HIERARCH ESO ISS PBLA%d%d END", tel1, tel2);
            if (amdlibGetInsCfgKeyword(&(rawData->insCfg), keyName,
                                       pos, errMsg) == amdlibSUCCESS)
            {
                sscanf(pos, "%lf", 
                       &(scienceData->issInfo.projectedAngleEnd[i]));
            } else unableToGetBaseInfo=1;
        }
        /* Print warning, only if it is not a calibration file */
        if (rawData->frameType == amdlibUNKNOWN_FRAME)
        {
            if (unableToGetBaseInfo)
            {
                amdlibLogWarning("No (u,v) position available !");
            }
            else
            {
                amdlibLogWarning("Using ISS (possibly wrong) baselines values" 
                                 "for (u,v) positions !");
            }
        }
    }
    else /* call compute baselines for start and end of time*/
    {
        /*Estimate total duration. here it is a bit difficult since the total
         * duration is NOT exactly the DIT*NDIT, so I read the time tag of the 
         * real data */
        duration=rawData->timeTag[rawData->nbFrames-1]-rawData->timeTag[0]; 
        /**in days*/
        duration=duration*24.0*3600.0;
        amdlibComputeBaselines(&scienceData->issInfo,nbBases,duration);
        /* Update ESO Keywords with Good Ones */
        for (i = 0; i < nbBases; i++)
        {
            int tel1;
            int tel2;
            if (nbBases == 1)
            {
                tel1 = 1;
                tel2 = 2;
            }
            else
            {
                tel1 = amdlibMin((i % nbBases) + 1, ((i+1) % nbBases) + 1);
                tel2 = amdlibMax((i % nbBases) + 1, ((i+1) % nbBases) + 1);
            }
            
            char pos[amdlibKEYW_VAL_LEN+1];
            char comment[amdlibKEYW_VAL_LEN+1];
            sprintf(keyName, "HIERARCH ESO ISS PBL%d%d START", tel1, tel2);
            sprintf(comment, " Projected baseline T%d T%d start", tel1, tel2);
            sprintf(pos," %lf ",scienceData->issInfo.projectedBaseStart[i]);
            amdlibSetInsCfgKeyword(&(rawData->insCfg), keyName,
                                   pos, comment, errMsg);
            sprintf(keyName, "HIERARCH ESO ISS PBL%d%d END", tel1, tel2);
            sprintf(comment, " Projected baseline T%d T%d end", tel1, tel2);
            sprintf(pos," %lf ",scienceData->issInfo.projectedBaseEnd[i]);
            amdlibSetInsCfgKeyword(&(rawData->insCfg), keyName,
                                   pos, comment, errMsg);
            sprintf(keyName, "HIERARCH ESO ISS PBLA%d%d START", tel1, tel2);
            sprintf(comment, " Projected base angle T%d T%d start", 
                    tel1, tel2);
            sprintf(pos," %lf ",scienceData->issInfo.projectedAngleStart[i]);
            amdlibSetInsCfgKeyword(&(rawData->insCfg), keyName,
                                   pos, comment, errMsg);
            sprintf(keyName, "HIERARCH ESO ISS PBLA%d%d END", tel1, tel2);
            sprintf(comment, " Projected base angle T%d T%d end", tel1, tel2);
            sprintf(pos," %lf ",scienceData->issInfo.projectedAngleEnd[i]);
            amdlibSetInsCfgKeyword(&(rawData->insCfg), keyName,
                                   pos, comment, errMsg);
       }
    }
    
    /* Get spectral channels */
    if (amdlibGetSpecChannels(rawData, scienceData, (int)upOffset,
                                (int)downOffset, errMsg) != amdlibSUCCESS)
    {
        return amdlibFAILURE;
    }

    /* Get science data from raw data: extract interferometry data and
     * compute photometry */
    if (amdlibGetScienceData(rawData, waveData, scienceData, (int)upOffset,
                             (int)downOffset, sumData, errMsg)!=amdlibSUCCESS)
    {
        return amdlibFAILURE;
    }

    /* Copy P2VM id */
    scienceData->p2vmId = rawData->p2vmId;

    /* Copy instrument configuration (if needed) and set visibility */
    for (i=0; i < rawData->insCfg.nbKeywords; i++)
    {    
        if (amdlibSetInsCfgKeyword(&scienceData->insCfg,
                                   rawData->insCfg.keywords[i].name,
                                   rawData->insCfg.keywords[i].value,
                                   rawData->insCfg.keywords[i].comment,
                                   errMsg) != amdlibSUCCESS)
        {
            return amdlibFAILURE;
        }
    }

    /* Store Photometric Beams spectral shifts */
    char value[amdlibKEYW_VAL_LEN+1];
    memset(value, '\0', (amdlibKEYW_VAL_LEN+1)*sizeof(char));    
    sprintf(value, " %f", waveData->photoOffset[0]);
    if (amdlibSetInsCfgKeyword(&scienceData->insCfg, 
                               "HIERARCH ESO QC P1 OFFSETY",
                               value, "", errMsg) != amdlibSUCCESS)
    {
        return amdlibFAILURE;
    }

    memset(value, '\0', (amdlibKEYW_VAL_LEN+1)*sizeof(char));    
    sprintf(value, " %f", waveData->photoOffset[1]);
    if (amdlibSetInsCfgKeyword(&scienceData->insCfg,
                               "HIERARCH ESO QC P2 OFFSETY",
                               value, "", errMsg) != amdlibSUCCESS)
    {
        return amdlibFAILURE;
    }
    memset(value, '\0', (amdlibKEYW_VAL_LEN+1)*sizeof(char));    
    sprintf(value, " %f", waveData->photoOffset[2]);
    if (amdlibSetInsCfgKeyword(&scienceData->insCfg, 
                               "HIERARCH ESO QC P3 OFFSETY",
                               value, "", errMsg) != amdlibSUCCESS)
    {
        return amdlibFAILURE;
    }


    return amdlibSUCCESS;
}

/* 
 * Protected functions 
 */
/**
 * Free memory allocated memory for science data structure.
 *
 * This function frees previously allocated memory (if any) where science data
 * has been stored. 
 * 
 * @param scienceData structure where science data is stored
 */
void amdlibFreeScienceData(amdlibSCIENCE_DATA *scienceData)
{
    int frame;

    amdlibLogTrace("amdlibFreeScienceData()");
    
    /* If science data structure is not initialized, do it */
    if (scienceData->thisPtr != scienceData)
    {
        /* Initialise data structure */
        amdlibInitScienceData(scienceData);
    }

    /* Free science data structure */
    if (scienceData->timeTag != NULL)
    {
        free(scienceData->timeTag);
    }

    if (scienceData->channelNo != NULL)
    {
        free(scienceData->channelNo);
        scienceData->channelNo = NULL;
    }

    if (scienceData->badPixels != NULL)
    {
        amdlibFree2DArrayUnsignedChar(scienceData->badPixelsPt);
        scienceData->badPixels = NULL;
    }

    /* For all defined frames */
    for (frame = 0; frame < scienceData->nbFrames; frame++)
    {
        /* Free arrays containing the data */
        if (scienceData->frame[frame].intf != NULL)
        {
            free(scienceData->frame[frame].intf);
            scienceData->frame[frame].intf = NULL;
        }
        if (scienceData->frame[frame].sigma2Intf != NULL)
        {
            free(scienceData->frame[frame].sigma2Intf);
            scienceData->frame[frame].sigma2Intf = NULL;
        }
        if (scienceData->frame[frame].photo1 != NULL)
        {
            free(scienceData->frame[frame].photo1);
            scienceData->frame[frame].photo1 = NULL;
        }
        if (scienceData->frame[frame].photo2 != NULL)
        {
            free(scienceData->frame[frame].photo2);
            scienceData->frame[frame].photo2 = NULL;
        }
        if (scienceData->frame[frame].photo3 != NULL)
        {
            free(scienceData->frame[frame].photo3);
            scienceData->frame[frame].photo3 = NULL;
        }

        if (scienceData->frame[frame].sigma2Photo1 != NULL)
        {
            free(scienceData->frame[frame].sigma2Photo1);
            scienceData->frame[frame].sigma2Photo1 = NULL;
        }
        if (scienceData->frame[frame].sigma2Photo2 != NULL)
        {
            free(scienceData->frame[frame].sigma2Photo2);
            scienceData->frame[frame].sigma2Photo2 = NULL;
        }
        if (scienceData->frame[frame].sigma2Photo3 != NULL)
        {
            free(scienceData->frame[frame].sigma2Photo3);
            scienceData->frame[frame].sigma2Photo3 = NULL;
        }
    }
    if (scienceData->frame != NULL)
    {
        free(scienceData->frame);
        scienceData->frame = NULL;
    }
    /* Reset the number of rows */
    scienceData->nbFrames = 0;
}

/*
 * Local functions
 */
/**
 * Initialize science data structure.
 *
 * @param scienceData pointer to science data structure
 */
void amdlibInitScienceData(amdlibSCIENCE_DATA *scienceData)
{
    amdlibLogTrace("amdlibInitScienceData()");
    
    /* Initialize data structure */
    memset (scienceData, '\0', sizeof(amdlibSCIENCE_DATA));
    scienceData->thisPtr = scienceData;
}

/**
 * Get number of the channels contained in the given raw data 
 *
 * This function gets the number (i.e index in the spectral dispersion table) of
 * the channels belonging to the given raw dat, taking into account the shifts
 * to be done on the photometric channels.
 *
 * @param rawData structure where raw data is stored
 * @param scienceData structure caontaining table to store the channel numbers
 * @param globalStartOffset the number of channels to be ignored at the
 * beginning of the spectral dispersion table; corresponding to the greatest
 * shifts to carry out on the photometric channels in downwards
 * @param globalEndOffset the number of channels to be ignored at the
 * end of the spectral dispersion table; corresponding to the greatest
 * shifts to carry out on the photometric channels in upwards
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned. 
 */
amdlibCOMPL_STAT amdlibGetSpecChannels(amdlibRAW_DATA     *rawData,
                                       amdlibSCIENCE_DATA *scienceData,
                                       int                globalStartOffset,
                                       int                globalEndOffset,
                                       amdlibERROR_MSG    errMsg)
{
    int row;
    int channel;
    int globalNbSpecChannels;
    int startPixel;
    int currentChannel;
    int currentNbSpecChannels;
    
    amdlibLogTrace("amdlibGetSpecChannels()");
    
    /* Set the global number of spectral channels : for each row, sum the
     * spectral channels considered in a column, then substract nbRows times 
     * the offset due to the offsets between interferometric channel and the 
     * photometric channels */
    globalNbSpecChannels = 0;
    for (row = 0; row < rawData->nbRows; row ++)
    {
        globalNbSpecChannels += rawData->region[row*rawData->nbCols].dimAxis[1];
    }
    scienceData->nbChannels = globalNbSpecChannels;
    /* This dimension, not taking into account the bad spectral channels, is
     * stored. Il will be modified when discarding bad frames (in
     * amdlibGetScienceData): bad spectral channels will be shifted at the end
     * of all our tables */

    /* Allocate memory for channelNo */
    scienceData->channelNo = (int *)calloc(globalNbSpecChannels, sizeof(int));
    if (scienceData->channelNo == NULL)
    {
        amdlibSetErrMsg("Could not allocate memory for channelNo table");
        return amdlibFAILURE;
    }
    
    /* Set the number of the channels belonging to the science data. */
    currentChannel = 0;
    for (row = 0; row < rawData->nbRows; row++)
    {
        /* Note: bad channels result of the photometric channels shift. 
         * Mark them as bad based upon offsets passed to the routine.
         */

        for (channel = 0; channel < (-1*globalEndOffset); channel ++)
        {
            scienceData->channelNo[currentChannel] = -1;
            currentChannel++;
        }

        startPixel = (rawData->region[row * rawData->nbCols].corner[1] - 1) + 
                     (-1*globalEndOffset);
        currentNbSpecChannels = rawData->region[row*rawData->nbCols].dimAxis[1]
                                - (globalStartOffset - globalEndOffset) ;

        for (channel = 0; channel < currentNbSpecChannels; channel ++)
        {
            scienceData->channelNo[currentChannel] = 
                startPixel + channel;
            currentChannel++;
        }

        for (channel = 0; channel < globalStartOffset ; channel ++)
        {
            scienceData->channelNo[currentChannel] = -1;
            currentChannel++;
        }
    }
    return amdlibSUCCESS;
}

/**
 * Extract science data from raw data 
 *
 * This function extracts the science data (i.e. spectrum of photometric
 * channels and interferometric data) from the raw data, and shifts the
 * photometric spectrum of the relative offset specified in wave data structure.
 *
 * @param rawData structure where raw data is stored
 * @param waveData structure containing spectral dispersion table and relative
 * offsets between photometric channels and interferometric channel. If a null
 * @param scienceData structure containing table to store the channel numbers
 * @param globalStartOffset the number of channels to be ignored at the
 * beginning of the spectral dispersion table; corresponding to the greatest
 * shifts to carry out on the photometric channels in downwards
 * @param globalEndOffset the number of channels to be ignored at the
 * end of the spectral dispersion table; corresponding to the greatest
 * shifts to carry out on the photometric channels in upwards
 * @param sumData if true, resulting science data is average of all frames
 * @param errMsg error description message returned if function fails.
 *
 * @return
 * amdlibSUCCESS on successful completion. Otherwise amdlibFAILURE is returned. 
 */
amdlibCOMPL_STAT amdlibGetScienceData(amdlibRAW_DATA       *rawData,
                                      amdlibWAVEDATA       *waveData,
                                      amdlibSCIENCE_DATA   *scienceData,
                                      int                  globalStartOffset,
                                      int                  globalEndOffset,
                                      amdlibBOOLEAN        sumData,
                                      amdlibERROR_MSG      errMsg)
{
    int       nbPhotoChannels;
    int       photoChannel;
    int       nbRawFrames;
    int       channel;
    int       f,l,i;
    int       iRow;  
    amdlibDOUBLE     *pPhoto;
    amdlibDOUBLE     *pSigma2Photo;
    amdlibDOUBLE     *pShiftPhoto;
    amdlibDOUBLE     *pShiftSigma2Photo;
    double    integratedPhoto;
    double    snrPhoto;
    int       nbFrames;
    int       nbChannels;
    int       intfWidth;
    amdlibDOUBLE     **intf,**sigma2Intf;
    amdlibDOUBLE     **photo;
    amdlibDOUBLE     **photo1 = NULL;
    amdlibDOUBLE     **photo2 = NULL;
    amdlibDOUBLE     **photo3 = NULL;
    amdlibDOUBLE     **sigma2Photo;
    amdlibDOUBLE     **sigma2Photo1 = NULL;
    amdlibDOUBLE     **sigma2Photo2 = NULL;
    amdlibDOUBLE     **sigma2Photo3 = NULL;
    double pixelShift;
    amdlibDOUBLE **sigma2IntfPtr;

    amdlibLogTrace("amdlibGetScienceData()");
    
    /* Number of frames in raw data */
    nbRawFrames =  rawData->region[0].dimAxis[2];

    /* Set the number of photometric channels */
    if (rawData->nbCols == 4)
    {
        nbPhotoChannels = 2;
    }
    else
    {
        nbPhotoChannels = 3;
    }
    /* Set number of frames */
    if (sumData == amdlibTRUE)
    {
        scienceData->nbFrames = 1;
    }
    else
    {
        scienceData->nbFrames = nbRawFrames;
    }
    /* Set number of columns */
    scienceData->nbCols = nbPhotoChannels + 1;    
    /* Set exposure time */
    scienceData->expTime = rawData->expTime;
   
    /* Allocate memory for Time Tag */
    scienceData->timeTag = 
        (double *)calloc(scienceData->nbFrames, sizeof(double));
    if (scienceData->timeTag == NULL)
    {
        amdlibSetErrMsg("Could not allocate memory for time tag");
        return amdlibFAILURE;
    }

    /* Copy raw data time tag to science data */
    memcpy(scienceData->timeTag, rawData->timeTag,
           scienceData->nbFrames * sizeof(*(scienceData->timeTag)));

   /* Allocate memory for all frames */
    scienceData->frame = calloc(scienceData->nbFrames,
                                sizeof(amdlibFRAME_SCIENCE_DATA));
    if (scienceData->frame == NULL)
    {
        amdlibSetErrMsg("Could not allocate memory for frames");
        return amdlibFAILURE;
    }

    /* Get dimensions of photometric and interferometric beams */
    intfWidth = rawData->region[3].dimAxis[0];
    nbChannels = 0;
    for (iRow = 0; iRow < rawData->nbRows; iRow ++)
    {
        nbChannels += rawData->region[iRow*rawData->nbCols].dimAxis[1];
    }
    nbFrames = scienceData->nbFrames;
    
    intf = (amdlibDOUBLE **)calloc(nbFrames, sizeof(amdlibDOUBLE *));
    if (intf == NULL)
    {
        amdlibSetErrMsg("Impossible to allocate memory for interferometric "
                        "beam");
        amdlibReleaseScienceData(scienceData);
        return amdlibFAILURE;
    }
    for (f = 0; f < nbFrames; f ++)
    {
        intf[f] = (amdlibDOUBLE *)calloc(intfWidth * nbChannels, sizeof(amdlibDOUBLE));
        if (intf[f] == NULL)
        {
            amdlibSetErrMsg("Impossible to allocate memory for interferometric "
                            "beam");
            amdlibReleaseScienceData(scienceData);
            return amdlibFAILURE;
        }
    }
    sigma2Intf = (amdlibDOUBLE **)calloc(nbFrames, sizeof(amdlibDOUBLE *));
    if (sigma2Intf == NULL)
    {
        amdlibSetErrMsg("Impossible to allocate memory for noise figures ");
        amdlibReleaseScienceData(scienceData);
        return amdlibFAILURE;
    }
    for (f = 0; f < nbFrames; f ++)
    {
        sigma2Intf[f] = (amdlibDOUBLE *)calloc(intfWidth * nbChannels, sizeof(amdlibDOUBLE));
        if (sigma2Intf[f] == NULL)
        {
            amdlibSetErrMsg("Impossible to allocate memory for noise figures ");
            amdlibReleaseScienceData(scienceData);
            return amdlibFAILURE;
        }
    }

    /* Allocate memory for badPixel map */
    scienceData->badPixelsPt = amdlibAlloc2DArrayUnsignedChar(intfWidth, nbChannels, errMsg);
     if (scienceData->badPixelsPt == NULL)
    {
        amdlibSetErrMsg("Could not allocate memory for bad pixel copy");
        return amdlibFAILURE;
    }
    scienceData->badPixels = scienceData->badPixelsPt[0];

    /* compute intf and sigma2 in one gesture */
    if (amdlibSumAndPackData(rawData, amdlibFALSE, amdlibFALSE, sumData,
                             amdlibINTERF_CHANNEL, intf,  sigma2Intf, errMsg) != amdlibSUCCESS)
    {
        amdlibReleaseScienceData(scienceData);
        return amdlibFAILURE;
    }

    /* Fill badPixel map Using the fact that sigma2Intf = 0 at bad Pixels */
    sigma2IntfPtr = amdlibWrap2DArrayDouble(sigma2Intf[0],intfWidth , nbChannels , errMsg);
    for (l = 0; l < nbChannels; l++)   
    {
        for (i=0; i < intfWidth; i++)   
        {
            if (sigma2IntfPtr[l][i] <= 0.0)
            {
                scienceData->badPixelsPt[l][i] = (unsigned char) 0;
            }
            else
            {
                scienceData->badPixelsPt[l][i] = (unsigned char) 1;
            }
        }
    }
    amdlibFree2DArrayDoubleWrapping(sigma2IntfPtr);
    /* For all photometric channels */
    for (photoChannel = 0; photoChannel < nbPhotoChannels; photoChannel++)
    {
        /* Allocate memory for photo and sigma2Photo for all frames */
        photo = (amdlibDOUBLE **)calloc(nbFrames, sizeof(amdlibDOUBLE *));
        sigma2Photo = (amdlibDOUBLE **)calloc(nbFrames, sizeof(amdlibDOUBLE *));
        if (photo == NULL)
        {
            amdlibSetErrMsg("Impossible to allocate memory for photometric "
                            "beam %d", photoChannel);
            return amdlibFAILURE;
        }
        if (sigma2Photo == NULL)
        {
            amdlibSetErrMsg("Impossible to allocate memory for photometric "
                            "error on beam %d", photoChannel);
            return amdlibFAILURE;
        }
        for (f = 0; f < nbFrames; f ++)
        {
            photo[f] = (amdlibDOUBLE *)calloc(nbChannels, sizeof(amdlibDOUBLE));
            if (photo[f] == NULL)
            {
                amdlibSetErrMsg("Impossible to allocate memory for photometric "
                                "beam %d", photoChannel);
                amdlibReleaseScienceData(scienceData);
                return amdlibFAILURE;
            }
            sigma2Photo[f] = (amdlibDOUBLE *)calloc(nbChannels, sizeof(amdlibDOUBLE));
            if (sigma2Photo[f] == NULL)
            {
                amdlibSetErrMsg("Impossible to allocate memory for photometric "
                                "error on beam %d", photoChannel);
                amdlibReleaseScienceData(scienceData);
                return amdlibFAILURE;
            }
        }
        
        /* Sum and pack photo and sigma2Photo for all frames */
        if (amdlibSumAndPackData(rawData, amdlibTRUE, amdlibFALSE, sumData, 
                                 photoChannel, photo, sigma2Photo, errMsg) != amdlibSUCCESS)
        {
            amdlibReleaseScienceData(scienceData);
            return amdlibFAILURE;
        }

        if (amdlibGetUserPref(amdlibGLOBAL_PHOTOMETRY).set==amdlibTRUE)
        {
            /* Test pour voir si la photometrie canal par canal a un sens... On ajoute tout...
             *  et ca marche */        
            for (f = 0; f < nbFrames; f ++)
            {
                int sumf=0, sumerrf=0;
                for (l = 0; l < nbChannels; l++)
                {
                    sumf+=photo[f][l];
                    sumerrf+=sigma2Photo[f][l];
                }
                for (l = 0; l < nbChannels; l++)
                {
                    photo[f][l]=sumf;
                    sigma2Photo[f][l]=sumf;
                }
            }
        }

       switch (photoChannel)
        {
            case amdlibPHOTO1_CHANNEL :
            {
                photo1 = photo;
                sigma2Photo1 = sigma2Photo;
                break;
            }
            case amdlibPHOTO2_CHANNEL :
            {
                photo2 = photo;
                sigma2Photo2 = sigma2Photo;
                break;
            }
            case amdlibPHOTO3_CHANNEL :
            {
                photo3 = photo;
                sigma2Photo3 = sigma2Photo;
                break;
            }
            default :
            {
                amdlibSetErrMsg("Reference to channel '%d' does not exist",
                                photoChannel);
                amdlibReleaseScienceData(scienceData);  
                return amdlibFAILURE;
            }
        }
    }

    /* Discard 'bad' channels in photometric and interferometric data, and then
     * discard 'bad' channels in the channel table. */
    for (f = 0; f < scienceData->nbFrames; f ++)
    {
        scienceData->frame[f].intf = intf[f];
        scienceData->frame[f].sigma2Intf = sigma2Intf[f];
        
        /* Interferometric data */
        if (amdlibDiscardBadChannelsIntf(scienceData->frame[f].intf, 
                                         scienceData->frame[f].sigma2Intf, 
                                         intfWidth,
                                         scienceData->channelNo,
                                         scienceData->nbChannels) == 
            amdlibFAILURE)
        {
            return amdlibFAILURE;
        }

        /* Photometric data */
        for (photoChannel = 0; photoChannel < nbPhotoChannels; photoChannel++)
        {
            switch (photoChannel)
            {
                case amdlibPHOTO1_CHANNEL :
                {
                    pPhoto = photo1[f];
                    pSigma2Photo = sigma2Photo1[f];
                    break;
                }
                case amdlibPHOTO2_CHANNEL :
                {
                    pPhoto = photo2[f];
                    pSigma2Photo = sigma2Photo2[f];
                    break;
                }
                case amdlibPHOTO3_CHANNEL :
                {
                    pPhoto = photo3[f];
                    pSigma2Photo = sigma2Photo3[f];
                    break;
                }
                default :
                {
                    amdlibSetErrMsg("Reference to channel '%d' does not exist",
                                    photoChannel);
                    amdlibReleaseScienceData(scienceData);  
                    return amdlibFAILURE;
                }
            }
            pShiftPhoto = (amdlibDOUBLE *)calloc(nbChannels, sizeof(amdlibDOUBLE));
            pShiftSigma2Photo = (amdlibDOUBLE *)calloc(nbChannels, sizeof(amdlibDOUBLE));
            pixelShift = -1 * waveData->photoOffset[photoChannel];
            if (amdlibShift(nbChannels, pPhoto, pixelShift, 
                            pShiftPhoto, errMsg) == amdlibFAILURE)
            {
                return amdlibFAILURE;
            }
            if (amdlibShift(nbChannels, pSigma2Photo, pixelShift, 
                            pShiftSigma2Photo, 
                            errMsg) == amdlibFAILURE)
            {
                return amdlibFAILURE;
            }
            if (amdlibDiscardBadChannelsPhoto(pShiftPhoto, pShiftSigma2Photo, 
                                              scienceData->channelNo,
                                              scienceData->nbChannels) == 
                amdlibFAILURE)
            {
                return amdlibFAILURE;
            }
            switch (photoChannel)
            {
                case 0 :
                {
                    scienceData->frame[f].photo1 = pShiftPhoto;
                    scienceData->frame[f].sigma2Photo1 = pShiftSigma2Photo;
                 break;
                }
                case 1 :
                {
                    scienceData->frame[f].photo2 = pShiftPhoto;
                    scienceData->frame[f].sigma2Photo2 = pShiftSigma2Photo;
                    break;
                }
                default :
                {
                    scienceData->frame[f].photo3 = pShiftPhoto;
                    scienceData->frame[f].sigma2Photo3 = pShiftSigma2Photo;
                    break;
                }  
            }
            free(pPhoto);
            free(pSigma2Photo);
        }
    }

    /* Channel table */
    if (amdlibDiscardBadChannelsNo(scienceData->channelNo,
                                   &scienceData->nbChannels) == amdlibFAILURE)
    {
        return amdlibFAILURE;
    }

    free(intf);
    free(sigma2Intf);
    free(photo1);
    free(photo2);
    free(photo3);
    free(sigma2Photo1);
    free(sigma2Photo2);
    free(sigma2Photo3);
    
    /* Calculate integratedPhoto and snrPhoto for each photometric beam */
    for (photoChannel = 0; photoChannel < nbPhotoChannels; photoChannel++)
    {
        for (f = 0; f < scienceData->nbFrames; f++)
        {
            switch (photoChannel)
            {
                case 0 :
                    {
                        pPhoto = scienceData->frame[f].photo1;
                        pSigma2Photo = scienceData->frame[f].sigma2Photo1;
                        break;
                    }
                case 1 :
                    {
                        pPhoto = scienceData->frame[f].photo2;
                        pSigma2Photo = scienceData->frame[f].sigma2Photo2;
                        break;
                    }
                default :
                    {
                        pPhoto = scienceData->frame[f].photo3;
                        pSigma2Photo = scienceData->frame[f].sigma2Photo3;
                        break;
                    }  
            }
            integratedPhoto = 0.;
            snrPhoto = 0;
            for (channel = 0; channel < scienceData->nbChannels; 
                 channel++)
            {
                integratedPhoto += pPhoto[channel];
                snrPhoto += pSigma2Photo[channel];
            }
             
            if (snrPhoto < 0.0)
            {
                amdlibLogWarning("Photometric channel %d, frame %d: "
                                 "%.2f", photoChannel, f, integratedPhoto); 
                amdlibLogWarning("Sigma² of photometric channel %d, frame %d is negative "
                                 "%.2f - set to zero.", photoChannel, f, snrPhoto); 
                snrPhoto = 0.0;
            }
            if (snrPhoto != 0.0) 
            {
                snrPhoto = integratedPhoto / sqrt(snrPhoto);
            }
            else
            {
                snrPhoto = 0.0;
            }
             
            switch (photoChannel)
            {
                case 0 :
                {
                    scienceData->frame[f].integratedPhoto1 = integratedPhoto;
                    scienceData->frame[f].snrPhoto1 = snrPhoto;
                    break;
                }
                case 1 :
                {
                    scienceData->frame[f].integratedPhoto2 = integratedPhoto;
                    scienceData->frame[f].snrPhoto2 = snrPhoto;
                    break;
                }
                default :
                {
                    scienceData->frame[f].integratedPhoto3 = integratedPhoto;
                    scienceData->frame[f].snrPhoto3 = snrPhoto;
                    break;
                }  
            }
        }
    }
    
    /**** Compute flux ratio */
    /* Compute total flux per channel */
    for (f = 0; f < scienceData->nbFrames; f++)
    {
        amdlibDOUBLE fluxPerChannel[amdlibNB_PHOTO_CHANNELS];
        amdlibDOUBLE totalFlux;
        amdlibDOUBLE minFlux;

        for (photoChannel = 0; photoChannel < nbPhotoChannels; photoChannel++)
        {
            fluxPerChannel[photoChannel] = 0.0;
        }
        fluxPerChannel[0] += scienceData->frame[f].integratedPhoto1;
        fluxPerChannel[1] += scienceData->frame[f].integratedPhoto2;
        if (nbPhotoChannels == 3)
        {
            fluxPerChannel[2] += scienceData->frame[f].integratedPhoto3;
        }

        /* If there is negative flux, shift all flux value to only get positive
         * values */
        minFlux = fluxPerChannel[0];
        for (photoChannel = 1; photoChannel < nbPhotoChannels; photoChannel++)
        {
            if (minFlux > fluxPerChannel[photoChannel])
            {
                minFlux = fluxPerChannel[photoChannel];
            }
        }
        if (minFlux < 0.0)
        {
            for (photoChannel = 0; 
                 photoChannel < nbPhotoChannels; 
                 photoChannel++)
            {
                fluxPerChannel[photoChannel] += minFlux;
            }
        }

        /* Compute total flux */ 
        totalFlux = 0.0;
        for (photoChannel = 0; photoChannel < nbPhotoChannels; photoChannel++)
        {
            totalFlux += fluxPerChannel[photoChannel];
        }

        /* Compute flux ratio */
        for (photoChannel = 0; photoChannel < nbPhotoChannels; photoChannel++)
        {
            if (totalFlux != 0)
            {
                scienceData->frame[f].fluxRatio[photoChannel] =
                    fluxPerChannel[photoChannel] / totalFlux * 100.0;
            }
            else
            {
                scienceData->frame[f].fluxRatio[photoChannel] = 
                    100.0 / nbPhotoChannels;
            }
        }

    }
    return amdlibSUCCESS;
}

/**
 * Discard bad channels in interferometric data; shift good channel at the
 * beginning of the table.
 *
 * @param intf array containing interferometric data
 * @param intfWidth width of the interferometric channel
 * @param channelNo array containing the channel number; number of bad channels
 * is -1
 * @param nbChannels number of spectral channels
 *
 * @return
 * always amdlibSUCCESS.
 */
amdlibCOMPL_STAT amdlibDiscardBadChannelsIntf(amdlibDOUBLE           *intf,
                                              amdlibDOUBLE           *sigma2Intf,
                                              int             intfWidth,
                                              int             *channelNo,
                                              int             nbChannels)
{
    int i1, i2, j, k;

    amdlibLogTrace("amdlibDiscardBadChannelsIntf()");
    
    i2 = 0;
    /* Scan all channels */
    for (i1 = 0; i1 < nbChannels; i1++)
    {
        /* If channel is bad */
        if (channelNo[i1] == -1)
        {
            /* Shift other channels */
            for (j = i2; j < nbChannels - 1; j++)
            {
                for (k = 0; k < intfWidth; k++)
                {
                    intf[j*intfWidth + k] = intf[(j+1)*intfWidth + k];
                    sigma2Intf[j*intfWidth + k] = sigma2Intf[(j+1)*intfWidth + k];
                }
            }
            /* Set the values of the last line to amdlibBLANKING_VALUE */
            for (k = 0; k < intfWidth; k++)
            {
                intf[(nbChannels-1)*intfWidth + k] = amdlibBLANKING_VALUE;
                sigma2Intf[(nbChannels-1)*intfWidth + k] = amdlibBLANKING_VALUE;
            }
        }
        else
        {
            i2++;
        }
    }
    return amdlibSUCCESS;
}

/**
 * Discard bad channels in photometric data; shift good channels at the
 * beginning of the table.
 *
 * @param photo array containing photometric data
 * @param sigma2Photo array containing noise of photometric data
 * @param channelNo array containing the channel number; number of bad channels
 * is -1
 * @param nbChannels number of spectral channels
 *
 * @return
 * always amdlibSUCCESS.
 */
amdlibCOMPL_STAT amdlibDiscardBadChannelsPhoto(amdlibDOUBLE           *photo,
                                               amdlibDOUBLE           *sigma2Photo,
                                               int             *channelNo,
                                               int             nbChannels)
{
    int i1, i2, j;

    amdlibLogTrace("amdlibDiscardBadChannelsPhoto()");

    i1 = 0;
    i2 = 0;
    /* Scan all channels */
    for (i1 = 0; i1 < nbChannels; i1++)
    {
        /* If channel is bad */
        if (channelNo[i1] == -1)
        {
            /* Shift other channels */
            for (j = i2; j < nbChannels - 1; j++)
            {
                photo[j] = photo[j+1];
                sigma2Photo[j] = sigma2Photo[j+1];
            }
            /* Set the values of the last line to amdlibBLANKING_VALUE */
            photo[nbChannels-1] = amdlibBLANKING_VALUE;
            sigma2Photo[nbChannels-1] = amdlibBLANKING_VALUE;
        }
        else
        {
            i2++;
        }
    }
    return amdlibSUCCESS;
}

/**
 * Discard bad channels in the channel number array; shift good channels at the
 * beginning of the table.
 *
 * @param channelNo array containing the channel number; number of bad channels
 * is -1
 * @param nbChannels number of spectral channels
 *
 * @return
 * always amdlibSUCCESS.
 */
amdlibCOMPL_STAT amdlibDiscardBadChannelsNo(int             *channelNo,
                                            int             *nbChannels)
{
    int i, j;
    int oldNbChannels;

    amdlibLogTrace("amdlibDiscardBadChannelsNo()");

    i = 0;
    oldNbChannels = *nbChannels;
    /* Scan all channels */
    while (i < *nbChannels)
    {
        /* If channel is bad */
        if (channelNo[i] == -1)
        {
            /* Shift other channels */
            for (j = i; j < oldNbChannels - 1; j++)
            {
                channelNo[j] = channelNo[j+1];
            }
            /* Set the values of the last channel to -1, and decrease the number
             * of channels */
            channelNo[oldNbChannels-1] = -1;
            (*nbChannels)--;
        }
        else
        {
            i++;
        }
    }

    return amdlibSUCCESS;
    
}


void amdlibDisplayScienceData(amdlibSCIENCE_DATA *scienceData)
{
    int i, j, f;
    
    printf("\n\n---------- results ----------\n");    
    printf("scienceData.nbFrames = %d\n", scienceData->nbFrames);
    printf("nbWlen = %d\n",scienceData->nbChannels);
    printf("1er canal ok (<=> startPixel 1ere row) = %d\n", 
           scienceData->channelNo[0]);
    printf("val photo1 :\n");
    for (i = 0; i < scienceData->nbChannels; i++)
    {
        printf ("i = %d, %f\n", i, scienceData->frame[0].sigma2Photo1[i]);
    }

    printf("val photo2 :\n");
    for (i = 0; i < scienceData->nbChannels; i++)
    {
        printf ("i = %d, %f\n", i, scienceData->frame[0].sigma2Photo2[i]);
    }

    if (scienceData->frame[0].photo3 != NULL)
    {
        printf("val photo3 :\n");
        for (i = 0; i < scienceData->nbChannels; i++)
        {
            printf ("i = %d, %f\n", i, scienceData->frame[0].sigma2Photo3[i]);
        }
    }



    printf("integrated photo et snr photo :\n");
    for (f = 0; f < scienceData->nbFrames; f++)
    {
        printf("frame : %d\n", f);
        printf("iP1 = %f, snrP1 = %f\n", 
               scienceData->frame[f].integratedPhoto1, 
               scienceData->frame[f].snrPhoto1);
        printf("iP2 = %f, snrP2 = %f\n", 
               scienceData->frame[f].integratedPhoto2,
               scienceData->frame[f].snrPhoto2);
        printf("iP3 = %f, snrP3 = %f\n", 
               scienceData->frame[f].integratedPhoto3, 
               scienceData->frame[f].snrPhoto3);
    }

    for (f=0; f < scienceData->nbFrames; f++)
    {
        printf ("Frame #%d :\n", f);
        printf ("\n+---------------+---------------+---------------+---------------|\n"); 
        printf("|               | Photo 1       | Photo 2       | Photo 3       |\n"); 
        printf ("+---------------+---------------+---------------+---------------|\n"); 
        printf("| Value         |");
        printf (" %13.1f |", scienceData->frame[f].integratedPhoto1);
        printf (" %13.1f |", scienceData->frame[f].integratedPhoto2);
        printf (" %13.1f |", scienceData->frame[f].integratedPhoto3);
        printf ("\n+---------------+---------------+---------------+---------------|\n"); 
        printf("| SNR           |");
        printf (" %13.1f |", scienceData->frame[f].snrPhoto1);
        printf (" %13.1f |", scienceData->frame[f].snrPhoto2);
        printf (" %13.1f |", scienceData->frame[f].snrPhoto3);
        printf ("\n+-------+-------+---------------+---------------+---------------|"); 
        printf("\n| Flux ratio    |");
        for (j=0; j < amdlibNB_PHOTO_CHANNELS; j++)
        {
            printf (" %13.1f |", scienceData->frame[f].fluxRatio[j]);
        }
        printf ("\n+---------------+---------------+---------------+---------------|\n"); 
        printf("\n");
    }

}

/*___oOo___*/
