/****************************************************************************/
/*                                                                          */
/*  buffer_mgr.c                                                            */
/*                                                                          */
/*  PURPOSE:    This file contains a set of fuctions for accessing image    */
/*              buffers.  All of these functions are centered around an     */
/*              in memory structure called the IA or "Image Array".         */ 
/*              This is simply a square array of 16-bit integers that       */
/*              holds an image.  Functions are provided that save this      */
/*              array to a FITS format file, copy the array to an image     */
/*              display screen device, Fill the array from the Mk4          */
/*              hardware RAM Buffer card, Fill the array with a test        */
/*              pattern and so on.  All image operations and movement       */
/*              in the server is done by calling functions in this file.    */
/*              If you dont see what you need add it here.                  */
/*                                                                          */
/****************************************************************************/


/* Used by RCS and ident */
char buffer_mgr_rcsid[] = 
    "$Id: buffer_mgr.c,v 1.5 1998/09/29 06:44:33 chris Exp chris $";

#ifndef VERS
#define VERS "TEST"
#endif
   
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>

#include "cdl.h"
#include "fitsio.h"
#include "ram_card.h"
#include "MLog.h"

#include "time_funct.h"
#include "parameters.h"

/* local function prototypes */
static void CheckFitsError(int status, char file[], int line);
int MallocIA(int NumCCDs, int X_sz, int Y_sz);
int GetIndex(int X, int Y);
int SetPixel(int CCD, int X, int Y, unsigned short int  Val);
int GetPixel(int CCD, int X, int Y, unsigned short int *Val);


/****************************************************************************/
/*                                                                          */
/* This is the image buffer.                                                */
/*                                                                          */
/* Our rule for allocating space is this:  Any function that writes to the  */
/* buffer must first allocate the space.  But before allocating if will     */
/* free the space first if any exists.  This way we handle variable size    */
/* reads.  At any time the buffer may be freed.  We used the following      */
/* convention:                                                              */
/*      1) IA[MAX_CCD] == NULL      The buffer has been freed               */
/*         IA[MAX_CCD] != NULL      The buffer has been allocated           */
/*                                                                          */
/* We always assume each CCD has the same number of pixels or if a          */
/* sub-raster is read out that the same subraster is readout of each chip   */
/*                                                                          */
/****************************************************************************/
#define MAX_CCD      2   /* Max number of CCDs in system    */
#define MAX_CCD_X 2048   /* Max image size in X direction   */
#define MAX_CCD_Y 2048   /* Max image size in Y direction   */

static                int   IA_x = 0;     /* current x size       */
static                int   IA_y = 0;     /* current x size       */

static unsigned short int  *IA[MAX_CCD];  /* An Array of pointers */
/******************** end image buffer **************************************/

/* The following "main" program is a test driver */
#if 0
#include "buffer_mgr.h"
void main(void)
{
     ml_setup(ML_DEBUG, NULL, NULL);
     FITS2IA();
     IA2FITS();
     IA2CDL();
}
#endif

/****************************************************************************/
/*                                                                          */
/*  RBC2IA      RAM Buffer Card to Image Array                              */
/*                                                                          */
/*  PURPOSE:    Copies data from the Mk IV hardware RAM Buffer card to a    */
/*              set of IAs (Image Arrays.)  Each CCD goes to its own IA.    */
/*                                                                          */
/*  ALGORITHM:                                                              */
/*      1) Open the buffer device.  Handle any errors.                      */
/*      2) Loop until end of file on buffer device                          */
/*          2.1) Read a block.                                              */
/*          2.2) "de-interlace" the bytes.  put CCD0 and CCD1 each into     */
/*               it's own plane.                                            */
/*      3) Set IA_x and IA_y depending on number of bytes transfered        */ 
/*                                                                          */
/*  RETURNS:    0  = All OK.                                                */
/*              -1 = Error.                                                 */
/*                                                                          */
/****************************************************************************/
int RBC2IA(void)
{
    int     res;
    int     CCD_Bytes;
    
    ml_print(ML_DEBUG, ML_INFORMATION, "RBC2IA called\n");
    
    
    /* Set the array bounds and figure out how much data to read */
    IA_x = gbl_CCD_Xsize;
    IA_y = gbl_CCD_Ysize;
    CCD_Bytes = IA_x * IA_y;
    
    
    /* Open the device for read */
    res = ram_open(O_RDONLY);
    if(res<0)
    {
        /* Open failed */
        ml_printf(ML_MANDATORY, ML_ERROR,
            "ERROR: open for read failed for RAM Buffer.\n");
        ml_printf(ML_MANDATORY, ML_ERROR,
            "    detected in %s:%d\n", __FILE__, __LINE__);
        return -1;
    }
    
    
    /* Tell the driver how many bytes are in the buffer */
    res = ram_set_buffer_full(CCD_Bytes);
    if(res<0)
    {
        /* set failed */
        ml_printf(ML_MANDATORY, ML_ERROR,
            "ERROR: set buffer failed for RAM Buffer.\n");
        ml_printf(ML_MANDATORY, ML_ERROR,
            "    detected in %s:%d\n", __FILE__, __LINE__);
        return -1;
    }
    
    
    /* Get a block of system RAM to hold the image */
    if(-1 == MallocIA(gbl_number_ccds, IA_x, IA_y))
    {
        /* Out of menory */
        ml_printf(ML_MANDATORY, ML_ERROR,
            "INTERNAL ERROR: out of memory.  can not read RAM Buffer.\n");
        ml_printf(ML_MANDATORY, ML_ERROR,
            "    detected in %s:%d\n", __FILE__, __LINE__);
        return -1;
    }
    
    /********************************************************************/
    /*          AT THIS TIME WE ONLY HANDLE SINGLE CCD SYSTEMS          */
    /********************************************************************/
    if(gbl_number_ccds != 1)
    {
        ml_printf(ML_MANDATORY, ML_ERROR,
            "ERROR: driver can only handle one CCD at present\n");
        ml_printf(ML_MANDATORY, ML_ERROR,
            "    detected in %s:%d\n", __FILE__, __LINE__);
        return -1;
    
    }
    
    /* Read the data.  Let the kernel level driver break this up.   */
    /* The ram_read_block function will d the read as many times    */
    /* as it takes to get the data.                                 */
    res = ram_read_block( (char *)IA[0], &CCD_Bytes);
    if(res<0)
    {
        /* read failed */
        ml_printf(ML_MANDATORY, ML_ERROR,
            "ERROR: read failed for RAM Buffer.\n");
        ml_printf(ML_MANDATORY, ML_ERROR,
            "    detected in %s:%d\n", __FILE__, __LINE__);
        return -1;
    }
    
    
    /* Close the device */
    res = ram_close();
    if(res<0)
    {
        /* close failed */
        ml_printf(ML_MANDATORY, ML_ERROR,
            "ERROR: close failed for RAM Buffer.\n");
        ml_printf(ML_MANDATORY, ML_ERROR,
            "    detected in %s:%d\n", __FILE__, __LINE__);
        return -1;
    }
    
    
    return 0;
}

/****************************************************************************/
/*                                                                          */
/*  IAclear     Clear Array                                                 */
/*                                                                          */
/*  PURPOSE:    Clears out the Image Array                                  */
/*                                                                          */
/*  RETURNS:    0  = All OK.                                                */
/*              -1 = Error.                                                 */
/*                                                                          */
/****************************************************************************/
int IAclear(void)
{
    int ccd;
    
    
    ml_print(ML_DEBUG, ML_INFORMATION, "IAclear called\n");
    
    for(ccd=0; ccd<gbl_number_ccds; ccd++)
    {
        if(IA[ccd])
        {
            free(IA[ccd]);
            IA[ccd] = NULL;
        }
    }
    
    IA_x = 0;
    IA_y = 0;
    
    return 0;
}

/****************************************************************************/
/*                                                                          */
/*  IA2FITS     Image Array to FITS file                                    */
/*                                                                          */
/*  PURPOSE:    Copies data from an IA (Image Array) to a FITS file.  Will  */
/*              also figure out a filename based on convention and will     */
/*              compute the FITS keywords                                   */
/*                                                                          */
/*  RETURNS:    0  = All OK.                                                */
/*              -1 = Error.                                                 */
/*                                                                          */
/****************************************************************************/
int IA2FITS(void)
{
    char        filename[FLEN_FILENAME];
    fitsfile   *fptr;
    int         fpixel;
    int         npixels;
    int         status;
    int         ccd_no;
    int         f_res;
    int         bitpix;
    int         naxis;
    long        naxes[3];
    double      JulianDate;
    double      TJD;
    char        tmp_val[80];
    char        comment[80];
    int         ccd;
    float       cfitsio_version;
    
    
    ml_print(ML_DEBUG, ML_INFORMATION, "IA2FITS called\n");
    
    /****************************************************************/
    /* Check if there is an image in the buffer                     */
    /****************************************************************/
    if (IA_x<1 || IA_y<1)
    {
        ml_printf(ML_MANDATORY, ML_ERROR,
            "ERROR: Attempt to write an empty image buffer to FITS file\n");
            
        return -1;
    }
    
    
    /****************************************************************/
    /* Process each CCD in turn.                                    */
    /****************************************************************/
    for(ccd=0; ccd<gbl_number_ccds; ccd++)
    {

        /************************************************************/
        /* Double check if there is an image in the buffer          */
        /************************************************************/
        if(!IA[ccd])
        {
            ml_printf(ML_MANDATORY, ML_ERROR,
                "ERROR: Attempt to write a NULL image buffer to FITS file\n");

            return -1;
        }
    
    
        /************************************************************/
        /* create the file                                          */
        /************************************************************/
        /* Create and open the FITS file.  If there was a problem   */
        /* try a diferent filename.  The filename is based on the   */
        /* date and time so we may fail if we try to open files     */
        /* to frequently and get a name collision.  On the second   */
        /* try we use a four digit time of day code.                */

        JulianDate = getJD();
        TJD = JulianDate - 2450000.0;
        sprintf (filename, "%s%1dR_%08.3f", gbl_sitename, ccd, TJD);

        status = 0;
        f_res = fits_create_file(&fptr, filename, &status);

        if (status == FILE_NOT_CREATED)
        {
            ml_printf(ML_VERBOSE, ML_ERROR,
                      "Could not create file %s ...", filename);

            sprintf (filename, "%s%1dR_%09.6f", gbl_sitename, ccd, TJD);

            ml_printf(ML_VERBOSE, ML_ERROR,
                      " trying %s\n", filename);

            status = 0;
            fits_create_file(&fptr, filename, &status); 

            if(f_res)
            {
                CheckFitsError(status, __FILE__, __LINE__);
                return -1;
            }
        }
        else if(f_res)
        {
            CheckFitsError(status, __FILE__, __LINE__);
            return -1;
        }

        /************************************************************/
        /* create the image and set the date fields                 */
        /************************************************************/
        bitpix = 16;
        naxis  = 2;
        naxes[0] = IA_x;
        naxes[1] = IA_y;

        status = 0;
        f_res = fits_create_img(fptr, bitpix, naxis, naxes, &status);
        if(f_res)
        {
            CheckFitsError(status, __FILE__, __LINE__);
            return -1;
        }

        status = 0;   
        f_res = fits_write_date(fptr, &status);
        if(f_res)
        {
            CheckFitsError(status, __FILE__, __LINE__);
            return -1;
        }

        status = 0;
        f_res = fits_read_key (fptr, TSTRING, "DATE", tmp_val, 
                comment, &status);
        if(f_res)
        {
            CheckFitsError(status, __FILE__, __LINE__);
            return -1;
        }

        status = 0;
        f_res = fits_write_key(fptr, TSTRING, "DATE-OBS", tmp_val,
            " ", &status);
        if(f_res)
        {
            CheckFitsError(status, __FILE__, __LINE__);
            return -1;
        }



        /****************************************************************/
        /* Observing site info                                          */
        /****************************************************************/
        status = 0;
        f_res = fits_write_key(fptr, TSTRING, "ORIGIN", gbl_observatory,
            " ", &status);
        if(f_res)
        {
            CheckFitsError(status, __FILE__, __LINE__);
            return -1;
        }    

        status = 0;
        f_res = fits_write_key(fptr, TSTRING, "TELESCOP", "TASS_Mk4",
            " ", &status);
        if(f_res)
        {
            CheckFitsError(status, __FILE__, __LINE__);
            return -1;
        }    

        sprintf(tmp_val, "CCD%d", 0);
        status = 0;
        f_res = fits_write_key(fptr, TSTRING, "INSTRUME", tmp_val,
            " ", &status);
        if(f_res)
        {
            CheckFitsError(status, __FILE__, __LINE__);
            return -1;
        }    

        status = 0;
        f_res = fits_write_key(fptr, TSTRING, "OBSERVER", gbl_observer,
            " ", &status);
        if(f_res)
        {
            CheckFitsError(status, __FILE__, __LINE__);
            return -1;
        }    

        status = 0;
        f_res = fits_write_key(fptr, TDOUBLE, "LATITUDE", &gbl_latitude,
            " ", &status);
        if(f_res)
        {
            CheckFitsError(status, __FILE__, __LINE__);
            return -1;
        }    

        status = 0;
        f_res = fits_write_key(fptr, TDOUBLE, "LONGITUD", &gbl_longitude,
            " ", &status);
        if(f_res)
        {
            CheckFitsError(status, __FILE__, __LINE__);
            return -1;
        }    

        status = 0;
        f_res = fits_write_key(fptr, TDOUBLE, "ALTITUDE", &gbl_altitude,
            " ", &status);
        if(f_res)
        {
            CheckFitsError(status, __FILE__, __LINE__);
            return -1;
        }    



        /************************************************************/
        /* Add some comments                                        */
        /************************************************************/

        /* Mk4d Version */
        snprintf(comment, sizeof(comment), "Driver version:         %s",
                 VERS);
        status = 0;
        f_res = fits_write_comment(fptr, comment, &status);
        if(f_res)
        {
            CheckFitsError(status, __FILE__, __LINE__);
            return -1;
        }

        /* Mk4d compile data & time */
        snprintf(comment, sizeof(comment), "Driver compiled on:     %s %s",
                __DATE__, __TIME__);
        status = 0;
        f_res = fits_write_comment(fptr, comment, &status);
        if(f_res)
        {
            CheckFitsError(status, __FILE__, __LINE__);
            return -1;
        }

        /* Hostname */
        gethostname(tmp_val, sizeof(tmp_val));
        snprintf(comment, sizeof(comment), "Driver run on host:     %s",
                 tmp_val);
        status = 0;
        f_res = fits_write_comment(fptr, comment, &status);
        if(f_res)
        {
            CheckFitsError(status, __FILE__, __LINE__);
            return -1;
        }

        /* Username */
        snprintf(comment, sizeof(comment), "Driver run by user:     %s",
                 cuserid(NULL));
        status = 0;
        f_res = fits_write_comment(fptr, comment, &status);
        if(f_res)
        {
            CheckFitsError(status, __FILE__, __LINE__);
            return -1;
        }

        /* CFITSIO version */
        fits_get_version(&cfitsio_version);
        snprintf(comment, sizeof(comment), "CFITSIO version:        %f",
                 cfitsio_version);
        status = 0;
        f_res = fits_write_comment(fptr, comment, &status);
        if(f_res)
        {
            CheckFitsError(status, __FILE__, __LINE__);
            return -1;
        }



        /************************************************************/
        /* write out the pixels                                     */
        /************************************************************/
        ccd_no  = 0;
        fpixel  = 1;
        npixels = naxes[0] * naxes[1];

        status = 0;
        f_res = fits_write_img(fptr, TSHORT, fpixel, npixels,
                IA[ccd_no], &status);
        if(f_res)
        {
            CheckFitsError(status, __FILE__, __LINE__);
            return -1;
        }


        status = 0;
        f_res = fits_write_chksum(fptr,  &status);
        if(f_res)
        {
            CheckFitsError(status, __FILE__, __LINE__);
            return -1;
        }

        /************************************************************/
        /* close the file                                           */
        /************************************************************/
        ml_printf(ML_DEBUG, ML_INFORMATION, "  closing file\n");
        f_res = fits_close_file(fptr, &status);
        if(f_res)
        {
            CheckFitsError(status, __FILE__, __LINE__);
            return -1;
        }

    }
    return 0;
}

/****************************************************************************/
/*                                                                          */
/*  IA2JPG      Image Array to JPG file                                     */
/*                                                                          */
/*  PURPOSE:    Copies data from an IA (Image Array) to a JPG file.         */
/*              uses the same filename convention as IA2FITS except for     */
/*              the extension.  This function will be usfull to someone     */
/*              who wants to put images on a web site in real-time.         */
/*                                                                          */
/*  RETURNS:    0  = All OK.                                                */
/*              -1 = Error.                                                 */
/*                                                                          */
/****************************************************************************/
int IA2JPG(void)
{
    ml_print(ML_DEBUG, ML_INFORMATION, "IA2JPG called\n");
    
    ml_print(ML_MANDATORY, ML_INFORMATION, 
        "WARNING: IA2JPG (Image Array to JPG file) not implemented.\n"); 
    return 0;
}

/****************************************************************************/
/*                                                                          */
/*  FITS2IA     FITS file to Image Array                                    */
/*                                                                          */
/*  PURPOSE:    Copies data from a FITS file to an IA.  Used for testing    */
/*              We make a shortcut here.  We will read the file only once   */
/*              then if there are >1 CCDs in the system copy the buffer     */
/*                                                                          */
/*              Like all functions that fill the buffer, this function will */
/*              also allocate the space and set IA_x and IA_y to the        */
/*              size of the array.                                          */
/*                                                                          */
/*  RETURNS:    0  = All OK.                                                */
/*              -1 = Error.                                                 */
/*                                                                          */
/****************************************************************************/
int FITS2IA(void)
{
    fitsfile   *fptr;
    int         fpixel;
    int         npixels;
    int         status;
    int         anynul;
    
    int         f_res;
    int         simple;
    int         bitpix;
    int         naxis;
    long        naxes[3];
    long        pcount;
    long        gcount;
    int         extend;
    int         ccd;

    ml_print(ML_DEBUG, ML_INFORMATION, "FITS2IA called\n");
    
    /****************************************************************/
    /* Open the file                                                */
    /****************************************************************/
    ml_printf(ML_DEBUG, ML_INFORMATION, "  opening file %s\n", gbl_fits_testimage);
    
    status = 0;
    f_res = fits_open_file(&fptr, gbl_fits_testimage, READONLY, &status);
    if(f_res)
    {
        CheckFitsError(status, __FILE__, __LINE__);
        return -1;
    }
    
    /****************************************************************/
    /* Read a few keywords to check if this is n aceptable file     */
    /****************************************************************/
    ml_printf(ML_DEBUG, ML_INFORMATION, "  reading header\n");
    
    status = 0;
    f_res = fits_read_imghdr(fptr, 2, 
            &simple, &bitpix, &naxis, naxes, &pcount, &gcount, &extend,
            &status);
    if(f_res)
    {
        CheckFitsError(status, __FILE__, __LINE__);
        return -1;
    }
            
    ml_printf(ML_DEBUG, ML_INFORMATION,
        "  simple: %d\n", simple);
    ml_printf(ML_DEBUG, ML_INFORMATION,
        "  bitpix: %d\n", bitpix);
    ml_printf(ML_DEBUG, ML_INFORMATION,
        "  naxis:  %d\n", naxis);
    ml_printf(ML_DEBUG, ML_INFORMATION,
        "  naxes:  %d %d\n", naxes[0], naxes[1]);
    ml_printf(ML_DEBUG, ML_INFORMATION,
        "  extend: %d\n", extend);
        
    /****************************************************************/
    /* Watch carful folks!   We must first get some memory in which */
    /* to store the image data.  We will only read the file once.   */
    /* if there are more CCDs than one in the system we do a binary */
    /* copy later.  But we always allocate memory for _all_ CCDs    */
    /* at this time.                                                */
    /****************************************************************/
    if(-1 == MallocIA(gbl_number_ccds, naxes[0], naxes[1]))
    {
        /* Out of menory */
        ml_printf(ML_MANDATORY, ML_ERROR,
            "INTERNAL ERROR: out of memory.  can not read RAM Buffer.\n");
        ml_printf(ML_MANDATORY, ML_ERROR,
            "    detected in %s:%d\n", __FILE__, __LINE__);
        return -1;
    }
    
    /****************************************************************/
    /* Now read in the image array                                  */
    /****************************************************************/
    ml_printf(ML_DEBUG, ML_INFORMATION, "  reading image array\n");
    
    fpixel  = 1;
    npixels = naxes[0] * naxes[1];
    anynul  = 0;
    status  = 0;
    f_res = fits_read_img(fptr, TUSHORT, fpixel, npixels, NULL, IA[0],
            &anynul, &status);
    if(f_res)
    {
        CheckFitsError(status, __FILE__, __LINE__);
        return -1;
    }
    
    
    /****************************************************************/
    /* close the file                                               */
    /****************************************************************/
    ml_printf(ML_DEBUG, ML_INFORMATION, "  closing file\n");
    f_res = fits_close_file(fptr, &status);
    if(f_res)
    {
        CheckFitsError(status, __FILE__, __LINE__);
        return -1;
    }
    
    /****************************************************************/
    /* Copy the data if >1 CCD in system                            */
    /* Note that the following loop may not execute even once.      */
    /****************************************************************/
    for(ccd=1; ccd<gbl_number_ccds; ccd++)
    {
        /* do a binary copy */
        memcpy(IA[ccd], IA[0], IA_x * IA_y * sizeof(short int)); 
    }
    
    return 0;
}

/****************************************************************************/
/*                                                                          */
/*  TEST2IA     Test Patern to Image Array                                  */
/*                                                                          */
/*  PURPOSE:    Write a test patern to an IA.  Used for testing             */
/*                                                                          */
/*  RETURNS:    0  = All OK.                                                */
/*              -1 = Error.                                                 */
/*                                                                          */
/****************************************************************************/
int TEST2IA(void)
{
    int i;
    int j;
    int ccd_no;
    
    
    ml_print(ML_DEBUG, ML_INFORMATION, "TEST2IA called\n");
    
    /********************************************************/
    /* We must first get some memory in which the pattern.  */
    /********************************************************/
    if(-1 == MallocIA(gbl_number_ccds, MAX_CCD_X, MAX_CCD_Y))
    {
        /* Out of menory */
        ml_printf(ML_MANDATORY, ML_ERROR,
            "INTERNAL ERROR: out of memory.  cannot pattern fill buffer.\n");
        ml_printf(ML_MANDATORY, ML_ERROR,
            "    detected in %s:%d\n", __FILE__, __LINE__);
        return -1;
    }
    
    
    for (ccd_no=0; ccd_no < gbl_number_ccds; ccd_no++)
    {
        for (j=0; j < MAX_CCD_Y; j++)
        {
            for (i=0; i < MAX_CCD_X; i++)
            {
                SetPixel(ccd_no, i, j, i + j);
            }
        }
    } 
    
    
    return 0;
}

/****************************************************************************/
/*                                                                          */
/*  IA2CDL      Image Array to CDL                                          */
/*                                                                          */
/*  PURPOSE:    Uses the CDL (Common Display Library) to write the content  */
/*              of an IA to a display server such as ximtool or SAOimage.   */
/*              If there is more then one CCD in the system, the image      */
/*              from each will be written to a diferent frame               */
/*                                                                          */
/*  ARGUMENT:   ServerAddress                                               */
/*                  Pointer to an address string.  See the CDL reference    */
/*                  guide for the format.  If NULL or blank a default       */
/*                  server address is used.                                 */
/*                                                                          */
/*  RETURNS:    0  = All OK.                                                */
/*              -1 = Error.                                                 */
/*                                                                          */
/****************************************************************************/
int IA2CDL(const char *ServerAddress)
{
    CDLPtr  cdl;
    int     ccd;
    int     nx;
    int     ny;
    int     bitpix;
    int     frame;
    int     fbconfig;
    int     zscale;
    char    server[256];
    
    
    ml_print(ML_DEBUG, ML_INFORMATION, "IA2CDL called\n");
    
    /****************************************************************/
    /* Check if there is an image in the buffer                     */
    /****************************************************************/
    if (IA_x<1 || IA_y<1)
    {
        ml_printf(ML_MANDATORY, ML_ERROR,
            "ERROR: Attempt to display an empty image buffer\n");
            
        return -1;
    }
    
    /****************************************************************/
    /* Get an address string either use agument or default          */
    /****************************************************************/
    if(!ServerAddress)
    {
        strncpy(server, gbl_display_server, sizeof(server));
    }
    else if (0==strlen(ServerAddress))
    {
        strncpy(server, gbl_display_server, sizeof(server));
    }
    else
    {
        strncpy(server, ServerAddress, sizeof(server));
    }
    
    
    /* Connect to image server */
    if ( (cdl = cdl_open (gbl_display_server)) == NULL )
    {
        /* error opening connection to display server */
        ml_printf(ML_MANDATORY, ML_ERROR,
            "ERROR: error opening display server <%s>\n", server);
        
        return -1;
    }
   
    for(ccd=0; ccd<gbl_number_ccds; ccd++)
    {
        /****************************************************************/
        /* Double check if there is an image in the buffer              */
        /****************************************************************/
        if(!IA[ccd])
        {
            ml_printf(ML_MANDATORY, ML_ERROR,
                "ERROR: Attempt to display a NULL image buffer.\n");

            return -1;
        }
    
        nx          = IA_x;
        ny          = IA_y;
        bitpix      = 16;
        frame       = ccd;
        fbconfig    = FB_AUTO;
        zscale      = 1;

        cdl_displayPix (cdl, IA[ccd], nx, ny, bitpix, frame, fbconfig, zscale);
    }
    
    cdl_close(cdl);
    return 0;
}

/****************************************************************************/
/******************* NON-PUBLIC FUNTIONS FOLLOW  ****************************/
/****************************************************************************/

void CheckFitsError(int status, char file[], int line)
{
    char    err_text[256];
    int     more;
    
    
    if(status)
    {
        fits_get_errstatus(status, err_text);
        ml_printf(ML_MANDATORY, ML_ERROR,
            "ERROR: %d -- %s\n", status, err_text);
         
        more = fits_read_errmsg(err_text);  
        while(more)
        {
            ml_printf(ML_MANDATORY, ML_ERROR,
            "       %s\n", err_text);
            more = fits_read_errmsg(err_text);
        }
        ml_printf(ML_MANDATORY, ML_ERROR,
            "       detected in %s:%d\n", file, line);
    }
    return;
}

int MallocIA(int NumCCDs, int X_sz, int Y_sz)
{
    int ccd;
    int i;
    
    /* check request for resonableness */
    if( (X_sz < 1)         ||
        (X_sz > MAX_CCD_X) ||
        (Y_sz < 1)         ||
        (Y_sz > MAX_CCD_Y) )
    {
        /* parameters out of range return null and log error */
        ml_printf(ML_MANDATORY, ML_ERROR,
            "INTERNAL ERROR: parameters to MallocIA out of range\n");
        ml_printf(ML_MANDATORY, ML_ERROR,
            "    X_sz: %d, Y_sz: %d\n", X_sz, Y_sz);
        ml_printf(ML_MANDATORY, ML_ERROR,
            "    detected in %s:%d\n", __FILE__, __LINE__);
            
        return -1;
    }
    
    /* Allocat space for each CCD */
    for(ccd=0; ccd<NumCCDs; ccd++)
    {
        if(IA[ccd])
        {
            /* Just in case we forgot to free a buffer */
            free(IA[ccd]);
        }
        IA[ccd] =(unsigned short int *)
                 calloc(X_sz * Y_sz, sizeof(unsigned short int));
    
        /* Did it work?  If not free whatever we got and return error */
        if(!IA[ccd])
        {
            ml_printf(ML_MANDATORY, ML_ERROR,
                "INTERNAL ERROR: unable to allocat buffer.\n");
            ml_printf(ML_MANDATORY, ML_ERROR,
                "    detected in %s:%d\n", __FILE__, __LINE__);

            /* free any buffers we did get */   
            if( (ccd > 0) && (NULL == IA[ccd]) )
            {
                for(i=ccd-1; i>=0; i--)
                {
                    free(IA[ccd]);
                } 
            }
        
            /* Send and error code back to caller */
            IA_x = 0;
            IA_y = 0;
            return -1;
        }
    }
    
    /* It worked */
    IA_x = X_sz;
    IA_y = Y_sz;
    return 0;
}

#define CCD_BOUNDS_CHECK

int GetIndex(int X, int Y)
{    
    int index;

#ifdef CCD_BOUNDS_CHECK    
    if( (X < 1) || (X > IA_x) ||
        (Y < 1) || (Y > IA_y) )
    {
        /* parameters out of range return null and log error */
        ml_printf(ML_MANDATORY, ML_ERROR,
            "INTERNAL ERROR: parameters to GetIndex out of range\n");
        ml_printf(ML_MANDATORY, ML_ERROR,
            "    X: %d, Y: %d\n", X, Y);
        ml_printf(ML_MANDATORY, ML_ERROR,
            "    detected in %s:%d\n", __FILE__, __LINE__);
            
        return 0;
    }
#endif
    
    index    = (X * IA_x) + Y;
    return index; 
}


int SetPixel(int CCD, int X, int Y, unsigned short int Val)
{
    IA[CCD][GetIndex(X, Y)] = Val;
    return 0; 
}

int GetPixel(int CCD, int X, int Y, unsigned short int *Val)
{
    *Val = IA[CCD][GetIndex(X, Y)];
    return 0; 
}
