/* ----------------------------------------------------------------------- */
/* -                   Astronomical CCD Camera Control                   - */
/* -                             INDI Driver                             - */
/* ----------------------------------------------------------------------- */
/* Copyright (c) 2011 John Kielkopf                                        */
/* kielkopf@louisville.edu                                                 */
/*                                                                         */
/* This file is part of XmCCD.                                             */
/*                                                                         */
/* XmCCD is free software: you can redistribute it and/or modify           */
/* it under the terms of the GNU General Public License as published by    */
/* the Free Software Foundation, either version 3 of the License, or       */
/* (at your option) any later version.                                     */
/*                                                                         */
/* XmCCD is distributed in the hope that it will be useful,                */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of          */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the           */
/* GNU General Public License for more details.                            */
/*                                                                         */
/* Credits: INDI components included here were derived from software       */
/* Copyright (C) 2003 Elwood C. Downey and originally distributed under    */
/* the terms of the GNU Lesser General Public License version 2.1.         */             
/*                                                                         */
/* Date: August 6, 2011                                                    */
/* Version: 3.1.3                                                          */

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

#include <fitsio.h>

#include "indidevapi.h"
#include "ccd.h"
#include "protocol.h"
         
/* Files */

FILE *fp_config;                       /* Configuration file pointer */
static char *configfile;               /* Configuration file name */
void read_config(void);
  
/* Internal flags and counters */

int  acquire = SINGLE;
int  guide = UNGUIDED;
int  guide_log_flag = FALSE;
int  guide_flag = TRUE;
int  detector = IMAGE;
int  frame = LIGHT;
int  phase = 0;
int  sequence = 0;
int  sequence_last = 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;

/* Internal character arrays used to hold text */

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

/* Fits header elements */

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;


/* 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 *image_width, int *image_height, double *image_gain,
 int *track_width, 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 SetCameraFilter(int filter);

extern int SetCameraAOTipTilt(int ao_x, int ao_y);

extern int ActivateCameraRelay(int relay, double ontime);

extern int SetCameraFilterName(int flag, 
  int new_filter_number, char *new_filter_name);
  
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;
int    image_height;
double image_exposure;
double image_gain; 
int    image_region;
int    image_region_x;
int    image_region_y; 
int    image_region_w;
int    image_region_h;

int    track_width;  
int    track_height;
double track_exposure;
double track_gain; 
int    track_region;
int    track_region_x;
int    track_region_y; 
int    track_region_w;
int    track_region_h; 

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

double declination;
double right_ascension;
double image_scale = ARCSEC_PER_PIXEL;

double setpoint;
double temperature;  
double cooler_power;  
int    cool;

int    ao_x=2048;
int    ao_y=2048;
int    tiptilt = EAST;

int    relay = EAST;
double relay_ontime = RELAYONTIME;

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


/* INDI prototypes and variables */

static void camera (void *p);
static int sendblob (char *filename);
static int sendimage (char *filename);
static struct timeval xtv;

/* INDI number                                */

/* Index name                                 */
/* Short description                          */
/* GUI display format                         */
/* Range, ignore if min == max                */
/* Step size, ignore if step == 0             */
/* Current value                              */
/* Pointer to parent vector property          */
/* Opt: handy place to hang helper info       */


/* INDI number property                       */

/* Device name                                */
/* Property name                              */
/* Short description                          */
/* GUI grouping hint                          */
/* Client accessibility hint                  */
/* Current max time to change, secs           */
/* Current property state                     */
/* Numbers comprising this vector             */
/* Dimension of np[]                          */
/* ISO 8601 timestamp of this event           */
/* Opt: handy place to hang helper info       */


/* Image exposure time */

static INumber indi_image_exposure = 
{ 
  "time", 
  "Time", 
  "%.1f", 
  IMGMINEXP, 
  IMGMAXEXP, 
  IMGINCEXP, 
  IMGDEFEXP 
};

static INumberVectorProperty indi_image_exposure_property = 
{ 
  MYDEV, 
  "image_exposure", 
  "Image Exposure", 
  "", 
  IP_RW, 
  0, 
  IPS_IDLE, 
  &indi_image_exposure, 
  1 
};

/* Track exposure time */

static INumber indi_track_exposure = 
{ 
  "time", 
  "Time", 
  "%.1f", 
  TRKMINEXP,  
  TRKMAXEXP,  
  TRKINCEXP,  
  TRKDEFEXP 
};

static INumberVectorProperty indi_track_exposure_property = 
{ 
  MYDEV, 
  "track_exposure", 
  "Track Exposure", 
  "", 
  IP_RW, 
  0, 
  IPS_IDLE, 
  &indi_track_exposure, 
  1 
};


/* Read-only parameters of camera, filter and acquistion system */

static INumber indi_ccd_system[] = 
{
  {"image_width",  "Image CCD Width",  "%.0f", 1, 1, 2, IMAGEXMAX},
  {"image_height", "Image CCD Height", "%.0f", 1, 1, 2, IMAGEYMAX},
  {"track_width",  "Track CCD Width",  "%.0f", 1, 1, 2, TRACKXMAX},
  {"track_height", "Track CCD Height", "%.0f", 1, 1, 2, TRACKYMAX},
  {"filter_max",   "Filters",          "%.0f", 1, 1, 1, FILTER_MAX},
};

static INumberVectorProperty indi_ccd_system_property = 
{ 
  MYDEV, 
  "ccd_system", 
  "CCD System", 
  "", 
  IP_RO, 
  0, 
  IPS_IDLE, 
  indi_ccd_system, 
  NARRAY(indi_ccd_system)
};


/* Image region */

static INumber indi_image_region[] = 
{
  {"x", "X", "%.0f", 1, IMAGEXMAX, 2, 1},
  {"y", "Y", "%.0f", 1, IMAGEYMAX, 2, 1},
  {"w", "W", "%.0f", 1, IMAGEXMAX, 2, IMAGEXMAX},
  {"h", "H", "%.0f", 1, IMAGEYMAX, 2, IMAGEYMAX},
};

static INumberVectorProperty indi_image_region_property = 
{ 
  MYDEV, 
  "image_region", 
  "Image Region", 
  "", 
  IP_RW, 
  0, 
  IPS_IDLE, 
  indi_image_region, 
  NARRAY(indi_image_region)
};

/* Track region */

static INumber indi_track_region[] = 
{
  {"x", "X", "%.0f", 1, TRACKXMAX, 2, 1},
  {"y", "Y", "%.0f", 1, TRACKYMAX, 2, 1},
  {"w", "W", "%.0f", 1, TRACKXMAX, 2, TRACKXMAX},
  {"h", "H", "%.0f", 1, TRACKYMAX, 2, TRACKYMAX},
};

static INumberVectorProperty indi_track_region_property = 
{ 
  MYDEV, 
  "track_region", 
  "Track Region", 
  "", 
  IP_RW, 
  0, 
  IPS_IDLE, 
  indi_track_region, 
  NARRAY(indi_track_region)
};

/* Celestial coordinates */

static INumber indi_coordinates[] = 
{
  {"declination", "Declination D:M:S", "%10.6m", -90, 90, 1, 0},
  {"right_ascension", "Right Ascension H:M:S", "%10.6m", 0, 24, 1, 12}
};

static INumberVectorProperty indi_coordinates_property = 
{ 
  MYDEV, 
  "coordinates", 
  "Coordinates", 
  "", 
  IP_RW, 
  0, 
  IPS_IDLE, 
  indi_coordinates, 
  NARRAY(indi_coordinates) 
};

/* Temperature control */

static INumber indi_temperature_control = 
{ 
  "setting", 
  "Setting", 
  "%.1f", 
  -50.,  
  30.,  
  1.,  
  -10. 
};

static INumberVectorProperty indi_temperature_control_property = 
{ 
  MYDEV, 
  "temperature_control", 
  "Temperature Control", 
  "", 
  IP_RW, 
  0, 
  IPS_IDLE, 
  &indi_temperature_control, 
  1 
};


/* Camera temperature reading */

static INumber indi_camera_temperature= 
{ 
  "reading", 
  "Reading", 
  "%.1f", 
  -80.,  
  30.,  
  1.,  
  -10. 
};

static INumberVectorProperty indi_camera_temperature_property = 
{ 
  MYDEV, 
  "camera_temperature", 
  "Camera Temperature", 
  "", 
  IP_RO, 
  0, 
  IPS_IDLE, 
  &indi_camera_temperature, 
  1 
};

/* Filter selection */

static INumber indi_filter_selection = 
{ 
  "selection", 
  "Selection", 
  "%.0f", 
  1,  
  FILTER_MAX,  
  1,  
  FILTER_START 
};


static INumberVectorProperty indi_filter_selection_property = 
{ 
  MYDEV, 
  "filter", 
  "Filter", 
  "", 
  IP_RW, 
  0, 
  IPS_IDLE, 
  &indi_filter_selection, 
  1 
};


/* Selection of image number */

static INumber indi_image_number = 
{ 
  "number", "Base Number", "%.0f", 1, 1, 1, 1000
};

static INumberVectorProperty indi_image_number_property = 
{ 
  MYDEV, 
  "image", 
  "Image Number", 
  "", 
  IP_RW, 
  0, 
  IPS_IDLE, 
  &indi_image_number,
  1
};

/* Selection of frame multi  */

static INumber indi_frame_multi = 
{ 
  "multi", 
  "Multi", 
  "%.0f", 
  1, 
  FRAMECOUNTMULTI, 
  1, 
  1000
};

static INumberVectorProperty indi_frame_multi_property = 
{ 
  MYDEV, 
  "frame", 
  "Frame", 
  "", 
  IP_RW, 
  0, 
  IPS_IDLE, 
  &indi_frame_multi,
  1
};



/* INDI switches                                   */

/* Index name                                      */
/* Switch label                                    */
/* Switch state                                    */
/* Opt: Pointer to parent                          */
/* Opt: Handy place to hang helper info            */

/* Device name                                     */
/* Property name                                   */
/* Short description will appear on a client GUI   */
/* GUI grouping hint                               */
/* Client accessibility hint                       */
/* Switch behavior hint                            */
/* Current max time to change, secs                */
/* Current property state                          */
/* Switches comprising this vector                 */
/* Dimension of sp[] (with help from NARRAY)       */
/* ISO 8601 timestamp of this event                */
/* Opt: handy place to hang helper info            */


/* Cooler switch  */

static ISwitch indi_cooler_power[]= 
{ 
  {"power", "Power", ISS_ON}
};

static ISwitchVectorProperty indi_cooler_power_property = 
{
  MYDEV, 
  "cooler", 
  "Cooler", 
  "", 
  IP_RW,
  ISR_NOFMANY, 
  0, 
  IPS_IDLE, 
  indi_cooler_power, 
  1
};

/* Image region control  */

static ISwitch indi_image_region_control[] = 
{ 
  {"option", "Option", ISS_OFF},
};


static ISwitchVectorProperty indi_image_region_control_property = 
{
  MYDEV, 
  "image_region_control", 
  "Image Region Control", 
  "", 
  IP_RW,
  ISR_NOFMANY, 
  0, 
  IPS_IDLE, 
  indi_image_region_control, 
  NARRAY(indi_image_region_control)
};

/* Track region control  */

static ISwitch indi_track_region_control[] = 
{ 
  {"option", "Option", ISS_OFF},
};

static ISwitchVectorProperty indi_track_region_control_property = 
{
  MYDEV, 
  "track_region_control", 
  "Track Region Control", 
  "", 
  IP_RW,
  ISR_NOFMANY, 
  0, 
  IPS_IDLE, 
  indi_track_region_control, 
  NARRAY(indi_track_region_control)
};


/* Frame type switch  */

static ISwitch indi_frame_type[] = 
{   
  {"dark",  "Dark", ISS_OFF},
  {"light", "Light",ISS_ON},
  {"bias",  "Bias", ISS_OFF},
  {"flat",  "Flat", ISS_OFF}
};

static ISwitchVectorProperty indi_frame_type_property = 
{
  MYDEV, 
  "frame", 
  "Frame", 
  "", 
  IP_RW,
  ISR_1OFMANY, 
  0, 
  IPS_IDLE, 
  indi_frame_type, 
  NARRAY(indi_frame_type)
};

/* Sequence switch  */

static ISwitch indi_sequence[] = 
{ 
  {"run", "Run", ISS_OFF},
};

/* Sequence type switch vector property */

static ISwitchVectorProperty indi_sequence_property = 
{
  MYDEV, 
  "sequence", 
  "Sequence", 
  "", 
  IP_RW,
  ISR_NOFMANY, 
  0, 
  IPS_IDLE, 
  indi_sequence, 
  NARRAY(indi_sequence)
};

/* Detector selection switch  */

static ISwitch indi_detector_selection[] = 
{ 
  {"image", "Image", ISS_ON},
  {"track", "Track", ISS_OFF},
};

/* Detector choice switch vector property */

static ISwitchVectorProperty indi_detector_selection_property = 
{
  MYDEV, 
  "detector", 
  "Detector", 
  "", 
  IP_RW,
  ISR_1OFMANY, 
  0, 
  IPS_IDLE, 
  indi_detector_selection, 
  NARRAY(indi_detector_selection)
};


/* Acquire mode switch  */

static ISwitch indi_acquire_mode[] = 
{ 
  {"single", "Single", ISS_ON},
  {"focus", "Focus",ISS_OFF},
  {"multi", "Multi",ISS_OFF},
};


static ISwitchVectorProperty indi_acquire_mode_property =
{
  MYDEV, 
  "acquire", 
  "Acquire", 
  "", 
  IP_RW,
  ISR_1OFMANY, 
  0, 
  IPS_IDLE, 
  indi_acquire_mode, 
  NARRAY(indi_acquire_mode)
};

/* Guide mode switch  */

static ISwitch indi_guide_mode[] = 
{ 
  {"unguided", "Unguided", ISS_ON},
  {"tracking", "Tracking",ISS_OFF},
  {"optical",  "Optical",ISS_OFF},
  {"self_tracking", "Self Tracking",ISS_OFF},
  {"self_optical",  "Self Optical",ISS_OFF},
};

/* Guide type switch vector property */

static ISwitchVectorProperty indi_guide_mode_property =
{
  MYDEV, 
  "guide", 
  "Guide", 
  "", 
  IP_RW,
  ISR_1OFMANY, 
  0, 
  IPS_IDLE, 
  indi_guide_mode, 
  NARRAY(indi_guide_mode)
};

/* Image file transfer mode switch  */

static ISwitch indi_image_transfer_mode[] = 
{ 
  {"none",    "None",     ISS_ON},
  {"network", "Network", ISS_OFF},
  {"indi",    "Indi",    ISS_OFF},
};

static ISwitchVectorProperty indi_image_transfer_mode_property = 
{
  MYDEV, 
  "image_transfer", 
  "Image Transfer", 
  "", 
  IP_RW,
  ISR_1OFMANY, 
  0, 
  IPS_IDLE, 
  indi_image_transfer_mode, 
  NARRAY(indi_image_transfer_mode)
};


/* Relay activation through camera  */
/* Assumes correct hardware mapping  */
/* Switch any or all on or off       */

static ISwitch indi_relay[] = 
{ 
  {"north", "N", ISS_OFF},
  {"south", "S", ISS_OFF},
  {"east",  "E", ISS_OFF},
  {"west",  "W", ISS_OFF}, 
};

static ISwitchVectorProperty indi_relay_property = 
{
  MYDEV, 
  "relay", 
  "Relay", 
  "", 
  IP_RW,
  ISR_NOFMANY, 
  0, 
  IPS_IDLE, 
  indi_relay, 
  NARRAY(indi_relay)
};


/* AO activation through camera  */
/* Assumes correct hardware mapping  */
/* Switch any or all on or off       */

static ISwitch indi_ao[] = 
{ 
  {"north", "N", ISS_OFF},
  {"south", "S", ISS_OFF},
  {"east",  "E", ISS_OFF},
  {"west",  "W", ISS_OFF}, 
};

static ISwitchVectorProperty indi_ao_property = 
{
  MYDEV, 
  "ao", 
  "AO", 
  "", 
  IP_RW,
  ISR_NOFMANY, 
  0, 
  IPS_IDLE, 
  indi_ao, 
  NARRAY(indi_ao)
};


/* INDI blob                             */

/* Index name                             */
/* BLOB's label                           */
/* Format attr                            */
/* Malloced binary large object bytes     */
/* Bytes in blob                          */
/* Number of uncompressed bytes           */
/* Pointer to parent                      */
/* Opt: handy place to hang helper info   */

/* Device name                            */
/* Property name                          */
/* Short description                      */
/* GUI grouping hint                      */
/* Client accessibility hint              */
/* Current max time to change, secs       */
/* Current property state                 */
/* BLOBs comprising this vector           */
/* Dimension of bp[]                      */
/* ISO 8601 timestamp of this event       */
/* Opt: handy place to hang helper info   */


/* Indi image transfer */

static IBLOB indi_image_blob = 
{
  "copy", 
  "Copy"
};

static IBLOBVectorProperty indi_image_blob_property = 
{
  MYDEV, 
  "image", 
  "Image", 
  "",
  IP_RO, 
  0, 
  IPS_IDLE, 
  &indi_image_blob, 
  1
};


/* INDI text */

/* Text descriptor */
/* Index name */
/* Short description */
/* Malloced text string */
/* Pointer to parent */
/* Handy place to hang helper info */


/* Text vector property descriptor */
/* Device name */
/* Property name */
/* Short description */
/* GUI grouping hint */
/* Client accessibility hint */
/* Current max time to change, secs */
/* Current property state */
/* Texts comprising this vector */
/* Dimension of tp[] */
/* ISO 8601 timestamp of this event */
/* Handy place to hang helper info */


/* Archive base_name */

static IText indi_archive_base_name[] = 
{
  {"base_name", "Base Name", base_name}
};

static ITextVectorProperty indi_archive_base_name_property = 
{
    MYDEV, 
    "archive", 
    "Archive", 
    "", 
    IP_RW, 
    30,
    IPS_IDLE, 
    indi_archive_base_name, 
    NARRAY(indi_archive_base_name),
};


/* Target fits_target */

static IText indi_archive_target[] = 
{
  {"fits_target", "Fits Target", fits_target}
};

static ITextVectorProperty indi_archive_target_property = 
{
    MYDEV, 
    "target", 
    "Target", 
    "", 
    IP_RW, 
    30,
    IPS_IDLE, 
    indi_archive_target, 
    NARRAY(indi_archive_target),
};


/* Telescope fits_telescope */

static IText indi_archive_telescope[] = 
{
  {"fits_telescope", "Fits Telescope", fits_telescope}
};

static ITextVectorProperty indi_archive_telescope_property = 
{
    MYDEV, 
    "telescope", 
    "Telescope", 
    "", 
    IP_RW, 
    30,
    IPS_IDLE, 
    indi_archive_telescope, 
    NARRAY(indi_archive_telescope),
};



/* File name */

static IText indi_file_name[] = 
{
  {"name", "Name", file_name}
};

static ITextVectorProperty indi_file_name_property = 
{
    MYDEV, 
    "file", 
    "File", 
    "", 
    IP_RO, 
    30,
    IPS_IDLE, 
    indi_file_name, 
    NARRAY(indi_file_name),
};


/* Filter names */

static IText indi_filter_name[] = 
{
  {"name", "Name", filter_name}
};


static ITextVectorProperty indi_filter_name_property = 
{
    MYDEV, 
    "filter", 
    "Filter", 
    "", 
    IP_RO, 
    30,
    IPS_IDLE, 
    indi_filter_name, 
    NARRAY(indi_filter_name),
};


/* INDI functions */

/* Call once before any other IS function */
/* This turns on a timer */
/* When the timer expires it calls a camera routine */
/* The camera routine will restart a new timer */

static void initccd()
{

  /* Initialization control */
  
  static int inited;
  static int startup;
  
  if (inited)
  { 
    return;
  }

  /* Set base_name to default */
  
  strcpy(base_name,"image_");
  
  /* Handle the configuration file to change other defaults */
  
  read_config();
  
  /* 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);
  
  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;
  
  /* Set fixed camera and filter parameters */
  
  indi_ccd_system[0].value = image_width;
  indi_ccd_system[1].value = image_height;
  indi_ccd_system[2].value = track_width;
  indi_ccd_system[3].value = track_height;
  indi_ccd_system[4].value = filter_max;
                    
  /* Set indi max and min parameters to match actual camera */

  indi_image_region[0].max = image_width;
  indi_image_region[1].max = image_height;
  indi_image_region[2].min = image_width;
  indi_image_region[2].max = image_width;
  indi_image_region[3].min = image_height;
  indi_image_region[3].max = image_height;
  indi_track_region[0].max = track_width;
  indi_track_region[1].max = track_height;
  indi_track_region[2].min = track_width;
  indi_track_region[2].max = track_width;
  indi_track_region[3].min = track_height;
  indi_track_region[3].max = track_height;
  
  

  /* 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,"                ");
    
                      
  /* Alert stderr to a problem and exit */
  
  if (startup != TRUE)
  {
    fprintf(stderr,"Camera did not respond to startup request");
    exit(1);
  }

  /* Update INDI with camera parameters */
  
  image_region_x = 1;
  indi_image_region[0].value = image_region_x;
  IDSetNumber (&indi_image_region_property, 
    "Image region x %d", image_region_x);
    
  image_region_y = 1;
  indi_image_region[1].value = image_region_y;
  IDSetNumber (&indi_image_region_property, 
    "Image region y %d", image_region_y);
    
  indi_image_region[2].value = image_width;
  IDSetNumber (&indi_image_region_property, 
    "Image region w %d", image_width);
    
  indi_image_region[3].value = image_height;
  IDSetNumber (&indi_image_region_property, 
    "Image region h %d", image_height);
       
  track_region_x = 1;
  indi_track_region[0].value = track_region_x;
  IDSetNumber (&indi_track_region_property, 
    "Track region x %d", track_region_x);
    
  track_region_y = 1;
  indi_track_region[1].value = track_region_y;
  IDSetNumber (&indi_track_region_property, 
    "Track region y %d", track_region_y);

  indi_track_region[2].value = track_width;
  IDSetNumber (&indi_track_region_property, 
    "Track region w %d", track_width);
    
  indi_track_region[3].value = track_height;
  IDSetNumber (&indi_track_region_property, 
    "Track region h %d", track_height);


  startup = SetCameraFilter(filter);
    
  startup = GetCameraFilter(filter_name, &filter);
  sprintf(fits_filter,"%s",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");
    filter_wheel = NO_FILTER_WHEEL;
  }
  else
  {
    indi_filter_selection.value = filter;
    IDSetNumber (&indi_filter_selection_property, 
      "Filter number %d", filter); 
  }


  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); 
  
  IDSetNumber (&indi_temperature_control_property, 
    "Setpoint %g (C)", 
    setpoint);  
  
  indi_cooler_power_property.s = IPS_OK;
  IDSetSwitch (&indi_cooler_power_property, 
    "Cooling power switch on at setpoint %g", 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,"");

  
  /* Alert stderr to a problem and exit */
  
  if (startup != TRUE)
  {
    fprintf(stderr,"Camera not found");
    exit(1);
  }
  
  /* Set flag so we do the initialization only once */
  
  inited = 1;

  /* Start the timer to poll the camera */
  
  IEAddTimer (POLLMS, camera, NULL);
}

/* Dummy definition for libip */

void pm_set (int p)
{
}

/* Send client the definitions of all properties we want it to know about */

void ISGetProperties (const char *dev)
{
   if (dev && strcmp (MYDEV, dev))
   {
     return;
   }
    
   initccd();
   IDDefSwitch (&indi_sequence_property, NULL);      
   IDDefNumber (&indi_image_exposure_property, NULL);
   IDDefNumber (&indi_track_exposure_property, NULL);
   IDDefNumber (&indi_frame_multi_property, NULL);   
   IDDefNumber (&indi_temperature_control_property, NULL);
   IDDefNumber (&indi_camera_temperature_property, NULL);
   IDDefSwitch (&indi_cooler_power_property, NULL);
   IDDefNumber (&indi_ccd_system_property, NULL);
   IDDefSwitch (&indi_image_region_control_property, NULL);
   IDDefNumber (&indi_image_region_property, NULL);
   IDDefSwitch (&indi_track_region_control_property, NULL);
   IDDefNumber (&indi_track_region_property, NULL);
   IDDefSwitch (&indi_detector_selection_property, NULL);
   IDDefSwitch (&indi_frame_type_property, NULL);   
   IDDefSwitch (&indi_acquire_mode_property, NULL);
   IDDefSwitch (&indi_guide_mode_property, NULL);
   IDDefNumber (&indi_filter_selection_property, NULL);
   IDDefText   (&indi_filter_name_property, NULL);
   IDDefNumber (&indi_coordinates_property, NULL);   
   IDDefText   (&indi_archive_base_name_property, NULL);
   IDDefNumber (&indi_image_number_property, NULL);
   IDDefText   (&indi_file_name_property, NULL);
   IDDefText   (&indi_archive_target_property, NULL);
   IDDefText   (&indi_archive_telescope_property, NULL);
   IDDefSwitch (&indi_image_transfer_mode_property, NULL);
   IDDefSwitch (&indi_relay_property, NULL);
   IDDefSwitch (&indi_ao_property, NULL);
   IDDefBLOB   (&indi_image_blob_property, NULL);
}


/* Client is sending new numbers */
/* Search number properties for a match and act accordingly */
/* In Elwood Downey's notation, the search arguments here are Nv not N */
/* In my notation the arguments are _property */


void ISNewNumber (const char *dev, const char *name,
  double *doubles, char *names[], int n)
{
  int i;
  
  if (strcmp (dev, MYDEV))
  { 
      /* Ignore if this is not for us */
      return;
  }
  
  if (!strcmp (name, indi_image_exposure_property.name) && n > 0) 
  {    
    
    /* New imaging exposure */
    
    if (indi_image_exposure_property.s == IPS_BUSY) 
    {
      indi_image_exposure_property.s = IPS_OK;
      indi_image_exposure.value = 0;      
      IDSetNumber (&indi_image_exposure_property, "Image exposure aborted");
      sequence_last = sequence;
      sequence = 0;
      indi_sequence[0].s = ISS_OFF; 
      indi_sequence_property.s = IPS_OK;      
      IDSetSwitch (&indi_sequence_property, "Waiting to start a sequence");      
    } 
    else 
    {
      indi_image_exposure.value = doubles[0];
      indi_image_exposure_property.s = IPS_OK;
      image_exposure = indi_image_exposure.value;
      IDSetNumber (&indi_image_exposure_property, 
        "Set %g second duration image exposure", 
        image_exposure);
    }
  }
  
  if (!strcmp (name, indi_track_exposure_property.name) && n > 0) 
  {
    
    /* New tracking exposure */
    
    if (indi_track_exposure_property.s == IPS_BUSY) 
    {
      indi_track_exposure_property.s = IPS_OK;
      indi_track_exposure.value = 0;
      IDSetNumber (&indi_track_exposure_property, "Track exposure aborted");
      sequence_last = sequence;
      sequence = 0;
      indi_sequence[0].s = ISS_OFF; 
      indi_sequence_property.s = IPS_OK;      
      IDSetSwitch (&indi_sequence_property, "Waiting to start a sequence");      
    } 
    else 
    {
      indi_track_exposure.value = doubles[0];
      indi_track_exposure_property.s = IPS_OK;
      track_exposure = indi_track_exposure.value;
      IDSetNumber (&indi_track_exposure_property,
        "Set %g second duration track exposure", 
        track_exposure);
    }
  }

  if (!strcmp (name, indi_image_number_property.name) && n > 0) 
  {
    
    /* A new image number has been sent */
    
    indi_image_number.value = doubles[0];
    image_number = indi_image_number.value;
    if (image_number < 2)
    {
      image_number = 1;
    }
    IDSetNumber (&indi_image_number_property, 
      "Image number %d", 
      image_number);
  }

  if (!strcmp (name, indi_frame_multi_property.name) && n > 0) 
  {
    
    /* A new maximum multi-mode frame count has been sent */
    
    indi_frame_multi.value = doubles[0];
    frame_count_multi = indi_frame_multi.value;
    if (frame_count_multi < 2)
    {
      frame_count_multi = 1;
    }
    IDSetNumber (&indi_frame_multi_property, 
      "Maximum multiframe count %d", 
      frame_count_multi);
  }
  
  if (!strcmp (name, indi_temperature_control_property.name) && n > 0) 
  {
    
    /* A new setpoint has been sent */
    
    indi_temperature_control.value = doubles[0];
    setpoint = indi_temperature_control.value;
    SetCameraTemperature(cool, setpoint);
    IDSetNumber (&indi_temperature_control_property, 
      "Setpoint %g (C)", 
      setpoint);
  }

  if (!strcmp (name, indi_image_region_property.name) && n > 0) 
  {
    /* New image region parameters have been sent */
    
    for (i = 0; i < n; i++)
    {
      if (strcmp (indi_image_region[0].name, names[i]) == 0)
      {
        image_region_x = doubles[i];
        if (image_region_x > image_width)
        {
          image_region_x = image_width;
        }
        if (image_region_x < 1)
        {
          image_region_x = 1;
        }
        indi_image_region[0].value = image_region_x;
        IDSetNumber (&indi_image_region_property, 
          "Image region x %d", image_region_x);
      }
      else if (strcmp(indi_image_region[1].name, names[i]) == 0)
      {
        image_region_y = doubles[i];
        if (image_region_y > image_height)
        {
          image_region_y = image_height;
        }
        if (image_region_y < 1)
        {
          image_region_y = 1;
        } 
        indi_image_region[1].value = image_region_y;
        IDSetNumber (&indi_image_region_property, 
          "Image region y %d", image_region_y);      
      }
      else if (strcmp(indi_image_region[2].name, names[i]) == 0)
      {
        image_region_w = doubles[i];
        if (image_region_w > image_width)
        {
          image_region_w = image_width;
        }
        if (image_region_w < 1)
        {
          image_region_w = 1;
        }        
        indi_image_region[2].value = image_region_w;
        IDSetNumber (&indi_image_region_property, 
          "Image region w %d", image_region_w);      
      }
      else if (strcmp(indi_image_region[3].name, names[i]) == 0)
      {
        image_region_h = doubles[i];
        if (image_region_h > image_height)
        {
          image_region_h = image_height;
        }
        if (image_region_h < 1)
        {
          image_region_h = 1;
        }         
        indi_image_region[3].value = image_region_h;
        IDSetNumber (&indi_image_region_property, 
          "Image region h %d", image_region_h);      
      }
      else 
      {
        IDSetNumber (&indi_image_region_property, 
          "Image region parameter setting error");      
      }                  
    }
  }
 
  if (!strcmp (name, indi_track_region_property.name) && n > 0) 
  {
    /* New track region parameters have been sent */
    
    for (i = 0; i < n; i++)
    {
      if (strcmp (indi_track_region[0].name, names[i]) == 0)
      {
        track_region_x = doubles[i];
        if (track_region_x > track_width)
        {
          track_region_x = track_width;
        }
        if (track_region_w < 1)
        {
          track_region_w = 1;
        }         
        indi_track_region[0].value = track_region_x;
        IDSetNumber (&indi_track_region_property, 
          "Track region x %d", track_region_x);
      }
      else if (strcmp(indi_track_region[1].name, names[i]) == 0)
      {
        track_region_y = doubles[i];
        if (track_region_y > track_height)
        {
          track_region_y = track_height;
        }
        if (track_region_y < 1)
        {
          track_region_y = 1;
        }
        indi_track_region[1].value = track_region_y;
        IDSetNumber (&indi_track_region_property, 
          "Track region y %d", track_region_y);      
      }
      else if (strcmp(indi_track_region[2].name, names[i]) == 0)
      {
        track_region_w = doubles[i];
        if (track_region_w > track_width)
        {
          track_region_w = track_width;
        }
        if (track_region_w < 1)
        {
          track_region_w = 1;
        }
        indi_track_region[2].value = track_region_w;
        IDSetNumber (&indi_track_region_property, 
          "Track region w %d", track_region_w);      
      }
      else if (strcmp(indi_track_region[3].name, names[i]) == 0)
      {
        track_region_h = doubles[i];
        if (track_region_h > track_height)
        {
          track_region_h = track_height;
        }
        if (track_region_h < 1)
        {
          track_region_h = 1;
        }
        indi_track_region[3].value = track_region_h;
        IDSetNumber (&indi_track_region_property, 
          "Track region h %d", track_region_h);      
      }
      else 
      {
        IDSetNumber (&indi_track_region_property, 
          "Track region parameter setting error");      
      }                  
    }
  }


  if (!strcmp (name, indi_filter_selection_property.name) && n > 0) 
  {    

    /* Filter selection request */

    if (indi_filter_selection_property.s != IPS_BUSY) 
    {
      indi_filter_selection.value = doubles[0];
      filter = indi_filter_selection.value;
      SetCameraFilter(filter);
      indi_filter_selection_property.s = IPS_OK;
      IDSetNumber (&indi_filter_selection_property, 
        "Selected filter %d", filter);
      GetCameraFilter(filter_name,&filter);
      IDSetText (&indi_filter_name_property, 
        "Filter name %s", filter_name);
      sprintf(fits_filter,"%s",filter_name);
    }
  }

  if (!strcmp (name, indi_coordinates_property.name) && n > 0) 
  {
    /* New celestial coordinates have been sent */
    
    for (i = 0; i < n; i++)
    {
      if (strcmp (indi_coordinates[0].name, names[i]) == 0)
      {
        indi_coordinates[0].value = doubles[i];
        right_ascension = doubles[i];
        IDSetNumber (&indi_coordinates_property, 
          "Right Ascension %10.6m", right_ascension);
      }
      else if (strcmp(indi_coordinates[1].name, names[i]) == 0)
      {
        indi_coordinates[1].value = doubles[i];
        declination = doubles[i];
        IDSetNumber (&indi_coordinates_property, 
          "Declination %10.6m", declination);      
      }
      else 
      {
        IDSetNumber (&indi_coordinates_property, 
          "Celestial coordinates setting error");      
      }                  
    }
  }

  /* New numbers have been handled */
  
}

/* Client is sending us a new blob */

void ISNewBLOB (const char *dev, const char *name, int sizes[],
  int blobsizes[], char *blobs[], char *formats[], char *names[], int n)
{
  /* This driver does not accept blobs */
}


/* Client is sending us new text */

/* In Elwood Downey's notation, the search arguments here are Tv not T */
/* In my notation the arguments are _property */


void ISNewText (const char *dev, const char *name, 
  char *texts[], char *names[], int n)
{
  if (strcmp (dev, MYDEV))
  { 
      /* Ignore if this is not for us */
      return;
  }
  
  if (!strcmp (name, indi_archive_base_name_property.name) && n > 0) 
  {  
    IText *tp = IUFindText (&indi_archive_base_name_property, names[0]);
    char *newtext = texts[0];

    if (!tp)
    {
      indi_archive_base_name_property.s = IPS_IDLE;
      IDSetText (&indi_archive_base_name_property, "New name not found");       
      return;
    }

    strncpy(base_name,newtext,17);
    base_name[16]='\0';
    indi_archive_base_name_property.s = IPS_OK;
    IDSetText (&indi_archive_base_name_property, 
      "Set base_name %s", base_name); 
    return;
  }

  if (!strcmp (name, indi_archive_target_property.name) && n > 0) 
  {  
    IText *tp = IUFindText (&indi_archive_target_property, names[0]);
    char *newtext = texts[0];

    if (!tp)
    {
      indi_archive_target_property.s = IPS_IDLE;
      IDSetText (&indi_archive_target_property, "New fits_target not found");       
      return;
    }

    strncpy(fits_target,newtext,17);
    fits_target[16]='\0';
    indi_archive_target_property.s = IPS_OK;
    IDSetText (&indi_archive_target_property, 
      "Set fits_target %s", fits_target); 
    return;
  }

  if (!strcmp (name, indi_archive_telescope_property.name) && n > 0) 
  {  
    IText *tp = IUFindText (&indi_archive_telescope_property, names[0]);
    char *newtext = texts[0];

    if (!tp)
    {
      indi_archive_telescope_property.s = IPS_IDLE;
      IDSetText (&indi_archive_telescope_property, "New fits_telescope not found");       
      return;
    }

    strncpy(fits_telescope,newtext,17);
    fits_telescope[16]='\0';
    indi_archive_telescope_property.s = IPS_OK;
    IDSetText (&indi_archive_telescope_property, 
      "Set fits_telescope %s", fits_telescope); 
    return;
  }


}

/* Client is sending us new switch settings */

/* In Elwood Downey's notation, the search arguments here are Sv not S */
/* In my notation the arguments are _property */


void  ISNewSwitch (const char *dev, const char *name, 
  ISState *states, char *names[], int n)
{
  int i;

  /* ignore if not ours */
  if (strcmp (dev, MYDEV))
      return;

  /* Test for new switch settings */

  if (!strcmp (name, indi_cooler_power_property.name)) 
  {
    for (i = 0; i < n; i++) 
    {
      ISwitch *sp = IUFindSwitch (&indi_cooler_power_property, names[i]);
      if (states[i] == ISS_ON) 
      {
        for (i = 0; i < NARRAY(indi_cooler_power); i++)
          indi_cooler_power[i].s = ISS_OFF;
        sp->s = ISS_ON;
        indi_cooler_power_property.s = IPS_OK;
        cool = TRUE;
        SetCameraTemperature(cool,setpoint);
        IDSetSwitch (&indi_cooler_power_property, 
          "Cooling power switch on at setpoint %g", setpoint);
        break;
      }
      if (states[i] == ISS_OFF) 
      {
        for (i = 0; i < NARRAY(indi_cooler_power); i++)
          indi_cooler_power[i].s = ISS_ON;
        sp->s = ISS_OFF;
        indi_cooler_power_property.s = IPS_ALERT;
        cool = FALSE;
        SetCameraTemperature(cool,setpoint);
        IDSetSwitch (&indi_cooler_power_property, "Cooling power switch off"); 
        break;
      }    
    }    
  }
  
  if (!strcmp (name, indi_image_region_control_property.name)) 
  {
    for (i = 0; i < n; i++) 
    {
      ISwitch *sp = IUFindSwitch (&indi_image_region_control_property, names[i]);
      if (states[i] == ISS_ON) 
      {
        for (i = 0; i < NARRAY(indi_image_region_control); i++)
          indi_image_region_control[i].s = ISS_OFF;
        sp->s = ISS_ON;
        indi_image_region_control_property.s = IPS_ALERT;
        image_region = TRUE;
          IDSetSwitch (&indi_image_region_control_property, 
            "Image region on");
        break;
      }
      if (states[i] == ISS_OFF) 
      {
        for (i = 0; i < NARRAY(indi_image_region_control); i++)
          indi_image_region_control[i].s = ISS_ON;
        sp->s = ISS_OFF;
        indi_image_region_control_property.s = IPS_OK;
        image_region = FALSE;
          IDSetSwitch (&indi_image_region_control_property, 
            "Image region set off");
        break;
      }      
    }
  }
  
  if (!strcmp (name, indi_track_region_control_property.name)) 
  {
    for (i = 0; i < n; i++) 
    {
      ISwitch *sp = IUFindSwitch (&indi_track_region_control_property, names[i]);
      if (states[i] == ISS_ON) 
      {
        for (i = 0; i < NARRAY(indi_track_region_control); i++)
          indi_track_region_control[i].s = ISS_OFF;
        sp->s = ISS_ON;
        indi_track_region_control_property.s = IPS_ALERT;
        track_region = TRUE;
        IDSetSwitch (&indi_track_region_control_property, 
          "Track region set on");
        break;
      }
      if (states[i] == ISS_OFF) 
      {
        for (i = 0; i < NARRAY(indi_track_region_control); i++)
          indi_track_region_control[i].s = ISS_ON;
        sp->s = ISS_OFF;
        indi_track_region_control_property.s = IPS_OK;
        track_region = FALSE;
        IDSetSwitch (&indi_track_region_control_property, 
          "Track region set off");
        break;
      }      
    }
  }
   
  if (!strcmp (name, indi_frame_type_property.name)) 
  {
    for (i = 0; i < n; i++) 
    {
      ISwitch *sp = IUFindSwitch (&indi_frame_type_property, names[i]);
      if (states[i] == ISS_ON) 
      {
        for (i = 0; i < NARRAY(indi_frame_type); i++)
          indi_frame_type[i].s = ISS_OFF;
        sp->s = ISS_ON;
        indi_frame_type_property.s = IPS_OK;
        
        /* Which frame type have we turned on? */
        
        if (!strcmp (sp->name, "dark"))
        {
          frame = DARK;
        }
        else if (!strcmp (sp->name, "light"))
        {
          frame = LIGHT;
        }
        else if (!strcmp (sp->name, "bias"))
        {
          frame = BIAS;
        }
        else if (!strcmp (sp->name, "flat"))
        {
          frame = FLAT;
        }
        else
        {
          frame = LIGHT;
        }                
                  
        /* Provide a visual alert that this is not a light frame */
        
        if (frame != LIGHT)
        {
          indi_frame_type_property.s = IPS_ALERT;
        }        
        
        IDSetSwitch (&indi_frame_type_property, 
          "Selected %s frame", sp->label);
        break;
      }
    }
  }
  
  if (!strcmp (name, indi_detector_selection_property.name)) 
  {
    for (i = 0; i < n; i++) 
    {
      ISwitch *sp = IUFindSwitch (&indi_detector_selection_property, names[i]);
      if (states[i] == ISS_ON) 
      {
        for (i = 0; i < NARRAY(indi_detector_selection); i++)
          indi_detector_selection[i].s = ISS_OFF;
        sp->s = ISS_ON;
        indi_detector_selection_property.s = IPS_OK;
 
        /* Which detector have we turned on? */
        
        if (!strcmp (sp->name, "image"))
        {
          detector = IMAGE;
        }
        else if (!strcmp (sp->name, "track"))
        {
          detector = TRACK;
        }
        else
        {      
          IDSetSwitch (&indi_detector_selection_property, 
            "Unknown detector selected");
          break;
        }

        /* Provide a visual alert that this is a dark frame */

        if (detector != IMAGE)
        {
          indi_detector_selection_property.s = IPS_ALERT;
        }
        IDSetSwitch (&indi_detector_selection_property, 
          "Selected %s detector set to %d", 
          sp->label, detector);
        break;
      }
    }
  }   
  
  if (!strcmp (name, indi_sequence_property.name)) 
  {
    for (i = 0; i < n; i++) 
    {
      ISwitch *sp = IUFindSwitch (&indi_sequence_property, names[i]);
      if (states[i] == ISS_ON) 
      {
        for (i = 0; i < NARRAY(indi_sequence); i++)
          indi_sequence[i].s = ISS_OFF;
        sp->s = ISS_ON;
        indi_sequence_property.s = IPS_BUSY;
        sequence_last = sequence;
        sequence = 1;
        IDSetSwitch (&indi_sequence_property, 
          "Sequence on");
        break;
      }
      if (states[i] == ISS_OFF) 
      {
        for (i = 0; i < NARRAY(indi_sequence); i++)
          indi_sequence[i].s = ISS_ON;
        sp->s = ISS_OFF;
        indi_sequence_property.s = IPS_OK;
        sequence_last = sequence;
        sequence = 0;
        IDSetSwitch (&indi_sequence_property, 
          "Sequence off");
        break;
      }      
    }
  }  
  
  if (!strcmp (name, indi_acquire_mode_property.name)) 
  {
    for (i = 0; i < n; i++) 
    {
      ISwitch *sp = IUFindSwitch (&indi_acquire_mode_property, names[i]);
      if (states[i] == ISS_ON) 
      {
        for (i = 0; i < NARRAY(indi_acquire_mode); i++)
          indi_acquire_mode[i].s = ISS_OFF;
        sp->s = ISS_ON;
        indi_acquire_mode_property.s = IPS_OK;

        /* Which mode have we selected? */
        
        if (!strcmp (sp->name, "single"))
        {
          acquire = SINGLE;
        }
        else if (!strcmp (sp->name, "focus"))
        {
          acquire = FOCUS;
        }
        else if (!strcmp (sp->name, "multi"))
        {
          acquire = MULTI;
        }       
        else
        {      
          IDSetSwitch (&indi_acquire_mode_property, 
            "Unknown acquisition mode selected");
          break;
        } 
          
        if (acquire != SINGLE)
        {
          indi_acquire_mode_property.s = IPS_BUSY;
        }          
        IDSetSwitch (&indi_acquire_mode_property, 
          "Acquisition mode %s", sp->label);
        break;
      }
    }
  }
  
  if (!strcmp (name, indi_guide_mode_property.name)) 
  {
    for (i = 0; i < n; i++) 
    {
      ISwitch *sp = IUFindSwitch (&indi_guide_mode_property, names[i]);
      if (states[i] == ISS_ON) 
      {
        for (i = 0; i < NARRAY(indi_guide_mode); i++)
          indi_guide_mode[i].s = ISS_OFF;
        sp->s = ISS_ON;
        indi_guide_mode_property.s = IPS_OK;

        /* Which mode have we selected? */
        
        if (!strcmp (sp->name, "unguided"))
        {
          guide = UNGUIDED;
        }
        else if (!strcmp (sp->name, "tracking"))
        {
          guide = TRACKING;
        }
        else if (!strcmp (sp->name, "optical"))
        {
          guide = OPTICAL;
        }
        else if (!strcmp (sp->name, "self_tracking"))
        {
          guide = SELFTRACKING;
        }
        else if (!strcmp (sp->name, "self_optical"))
        {
          guide = SELFOPTICAL;
        }               
        else
        {      
          IDSetSwitch (&indi_guide_mode_property, 
            "Unknown guide mode selected");
          break;
        } 

        if (guide != UNGUIDED)
        {
          indi_guide_mode_property.s = IPS_BUSY;
        }
        IDSetSwitch (&indi_guide_mode_property, 
          "Guide mode %s", sp->label);
        break;
      }
    }
  }
      
  if (!strcmp (name, indi_relay_property.name)) 
  {
    for (i = 0; i < n; i++) 
    {
      ISwitch *sp = IUFindSwitch (&indi_relay_property, names[i]);
      if (states[i] == ISS_ON) 
      {
        for (i = 0; i < NARRAY(indi_relay); i++)
          indi_relay[i].s = ISS_OFF;
        sp->s = ISS_OFF;
        indi_relay_property.s = IPS_OK;

        /* Which relay have we selected? */
        
        if (!strcmp (sp->name, "north"))
        {
          relay = NORTH;
        }
        else if (!strcmp (sp->name, "south"))
        {
          relay = SOUTH;
        }
        else if (!strcmp (sp->name, "east"))
        {
          relay = EAST;
        }  
        else if (!strcmp (sp->name, "west"))
        {
          relay = WEST;
        }    
        else
        {      
          IDSetSwitch (&indi_relay_property, 
            "Unknown relay direction selected");
          break;
        }         
        
        IDSetSwitch (&indi_relay_property, 
          "Relay direction activated %s", sp->label);

        /* Direction and mode have been set */
        /* Activate the request through the camera hardware interface */
          
        relay_ontime = RELAYONTIME;
        ActivateCameraRelay(relay, relay_ontime);
        break;
      }
    }
  }  

  if (!strcmp (name, indi_ao_property.name)) 
  {
    for (i = 0; i < n; i++) 
    {
      ISwitch *sp = IUFindSwitch (&indi_ao_property, names[i]);
      if (states[i] == ISS_ON) 
      {
        for (i = 0; i < NARRAY(indi_ao); i++)
          indi_ao[i].s = ISS_OFF;
        sp->s = ISS_OFF;
        indi_ao_property.s = IPS_OK;

        /* Which ao tip have we selected? */
        
        if (!strcmp (sp->name, "north"))
        {
          tiptilt = NORTH;
        }
        else if (!strcmp (sp->name, "south"))
        {
          tiptilt = SOUTH;
        }
        else if (!strcmp (sp->name, "east"))
        {
          tiptilt = EAST;
        }  
        else if (!strcmp (sp->name, "west"))
        {
          tiptilt = WEST;
        }    
        else
        {      
          IDSetSwitch (&indi_ao_property, 
            "Unknown ao tip/tilt selected");
          break;
        }         
        
        IDSetSwitch (&indi_ao_property, 
          "AO tip activated %s", sp->label);

        /* Tip has been set */
        /* Activate the request through the camera hardware interface */
          
        if (tiptilt == NORTH)
        {
          ao_y = ao_y + 1;
          if (ao_y > 4095)
          {
            ao_y = 4095;
          }            
        }
        if (tiptilt == SOUTH)
        {
          ao_y = ao_y - 1;
          if (ao_y < 1)
          {
            ao_y = 1;
          } 
        } 
        if (tiptilt == EAST)
        {
          ao_x = ao_x + 1;
          if (ao_x > 4095)
          {
            ao_x = 4095;
          }
        }
        if (tiptilt == WEST)
        {
          ao_x = ao_x - 1;
          if (ao_y < 1)
          {
            ao_y = 1;
          }             
        }                    
        SetCameraAOTipTilt(ao_x,ao_y);
        break;
      }
    }
  }  


  if (!strcmp (name, indi_image_transfer_mode_property.name)) 
  {
    for (i = 0; i < n; i++) 
    {
      ISwitch *sp = IUFindSwitch (&indi_image_transfer_mode_property, names[i]);
      if (states[i] == ISS_ON) 
      {
        for (i = 0; i < NARRAY(indi_image_transfer_mode); i++)
          indi_image_transfer_mode[i].s = ISS_OFF;
        sp->s = ISS_ON;
        indi_image_transfer_mode_property.s = IPS_OK;
 
        /* Which mode have we selected? */
        
        if (!strcmp (sp->name, "none"))
        {
          transfer = NOCOPY;
        }
        else if (!strcmp (sp->name, "network"))
        {
          transfer = NETCOPY;
        }        
        else if (!strcmp (sp->name, "indi"))
        {
          transfer = INDICOPY;
        }
        else
        {      
          IDSetSwitch (&indi_guide_mode_property, 
            "Unknown transfer mode selected");
          break;
        }          
        transfer = TRUE;
        IDSetSwitch (&indi_image_transfer_mode_property, 
          "Transfer mode %s", sp->label);
        break;
      }                
    }
  }
  
  /* New switch settings have been handled */

}


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

static void camera (void *p)
{
  static int tcount;
  
  if ((tcount < 0) || (tcount > 60))
  {
    /* Take the device temperature every 15 s on a 250 ms polling cycle */
    tcount = 1;
    GetCameraTemperature(&cooler_power,&temperature,&setpoint);
    indi_camera_temperature.value = temperature;
    IDSetNumber (&indi_camera_temperature_property, NULL);
  }
  tcount++;
    
  if ((sequence < sequence_last))
  {
    /* Downward transition of sequence has flagged an interrupt */
        
    if ( phase != 0 )
    {
      /* End any current exposure */
      
      phase = 2;
      CaptureEnd(&phase); 
    }
      
    frame_count = 1;
    phase = 0;
    sequence_last = sequence;
    sequence = 0;                    
    
    /* Set the exposure property to show that there is no exposure */
    indi_image_exposure_property.s = IPS_OK;
    IDSetNumber (&indi_image_exposure_property, "Exposure interrupted");
    
    /* Set the sequence switch to show that it is not busy */
    indi_sequence[0].s = ISS_OFF;
    indi_sequence_property.s = IPS_OK;
    IDSetSwitch (&indi_sequence_property, "Waiting to start a sequence");           

    /* Set exposures to show starting values */
    if ( detector == TRACK )
    {
      indi_track_exposure.value = track_exposure;
      IDSetNumber (&indi_track_exposure_property, NULL);
    }
    else
    {
      indi_image_exposure.value = image_exposure;
      IDSetNumber (&indi_image_exposure_property, NULL);
    } 
    
  }
    
  if ((sequence > sequence_last)  && (phase == 0))
  {
    /* Start a new sequence */
        
    sequence_last = sequence;
    sequence = 1;
    frame_count = 1;
    
    if (acquire == SINGLE)
    {
      frame_count_max = 1;
    }
    else
    {
      frame_count_max = frame_count_multi;
    } 

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

    /* Set the sequence switch to busy */
    indi_sequence_property.s = IPS_BUSY;
    IDSetSwitch (&indi_sequence_property, "Starting a sequence");
  }
     
  if ((sequence == 1) && ( phase == 0 )) 
  {               
        
    
    /* Start a new exposure */
    /* 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; 
      indi_image_exposure_property.s = IPS_BUSY;
      IDSetNumber (&indi_image_exposure_property, "Guided exposure in progress");
    }
    else 
    {      

      /* Start an exposure with the detector of choice */
      
      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);
        indi_track_exposure_property.s = IPS_BUSY;
        IDSetNumber (&indi_track_exposure_property, "Track exposure in progress");
      }
      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);
        indi_image_exposure_property.s = IPS_BUSY;
        IDSetNumber (&indi_image_exposure_property, "Image exposure in progress");
      }
    }                
  }  

  /* 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)
      {
        indi_track_exposure.value = track_exposure;
        indi_track_exposure_property.s = IPS_OK;
        IDSetNumber (&indi_track_exposure_property, "Track readout");
      }
      else
      {
        indi_image_exposure.value = image_exposure;
        indi_image_exposure_property.s = IPS_OK;
        IDSetNumber (&indi_image_exposure_property, "Image readout");
      }
      
      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 */
      
      /* Update exposure time counters */
      
      if ( detector == TRACK )
      {
        indi_track_exposure.value = current_exposure - dt;
        IDSetNumber (&indi_track_exposure_property, NULL);
      }
      else
      {
        indi_image_exposure.value = current_exposure - dt;
        IDSetNumber (&indi_image_exposure_property, NULL);
      }      

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

    /* 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 with an appropriate name */
  
      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);
        IDSetText (&indi_file_name_property, 
          "File name %s", file_name); 
      }
      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);
        IDSetText (&indi_file_name_property, 
          "File name %s", file_name); 
      }
    }
    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);
        IDSetText (&indi_file_name_property, 
          "File name %s", file_name);         
      }
      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);
        IDSetText (&indi_file_name_property, 
          "File name %s", file_name);   
      }   
    }

    /* Send the frame by the method selected with transfer switch */
    
    if (transfer != NOCOPY)
    {
      if (transfer == INDICOPY)
      {    
        IDSetSwitch (&indi_image_transfer_mode_property, 
          "Sending frame by INDI");
        if (sendblob(file_name) == 0) 
        {
          indi_image_transfer_mode_property.s = IPS_ALERT;
          IDSetSwitch (&indi_image_transfer_mode_property, 
            "Indi frame transfer error");
        } 
        else 
        {
          indi_image_transfer_mode_property.s = IPS_OK;
          IDSetSwitch (&indi_image_transfer_mode_property, 
            "Frame sent by INDI");
        }
      }
      else if (transfer == NETCOPY)
      {    
        IDSetSwitch (&indi_image_transfer_mode_property, 
          "Frame transfer requested");
        if (sendimage(file_name) == 0) 
        {
          indi_image_transfer_mode_property.s = IPS_ALERT;
          IDSetSwitch (&indi_image_transfer_mode_property, 
            "Frame transfer error");
        } 
        else 
        {
          indi_image_transfer_mode_property.s = IPS_OK;
          IDSetSwitch (&indi_image_transfer_mode_property, 
            "Frame dispatched");
        }
      }     
    }

    /* 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);                   
        }   
      }    
    }
    
    /* Reset for the next exposure */
    
    if ( detector == TRACK )
    {
      indi_track_exposure.value = track_exposure;
      IDSetNumber (&indi_track_exposure_property, NULL);
    }
    else
    {
      indi_image_exposure.value = image_exposure;
      IDSetNumber (&indi_image_exposure_property, NULL);
    } 
        
    phase = 0;
    frame_count++;
    
    if ( frame_count > frame_count_max )
    {
      sequence_last = sequence;
      sequence = 0;
      frame_count = 1;
      indi_sequence[0].s = ISS_OFF; 
      indi_sequence_property.s = IPS_OK;      
      IDSetSwitch (&indi_sequence_property, "Waiting to start a sequence");
    }
  }   
      
  /* Start the timer again */
  IEAddTimer (POLLMS, camera, NULL);
}

/* Send the latest fits file through a system network routine */
/* Return 1 if sent, 0 if not sent */

static int sendimage(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);

}


/* Send the latest fits file as a BLOB to the client through INDI */
/* No compression -- this version does not use zlib */
/* Return 1 if ok or else return 0 */

int sendblob(char *name)
{
  
  FILE *fp = fopen (name, "r");
  if ( fp == NULL )
  {
    return (0);
  }   
  
  char *blob = malloc (4096);
  int n, nblob = 0;
  int size;

  /* Read into memory */
  
  while ((n = fread (blob+nblob, 1, 4096, fp)) > 0)
  {
    blob = realloc (blob, (nblob+=n)+4096);
  }
  fclose (fp);
  size = nblob;

  /* Alert the operator */
  
  strcpy (indi_image_blob.format, ".fits");
  fprintf (stderr, "Sending %s through INDI", name);

  /* Send */
  
  indi_image_blob.blob = blob;
  indi_image_blob.bloblen = nblob;
  indi_image_blob.size = size;
  indi_image_blob_property.s = IPS_OK;
  IDSetBLOB (&indi_image_blob_property, NULL);
  free (blob);
  return (1);
}


/* End of  INDI functions */

/* 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;
          frame_count_max = frame_count_multi;
          fprintf(stderr,"Configuration error: attempt to set multi frame count < 2\n");
        }
        else
        {       
          frame_count_max = frame_count_multi;
          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);      
        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); 
        if (test_flag == 1)
        {
          guide_log_flag = TRUE;
          fprintf(stderr,"guide_log_flag true\n");
        }
        else
        {
          guide_log_flag = FALSE;
          fprintf(stderr,"guide_log_flag false\n");
        } 
      }  
    }     

    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);

}


