/* ----------------------------------------------------------------------- */
/* -                 Astronomical CCD Camera Control                     - */
/* -                       SBIG Camera Protocol                          - */
/* ----------------------------------------------------------------------- */
/*                                                                         */
/* Copyright 2010 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.                            */   
/*                                                                         */
/* Date: May 30, 2011                                                      */
/*                                                                         */
/* Version 3.1.3                                                           */
/*   New cfitsio libraries                                                 */
/*   Documented issue with filter wheel selection                          */
/*                                                                         */
/* Version 3.0.9                                                           */
/*   Updated to SBIG to match Apogee version                               */
/*                                                                         */
/* Version 3.0.6                                                           */
/*   CaptureEnd function added for universal handling of interrupt         */
/*                                                                         */
/* Version 3.0.5                                                           */
/*   Completely revised in version 3.0                                     */
/*   New functions should be adaptable to any CCD                          */
/*   Recursive capture routines enable monitoring exposure progress        */
/*   Subarea imaging                                                       */
/*   Internal guiding                                                      */
/*                                                                         */
/* Version 3.0.2                                                           */
/*   New keywords added to fits header                                     */
/*                                                                         */
/* Version 3.0.3                                                           */
/*   Tested release version                                                */
/*                                                                         */
/* Version 3.0.4                                                           */
/*   External filter wheel option added                                    */
/*                                                                         */
/* For use with XmCCD and the INDI driver                                  */
/*                                                                         */
/* ----------------------------------------------------------------------- */



/* System */

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


/* Define constants */

#include "protocol.h"


/* Include the cfitsio library */

#include <fitsio.h>


/* The follow functions are exported and availble for use in other programs */
/* As much as possible these prototypes will be stable in subsequent versions */

/* Camera system operations */
/* return(1) on success, 0 on failure */
/* May send messages to stderr */

int OpenCamera(int device);     

int CloseCamera(int device);    

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);
 
int GetCameraFilter(char *filter_name, int *filter);

int GetCameraStatus(int *image_status, int *track_status);
int GetCameraTemperature(double *power, 
  double *temperature, double *setpoint);
int SetCameraTemperature(int state, double temperature);
int SetCameraFilter(int filter);
int SetCameraFilterName(int flag,  
  int new_filter_number, char *new_filter_name);
int SetCameraAOTipTilt(int ao_x, int ao_y);
int ActivateCameraRelay(int relay, double ontime);
int SetCameraGuideParameters(int threshold,
  double x2trackx, double y2trackx, double x2tracky, double y2tracky,
  double x2aox, double y2aox, double x2aoy, double y2aoy);
             
/* Image capture functions for external use                    */
/* Returns 1 on success and 0 on failure                       */
/* May send messages to stderr                                 */
/* Images are always saved as a file on disk                   */
/* Most recent image is always available in allocated storage  */

/* Reads these  parameters as needed:                          */
/*   phase     0 start exposure                                */
/*             1 readout if ready                              */
/*             2 interrupt and reset                           */
/*   frame     0 dark                                          */
/*             1 light                                         */
/*             2 bias                                          */
/*             3 flat                                          */
/*   exposure  time in seconds                                 */
/*   data      malloc'd image storage                          */
/*   subarea   TRUE or FALSE for subarea extraction            */
/*   x         initial x for subarea                           */
/*   y         initial y for subarea                           */
/*   width     width of subarea                                */
/*   height    height of subarea                               */
/*   guide     0 unguided                                      */
/*             1 tracking                                      */
/*             2 optical                                       */
/*   phase     0 exposure not started                          */
/*             1 exposure in progress                          */
/*             2 exposure complete                             */


/* Usage:                                                      */
/*   Make the first call with phase = 0 to start an exposure   */
/*   Make repeated calls with phase = 1 to ask for a readout   */
/*   User may delay asking for phase 1 or poll                 */
/*   Make one call with phase = 2 to interrupt an exposure     */
/*   Return value of phase indicates whether image was read    */
/*   Function will set phase = 2 when the exposure is done     */
/*   A minimum of two calls are required to capture an image   */


int CaptureImage(int *phase,  unsigned short *data,
  int frame, double exposure, int subarea, 
  int x, int y, int width, int height);
  
int CaptureTrack(int *phase, unsigned short *data,
  int frame, double exposure, int subarea, 
  int x, int y, int width, int height);
  
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 guide_log_flag);

int CaptureEnd(int *phase);

int GuideImage(unsigned short *data,
  int width, int height,
  int guide, int guide_flag, int guide_log_flag);  

/* Save an image as in FITS file format with header information */
  
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);    
  
/* The following functions are intended primarily for internal use */

/* Error handler used in WriteFits */

void show_cfitsio_error(int status);

/* Guiding utilities */
/*                                                                           */
/*   find_guide_star() scans a track subarea image for the brightest star    */
/*     and calculates its position and returns the coordinates               */
/*                                                                           */
/*   feedback_tracking() and feedback_ao() control pointing                  */
/*     by using drive or ao system to hold star in place                     */
                    
void feedback_tracking(double x_err, double y_err);
           
void feedback_ao(double x_err, double y_err);

void find_guide_star(double *data, int w, int h, double *x_s, double *y_s);    
    

/* SBIG variable prototypes */

OpenDeviceParams                odp;
GetErrorStringParams            gesp;
GetErrorStringResults           gesr;
EstablishLinkParams             elp;
EstablishLinkResults            elr;
GetCCDInfoParams                gip;
GetCCDInfoResults0              info_results_imaging;
GetCCDInfoResults0              info_results_tracking;
QueryCommandStatusParams        qcsp;
QueryCommandStatusResults       qcsr;
QueryTemperatureStatusResults   qtsr;
SetTemperatureRegulationParams  strp;
CFWParams                       cfwp; 
CFWResults                      cfwr;
AOTipTiltParams                 attp;
ActivateRelayParams             arp;
StartExposureParams             sep;
EndExposureParams               eep;
StartReadoutParams              srp;
MiscellaneousControlParams      mcp;
ReadoutLineParams               rlp;
GetDriverInfoParams             gdip;
GetDriverInfoResults0           gdir;

/* A fixed ip address for SBIG ethernet cameras */

unsigned long camera_ip_address = CCD_ETH; 

/* SBIG CCD temperature lookup table */

static short ccd_temp[4096] = {
10000,2134,1950,1842,1766,1707,1659,1618,1582,1551,1523,1498,
1475,1453,1434,1415,1398,1382,1367,1353,1339,1326,1314,1302,
1290,1279,1269,1259,1249,1240,1231,1222,1214,1205,1197,1190,
1182,1175,1168,1161,1154,1147,1141,1135,1129,1122,1117,1111,
1105,1100,1094,1089,1084,1079,1074,1069,1064,1059,1054,1050,
1045,1041,1036,1032,1028,1024,1020,1016,1012,1008,1004,1000,
996,993,989,985,982,978,975,971,968,964,961,958,
955,951,948,945,942,939,936,933,930,927,924,921,
918,916,913,910,907,905,902,899,897,894,892,889,
886,884,881,879,877,874,872,869,867,865,862,860,
858,855,853,851,849,847,844,842,840,838,836,834,
832,830,828,826,824,822,820,818,816,814,812,810,
808,806,804,802,800,798,797,795,793,791,789,788,
786,784,782,781,779,777,775,774,772,770,769,767,
765,764,762,761,759,757,756,754,753,751,749,748,
746,745,743,742,740,739,737,736,734,733,731,730,
728,727,726,724,723,721,720,718,717,716,714,713,
712,710,709,707,706,705,703,702,701,700,698,697,
696,694,693,692,690,689,688,687,685,684,683,682,
680,679,678,677,676,674,673,672,671,670,668,667,
666,665,664,663,661,660,659,658,657,656,655,653,
652,651,650,649,648,647,646,645,643,642,641,640,
639,638,637,636,635,634,633,632,631,630,629,628,
627,626,625,623,622,621,620,619,618,617,616,615,
614,613,612,611,611,610,609,608,607,606,605,604,
603,602,601,600,599,598,597,596,595,594,593,592,
592,591,590,589,588,587,586,585,584,583,583,582,
581,580,579,578,577,576,575,575,574,573,572,571,
570,569,569,568,567,566,565,564,563,563,562,561,
560,559,558,558,557,556,555,554,554,553,552,551,
550,549,549,548,547,546,545,545,544,543,542,542,
541,540,539,538,538,537,536,535,535,534,533,532,
531,531,530,529,528,528,527,526,525,525,524,523,
522,522,521,520,520,519,518,517,517,516,515,514,
514,513,512,512,511,510,509,509,508,507,507,506,
505,504,504,503,502,502,501,500,500,499,498,498,
497,496,495,495,494,493,493,492,491,491,490,489,
489,488,487,487,486,485,485,484,483,483,482,481,
481,480,479,479,478,478,477,476,476,475,474,474,
473,472,472,471,470,470,469,469,468,467,467,466,
465,465,464,464,463,462,462,461,460,460,459,459,
458,457,457,456,456,455,454,454,453,453,452,451,
451,450,450,449,448,448,447,447,446,445,445,444,
444,443,442,442,441,441,440,440,439,438,438,437,
437,436,436,435,434,434,433,433,432,432,431,430,
430,429,429,428,428,427,426,426,425,425,424,424,
423,423,422,421,421,420,420,419,419,418,418,417,
417,416,415,415,414,414,413,413,412,412,411,411,
410,410,409,408,408,407,407,406,406,405,405,404,
404,403,403,402,402,401,401,400,399,399,398,398,
397,397,396,396,395,395,394,394,393,393,392,392,
391,391,390,390,389,389,388,388,387,387,386,386,
385,385,384,384,383,383,382,382,381,381,380,380,
379,379,378,378,377,377,376,376,375,375,374,374,
373,373,372,372,371,371,370,370,370,369,369,368,
368,367,367,366,366,365,365,364,364,363,363,362,
362,361,361,360,360,360,359,359,358,358,357,357,
356,356,355,355,354,354,353,353,353,352,352,351,
351,350,350,349,349,348,348,348,347,347,346,346,
345,345,344,344,343,343,343,342,342,341,341,340,
340,339,339,339,338,338,337,337,336,336,335,335,
335,334,334,333,333,332,332,331,331,331,330,330,
329,329,328,328,328,327,327,326,326,325,325,325,
324,324,323,323,322,322,322,321,321,320,320,319,
319,319,318,318,317,317,316,316,316,315,315,314,
314,314,313,313,312,312,311,311,311,310,310,309,
309,309,308,308,307,307,306,306,306,305,305,304,
304,304,303,303,302,302,302,301,301,300,300,299,
299,299,298,298,297,297,297,296,296,295,295,295,
294,294,293,293,293,292,292,291,291,291,290,290,
289,289,289,288,288,287,287,287,286,286,286,285,
285,284,284,284,283,283,282,282,282,281,281,280,
280,280,279,279,278,278,278,277,277,277,276,276,
275,275,275,274,274,273,273,273,272,272,272,271,
271,270,270,270,269,269,269,268,268,267,267,267,
266,266,266,265,265,264,264,264,263,263,263,262,
262,261,261,261,260,260,260,259,259,258,258,258,
257,257,257,256,256,255,255,255,254,254,254,253,
253,253,252,252,251,251,251,250,250,250,249,249,
248,248,248,247,247,247,246,246,246,245,245,245,
244,244,243,243,243,242,242,242,241,241,241,240,
240,239,239,239,238,238,238,237,237,237,236,236,
236,235,235,235,234,234,233,233,233,232,232,232,
231,231,231,230,230,230,229,229,229,228,228,227,
227,227,226,226,226,225,225,225,224,224,224,223,
223,223,222,222,222,221,221,221,220,220,220,219,
219,218,218,218,217,217,217,216,216,216,215,215,
215,214,214,214,213,213,213,212,212,212,211,211,
211,210,210,210,209,209,209,208,208,208,207,207,
207,206,206,206,205,205,205,204,204,204,203,203,
203,202,202,202,201,201,201,200,200,200,199,199,
199,198,198,198,197,197,197,196,196,196,195,195,
195,194,194,194,193,193,193,192,192,192,191,191,
191,190,190,190,189,189,189,188,188,188,188,187,
187,187,186,186,186,185,185,185,184,184,184,183,
183,183,182,182,182,181,181,181,180,180,180,179,
179,179,178,178,178,178,177,177,177,176,176,176,
175,175,175,174,174,174,173,173,173,172,172,172,
171,171,171,171,170,170,170,169,169,169,168,168,
168,167,167,167,166,166,166,166,165,165,165,164,
164,164,163,163,163,162,162,162,161,161,161,161,
160,160,160,159,159,159,158,158,158,157,157,157,
157,156,156,156,155,155,155,154,154,154,153,153,
153,153,152,152,152,151,151,151,150,150,150,149,
149,149,149,148,148,148,147,147,147,146,146,146,
145,145,145,145,144,144,144,143,143,143,142,142,
142,142,141,141,141,140,140,140,139,139,139,139,
138,138,138,137,137,137,136,136,136,136,135,135,
135,134,134,134,133,133,133,133,132,132,132,131,
131,131,130,130,130,130,129,129,129,128,128,128,
128,127,127,127,126,126,126,125,125,125,125,124,
124,124,123,123,123,122,122,122,122,121,121,121,
120,120,120,120,119,119,119,118,118,118,118,117,
117,117,116,116,116,115,115,115,115,114,114,114,
113,113,113,113,112,112,112,111,111,111,111,110,
110,110,109,109,109,109,108,108,108,107,107,107,
106,106,106,106,105,105,105,104,104,104,104,103,
103,103,102,102,102,102,101,101,101,100,100,100,
100,99,99,99,98,98,98,98,97,97,97,96,
96,96,96,95,95,95,94,94,94,94,93,93,
93,92,92,92,92,91,91,91,90,90,90,90,
89,89,89,89,88,88,88,87,87,87,87,86,
86,86,85,85,85,85,84,84,84,83,83,83,
83,82,82,82,81,81,81,81,80,80,80,80,
79,79,79,78,78,78,78,77,77,77,76,76,
76,76,75,75,75,75,74,74,74,73,73,73,
73,72,72,72,71,71,71,71,70,70,70,70,
69,69,69,68,68,68,68,67,67,67,66,66,
66,66,65,65,65,65,64,64,64,63,63,63,
63,62,62,62,62,61,61,61,60,60,60,60,
59,59,59,58,58,58,58,57,57,57,57,56,
56,56,55,55,55,55,54,54,54,54,53,53,
53,52,52,52,52,51,51,51,51,50,50,50,
49,49,49,49,48,48,48,48,47,47,47,46,
46,46,46,45,45,45,45,44,44,44,44,43,
43,43,42,42,42,42,41,41,41,41,40,40,
40,39,39,39,39,38,38,38,38,37,37,37,
37,36,36,36,35,35,35,35,34,34,34,34,
33,33,33,32,32,32,32,31,31,31,31,30,
30,30,30,29,29,29,28,28,28,28,27,27,
27,27,26,26,26,26,25,25,25,24,24,24,
24,23,23,23,23,22,22,22,22,21,21,21,
20,20,20,20,19,19,19,19,18,18,18,18,
17,17,17,16,16,16,16,15,15,15,15,14,
14,14,14,13,13,13,12,12,12,12,11,11,
11,11,10,10,10,10,9,9,9,9,8,8,
8,7,7,7,7,6,6,6,6,5,5,5,
5,4,4,4,3,3,3,3,2,2,2,2,
1,1,1,1,0,0,0,0,0,0,0,-1,
-1,-1,-1,-2,-2,-2,-2,-3,-3,-3,-3,-4,
-4,-4,-4,-5,-5,-5,-5,-6,-6,-6,-7,-7,
-7,-7,-8,-8,-8,-8,-9,-9,-9,-9,-10,-10,
-10,-10,-11,-11,-11,-12,-12,-12,-12,-13,-13,-13,
-13,-14,-14,-14,-14,-15,-15,-15,-15,-16,-16,-16,
-16,-17,-17,-17,-18,-18,-18,-18,-19,-19,-19,-19,
-20,-20,-20,-20,-21,-21,-21,-21,-22,-22,-22,-22,
-23,-23,-23,-24,-24,-24,-24,-25,-25,-25,-25,-26,
-26,-26,-26,-27,-27,-27,-27,-28,-28,-28,-28,-29,
-29,-29,-30,-30,-30,-30,-31,-31,-31,-31,-32,-32,
-32,-32,-33,-33,-33,-33,-34,-34,-34,-34,-35,-35,
-35,-35,-36,-36,-36,-37,-37,-37,-37,-38,-38,-38,
-38,-39,-39,-39,-39,-40,-40,-40,-40,-41,-41,-41,
-41,-42,-42,-42,-42,-43,-43,-43,-44,-44,-44,-44,
-45,-45,-45,-45,-46,-46,-46,-46,-47,-47,-47,-47,
-48,-48,-48,-48,-49,-49,-49,-49,-50,-50,-50,-51,
-51,-51,-51,-52,-52,-52,-52,-53,-53,-53,-53,-54,
-54,-54,-54,-55,-55,-55,-55,-56,-56,-56,-56,-57,
-57,-57,-58,-58,-58,-58,-59,-59,-59,-59,-60,-60,
-60,-60,-61,-61,-61,-61,-62,-62,-62,-62,-63,-63,
-63,-63,-64,-64,-64,-65,-65,-65,-65,-66,-66,-66,
-66,-67,-67,-67,-67,-68,-68,-68,-68,-69,-69,-69,
-69,-70,-70,-70,-70,-71,-71,-71,-71,-72,-72,-72,
-73,-73,-73,-73,-74,-74,-74,-74,-75,-75,-75,-75,
-76,-76,-76,-76,-77,-77,-77,-77,-78,-78,-78,-78,
-79,-79,-79,-80,-80,-80,-80,-81,-81,-81,-81,-82,
-82,-82,-82,-83,-83,-83,-83,-84,-84,-84,-84,-85,
-85,-85,-85,-86,-86,-86,-86,-87,-87,-87,-88,-88,
-88,-88,-89,-89,-89,-89,-90,-90,-90,-90,-91,-91,
-91,-91,-92,-92,-92,-92,-93,-93,-93,-93,-94,-94,
-94,-95,-95,-95,-95,-96,-96,-96,-96,-97,-97,-97,
-97,-98,-98,-98,-98,-99,-99,-99,-99,-100,-100,-100,
-100,-101,-101,-101,-102,-102,-102,-102,-103,-103,-103,-103,
-104,-104,-104,-104,-105,-105,-105,-105,-106,-106,-106,-106,
-107,-107,-107,-108,-108,-108,-108,-109,-109,-109,-109,-110,
-110,-110,-110,-111,-111,-111,-111,-112,-112,-112,-112,-113,
-113,-113,-113,-114,-114,-114,-115,-115,-115,-115,-116,-116,
-116,-116,-117,-117,-117,-117,-118,-118,-118,-118,-119,-119,
-119,-119,-120,-120,-120,-121,-121,-121,-121,-122,-122,-122,
-122,-123,-123,-123,-123,-124,-124,-124,-124,-125,-125,-125,
-126,-126,-126,-126,-127,-127,-127,-127,-128,-128,-128,-128,
-129,-129,-129,-129,-130,-130,-130,-130,-131,-131,-131,-132,
-132,-132,-132,-133,-133,-133,-133,-134,-134,-134,-134,-135,
-135,-135,-135,-136,-136,-136,-137,-137,-137,-137,-138,-138,
-138,-138,-139,-139,-139,-139,-140,-140,-140,-140,-141,-141,
-141,-142,-142,-142,-142,-143,-143,-143,-143,-144,-144,-144,
-144,-145,-145,-145,-145,-146,-146,-146,-147,-147,-147,-147,
-148,-148,-148,-148,-149,-149,-149,-149,-150,-150,-150,-151,
-151,-151,-151,-152,-152,-152,-152,-153,-153,-153,-153,-154,
-154,-154,-154,-155,-155,-155,-156,-156,-156,-156,-157,-157,
-157,-157,-158,-158,-158,-158,-159,-159,-159,-160,-160,-160,
-160,-161,-161,-161,-161,-162,-162,-162,-162,-163,-163,-163,
-164,-164,-164,-164,-165,-165,-165,-165,-166,-166,-166,-166,
-167,-167,-167,-168,-168,-168,-168,-169,-169,-169,-169,-170,
-170,-170,-171,-171,-171,-171,-172,-172,-172,-172,-173,-173,
-173,-173,-174,-174,-174,-175,-175,-175,-175,-176,-176,-176,
-176,-177,-177,-177,-177,-178,-178,-178,-179,-179,-179,-179,
-180,-180,-180,-180,-181,-181,-181,-182,-182,-182,-182,-183,
-183,-183,-183,-184,-184,-184,-185,-185,-185,-185,-186,-186,
-186,-186,-187,-187,-187,-188,-188,-188,-188,-189,-189,-189,
-189,-190,-190,-190,-191,-191,-191,-191,-192,-192,-192,-192,
-193,-193,-193,-194,-194,-194,-194,-195,-195,-195,-195,-196,
-196,-196,-197,-197,-197,-197,-198,-198,-198,-198,-199,-199,
-199,-200,-200,-200,-200,-201,-201,-201,-201,-202,-202,-202,
-203,-203,-203,-203,-204,-204,-204,-205,-205,-205,-205,-206,
-206,-206,-206,-207,-207,-207,-208,-208,-208,-208,-209,-209,
-209,-209,-210,-210,-210,-211,-211,-211,-211,-212,-212,-212,
-213,-213,-213,-213,-214,-214,-214,-214,-215,-215,-215,-216,
-216,-216,-216,-217,-217,-217,-218,-218,-218,-218,-219,-219,
-219,-220,-220,-220,-220,-221,-221,-221,-221,-222,-222,-222,
-223,-223,-223,-223,-224,-224,-224,-225,-225,-225,-225,-226,
-226,-226,-227,-227,-227,-227,-228,-228,-228,-229,-229,-229,
-229,-230,-230,-230,-231,-231,-231,-231,-232,-232,-232,-233,
-233,-233,-233,-234,-234,-234,-235,-235,-235,-235,-236,-236,
-236,-236,-237,-237,-237,-238,-238,-238,-238,-239,-239,-239,
-240,-240,-240,-241,-241,-241,-241,-242,-242,-242,-243,-243,
-243,-243,-244,-244,-244,-245,-245,-245,-245,-246,-246,-246,
-247,-247,-247,-247,-248,-248,-248,-249,-249,-249,-249,-250,
-250,-250,-251,-251,-251,-251,-252,-252,-252,-253,-253,-253,
-254,-254,-254,-254,-255,-255,-255,-256,-256,-256,-256,-257,
-257,-257,-258,-258,-258,-258,-259,-259,-259,-260,-260,-260,
-261,-261,-261,-261,-262,-262,-262,-263,-263,-263,-263,-264,
-264,-264,-265,-265,-265,-266,-266,-266,-266,-267,-267,-267,
-268,-268,-268,-269,-269,-269,-269,-270,-270,-270,-271,-271,
-271,-272,-272,-272,-272,-273,-273,-273,-274,-274,-274,-275,
-275,-275,-275,-276,-276,-276,-277,-277,-277,-278,-278,-278,
-278,-279,-279,-279,-280,-280,-280,-281,-281,-281,-281,-282,
-282,-282,-283,-283,-283,-284,-284,-284,-284,-285,-285,-285,
-286,-286,-286,-287,-287,-287,-288,-288,-288,-288,-289,-289,
-289,-290,-290,-290,-291,-291,-291,-292,-292,-292,-292,-293,
-293,-293,-294,-294,-294,-295,-295,-295,-296,-296,-296,-296,
-297,-297,-297,-298,-298,-298,-299,-299,-299,-300,-300,-300,
-300,-301,-301,-301,-302,-302,-302,-303,-303,-303,-304,-304,
-304,-305,-305,-305,-305,-306,-306,-306,-307,-307,-307,-308,
-308,-308,-309,-309,-309,-310,-310,-310,-311,-311,-311,-311,
-312,-312,-312,-313,-313,-313,-314,-314,-314,-315,-315,-315,
-316,-316,-316,-317,-317,-317,-318,-318,-318,-318,-319,-319,
-319,-320,-320,-320,-321,-321,-321,-322,-322,-322,-323,-323,
-323,-324,-324,-324,-325,-325,-325,-326,-326,-326,-327,-327,
-327,-328,-328,-328,-329,-329,-329,-329,-330,-330,-330,-331,
-331,-331,-332,-332,-332,-333,-333,-333,-334,-334,-334,-335,
-335,-335,-336,-336,-336,-337,-337,-337,-338,-338,-338,-339,
-339,-339,-340,-340,-340,-341,-341,-341,-342,-342,-342,-343,
-343,-343,-344,-344,-344,-345,-345,-345,-346,-346,-346,-347,
-347,-347,-348,-348,-348,-349,-349,-349,-350,-350,-350,-351,
-351,-351,-352,-352,-353,-353,-353,-354,-354,-354,-355,-355,
-355,-356,-356,-356,-357,-357,-357,-358,-358,-358,-359,-359,
-359,-360,-360,-360,-361,-361,-361,-362,-362,-362,-363,-363,
-364,-364,-364,-365,-365,-365,-366,-366,-366,-367,-367,-367,
-368,-368,-368,-369,-369,-369,-370,-370,-371,-371,-371,-372,
-372,-372,-373,-373,-373,-374,-374,-374,-375,-375,-375,-376,
-376,-377,-377,-377,-378,-378,-378,-379,-379,-379,-380,-380,
-380,-381,-381,-382,-382,-382,-383,-383,-383,-384,-384,-384,
-385,-385,-386,-386,-386,-387,-387,-387,-388,-388,-388,-389,
-389,-390,-390,-390,-391,-391,-391,-392,-392,-392,-393,-393,
-394,-394,-394,-395,-395,-395,-396,-396,-397,-397,-397,-398,
-398,-398,-399,-399,-400,-400,-400,-401,-401,-401,-402,-402,
-403,-403,-403,-404,-404,-404,-405,-405,-406,-406,-406,-407,
-407,-407,-408,-408,-409,-409,-409,-410,-410,-410,-411,-411,
-412,-412,-412,-413,-413,-414,-414,-414,-415,-415,-415,-416,
-416,-417,-417,-417,-418,-418,-419,-419,-419,-420,-420,-421,
-421,-421,-422,-422,-422,-423,-423,-424,-424,-424,-425,-425,
-426,-426,-426,-427,-427,-428,-428,-428,-429,-429,-430,-430,
-430,-431,-431,-432,-432,-432,-433,-433,-434,-434,-434,-435,
-435,-436,-436,-436,-437,-437,-438,-438,-438,-439,-439,-440,
-440,-441,-441,-441,-442,-442,-443,-443,-443,-444,-444,-445,
-445,-445,-446,-446,-447,-447,-448,-448,-448,-449,-449,-450,
-450,-450,-451,-451,-452,-452,-453,-453,-453,-454,-454,-455,
-455,-455,-456,-456,-457,-457,-458,-458,-458,-459,-459,-460,
-460,-461,-461,-461,-462,-462,-463,-463,-464,-464,-464,-465,
-465,-466,-466,-467,-467,-467,-468,-468,-469,-469,-470,-470,
-471,-471,-471,-472,-472,-473,-473,-474,-474,-475,-475,-475,
-476,-476,-477,-477,-478,-478,-479,-479,-479,-480,-480,-481,
-481,-482,-482,-483,-483,-483,-484,-484,-485,-485,-486,-486,
-487,-487,-488,-488,-488,-489,-489,-490,-490,-491,-491,-492,
-492,-493,-493,-494,-494,-494,-495,-495,-496,-496,-497,-497,
-498,-498,-499,-499,-500,-500,-501,-501,-502,-502,-502,-503,
-503,-504,-504,-505,-505,-506,-506,-507,-507,-508,-508,-509,
-509,-510,-510,-511,-511,-512,-512,-513,-513,-514,-514,-515,
-515,-516,-516,-517,-517,-517,-518,-518,-519,-519,-520,-520,
-521,-521,-522,-522,-523,-523,-524,-524,-525,-525,-526,-526,
-527,-527,-528,-529,-529,-530,-530,-531,-531,-532,-532,-533,
-533,-534,-534,-535,-535,-536,-536,-537,-537,-538,-538,-539,
-539,-540,-540,-541,-541,-542,-542,-543,-544,-544,-545,-545,
-546,-546,-547,-547,-548,-548,-549,-549,-550,-550,-551,-552,
-552,-553,-553,-554,-554,-555,-555,-556,-556,-557,-558,-558,
-559,-559,-560,-560,-561,-561,-562,-563,-563,-564,-564,-565,
-565,-566,-566,-567,-568,-568,-569,-569,-570,-570,-571,-572,
-572,-573,-573,-574,-574,-575,-576,-576,-577,-577,-578,-578,
-579,-580,-580,-581,-581,-582,-583,-583,-584,-584,-585,-586,
-586,-587,-587,-588,-589,-589,-590,-590,-591,-592,-592,-593,
-593,-594,-595,-595,-596,-596,-597,-598,-598,-599,-599,-600,
-601,-601,-602,-603,-603,-604,-604,-605,-606,-606,-607,-608,
-608,-609,-610,-610,-611,-611,-612,-613,-613,-614,-615,-615,
-616,-617,-617,-618,-619,-619,-620,-621,-621,-622,-622,-623,
-624,-624,-625,-626,-626,-627,-628,-629,-629,-630,-631,-631,
-632,-633,-633,-634,-635,-635,-636,-637,-637,-638,-639,-639,
-640,-641,-642,-642,-643,-644,-644,-645,-646,-646,-647,-648,
-649,-649,-650,-651,-651,-652,-653,-654,-654,-655,-656,-657,
-657,-658,-659,-660,-660,-661,-662,-662,-663,-664,-665,-665,
-666,-667,-668,-668,-669,-670,-671,-672,-672,-673,-674,-675,
-675,-676,-677,-678,-679,-679,-680,-681,-682,-682,-683,-684,
-685,-686,-686,-687,-688,-689,-690,-690,-691,-692,-693,-694,
-695,-695,-696,-697,-698,-699,-700,-700,-701,-702,-703,-704,
-705,-705,-706,-707,-708,-709,-710,-711,-711,-712,-713,-714,
-715,-716,-717,-718,-719,-719,-720,-721,-722,-723,-724,-725,
-726,-727,-728,-728,-729,-730,-731,-732,-733,-734,-735,-736,
-737,-738,-739,-740,-741,-742,-742,-743,-744,-745,-746,-747,
-748,-749,-750,-751,-752,-753,-754,-755,-756,-757,-758,-759,
-760,-761,-762,-763,-764,-765,-766,-767,-768,-770,-771,-772,
-773,-774,-775,-776,-777,-778,-779,-780,-781,-782,-783,-785,
-786,-787,-788,-789,-790,-791,-792,-793,-795,-796,-797,-798,
-799,-800,-801,-803,-804,-805,-806,-807,-809,-810,-811,-812,
-813,-815,-816,-817,-818,-819,-821,-822,-823,-824,-826,-827,
-828,-830,-831,-832,-833,-835,-836,-837,-839,-840,-841,-843,
-844,-845,-847,-848,-849,-851,-852,-853,-855,-856,-858,-859,
-860,-862,-863,-865,-866,-868,-869,-871,-872,-874,-875,-877,
-878,-880,-881,-883,-884,-886,-887,-889,-890,-892,-893,-895,
-897,-898,-900,-902,-903,-905,-906,-908,-910,-912,-913,-915,
-917,-918,-920,-922,-924,-925,-927,-929,-931,-933,-934,-936,
-938,-940,-942,-944,-946,-948,-949,-951,-953,-955,-957,-959,
-961,-963,-965,-967,-969,-972,-974,-976,-978,-980,-982,-984,
-987,-989,-991,-993,-996,-998,-1000,-1002,-1005,-1007,-1010,-1012,
-1014,-1017,-1019,-1022,-1024,-1027,-1029,-1032,-1034,-1037,-1040,-1042,
-1045,-1048,-1051,-1053,-1056,-1059,-1062,-1065,-1068,-1071,-1074,-1077,
-1080,-1083,-1086,-1089,-1092,-1096,-1099,-1102,-1106,-1109,-1112,-1116,
-1119,-1123,-1127,-1130,-1134,-1138,-1142,-1145,-1149,-1153,-1157,-1162,
-1166,-1170,-1174,-1179,-1183,-1188,-1192,-1197,-1202,-1206,-1211,-1216,
-1222,-1227,-1232,-1237,-1243,-1249,-1254,-1260,-1266,-1272,-1279,-1285,
-1292,-1299,-1305,-1313,-1320,-1327,-1335,-1343,-1351,-1360,-1369,-1378,
-1387,-1397,-1407,-1417,-1428,-1439,-1451,-1464,-1477,-1490,-1505,-1520,
-1536,-1553,-1572,-1591,-1612,-1636,-1661,-1689,-1720,-1756,-1796,-1845,
-1904,-1980,-2088,-2271 };

/* CCD parameters saved for internal use */
/* Modify with caution */

static int ccd_type;
static int ccd_image_width;
static int ccd_image_height;
static int ccd_track_width;
static int ccd_track_height;
static double ccd_image_gain;   
static double ccd_track_gain;
static int ccd_phase;
static int ccd_image_status;
static int ccd_track_status;
int ccd_filter_wheel = FILTER_WHEEL;
int ccd_filter = FILTER_START;
int ccd_filter_max = FILTER_MAX;
int ccd_tracking = SELF_TRACKING;
int ccd_ext_tracking = SELF_EXT_TRACKING;

/* Array of labels for filters in the wheel                */
/* This could be changed here but keep the length the same */
/* The names can be changed by calling SetCameraFilterName */

char ccd_filter_name[][24] = {
  " (1) I (725-1025) ",
  " (2) R (570-690)  ",
  " (3) V (490-580)  ",
  " (4) B (375-490)  ",
  " (5) U (330-390)  ",
  " (6)              ",  
  " (7)              ",  
  " (8)              ",
  " (9)              ", 
  "(10)              "
 }; 
 
 
/* Array of default labels that simply number the filters */ 
 
char default_filter_name[][24] = {
  " (1)              ",
  " (2)              ",
  " (3)              ",
  " (4)              ",
  " (5)              ",
  " (6)              ",  
  " (7)              ",  
  " (8)              ",
  " (9)              ", 
  "(10)              "
 };     
 
 
/* Initial values of the guide parameters are set in the header file      */
/* Change these by calling SetCameraGuideParameters                       */
/* Values of  x and y refer to coordinates in the image                   */
/* Signed values of ao_x, ao_y, track_x and track_y r to guide device     */
/* Units of ao_x are step count for rotation of the refractor plate       */
/* Units of track_x are time in seconds for holding the relay closed      */

static int guide_threshold = GUIDE_THRESHOLD;
static double guide_x_to_ao_x = GUIDE_X2AOX;
static double guide_y_to_ao_x = GUIDE_Y2AOX;
static double guide_x_to_ao_y = GUIDE_X2AOY;
static double guide_y_to_ao_y = GUIDE_Y2AOY;
static double guide_x_to_track_x = GUIDE_X2TRACKX;
static double guide_y_to_track_x = GUIDE_Y2TRACKX;
static double guide_x_to_track_y = GUIDE_X2TRACKY;
static double guide_y_to_track_y = GUIDE_Y2TRACKY;

/* Open connection to the camera */

int OpenCamera(int device)
{
  int sbig_device;
  int err;
  
  if (device == USB)
  { 
    sbig_device = DEV_USB;
  }
  else if  (device == ETH)
  {
    sbig_device = DEV_ETH;
  }
  else
  {
    fprintf(stderr,"Only USB and ETH devices are supported");
    return(0);    
  }
      
  
  /* Open the driver */
  
  err = SBIGUnivDrvCommand(CC_OPEN_DRIVER, NULL, NULL);
  if (err != CE_NO_ERROR)
  {
    fprintf(stderr,"Error opening camera driver");
    return(0);
  }
  
  /* Only DEV_USB accepted at this time */
  /* Trap other requests */
  
  if (sbig_device != DEV_USB)
  {
    fprintf(stderr,"Only the USB device is supported in this version");
    return(0);
  }
  
  /* For future use */
  
  switch (sbig_device) 
  {
    case DEV_USB:
      odp.deviceType = DEV_USB;
      break;
    case DEV_ETH:
      odp.deviceType = DEV_ETH;
      odp.ipAddress =  camera_ip_address;   
      break;
    case DEV_USB1:
      odp.deviceType = DEV_USB1;
      break;
    case DEV_USB2:
      odp.deviceType = DEV_USB2;
      break;
    case DEV_USB3:
      odp.deviceType = DEV_USB3;
      break;
    case DEV_USB4:
      odp.deviceType = DEV_USB4;
    break;           
    default:
      fprintf(stderr,"Unsupported camera device %d",device);
      return(0);
  }

  err = SBIGUnivDrvCommand(CC_OPEN_DEVICE, &odp, NULL);
  if (err != CE_NO_ERROR)
  {
    fprintf(stderr,"Error opening camera device %d",device);
    return(0);
  }
  
  elp.sbigUseOnly = 0;
  err = SBIGUnivDrvCommand(CC_ESTABLISH_LINK, &elp, &elr);
  if ( err != CE_NO_ERROR ) 
  {
    fprintf(stderr,"Link to camera could not be established");
    return(0);
  }
    
  return(1);
}



/* Close the SBIG camera and driver */
/* Device argument is in the call but is ignored by SBIG */
/* Returns TRUE on success and FALSE on failure */
/* This should be the last action on shutdown */

int CloseCamera(int device)
{
  int success, errdevice, errdriver;

  success = 1;
  errdevice = SBIGUnivDrvCommand(CC_CLOSE_DEVICE, NULL, NULL);
  if (errdevice != CE_NO_ERROR )
  {
    fprintf (stderr, "SBIG close device error");
    success = 0;
  }
  errdriver = SBIGUnivDrvCommand(CC_CLOSE_DRIVER, NULL, NULL);
  if ( errdriver != CE_NO_ERROR ) 
  {
    fprintf (stderr, "SBIG close driver error");
    success = 0;
  } 
  return success;
}


/* Get information on the imaging and tracking CCD's from the camera driver */
/* Probe the first 2 of perhaps 6 or more request types */
/* Filter wheel type is set elsewhere and provided to the user here */
/* Returns TRUE on success and FALSE otherwise */

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)
{
  int errimage = CE_NO_ERROR; 
  int errtrack = CE_NO_ERROR;
  int info_mode;
  int sbig_type = NO_CAMERA;
  
  GetCCDInfoResults0  *infop;
    
  /* Assume that no camera is present until proven otherwise */
  
  ccd_type = NO_CAMERA;

  gip.request = CCD_INFO_IMAGING;
  errimage = SBIGUnivDrvCommand(CC_GET_CCD_INFO, &gip, &info_results_imaging);
  gip.request = CCD_INFO_TRACKING;
  errtrack = SBIGUnivDrvCommand(CC_GET_CCD_INFO, &gip, &info_results_tracking);
  
  /* Save imaging CCD information and update camera type */
  
  if ( errimage == CE_NO_ERROR )
  {
    info_mode = 0;
    infop = &info_results_imaging;
    sbig_type  = info_results_imaging.cameraType;
    ccd_image_width  = infop->readoutInfo[info_mode].width;
    ccd_image_height = infop->readoutInfo[info_mode].height;
    ccd_image_gain   = infop->readoutInfo[info_mode].gain;
    
    if (sbig_type == ST7_CAMERA)
    {
      ccd_type = SBIG_ST7;
    }
    else if (sbig_type == ST8_CAMERA)
    {
      ccd_type = SBIG_ST8;
    }
    else if (sbig_type == ST9_CAMERA)
    {
      ccd_type = SBIG_ST9;
    }
    else if (sbig_type == ST10_CAMERA)
    {
      ccd_type = SBIG_ST10;
    }
    else if (sbig_type == STL_CAMERA)
    {
      ccd_type = SBIG_STL;
    }
    else
    {
      ccd_type = SBIG;
    }    
  }
  else
  {
    fprintf(stderr,"Image CCD information not available");
    ccd_type  = NO_CAMERA;
    ccd_image_width  = 0;
    ccd_image_height = 0;
    ccd_image_gain   = 0.;  
  }  

  /* Save tracking CCD information if available */
  
  if ( errtrack == CE_NO_ERROR )
  {
    info_mode = 0;
    infop = &info_results_tracking;
    ccd_track_width  = infop->readoutInfo[info_mode].width;
    ccd_track_height = infop->readoutInfo[info_mode].height;
    ccd_track_gain   = infop->readoutInfo[info_mode].gain;
  }
  else
  {
    fprintf(stderr,"Track CCD information not available");
    ccd_track_width  = 0;
    ccd_track_height = 0;
    ccd_track_gain   = 0;    
  }  
  
  *type = ccd_type;
  *image_width  = ccd_image_width;  
  *image_height = ccd_image_height; 
  *image_gain = ccd_image_gain;
  *track_width  = ccd_track_width;  
  *track_height = ccd_track_height;
  *track_gain = ccd_track_gain; 
  *wheel = ccd_filter_wheel;
  *filter_max = ccd_filter_max;
  
  if ( ccd_type == NO_CAMERA ) 
  {
    return(0);
  }

  return(1);
  
}

/* Inquire about the exposure status of the camera */
/* Note use of shift >> 2 bits to the right  to recover image status */
/* Sets internal ccd_track_status and ccd_image_status */
/* Returns these values to the calling routine */

int GetCameraStatus(int *image_status, int *track_status)
{
  int err;

  ccd_track_status = IDLE;
  ccd_image_status = IDLE;
  qcsp.command = CC_START_EXPOSURE;
  err = SBIGUnivDrvCommand(CC_QUERY_COMMAND_STATUS, &qcsp, &qcsr);

  if ( err != CE_NO_ERROR )
  { 
    return(0);
  }

  switch ( qcsr.status&3 )
  {
    case CS_IDLE:
      ccd_image_status = IDLE;  
    break;
    
    case CS_IN_PROGRESS:
      ccd_image_status = INTEGRATING;
    break;
    
    case CS_INTEGRATING:
      ccd_image_status = INTEGRATING;
    break;
    
    case CS_INTEGRATION_COMPLETE:
      ccd_image_status = COMPLETE;
    break;
  }
  
  switch ((qcsr.status >> 2) & 3)
  {
    case CS_IDLE:
      ccd_track_status = IDLE;  
    break;
    
    case CS_IN_PROGRESS:
      ccd_track_status = INTEGRATING;
    break;
    
    case CS_INTEGRATING:
      ccd_track_status = INTEGRATING;
    break;
    
    case CS_INTEGRATION_COMPLETE:
      ccd_track_status = COMPLETE;
    break;
  }    

  *image_status = ccd_image_status;
  *track_status = ccd_track_status;
  return(1);
}


/* Inquire about the CCD temperature, setpoint, and cooler power level */

int GetCameraTemperature(double *power, double *temperature, double *setpoint)
{
  int err;
  double ccd_cooler_power;
  double ccd_temperature;
  double ccd_setpoint;
  
  err = SBIGUnivDrvCommand(CC_QUERY_TEMPERATURE_STATUS,NULL,&qtsr);
  ccd_cooler_power = (100./256.)* (double) qtsr.power;
  ccd_temperature = 0.1*ccd_temp[qtsr.ccdThermistor];
  ccd_setpoint = 0.1*ccd_temp[qtsr.ccdSetpoint];

  if ( err != CE_NO_ERROR ) 
  {
    return(0);
  } 

  *power = ccd_cooler_power;
  *temperature = ccd_temperature;
  *setpoint = ccd_setpoint;
  return(1);
}


/* CCD cooler */

int SetCameraTemperature(int state, double temperature)
{
  int err;
  int  i,t;
  
  if( state == TRUE )
  {
    strp.regulation = 1;
  }
  else if ( state == FALSE )
  {
    strp.regulation = 0;
  }
  else
  {
    fprintf(stderr,"Unknown value of ccd_cool");
  }      
  
  /* Convert the temperature to the integer stored in the lookup table */

  t = 10.0*temperature + 0.5;

  /* Limit the search to a reasonable range */

  if (t > 500)
  {  
    t = 500;
  }
  else if (t < -500)
  {
    t = -500;
  }

  /* Do a simple linear reverse lookup to find the nearest setpoint */

  for (i = 0; i < 4096; ++i)
  {
    if (ccd_temp[i] <= t)
    break;
  }
  
  strp.ccdSetpoint = i;    

  err = SBIGUnivDrvCommand(CC_SET_TEMPERATURE_REGULATION, &strp, NULL);

  if ( err != CE_NO_ERROR ) 
  { 
    return(0);    
  } 
  return(1);
}

/* Provide filter information */

int GetCameraFilter(char filter_name[24], int *filter)
{
  if (ccd_filter_wheel == NO_FILTER_WHEEL)
  {
    strcpy(filter_name,"Open");    
    return(1);
  }
  if ( ( *filter >=1 ) && ( *filter <= ccd_filter_max ) )
  { 
    strcpy(filter_name,ccd_filter_name[ccd_filter-1]);
    return(1);
  }
  return(0);
}

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

int SetCameraFilterName(int flag, 
  int new_filter_number, char *new_filter_name)
{
  int i;
  
  if ( ccd_filter_wheel == NO_FILTER_WHEEL )
  {
    return(0);
  }
    
  if ( flag > 0 )
  {
    
    /* Save new filter name */
            
    i = new_filter_number - 1;
    if ( (i >= 0) & (i < ccd_filter_max) )
    { 
      if ( strlen(new_filter_name) < 24 )
      {
        strcpy(ccd_filter_name[i],new_filter_name);
      }
      else
      {
        strcpy(ccd_filter_name[i],default_filter_name[i]);
      }
    }
    
  }
  else
  {
    
    /* Restore defaults */
    
    ccd_filter_max = FILTER_MAX;
  
    for (i=0; i<10; i++)
    {
      strcpy(ccd_filter_name[i],default_filter_name[i]);  
    }
  }
  return(1);
}

/* Set SBIG CCD filter */
/* Filter numbers are from 1 to FILTER_MAX */

int SetCameraFilter(int filter)
{
  int err;
  char cmdstr[256];
  
  /* Check if the wheel is actually there */
  
  if (ccd_filter_wheel == NO_FILTER_WHEEL)
  {
    ccd_filter = 1;
    return(1);
  }

  /* Correct errors in filter number request */
  
  if (filter < 1 )
  {
    filter = 1;
  }
  if (filter > ccd_filter_max )
  {
    filter = ccd_filter_max;
  }
  
  if (ccd_filter_wheel == EXT_FILTER_WHEEL)
  {
  
    /* Execute external set_camera_filter */
  
    sprintf(cmdstr,"set_camera_filter %d",filter);
    system(cmdstr);  
  }
  else
  {  
    cfwp.cfwCommand = CFWC_GOTO;
    cfwp.cfwModel = ccd_filter_wheel;
    cfwp.cfwParam1 = filter;
    err= SBIGUnivDrvCommand(CC_CFW, &cfwp, &cfwr);
    if ( err != CE_NO_ERROR ) 
    {    
      return(0);
    }
  }   

  /* Pause to allow time for wheel to set */
  
  usleep(5000000);
  
  /* Keep a record of the last filter selection */

  ccd_filter = filter;
  
  return(1);
}


/* Set the guiding parameters */

int SetCameraGuideParameters(int threshold, 
  double x2trackx, double y2trackx, double x2tracky, double y2tracky,
  double x2aox, double y2aox, double x2aoy, double y2aoy)
{
  /* Save these values for use by the guiding routines */
  
  guide_threshold = threshold;
  guide_x_to_ao_x =    x2aox;
  guide_y_to_ao_x =    y2aox;
  guide_x_to_ao_y =    x2aoy;
  guide_y_to_ao_y =    y2aoy;
  guide_x_to_track_x = x2trackx;
  guide_y_to_track_x = y2trackx;
  guide_x_to_track_y = x2tracky;
  guide_y_to_track_y = y2tracky;

  return(1);
}


/* Set the AO tip and tilt with limit protection */

int SetCameraAOTipTilt(int ao_x, int ao_y)
{
  int err;
  
  if (ao_x < 0 )   ao_x = 0;
  if (ao_x > 4095) ao_x = 4095; 
  if (ao_y < 0 )   ao_y = 0;
  if (ao_y > 4095) ao_y = 4095;   
  
  attp.xDeflection = ao_x;
  attp.yDeflection = ao_y;

  err = SBIGUnivDrvCommand(CC_AO_TIP_TILT, &attp, NULL);
  if ( err != CE_NO_ERROR ) 
  {
    return(0);
  } 
  
  return(1);

}


/* Activate one tracking relay at a time */
/* May be generalized for both x and y simultaneously */
/* Relay ID is the bit pattern 0001 0010 0100 1000 */
/* Time is in seconds to a max of 30 seconds in hardware */
/* We trap at 2 seconds for guiding in a single call */

int ActivateCameraRelay(int relay, double ontime)
{
  int err;
  int centisecs;
  
  centisecs = (int) 100.*ontime;
   
  if (centisecs > 200) centisecs = 200;
  if (centisecs < 0)    centisecs = 0;

  arp.tXPlus  = 0;
  arp.tXMinus = 0;
  arp.tYPlus  = 0;
  arp.tYMinus = 0; 

  switch (relay)
  {
     case 1:
       arp.tXPlus  = centisecs;
       break;
     case 2:    
       arp.tXMinus = centisecs;
       break;
     case 4:
       arp.tYPlus  = centisecs;
       break;
     case 8:
       arp.tYMinus = centisecs; 
     break;
  }   

  err = SBIGUnivDrvCommand(CC_ACTIVATE_RELAY, &arp, NULL);
  if ( err != CE_NO_ERROR ) 
  {
    fprintf(stderr,"Camera relay error\n");
    return(0);    
  } 
      
  return(1);
}


/* Acquire an image frame */

int CaptureImage(int *phase,  unsigned short *data,
  int frame, double exposure, int subarea, 
  int x, int y, int width, int height)
{ 
  int err;
  int i; 
  
  ccd_phase = *phase;
  
  if ( ccd_phase == 2 )
  {

    /* End an exposure without readout */

    eep.ccd = CCD_IMAGING;
    mcp.fanEnable = TRUE;           /* Fan on */
    mcp.shutterCommand = 2;         /* Shutter closed */
    mcp.ledState = 0;               /* LED off */
    err=SBIGUnivDrvCommand(CC_MISCELLANEOUS_CONTROL, &mcp, NULL);
    err=SBIGUnivDrvCommand(CC_END_EXPOSURE, &eep, NULL);
    ccd_phase = 0;
    *phase = ccd_phase;
    if (err != CE_NO_ERROR)  
    {
      fprintf(stderr,"Error ending image exposure");
      return(0);
    }
    return(1);
  }
    
  if ( ccd_phase == 0 )
  { 
  
    /* Send start request to the camera */
      
    sep.ccd = CCD_IMAGING;
    sep.abgState = ABG_LOW7;
    if ( ( frame == LIGHT ) | (frame == FLAT) )
    {  
      /* Shutter open */
      sep.openShutter =  SC_OPEN_SHUTTER;
    }
    else if ( ( frame == DARK ) | (frame == BIAS ) )
    {
      /* Shutter closed */
      sep.openShutter =  SC_CLOSE_SHUTTER;
    }
    else
    {
      fprintf(stderr,"Unknown frame type requested in CaptureImage");
      return(0);
    }
    sep.exposureTime = (int)(100.0*exposure + 0.5);
    err=SBIGUnivDrvCommand(CC_START_EXPOSURE, &sep, NULL);   

    /* Did this work? */

    if ( err != CE_NO_ERROR ) 
    {
      fprintf(stderr,"Request to start camera exposure ignored");
      return(0);
    }
   
    /* Return indicating succesful start and  exposure in progress */
    
    ccd_phase = 1;
    *phase = ccd_phase;
    return(1);
  }   
    
  if ( ccd_phase != 1 )
  {
    /* We have already handled all allowed values except 1 */
    /* Reset phase to 0.  This permits recursive calls */
    ccd_phase = 0;
    *phase = ccd_phase;
    return(0) ;
  }
  
  /* Are we done yet? */
  /* If so, read it, if not reset phase and return */

  GetCameraStatus(&ccd_image_status, &ccd_track_status);
  
  if ( ccd_image_status != COMPLETE )
  {
    /* The exposure is still underway */
    ccd_phase = 1;
    *phase = ccd_phase;
    return(1);
  }  
  
  /* Test for subarea request */
  /* If not subarea, then default to driver's values for ccd dimensions */
  /* If subarea, then check validity and clamp to allowed bounds */

  if (subarea == FALSE)
  {
     x = 0;
     y = 0;
     width = ccd_image_width;
     height = ccd_image_height;
  }  
  else 
  {
    if (x < 0)
    {
      x = 0;
    }
    if (y < 0)
    {
      y = 0;
    }
    if (x > ccd_image_width  )
    {
      x = ccd_image_width;
    }
    if (y > ccd_image_height )
    {
      y = ccd_image_height;
    }
  }          
  
         
  /* Flush the buffers */
  /* This probably isn't necessary.  It's a carryover from earlier versions. */
        
  fflush(stdout);
  fflush(stderr);
  sync();
      
  /* End the exposure */
  
  eep.ccd = CCD_IMAGING;
  srp.ccd = CCD_IMAGING;
  rlp.ccd = CCD_IMAGING;
  err=SBIGUnivDrvCommand(CC_END_EXPOSURE, &eep, NULL);
  if ( err != CE_NO_ERROR ) 
  {
    return(0);
  } 
  
  /* Prepare to read the image */

  sync();
  fflush(stdout);
       
  /* Read it */

  srp.readoutMode = 0;
  srp.top = y;
  srp.left = x;
  srp.width = width;
  srp.height = height;
  err = SBIGUnivDrvCommand(CC_START_READOUT, &srp, NULL);
  if ( err != CE_NO_ERROR ) 
  {
    return(0);
  } 
  
  for (i = 0; i < srp.height; ++i) 
  {
    rlp.readoutMode = 0;
    rlp.pixelStart = x;
    rlp.pixelLength = width;
    err = SBIGUnivDrvCommand(CC_READOUT_LINE, &rlp, data + i*width);
    if (err != CE_NO_ERROR) 
    {
      fprintf(stderr,"Unable to read image data from camera");
      ccd_phase = 0;
      *phase = ccd_phase;
      return(0);
    }
  }
  
  /* Successful readout */
  /* Indicate that a new image is available */
  
  ccd_phase = 2;
  *phase = ccd_phase;  
  return(1);
}
  

/* Acquire a track frame */

int CaptureTrack(int *phase,  unsigned short *data,
  int frame, double exposure, int subarea, 
  int x, int y, int width, int height)
{ 
  int err;
  int i; 
    
  ccd_phase = *phase;
  
  if ( ccd_phase == 2 )
  {

    /* End an exposure without readout */

    eep.ccd = CCD_TRACKING;
    mcp.fanEnable = TRUE;           /* Fan on */
    mcp.shutterCommand = 2;         /* Shutter closed */
    mcp.ledState = 0;               /* LED off */
    err=SBIGUnivDrvCommand(CC_MISCELLANEOUS_CONTROL, &mcp, NULL);
    err=SBIGUnivDrvCommand(CC_END_EXPOSURE, &eep, NULL);
    ccd_phase = 0;
    *phase = ccd_phase;
    if (err != CE_NO_ERROR)  
    {
      fprintf(stderr,"Error ending exposure");
      return(0);
    }
    return(1);
  }
    
  if ( ccd_phase == 0 )
  { 
  
    /* Send start request to the camera */
      
    sep.ccd = CCD_TRACKING;
    sep.abgState = ABG_LOW7;
    if ( frame == LIGHT )
    {  
      sep.openShutter =  SC_OPEN_SHUTTER;
    }
    else if ( frame == DARK )
    {
      sep.openShutter =  SC_CLOSE_SHUTTER;
    }
    else
    {
      fprintf(stderr,"Unknown frame type requested in CaptureTrack");
      return(0);
    }
    sep.exposureTime = (int)(100.0*exposure + 0.5);
    err=SBIGUnivDrvCommand(CC_START_EXPOSURE, &sep, NULL);   

    /* Did this work? */

    if ( err != CE_NO_ERROR ) 
    {
      fprintf(stderr,"Request to start camera track exposure ignored");
      return(0);
    }
   
    /* Return indicating succesful start and exposure in progress */
    
    ccd_phase = 1;
    *phase = ccd_phase;
    return(1);
  }   
    
  if ( ccd_phase != 1 )
  {
    /* We have already handled all allowed values except 1 */
    /* Reset phase to 0.  This permits recursive calls */
    ccd_phase = 0;
    *phase = ccd_phase;
    return(0) ;
  }
  
  /* Are we done yet? */
  /* If so, read it, if not reset phase and return */

  GetCameraStatus(&ccd_image_status, &ccd_track_status);
  
  if ( ccd_track_status != COMPLETE )
  {
    /* The exposure is still underway */
    ccd_phase = 1;
    *phase = ccd_phase;
    return(1);
  }  
  
  /* Test for subarea request */
  /* If not subarea, then default to driver's values for ccd dimensions */
  /* If subarea, then check validity and clamp to allowed bounds */
  
  if (subarea == FALSE)
  {
     x = 0;
     y = 0;
     width = ccd_track_width;
     height = ccd_track_height;
  }  
  else 
  {
    if (x < 0)
    {
      x = 0;
    }
    if (y < 0)
    {
      y = 0;
    }
    if (x > ccd_track_width  )
    {
      x = ccd_track_width;
    }
    if (y > ccd_track_height )
    {
      y = ccd_track_height;
    }
  }          
  
         
  /* Flush the buffers */
  /* This probably isn't necessary.  It's a carryover from earlier versions. */
        
  fflush(stdout);
  fflush(stderr);
  sync();
      
  /* End the exposure */
  
  eep.ccd = CCD_TRACKING;
  srp.ccd = CCD_TRACKING;
  rlp.ccd = CCD_TRACKING;
  err=SBIGUnivDrvCommand(CC_END_EXPOSURE, &eep, NULL);
  if ( err != CE_NO_ERROR ) 
  {
    return(0);
  } 
  
  /* Prepare to read the image */

  sync();
  fflush(stdout);
       
  /* Read it */

  srp.readoutMode = 0;
  srp.top = y;
  srp.left = x;
  srp.width = width;
  srp.height = height;
  err = SBIGUnivDrvCommand(CC_START_READOUT, &srp, NULL);
  if ( err != CE_NO_ERROR ) 
  {
    return(0);
  } 
  
  for (i = 0; i < srp.height; ++i) 
  {
    rlp.readoutMode = 0;
    rlp.pixelStart = x;
    rlp.pixelLength = width;
    err = SBIGUnivDrvCommand(CC_READOUT_LINE, &rlp, data + i*width);
    if (err != CE_NO_ERROR) 
    {
      fprintf(stderr,"Unable to read track data from camera");
      ccd_phase = 0;
      *phase = ccd_phase;
      return(0);
    }
  }
  
  /* Successful readout */
  /* Indicate that a new image is available */
  
  ccd_phase = 2;
  *phase = ccd_phase;  
  return(1);
}


/* Use image data to find and apply guiding feedback     */
/* Functions:                                            */
/*   Find a single guide star within the image subarea   */
/*   Center star in the subarea                          */
/*   Write residual error to a log file                  */

int GuideImage(unsigned short *data,
  int width, int height,
  int guide, int guide_flag, int guide_log_flag)
{ 
  int i,j,k;
  unsigned short pixdata;
  double dataval;
  double bkgval;
  double anbkg;
  int nbkg;
  int guide_n;
  double x_err, y_err;
  double x_0, y_0;
  double *guide_star;
  FILE *fp_log; 
  
  guide_n = width*height;
  guide_star = (double *) malloc((guide_n)*sizeof(double));
  
  /* Average perimeter for a background estimate */
  /* Corners are weighted by 2 */
  
  k = 0;
  nbkg = 0;
  bkgval = 0.;
  for (j = 0; j < height; j++)
  {
     for (i = 0; i < width; i++)
    {
      pixdata = *(data + k);
      dataval = (double) pixdata;
      if ( (j == 0) || (j == (height - 1)) ) 
      {
        bkgval = bkgval + dataval;
        nbkg++;
      }
      if ( (i == 0) || (i == (width - 1))) 
      {
        bkgval = bkgval + dataval;
        nbkg++;
      }       
      guide_star[k] = dataval;
      k++;
    }
  } 

  anbkg = (double) nbkg;
  bkgval = bkgval / anbkg;
  
  /* Subtract the background */
  
  k = 0;
  for (j = 0; j < height; j++)
  {
    for (i = 0; i < width; i++)
    {
      guide_star[k] = guide_star[k] - bkgval;
      k++;
    }
  }
      
  /* Find the guide star in this new image */
  
  find_guide_star(guide_star, width, height, &x_err, &y_err);
  
  /* Find the error from the center of the image subarea  */
    
  x_0 = (double) width;
  x_0 = x_0/2.;
  y_0 = (double) height;
  y_0 = y_0/2.;
  
  x_err = x_err - x_0;
  y_err = y_err - y_0; 
  
  printf("jfk: %lf %lf\n", x_err, y_err);
    
  /* Use the latest error to correct the image */
  
  if ( guide_flag == TRUE )
  {
    if ( guide == TRACKING )
    {
      /* Correct telescope pointing based on guide star location */

      feedback_tracking(x_err, y_err);
    }
    else if ( guide == SELFTRACKING )
    {
      /* Correct telescope pointing based on guide star location */

      feedback_tracking(x_err, y_err);
    }
    else if  ( guide == OPTICAL )
    {
      /* Correct adaptive optics based on guide star location */
      
      feedback_ao(x_err, y_err);
    }
    else if  ( guide == SELFOPTICAL )
    {
      /* Correct adaptive optics based on guide star location */
      
      feedback_ao(x_err, y_err);
    }
  }
  
  if ( guide_log_flag == TRUE )
  {
    time_t timebuf;
    char *gmtstr;
    
    time(&timebuf);
    gmtstr = asctime(gmtime(&timebuf));
    if (gmtstr[strlen(gmtstr) - 1] == '\n') gmtstr[strlen(gmtstr) - 1]= '\0';
    fp_log=fopen("guide.log", "a");
    fprintf(fp_log,"%s  %f  %f\n", gmtstr, x_err, y_err);
    fflush(fp_log);
    fclose(fp_log);
  }
  
  free(guide_star);
  return(1);
}
   

/* Acquire a self-guided image frame and save it as a fits file */
/* Uses auxiliary CCD to find and apply guiding corrections     */

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 guide_log_flag)
{
  int err;
  int i;
  
  static int guide_phase;
  static int guide_x;
  static int guide_y;
  static int guide_w;
  static int guide_h; 
  static int guide_n;
  
       
  ccd_phase = *phase;   
  
  if ( ccd_phase == 2 )
  {
    /* End both track and image exposures without readout */

    eep.ccd = CCD_IMAGING;
    mcp.fanEnable = TRUE;           /* Fan on */
    mcp.shutterCommand = 2;         /* Shutter closed */
    mcp.ledState = 0;               /* LED off */
    err=SBIGUnivDrvCommand(CC_MISCELLANEOUS_CONTROL, &mcp, NULL);

    eep.ccd = CCD_IMAGING;
    err=SBIGUnivDrvCommand(CC_END_EXPOSURE, &eep, NULL);
    eep.ccd = CCD_TRACKING;
    err=SBIGUnivDrvCommand(CC_END_EXPOSURE, &eep, NULL);
    ccd_phase = 0;
    *phase = ccd_phase;
    if (err != CE_NO_ERROR)  
    {
      fprintf(stderr,"Error ending guided exposure");
      return(0);
    }
    return(1);
  } 

  /* Test that this is indeed a guided image request */
  /* If not, then use another routine */

  if ( guide == UNGUIDED )
  {
    fprintf(stderr,"Request for unguided exposure made to CaptureGuide");
    fprintf (stderr,"Use CaptureImage instead");
    return(0);
  } 
  
  /* Test that the camera has an internal or external track ccd */
  
  if ( (ccd_tracking == FALSE) && (ccd_ext_tracking == FALSE) )
  {
    fprintf(stderr,"Request for self-guided exposure with unsupported hardware ");
    fprintf (stderr,"Use CaptureImage instead");
    return(0);
  }    

  /* Test that guide feedback type is appropriate */

  if ( ( guide != SELFTRACKING ) && ( guide != SELFOPTICAL ) )
  {
    fprintf(stderr,"Request for unknown guide type %d made to CaptureGuide\n", guide);
    return(0);
  }

  if ( ccd_phase == 0 )
  { 
    /* Start image exposure */
  
    sep.ccd = CCD_IMAGING;
    sep.abgState = ABG_LOW7;
    sep.openShutter =  SC_OPEN_SHUTTER;
    sep.exposureTime = (int)(100.0*exposure_i + 0.5);
    err=SBIGUnivDrvCommand(CC_START_EXPOSURE, &sep, NULL);   

    /* Did this work? */

    if ( err != CE_NO_ERROR ) 
    {
      fprintf(stderr,"Request to start imaging exposure ignored");
      return(0);
    } 
   
    /* Start guide exposure */
  
    sep.ccd = CCD_TRACKING;
    sep.abgState = ABG_LOW7;
    sep.openShutter =  SC_LEAVE_SHUTTER;
    sep.exposureTime = (int)(100.0*exposure_t + 0.5);
    err=SBIGUnivDrvCommand(CC_START_EXPOSURE, &sep, NULL);

    /* Did this work? */

    if ( err != CE_NO_ERROR ) 
    {
      fprintf(stderr,"Request to start tracking exposure ignored");
      return(0);
    } 

    /* Check and save tracking ccd bounds */
    
    if (subarea_t == FALSE)
    {
      x_t = 0;
      y_t = 0;
      width_t = ccd_track_width;
      height_t = ccd_track_height;
    }  
    else 
    {
      if (x_t < 0)
      {
        x_t = 0;
      }
      if (y_t < 0)
      {
        y_t = 0;
      }
      if (x_t > ccd_track_width  )
      {
        x_t = ccd_track_width;
      }
      if (y_t > ccd_track_height )
      {
        y_t = ccd_track_height;
      }
    }
    
    guide_x = x_t;
    guide_y = y_t;
    guide_w = width_t;
    guide_h = height_t;
    guide_n = guide_w*guide_h;       
    
    /* Center AO at the start of an exposure if OPTICAL */ 
    
    if  ( guide == OPTICAL )
    {
      SetCameraAOTipTilt(2048,2048); 
    }
    
    /* Return indicating that a guided exposure has been started               */

    ccd_phase = 1;
    *phase = ccd_phase;
    return(1);
  }    

  if ( ccd_phase != 1 )
  {
    /* We have already handled all allowed values except 1 */

    *phase = ccd_phase;
    return(0) ;     
  }  

  /* Are we done yet? */

  GetCameraStatus(&ccd_image_status, &ccd_track_status);
 
  /* Test for need to service tracking */
   
  if (ccd_track_status == COMPLETE)
  {    
    
    /* Transfer the track CCD data  */
    /* Any camera errors are noted but treated as non-fatal */ 
    
    eep.ccd = CCD_TRACKING;
    srp.ccd = CCD_TRACKING;
    rlp.ccd = CCD_TRACKING;
    err=SBIGUnivDrvCommand(CC_END_EXPOSURE, &eep, NULL);
    if ( err != CE_NO_ERROR ) 
    { 
      fprintf(stderr,"Error ending track ccd exposure during guided image");
      return(0);
    }  
    
    srp.readoutMode = 0;
    srp.top = guide_y;
    srp.left = guide_x;
    srp.width = guide_w;
    srp.height = guide_h;
    err = SBIGUnivDrvCommand(CC_START_READOUT, &srp, NULL);
    if ( err != CE_NO_ERROR ) 
    {
      fprintf(stderr,"Error starting track ccd readout during guided image");
      return(0);
    } 

    for (i = 0; i < srp.height; ++i) 
    {
      rlp.readoutMode = 0;
      rlp.pixelStart = guide_x;
      rlp.pixelLength = guide_w;
      err = SBIGUnivDrvCommand(CC_READOUT_LINE, &rlp, track_data + i*guide_w);
      if (err != CE_NO_ERROR) 
      {
        fprintf(stderr,"Error reading track ccd for guide star image");
        return(0);
      }
    }
                
    /* Use the latest tracking data to correct the pointing */
      
    GuideImage(track_data, 
      guide_w, guide_h, guide, guide_flag, guide_log_flag);
 
    /* Flush the buffers */
          
    fflush(stdout);
    sync();
    
    /* Start another track exposure if the image exposure is not complete */

    if ( ccd_image_status != COMPLETE )
    {    
            
      err=SBIGUnivDrvCommand(CC_START_EXPOSURE, &sep, NULL);
      if (err != CE_NO_ERROR) 
      {
        fprintf(stderr,"Error starting new track exposure");
        return(0);
      }
    }        

    /* Return with value indicating exposure still underway */
    
    ccd_phase = 1;
    *phase = ccd_phase; 
    return(1);
  } 
  
  /* Check on the image CCD and return if it is not complete */   
   
  if ( ccd_image_status != COMPLETE )
  {
    ccd_phase = 1;
    *phase = ccd_phase;    
    /* The exposure is still underway */
    return(1);
  }    

  /* Test for image subarea request and adjust parameters as needed */
 
  if (subarea_i == FALSE)
  {
     x_i = 0;
     y_i = 0;
     width_i = ccd_image_width;
     height_i = ccd_image_height;
  }  
  else 
  {
    if (x_i < 0)
    {
      x_i = 0;
    }
    if (y_i < 0)
    {
      y_i = 0;
    }
    if (x_i > ccd_image_width  )
    {
      x_i = ccd_image_width;
    }
    if (y_i > ccd_image_height )
    {
      y_i = ccd_image_height;
    }
  }          
  
  /* Transfer the CCD data to image_data */
  
  eep.ccd = CCD_IMAGING;
  srp.ccd = CCD_IMAGING;
  rlp.ccd = CCD_IMAGING;
  err=SBIGUnivDrvCommand(CC_END_EXPOSURE, &eep, NULL);
  if ( err != CE_NO_ERROR ) 
  {
    fprintf(stderr,"Error starting ending image exposure");
    return(0);
  } 
  
  /* Read it */

  srp.readoutMode = 0;
  srp.top = y_i;
  srp.left = x_i;
  srp.width = width_i;
  srp.height = height_i;
  err = SBIGUnivDrvCommand(CC_START_READOUT, &srp, NULL);
  if ( err != CE_NO_ERROR ) 
  {
    fprintf(stderr,"Error starting image exposure readout");
    return(0);
  } 
  
  for (i = 0; i < srp.height; ++i) 
  {
    rlp.readoutMode = 0;
    rlp.pixelStart = x_i;
    rlp.pixelLength = width_i;
    err = SBIGUnivDrvCommand(CC_READOUT_LINE,&rlp,image_data + i*width_i);
    if (err != CE_NO_ERROR) 
    {
      fprintf(stderr,"Error within image readout after guided exposure");
      return(0);
    }
  }

  /* Successful image readout ends guiding too */
  
  guide_phase = 0;
  
  /* Indicate that a new image is available */
  
  ccd_phase = 2;
  *phase = ccd_phase;  
  return(1);
}

int CaptureEnd(int *phase)
{
  int err;
         
  ccd_phase = *phase;   
  
  if ( ccd_phase == 2 )
  {
    /* End both track and image exposures without readout */

    eep.ccd = CCD_IMAGING;
    mcp.fanEnable = TRUE;           /* Fan on */
    mcp.shutterCommand = 2;         /* Shutter closed */
    mcp.ledState = 0;               /* LED off */
    err=SBIGUnivDrvCommand(CC_MISCELLANEOUS_CONTROL, &mcp, NULL);

    eep.ccd = CCD_IMAGING;
    err=SBIGUnivDrvCommand(CC_END_EXPOSURE, &eep, NULL);
    eep.ccd = CCD_TRACKING;
    err=SBIGUnivDrvCommand(CC_END_EXPOSURE, &eep, NULL);
    ccd_phase = 0;
    *phase = ccd_phase;
    if (err != CE_NO_ERROR)  
    {
      fprintf(stderr,"Error ending guided exposure");
      return(0);
    }
    return(1);
  } 
  return(0);
}

/* FITS routines                               */
/*                                             */
/* Write a FITS primary array with a 2-D image */
/*   and a header with keywords                */
/*                                             */             
  
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)  
{
    fitsfile *fptr;       /* pointer to the FITS file, defined in fitsio.h */
    int status;
    long  fpixel, nelements;

    /* Initialize FITS image parameters */

    int bitpix   =  USHORT_IMG;       /* 16-bit unsigned short pixel values */
    long naxis    =   2;              /* 2-dimensional image                */    
    long naxes[2] = { 256,256 };      /* default image 256 wide by 256 rows */
    
    /* Set the actual width and height of the image */

    naxes[0] = w;
    naxes[1] = h;

    /* Delete old FITS file if it already exists */  

    remove(filename); 
     
    /* Must initialize status before calling fitsio routines */           

    status = 0;  
    
    /* Create a new FITS file and show error message if one occurs */

    if (fits_create_file(&fptr, filename, &status)) 
         show_cfitsio_error( status );           

    /* Write the required keywords for the primary array image.       */
    /* Since bitpix = USHORT_IMG, this will cause cfitsio to create   */
    /* a FITS image with BITPIX = 16 (signed short integers) with     */
    /* BSCALE = 1.0 and BZERO = 32768.  This is the convention that   */
    /* FITS uses to store unsigned integers.  Note that the BSCALE    */
    /* and BZERO keywords will be automatically written by cfitsio    */
    /* in this case.                                                */

    if ( fits_create_img(fptr,  bitpix, naxis, naxes, &status) )
        show_cfitsio_error( status );          

    fpixel = 1;                               /* first pixel to write      */
    nelements = naxes[0] * naxes[1];          /* number of pixels to write */

    /* Write the array of unsigned integers to the FITS file */
         
    if ( fits_write_img(fptr, TUSHORT, fpixel, nelements, data, &status) )
      show_cfitsio_error( status );
        
    /* Write optional keywords to the header */
    
    if ( fits_update_key_dbl(fptr, "EXPTIME", obs_duration, -2,
      "exposure time (seconds)", &status) )
      show_cfitsio_error( status );
           
    if ( fits_update_key_str(fptr, "DATE-OBS", 
         obs_date, "date of observation (UT)", &status) )
         show_cfitsio_error( status );
         
    if ( fits_update_key_str(fptr, "IMAGETYP", 
         obs_type, "image type", &status) )
         show_cfitsio_error( status );         

    if ( fits_update_key_str(fptr, "TARGET", 
         obs_target, "target", &status) )
         show_cfitsio_error( status );
                   
    if ( fits_update_key_str(fptr, "INSTRUME", 
         obs_instrument, "instrument", &status) )
         show_cfitsio_error( status ); 
                         
    if ( fits_update_key_str(fptr, "FILTER", 
         obs_filter,"filter", &status) )
         show_cfitsio_error( status );

    if ( fits_update_key_str(fptr, "TELESCOP", 
         obs_telescope,"telescope", &status) )
         show_cfitsio_error( status );         
           
    if ( fits_write_date(fptr, &status) )
         show_cfitsio_error( status );                   
          
    /* Close the file */             

    if ( fits_close_file(fptr, &status) )              
         show_cfitsio_error( status );           

    return;
}


/* Print cfitsio error report */

void show_cfitsio_error(int status)
{
  if (status)
  {
     fits_report_error(stderr, status); 
  }
  return;
}  


/* Autoguiding */

/* Feedback to relays based on guide star error signal */
/* Global guide parameters deliver calibrated response */
/* Uses int relay 1 2 4 8 -> +X -X +Y -Y               */
/* Activates the relay servicing largest error         */
/* Make repeated calls to bring both errors down       */

void feedback_tracking(double x_err, double y_err)
{
  int relay;
  double relay_on_time;
  double fx, fy;
     
  fx = guide_x_to_track_x *x_err + guide_y_to_track_x*y_err;
  fy = guide_x_to_track_y *x_err + guide_y_to_track_y*y_err;
  
  printf("jfk: fx=%lf fy=%lf\n", fx, fy);
    
  if (fx*fx > fy*fy)
  {
    if ( fx > 0. )
    {
      relay = 1;
      relay_on_time = fx;  
      printf("jfk: relay = %d  time = %lf\n",relay, relay_on_time);
      ActivateCameraRelay(relay, relay_on_time);
    }
    else if ( fx < 0. )
    {
      relay = 2;
      relay_on_time = -fx; 
      printf("jfk: relay = %d  time = %lf\n",relay, relay_on_time);
      ActivateCameraRelay(relay, relay_on_time);
    }  
  }
  else
  {
    if ( fy > 0. )
    {
      relay = 4;
      relay_on_time = fy;  
      printf("jfk: relay = %d  time = %lf\n",relay, relay_on_time);
      ActivateCameraRelay(relay, relay_on_time);
    }
    else if ( fy < 0. )
    {
      relay = 8;
      relay_on_time = -fy;  
      printf("jfk: relay = %d  time = %lf\n",relay, relay_on_time);
      ActivateCameraRelay(relay, relay_on_time);
    }
  }  
  return;
}

/* Feedback ao system based on guide star offset       */
/* Global guide parameters deliver calibrated response */
/* AO center is (2048,2048)                            */ 
/* Error coordinate center is (0,0)                    */
/* Uses SetCameraAOTipTilt to check bounds             */

void feedback_ao(double x_err,double y_err)
{
  int ao_x, ao_y;
  double fx, fy;
   
  fx = guide_x_to_ao_x *x_err + guide_y_to_ao_x*y_err;
  fy = guide_x_to_ao_y *x_err + guide_y_to_ao_y*y_err;
  ao_x = 2048 + (int) fx;
  ao_y = 2048 + (int) fy;
  
  SetCameraAOTipTilt(ao_x,ao_y);
  
  return;
}

/* Determine barycenter of guide star in image subarray */

void find_guide_star(double *data, int w, int h, double *x_s, double *y_s)
{
  int i,j,k;
  double xval,yval,pixval,sumval,xav,yav;
    
  k = 0;
  sumval = 0.;
  xav = 0.;
  yav = 0.;
  for (j = 0; j < h; j++)
  {
    yval = (double) j;
    for (i = 0; i < w; i++)
    {
      pixval = data[k];
      
      if ( pixval >= guide_threshold )
      {
        xval = (double) i;
        sumval = sumval + pixval;
        xav = xav + xval * pixval;
        yav = yav + yval * pixval;
      }
      k++;
    }
  }
    
  /* Handle frame without signal */ 
  
  if (sumval <= 0.)
  {
    *x_s = ((double) w)/2.; 
    *y_s = ((double) h)/2.;
    return;
  }
     
  xav = xav / sumval;
  yav = yav / sumval;
  *x_s = xav;
  *y_s = yav;
    
  return;
}

