3 INDI Interface
for Apogee PPI
4 Copyright (C
) 2003 Jasem
Mutlaq (mutlaqja@ikarustech
.com
)
6 This library is free software
; you can redistribute it
and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation
; either
9 version
2.1 of the License
, or (at your option
) any later version
.
11 This library is distributed in the hope that it will be useful
,
12 but WITHOUT ANY WARRANTY
; without even the implied warranty of
13 MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE
. See the GNU
14 Lesser General Public License
for more details
.
16 You should have received a copy of the GNU Lesser General Public
17 License along with
this library
; if not, write to the Free Software
18 Foundation
, Inc
., 59 Temple Place
, Suite
330, Boston
, MA
02111-1307 USA
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
31 #include "apogee_ppi.h"
35 extern char* me
; /* argv[0] */
36 ApogeeCam
*MainCam
= NULL
; /* Main and only camera */
38 /* send client definitions of all properties */
42 MainCam
= new ApogeeCam();
45 void ISGetProperties (const char *dev
)
47 if (dev
&& strcmp (mydev
, dev
))
52 MainCam
->ISGetProperties(dev
);
56 void ISNewSwitch (const char *dev
, const char *name
, ISState
*states
, char *names
[], int n
)
59 /* ignore if not ours */
60 if (dev
&& strcmp (dev
, mydev
))
65 MainCam
->ISNewSwitch(dev
, name
, states
, names
, n
);
68 void ISNewText (const char *dev
, const char *name
, char *texts
[], char *names
[], int n
)
70 /* ignore if not ours */
71 if (dev
&& strcmp (mydev
, dev
))
76 MainCam
->ISNewText(dev
, name
, texts
, names
, n
);
80 void ISNewNumber (const char *dev
, const char *name
, double values
[], char *names
[], int n
)
83 /* ignore if not ours */
84 if (dev
&& strcmp (dev
, mydev
))
89 MainCam
->ISNewNumber(dev
, name
, values
, names
, n
);
92 void ISNewBLOB (const char */
*dev*/
, const char */
*name*/
, int */
*sizes
[]*/
, char **/
*blobs
[]*/
, char **/
*formats
[]*/
, char **/
*names
[]*/
, int /*n*/)
97 ApogeeCam::ApogeeCam()
105 ApogeeCam::~ApogeeCam()
110 void ApogeeCam::initProperties()
112 fillSwitch(&PowerS
[0], "CONNECT", "Connect", ISS_OFF
);
113 fillSwitch(&PowerS
[1], "DISCONNECT", "Disconnect", ISS_ON
);
114 fillSwitchVector(&PowerSP
, PowerS
, NARRAY(PowerS
), mydev
, "CONNECTION", "Connection", COMM_GROUP
, IP_RW
, ISR_1OFMANY
, 60, IPS_IDLE
);
116 fillSwitch(&FrameTypeS
[0], "FRAME_LIGHT", "Light", ISS_ON
);
117 fillSwitch(&FrameTypeS
[1], "FRAME_BIAS", "Bias", ISS_OFF
);
118 fillSwitch(&FrameTypeS
[2], "FRAME_DARK", "Dark", ISS_OFF
);
119 fillSwitch(&FrameTypeS
[3], "FRAME_FLAT", "Flat Field", ISS_OFF
);
120 fillSwitchVector(&FrameTypeSP
, FrameTypeS
, NARRAY(FrameTypeS
), mydev
, "FRAME_TYPE", "Frame Type", EXPOSE_GROUP
, IP_RW
, ISR_1OFMANY
, 60, IPS_IDLE
);
122 fillNumber(&FrameN
[0], "X", "", "%.0f", 0., MAX_PIXELS
, 1., 0.);
123 fillNumber(&FrameN
[1], "Y", "", "%.0f", 0., MAX_PIXELS
, 1., 0.);
124 fillNumber(&FrameN
[2], "Width", "", "%.0f", 0., MAX_PIXELS
, 1., 0.);
125 fillNumber(&FrameN
[3], "Height", "", "%.0f", 0., MAX_PIXELS
, 1., 0.);
126 fillNumberVector(&FrameNP
, FrameN
, NARRAY(FrameN
), mydev
, "FRAME", "Frame", IMAGE_GROUP
, IP_RW
, 60, IPS_IDLE
);
128 fillNumber(&BinningN
[0], "HOR_BIN", "X", "%0.f", 1., MAXHBIN
, 1., 1.);
129 fillNumber(&BinningN
[1], "VER_BIN", "Y", "%0.f", 1., MAXVBIN
, 1., 1.);
130 fillNumberVector(&BinningNP
, BinningN
, NARRAY(BinningN
), mydev
, "BINNING", "Binning", IMAGE_GROUP
, IP_RW
, 60, IPS_IDLE
);
132 fillNumber(&ExposeTimeN
[0], "EXPOSE_S", "Duration (s)", "%5.2f", 0., 36000., 0.5, 1.);
133 fillNumberVector(&ExposeTimeNP
, ExposeTimeN
, NARRAY(ExposeTimeN
), mydev
, "EXPOSE_DURATION", "Expose", EXPOSE_GROUP
, IP_RW
, 60, IPS_IDLE
);
135 fillNumber(&TemperatureN
[0], "TEMPERATURE", "Temperature", "%+06.2f", MIN_CCD_TEMP
, MAX_CCD_TEMP
, 0.2, 0.);
136 fillNumberVector(&TemperatureNP
, TemperatureN
, NARRAY(TemperatureN
), mydev
, "CCD_TEMPERATURE", "Expose", EXPOSE_GROUP
, IP_RW
, 60, IPS_IDLE
);
138 strcpy(imageB
.name
, "CCD1");
139 strcpy(imageB
.label
, "Feed");
140 strcpy(imageB
.format
, "");
149 strcpy(imageBP
.device
, mydev
);
150 strcpy(imageBP
.name
, "Video");
151 strcpy(imageBP
.label
, "Video");
152 strcpy(imageBP
.group
, COMM_GROUP
);
153 strcpy(imageBP
.timestamp
, "");
156 imageBP
.s
= IPS_IDLE
;
157 imageBP
.bp
= &imageB
;
164 bool ApogeeCam::loadXMLModel()
166 LilXML
*XMLParser
= newLilXML();
167 XMLEle
*root
= NULL
, *camera
= NULL
;
169 FILE *modelSpecFile
= NULL
;
173 //IDLog("Top dir is "TOP_DATADIR, NULL);
174 modelSpecFile
= fopen(TOP_DATADIR
"/apogee_caminfo.xml", "r");
175 //modelSpecFile = fopen("/opt/kde3/share/apps/kstars/apogee_caminfo.xml", "r");
176 if (modelSpecFile
== NULL
)
178 IDLog("Error: Unable to open file apogee_caminfo.xml\n");
179 IDMessage(mydev
, "Error: Unable to open file apogee_caminfo.xml");
183 root
= readXMLFile(modelSpecFile
, XMLParser
, errmsg
);
186 IDLog("Error: Unable to process apogee_caminfo.xml. %s\n", errmsg
);
187 IDMessage(mydev
, "Error: Unable to process apogee_caminfo.xml. %s\n", errmsg
);
188 fclose(modelSpecFile
);
189 delLilXML(XMLParser
);
193 for (camera
= nextXMLEle (root
, 1); camera
!= NULL
; camera
= nextXMLEle (root
, 0))
195 modelName
= findXMLAtt(camera
, "model");
196 if (modelName
== NULL
)
199 ApogeeModelS
= (ApogeeModelS
== NULL
) ? (ISwitch
*) malloc (sizeof(ISwitch
))
200 : (ISwitch
*) realloc(ApogeeModelS
, sizeof(ISwitch
) * (ncams
+ 1));
202 snprintf(ApogeeModelS
[ncams
].name
, MAXINDINAME
, "Model%d", ncams
);
203 strcpy(ApogeeModelS
[ncams
].label
, valuXMLAtt(modelName
));
204 ApogeeModelS
[ncams
].s
= (ncams
== 0) ? ISS_ON
: ISS_OFF
;
205 ApogeeModelS
[ncams
].svp
= NULL
;
206 ApogeeModelS
[ncams
].aux
= NULL
;
211 fclose(modelSpecFile
);
212 delLilXML(XMLParser
);
216 fillSwitchVector(&ApogeeModelSP
, ApogeeModelS
, ncams
, mydev
, "Model", "", COMM_GROUP
, IP_RW
, ISR_1OFMANY
, 60, IPS_IDLE
);
224 void ApogeeCam::ISGetProperties(const char */
*dev*/
)
228 IDDefSwitch(&PowerSP
, NULL
);
230 IDDefSwitch(&ApogeeModelSP
, NULL
);
232 IDMessage(mydev
, "Error: Unable to read camera specifications. Driver is disabled.");
233 IDDefBLOB(&imageBP
, NULL
);
236 IDDefSwitch(&FrameTypeSP
, NULL
);
237 IDDefNumber(&ExposeTimeNP
, NULL
);
238 IDDefNumber(&TemperatureNP
, NULL
);
241 IDDefNumber(&FrameNP
, NULL
);
242 IDDefNumber(&BinningNP
, NULL
);
244 IEAddTimer (POLLMS
, ApogeeCam::ISStaticPoll
, this);
249 void ApogeeCam::ISNewSwitch (const char */
*dev*/
, const char *name
, ISState
*states
, char *names
[], int n
)
253 if (!strcmp (name
, PowerSP
.name
))
255 IUResetSwitches(&PowerSP
);
256 IUUpdateSwitches(&PowerSP
, states
, names
, n
);
262 if (!strcmp(FrameTypeSP
.name
, name
))
264 if (checkPowerS(&FrameTypeSP
))
267 IUResetSwitches(&FrameTypeSP
);
268 IUUpdateSwitches(&FrameTypeSP
, states
, names
, n
);
269 FrameTypeSP
.s
= IPS_OK
;
270 IDSetSwitch(&FrameTypeSP
, NULL
);
276 if (!strcmp(ApogeeModelSP
.name
, name
))
278 IUResetSwitches(&ApogeeModelSP
);
279 IUUpdateSwitches(&ApogeeModelSP
, states
, names
, n
);
280 ApogeeModelSP
.s
= IPS_OK
;
281 IDSetSwitch(&ApogeeModelSP
, NULL
);
287 void ApogeeCam::ISNewText (const char */
*dev*/
, const char */
*name*/
, char **/
*texts
[]*/
, char **/
*names
[]*/
, int /*n*/)
292 void ApogeeCam::ISNewNumber (const char */
*dev*/
, const char *name
, double values
[], char *names
[], int n
)
295 if (!strcmp (ExposeTimeNP
.name
, name
))
297 if (checkPowerN(&ExposeTimeNP
))
300 if (ExposeTimeNP
.s
== IPS_BUSY
)
303 ExposeTimeNP
.s
= IPS_IDLE
;
304 ExposeTimeN
[0].value
= 0;
306 IDSetNumber(&ExposeTimeNP
, "Exposure cancelled.");
307 IDLog("Exposure Cancelled.\n");
311 ExposeTimeNP
.s
= IPS_IDLE
;
313 IUUpdateNumbers(&ExposeTimeNP
, values
, names
, n
);
315 IDLog("Exposure Time (ms) is: %g\n", ExposeTimeN
[0].value
);
317 handleExposure(NULL
);
321 if (!strcmp(TemperatureNP
.name
, name
))
323 if (checkPowerN(&TemperatureNP
))
326 TemperatureNP
.s
= IPS_IDLE
;
328 if (values
[0] < MIN_CCD_TEMP
|| values
[0] > MAX_CCD_TEMP
)
330 IDSetNumber(&TemperatureNP
, "Error: valid range of temperature is from %d to %d", MIN_CCD_TEMP
, MAX_CCD_TEMP
);
334 targetTemp
= values
[0];
335 cam
->write_CoolerMode(0);
336 cam
->write_CoolerMode(1);
337 cam
->write_CoolerSetPoint(targetTemp
);
339 TemperatureNP
.s
= IPS_BUSY
;
341 IDSetNumber(&TemperatureNP
, "Setting CCD temperature to %+06.2f C", values
[0]);
342 IDLog("Setting CCD temperature to %+06.2f C\n", values
[0]);
346 if (!strcmp(FrameNP
.name
, name
))
348 if (checkPowerN(&FrameNP
))
352 IUUpdateNumbers(&FrameNP
, values
, names
, n
);
354 cam
->m_StartX
= (int) FrameN
[0].value
;
355 cam
->m_StartY
= (int) FrameN
[1].value
;
356 cam
->m_NumX
= (int) FrameN
[2].value
;
357 cam
->m_NumY
= (int) FrameN
[3].value
;
358 IDSetNumber(&FrameNP
, NULL
);
363 if (!strcmp(BinningNP
.name
, name
))
365 if (checkPowerN(&BinningNP
))
369 BinningNP
.s
= IPS_OK
;
370 IUUpdateNumbers(&BinningNP
, values
, names
, n
);
372 cam
->m_BinX
= (int) BinningN
[0].value
;
373 cam
->m_BinY
= (int) BinningN
[1].value
;
375 IDLog("Binning is: %.0f x %.0f\n", BinningN
[0].value
, BinningN
[1].value
);
381 void ApogeeCam::ISStaticPoll(void *p
)
383 if (!((ApogeeCam
*)p
)->isCCDConnected())
385 IEAddTimer (POLLMS
, ApogeeCam::ISStaticPoll
, p
);
389 ((ApogeeCam
*) p
)->ISPoll();
391 IEAddTimer (POLLMS
, ApogeeCam::ISStaticPoll
, p
);
394 void ApogeeCam::ISPoll()
400 switch (ExposeTimeNP
.s
)
408 readStatus
= cam
->read_Status();
411 IDLog("Error in exposure!\n");
412 ExposeTimeNP
.s
= IPS_IDLE
;
413 ExposeTimeN
[0].value
= 0;
414 IDSetNumber(&ExposeTimeNP
, "Error in exposure procedure.");
417 else if (readStatus
== Camera_Status_ImageReady
)
419 ExposeTimeN
[0].value
= 0;
420 ExposeTimeNP
.s
= IPS_OK
;
421 IDSetNumber(&ExposeTimeNP
, "Exposure done, downloading image...");
422 IDLog("Exposure done, downloading image...\n");
423 /* grab and save image */
428 ExposeTimeN
[0].value
--;
429 IDSetNumber(&ExposeTimeNP
, NULL
);
436 switch (TemperatureNP
.s
)
444 TemperatureN
[0].value
= cam
->read_Temperature();
445 IDSetNumber(&TemperatureNP
, NULL
);
452 ccdTemp
= cam
->read_Temperature();
454 if (fabs(targetTemp
- ccdTemp
) <= TEMP_THRESHOLD
)
455 TemperatureNP
.s
= IPS_OK
;
458 TemperatureN
[0].value
= ccdTemp
;
459 IDSetNumber(&TemperatureNP
, NULL
);
468 /* Downloads the image from the CCD row by row and store them
470 N.B. No processing is done on the image */
471 void ApogeeCam::grabImage()
477 char filename
[] = "/tmp/fitsXXXXXX";
479 if ((fd
= mkstemp(filename
)) < 0)
481 IDMessage(mydev
, "Error making temporary filename.");
482 IDLog("Error making temporary filename.\n");
487 img_size
= APGFrame
.width
* APGFrame
.height
* sizeof(unsigned short);
489 APGFrame
.img
= (unsigned short *) malloc (img_size
);
491 if (APGFrame
.img
== NULL
)
493 IDMessage(mydev
, "Not enough memory to store image.");
494 IDLog("Not enough memory to store image.\n");
498 if (!cam
->GetImage( APGFrame
.img
, APGFrame
.width
, APGFrame
.height
))
501 IDMessage(mydev
, "GetImage() failed.");
502 IDLog("GetImage() failed.");
506 err
= writeFITS(filename
, errmsg
);
511 IDMessage(mydev
, errmsg
, NULL
);
519 int ApogeeCam::writeFITS(char *filename
, char errmsg
[])
523 int bpp
, bpsl
, width
, height
;
527 ofp
= fits_open (filename
, "w");
530 sprintf(errmsg
, "Error: cannot open file for writing.");
534 width
= APGFrame
.width
;
535 height
= APGFrame
.height
;
536 bpp
= sizeof(unsigned short); /* Bytes per Pixel */
537 bpsl
= bpp
* APGFrame
.width
; /* Bytes per Line */
540 hdu
= create_fits_header (ofp
, width
, height
, bpp
);
543 sprintf(errmsg
, "Error: creating FITS header failed.");
546 if (fits_write_header (ofp
, hdu
) < 0)
548 sprintf(errmsg
, "Error: writing to FITS header failed.");
552 for (int i
=0; i
< height
; i
++)
553 for (int j
=0 ; j
< width
; j
++)
554 APGFrame
.img
[width
* i
+ j
] = getBigEndian( (APGFrame
.img
[width
* i
+ j
]) );
556 for (int i
= 0; i
< height
; i
++)
558 fwrite(APGFrame
.img
+ (i
* width
), 2, width
, ofp
->fp
);
562 nbytes
= nbytes
% FITS_RECORD_SIZE
;
565 while (nbytes
++ < FITS_RECORD_SIZE
)
569 if (ferror (ofp
->fp
))
571 sprintf(errmsg
, "Error: write error occured");
578 ExposeTimeNP
.s
= IPS_OK
;
579 IDSetNumber(&ExposeTimeNP
, NULL
);
580 IDLog("Loading FITS image...\n");
582 uploadFile(filename
);
588 void ApogeeCam::uploadFile(char * filename
)
592 unsigned char *fitsData
, *compressedData
;
594 unsigned int i
=0, nr
= 0;
595 uLongf compressedBytes
=0;
599 if ( -1 == stat (filename
, &stat_p
))
601 IDLog(" Error occoured attempting to stat %s\n", filename
);
605 totalBytes
= stat_p
.st_size
;
606 fitsData
= (unsigned char *) malloc (sizeof(unsigned char) * totalBytes
);
607 compressedData
= (unsigned char *) malloc (sizeof(unsigned char) * totalBytes
+ totalBytes
/ 64 + 16 + 3);
609 if (fitsData
== NULL
|| compressedData
== NULL
)
611 IDLog("Error! low memory. Unable to initialize fits buffers.\n");
615 fitsFile
= fopen(filename
, "r");
617 if (fitsFile
== NULL
)
620 /* #1 Read file from disk */
621 for (i
=0; i
< totalBytes
; i
+= nr
)
623 nr
= fread(fitsData
+ i
, 1, totalBytes
- i
, fitsFile
);
627 IDLog("Error reading temporary FITS file.\n");
632 compressedBytes
= sizeof(char) * totalBytes
+ totalBytes
/ 64 + 16 + 3;
635 r
= compress2(compressedData
, &compressedBytes
, fitsData
, totalBytes
, 9);
638 /* this should NEVER happen */
639 IDLog("internal error - compression failed: %d\n", r
);
644 imageB
.blob
= compressedData
;
645 imageB
.bloblen
= compressedBytes
;
646 imageB
.size
= totalBytes
;
647 strcpy(imageB
.format
, ".fits.z");
649 IDSetBLOB (&imageBP
, NULL
);
652 free (compressedData
);
656 /* Initiates the exposure procedure */
657 void ApogeeCam::handleExposure(void *p
)
660 int curFrame
= getOnSwitch(&FrameTypeSP
);
669 if (!cam
->Expose( (int) ExposeTimeN
[0].value
, true ))
671 ExposeTimeNP
.s
= IPS_IDLE
;
672 IDSetNumber(&ExposeTimeNP
, "Light Camera exposure failed.");
673 IDLog("Light Camera exposure failed.\n");
678 /* BIAS frame is the same as DARK but with minimum period. i.e. readout from camera electronics.
681 if (!cam
->Expose( 0.05 , false ))
683 ExposeTimeNP
.s
= IPS_IDLE
;
684 IDSetNumber(&ExposeTimeNP
, "Bias Camera exposure failed.");
685 IDLog("Bias Camera exposure failed.\n");
692 if (!cam
->Expose( (int) ExposeTimeN
[0].value
, false ))
694 ExposeTimeNP
.s
= IPS_IDLE
;
695 IDSetNumber(&ExposeTimeNP
, "Dark Camera exposure failed.");
696 IDLog("Dark Camera exposure failed.\n");
702 if (!cam
->Expose( (int) ExposeTimeN
[0].value
, true ))
704 ExposeTimeNP
.s
= IPS_IDLE
;
705 IDSetNumber(&ExposeTimeNP
, "Flat Camera exposure failed.");
706 IDLog("Flat Camera exposure failed.\n");
712 APGFrame
.frameType
= curFrame
;
713 APGFrame
.width
= (int) FrameN
[2].value
;
714 APGFrame
.height
= (int) FrameN
[3].value
;
715 APGFrame
.expose
= (int) ExposeTimeN
[0].value
;
716 APGFrame
.temperature
= TemperatureN
[0].value
;
717 APGFrame
.binX
= (int) BinningN
[0].value
;
718 APGFrame
.binY
= (int) BinningN
[1].value
;
720 ExposeTimeNP
.s
= IPS_BUSY
;
722 IDSetNumber(&ExposeTimeNP
, "Taking a %g seconds frame...", ExposeTimeN
[0].value
);
723 IDLog("Taking a frame...\n");
727 /* Retrieves basic data from the CCD upon connection like temperature, array size, firmware..etc */
728 void ApogeeCam::getBasicData()
732 // Maximum resolution
733 FrameN
[2].max
= cam
->m_NumX
;
734 FrameN
[3].max
= cam
->m_NumY
;
735 IUUpdateMinMax(&FrameNP
);
738 BinningN
[0].max
= cam
->m_MaxBinX
;
739 BinningN
[1].max
= cam
->m_MaxBinX
;
740 IUUpdateMinMax(&BinningNP
);
742 // Current Temperature
743 TemperatureN
[0].value
= cam
->read_Temperature();
744 IDSetNumber(&TemperatureNP
, NULL
);
748 int ApogeeCam::manageDefaults(char errmsg
[])
756 exposeTimeMS
= (int) (ExposeTimeN
[0].value
* 1000.);
758 IDLog("Setting default exposure time of %d ms.\n", exposeTimeMS
);
759 if ( (err
= FLISetExposureTime(fli_dev
, exposeTimeMS
) ))
761 sprintf(errmsg
, "FLISetExposureTime() failed. %s.\n", strerror((int)-err
));
766 /* Default frame type is NORMAL */
767 if ( (err
= FLISetFrameType(fli_dev
, FLI_FRAME_TYPE_NORMAL
) ))
769 sprintf(errmsg
, "FLISetFrameType() failed. %s.\n", strerror((int)-err
));
774 /* X horizontal binning */
775 if ( (err
= FLISetHBin(fli_dev
, BinningN
[0].value
) ))
777 sprintf(errmsg
, "FLISetBin() failed. %s.\n", strerror((int)-err
));
782 /* Y vertical binning */
783 if ( (err
= FLISetVBin(fli_dev
, BinningN
[1].value
) ))
785 sprintf(errmsg
, "FLISetVBin() failed. %s.\n", strerror((int)-err
));
790 IDLog("Setting default binning %f x %f.\n", BinningN
[0].value
, BinningN
[1].value
);
792 FLISetNFlushes(fli_dev
, NFLUSHES
);
795 if (setImageArea(errmsg
))
805 int ApogeeCam::getOnSwitch(ISwitchVectorProperty
*sp
)
808 for (i
=0; i
< sp
->nsp
; i
++)
810 /*IDLog("Switch %s is %s\n", sp->sp[i].name, sp->sp[i].s == ISS_ON ? "On" : "Off");*/
811 if (sp
->sp
[i
].s
== ISS_ON
)
818 int ApogeeCam::checkPowerS(ISwitchVectorProperty
*sp
)
820 if (PowerSP
.s
!= IPS_OK
)
822 if (!strcmp(sp
->label
, ""))
823 IDMessage (mydev
, "Cannot change property %s while the CCD is offline.", sp
->name
);
825 IDMessage (mydev
, "Cannot change property %s while the CCD is offline.", sp
->label
);
828 IDSetSwitch(sp
, NULL
);
835 int ApogeeCam::checkPowerN(INumberVectorProperty
*np
)
837 if (PowerSP
.s
!= IPS_OK
)
839 if (!strcmp(np
->label
, ""))
840 IDMessage (mydev
, "Cannot change property %s while the CCD is offline.", np
->name
);
842 IDMessage (mydev
, "Cannot change property %s while the CCD is offline.", np
->label
);
845 IDSetNumber(np
, NULL
);
852 int ApogeeCam::checkPowerT(ITextVectorProperty
*tp
)
855 if (PowerSP
.s
!= IPS_OK
)
857 if (!strcmp(tp
->label
, ""))
858 IDMessage (mydev
, "Cannot change property %s while the CCD is offline.", tp
->name
);
860 IDMessage (mydev
, "Cannot change property %s while the CCD is offline.", tp
->label
);
871 void ApogeeCam::connectCCD()
874 /* USB by default {USB, SERIAL, PARALLEL, INET} */
881 PowerS
[0].s
= ISS_ON
;
882 PowerS
[1].s
= ISS_OFF
;
884 IDSetSwitch(&PowerSP
, "CCD is online. Retrieving basic data.");
885 IDLog("CCD is online. Retrieving basic data.\n");
891 PowerSP
.s
= IPS_IDLE
;
892 PowerS
[0].s
= ISS_OFF
;
893 PowerS
[1].s
= ISS_ON
;
894 IDSetSwitch(&PowerSP
, "Error: no cameras were detected.");
895 IDLog("Error: no cameras were detected.\n");
902 PowerS
[0].s
= ISS_OFF
;
903 PowerS
[1].s
= ISS_ON
;
904 PowerSP
.s
= IPS_IDLE
;
905 IDSetSwitch(&PowerSP
, "CCD is offline.");
911 bool ApogeeCam::initCamera()
913 LilXML
*XMLParser
= newLilXML();
914 XMLEle
*root
= NULL
, *camera
= NULL
, *ele
= NULL
;
915 XMLEle
*system
= NULL
, *geometry
= NULL
, *temp
= NULL
, *ccd
= NULL
;
920 spFile
= fopen(TOP_DATADIR
"/apogee_caminfo.xml", "r");
921 //spFile = fopen("/opt/kde3/share/apps/kstars/apogee_caminfo.xml", "r");
924 IDLog("Error: Unable to open file apogee_caminfo.xml\n");
925 IDMessage(mydev
, "Error: Unable to open file apogee_caminfo.xml");
929 root
= readXMLFile(spFile
, XMLParser
, errmsg
);
932 IDLog("Error: Unable to process apogee_caminfo.xml. %s\n", errmsg
);
933 IDMessage(mydev
, "Error: Unable to process apogee_caminfo.xml. %s\n", errmsg
);
935 delLilXML(XMLParser
);
941 // Let's locate which camera to load the configuration for
942 camera
= findXMLEle(root
, "Apogee_Camera");
946 IDLog("Error: Unable to find Apogee_Camera element.\n");
947 IDMessage(mydev
, "Error: Unable to find Apogee_Camera element.");
948 delLilXML(XMLParser
);
952 IDLog("Looking for %s - len %d\n", ApogeeModelS
[getOnSwitch(&ApogeeModelSP
)].label
, strlen(ApogeeModelS
[getOnSwitch(&ApogeeModelSP
)].label
));
954 ap
= findXMLAtt(camera
, "model");
957 IDLog("Error: Unable to find attribute model.\n");
958 IDMessage(mydev
, "Error: Unable to find attribute model.");
962 if (strcmp(valuXMLAtt(ap
), ApogeeModelS
[getOnSwitch(&ApogeeModelSP
)].label
))
964 IDLog("Camera %s not found in XML file\n", ApogeeModelS
[getOnSwitch(&ApogeeModelSP
)].label
);
965 IDMessage(mydev
, "Camera %s not found in XML file\n", ApogeeModelS
[getOnSwitch(&ApogeeModelSP
)].label
);
966 delLilXML(XMLParser
);
970 // Let's get the subsections now
971 system
= findXMLEle(camera
, "System");
972 geometry
= findXMLEle(camera
, "Geometry");
973 temp
= findXMLEle(camera
, "Temp");
974 ccd
= findXMLEle(camera
, "CCD");
978 IDLog("Error: Unable to find System element in camera.\n");
979 IDMessage(mydev
, "Error: Unable to find System element in camera.");
980 delLilXML(XMLParser
);
984 if (geometry
== NULL
)
986 IDLog("Error: Unable to find Geometry element in camera.\n");
987 IDMessage(mydev
, "Error: Unable to find Geometry element in camera.");
988 delLilXML(XMLParser
);
994 IDLog("Error: Unable to find Temp element in camera.\n");
995 IDMessage(mydev
, "Error: Unable to find Temp element in camera.");
996 delLilXML(XMLParser
);
1002 IDLog("Error: Unable to find CCD element in camera.\n");
1003 IDMessage(mydev
, "Error: Unable to find CCD element in camera.");
1004 delLilXML(XMLParser
);
1008 cam
= new CCameraIO();
1012 IDLog("Error: Failed to create CCameraIO object.\n");
1013 IDMessage(mydev
, "Error: Failed to create CCameraIO object.");
1014 delLilXML(XMLParser
);
1021 bAddr
= hextoi(valuXMLAtt(findXMLAtt(system
, "Base"))) & 0xFFF;
1024 ap
= findXMLAtt(geometry
, "Rows");
1027 IDLog("Error: Unable to find attribute Rows.\n");
1028 IDMessage(mydev
, "Error: Unable to find attribute Rows.");
1029 delLilXML(XMLParser
);
1033 cam
->m_Rows
= hextoi(valuXMLAtt(ap
));
1036 ap
= findXMLAtt(geometry
, "Columns");
1039 IDLog("Error: Unable to find attribute Columns.\n");
1040 IDMessage(mydev
, "Error: Unable to find attribute Columns.");
1041 delLilXML(XMLParser
);
1045 cam
->m_Columns
= hextoi(valuXMLAtt(ap
));
1048 ele
= findXMLEle(system
, "PP_Repeat");
1051 IDLog("Error: Unable to find element PP_Repeat.\n");
1052 IDMessage(mydev
, "Error: Unable to find element PP_Repeat.");
1053 delLilXML(XMLParser
);
1057 val
= hextoi(pcdataXMLEle(ele
));
1058 if (val
> 0 && val
<= 1000)
1059 cam
->m_PPRepeat
= val
;
1062 if (!cam
->InitDriver(0))
1064 IDLog("Error: Failed to Init Driver.\n");
1065 IDMessage(mydev
, "Error: Failed to Init Driver.");
1066 delLilXML(XMLParser
);
1073 ele
= findXMLEle(system
, "Cable");
1076 IDLog("Error: Unable to find element Cable.\n");
1077 IDMessage(mydev
, "Error: Unable to find element Cable.");
1078 delLilXML(XMLParser
);
1082 if (!strcmp("Long", pcdataXMLEle(ele
)))
1084 cam
->write_LongCable( true );
1085 IDLog("Cable is long\n");
1089 cam
->write_LongCable( false );
1090 IDLog("Cable is short\n");
1094 if (!cam
->read_Present())
1096 IDLog("Error: read_Present() failed.\n");
1097 IDMessage(mydev
, "Error: read_Present() failed.");
1098 delLilXML(XMLParser
);
1103 cam
->write_UseTrigger( false );
1104 cam
->write_ForceShutterOpen( false );
1107 ele
= findXMLEle(system
, "High_Priority");
1110 if (!strcmp(pcdataXMLEle(ele
), "True"))
1111 cam
->m_HighPriority
= true;
1113 cam
->m_HighPriority
= false;
1117 ele
= findXMLEle(system
, "Data_Bits");
1120 val
= hextoi(pcdataXMLEle(ele
));
1121 if (val
>= 8 && val
<= 18) cam
->m_DataBits
= val
;
1125 ele
= findXMLEle(system
, "Sensor");
1128 if (!strcmp(pcdataXMLEle(ele
), "CCD"))
1129 cam
->m_SensorType
= Camera_SensorType_CCD
;
1131 cam
->m_SensorType
= Camera_SensorType_CMOS
;
1135 ele
= findXMLEle(system
, "Mode");
1138 val
= hextoi(pcdataXMLEle(ele
)) & 0xF;
1139 cam
->write_Mode( val
);
1140 IDLog("Mode %d\n", val
);
1143 cam
->write_Mode( 0 );
1146 ele
= findXMLEle(system
, "Test");
1149 val
= hextoi(pcdataXMLEle(ele
)) & 0xF;
1150 cam
->write_TestBits( val
);
1151 IDLog("Test bits %d\n", val
);
1154 cam
->write_TestBits( 0 );
1157 ele
= findXMLEle(system
, "Test2");
1160 val
= hextoi(pcdataXMLEle(ele
)) & 0xF;
1161 cam
->write_Test2Bits( val
);
1162 IDLog("Test 2 bits %d\n", val
);
1165 cam
->write_Test2Bits( 0 );
1168 ele
= findXMLEle(system
, "Shutter_Speed");
1171 cam
->m_MaxExposure
= 10485.75;
1173 if (!strcmp(pcdataXMLEle(ele
), "Normal"))
1175 cam
->m_FastShutter
= false;
1176 cam
->m_MinExposure
= 0.01;
1177 IDLog("Shutter speed normal\n");
1179 else if ( (!strcmp(pcdataXMLEle(ele
), "Fast")) || (!strcmp(pcdataXMLEle(ele
), "Dual")) )
1181 cam
->m_FastShutter
= true;
1182 cam
->m_MinExposure
= 0.001;
1183 IDLog("Shutter speed fast\n");
1188 ele
= findXMLEle(system
, "Shutter_Bits");
1191 val
= hextoi(pcdataXMLEle(ele
));
1192 cam
->m_FastShutterBits_Mode
= val
& 0x0F;
1193 cam
->m_FastShutterBits_Test
= ( val
& 0xF0 ) >> 4;
1194 IDLog("Shutter bits %d\n", val
);
1198 ele
= findXMLEle(system
, "MaxBinX");
1201 val
= hextoi(pcdataXMLEle(ele
));
1202 if (val
>= 1 && val
<= MAXHBIN
)
1203 cam
->m_MaxBinX
= val
;
1207 ele
= findXMLEle(system
, "MaxBinY");
1210 val
= hextoi(pcdataXMLEle(ele
));
1211 if (val
>= 1 && val
<= MAXVBIN
)
1212 cam
->m_MaxBinY
= val
;
1216 ele
= findXMLEle(system
, "Guider_Relays");
1219 if (!strcmp(pcdataXMLEle(ele
), "True"))
1220 cam
->m_GuiderRelays
= true;
1222 cam
->m_GuiderRelays
= false;
1226 ele
= findXMLEle(system
, "Timeout");
1229 double dval
= atof(pcdataXMLEle(ele
));
1230 if (dval
>= 0.0 && dval
<= 10000.0) cam
->m_Timeout
= dval
;
1234 ele
= findXMLEle(geometry
, "BIC");
1237 val
= hextoi(pcdataXMLEle(ele
));
1238 if (val
>= 1 && val
<= MAXCOLUMNS
)
1243 ele
= findXMLEle(geometry
, "BIR");
1246 val
= hextoi(pcdataXMLEle(ele
));
1247 if (val
>= 1 && val
<= MAXROWS
)
1252 ele
= findXMLEle(geometry
, "SKIPC");
1255 val
= hextoi(pcdataXMLEle(ele
));
1256 if (val
>= 1 && val
<= MAXCOLUMNS
)
1261 ele
= findXMLEle(geometry
, "SKIPR");
1264 val
= hextoi(pcdataXMLEle(ele
));
1265 if (val
>= 1 && val
<= MAXROWS
)
1270 ele
= findXMLEle(geometry
, "ImgCols");
1273 val
= hextoi(pcdataXMLEle(ele
));
1274 if (val
>= 1 && val
<= MAXTOTALCOLUMNS
)
1275 cam
->m_ImgColumns
= val
;
1278 cam
->m_ImgColumns
= cam
->m_Columns
- cam
->m_BIC
- cam
->m_SkipC
;
1281 ele
= findXMLEle(geometry
, "ImgRows");
1284 val
= hextoi(pcdataXMLEle(ele
));
1285 if (val
>= 1 && val
<= MAXTOTALROWS
)
1286 cam
->m_ImgRows
= val
;
1289 cam
->m_ImgRows
= cam
->m_Rows
- cam
->m_BIR
- cam
->m_SkipR
;
1292 ele
= findXMLEle(geometry
, "HFlush");
1295 val
= hextoi(pcdataXMLEle(ele
));
1296 if (val
>= 1 && val
<= MAXHBIN
)
1297 cam
->m_HFlush
= val
;
1301 ele
= findXMLEle(geometry
, "VFlush");
1304 val
= hextoi(pcdataXMLEle(ele
));
1305 if (val
>= 1 && val
<= MAXVBIN
)
1306 cam
->m_VFlush
= val
;
1309 // Default to full frame
1310 cam
->m_NumX
= cam
->m_ImgColumns
;
1311 cam
->m_NumY
= cam
->m_ImgRows
;
1314 ap
= findXMLAtt(temp
, "Control");
1317 if (!strcmp(valuXMLAtt(ap
), "True"))
1318 cam
->m_TempControl
= true;
1320 cam
->m_TempControl
= false;
1324 ap
= findXMLAtt(temp
, "Cal");
1327 val
= hextoi(valuXMLAtt(ap
));
1328 if (val
>= 1 && val
<= 255)
1329 cam
->m_TempCalibration
= val
;
1333 ap
= findXMLAtt(temp
, "Scale");
1336 double dval
= atof(valuXMLAtt(ap
));
1337 if (dval
>= 1.0 && dval
<= 10.0)
1338 cam
->m_TempScale
= dval
;
1342 ap
= findXMLAtt(temp
, "Target");
1345 double dval
= atof(valuXMLAtt(ap
));
1346 if (dval
>= -60.0 && dval
<= 40.0)
1347 cam
->write_CoolerSetPoint( dval
);
1349 cam
->write_CoolerSetPoint( -10.0 ); // Default
1351 IDLog("Target: %g\n", dval
);
1355 ap
= findXMLAtt(ccd
, "Sensor");
1358 strncpy (cam
->m_Sensor
, valuXMLAtt(ap
), 255);
1359 IDLog("Sensor: %s\n", cam
->m_Sensor
);
1363 ele
= findXMLEle(ccd
, "Color");
1366 if (!strcmp(pcdataXMLEle(ele
), "True"))
1368 cam
->m_Color
= true;
1369 IDLog("Color: true\n");
1373 cam
->m_Color
= false;
1374 IDLog("Color: false\n");
1379 ele
= findXMLEle(ccd
, "Noise");
1381 cam
->m_Noise
= atof( pcdataXMLEle(ele
) );
1384 ele
= findXMLEle(ccd
, "Gain");
1386 cam
->m_Gain
= atof( pcdataXMLEle(ele
) );
1389 ele
= findXMLEle(ccd
, "PixelXSize");
1392 cam
->m_PixelXSize
= atof( pcdataXMLEle(ele
) );
1393 IDLog("Pixel X Size: %g\n", cam
->m_PixelXSize
);
1397 ele
= findXMLEle(ccd
, "PixelYSize");
1400 cam
->m_PixelYSize
= atof( pcdataXMLEle(ele
) );
1401 IDLog("Pixel Y Size: %g\n", cam
->m_PixelYSize
);
1405 IDLog("Cam Row: %d - Cam Cols: %d - PP_Repeat %d\n",cam
->m_Rows
, cam
->m_Columns
, cam
->m_PPRepeat
);
1406 IDLog("High_Priority %s - Data_Bits %d - Sensor %s\n", cam
->m_HighPriority
? "true" : "false", cam
->m_DataBits
, (cam
->m_SensorType
== Camera_SensorType_CCD
) ? "CCD" : "CMOS");
1407 IDLog("Max X Bin: %d - Max Y Bin: %d - Guider Relays: %s\n", cam
->m_MaxBinX
, cam
->m_MaxBinY
, cam
->m_GuiderRelays
? "true" : "false");
1408 IDLog("BIC: %d - BIR: %d - SKIPC: %d - SKIPR: %d - ImgRows: %d - ImgCols %d\n", cam
->m_BIC
, cam
->m_BIR
, cam
->m_SkipC
, cam
->m_SkipR
, cam
->m_ImgRows
, cam
->m_ImgColumns
);
1409 IDLog("HFlush: %d - VFlush: %d - Control: %s - Cal: %d - Scale: %g\n", cam
->m_HFlush
, cam
->m_VFlush
, cam
->m_TempControl
? "true" : "false", cam
->m_TempCalibration
, cam
->m_TempScale
);
1411 delLilXML(XMLParser
);
1416 /* isCCDConnected: return 1 if we have a connection, 0 otherwise */
1417 int ApogeeCam::isCCDConnected(void)
1419 return ((PowerS
[0].s
== ISS_ON
) ? 1 : 0);
1422 FITS_HDU_LIST
* ApogeeCam::create_fits_header (FITS_FILE
*ofp
, uint width
, uint height
, uint bpp
)
1425 FITS_HDU_LIST
*hdulist
;
1427 char temp_s
[FITS_CARD_SIZE
], expose_s
[FITS_CARD_SIZE
], binning_s
[FITS_CARD_SIZE
], pixel_s
[FITS_CARD_SIZE
], frame_s
[FITS_CARD_SIZE
];
1428 char obsDate
[FITS_CARD_SIZE
];
1430 snprintf(obsDate
, FITS_CARD_SIZE
, "DATE-OBS= '%s' /Observation Date UTC", timestamp());
1432 hdulist
= fits_add_hdu (ofp
);
1433 if (hdulist
== NULL
) return (NULL
);
1435 hdulist
->used
.simple
= 1;
1436 hdulist
->bitpix
= 16;
1438 hdulist
->naxisn
[0] = width
;
1439 hdulist
->naxisn
[1] = height
;
1440 hdulist
->naxisn
[2] = bpp
;
1441 // JM: TODO Record here the minimum and maximum pixel values
1442 /*hdulist->used.datamin = min();
1443 hdulist->datamin = min();
1444 hdulist->used.datamax = max();
1445 hdulist->datamax = max();*/
1446 hdulist
->used
.bzero
= 1;
1447 hdulist
->bzero
= 0.0;
1448 hdulist
->used
.bscale
= 1;
1449 hdulist
->bscale
= 1.0;
1451 snprintf(temp_s
, FITS_CARD_SIZE
, "CCD-TEMP= %g / degrees celcius", APGFrame
.temperature
);
1452 snprintf(expose_s
, FITS_CARD_SIZE
, "EXPOSURE= %d / milliseconds", APGFrame
.expose
);
1453 snprintf(binning_s
, FITS_CARD_SIZE
, "BINNING = '(%d x %d)'", APGFrame
.binX
, APGFrame
.binY
);
1454 //sprintf(pixel_s, "PIX-SIZ = '%.0f microns square'", PixelSizeN[0].value);
1455 switch (APGFrame
.frameType
)
1458 strcpy(frame_s
, "FRAME = 'Light'");
1461 strcpy(frame_s
, "FRAME = 'Bias'");
1464 strcpy(frame_s
, "FRAME = 'Flat Field'");
1467 strcpy(frame_s
, "FRAME = 'Dark'");
1471 fits_add_card (hdulist
, frame_s
);
1472 fits_add_card (hdulist
, temp_s
);
1473 fits_add_card (hdulist
, expose_s
);
1474 //fits_add_card (hdulist, pixel_s);
1475 fits_add_card (hdulist
, "INSTRUME= 'Apogee CCD'");
1476 fits_add_card (hdulist
, obsDate
);
1481 // Convert a string to a decimal or hexadecimal integer
1482 // Code taken from Dave Mills Apogee driver
1483 unsigned short ApogeeCam::hextoi(char *instr
)
1485 unsigned short val
, tot
= 0;
1488 long n
= strlen( instr
);
1490 { // Look for hex format e.g. 8Fh, A3H or 0x5D
1491 if ( instr
[ n
- 1 ] == 'h' || instr
[ n
- 1 ] == 'H' )
1493 else if ( *instr
== '0' && *(instr
+1) == 'x' )
1502 while (instr
&& *instr
&& isxdigit(*instr
))
1504 val
= *instr
++ - '0';
1508 tot
|= (val
& 0x0f);
1512 tot
= atoi( instr
);