moved kdeaccessibility kdeaddons kdeadmin kdeartwork kdebindings kdeedu kdegames...
[kdeedu.git] / kstars / kstars / indi / apogee_ppi.cpp
blob0c61aa2f4d1ce412d556fd16a5b297edcad3d638
1 #if 0
2 FLI CCD
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
20 #endif
22 #include <ctype.h>
23 #include <sys/stat.h>
24 #include <sys/time.h>
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <netinet/in.h>
28 #include <netdb.h>
29 #include <zlib.h>
31 #include "apogee_ppi.h"
32 #include "lilxml.h"
33 #include "base64.h"
35 extern char* me; /* argv[0] */
36 ApogeeCam *MainCam = NULL; /* Main and only camera */
38 /* send client definitions of all properties */
39 void ISInit()
41 if (MainCam == NULL)
42 MainCam = new ApogeeCam();
45 void ISGetProperties (const char *dev)
47 if (dev && strcmp (mydev, dev))
48 return;
50 ISInit();
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))
61 return;
63 ISInit();
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))
72 return;
74 ISInit();
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))
85 return;
87 ISInit();
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()
99 ApogeeModelS = NULL;
101 initProperties();
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, "");
141 imageB.blob = 0;
142 imageB.bloblen = 0;
143 imageB.size = 0;
144 imageB.bvp = 0;
145 imageB.aux0 = 0;
146 imageB.aux1 = 0;
147 imageB.aux2 = 0;
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, "");
154 imageBP.p = IP_RO;
155 imageBP.timeout = 0;
156 imageBP.s = IPS_IDLE;
157 imageBP.bp = &imageB;
158 imageBP.nbp = 1;
159 imageBP.aux = 0;
161 loadXMLModel();
164 bool ApogeeCam::loadXMLModel()
166 LilXML *XMLParser = newLilXML();
167 XMLEle *root = NULL, *camera = NULL;
168 XMLAtt *modelName;
169 FILE *modelSpecFile = NULL;
170 char errmsg[1024];
171 int ncams = 0;
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");
180 return false;
183 root = readXMLFile(modelSpecFile, XMLParser, errmsg);
184 if (root == NULL)
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);
190 return false;
193 for (camera = nextXMLEle (root, 1); camera != NULL; camera = nextXMLEle (root, 0))
195 modelName = findXMLAtt(camera, "model");
196 if (modelName == NULL)
197 continue;
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;
208 ncams++;
211 fclose(modelSpecFile);
212 delLilXML(XMLParser);
214 if (ncams > 0)
216 fillSwitchVector(&ApogeeModelSP, ApogeeModelS, ncams, mydev, "Model", "", COMM_GROUP, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
217 return true;
220 return false;
224 void ApogeeCam::ISGetProperties(const char */*dev*/)
227 /* COMM_GROUP */
228 IDDefSwitch(&PowerSP, NULL);
229 if (loadXMLModel())
230 IDDefSwitch(&ApogeeModelSP, NULL);
231 else
232 IDMessage(mydev, "Error: Unable to read camera specifications. Driver is disabled.");
233 IDDefBLOB(&imageBP, NULL);
235 /* Expose */
236 IDDefSwitch(&FrameTypeSP, NULL);
237 IDDefNumber(&ExposeTimeNP, NULL);
238 IDDefNumber(&TemperatureNP, NULL);
240 /* Image Group */
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)
252 /* Connection */
253 if (!strcmp (name, PowerSP.name))
255 IUResetSwitches(&PowerSP);
256 IUUpdateSwitches(&PowerSP, states, names, n);
257 connectCCD();
258 return;
261 /* Frame Type */
262 if (!strcmp(FrameTypeSP.name, name))
264 if (checkPowerS(&FrameTypeSP))
265 return;
267 IUResetSwitches(&FrameTypeSP);
268 IUUpdateSwitches(&FrameTypeSP, states, names, n);
269 FrameTypeSP.s = IPS_OK;
270 IDSetSwitch(&FrameTypeSP, NULL);
272 return;
275 /* Apogee Model */
276 if (!strcmp(ApogeeModelSP.name, name))
278 IUResetSwitches(&ApogeeModelSP);
279 IUUpdateSwitches(&ApogeeModelSP, states, names, n);
280 ApogeeModelSP.s = IPS_OK;
281 IDSetSwitch(&ApogeeModelSP, NULL);
282 return;
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)
294 /* Exposure time */
295 if (!strcmp (ExposeTimeNP.name, name))
297 if (checkPowerN(&ExposeTimeNP))
298 return;
300 if (ExposeTimeNP.s == IPS_BUSY)
302 cam->Reset();
303 ExposeTimeNP.s = IPS_IDLE;
304 ExposeTimeN[0].value = 0;
306 IDSetNumber(&ExposeTimeNP, "Exposure cancelled.");
307 IDLog("Exposure Cancelled.\n");
308 return;
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);
318 return;
321 if (!strcmp(TemperatureNP.name, name))
323 if (checkPowerN(&TemperatureNP))
324 return;
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);
331 return;
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]);
343 return;
346 if (!strcmp(FrameNP.name, name))
348 if (checkPowerN(&FrameNP))
349 return;
351 FrameNP.s = IPS_OK;
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);
360 } /* end FrameNP */
363 if (!strcmp(BinningNP.name, name))
365 if (checkPowerN(&BinningNP))
366 return;
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);
376 return;
381 void ApogeeCam::ISStaticPoll(void *p)
383 if (!((ApogeeCam *)p)->isCCDConnected())
385 IEAddTimer (POLLMS, ApogeeCam::ISStaticPoll, p);
386 return;
389 ((ApogeeCam *) p)->ISPoll();
391 IEAddTimer (POLLMS, ApogeeCam::ISStaticPoll, p);
394 void ApogeeCam::ISPoll()
396 static int mtc=5;
397 int readStatus=0;
398 double ccdTemp;
400 switch (ExposeTimeNP.s)
402 case IPS_IDLE:
403 case IPS_OK:
404 break;
406 case IPS_BUSY:
408 readStatus = cam->read_Status();
409 if (readStatus < 0)
411 IDLog("Error in exposure!\n");
412 ExposeTimeNP.s = IPS_IDLE;
413 ExposeTimeN[0].value = 0;
414 IDSetNumber(&ExposeTimeNP, "Error in exposure procedure.");
415 return;
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 */
424 grabImage();
425 return;
428 ExposeTimeN[0].value --;
429 IDSetNumber(&ExposeTimeNP, NULL);
430 break;
432 case IPS_ALERT:
433 break;
436 switch (TemperatureNP.s)
438 case IPS_IDLE:
439 case IPS_OK:
440 mtc--;
442 if (mtc == 0)
444 TemperatureN[0].value = cam->read_Temperature();
445 IDSetNumber(&TemperatureNP, NULL);
446 mtc = 5;
448 break;
450 case IPS_BUSY:
452 ccdTemp = cam->read_Temperature();
454 if (fabs(targetTemp - ccdTemp) <= TEMP_THRESHOLD)
455 TemperatureNP.s = IPS_OK;
457 mtc = 1;
458 TemperatureN[0].value = ccdTemp;
459 IDSetNumber(&TemperatureNP, NULL);
460 break;
462 case IPS_ALERT:
463 break;
468 /* Downloads the image from the CCD row by row and store them
469 in a raw file.
470 N.B. No processing is done on the image */
471 void ApogeeCam::grabImage()
474 long err;
475 int img_size, fd;
476 char errmsg[1024];
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");
483 return;
485 close(fd);
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");
495 return;
498 if (!cam->GetImage( APGFrame.img , APGFrame.width, APGFrame.height ))
500 free(APGFrame.img);
501 IDMessage(mydev, "GetImage() failed.");
502 IDLog("GetImage() failed.");
503 return;
506 err = writeFITS(filename, errmsg);
508 if (err)
510 free(APGFrame.img);
511 IDMessage(mydev, errmsg, NULL);
512 return;
515 free(APGFrame.img);
519 int ApogeeCam::writeFITS(char *filename, char errmsg[])
522 FITS_FILE* ofp;
523 int bpp, bpsl, width, height;
524 long nbytes;
525 FITS_HDU_LIST *hdu;
527 ofp = fits_open (filename, "w");
528 if (!ofp)
530 sprintf(errmsg, "Error: cannot open file for writing.");
531 return (-1);
534 width = APGFrame.width;
535 height = APGFrame.height;
536 bpp = sizeof(unsigned short); /* Bytes per Pixel */
537 bpsl = bpp * APGFrame.width; /* Bytes per Line */
538 nbytes = 0;
540 hdu = create_fits_header (ofp, width, height, bpp);
541 if (hdu == NULL)
543 sprintf(errmsg, "Error: creating FITS header failed.");
544 return (-1);
546 if (fits_write_header (ofp, hdu) < 0)
548 sprintf(errmsg, "Error: writing to FITS header failed.");
549 return (-1);
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);
559 nbytes += bpsl;
562 nbytes = nbytes % FITS_RECORD_SIZE;
563 if (nbytes)
565 while (nbytes++ < FITS_RECORD_SIZE)
566 putc (0, ofp->fp);
569 if (ferror (ofp->fp))
571 sprintf(errmsg, "Error: write error occured");
572 return (-1);
575 fits_close (ofp);
577 /* Success */
578 ExposeTimeNP.s = IPS_OK;
579 IDSetNumber(&ExposeTimeNP, NULL);
580 IDLog("Loading FITS image...\n");
582 uploadFile(filename);
584 return 0;
588 void ApogeeCam::uploadFile(char * filename)
591 FILE * fitsFile;
592 unsigned char *fitsData, *compressedData;
593 int r=0;
594 unsigned int i =0, nr = 0;
595 uLongf compressedBytes=0;
596 uLong totalBytes;
597 struct stat stat_p;
599 if ( -1 == stat (filename, &stat_p))
601 IDLog(" Error occoured attempting to stat %s\n", filename);
602 return;
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");
612 return;
615 fitsFile = fopen(filename, "r");
617 if (fitsFile == NULL)
618 return;
620 /* #1 Read file from disk */
621 for (i=0; i < totalBytes; i+= nr)
623 nr = fread(fitsData + i, 1, totalBytes - i, fitsFile);
625 if (nr <= 0)
627 IDLog("Error reading temporary FITS file.\n");
628 return;
632 compressedBytes = sizeof(char) * totalBytes + totalBytes / 64 + 16 + 3;
634 /* #2 Compress it */
635 r = compress2(compressedData, &compressedBytes, fitsData, totalBytes, 9);
636 if (r != Z_OK)
638 /* this should NEVER happen */
639 IDLog("internal error - compression failed: %d\n", r);
640 return;
643 /* #3 Send it */
644 imageB.blob = compressedData;
645 imageB.bloblen = compressedBytes;
646 imageB.size = totalBytes;
647 strcpy(imageB.format, ".fits.z");
648 imageBP.s = IPS_OK;
649 IDSetBLOB (&imageBP, NULL);
651 free (fitsData);
652 free (compressedData);
656 /* Initiates the exposure procedure */
657 void ApogeeCam::handleExposure(void *p)
660 int curFrame = getOnSwitch(&FrameTypeSP);
662 /* no warning */
663 p=p;
665 switch (curFrame)
667 /* Light frame */
668 case LIGHT_FRAME:
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");
674 return;
676 break;
678 /* BIAS frame is the same as DARK but with minimum period. i.e. readout from camera electronics.
680 case BIAS_FRAME:
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");
686 return;
688 break;
690 /* Dark frame */
691 case DARK_FRAME:
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");
697 return;
699 break;
701 case FLAT_FRAME:
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");
707 return;
709 break;
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);
737 // Maximum Bin
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[])
751 #if 0
753 long err;
754 int exposeTimeMS;
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));
762 IDLog(errmsg, NULL);
763 return -1;
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));
770 IDLog(errmsg, NULL);
771 return -1;
774 /* X horizontal binning */
775 if ( (err = FLISetHBin(fli_dev, BinningN[0].value) ))
777 sprintf(errmsg, "FLISetBin() failed. %s.\n", strerror((int)-err));
778 IDLog(errmsg, NULL);
779 return -1;
782 /* Y vertical binning */
783 if ( (err = FLISetVBin(fli_dev, BinningN[1].value) ))
785 sprintf(errmsg, "FLISetVBin() failed. %s.\n", strerror((int)-err));
786 IDLog(errmsg, NULL);
787 return -1;
790 IDLog("Setting default binning %f x %f.\n", BinningN[0].value, BinningN[1].value);
792 FLISetNFlushes(fli_dev, NFLUSHES);
794 /* Set image area */
795 if (setImageArea(errmsg))
796 return -1;
798 #endif
800 /* Success */
801 return 0;
805 int ApogeeCam::getOnSwitch(ISwitchVectorProperty *sp)
807 int i=0;
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)
812 return i;
815 return -1;
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);
824 else
825 IDMessage (mydev, "Cannot change property %s while the CCD is offline.", sp->label);
827 sp->s = IPS_IDLE;
828 IDSetSwitch(sp, NULL);
829 return -1;
832 return 0;
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);
841 else
842 IDMessage (mydev, "Cannot change property %s while the CCD is offline.", np->label);
844 np->s = IPS_IDLE;
845 IDSetNumber(np, NULL);
846 return -1;
849 return 0;
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);
859 else
860 IDMessage (mydev, "Cannot change property %s while the CCD is offline.", tp->label);
862 tp->s = IPS_IDLE;
863 IDSetText(tp, NULL);
864 return -1;
867 return 0;
871 void ApogeeCam::connectCCD()
874 /* USB by default {USB, SERIAL, PARALLEL, INET} */
875 switch (PowerS[0].s)
877 case ISS_ON:
878 if (initCamera())
880 /* Sucess! */
881 PowerS[0].s = ISS_ON;
882 PowerS[1].s = ISS_OFF;
883 PowerSP.s = IPS_OK;
884 IDSetSwitch(&PowerSP, "CCD is online. Retrieving basic data.");
885 IDLog("CCD is online. Retrieving basic data.\n");
886 getBasicData();
889 else
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");
896 return;
899 break;
901 case ISS_OFF:
902 PowerS[0].s = ISS_OFF;
903 PowerS[1].s = ISS_ON;
904 PowerSP.s = IPS_IDLE;
905 IDSetSwitch(&PowerSP, "CCD is offline.");
906 break;
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;
916 XMLAtt *ap;
917 FILE *spFile = NULL;
918 char errmsg[1024];
920 spFile = fopen(TOP_DATADIR"/apogee_caminfo.xml", "r");
921 //spFile = fopen("/opt/kde3/share/apps/kstars/apogee_caminfo.xml", "r");
922 if (spFile == NULL)
924 IDLog("Error: Unable to open file apogee_caminfo.xml\n");
925 IDMessage(mydev, "Error: Unable to open file apogee_caminfo.xml");
926 return false;
929 root = readXMLFile(spFile, XMLParser, errmsg);
930 if (root == NULL)
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);
934 fclose(spFile);
935 delLilXML(XMLParser);
936 return false;
939 fclose(spFile);
941 // Let's locate which camera to load the configuration for
942 camera = findXMLEle(root, "Apogee_Camera");
944 if (camera == NULL)
946 IDLog("Error: Unable to find Apogee_Camera element.\n");
947 IDMessage(mydev, "Error: Unable to find Apogee_Camera element.");
948 delLilXML(XMLParser);
949 return false;
952 IDLog("Looking for %s - len %d\n", ApogeeModelS[getOnSwitch(&ApogeeModelSP)].label, strlen(ApogeeModelS[getOnSwitch(&ApogeeModelSP)].label));
954 ap = findXMLAtt(camera, "model");
955 if (!ap)
957 IDLog("Error: Unable to find attribute model.\n");
958 IDMessage(mydev, "Error: Unable to find attribute model.");
959 return false;
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);
967 return false;
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");
976 if (system == NULL)
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);
981 return false;
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);
989 return false;
992 if (temp == NULL)
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);
997 return false;
1000 if (ccd == NULL)
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);
1005 return false;
1008 cam = new CCameraIO();
1010 if (cam == NULL)
1012 IDLog("Error: Failed to create CCameraIO object.\n");
1013 IDMessage(mydev, "Error: Failed to create CCameraIO object.");
1014 delLilXML(XMLParser);
1015 return false;
1018 int bAddr = 0x378;
1019 int val = 0;
1021 bAddr = hextoi(valuXMLAtt(findXMLAtt(system, "Base"))) & 0xFFF;
1023 // Rows
1024 ap = findXMLAtt(geometry, "Rows");
1025 if (!ap)
1027 IDLog("Error: Unable to find attribute Rows.\n");
1028 IDMessage(mydev, "Error: Unable to find attribute Rows.");
1029 delLilXML(XMLParser);
1030 return false;
1033 cam->m_Rows = hextoi(valuXMLAtt(ap));
1035 // Columns
1036 ap = findXMLAtt(geometry, "Columns");
1037 if (!ap)
1039 IDLog("Error: Unable to find attribute Columns.\n");
1040 IDMessage(mydev, "Error: Unable to find attribute Columns.");
1041 delLilXML(XMLParser);
1042 return false;
1045 cam->m_Columns = hextoi(valuXMLAtt(ap));
1047 // pp_repeat
1048 ele = findXMLEle(system, "PP_Repeat");
1049 if (!ele)
1051 IDLog("Error: Unable to find element PP_Repeat.\n");
1052 IDMessage(mydev, "Error: Unable to find element PP_Repeat.");
1053 delLilXML(XMLParser);
1054 return false;
1057 val = hextoi(pcdataXMLEle(ele));
1058 if (val > 0 && val <= 1000)
1059 cam->m_PPRepeat = val;
1061 // Initiate driver
1062 if (!cam->InitDriver(0))
1064 IDLog("Error: Failed to Init Driver.\n");
1065 IDMessage(mydev, "Error: Failed to Init Driver.");
1066 delLilXML(XMLParser);
1067 return false;
1070 cam->Reset();
1072 // Cable length
1073 ele = findXMLEle(system, "Cable");
1074 if (!ele)
1076 IDLog("Error: Unable to find element Cable.\n");
1077 IDMessage(mydev, "Error: Unable to find element Cable.");
1078 delLilXML(XMLParser);
1079 return false;
1082 if (!strcmp("Long", pcdataXMLEle(ele)))
1084 cam->write_LongCable( true );
1085 IDLog("Cable is long\n");
1087 else
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);
1099 return false;
1102 // Default settings
1103 cam->write_UseTrigger( false );
1104 cam->write_ForceShutterOpen( false );
1106 // High priority
1107 ele = findXMLEle(system, "High_Priority");
1108 if (ele)
1110 if (!strcmp(pcdataXMLEle(ele), "True"))
1111 cam->m_HighPriority = true;
1112 else
1113 cam->m_HighPriority = false;
1116 // Data bits
1117 ele = findXMLEle(system, "Data_Bits");
1118 if (ele)
1120 val = hextoi(pcdataXMLEle(ele));
1121 if (val >= 8 && val <= 18) cam->m_DataBits = val;
1124 // Sensor
1125 ele = findXMLEle(system, "Sensor");
1126 if (ele)
1128 if (!strcmp(pcdataXMLEle(ele), "CCD"))
1129 cam->m_SensorType = Camera_SensorType_CCD;
1130 else
1131 cam->m_SensorType = Camera_SensorType_CMOS;
1134 // Mode
1135 ele = findXMLEle(system, "Mode");
1136 if (ele)
1138 val = hextoi(pcdataXMLEle(ele)) & 0xF;
1139 cam->write_Mode( val );
1140 IDLog("Mode %d\n", val);
1142 else
1143 cam->write_Mode( 0 );
1145 // Test
1146 ele = findXMLEle(system, "Test");
1147 if (ele)
1149 val = hextoi(pcdataXMLEle(ele)) & 0xF;
1150 cam->write_TestBits( val );
1151 IDLog("Test bits %d\n", val);
1153 else
1154 cam->write_TestBits( 0 );
1156 // Test2
1157 ele = findXMLEle(system, "Test2");
1158 if (ele)
1160 val = hextoi(pcdataXMLEle(ele)) & 0xF;
1161 cam->write_Test2Bits( val );
1162 IDLog("Test 2 bits %d\n", val);
1164 else
1165 cam->write_Test2Bits( 0 );
1167 // Shutter Speed
1168 ele = findXMLEle(system, "Shutter_Speed");
1169 if (ele)
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");
1187 // Shutter Bits
1188 ele = findXMLEle(system, "Shutter_Bits");
1189 if (ele)
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);
1197 // Max X Bin
1198 ele = findXMLEle(system, "MaxBinX");
1199 if (ele)
1201 val = hextoi(pcdataXMLEle(ele));
1202 if (val >= 1 && val <= MAXHBIN)
1203 cam->m_MaxBinX = val;
1206 // Max Y Bin
1207 ele = findXMLEle(system, "MaxBinY");
1208 if (ele)
1210 val = hextoi(pcdataXMLEle(ele));
1211 if (val >= 1 && val <= MAXVBIN)
1212 cam->m_MaxBinY = val;
1215 // Guider Relays
1216 ele = findXMLEle(system, "Guider_Relays");
1217 if (ele)
1219 if (!strcmp(pcdataXMLEle(ele), "True"))
1220 cam->m_GuiderRelays = true;
1221 else
1222 cam->m_GuiderRelays = false;
1225 // Timeout
1226 ele = findXMLEle(system, "Timeout");
1227 if (ele)
1229 double dval = atof(pcdataXMLEle(ele));
1230 if (dval >= 0.0 && dval <= 10000.0) cam->m_Timeout = dval;
1233 // BIC
1234 ele = findXMLEle(geometry, "BIC");
1235 if (ele)
1237 val = hextoi(pcdataXMLEle(ele));
1238 if (val >= 1 && val <= MAXCOLUMNS)
1239 cam->m_BIC = val;
1242 // BIR
1243 ele = findXMLEle(geometry, "BIR");
1244 if (ele)
1246 val = hextoi(pcdataXMLEle(ele));
1247 if (val >= 1 && val <= MAXROWS)
1248 cam->m_BIR = val;
1251 // SKIPC
1252 ele = findXMLEle(geometry, "SKIPC");
1253 if (ele)
1255 val = hextoi(pcdataXMLEle(ele));
1256 if (val >= 1 && val <= MAXCOLUMNS)
1257 cam->m_SkipC = val;
1260 // SKIPR
1261 ele = findXMLEle(geometry, "SKIPR");
1262 if (ele)
1264 val = hextoi(pcdataXMLEle(ele));
1265 if (val >= 1 && val <= MAXROWS)
1266 cam->m_SkipR = val;
1269 // IMG COlS
1270 ele = findXMLEle(geometry, "ImgCols");
1271 if (ele)
1273 val = hextoi(pcdataXMLEle(ele));
1274 if (val >= 1 && val <= MAXTOTALCOLUMNS)
1275 cam->m_ImgColumns = val;
1277 else
1278 cam->m_ImgColumns = cam->m_Columns - cam->m_BIC - cam->m_SkipC;
1280 // IMG ROWS
1281 ele = findXMLEle(geometry, "ImgRows");
1282 if (ele)
1284 val = hextoi(pcdataXMLEle(ele));
1285 if (val >= 1 && val <= MAXTOTALROWS)
1286 cam->m_ImgRows = val;
1288 else
1289 cam->m_ImgRows = cam->m_Rows - cam->m_BIR - cam->m_SkipR;
1291 // Hor Flush
1292 ele = findXMLEle(geometry, "HFlush");
1293 if (ele)
1295 val = hextoi(pcdataXMLEle(ele));
1296 if (val >= 1 && val <= MAXHBIN)
1297 cam->m_HFlush = val;
1300 // Ver Flush
1301 ele = findXMLEle(geometry, "VFlush");
1302 if (ele)
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;
1313 // Temp Control
1314 ap = findXMLAtt(temp, "Control");
1315 if (ap)
1317 if (!strcmp(valuXMLAtt(ap), "True"))
1318 cam->m_TempControl = true;
1319 else
1320 cam->m_TempControl = false;
1323 // Calibration
1324 ap = findXMLAtt(temp, "Cal");
1325 if (ap)
1327 val = hextoi(valuXMLAtt(ap));
1328 if (val >= 1 && val <= 255)
1329 cam->m_TempCalibration = val;
1332 // Scale
1333 ap = findXMLAtt(temp, "Scale");
1334 if (ap)
1336 double dval = atof(valuXMLAtt(ap));
1337 if (dval >= 1.0 && dval <= 10.0)
1338 cam->m_TempScale = dval;
1341 // Target
1342 ap = findXMLAtt(temp, "Target");
1343 if (ap)
1345 double dval = atof(valuXMLAtt(ap));
1346 if (dval >= -60.0 && dval <= 40.0)
1347 cam->write_CoolerSetPoint( dval );
1348 else
1349 cam->write_CoolerSetPoint( -10.0 ); // Default
1351 IDLog("Target: %g\n", dval);
1354 // Sensor
1355 ap = findXMLAtt(ccd, "Sensor");
1356 if (ap)
1358 strncpy (cam->m_Sensor, valuXMLAtt(ap), 255);
1359 IDLog("Sensor: %s\n", cam->m_Sensor);
1362 // Color
1363 ele = findXMLEle(ccd, "Color");
1364 if (ele)
1366 if (!strcmp(pcdataXMLEle(ele), "True"))
1368 cam->m_Color = true;
1369 IDLog("Color: true\n");
1371 else
1373 cam->m_Color = false;
1374 IDLog("Color: false\n");
1378 // Noise
1379 ele = findXMLEle(ccd, "Noise");
1380 if (ele)
1381 cam->m_Noise = atof( pcdataXMLEle(ele) );
1383 // Noise
1384 ele = findXMLEle(ccd, "Gain");
1385 if (ele)
1386 cam->m_Gain = atof( pcdataXMLEle(ele) );
1388 // Pixel X Size
1389 ele = findXMLEle(ccd, "PixelXSize");
1390 if (ele)
1392 cam->m_PixelXSize = atof( pcdataXMLEle(ele) );
1393 IDLog("Pixel X Size: %g\n", cam->m_PixelXSize);
1396 // Pixel Y Size
1397 ele = findXMLEle(ccd, "PixelYSize");
1398 if (ele)
1400 cam->m_PixelYSize = atof( pcdataXMLEle(ele) );
1401 IDLog("Pixel Y Size: %g\n", cam->m_PixelYSize);
1404 // Log all values
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);
1413 return true;
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;
1437 hdulist->naxis = 2;
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)
1457 case LIGHT_FRAME:
1458 strcpy(frame_s, "FRAME = 'Light'");
1459 break;
1460 case BIAS_FRAME:
1461 strcpy(frame_s, "FRAME = 'Bias'");
1462 break;
1463 case FLAT_FRAME:
1464 strcpy(frame_s, "FRAME = 'Flat Field'");
1465 break;
1466 case DARK_FRAME:
1467 strcpy(frame_s, "FRAME = 'Dark'");
1468 break;
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);
1478 return (hdulist);
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;
1486 bool IsHEX = false;
1488 long n = strlen( instr );
1489 if ( n > 1 )
1490 { // Look for hex format e.g. 8Fh, A3H or 0x5D
1491 if ( instr[ n - 1 ] == 'h' || instr[ n - 1 ] == 'H' )
1492 IsHEX = true;
1493 else if ( *instr == '0' && *(instr+1) == 'x' )
1495 IsHEX = true;
1496 instr += 2;
1500 if ( IsHEX )
1502 while (instr && *instr && isxdigit(*instr))
1504 val = *instr++ - '0';
1505 if (9 < val)
1506 val -= 7;
1507 tot <<= 4;
1508 tot |= (val & 0x0f);
1511 else
1512 tot = atoi( instr );
1514 return tot;