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

  

#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/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 <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_name_item;
Widget file_target_item;
Widget file_telescope_item;
Widget file_exit_item;

Widget set_menu;
Widget set_image_exp_item;
Widget set_track_exp_item;
Widget set_image_number_item;
Widget set_frame_count_multi_item;
Widget set_image_region_item;
Widget set_track_region_item;
Widget set_temperature_item;

Widget filter_menu;
Widget filter_item[10];

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

Widget detector_menu;
Widget detector_image_item;
Widget detector_track_item;

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


Widget guide_menu;
Widget guide_unguided_item;
Widget guide_tracking_item;
Widget guide_optical_item;
Widget guide_self_tracking_item;
Widget guide_self_optical_item;

Widget toplevel;

Widget file_name_dialog;
Widget file_target_dialog;
Widget file_telescope_dialog;
Widget image_exposure_dialog;
Widget track_exposure_dialog;
Widget image_number_dialog;
Widget frame_count_multi_dialog;
Widget image_region_dialog;
Widget track_region_dialog;
Widget temperature_dialog;

Widget button[3][3];
Widget command[3];
Widget toggle[2];
Widget label_info[8];
Widget show_info[8];
Widget form;
Widget sep_btns;
Widget sep_status;
Widget radio_box;

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 trk_name[][15] = {
  "Tracking",
  "Optical"
 };

static char infotext[][24] = {
  "Track exposure",
  "Image exposure",
  "Track region",
  "Image region",
  "CCD temperature",
  "File name",
  "Filter",
  "Status"
};

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

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



void create_menus(Widget menu_bar);  /* Creates all menus                   */
void menuCB();                     /* Callback for all pull-down menus      */
void file_name_dialogCB();         /* Callback for file base name dialog    */
void file_target_dialogCB();       /* Callback for file target dialog       */
void file_telescope_dialogCB();    /* Callback for file telescope dialog    */
void image_exposure_dialogCB();    /* Callback for image exposure dialog    */
void track_exposure_dialogCB();    /* Callback for track exposure dialog    */
void image_number_dialogCB();      /* Callback for image number dialog      */
void frame_count_multi_dialogCB(); /* Callback for frame count multi dialog */
void image_region_dialogCB();      /* Callback for image region dialog      */
void track_region_dialogCB();      /* Callback for track region dialog      */
void temperature_dialogCB();       /* Callback for temperature dialog       */

void buttonCB();           /* Callback function for the paddle buttons      */
void feedbackCB();         /* Callback function for the feedback toggles    */
void commandCB();          /* Callback function for command buttons         */

/* Manage the camera */

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

/* Parameter io */

void input_image_time();          /* Set image exposure time             */
void input_track_time();          /* Set track exposure time             */
void input_image_number();        /* Set image number                    */
void input_frame_count_multi();   /* Set maximum multiframe count        */
void input_image_region();        /* Set image region                    */
void input_track_region();        /* Set track region                    */
void input_frame_count_multi();   /* Set maximum multiframe count        */
void input_setpoint();            /* Set CCD temperature                 */
void input_filter();              /* Select filter                       */


/* Parameter reporting */

void set_status_buffer(void);
void update_image_exposure_display();     /* Display image exposure time  */
void update_track_exposure_display();     /* Display track exposure time  */
void update_image_region_display();       /* Display image region         */
void update_track_region_display();       /* Display track 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          */

/* 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  */
int send_image(char *name);          /* Network file transfer             */


/* 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  guide = UNGUIDED;
int  guide_log_flag = FALSE;
int  guide_flag = TRUE;
int  panelguide = TRACKING;
int  detector = IMAGE;
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  transfer = NOCOPY;
int  image_number = 1;
int  startup = FALSE;
int  expose_menu = TRUE;

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];
char fits_filter[64];
char fits_telescope[64];

/* Fits header exposure start time */

time_t date_obs;

/* 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 CaptureImage(int *phase,  unsigned short *data,
  int frame, double exposure, int region, 
  int x, int y, int width, int height);
  
extern int CaptureTrack(int *phase, unsigned short *data,
  int frame, double exposure, int region, 
  int x, int y, int width, int height);
    
extern int CaptureGuide(int *phase,
  unsigned short *image_data, 
  unsigned short *track_data,
  double exposure_i, int subarea_i, 
  int x_i, int y_i, int width_i, int height_i,
  double exposure_t, int subarea_t, 
  int x_t, int y_t, int width_t, int height_t,
  int guide, int guide_flag, int log_flag);
  
extern int CaptureEnd(int *phase);  

extern int GuideImage(unsigned short *data,
  int width, int height,
  int guide, int guide_flag, int guide_log_flag); 
       
extern void WriteFits(char *filename, int w, int h, unsigned short *data, 
  double obs_duration, 
  char*  obs_type, 
  char*  obs_date, 
  char*  obs_target,
  char*  obs_instrument, 
  char*  obs_filter, 
  char*  obs_telescope);    
 
extern int OpenCamera(int device);     

extern int CloseCamera(int device);    

extern int GetCameraInfo(int *type, int *wheel, int *filter_max,
 int *imagewidth, int *image_height, double *image_gain,
 int *trackwidth, int *track_height, double *track_gain);
 
extern int GetCameraFilter(char *filter_name, int *filter);

extern int GetCameraStatus(int *image_status, int *track_status);

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

extern int SetCameraTemperature(int state, double temperature);

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

extern int SetCameraFilter(int filter);

extern int SetCameraAOTipTilt(int ao_x, int ao_y);

extern int ActivateCameraRelay(int relay, double ontime);

extern int SetCameraGuideParameters(int threshold, 
  double x2trackx, double y2trackx, double x2tracky, double y2tracky,
  double x2aox, double y2aox, double x2aoy, double y2aoy);



/* Camera variables */

unsigned short *image_data, *track_data;

int    camera_type;
double current_exposure;
int    image_width = IMAGEXMAX;
int    image_height = IMAGEYMAX;
double image_exposure = 1.;
double image_exposure_remaining = 1.;
double image_gain;
int    image_region = 0;
int    image_region_x = 1;
int    image_region_y = 1; 
int    image_region_w = IMAGEXMAX;
int    image_region_h = IMAGEYMAX;

int    track_width = TRACKXMAX;;  
int    track_height = TRACKYMAX;
double track_exposure = .4;
double track_exposure_remaining = .4;
double track_gain;
int    track_region = 0;
int    track_region_x = 1;
int    track_region_y = 1; 
int    track_region_w = TRACKXMAX;
int    track_region_h = TRACKYMAX; 

/* Guide parameters should be changed in a prefs file */
/* Reset through SetCameraGuideParameters to have effect */

int    threshold=100;
double x2aox=10.0;
double y2aox=0.0;
double x2aoy=0.0;
double y2aoy=10.0;
double x2trackx=10.0;
double y2trackx=0.0;
double x2tracky=0.0;
double y2tracky=-10.0;

int    region_x = 1;
int    region_y = 1;
int    region_w = 512;
int    region_h = 512;
int    region_r = 256;
 
double declination;
double right_ascension;
double image_scale = ARCSEC_PER_PIXEL;

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

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

int    ao_x=2048;
int    ao_y=2048;

int    tiptilt = EAST;
int    relay = EAST;
double relay_ontime = RELAYONTIME;

/* GUI main starts here */

int main(int argc, char *argv[])
{
  Arg al[10];
  int ac;
  int 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 */

  /* Create a radio box to hold the feedback toggles and attach it to form */
  
  ac=0;
  XtSetArg(al[ac],XmNorientation,XmHORIZONTAL); ac++;
  XtSetArg(al[ac],XmNtopAttachment,XmATTACH_POSITION); ac++;
  XtSetArg(al[ac],XmNtopPosition,5); ac++; 
  XtSetArg(al[ac],XmNleftAttachment,XmATTACH_POSITION); ac++;
  XtSetArg(al[ac],XmNleftPosition,30); ac++; 
  XtSetArg(al[ac],XmNrightAttachment,XmATTACH_POSITION); ac++;
  XtSetArg(al[ac],XmNrightPosition,90); ac++; 
  radio_box=XmCreateRadioBox(form,"radio_box",al,ac);
  XtManageChild(radio_box);

  /* Create feedback toggles */
  
  for (x=0; x<2; x++)
  {
    ac=0;
    XtSetArg(al[ac],XmNlabelString,
        XmStringCreate(trk_name[x],char_set)); ac++;
    toggle[x]=XmCreateToggleButton(radio_box,"toggle",al,ac);
    XtManageChild(toggle[x]);
    XtAddCallback (toggle[x], 
      XmNvalueChangedCallback, feedbackCB, (int *) x);
  }     

  /* Set the tracking togglebutton to initial value of guide_button */

  XmToggleButtonSetState (toggle[0], True, False);

  /* 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,12+y*9); ac++;
        XtSetArg(al[ac],XmNbottomAttachment,
            XmATTACH_POSITION); ac++;
        XtSetArg(al[ac],XmNbottomPosition, 20+y*9); ac++;
        button[x][y]=XmCreatePushButton(form,"label",al,ac);
        mask = ButtonReleaseMask | ButtonPressMask;
        XtAddEventHandler(button[x][y],mask,False,
            buttonCB,(int *) 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<4; 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,44+y*6); ac++;
      XtSetArg(al[ac],XmNbottomAttachment,
        XmATTACH_POSITION); ac++;
      XtSetArg(al[ac],XmNbottomPosition,46+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<4; 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,46+y*6); ac++;
      XtSetArg(al[ac],XmNbottomAttachment,
        XmATTACH_POSITION); ac++;
      XtSetArg(al[ac],XmNbottomPosition,48+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,(int *) cmd_number);
  }

  /* Create prompt dialogs and leave them unmanaged until needed */
  
  /* 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++;
  file_name_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(file_name_dialog, XmNokCallback, file_name_dialogCB, 
    (int *) OK);
  XtAddCallback(file_name_dialog, XmNcancelCallback, file_name_dialogCB, 
    (int *) CANCEL);
  XtUnmanageChild (XmSelectionBoxGetChild (file_name_dialog, 
    XmDIALOG_HELP_BUTTON));

  /* File target */

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

  /* File telescope */

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

  /* 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++;
  image_exposure_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(image_exposure_dialog, XmNokCallback, image_exposure_dialogCB, (int *) OK);
  XtAddCallback(image_exposure_dialog, XmNcancelCallback, image_exposure_dialogCB, (int *) CANCEL);
  XtUnmanageChild (XmSelectionBoxGetChild (image_exposure_dialog, XmDIALOG_HELP_BUTTON));
 
  /* Track exposure */

  ac=0;
  XtSetArg(al[ac], XmNselectionLabelString,
    XmStringCreateLtoR("Enter time (s): ",char_set)); ac++;
  XtSetArg(al[ac], XmNdialogTitle,
    XmStringCreateLtoR("Tracking CCD Exposure",char_set)); ac++;
  track_exposure_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(track_exposure_dialog, XmNokCallback, track_exposure_dialogCB, (int *) OK);
  XtAddCallback(track_exposure_dialog, XmNcancelCallback, track_exposure_dialogCB, (int *) CANCEL);
  XtUnmanageChild (XmSelectionBoxGetChild (track_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++;
  image_number_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(image_number_dialog, XmNokCallback, image_number_dialogCB, (int *) OK);
  XtAddCallback(image_number_dialog, XmNcancelCallback, image_number_dialogCB, (int *) CANCEL);
  XtUnmanageChild (XmSelectionBoxGetChild (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++;
  frame_count_multi_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(frame_count_multi_dialog, XmNokCallback, frame_count_multi_dialogCB, (int *) OK);
  XtAddCallback(frame_count_multi_dialog, XmNcancelCallback, frame_count_multi_dialogCB, (int *) CANCEL);
  XtUnmanageChild (XmSelectionBoxGetChild (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++;
  image_region_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(image_region_dialog, XmNokCallback, image_region_dialogCB, (int *) IMAGE_SUBAREA_ON);
  XtAddCallback(image_region_dialog, XmNcancelCallback, image_region_dialogCB, (int *) CANCEL);
  XtAddCallback(image_region_dialog, XmNhelpCallback, image_region_dialogCB, (int *) IMAGE_SUBAREA_OFF);

  /* Track region */

  ac=0;
  XtSetArg(al[ac], XmNselectionLabelString,
    XmStringCreateLtoR("Enter track x,y,w,h : ",char_set)); ac++;
  XtSetArg(al[ac], XmNdialogTitle,
    XmStringCreateLtoR("Track Region",char_set)); ac++;
  XtSetArg(al[ac], XmNokLabelString,
    XmStringCreateLtoR("On",char_set)); ac++;
  XtSetArg(al[ac], XmNhelpLabelString,
    XmStringCreateLtoR("Off",char_set)); ac++;
  track_region_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(track_region_dialog, XmNokCallback, track_region_dialogCB, (int *) TRACK_SUBAREA_ON);
  XtAddCallback(track_region_dialog, XmNcancelCallback, track_region_dialogCB, (int *) CANCEL);
  XtAddCallback(track_region_dialog, XmNhelpCallback, track_region_dialogCB, (int *) TRACK_SUBAREA_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++;
  temperature_dialog=XmCreatePromptDialog(toplevel,"dialog",al,ac);
  XtAddCallback(temperature_dialog, XmNokCallback, temperature_dialogCB, (int *) COOL_ON);
  XtAddCallback(temperature_dialog, XmNcancelCallback, temperature_dialogCB, (int *) CANCEL);
  XtAddCallback(temperature_dialog, XmNhelpCallback, temperature_dialogCB, (int *) COOL_OFF);


  /* Create the menubar */

  create_menus(menu_bar); 

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

     
  if ( track_width <=1 )
  {
    XtSetSensitive(set_track_exp_item,False);
    XtSetSensitive(set_track_region_item,False);
    XtSetSensitive(detector_track_item,False);
    XtSetSensitive(guide_self_tracking_item,False);
    XtSetSensitive(guide_self_optical_item ,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(item_name,client_data,menu)
  char *item_name;
  int 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(item_name,client_data,menu)
  char *item_name;
  int 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,(int *) client_data);      
  XtSetSensitive(item,True);
  return(item);
}

/* Create a menu on the menu bar */

Widget make_menu(menu_name,menu_bar)
  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_name_item = make_menu_item("Base name",FILENAME,file_menu);
  file_target_item = make_menu_item("Target",FILETARGET,file_menu);
  file_telescope_item = make_menu_item("Telescope",FILETELESCOPE,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_exp_item = make_menu_item("Image exposure",SETIMAGEEXP,set_menu);
  set_track_exp_item = make_menu_item("Track exposure",SETTRACKEXP,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_image_region_item = make_menu_item("Image region",SETIMAGESUBAREA,set_menu);
  set_track_region_item = make_menu_item("Track region",SETTRACKSUBAREA,set_menu);
  set_temperature_item = make_menu_item("CCD temperature",SETTEMPERATURE,set_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 detector menu */
  detector_menu = make_menu("Detector",menu_bar);
  detector_image_item = make_menu_item_toggle("Image",DETECTORIMAGE,detector_menu);
  detector_track_item = make_menu_item_toggle("Track",DETECTORTRACK,detector_menu);
       
  /* Add radio behavior to the detector menu */
  ac=0;
  XtSetArg(al[ac],XmNradioBehavior,True); ac++;
  XtSetValues(detector_menu,al,ac);
  
  /* Set the initial detector_menu button */
  XmToggleButtonSetState(detector_image_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 a toggle button guide menu */
  guide_menu = make_menu("Guide",menu_bar);
  guide_unguided_item = make_menu_item_toggle("Unguided",GUIDEUNGUIDED,guide_menu);
  guide_tracking_item = make_menu_item_toggle("Tracking",GUIDETRACKING,guide_menu);
  guide_optical_item = make_menu_item_toggle("Optical",GUIDEOPTICAL,guide_menu);
  guide_self_tracking_item = make_menu_item_toggle("Self Tracking",GUIDESELFTRACKING,guide_menu);
  guide_self_optical_item = make_menu_item_toggle("Self Optical",GUIDESELFOPTICAL,guide_menu);
      
  /* Add radio behavior to the guide menu */
  ac=0;
  XtSetArg(al[ac],XmNradioBehavior,True); ac++;
  XtSetValues(guide_menu,al,ac);
  
  /* Set the initial guide_menu button */
  XmToggleButtonSetState(guide_unguided_item, True, False);

}

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

void menuCB(w,client_data,call_data)
  Widget w;
  int client_data;
  XmAnyCallbackStruct *call_data;
{
  int i;  
  if (client_data==FILENAME)
  {
    XtManageChild(file_name_dialog);
  } 
   
  if (client_data==FILETARGET)
  {
    XtManageChild(file_target_dialog);
  } 

  if (client_data==FILETELESCOPE)
  {
    XtManageChild(file_telescope_dialog);
  }

  if (client_data==FILEEXIT)   
  {
    free(image_data);
    free(track_data);
    exit(0);
  }
  
  if (client_data==SETIMAGEEXP)
  {
    XtManageChild(image_exposure_dialog);
  }
  
  if (client_data==SETTRACKEXP) 
  {
    XtManageChild(track_exposure_dialog);
  }
  
  if (client_data==SETIMAGENUMBER) 
  {
    XtManageChild(image_number_dialog);
  }   
  
  if (client_data==SETFRAMECOUNTMULTI) 
  {
    XtManageChild(frame_count_multi_dialog);
  }  
  
  
  if (client_data==SETIMAGESUBAREA)
  {
    XtManageChild(image_region_dialog);
  }
  
  if (client_data==SETTRACKSUBAREA) 
  {
    XtManageChild(track_region_dialog);
  }

  if (client_data==SETTEMPERATURE) 
  {
    XtManageChild(temperature_dialog);
  }

  for (i=0; i<filter_max; i++)
  {
    if ( ( client_data == (FILTER1 + i)) && (filter_max > i) )
    {
      if( XmToggleButtonGetState(filter_item[i]) )
      {
        filter = i + 1;
        SetCameraFilter(filter);
        GetCameraFilter(filter_name, &filter);
        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==DETECTORIMAGE)
  {
    if( XmToggleButtonGetState(detector_image_item) )
    {
      detector = IMAGE;
    }
  }
  
  if (client_data==DETECTORTRACK)
  {
    if( XmToggleButtonGetState(detector_track_item) )
    {
      detector = TRACK;
    }
  }

  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==GUIDEUNGUIDED)
  {
    if( XmToggleButtonGetState(guide_unguided_item) )
    {
      guide = UNGUIDED;
    }
  }

  if (client_data==GUIDETRACKING)
  {
    if( XmToggleButtonGetState(guide_tracking_item) )
    {
      guide = TRACKING;
    }
  }

  if (client_data==GUIDEOPTICAL)
  {
    if( XmToggleButtonGetState(guide_optical_item) )
    {
      guide = OPTICAL;
    }
  }

  if (client_data==GUIDESELFTRACKING)
  {
    if( XmToggleButtonGetState(guide_self_tracking_item) )
    {
      guide = SELFTRACKING;
    }
  }  

  if (client_data==GUIDESELFOPTICAL)
  {
    if( XmToggleButtonGetState(guide_self_optical_item) )
    {
      guide = SELFOPTICAL;
    }
  }  
  

  /* Take no action on cancel */
  
  if (client_data==CANCEL); 

}

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

void file_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 file_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 file_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 image exposure time */

void 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 < IMGMINEXP )
      {
        image_exposure = IMGMINEXP;
      }
      if ( image_exposure > IMGMAXEXP )
      {
        image_exposure = IMGMAXEXP;
      }
      image_exposure_remaining = image_exposure;
      XtFree(s);
      break;
    case CANCEL:
      break;
  }
  update_image_exposure_display();
  XtUnmanageChild(w);
}

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

void track_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);
      track_exposure = atof(s);
      if ( track_exposure < TRKMINEXP )
      {
        track_exposure = TRKMINEXP;
      }
      if ( track_exposure > TRKMAXEXP )
      {
        track_exposure = TRKMAXEXP;
      }
      track_exposure_remaining = track_exposure;
      XtFree(s);
      break;
    case CANCEL:
      break;
  }
  update_track_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 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 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 image_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 IMAGE_SUBAREA_ON:

      /* Set the switch on */
      
      image_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_width )
        {
          in_x = image_width;
        }       
        if (in_y < 1)
        {
          in_y = 1;
        }
        if (in_y > image_height)
        {
          in_y = image_height;
        }       
        if (in_w < 1)
        {
          in_w = 1;
        }
        if (in_w > image_width)
        {
          in_w = image_width;
        }      
        if (in_h < 1)
        {
          in_h = 1;
        }
        if (in_h > image_height)
        {
          in_h = image_height;
        }      
        if (in_x + in_w > image_width + 1)
        {
          in_w = image_width - in_x + 1;
        }
        if (in_y + in_h > image_height + 1)
        {
          in_h = image_height - in_y + 1;
        }           
        image_region_x = in_x;
        image_region_y = in_y;
        image_region_w = in_w;
        image_region_h = in_h;
      }
      XtFree(s); 
      break;

    case IMAGE_SUBAREA_OFF:
      
      /* Set the switch off */
      
      image_region = FALSE;
      image_region_x = 1;
      image_region_y = 1;
      image_region_w = image_width;
      image_region_h = image_height;
      
      break;  

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

/* Dialog for user input of track region */

void track_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 TRACK_SUBAREA_ON:

      /* Set the switch on */
      
      track_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 > track_width )
        {
          in_x = track_width;
        }       
        if (in_y < 1)
        {
          in_y = 1;
        }
        if (in_y > track_height)
        {
          in_y = track_height;
        }       
        if (in_w < 1)
        {
          in_w = 1;
        }
        if (in_w > track_width)
        {
          in_w = track_width;
        }      
        if (in_h < 1)
        {
          in_h = 1;
        }
        if (in_h > track_height)
        {
          in_h = track_height;
        }      
        if (in_x + in_w > track_width + 1)
        {
          in_w = track_width - in_x + 1;
        }
        if (in_y + in_h > track_height + 1)
        {
          in_h = track_height - in_y + 1;
        }      
        track_region_x = in_x;
        track_region_y = in_y;
        track_region_w = in_w;
        track_region_h = in_h;
      }
      XtFree(s); 
      break;
 
    case TRACK_SUBAREA_OFF:
      track_region = FALSE;
      track_region_x = 1;
      track_region_y = 1;
      track_region_w = track_width;
      track_region_h = track_height;
      break;

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

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

void 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 radio buttons that select guide grid function */

void feedbackCB(w,client_data,call_data)
    Widget w;
    int client_data;
    XmAnyCallbackStruct *call_data;
{
    int n;

    /* Identify the radio button */
    n=client_data;
       
    /* Assign functionality to the panel guide pushbuttons */

    switch (n) 
    {
       /* Pushbuttons control relays on the drive tracking system */    
       case 0: 
         panelguide=TRACKING;
         break; 
          
       /* Pushbuttons control the adaptive optics */
       case 1: 
         panelguide=OPTICAL;
         break;                   
    } 
}

/* 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;
   
  /* identify the button number n = 3*x+y */
  n=client_data;

  /* identify the action */
  
  dn = et == ButtonPress;
  up = et == ButtonRelease;
      
  /* respond to the request */
  
  if (panelguide == TRACKING)
  {
    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 the telescope to respond before accepting another cycle */

      usleep(100000);
    }
  }
  else if (panelguide == OPTICAL)
  {
    if(dn)
    {
      switch (n) 
      {
     
        /* NoOp */
        case 0:
        break;

        /* E */
        case 1: 

        break;

        /* NoOp */
        case 2: 
        break;

        /* N */
        case 3: 

        break;

        /* NoOp */
        case 4:
        break;

        /* S */
        case 5: 

        break;

        /* NoOp */
        case 6:
        break;

        /* W */
        case 7:

        break;

        /* NoOp */
        case 8: 
        break;    

      }
          
    }
    
    if(up)
    {     
      /* wait for the telescope to respond before accepting another cycle */

      usleep(100000);
    }
  }

}


/* Callback for the camera command buttons */

void commandCB(w,client_data,call_data)
  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 (detector == IMAGE)
      {
        if ( region_flag >= 3 )
        {
          /* Test and clip boundaries */
          if (region_x < 1)
          {
            region_x = 1;
          }
          if (region_x > image_width )
          {
            region_x = image_width;
          }       
          if (region_y < 1)
          {
            region_y = 1;
          }
          if (region_y > image_height)
          {
            region_y = image_height;
          }       
          if (region_w < 1)
          {
            region_w = 1;
          }
          if (region_w > image_width)
          {
            region_w = image_width;
          }      
          if (region_h < 1)
          {
            region_h = 1;
          }
          if (region_h > image_height)
          {
            region_h = image_height;
          }      
          if (region_x + region_w > image_width + 1)
          {
            region_w = image_width - region_x + 1;
          }
          if (region_y + region_h > image_height + 1)
          {
            region_h = image_height - region_y + 1;
          }           
          image_region = TRUE;
          image_region_x = region_x;
          image_region_y = region_y;
          image_region_w = region_w;
          image_region_h = region_h;
        }
        else
        {
          image_region = FALSE;
          image_region_x = 1;
          image_region_y = 1;
          image_region_w = image_width;
          image_region_h = image_height;
        }
      }
      else if (detector == TRACK)
      {
        if ( region_flag >= 3 )
        {
          /* Test and clip boundaries */
          if (region_x < 1)
          {
            region_x = 1;
          }
          if (region_x > track_width )
          {
            region_x = track_width;
          }       
          if (region_y < 1)
          {
            region_y = 1;
          }
          if (region_y > track_height)
          {
            region_y = track_height;
          }       
          if (region_w < 1)
          {
            region_w = 1;
          }
          if (region_w > track_width)
          {
            region_w = track_width;
          }      
          if (region_h < 1)
          {
            region_h = 1;
          }
          if (region_h > track_height)
          {
            region_h = track_height;
          }      
          if (region_x + region_w > track_width + 1)
          {
            region_w = track_width - region_x + 1;
          }
          if (region_y + region_h > track_height + 1)
          {
            region_h = track_height - region_y + 1;
          }           
          track_region = TRUE;
          track_region_x = region_x;
          track_region_y = region_y;
          track_region_w = region_w;
          track_region_h = region_h;
        }
        else
        {
          track_region = FALSE;
          track_region_x = 1;
          track_region_y = 1;
          track_region_w = track_width;
          track_region_h = track_height;        
        }
      }
      update_all_displays();
      break;
  }
  update_all_displays();
}


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

void update_track_exposure_display()
{
  XmString xmstr;
  Arg al[10];
  int ac;
  
  if ( track_width > 0 )
  {
    sprintf(buf,"%4.3lf",track_exposure_remaining);
  }
  else
  {
    sprintf(buf,"     ");  
  } 

  xmstr = XmStringCreateLtoR(buf,char_set);
  ac=0;
  XtSetArg(al[ac],XmNlabelString,xmstr); ac++;
  XtSetValues(show_info[0],al,ac);   
  XmStringFree(xmstr);
}    

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

void update_image_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 track region selection in the window */

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

  if ( track_width > 0 )
  {
    if ( track_region == TRUE )
    {
    sprintf(buf,"%d %d %d %d [On]",
      track_region_x, track_region_y, track_region_w, track_region_h);
    }
    else
    {
    sprintf(buf,"%d %d %d %d [Off]",
      track_region_x, track_region_y, track_region_w, track_region_h);
    }
  }
  else
  {
    sprintf(buf,"                  ");
  }
  xmstr = XmStringCreateLtoR(buf,char_set);
  ac=0;
  XtSetArg(al[ac],XmNlabelString,xmstr); ac++;
  XtSetValues(show_info[2],al,ac);   
  XmStringFree(xmstr);
} 

   
/* Update image region selection in the window */

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

  if ( image_region == TRUE )
  {
  sprintf(buf,"%d %d %d %d [On]",
    image_region_x, image_region_y, image_region_w, image_region_h);
  }
  else
  {
  sprintf(buf,"%d %d %d %d [Off]",
    image_region_x, image_region_y, image_region_w, image_region_h);
  }
  
  xmstr = XmStringCreateLtoR(buf,char_set);
  ac=0;
  XtSetArg(al[ac],XmNlabelString,xmstr); ac++;
  XtSetValues(show_info[3],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[4],al,ac);   
  XmStringFree(xmstr);
} 

/* 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[5],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[6],al,ac);   
  XmStringFree(xmstr);
} 

/* Update filter name in the drop down menu */

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

  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[7],al,ac);   
  XmStringFree(xmstr);
}   
 
/* Update all display buffers */

void update_all_displays()
{
  update_status_display();
  update_temperature_display();
  update_filter_display();
  update_image_exposure_display();
  update_track_exposure_display();
  update_image_region_display();
  update_track_region_display();
}   

/* User input of tracking ccd exposure time */

void input_track_time() 
{
  XtManageChild(track_exposure_dialog);
}

/* User input of image ccd exposure time */

void input_image_time() 
{
  XtManageChild(image_exposure_dialog);
}

/* User input of multiframe count max */

void input_frame_count_multi() 
{
  XtManageChild(frame_count_multi_dialog);
}

/* User input of ccd temperature setpoint */

void input_setpoint() 
{
  XtManageChild(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 (detector == IMAGE )
    {
      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 if (detector == TRACK)
    {  
      if(acquire == SINGLE)
      {
        if (frame == DARK)
        {
          sprintf(status_buf,"Single track dark");
        }
        else if (frame == BIAS)
        {
          sprintf(status_buf,"Single track bias");
        }
        else if (frame == FLAT)
        {
          sprintf(status_buf,"Single track flat");
        }
        else
        {
          sprintf(status_buf,"Single track");
        }  
      }
      else if(acquire == FOCUS)
      {
        if (frame == DARK)
        {
          sprintf(status_buf,"Focus track dark");
        }
        else if (frame == BIAS)
        {
          sprintf(status_buf,"Focus track bias");
        }
        else if (frame == FLAT)
        {
          sprintf(status_buf,"Focus track flat");
        }
        else
        {
          sprintf(status_buf,"Focus track");
        }               
      }
      else if(acquire == MULTI)
      {
        if (frame == DARK)
        {
          sprintf(status_buf,"Multiple track dark");
        }
        else if (frame == BIAS)
        {
          sprintf(status_buf,"Multiple track bias");
        }
        else if (frame == FLAT)
        {
          sprintf(status_buf,"Multiple track flat");
        }
        else
        {
          sprintf(status_buf,"Multiple track");
        } 
      }
      else 
      {
        sprintf(status_buf,"Track");
      }
    }
  }  
  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);
          region_r = (region_h + region_w)/2;  
        }
        if (j == 3)  
        {
          region_x = (int) (xc - dx);
          region_y = (int) (yc - dx);
          region_w = (int) (dx + dx);
          region_h = region_w;
          region_r = 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 1 if sent, 0 if not sent */

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)
{
    
  /* Initialize the driver for a generic USB camera */

  startup = OpenCamera(USB);

  /* Alert stderr to a problem and exit */
  
  if (startup != TRUE)
  {
    fprintf(stderr,"Camera not found\n");
    exit(1);
  }
  
  /* Contact the camera and allow its driver to update parameters */
  
  startup = GetCameraInfo(&camera_type, &filter_wheel, &filter_max, 
    &image_width, &image_height, &image_gain,
    &track_width, &track_height, &track_gain);
        
  /* Set base_name to default */
  
  strcpy(base_name,"image_");

  /* Handle the configuration file to change other defaults */
  
  read_config();

  /* Set fits instrument header for camera */

  switch (camera_type) 
  {
    case SIM_CAMERA:
      sprintf(fits_instrument,"Simulated CCD");
      break;
    case SBIG_STL:
      sprintf(fits_instrument,"SBIG STL");
      break;
    case SBIG_ST10:
      sprintf(fits_instrument,"SBIG ST10");
      break;         
    case SBIG_ST9:
      sprintf(fits_instrument,"SBIG ST9");
      break; 
    case SBIG_ST8:
      sprintf(fits_instrument,"SBIG ST8");
      break; 
    case SBIG_ST7:
      sprintf(fits_instrument,"SBIG ST7");
      break; 
    case SBIG:
      sprintf(fits_instrument,"SBIG CCD");
      break;
    case APOGEE:
      sprintf(fits_instrument,"APOGEE CCD");
      break;
  
    default:
      sprintf(fits_instrument,"CCD");        

  }
  
  /* Initialize the other fits headers */
  
  sprintf(fits_type,"                ");
  sprintf(fits_date,"                ");
  sprintf(fits_target,"                ");
  sprintf(fits_filter,"                ");
  sprintf(fits_telescope,"                ");
    
  /* Set the image sizes */

  image_region_x = 1;
  image_region_y = 1; 
  image_region_w = image_width;
  image_region_h = image_height;
  
  
  track_region_x = 1;
  track_region_y = 1; 
  track_region_w = track_width;
  track_region_h = track_height;

      
  /* Alert stderr to a problem and exit */
  
  if (startup != TRUE)
  {
    fprintf(stderr,"Camera did not respond to startup request");
    exit(1);
  }
  
  /* Select filter and save header information */
  
  startup = SetCameraFilter(filter);
  startup = GetCameraFilter(filter_name, &filter);
  sprintf(fits_filter,filter_name);
         
  /* Alert operator to absence of filter wheel or set filter wheel error */
  
  if (startup != TRUE)
  {
    fprintf(stderr,"Filter wheel did not respond to startup request\n");
    filter_wheel = NO_FILTER_WHEEL;
  }

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

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

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

  image_data = malloc((image_width*image_height)*sizeof(unsigned short));    
  track_data = malloc((track_width*track_height)*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 (startup != TRUE)
  {
    fprintf(stderr,"Camera not found");
    exit(1);
  }
}

/* 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;

  /* 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);
    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;
    
      /* Reset the exposure time displays */
      if (detector == TRACK)
      {
        track_exposure_remaining = track_exposure;
      }
      else
      {
        image_exposure_remaining = image_exposure;
      }
      update_all_displays();
    }

    if (frame_count > 1)
    {
      frame_count = 1;
    
      /* Reset the exposure time displays */
      if (detector == TRACK)
      {
        track_exposure_remaining = track_exposure;
      }
      else
      {
        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(detector_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 but allow guide change during sequence */
      XtSetSensitive(set_menu,False);
      XtSetSensitive(mode_menu,False);
      XtSetSensitive(detector_menu,False);
      XtSetSensitive(type_menu,False);
      expose_menu = FALSE;
      update_all_displays();    
    }
    
    /* Note the time it started and save as xtv */
    gettimeofday (&xtv, NULL); 
    
    /* Set the date of observation for the image header */ 
    date_obs = time(NULL);
    strftime(fits_date, 32, "%FT%T", gmtime(&date_obs)); 

    if ( ( (guide == SELFTRACKING) || (guide == SELFOPTICAL) ) && (detector == IMAGE) )
    {
      
      /* Start a self-guided exposure if appropriate */

      CaptureGuide(&phase, 
      image_data, track_data,
      image_exposure, image_region, 
      image_region_x, image_region_y, image_region_w, image_region_h,
      track_exposure, track_region, 
      track_region_x, track_region_y, track_region_w, track_region_h,
      guide, guide_flag, guide_log_flag);
      current_exposure = image_exposure; 
    }
    else 
    {      
            
      if (detector == TRACK)
      {
        if (frame != BIAS)
        {
          current_exposure = track_exposure;
        }
        else
        {
          current_exposure = 0.001;
        } 
        
        CaptureTrack(&phase, track_data,
        frame, current_exposure, track_region, 
        track_region_x, track_region_y, track_region_w, track_region_h);
      }
      else if (detector == IMAGE)
      {
        if (frame != BIAS)
        {
          current_exposure = image_exposure;
        }
        else
        {
          current_exposure = 0.001;
        } 
        
        CaptureImage(&phase, image_data,
        frame, current_exposure, image_region, 
        image_region_x, image_region_y, image_region_w, image_region_h);
      }
    }                
    update_all_displays();
  }  

  /* 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 */
      if (detector == TRACK)
      {
        track_exposure_remaining = track_exposure;
        update_track_exposure_display();
      }
      else
      {
        image_exposure_remaining = image_exposure;
        update_image_exposure_display();
      }

      if ( ( (guide == SELFTRACKING) || (guide == SELFOPTICAL) ) && (detector == IMAGE) )
      {
        CaptureGuide(&phase, 
          image_data, track_data,
          current_exposure, image_region, 
          image_region_x, image_region_y, image_region_w, image_region_h,
          track_exposure, track_region, 
          track_region_x, track_region_y, track_region_w, track_region_h,
          guide, guide_flag, guide_log_flag);  
      }
      else 
      {      
        if (detector == TRACK)
        {
          CaptureTrack(&phase, track_data,
            frame, current_exposure, track_region, 
            track_region_x, track_region_y, track_region_w, track_region_h);
        }
        else if (detector == IMAGE)
        {
          CaptureImage(&phase, image_data,
            frame, current_exposure, image_region, 
            image_region_x, image_region_y, image_region_w, image_region_h);
        }
      }    
    } 
    else 
    {
      /* Exposure is not complete */
      if ( detector == TRACK )
      {
        track_exposure_remaining = current_exposure - dt;
        update_track_exposure_display();
      }
      else
      {
        image_exposure_remaining = current_exposure - dt;
        update_image_exposure_display();
      } 

      /* Poll selfguiding feedback from the track ccd */   

      if ( ( (guide == SELFTRACKING) || (guide == SELFOPTICAL) ) && (detector == IMAGE) )
      {
        CaptureGuide(&phase, 
          image_data, track_data,
          current_exposure, image_region, 
          image_region_x, image_region_y, image_region_w, image_region_h,
          track_exposure, track_region, 
          track_region_x, track_region_y, track_region_w, track_region_h,
          guide, guide_flag, guide_log_flag);  
      }              
    }
  }
  
  if (phase == 2) 
  {
    /* Write a file appropriate for the detector and 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 (detector == TRACK)
    {
      if (frame != BIAS)
      {
        fits_exposure = track_exposure;
      }
      else
      {
        fits_exposure = 0.;  
      }
 
      if (track_region == FALSE)
      {
        WriteFits(file_name, track_width, track_height, track_data, 
          fits_exposure, 
          fits_type, 
          fits_date, 
          fits_target,                                               
          fits_instrument, 
          fits_filter, 
          fits_telescope);
      }
      else
      {
        WriteFits(file_name, track_region_w, track_region_h, track_data, 
          fits_exposure, 
          fits_type, 
          fits_date, 
          fits_target,                                               
          fits_instrument, 
          fits_filter, 
          fits_telescope);
      }
    }
    else
    {
      if (frame != BIAS)
      {
        fits_exposure = image_exposure;
      }
      else
      {
        fits_exposure = 0.;  
      }

      if (image_region == FALSE)
      {
        WriteFits(file_name, image_width, image_height, image_data, 
          fits_exposure, 
          fits_type, 
          fits_date, 
          fits_target,                                               
          fits_instrument, 
          fits_filter, 
          fits_telescope);
      }
      else
      {
        WriteFits(file_name, image_region_w, image_region_h, image_data, 
          fits_exposure, 
          fits_type, 
          fits_date, 
          fits_target,                                               
          fits_instrument, 
          fits_filter, 
          fits_telescope);
      }   
    }

    /* Send the frame by the method selected with transfer switch */
    
    if (transfer != NOCOPY)
    {
      if (transfer == INDICOPY)
      {
        fprintf(stderr, "INDI image file transfer is not available\n");    
      }
      else if (transfer == NETCOPY)
      {    
        if (send_image(file_name) == 0) 
        {
          fprintf(stderr, "Image transfer error\n");
        } 
        else 
        {
          fprintf(stderr, "Image file dispatched\n");
        }
      }     
    }

    /* Applying guiding corrections if requested */
    
    if ( (guide == TRACKING) || (guide == OPTICAL) )
    {
      if (detector == TRACK)
      {
        if (track_region == FALSE)
        {
          GuideImage(track_data, track_height, track_width,
            guide, guide_flag, guide_log_flag); 
        }
        else
        {
          GuideImage(track_data,track_region_w, track_region_h,
            guide, guide_flag, guide_log_flag);                   
        }
      }
      else
      {
        if (image_region == FALSE)
        {
          GuideImage(image_data, image_height, image_width,
            guide, guide_flag, guide_log_flag); 
        }
        else
        {
          GuideImage(image_data, image_region_w, image_region_h,
            guide, guide_flag, guide_log_flag);                   
        }   
      }    
    }
    
    /* 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 */
    if ( detector == TRACK )
    {
      track_exposure_remaining = track_exposure;
    }
    else
    {
      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(detector_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);
}  


/* Read and parse the initial configuration file */

void read_config(void)
{
  char configstr[121];
  char *configptr = configstr;
  int n;
  int test_flag;
    
  configfile = (char *) malloc (MAXPATHLEN);
  strcpy(configfile,CONFIGFILE);  
  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_max");
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%d",&filter_max); 
        if (filter_max > 10)
        {
          filter_max = 0;
          fprintf(stderr,"Configuration error: attempt to set max filter count > 10\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 )
        {  
          filter_name[n-1]='\0';
        }
        SetCameraFilterName(1, 1, filter_name);
        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 )
        {  
          filter_name[n-1]='\0';
        }
        SetCameraFilterName(1, 2, filter_name);
        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 )
        {  
          filter_name[n-1]='\0';
        }
        SetCameraFilterName(1, 3, filter_name);
        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 )
        {  
          filter_name[n-1]='\0';
        }
        SetCameraFilterName(1, 4, filter_name);
        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 )
        {  
          filter_name[n-1]='\0';
        }
        SetCameraFilterName(1, 5, filter_name);
        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 )
        {  
          filter_name[n-1]='\0';
        }
        SetCameraFilterName(1, 6, filter_name);
        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 )
        {  
          filter_name[n-1]='\0';
        }
        SetCameraFilterName(1, 7, filter_name);
        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 )
        {  
          filter_name[n-1]='\0';
        }
        SetCameraFilterName(1, 8, filter_name);
        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 )
        {  
          filter_name[n-1]='\0';
        }
        SetCameraFilterName(1, 9, filter_name);
        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 )
        {  
          filter_name[n-1]='\0';
        }
        SetCameraFilterName(1, 10, filter_name);
        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.guide_flag");
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%d",&test_flag);
        if (test_flag == 1)
        {
          guide_flag = TRUE;
          fprintf(stderr,"guide_flag true\n");
        }
        else
        {
          guide_flag = FALSE;
          fprintf(stderr,"guide_flag false\n");
        }    
      }  
    }

    configptr = strstr(configstr,"ccd.guide_log_flag");
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%d",&guide_log_flag); 
        fprintf(stderr,"guide_log_flag: %d\n",guide_log_flag);
      }  
    }     

    configptr = strstr(configstr,"ccd.threshold");
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%d",&threshold); 
        SetCameraGuideParameters(threshold,
          x2trackx, y2trackx, x2tracky, y2tracky,
          x2aox, y2aox, x2aoy, y2aoy);             
        fprintf(stderr,"threshold: %d\n",threshold);
      }  
    }

    configptr = strstr(configstr,"ccd.x2aox");
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%lf",&x2aox); 
        SetCameraGuideParameters(threshold, 
          x2trackx, y2trackx, x2tracky, y2tracky,
          x2aox, y2aox, x2aoy, y2aoy);             
        fprintf(stderr,"x2aox: %f\n",x2aox);
      }  
    } 

    configptr = strstr(configstr,"ccd.y2aox");
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%lf",&y2aox); 
        SetCameraGuideParameters(threshold, 
          x2trackx, y2trackx, x2tracky, y2tracky,
          x2aox, y2aox, x2aoy, y2aoy);             
        fprintf(stderr,"y2aox: %f\n",y2aox);
      }  
    } 

    configptr = strstr(configstr,"ccd.x2aoy");
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%lf",&x2aoy); 
        SetCameraGuideParameters(threshold, 
          x2trackx, y2trackx, x2tracky, y2tracky,
          x2aox, y2aox, x2aoy, y2aoy);             
        fprintf(stderr,"x2aoy: %f\n",x2aoy);
      }  
    }

    configptr = strstr(configstr,"ccd.y2aoy");
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%lf",&y2aoy); 
        SetCameraGuideParameters(threshold, 
          x2trackx, y2trackx, x2tracky, y2tracky,
          x2aox, y2aox, x2aoy, y2aoy);             
        fprintf(stderr,"y2aoy: %f\n",y2aoy);
      }  
    }

    configptr = strstr(configstr,"ccd.x2trackx");
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%lf",&x2trackx); 
        SetCameraGuideParameters(threshold, 
          x2trackx, y2trackx, x2tracky, y2tracky,
          x2aox, y2aox, x2aoy, y2aoy);             
        fprintf(stderr,"x2trackx: %f\n",x2trackx);
      }  
    } 

    configptr = strstr(configstr,"ccd.y2trackx");
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%lf",&y2trackx); 
        SetCameraGuideParameters(threshold, 
          x2trackx, y2trackx, x2tracky, y2tracky,
          x2aox, y2aox, x2aoy, y2aoy);             
        fprintf(stderr,"y2trackx: %f\n",y2trackx);
      }  
    } 

    configptr = strstr(configstr,"ccd.x2tracky");
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%lf",&x2tracky); 
        SetCameraGuideParameters(threshold, 
          x2trackx, y2trackx, x2tracky, y2tracky,
          x2aox, y2aox, x2aoy, y2aoy);             
        fprintf(stderr,"x2tracky: %f\n",x2tracky);
      }  
    }

    configptr = strstr(configstr,"ccd.y2tracky");
    if ( configptr != NULL)
    {
      configptr = strstr(configstr,"=");
      if ( configptr != NULL)
      {
        configptr = configptr + 1;
        sscanf(configptr,"%lf",&y2tracky); 
        SetCameraGuideParameters(threshold, 
          x2trackx, y2trackx, x2tracky, y2tracky,
          x2aox, y2aox, x2aoy, y2aoy);             
        fprintf(stderr,"y2tracky: %f\n",y2tracky);
      }  
    }
  }
  
  fclose(fp_config);

}


