/* -------------------------------------------------------------------------- */
/* -                   Astronomical CCD Camera Control                      - */
/* -                         Motif User Interface                           - */
/* -                         Standalone Operation                           - */
/* -------------------------------------------------------------------------- */
/*                                                                            */
/* Copyright (c) 2004-2013 John Kielkopf                                      */
/* kielkopf@louisville.edu                                                    */
/*                                                                            */
/* This file is part of XmCCD.                                                */
/*                                                                            */
/* Distributed under the terms of the General Public License (see LICENSE)    */
/*                                                                            */
/* Date: September 3, 2013                                                    */
/* Version: 4.2                                                               */

  

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <sys/types.h>

#include <Xm/Xm.h>
#include <Xm/Form.h>
#include <Xm/FileSB.h>
#include <Xm/ToggleB.h>
#include <Xm/PushB.h>
#include <Xm/Label.h>
#include <Xm/RowColumn.h> 
#include <Xm/CascadeB.h>
#include <Xm/MessageB.h>
#include <Xm/Separator.h>
#include <Xm/SelectioB.h>
#include <Xm/Text.h>
#include <Xm/List.h>


#include <xpa.h>
#include "xmccd1.h"
#include "protocol.h"


/* Files */

FILE *fp_config;                       /* Configuration file pointer */
static char *configfile;               /* Configuration file name */
void read_config(void);
 
  
/* User interface */

XtAppContext context;
XmStringCharSet char_set=XmSTRING_DEFAULT_CHARSET;

Widget menu_bar;

Widget file_menu;
Widget file_script_item;
Widget file_config_item;
Widget file_exit_item;

Widget set_menu;
Widget set_image_exposure_item;
Widget set_image_number_item;
Widget set_frame_count_multi_item;
Widget set_region_item;
Widget set_ccd_temperature_item;

Widget fits_menu;
Widget fits_name_item;
Widget fits_telescope_item;
Widget fits_instrument_item;
Widget fits_target_item;

Widget filter_menu;
Widget filter_item[10];

Widget mode_menu;
Widget mode_single_item;
Widget mode_focus_item;
Widget mode_multi_item;

Widget type_menu;
Widget type_light_item;
Widget type_dark_item;
Widget type_bias_item;
Widget type_flat_item;

Widget processing_menu;
Widget processing_scripting_toggle;

Widget toplevel;

Widget file_script_dialog;
Widget file_config_dialog;
Widget set_image_exposure_dialog;
Widget set_image_number_dialog;
Widget set_frame_count_multi_dialog;
Widget set_region_dialog;
Widget set_ccd_temperature_dialog;
Widget fits_name_dialog;
Widget fits_target_dialog;
Widget fits_telescope_dialog;
Widget fits_instrument_dialog;

Widget button[3][3];
Widget command[3];
Widget label_info[6];
Widget show_info[6];
Widget form;
Widget sep_btns;
Widget sep_status;

Widget make_menu();              /* creates a menu on the menu bar */
Widget make_menu_item();         /* adds an item into the menu */
Widget make_menu_item_toggle();  /* adds a toggle button item into the menu */

/* labels for the buttons top to bottom then left to right */

static char btn_name[][6] = {
  "NoOp", 
  "East",
  "NoOp",
  "North",
  "NoOp",
  "South",
  "NoOp",
  "West",
  "NoOp"
};

static char cmd_name[][30] = {
  "Expose",
  "Interrupt",
  "Region"
};


static char infotext[][24] = {
  "Region",
  "Exposure",
  "Temperature",
  "File name",
  "Filter",
  "Status"
};

static char infodata[][24] = {
  "-",
  "-",
  "-",
  "-",
  "-",
  "Camera not ready"
};


/* Array of dynamic labels for the filters */ 

static char filter_label[10][32] = {
  " (1)              ",
  " (2)              ",
  " (3)              ",
  " (4)              ",
  " (5)              ",
  " (6)              ",  
  " (7)              ",  
  " (8)              ",
  " (9)              ", 
  "(10)              "
 };


/* Array of default labels for the filters */ 

static char default_filter_label[10][32] = {
  " (1)              ",
  " (2)              ",
  " (3)              ",
  " (4)              ",
  " (5)              ",
  " (6)              ",  
  " (7)              ",  
  " (8)              ",
  " (9)              ", 
  "(10)              "
 };
 

void create_menus(Widget menu_bar);      /* Create all menus            */
void menuCB();                           /* All pull-down menus         */
void file_script_dialogCB();             /* Get script file name        */
void file_config_dialogCB();             /* Configuration file dialog   */

void set_image_exposure_dialogCB();      /* Exposure time dialog        */
void set_image_number_dialogCB();        /* Image number dialog         */
void set_frame_count_multi_dialogCB();   /* Multi frame count dialog    */
void set_region_dialogCB();              /* Region dialog               */
void set_ccd_temperature_dialogCB();     /* Temperature dialog          */
void fits_name_dialogCB();               /* Set base name dialog        */
void fits_target_dialogCB();             /* Set target dialog           */
void fits_telescope_dialogCB();          /* Set telescope dialog        */
void fits_instrument_dialogCB();         /* Set instrument dialog       */

void buttonCB();                         /* The paddle buttons          */
void commandCB();                        /* Command buttons             */


/* Manage the camera */

void init(void);
void camera(void);


/* Parameter io */

void input_image_exposure();              /* Image exposure time     */
void input_image_number();                /* Image number            */
void input_frame_count_multi();           /* Multi frame count       */
void input_region();                      /* Image region            */
void input_ccd_temperature();             /* CCD temperature         */
void input_filter();                      /* Filter                  */


/* Parameter reporting */

void set_status_buffer(void);
void update_exposure_display();     /* Display image exposure time  */
void update_region_display();       /* Display image region         */
void update_temperature_display();  /* Display temperature control  */
void update_file_name_display();    /* Display last file name       */
void update_filter_display();       /* Display filter number        */
void update_filter_menu(int i);     /* Update drop down menu entry  */
void update_status_display();       /* Display status buffer string */
void update_all_displays();         /* Display all buffers          */
void save_ccd_temperature();        /* Write ccd status file        */


/* Interface with ds9 */

void start_ds9();                    /* Initiate ds9 display              */
void update_ds9_image(char *name);   /* XPA to ds9:  read file name       */
int get_ds9_region();                /* XPA from ds9: send me the region  */


/* Buffers */

char buf[256];                   /* General purpose character buffer */
char status_buf[24];             /* Short buffer for status string */


/* Internal flags and counters */

int  diagnostics = DIAGNOSTICS;
int  acquire = SINGLE;
int  frame = LIGHT;
int  phase = 0;
int  sequence = 0;
int  frame_count = 1;
int  frame_count_max = FRAMECOUNTMULTI;
int  frame_count_multi = FRAMECOUNTMULTI;
int  archive = FALSE;
int  image_number = 1;
int  startup = FALSE;
int  expose_menu = TRUE;
int  scripting = FALSE;

char base_name[64]; 
char file_name[64];
char filter_name[24];

/* Fits header strings */

double fits_exposure;
char fits_date[64];
char fits_type[64];
char fits_target[64];
char fits_instrument[64];
double fits_temperature;
char fits_filter[64];
char fits_telescope[64];

/* Fits header exposure start time */

time_t date_obs;

/* Executable external script */

char ccd_script[64];


/* User interface polling */

unsigned long poll_interval = POLLMS;      
void poll_interval_handler(XtPointer client_data_ptr, XtIntervalId *client_id);            
XtIntervalId poll_interval_id;
XtPointer poll_interval_data_ptr;
static struct timeval xtv;

/* Functions from the camera protocol */

extern int GetCameraInfo(int *image_w, int *image_h);

extern int CaptureImage(int *phase,  unsigned short *data,
  int frame, double exposure, int region, 
  int x, int y, int w, int h);
  
extern int CaptureEnd(int *phase);  
       
extern void WriteFits(char *filename, int w, int h, unsigned short *data, 
  double fits_duration, 
  char*  fits_type, 
  char*  fits_date, 
  char*  fits_target,
  char*  fits_instrument, 
  double fits_temperature,
  char*  fits_filter, 
  char*  fits_telescope);    
 
extern int OpenCamera(int device);     

extern int CloseCamera(int device);    

extern int GetCameraStatus(int *status);

extern int GetCameraTemperature(double *power, 
  double *temperature, double *setpoint);

extern int SetCameraTemperature(int state, double temperature);

extern int ActivateCameraRelay(int relay, double ontime);
 
extern int GetCameraFilterName(int new_filter_number, char *filter_name);

extern int SetCameraFilterName(int flag,  
  int new_filter_number, char *filter_name);

extern int SetCameraFilter(int new_filter_number);


/* Camera variables */

unsigned short *image_data;

double current_exposure;
int    image_w = 1024;
int    image_h = 1024;
double image_exposure = 1.;
double image_exposure_remaining = 1.;
int    region = 0;
int    region_x = 1;
int    region_y = 1; 
int    region_w = 512;
int    region_h = 512;

/* Temperature control */

double setpoint = -10.;
double temperature = 25.;  
double cooler_power;  
int    cool = 0;

/* Filter functions */

int get_filter_name(int new_filter_number, char *filter_name);
int set_filter(int filter);
int set_filter_name(int flag, int new_filter_number, char *filter_name);

/* Filter variables */

int filter_max = FILTER_MAX;
int filter_max_gui = 10;   
int filter = FILTER_START;
int filter_wheel = FILTER_WHEEL;

/* External scripting */

int runscript(char *script);

/* Camera relay controls */

double relay_ontime = RELAYONTIME;

/* GUI main starts here */

int main(int argc, char *argv[])
{
  Arg al[10];
  int ac;
  unsigned long x,y,btn_number,cmd_number;
  int info_number;
  
  EventMask mask;

  /* Contact the camera */
  
  init();
  
  /* Start ds9 for image display */
  
  start_ds9();

  /* Create the toplevel shell */

  toplevel = XtAppInitialize(&context,"",NULL,0,&argc,argv,
      NULL,NULL,0);
  
  /* Set the default size of the window */ 

  ac=0;
  XtSetArg(al[ac],XmNwidth,400); ac++;
  XtSetArg(al[ac],XmNheight,700); ac++;
  XtSetValues(toplevel,al,ac);

  /* Create a form widget */

  ac=0;
  form=XmCreateForm(toplevel,"form",al,ac);
  XtManageChild(form);


  /* Create the menu bar and attach it to the form */
  
  ac=0;
  XtSetArg(al[ac],XmNtopAttachment,XmATTACH_FORM); ac++;
  XtSetArg(al[ac],XmNrightAttachment,XmATTACH_FORM); ac++;
  XtSetArg(al[ac],XmNleftAttachment,XmATTACH_FORM); ac++;
  menu_bar=XmCreateMenuBar(form,"menu_bar",al,ac);
  XtManageChild(menu_bar);
  
  /* Locations of buttons are in percent of window dimension */

  /* Set up the handpaddle buttons, add event handlers, attach to form */
  /* There are 9 places but only 4 are used */
  
  for (x=0; x<3; x++)
  {
    for (y=0; y<3; y++)
    {
      btn_number=3*x+y;
      if ((btn_number==1) | (btn_number==3) | (btn_number==5) | (btn_number==7) ) 
      {
        ac=0;
        XtSetArg(al[ac],XmNlabelString,
            XmStringCreate(btn_name[btn_number],char_set)); ac++;
        XtSetArg(al[ac],XmNleftAttachment,
            XmATTACH_POSITION); ac++;
        XtSetArg(al[ac],XmNleftPosition,27+x*16); ac++;
        XtSetArg(al[ac],XmNrightAttachment,
            XmATTACH_POSITION); ac++;
        XtSetArg(al[ac],XmNrightPosition,41+x*16); ac++;
        XtSetArg(al[ac],XmNtopAttachment,
            XmATTACH_POSITION); ac++;
        XtSetArg(al[ac],XmNtopPosition,9+y*9); ac++;
        XtSetArg(al[ac],XmNbottomAttachment,
            XmATTACH_POSITION); ac++;
        XtSetArg(al[ac],XmNbottomPosition, 17+y*9); ac++;
        button[x][y]=XmCreatePushButton(form,"label",al,ac);
        mask = ButtonReleaseMask | ButtonPressMask;
        XtAddEventHandler(button[x][y],mask,False,
            buttonCB,(XtPointer) btn_number);              
        XtManageChild(button[x][y]);
      }
    }
  }

  /* Create a separator widget and attach it to the form */

  ac=0;
  XtSetArg(al[ac],XmNtopAttachment,XmATTACH_POSITION); ac++;
  XtSetArg(al[ac],XmNtopPosition,42); ac++;
  XtSetArg(al[ac],XmNrightAttachment,XmATTACH_FORM); ac++; 
  XtSetArg(al[ac],XmNleftAttachment,XmATTACH_FORM); ac++;  
  XtSetArg(al[ac],XmNbottomAttachment,XmATTACH_NONE); ac++;  
  sep_btns=XmCreateSeparator(form,"sep",al,ac);
  XtManageChild(sep_btns);   
 
  /* Set up the information label widgets and attach them to the form */

  for  (x=0; x<2; x++)
  {
    for (y=0; y<3; y++)
    {
      info_number=2*y+x;
      ac=0;
      XtSetArg(al[ac],XmNlabelString,
        XmStringCreate(infotext[info_number],char_set)); ac++;
      XtSetArg(al[ac],XmNleftAttachment,
        XmATTACH_POSITION); ac++;
      XtSetArg(al[ac],XmNleftPosition,5+x*50); ac++;
      XtSetArg(al[ac],XmNrightAttachment,
        XmATTACH_POSITION); ac++;
      XtSetArg(al[ac],XmNrightPosition,45+x*50); ac++;
      XtSetArg(al[ac],XmNtopAttachment,
        XmATTACH_POSITION); ac++;
      XtSetArg(al[ac],XmNtopPosition,47+y*6); ac++;
      XtSetArg(al[ac],XmNbottomAttachment,
        XmATTACH_POSITION); ac++;
      XtSetArg(al[ac],XmNbottomPosition,49+y*6); ac++;
      label_info[info_number]=XmCreateLabel(form,"label",al,ac);
      XtManageChild(label_info[info_number]); 
    }
  }

  /* Set up the information data widgets and attach them to the form */

  for  (x=0; x<2; x++)
  {
    for (y=0; y<3; y++)
    {
      info_number=2*y+x;
      ac=0;
      XtSetArg(al[ac],XmNlabelString,
        XmStringCreate(infodata[info_number],char_set)); ac++;
      XtSetArg(al[ac],XmNleftAttachment,
        XmATTACH_POSITION); ac++;
      XtSetArg(al[ac],XmNleftPosition,5+x*50); ac++;
      XtSetArg(al[ac],XmNrightAttachment,
        XmATTACH_POSITION); ac++;
      XtSetArg(al[ac],XmNrightPosition,45+x*50); ac++;
      XtSetArg(al[ac],XmNtopAttachment,
        XmATTACH_POSITION); ac++;
      XtSetArg(al[ac],XmNtopPosition,49+y*6); ac++;
      XtSetArg(al[ac],XmNbottomAttachment,
        XmATTACH_POSITION); ac++;
      XtSetArg(al[ac],XmNbottomPosition,51+y*6); ac++;
      show_info[info_number]=XmCreateLabel(form,"label",al,ac);
      XtManageChild(show_info[info_number]); 
    }
  }

  /* Create a separator widget and attach it to the form */

  ac=0;
  XtSetArg(al[ac],XmNtopAttachment,XmATTACH_POSITION); ac++;
  XtSetArg(al[ac],XmNtopPosition,70); ac++;
  XtSetArg(al[ac],XmNrightAttachment,XmATTACH_FORM); ac++; 
  XtSetArg(al[ac],XmNleftAttachment,XmATTACH_FORM); ac++;  
  XtSetArg(al[ac],XmNbottomAttachment,XmATTACH_NONE); ac++;      
  sep_status=XmCreateSeparator(form,"sep",al,ac);
  XtManageChild(sep_status);   

  /* Set up the 3 command buttons and attach them to the form */

  for (y=0; y<3; y++)
  {
    cmd_number=y;
    ac=0;
    XtSetArg(al[ac],XmNlabelString,
      XmStringCreate(cmd_name[cmd_number],char_set)); ac++;
    XtSetArg(al[ac],XmNleftAttachment,
      XmATTACH_POSITION); ac++;
    XtSetArg(al[ac],XmNleftPosition,20); ac++;
    XtSetArg(al[ac],XmNrightAttachment,
      XmATTACH_POSITION); ac++;
    XtSetArg(al[ac],XmNrightPosition,80); ac++;
    XtSetArg(al[ac],XmNtopAttachment,
      XmATTACH_POSITION); ac++;
    XtSetArg(al[ac],XmNtopPosition,74+y*8); ac++;
    XtSetArg(al[ac],XmNbottomAttachment,
      XmATTACH_POSITION); ac++;
    XtSetArg(al[ac],XmNbottomPosition, 80+y*8); ac++;
    command[y]=XmCreatePushButton(form,"label",al,ac);
    XtManageChild(command[y]);
    XtAddCallback(command[y],XmNactivateCallback,
      commandCB,(XtPointer) cmd_number);
  }

  /* Create prompt dialogs and leave them unmanaged until needed */
  
  /* Image exposure */

  ac=0;
  XtSetArg(al[ac], XmNselectionLabelString,
    XmStringCreateLtoR("Enter time (s): ",char_set)); ac++;
  XtSetArg(al[ac], XmNdialogTitle,
    XmStringCreateLtoR("Image Exposure",char_set)); ac++;
  set_image_exposure_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(set_image_exposure_dialog, XmNokCallback, set_image_exposure_dialogCB, (int *) OK);
  XtAddCallback(set_image_exposure_dialog, XmNcancelCallback, set_image_exposure_dialogCB, (int *) CANCEL);
  XtUnmanageChild (XmSelectionBoxGetChild (set_image_exposure_dialog, XmDIALOG_HELP_BUTTON));
 
  /* Image number */

  ac=0;
  XtSetArg(al[ac], XmNselectionLabelString,
    XmStringCreateLtoR("Enter new image number: ",char_set)); ac++;
  XtSetArg(al[ac], XmNdialogTitle,
    XmStringCreateLtoR("Image Number",char_set)); ac++;
  set_image_number_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(set_image_number_dialog, XmNokCallback, set_image_number_dialogCB, (int *) OK);
  XtAddCallback(set_image_number_dialog, XmNcancelCallback, set_image_number_dialogCB, (int *) CANCEL);
  XtUnmanageChild (XmSelectionBoxGetChild (set_image_number_dialog, XmDIALOG_HELP_BUTTON));

  /* Frame count multi */

  ac=0;
  XtSetArg(al[ac], XmNselectionLabelString,
    XmStringCreateLtoR("Enter maximum multiframe count: ",char_set)); ac++;
  XtSetArg(al[ac], XmNdialogTitle,
    XmStringCreateLtoR("Multiframe Maximum",char_set)); ac++;
  set_frame_count_multi_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(set_frame_count_multi_dialog, XmNokCallback, set_frame_count_multi_dialogCB, (int *) OK);
  XtAddCallback(set_frame_count_multi_dialog, XmNcancelCallback, set_frame_count_multi_dialogCB, (int *) CANCEL);
  XtUnmanageChild (XmSelectionBoxGetChild (set_frame_count_multi_dialog, XmDIALOG_HELP_BUTTON));
  
  /* Image region */

  ac=0;
  XtSetArg(al[ac], XmNselectionLabelString,
    XmStringCreateLtoR("Enter image x,y,w,h : ",char_set)); ac++;
  XtSetArg(al[ac], XmNdialogTitle,
    XmStringCreateLtoR("Image Region",char_set)); ac++;
  XtSetArg(al[ac], XmNokLabelString,
    XmStringCreateLtoR("On",char_set)); ac++;
  XtSetArg(al[ac], XmNhelpLabelString,
    XmStringCreateLtoR("Off",char_set)); ac++;
  set_region_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(set_region_dialog, XmNokCallback, set_region_dialogCB, (int *) REGION_ON);
  XtAddCallback(set_region_dialog, XmNcancelCallback, set_region_dialogCB, (int *) CANCEL);
  XtAddCallback(set_region_dialog, XmNhelpCallback, set_region_dialogCB, (int *) REGION_OFF);

  /* Temperature */

  ac=0;
  XtSetArg(al[ac], XmNselectionLabelString,
    XmStringCreateLtoR("Enter temperature (C): ",char_set)); ac++;
  XtSetArg(al[ac], XmNdialogTitle,
    XmStringCreateLtoR("CCD Temperature Setpoint",char_set)); ac++;
  XtSetArg(al[ac], XmNokLabelString,
    XmStringCreateLtoR("On",char_set)); ac++;
  XtSetArg(al[ac], XmNhelpLabelString,
    XmStringCreateLtoR("Off",char_set)); ac++;
  set_ccd_temperature_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(set_ccd_temperature_dialog, XmNokCallback, set_ccd_temperature_dialogCB, (int *) COOL_ON);
  XtAddCallback(set_ccd_temperature_dialog, XmNcancelCallback, set_ccd_temperature_dialogCB, (int *) CANCEL);
  XtAddCallback(set_ccd_temperature_dialog, XmNhelpCallback, set_ccd_temperature_dialogCB, (int *) COOL_OFF);

  /* Set file name */

  ac=0;
  XtSetArg(al[ac], XmNselectionLabelString,
    XmStringCreateLtoR("Base for image file names: ",char_set)); ac++;
  XtSetArg(al[ac], XmNdialogTitle,
    XmStringCreateLtoR("Image file name entry",char_set)); ac++;
  fits_name_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(fits_name_dialog, XmNokCallback, fits_name_dialogCB, 
    (int *) OK);
  XtAddCallback(fits_name_dialog, XmNcancelCallback, fits_name_dialogCB, 
    (int *) CANCEL);
  XtUnmanageChild (XmSelectionBoxGetChild (fits_name_dialog, 
    XmDIALOG_HELP_BUTTON));

  /* Set target */

  ac=0;
  XtSetArg(al[ac], XmNselectionLabelString,
    XmStringCreateLtoR("FITS header TARGET: ",char_set)); ac++;
  XtSetArg(al[ac], XmNdialogTitle,
    XmStringCreateLtoR("FITS header target entry",char_set)); ac++;
  fits_target_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(fits_target_dialog, XmNokCallback, fits_target_dialogCB, 
    (int *) OK);
  XtAddCallback(fits_target_dialog, XmNcancelCallback, 
    fits_target_dialogCB, (int *) CANCEL);
  XtUnmanageChild (XmSelectionBoxGetChild (fits_target_dialog, 
    XmDIALOG_HELP_BUTTON));

  /* Set telescope */

  ac=0;
  XtSetArg(al[ac], XmNselectionLabelString,
    XmStringCreateLtoR("FITS header TELESCOPE: ",char_set)); ac++;
  XtSetArg(al[ac], XmNdialogTitle,
    XmStringCreateLtoR("FITS header telescope entry",char_set)); ac++;
  fits_telescope_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(fits_telescope_dialog, XmNokCallback, 
    fits_telescope_dialogCB, (int *) OK);
  XtAddCallback(fits_telescope_dialog, XmNcancelCallback, 
    fits_telescope_dialogCB, (int *) CANCEL);
  XtUnmanageChild (XmSelectionBoxGetChild (fits_telescope_dialog, 
    XmDIALOG_HELP_BUTTON));

  /* Set instrument */

  ac=0;
  XtSetArg(al[ac], XmNselectionLabelString,
    XmStringCreateLtoR("FITS header INSTRUMENT: ",char_set)); ac++;
  XtSetArg(al[ac], XmNdialogTitle,
    XmStringCreateLtoR("FITS header instrument entry",char_set)); ac++;
  fits_instrument_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(fits_instrument_dialog, XmNokCallback, 
    fits_instrument_dialogCB, (int *) OK);
  XtAddCallback(fits_instrument_dialog, XmNcancelCallback, 
    fits_instrument_dialogCB, (int *) CANCEL);
  XtUnmanageChild (XmSelectionBoxGetChild (fits_instrument_dialog, 
    XmDIALOG_HELP_BUTTON));

  /* Create a dialog to select the processing script but leave it unmanaged */

  ac=0;
  XtSetArg(al[ac],XmNmustMatch,True); ac++;
  XtSetArg(al[ac],XmNautoUnmanage,False); ac++;
  file_script_dialog=XmCreateFileSelectionDialog(toplevel,"file_script_dialog",al,ac);
  XtAddCallback (file_script_dialog, XmNokCallback, file_script_dialogCB, (XtPointer) OK);
  XtAddCallback (file_script_dialog, XmNcancelCallback, file_script_dialogCB, (XtPointer) CANCEL);
  XtUnmanageChild(XmFileSelectionBoxGetChild(file_script_dialog,
    XmDIALOG_HELP_BUTTON));


  /* Create a dialog to select and read the configuration but leave it unmanaged */

  ac=0;
  XtSetArg(al[ac],XmNmustMatch,True); ac++;
  XtSetArg(al[ac],XmNautoUnmanage,False); ac++;
  file_config_dialog=XmCreateFileSelectionDialog(toplevel,"file_config_dialog",al,ac);
  XtAddCallback (file_config_dialog, XmNokCallback, file_config_dialogCB, (XtPointer) OK);
  XtAddCallback (file_config_dialog, XmNcancelCallback, file_config_dialogCB, (XtPointer) CANCEL);
  XtUnmanageChild(XmFileSelectionBoxGetChild(file_config_dialog,
    XmDIALOG_HELP_BUTTON));

  /* Create the menubar */

  create_menus(menu_bar); 

  /* Disable menu items not used because of hardware limitations */
    
  if ( filter_max <= 1 )
  {
    XtSetSensitive(filter_menu,False);
  }   

  /* Service status display and contact with camera software */

  poll_interval_id = XtAppAddTimeOut(context,
    poll_interval,poll_interval_handler,poll_interval_data_ptr);  
  
  /* Start the user interface */

  XtRealizeWidget(toplevel);
  XtAppMainLoop(context);

  exit(0);
}


/* Add an item into a menu in the menubar */   

Widget make_menu_item(char *item_name, unsigned long client_data, Widget menu)
{
  int ac;
  Arg al[10];
  Widget item;

  ac = 0;
  XtSetArg(al[ac], XmNlabelString,
    XmStringCreateLtoR(item_name,char_set)); ac++;      
  item=XmCreatePushButton(menu,item_name,al,ac);
  XtManageChild(item);
  XtAddCallback (item,XmNactivateCallback,menuCB,(int *) client_data);      
  XtSetSensitive(item,True);
  return(item);
}

/* Add a toggle item into a menu in the menubar */ 

Widget make_menu_item_toggle(char *item_name, unsigned long client_data, Widget menu)
{
  int ac;
  Arg al[10];
  Widget item;

  ac = 0;
  XtSetArg(al[ac], XmNlabelString,
      XmStringCreateLtoR(item_name,char_set)); ac++; 
  item=XmCreateToggleButton(menu,item_name,al,ac);
  XtManageChild(item);
  XtAddCallback (item,XmNvalueChangedCallback,menuCB,(XtPointer) client_data);      
  XtSetSensitive(item,True);
  return(item);
}

/* Create a menu on the menu bar */

Widget make_menu(char *menu_name, Widget menu_bar)
{
  int ac;
  Arg al[10];
  Widget menu, cascade;

  menu=XmCreatePulldownMenu(menu_bar,menu_name,NULL,0);
  ac=0;
  XtSetArg(al[ac],XmNsubMenuId,menu);  ac++;
  XtSetArg(al[ac],XmNlabelString,
    XmStringCreateLtoR(menu_name,char_set)); ac++;
  cascade=XmCreateCascadeButton(menu_bar,menu_name,al,ac);      
  XtManageChild (cascade); 
  return(menu);
}


/* Create all the menubar entries organized into submenus*/

void create_menus(Widget menu_bar)
{
  int ac;
  Arg al[10];
  int i;
     
  /* Create the file menu */
  file_menu = make_menu("File",menu_bar);
  file_script_item = make_menu_item("Script",FILESCRIPT,file_menu);
  file_config_item = make_menu_item("New Config",FILECONFIG,file_menu);
  file_exit_item = make_menu_item("Exit",FILEEXIT,file_menu);
  
  /* Create the set menu */
  set_menu = make_menu("Set",menu_bar);
  set_image_exposure_item = make_menu_item("Image exposure",SETIMAGEEXPOSURE,set_menu);
  set_image_number_item = make_menu_item("Image number",SETIMAGENUMBER,set_menu);
  set_frame_count_multi_item = make_menu_item("Multiframe count",SETFRAMECOUNTMULTI,set_menu);
  set_region_item = make_menu_item("Image region",SETREGION,set_menu);
  set_ccd_temperature_item = make_menu_item("CCD temperature",SETCCDTEMPERATURE,set_menu);
  fits_menu = make_menu("FITS",menu_bar);
  fits_name_item = make_menu_item("Base name",FITSNAME,fits_menu);
  fits_target_item = make_menu_item("Target",FITSTARGET,fits_menu);
  fits_telescope_item = make_menu_item("Telescope",FITSTELESCOPE,fits_menu);
  fits_instrument_item = make_menu_item("Instrument",FITSINSTRUMENT,fits_menu);
  
  /* Create the filter menu */
  filter_menu = make_menu("Filter",menu_bar);
  for ( i=0; i<filter_max; i++ )
  {
    filter_item[i] = make_menu_item_toggle(filter_label[i],FILTER1+i,filter_menu);
  }
   
  /* Add radio behavior to the filter menu */
  ac=0;
  XtSetArg(al[ac],XmNradioBehavior,True); ac++;
  XtSetValues(filter_menu,al,ac);

  /* Create a toggle button mode menu */
  mode_menu = make_menu("Mode",menu_bar);
  mode_single_item = make_menu_item_toggle("Single",MODESINGLE,mode_menu);
  mode_focus_item = make_menu_item_toggle("Focus",MODEFOCUS,mode_menu);
  mode_multi_item  = make_menu_item_toggle("Multi",MODEMULTI,mode_menu);
     
  /* Add radio behavior to the mode menu */
  ac=0;
  XtSetArg(al[ac],XmNradioBehavior,True); ac++;
  XtSetValues(mode_menu,al,ac);
  
  /* Set an initial mode_menu button state */
  XmToggleButtonSetState(mode_single_item, True, False);
           
  /* Create a toggle button image type menu */
  type_menu = make_menu("Type",menu_bar);
  type_light_item = make_menu_item_toggle("Light",TYPELIGHT,type_menu);
  type_dark_item = make_menu_item_toggle("Dark",TYPEDARK,type_menu);
  type_bias_item = make_menu_item_toggle("Bias",TYPEBIAS,type_menu);
  type_flat_item = make_menu_item_toggle("Flat",TYPEFLAT,type_menu);
  
       
  /* Add radio behavior to the type menu */
  ac=0;
  XtSetArg(al[ac],XmNradioBehavior,True); ac++;
  XtSetValues(type_menu,al,ac);
  
  /* Set the initial type_menu button */
  XmToggleButtonSetState(type_light_item, True, False);

  /* Create the processing menu */
  processing_menu = make_menu("Processing", menu_bar);
  processing_scripting_toggle = make_menu_item_toggle("Scripting", PROCSCRIPTING, processing_menu);
  
}

/* Callback on menubar action sorts requests based on client_data */

void menuCB(Widget w, int client_data, XmAnyCallbackStruct *call_data)
{
  int i;  
    
  if (client_data==FILESCRIPT)   
  {
    XtManageChild(file_script_dialog);
  }

  if (client_data==FILECONFIG)
  {
    /* make the configuration file dialog appear */
    
    XtManageChild(file_config_dialog);
  }  
 
  if (client_data==FILEEXIT)   
  {
    free(image_data);
    exit(0);
  }
  
  if (client_data==SETIMAGEEXPOSURE)
  {
    XtManageChild(set_image_exposure_dialog);
  }
    
  if (client_data==SETIMAGENUMBER) 
  {
    XtManageChild(set_image_number_dialog);
  }   
  
  if (client_data==SETFRAMECOUNTMULTI) 
  {
    XtManageChild(set_frame_count_multi_dialog);
  }  
  
  
  if (client_data==SETREGION)
  {
    XtManageChild(set_region_dialog);
  }
  
  if (client_data==SETCCDTEMPERATURE) 
  {
    XtManageChild(set_ccd_temperature_dialog);
  }

  if (client_data==FITSNAME)
  {
    XtManageChild(fits_name_dialog);
  } 

  if (client_data==FITSTARGET)
  {
    XtManageChild(fits_target_dialog);
  } 

  if (client_data==FITSTELESCOPE)
  {
    XtManageChild(fits_telescope_dialog);
  }
  
  if (client_data==FITSINSTRUMENT)
  {
    XtManageChild(fits_instrument_dialog);
  }

  for (i=0; i<filter_max; i++)
  {
    if ( ( client_data == (FILTER1 + i)) && (filter_max > i) )
    {
      if( XmToggleButtonGetState(filter_item[i]) )
      {
        filter = i + 1;
        set_filter(filter);
        get_filter_name(filter, filter_name);
        sprintf(fits_filter,"%s", filter_name);
        update_filter_display();
        update_filter_menu(i);
      }
    }
  }

  if (client_data==MODESINGLE)
  {
    if( XmToggleButtonGetState(mode_single_item) )
    {
      acquire = SINGLE;
    }
  }
  
  if (client_data==MODEFOCUS)
  {
    if( XmToggleButtonGetState(mode_focus_item) )
    {
      acquire = FOCUS;
    }
  }
      
  if (client_data==MODEMULTI)
  {
    if( XmToggleButtonGetState(mode_multi_item) )
    {
      acquire = MULTI;
    }
  }

  
  if (client_data==TYPELIGHT)
  {
    if( XmToggleButtonGetState(type_light_item) )
    {
      frame = LIGHT;
    }
  }
  if (client_data==TYPEDARK)
  {
    if( XmToggleButtonGetState(type_dark_item) )
    {
      frame = DARK;
    }
  }
  if (client_data==TYPEBIAS)
  {
    if( XmToggleButtonGetState(type_bias_item) )
    {
      frame = BIAS;
    }
  } 
  if (client_data==TYPEFLAT)
  {
    if( XmToggleButtonGetState(type_flat_item) )
    {
      frame = FLAT;
    }
  }
  if (client_data==PROCSCRIPTING)
  { 
    if( XmToggleButtonGetState(processing_scripting_toggle) )
    {
      scripting = TRUE;
    }
    else
    {
      scripting = FALSE;
    }      
  } 
       
    
  /* Take no action on cancel */
  
  if (client_data==CANCEL); 

}

/* Dialog for user input of script file name */

void file_script_dialogCB(w,client_data,call_data)
  Widget w;
  int client_data;
  XmAnyCallbackStruct *call_data;
  
{
  char *newscript;
  int n;
  XmFileSelectionBoxCallbackStruct *s =
      (XmFileSelectionBoxCallbackStruct *) call_data;
  
  /* Do nothing if cancel is selected. */
  if (client_data==CANCEL) 
  {
     XtUnmanageChild(file_script_dialog);
     return;
  }

  /* Assign a new script if ok is selected. */
  if (client_data==OK) 
  {
     /* Wipe the old file name */
     
     ccd_script[0] = '\0';
     
     /* Get the new file name from the file selection box */
     
     XmStringGetLtoR(s->value, char_set, &newscript); 
     strncpy(ccd_script,newscript,64);
     n = strlen(ccd_script);
     if ((n >0 ) && (n < 64))
     {  
       ccd_script[n]='\0';
     }
     else
     {
       ccd_script[0]='\0';
     }        
    
     /* Close the box */
     
     XtUnmanageChild(file_script_dialog);
     return;
  }
  
  /* Handle unexpected client data */
  
  XtUnmanageChild(file_script_dialog);
}
  

/* Dialog for user input of fits file base name */

void fits_name_dialogCB(w,client_data,call_data)
  Widget w;
  int client_data;
  XmSelectionBoxCallbackStruct  *call_data;
{  
  char *s;
  
  switch (client_data)
  {
    case OK:
      XmStringGetLtoR(call_data->value,char_set,&s);
      strncpy(base_name,s,17);
      base_name[16]='\0';
      XtFree(s);
      break;
    case CANCEL:
      break;
  }
  XtUnmanageChild(w);
}

/* Dialog for user input of fits file header target entry */

void fits_target_dialogCB(w,client_data,call_data)
  Widget w;
  int client_data;
  XmSelectionBoxCallbackStruct  *call_data;
{  
  char *s;
  
  switch (client_data)
  {
    case OK:
      XmStringGetLtoR(call_data->value,char_set,&s);
      strncpy(fits_target,s,17);
      fits_target[16] = '\0';
      XtFree(s);
      break;
    case CANCEL:
      break;
  }
  XtUnmanageChild(w);
}

/* Dialog for user input of fits file header telescope entry */

void fits_telescope_dialogCB(w,client_data,call_data)
  Widget w;
  int client_data;
  XmSelectionBoxCallbackStruct  *call_data;
{  
  char *s;
  
  switch (client_data)
  {
    case OK:
      XmStringGetLtoR(call_data->value,char_set,&s);
      strncpy(fits_telescope,s,17);
      fits_telescope[16] = '\0';
      XtFree(s);
      break;
    case CANCEL:
      break;
  }
  XtUnmanageChild(w);
}

/* Dialog for user input of fits file header instrument entry */

void fits_instrument_dialogCB(w,client_data,call_data)
  Widget w;
  int client_data;
  XmSelectionBoxCallbackStruct  *call_data;
{  
  char *s;
  
  switch (client_data)
  {
    case OK:
      XmStringGetLtoR(call_data->value,char_set,&s);
      strncpy(fits_instrument,s,17);
      fits_instrument[16] = '\0';
      XtFree(s);
      break;
    case CANCEL:
      break;
  }
  XtUnmanageChild(w);
}

/* Dialog for user input of image exposure time */

void set_image_exposure_dialogCB(w,client_data,call_data)
  Widget w;
  int client_data;
  XmSelectionBoxCallbackStruct  *call_data;
{    
  char *s;

  switch (client_data)
  {
    case OK:
      XmStringGetLtoR(call_data->value,char_set,&s);
      image_exposure = atof(s);
      if ( image_exposure < MINEXP )
      {
        image_exposure = MINEXP;
      }
      if ( image_exposure > MAXEXP )
      {
        image_exposure = MAXEXP;
      }
      image_exposure_remaining = image_exposure;
      XtFree(s);
      break;
    case CANCEL:
      break;
  }
  update_exposure_display();
  XtUnmanageChild(w);
}

/* Dialog for user input of image number */
/* Should add warnings here about possibly overwriting existing files */
/* This allows for changing number sequence during a run */

void set_image_number_dialogCB(w,client_data,call_data)
  Widget w;
  int client_data;
  XmSelectionBoxCallbackStruct  *call_data;
{    
  char *s;
  int new_number;

  switch (client_data)
  {
    case OK:
      XmStringGetLtoR(call_data->value,char_set,&s);
      new_number = (int) atof(s);
      if ( new_number < 2 )
      {
        new_number = 1;
      }
      image_number = new_number;
      XtFree(s);
      break;
    case CANCEL:
      break;
  }
  XtUnmanageChild(w);
}

/* Dialog for user input of multiframe maximum frame count */

void set_frame_count_multi_dialogCB(w,client_data,call_data)
  Widget w;
  int client_data;
  XmSelectionBoxCallbackStruct  *call_data;
{    
  char *s;

  switch (client_data)
  {
    case OK:
      XmStringGetLtoR(call_data->value,char_set,&s);
      frame_count_multi = (int) atof(s);
      if ( frame_count_multi < 2 )
      {
        frame_count_multi = 2;
      }
      frame_count_max = frame_count_multi;
      XtFree(s);
      break;
    case CANCEL:
      break;
  }
  XtUnmanageChild(w);
}


/* Dialog for user input of image region */

void set_region_dialogCB(w,client_data,call_data)
    Widget w;
    int client_data;
    XmSelectionBoxCallbackStruct  *call_data;
{  
  char *s;
  int in_x, in_y, in_w, in_h;

  switch (client_data)
  {
    case REGION_ON:

      /* Set the switch on */
      
      region = TRUE;
      XmStringGetLtoR(call_data->value,char_set,&s);
      if (sscanf(s, "%d,%d,%d,%d", &in_x, &in_y, &in_w, &in_h) == 4)
      {
        
        /* Test and clip subimage boundaries */
        
        if (in_x < 1)
        {
          in_x = 1;
        }
        if (in_x > image_w )
        {
          in_x = image_w;
        }       
        if (in_y < 1)
        {
          in_y = 1;
        }
        if (in_y > image_h)
        {
          in_y = image_h;
        }       
        if (in_w < 1)
        {
          in_w = 1;
        }
        if (in_w > image_w)
        {
          in_w = image_w;
        }      
        if (in_h < 1)
        {
          in_h = 1;
        }
        if (in_h > image_h)
        {
          in_h = image_h;
        }      
        if (in_x + in_w > image_w + 1)
        {
          in_w = image_w - in_x + 1;
        }
        if (in_y + in_h > image_h + 1)
        {
          in_h = image_h - in_y + 1;
        }           
        region_x = in_x;
        region_y = in_y;
        region_w = in_w;
        region_h = in_h;
      }
      XtFree(s); 
      break;

    case REGION_OFF:
      
      /* Set the switch off */
      
      region = FALSE;
      region_x = 1;
      region_y = 1;
      region_w = image_w;
      region_h = image_h;
      
      break;  

    case CANCEL:
      break;
  }
  update_region_display();
  XtUnmanageChild(w);
}

/* Dialog for user input of ccd temperature setpoint */

void set_ccd_temperature_dialogCB(w,client_data,call_data)
  Widget w;
  int client_data;
  XmSelectionBoxCallbackStruct  *call_data;
{    
  char *s;

  switch (client_data)
  {
    case COOL_ON:
      XmStringGetLtoR(call_data->value,char_set,&s);
      setpoint=atof(s);
      XtFree(s); 
      cool = TRUE;        
      break;
    case COOL_OFF:
      cool = FALSE;
      break;
    case CANCEL:
      break;
  }
  SetCameraTemperature(cool, setpoint);
  update_temperature_display();
  XtUnmanageChild(w);
}

/* Callback function for the 9 grid buttons */

void buttonCB(w,client_data,call_event)
  Widget w;
  int client_data;
  XEvent *call_event;
{
  int n;
  int et = call_event->type;
  int dn,up;
  int relay;
   
  /* identify the button number n = 3*x+y */
  n=client_data;

  /* identify the action */
  
  dn = et == ButtonPress;
  up = et == ButtonRelease;
      
  if(dn)
  {
    switch (n) 
    {
   
      /* NoOp */
      case 0:
      break;

      /* E */
      case 1: 
      relay_ontime = RELAYONTIME;
      relay = 1;
      ActivateCameraRelay(relay, relay_ontime);
      break;

      /* NoOp */
      case 2: 
      break;

      /* N */
      case 3: 
      relay_ontime = RELAYONTIME;
      relay = 4;
      ActivateCameraRelay(relay, relay_ontime);
      break;

      /* NoOp */
      case 4:
      break;

      /* S */
      case 5: 
      relay_ontime = RELAYONTIME;
      relay = 8;
      ActivateCameraRelay(relay, relay_ontime);

      break;

      /* NoOp */
      case 6:
      break;

      /* W */
      case 7:
      relay_ontime = RELAYONTIME;
      relay = 2;
      ActivateCameraRelay(relay, relay_ontime);
      break;

      /* NoOp */
      case 8: 
      break;    

    }
  }      
  
  if(up)
  {     
    /* Wait for response before accepting another cycle */

    usleep(100000);
  }

}


/* Callback for the camera command buttons */

void commandCB(Widget w, int client_data, XmAnyCallbackStruct *call_data)
{
  
  int n, cmd_number, region_flag;

  /* Identify the button */
  cmd_number = client_data;

  /* Respond to the request */
  /* Test ccd state, set flags, execute request */
  n = cmd_number;

  switch (n) 
  {

    /* Expose */
    case 0:
      
      /* Start the sequence */
      sequence = 1;
      break;
      
    /* Interrupt */
    case 1:

      /* Stop the sequence */
      sequence = 0;
      break;

    /* Capture region parameters from ds9 */
    case 2:
      region_flag = get_ds9_region();

      if ( region_flag >= 3 )
      {
        /* Test and clip boundaries */
        if (region_x < 1)
        {
          region_x = 1;
        }
        if (region_x > image_w )
        {
          region_x = image_w;
        }       
        if (region_y < 1)
        {
          region_y = 1;
        }
        if (region_y > image_h)
        {
          region_y = image_h;
        }       
        if (region_w < 1)
        {
          region_w = 1;
        }
        if (region_w > image_w)
        {
          region_w = image_w;
        }      
        if (region_h < 1)
        {
          region_h = 1;
        }
        if (region_h > image_h)
        {
          region_h = image_h;
        }      
        if (region_x + region_w > image_w + 1)
        {
          region_w = image_w - region_x + 1;
        }
        if (region_y + region_h > image_h + 1)
        {
          region_h = image_h - region_y + 1;
        }           
        region = TRUE;
        region_x = region_x;
        region_y = region_y;
        region_w = region_w;
        region_h = region_h;
      }
      else
      {
        region = FALSE;
        region_x = 1;
        region_y = 1;
        region_w = image_w;
        region_h = image_h;
      }
      update_all_displays();
      break;
  }
  update_all_displays();
}

/* Callback function for selecting and reading the configuration file */

void file_config_dialogCB(w,client_data,call_data)
  Widget w;
  int client_data;
  XmAnyCallbackStruct *call_data;
  
{
  XmFileSelectionBoxCallbackStruct *s =
      (XmFileSelectionBoxCallbackStruct *) call_data;
  
  /* Do nothing if cancel is selected. */
  if (client_data==CANCEL) 
  {
     XtUnmanageChild(file_config_dialog);
     return;
  }

  /* Assign a new file name if ok is selected. */
  if (client_data==OK) 
  {
     /* Wipe the old file name */
     
     configfile[0] = '\0';
     
     /* Get the new file name from the file selection box */
     
     XmStringGetLtoR(s->value, char_set, &configfile);   
     
     /* Read the new configuration */
        
     read_config();
     
     /* Close the box */
     
     XtUnmanageChild(file_config_dialog);
     return;
  }
  
  /* Handle unexpected client data */
  
  XtUnmanageChild(file_config_dialog);
} 
 

/* Update imaging ccd exposure time in the window */

void update_exposure_display()
{
  XmString xmstr;
  Arg al[10];
  int ac;

  sprintf(buf,"%4.3lf",image_exposure_remaining);
  xmstr = XmStringCreateLtoR(buf,char_set);
  ac=0;
  XtSetArg(al[ac],XmNlabelString,xmstr); ac++;
  XtSetValues(show_info[1],al,ac);   
  XmStringFree(xmstr);
}  

   
/* Update image region selection in the window */

void update_region_display()
{
  XmString xmstr;
  Arg al[10];
  int ac;

  if ( region == TRUE )
  {
  sprintf(buf,"%d %d %d %d [On]",
    region_x, region_y, region_w, region_h);
  }
  else
  {
  sprintf(buf,"%d %d %d %d [Off]",
    region_x, region_y, region_w, region_h);
  }
  
  xmstr = XmStringCreateLtoR(buf,char_set);
  ac=0;
  XtSetArg(al[ac],XmNlabelString,xmstr); ac++;
  XtSetValues(show_info[0],al,ac);   
  XmStringFree(xmstr);
} 

/* Update ccd temperature in the window                  */
/* Values must have been previously read from the camera */

void update_temperature_display()
{
  XmString xmstr;
  Arg al[10];
  int ac;
  if (cool == 1)
  {
    sprintf(buf,"%3.1lf [%3.1lf (On)]", temperature,setpoint);
  }
  else
  {
    sprintf(buf,"%3.1lf [%3.1lf (Off)]", temperature,setpoint); 
  }
  xmstr = XmStringCreateLtoR(buf,char_set);
  ac=0;
  XtSetArg(al[ac],XmNlabelString,xmstr); ac++;
  XtSetValues(show_info[2],al,ac);   
  XmStringFree(xmstr);
} 

/* Write ccd temperature to a system file */

void save_ccd_temperature()
{  
  FILE* outfile;
  outfile = fopen("/usr/local/observatory/status/ccdtemperature","w");
  if ( outfile == NULL )
  {
    fprintf(stderr,"Cannot update ccdtemperature file\n");
    return;
  }

  fprintf(outfile, "%4.2lf\n", temperature);      
  fclose(outfile);
}


/* Update the file name in the main window */

void update_file_name_display()
{
  XmString xmstr;
  Arg al[10];
  int ac; 
    
  xmstr = XmStringCreateLtoR(file_name,char_set);
    
  ac=0;
  XtSetArg(al[ac],XmNlabelString,xmstr); ac++;
  XtSetValues(show_info[3],al,ac);   
  XmStringFree(xmstr);
}       

/* Update filter data in the main window */

void update_filter_display()
{
  XmString xmstr;
  Arg al[10];
  int ac; 
  
  xmstr = XmStringCreateLtoR(filter_name,char_set);
  
  ac=0;
  XtSetArg(al[ac],XmNlabelString,xmstr); ac++;
  XtSetValues(show_info[4],al,ac);   
  XmStringFree(xmstr);
} 

/* Update a filter name in the drop down menu  */
/* Names are numbered from 0                   */
/* Filters in the wheel are numbered from 1    */

void update_filter_menu(int i)
{
  XmString xmstr;
  Arg al[10];
  int ac; 
  
  /* This routine will segfault if the GUI as not been initialized        */

  if ( startup == TRUE )
  {
    sprintf(filter_label[i],"%s",filter_name);
    xmstr = XmStringCreateLtoR(filter_name,char_set);
    
    ac=0;
    XtSetArg(al[ac],XmNlabelString,xmstr); ac++;
    XtSetValues(filter_item[i],al,ac); 
    XmStringFree(xmstr);
  }
} 

/* Update status in the window */

void update_status_display()
{
  XmString xmstr;
  Arg al[10];
  int ac;
  set_status_buffer();
  xmstr = XmStringCreateLtoR(status_buf,char_set);
  ac=0;
  XtSetArg(al[ac],XmNlabelString,xmstr); ac++;
  XtSetValues(show_info[5],al,ac);   
  XmStringFree(xmstr);
}   
 
/* Update all display buffers */

void update_all_displays()
{
  update_status_display();
  update_temperature_display();
  update_filter_display();
  update_exposure_display();
  update_region_display();
}   

/* User input of image ccd exposure time */

void input_image_exposure() 
{
  XtManageChild(set_image_exposure_dialog);
}

/* User input of multiframe count max */

void input_frame_count_multi() 
{
  XtManageChild(set_frame_count_multi_dialog);
}

/* User input of ccd temperature setpoint */

void input_setpoint() 
{
  XtManageChild(set_ccd_temperature_dialog);
}


/* Set panel status buffer based on information from the user interface */

void set_status_buffer(void)
{
  
  /* Set the display message */
     
  if (sequence == 1)
  {
     if (acquire == SINGLE)
     {
       if (frame == DARK)
       {
         sprintf(status_buf,"Single dark");
       }
       else if (frame == BIAS)
       {
         sprintf(status_buf,"Single bias");
       }
       else if (frame == FLAT)
       {
         sprintf(status_buf,"Single flat");
       }        
       else
       {
         sprintf(status_buf,"Single");
       }  
     }
     else if (acquire == FOCUS)
     {
       if(frame == DARK)
       {
         sprintf(status_buf,"Focus dark");
       }
       else if(frame == BIAS)
       {
         sprintf(status_buf,"Focus bias");
       }
       else if(frame == FLAT)
       {
         sprintf(status_buf,"Focus flat");
       }
       else
       {
         sprintf(status_buf,"Focus");
       }               
     }
     else if (acquire == MULTI)
     {
       if (frame == DARK)
       {
         sprintf(status_buf,"Multiple dark");
       }
       else if (frame == BIAS)
       {
         sprintf(status_buf,"Multiple bias");
       }
       else if (frame == FLAT)
       {
         sprintf(status_buf,"Multiple flat");
       }
       else
       {
         sprintf(status_buf,"Multiple");
       } 
     }
     else 
     {
       sprintf(status_buf,"Image");
     }             
  } 
  else
  {
    sprintf(status_buf,"Camera ready");
  }
}

/* Start ds9 as a new process */

void start_ds9(void)
{
  strcpy(buf, "ds9 &");
  system(buf);
}

/* Use xpa to tell ds9 to read an image name */

void update_ds9_image(char *name)
{  
  #define NXPA 10
  int i, nresponses;
  int buffer_length = 0;
  char *data_buffer = NULL;
  char *server_names[NXPA];
  char *server_messages[NXPA];
  char ds9msg[64];
  sprintf(ds9msg, "file fits %s",name);
  
  nresponses = XPASet(NULL, "ds9", ds9msg, NULL, data_buffer,
    buffer_length, server_names, server_messages, NXPA);
  
  for(i=0; i < nresponses; i++)
  {
    if( server_messages[i] != NULL )
    {
    
      /* Process error messages */
    
      printf("%s (%s)\n", server_messages[i], server_names[i]);
    
    }
    if( server_names[i] )
    {  
      free(server_names[i]);
    }
    if( server_messages[i] )
    { 
     free(server_messages[i]);  
    }
  }
}
       
/* Use xpa to read last region from ds9 and set region parameters */
/* Returns number of coordinates that have been read              */
/* A box will return 4 and a circle will return 3                 */
/* Coordinates are with respect to the lower left corner          */

int get_ds9_region(void)
{  
  #define NXPA 10
  int i, j, nresponses;
  int buffer_lengths[NXPA]={0};
  char *data_buffer[NXPA]; 
  char *server_names[NXPA];
  char *server_messages[NXPA];
  char *regionptr;
  double xc,yc,dx,dy;
  
  j=0;
  nresponses = XPAGet(NULL, "ds9", "regions", NULL, data_buffer, 
    buffer_lengths, server_names, server_messages, NXPA);
  
  for(i=0; i < nresponses; i++)
  {
    if( server_messages[i] == NULL )
    {
          
      /* Process data_buffer[i] contents */
    
      /* Format:                             */
      /*   circle(x_center,y_center,radius)  */
      /*   box(x_center,y_center,dx,dy)      */
      /*   relative to lower left            */ 
      
      regionptr=strrchr(data_buffer[i], '(' );
      if(regionptr!=NULL)
      {
        regionptr++;
        j=sscanf(regionptr,"%lf,%lf,%lf,%lf",
          &xc,&yc,&dx,&dy);
        if (j == 4)  
        {
          region_x = (int) (xc - dx/2);
          region_y = (int) (yc - dy/2);
          region_w = (int) (dx);
          region_h = (int) (dy);
        }
        if (j == 3)  
        {
          region_x = (int) (xc - dx);
          region_y = (int) (yc - dx);
          region_w = (int) (dx + dx);
          region_h = region_w;
        }        
      }           
    }
    else
    {
    
      /* Process error messages */
    
      fprintf(stderr,"%s (%s)\n", server_messages[i], server_names[i]);
    
    }
    if( server_names[i] )
    {  
      free( server_names[i] );
    }
    if( server_messages[i] )
    { 
     free( server_messages[i] );  
    }
  }
  return j;
}

/* Send the latest fits file through a system network routine */
/* Return system flag */

int send_image(char *name)
{
  int flag;
  char cmdstr[256];
  FILE *fp = fopen (name, "r");
  if ( fp == NULL )
  {
    return (0);
  }   

  /* Send file "name" by using a system command */

  sprintf(cmdstr,"transfer_image %s &",name);   
  flag = system(cmdstr);
      
  return (flag);

}


/* Camera startup */

void init(void)
{
    
  int camera_status = FALSE;
  int filter_status = FALSE;
  int cooler_status = FALSE;
    
  if (startup == TRUE)
  {
    return;
  }
      
  /* Initialize the driver for a generic USB camera */

  camera_status = OpenCamera(USB);

  /* Alert stderr to a problem and exit */
  
  if (camera_status != TRUE)
  {
    fprintf(stderr,"Camera did not respond to startup request \n");
    exit(1);
  }
  
  /* Contact the camera and allow its driver to update parameters */
  
  camera_status = GetCameraInfo(&image_w, &image_h);
        
  /* Set base_name to default */
  
  strcpy(base_name,"image_");

  
  /* Initialize the fits headers */
  
  sprintf(fits_type, "                ");
  sprintf(fits_date, "                ");
  sprintf(fits_target, "                ");
  sprintf(fits_filter, "                ");
  sprintf(fits_instrument, "                ");
  sprintf(fits_telescope, "                ");
  
  /* Handle the configuration file to change defaults */
  
  configfile = (char *) malloc (MAXPATHLEN);
  strcpy(configfile,CONFIGFILE);  

  read_config();  
  
  /*  Note that configfile may be changed later through the File menu */
   
  /* Set the image sizes */

  region_x = 1;
  region_y = 1; 
  region_w = image_w;
  region_h = image_h;
          
  /* Select filter and save header information */
  
  filter_status = set_filter(filter);
  filter_status = get_filter_name(filter, filter_name);    

  sprintf(fits_filter,filter_name);
         
  /* Alert operator to absence of filter wheel or set filter wheel error */
  
  if (filter_status != TRUE)
  {
    fprintf(stderr,"Filter wheel did not respond to the startup request\n");
    filter_wheel = NO_FILTER_WHEEL;
  }

  cooler_status = GetCameraTemperature(&cooler_power, 
    &temperature, &setpoint);
  fits_temperature = temperature;
      
  /* Alert stderr to a temperature controller problem but do not exit*/
  
  if (cooler_status != TRUE)
  {
    fprintf(stderr,"Camera temperature control did not respond to the startup request \n");
  }

  /* Set initial cooler temperature and turn on controller */

  setpoint = -10.;
  cool = TRUE;
  SetCameraTemperature(cool, setpoint); 
      
  /* Allocate memory for images */

  image_data = malloc((image_w*image_h)*sizeof(unsigned short));    
    
  /* Set empty string for file_name until the first image is stored */
  
  strcpy(file_name,"");

  /* Set temperature setpoint to default */
  
  setpoint = -10.;
  
  /* Alert stderr to a problem and exit */
  
  if (camera_status != TRUE)
  {
    fprintf(stderr,"The camera did not respond to the startup request \n");
    exit(1);
  }
  startup = TRUE;

}

/* Camera function called during the polling cycle                   */
/* Count cycles and find the temperature every minute                */
/* Start a sequence when the sequence flag is reset 1                */
/* Stop a sequence when the sequence flag is reset 0                 */
/* Start a new exposure when the phase is 0 while the sequence is 1  */
/* Update the time remaining in an ongoing exposure                  */
/* Restart the timer to call this function again                     */

void camera (void)
{
  static int tcount, inited;
  int year, month, day;
  int hours, minutes, seconds, milliseconds;
  long int microseconds;
  
  time_t now;
  struct tm *g;

  /* Test to see if the camera is available */
  if (startup != TRUE)
  {
    return;
  }
  
  /* Make sure that the display has the initial parameters */
  if (inited != TRUE)
  {
    inited = TRUE;
    update_all_displays();
  }

  /* Take the device temperature 15 seconds on a 250 ms polling cycle */      
  if ((tcount < 0) || (tcount > 60))
  {
    tcount = 1;
    GetCameraTemperature(&cooler_power,&temperature,&setpoint);
    fits_temperature = temperature;
    update_temperature_display();
  }
  tcount++;
           
  /*  Handle a request to interrupt a sequence */
  if ( sequence == 0 )
  {
    if (phase != 0)
    {
      /* End any exposure in progress */
      phase = 2;
      CaptureEnd(&phase); 
      phase=0;
      frame_count = 1;    
      image_exposure_remaining = image_exposure;
      update_all_displays();
    }

    if (frame_count > 1)
    {
      frame_count = 1;
    
      image_exposure_remaining = image_exposure;
      update_all_displays(); 
    }        

    if (expose_menu != TRUE)
    {
      /* Enable expose button */
      XtSetSensitive(command[0],True);
     
      /* Enable menubar options */
      XtSetSensitive(set_menu,True);
      if ( filter_max > 1 )
      {      
        XtSetSensitive(filter_menu,True);
      }
      XtSetSensitive(mode_menu,True);
      XtSetSensitive(type_menu,True);
      expose_menu = TRUE;
      update_all_displays();
    }
  }
  
  /* Handle a request for a new exposure */  
  if ((sequence == 1) && ( phase == 0 )) 
  {               
    /* Set parameters for new sequence if needed */
    if (frame_count == 1)
    {
      /* Start a new sequence */
      if (acquire == SINGLE)
      {
        frame_count_max = 1;
      }
      else
      {
        frame_count_max = frame_count_multi;
      } 

      if (acquire == FOCUS)
      {
        archive = FALSE;
      }
      else
      {
        archive = TRUE;
      } 

      /* Disable expose button */
      XtSetSensitive(command[0],False);
      
      /* Disable menubar options during sequence */
      XtSetSensitive(set_menu,False);
      XtSetSensitive(mode_menu,False);
      XtSetSensitive(type_menu,False);
      expose_menu = FALSE;
      update_all_displays();    
    }
    
    /* Note the time it started and save as xtv */

    gettimeofday (&xtv, NULL); 
    
    /* Copy seconds part to convert to UTC */

    now = xtv.tv_sec;
    
    /* Copy microseconds part to handle milliseconds of UTC */

    microseconds = xtv.tv_usec;
    
    /* Convert the microseconds part to milliseconds */
    
    milliseconds = microseconds/1000;
    
    /* Convert unix time now to UTC */
    
    g = gmtime(&now);

    /* Copy values pointed to by g to variables we will use later */

    year = g->tm_year;
    year = year + 1900;
    month = g->tm_mon;
    month = month + 1;
    day = g->tm_mday;
    hours = g->tm_hour;
    minutes = g->tm_min;
    seconds = g->tm_sec;
    
    /* Create ascii string for fits header */
       
    sprintf(fits_date, "%4d-%02d-%02dT%02d:%02d:%02d.%03d",
      year,month,day,hours,minutes,seconds,milliseconds);

    if (frame != BIAS)
    {
      current_exposure = image_exposure;
    }
    else
    {
      current_exposure = 0.001;
    } 
    
    CaptureImage(&phase, image_data,
    frame, current_exposure, region, 
    region_x, region_y, region_w, region_h);    
  }

  /* Update the remaining time if an exposure is in progress */
  /* Read the image into memory when done */
  if (phase == 1) 
  {
    struct timeval tv;
    double dt;

    /* Find total elapsed time since starting exposure */
    gettimeofday (&tv, NULL);
    dt = tv.tv_sec - xtv.tv_sec + (tv.tv_usec - xtv.tv_usec)/1e6;
    
    if (dt >= current_exposure) 
    {
      /* Exposure is ready to read */
      /* Read to memory */
      /* Reset the exposure time counter */
        image_exposure_remaining = image_exposure;
        update_exposure_display();

      CaptureImage(&phase, image_data,
        frame, current_exposure, region, 
        region_x, region_y, region_w, region_h);
    } 
    else 
    {
      /* Exposure is not complete */
        
        image_exposure_remaining = current_exposure - dt;
        update_exposure_display();
              
    }
  }
  
  if (phase == 2) 
  {
    /* Write a file appropriate for the acquisition mode */
    
    /* Set the image type description */

    if ( frame == DARK )
    {
      sprintf(fits_type, "%s", "Dark Frame");
    }
    else if ( frame == LIGHT )
    {
      sprintf(fits_type, "%s", "Light Frame");
    }
    else if ( frame == BIAS )
    {
      sprintf(fits_type, "%s", "Bias Frame");
    }
    else if ( frame == FLAT )
    {
      sprintf(fits_type, "%s", "Flat Frame");
    }    
    else
    {
      sprintf(fits_type, "%s", "");
    }      

    if (archive)
    {
      /* Archive an ennumerated file */
      
      if ( frame == DARK )
      {
        sprintf(file_name, "%s%05d.fits", "dark_", image_number);
      }
      else if ( frame == LIGHT )
      {
        sprintf(file_name, "%s%05d.fits", base_name, image_number);
      }
      else if ( frame == BIAS )
      {
        sprintf(file_name, "%s%05d.fits", "bias_", image_number);
      }
      else if ( frame == FLAT )
      {
        sprintf(file_name, "%s%05d.fits", "flat_", image_number);
      }    
      else
      {
        sprintf(file_name, "%s%05d.fits", base_name, image_number);
      } 
      image_number++;    
  
    }
    else
    {
      /* Save a generic file */
      sprintf(file_name, "image.fits");    
    }

    
    if (frame != BIAS)
    {
      fits_exposure = image_exposure;
    }
    else
    {
      fits_exposure = 0.;  
    }

    if (region == FALSE)
    {
      WriteFits(file_name, image_w, image_h, image_data, 
        fits_exposure, 
        fits_type, 
        fits_date, 
        fits_target,                                               
        fits_instrument,
        fits_temperature, 
        fits_filter, 
        fits_telescope);
      save_ccd_temperature();
    }
    else
    {
      WriteFits(file_name, region_w, region_h, image_data, 
        fits_exposure, 
        fits_type, 
        fits_date, 
        fits_target,                                               
        fits_instrument,
        fits_temperature, 
        fits_filter, 
        fits_telescope);
      save_ccd_temperature();  
    }   
    
    /* Run a script if flag is set */
    
    if (scripting == TRUE)
    {
      if (runscript(ccd_script) == -1) 
      {
        fprintf(stderr,"Script returned system error flag\n");
      } 
      else 
      {
        fprintf(stderr,"Script started\n");
      }        
    }   
    
    
    /* Update the status and the displayed file name */
    update_file_name_display();
    update_status_display();
    
    /* Display the frame */
    update_ds9_image(file_name);    

    /* Reset for the next exposure */
    image_exposure_remaining = image_exposure;        
    phase = 0;
    frame_count++;
    
    /* End the sequence when requested frames have been taken */
    if ( frame_count > frame_count_max )
    {
      sequence = 0;
      frame_count = 1;
    
      /* Enable expose button */
      XtSetSensitive(command[0],True);
    
      /* Enable menubar options */
      XtSetSensitive(set_menu,True);
      XtSetSensitive(mode_menu,True);
      XtSetSensitive(type_menu,True);
      expose_menu = TRUE;
    }
    update_all_displays();    
  }   
}

void poll_interval_handler(XtPointer client_data_ptr, XtIntervalId *client_id) 
{
    
  /* Manage the camera, file storage, image display, and user inferface updates */ 
  
  camera();
            
  /* Start the timer again.  It will return to the handler after poll_interval. */

  poll_interval_id = XtAppAddTimeOut(context, poll_interval,
    poll_interval_handler, poll_interval_data_ptr);
}  

/* Provide filter name for a filter number */

int get_filter_name(int new_filter_number, char filter_name[24])
{
  int rflag;
  
  if ( filter_wheel == NO_FILTER_WHEEL )
  {
    strcpy(filter_name,"Open"); 
    return(1);
  }
  
  
  if ( filter_wheel == INT_FILTER_WHEEL )
  {
    rflag = GetCameraFilterName(new_filter_number, filter_name);
    return(rflag);
  }
  
  if ( filter_wheel != EXT_FILTER_WHEEL )
  {
    return(0);
  }  
  
  if ( ( new_filter_number >=1 ) && ( new_filter_number <= filter_max ) )
  { 
    strcpy(filter_name,filter_label[new_filter_number - 1]);
    return(1);
  }
  return(0);
}

/* Replace a filter name with a new one for positive flag    */
/* Restore defaults for zero or negative flag                */
/* Return 1: success                                         */ 
/* Return 0: if filter wheel not present no action taken     */
/* Return 0: out of bounds filter_number no action taken     */
/* Use default filter name if incoming name is too long      */
/* Filter number is the internal number starting from 0      */

int set_filter_name(int flag, 
  int new_filter_number, char *filter_name)
{
  int i;
  int rflag;
  
  if ( filter_wheel == NO_FILTER_WHEEL )
  {
    return(0);
  }
  
  
  if ( filter_wheel == INT_FILTER_WHEEL )
  {
    rflag = SetCameraFilterName(flag, new_filter_number, filter_name);
    return(rflag);
  }
  
  if ( filter_wheel != EXT_FILTER_WHEEL )
  {
    return(0);
  }  
  
    
  if ( flag > 0 )
  {
    
    /* Save new filter name */
            
    i = new_filter_number;
    if ( (i >= 0) & (i < filter_max) )
    { 
      if ( strlen(filter_name) < 21 )
      {
        strcpy(filter_label[i],filter_name);
      }
      else
      {
        strcpy(filter_label[i],default_filter_label[i]);
      }
    }
    
  }
  else
  {
    
    /* Restore defaults */
    
    filter_max = FILTER_MAX;
      
    for (i=0; i< filter_max_gui; i++)
    {
      strcpy(filter_label[i],default_filter_label[i]);  
    }
  }
  return(1);
}


/* Request filter change                                     */
/* Filter numbers are from 1 to filter_max                   */
/* Sends request to system script for generic filter support */

int set_filter(int filter)
{
  char cmdstr[256];
  /* Respond to request if the filter wheel is available */
  
  int rflag;
     
  if ( filter_wheel == NO_FILTER_WHEEL )
  {
    return(0);
  }
       
  if ( filter_wheel == INT_FILTER_WHEEL )
  {
    rflag = SetCameraFilter(filter);
    return(rflag);
  }  
  
  if ( filter_wheel != EXT_FILTER_WHEEL )
  {
    return(0);
  }  

  /* Execute external set_camera_filter */
  
  sprintf(cmdstr,"set_camera_filter %d",filter);
  system(cmdstr);

  /* Pause to allow time for wheel to set */
  
  usleep(5000000);
  
  return(1);

}

/* Run an external script */
/* Return system flag */

int runscript(char *script)
{
  int flag;
  char cmdstr[256];

  /* Run the external script by using a system command */

  sprintf(cmdstr,"%s &",script);   
  flag = system(cmdstr);

  return (flag);
}


/* Read and parse a configuration file */

void read_config(void)
{
  char configstr[121];
  char *configptr = configstr;
  int n;
  char filter_name[24];
    
  fp_config = fopen(configfile, "r");
  
  if ( fp_config == NULL )
  {
    fprintf(stderr,"New camera configuration not found.\n");
    fprintf(stderr,"Using default camera parameters.\n");
    return;
  }
  else
  {
    fprintf(stderr,"Camera parameters redefined.\n");
  }
   
  while ( configstr == fgets(configstr,80,fp_config) )
  {

    configptr = strstr(configstr,"ccd.frame_count_multi");
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%d",&frame_count_multi); 
        if (frame_count_multi < 2)
        {
          frame_count_multi = 2;
          fprintf(stderr,"Configuration error: attempt to set multi frame count < 2\n");
        }
        else
        {       
          fprintf(stderr,"Multi mode frame count: %d\n",frame_count_multi);
        }
      }  
    }
    
    configptr = strstr(configstr,"ccd.filter_wheel");
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%d",&filter_wheel); 
        if (filter_wheel > 2)
        {
          filter_wheel = 0;
          fprintf(stderr,"Configuration error: attempt to set filter wheel outside range\n");
        }
        else
        {       
          fprintf(stderr,"Filter wheel: %d\n",filter_wheel);
        }
      }  
    } 

    configptr = strstr(configstr,"ccd.filter_max");
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%d",&filter_max); 
        if (filter_max > filter_max_gui)
        {
          filter_max = 0;
          fprintf(stderr,"Configuration error: attempt to set max filter count above %d\n", filter_max_gui);
        }
        else if (filter_max < 0)
        {
          filter_max = 0;
          fprintf(stderr,"Configuration error: attempt to set max filter count < 0\n");       
        }
        else
        {
          fprintf(stderr,"Max filter count:  %d\n",filter_max);
        }
      }  
    } 
        
    configptr = strstr(configstr,"ccd.filter_01");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(filter_name,configptr,21);
        n=strlen(filter_name);
        if ((n >0 ) && (n < 21))
        {  
          filter_name[n-1]='\0';
          set_filter_name(1, 0, filter_name);
          update_filter_menu(0);
        }
        fprintf(stderr,"Filter 1: %s\n",filter_name);
      }  
    }
  
    configptr = strstr(configstr,"ccd.filter_02");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(filter_name,configptr,21);
        n=strlen(filter_name);
        if ((n >0 ) && (n < 21))
        {  
          filter_name[n-1]='\0';
          set_filter_name(1, 1, filter_name);
          update_filter_menu(1);
        }
        fprintf(stderr,"Filter 2: %s\n",filter_name);
      }  
    }

    configptr = strstr(configstr,"ccd.filter_03");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(filter_name,configptr,21);
        n=strlen(filter_name);
        if ((n >0 ) && (n < 21))
        {  
          filter_name[n-1]='\0';
          set_filter_name(1, 2, filter_name);
          update_filter_menu(2);
        }
        fprintf(stderr,"Filter 3: %s\n",filter_name);
      }  
    }

    configptr = strstr(configstr,"ccd.filter_04");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(filter_name,configptr,21);
        n=strlen(filter_name);
        if ((n >0 ) && (n < 21))
        {  
          filter_name[n-1]='\0';
          set_filter_name(1, 3, filter_name);
          update_filter_menu(3);
        }
        fprintf(stderr,"Filter 4: %s\n",filter_name);
      }  
    }

    configptr = strstr(configstr,"ccd.filter_05");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(filter_name,configptr,21);
        n=strlen(filter_name);
        if ((n >0 ) && (n < 21))
        {  
          filter_name[n-1]='\0';
          set_filter_name(1, 4, filter_name);
          update_filter_menu(4);
        }
        fprintf(stderr,"Filter 5: %s\n",filter_name);
      }  
    }

    configptr = strstr(configstr,"ccd.filter_06");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(filter_name,configptr,21);
        n=strlen(filter_name);
        if ((n >0 ) && (n < 21))
        {  
          filter_name[n-1]='\0';
          set_filter_name(1, 5, filter_name);
          update_filter_menu(5);
        }
        fprintf(stderr,"Filter 6: %s\n",filter_name);
      }  
    }

    configptr = strstr(configstr,"ccd.filter_07");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(filter_name,configptr,21);
        n=strlen(filter_name);
        if ((n >0 ) && (n < 21))
        {  
          filter_name[n-1]='\0';
          set_filter_name(1, 6, filter_name);
          update_filter_menu(6);
        }
        fprintf(stderr,"Filter 7: %s\n",filter_name);
      }  
    }

    configptr = strstr(configstr,"ccd.filter_08");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(filter_name,configptr,21);
        n=strlen(filter_name);
        if ((n >0 ) && (n < 21))
        {  
          filter_name[n-1]='\0';
          set_filter_name(1, 7, filter_name);
          update_filter_menu(7);
        }
        fprintf(stderr,"Filter 8: %s\n",filter_name);
      }  
    }

    configptr = strstr(configstr,"ccd.filter_09");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(filter_name,configptr,21);
        n=strlen(filter_name);
        if ((n >0 ) && (n < 21))
        {  
          filter_name[n-1]='\0';
          set_filter_name(1, 8, filter_name);
          update_filter_menu(8);
        }
        fprintf(stderr,"Filter 9: %s\n",filter_name);
      }  
    }

    configptr = strstr(configstr,"ccd.filter_10");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(filter_name,configptr,21);
        n=strlen(filter_name);
        if ((n >0 ) && (n < 21))
        {  
          filter_name[n-1]='\0';
          set_filter_name(1, 9, filter_name);
          update_filter_menu(9);
        }
        fprintf(stderr,"Filter 10: %s\n",filter_name);
      }  
    }

    configptr = strstr(configstr,"ccd.base_name");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%s",base_name);
        fprintf(stderr,"Base name: %s\n",base_name);
      }  
    } 

    configptr = strstr(configstr,"ccd.fits_telescope");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        strncpy(fits_telescope,configptr,64);
        n=strlen(fits_telescope);
        if ((n >0 ) && (n < 64))
        {  
          fits_telescope[n-1]='\0';
        }
        else
        {
          fits_telescope[0]='\0';
        }        
        fprintf(stderr,"FITS telescope: %s\n",fits_telescope);
      }  
    } 
    configptr = strstr(configstr,"ccd.ccd_script");    
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%s",ccd_script);
        fprintf(stderr,"External script %s\n",ccd_script);
      }  
    }
  
  }
  fclose(fp_config);

}


