moved kdeaccessibility kdeaddons kdeadmin kdeartwork kdebindings kdeedu kdegames...
[kdeedu.git] / kstars / kstars / indi / sbigccd.cpp
blob9b1ecf382cabf1875bed5776355be1d0eb69aa7b
1 #if 0
2 INDI driver for SBIG CCD
3 Copyright (C) 2005 Chris Curran (ccurran AT planetcurran DOT com)
5 Based on Apogee PPI driver by Jasem Mutlaq (mutlaqja AT ikarustech DOT com)
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #endif
23 #include <ctype.h>
24 #include <sys/stat.h>
25 #include <sys/time.h>
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <netdb.h>
30 #include <zlib.h>
32 #include "sbigccd.h"
33 #include "lilxml.h"
34 #include "base64.h"
36 extern char* me; /* argv[0] */
37 SBIGCam *MainCam = NULL; /* Main and only camera */
39 /* send client definitions of all properties */
40 void ISInit()
42 if (MainCam == NULL)
43 MainCam = new SBIGCam();
46 void ISGetProperties (const char *dev)
48 if (dev && strcmp (mydev, dev))
49 return;
51 ISInit();
53 MainCam->ISGetProperties(dev);
57 void ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n)
60 /* ignore if not ours */
61 if (dev && strcmp (dev, mydev))
62 return;
64 ISInit();
66 MainCam->ISNewSwitch(dev, name, states, names, n);
69 void ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n)
71 /* ignore if not ours */
72 if (dev && strcmp (mydev, dev))
73 return;
75 ISInit();
77 MainCam->ISNewText(dev, name, texts, names, n);
81 void ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n)
84 /* ignore if not ours */
85 if (dev && strcmp (dev, mydev))
86 return;
88 ISInit();
90 MainCam->ISNewNumber(dev, name, values, names, n);
93 void ISNewBLOB (const char */*dev*/, const char */*name*/, int */*sizes[]*/, char **/*blobs[]*/, char **/*formats[]*/, char **/*names[]*/, int /*n*/)
96 // We use this if we're receving binary data from the client. Most likely we won't for this driver.
100 SBIGCam::SBIGCam()
102 initProperties();
103 IEAddTimer (POLLMS, SBIGCam::ISStaticPoll, this);
106 SBIGCam::~SBIGCam()
111 void SBIGCam::initProperties()
113 fillSwitch(&PowerS[0], "CONNECT", "Connect", ISS_OFF);
114 fillSwitch(&PowerS[1], "DISCONNECT", "Disconnect", ISS_ON);
115 fillSwitchVector(&PowerSP, PowerS, NARRAY(PowerS), mydev, "CONNECTION", "Connection", COMM_GROUP, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
117 fillSwitch(&FrameTypeS[0], "FRAME_LIGHT", "Light", ISS_ON);
118 fillSwitch(&FrameTypeS[1], "FRAME_BIAS", "Bias", ISS_OFF);
119 fillSwitch(&FrameTypeS[2], "FRAME_DARK", "Dark", ISS_OFF);
120 fillSwitch(&FrameTypeS[3], "FRAME_FLAT", "Flat Field", ISS_OFF);
121 fillSwitchVector(&FrameTypeSP, FrameTypeS, NARRAY(FrameTypeS), mydev, "FRAME_TYPE", "Frame Type", EXPOSE_GROUP, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
123 fillNumber(&FrameN[0], "X", "", "%.0f", 0., MAX_PIXELS, 1., 0.);
124 fillNumber(&FrameN[1], "Y", "", "%.0f", 0., MAX_PIXELS, 1., 0.);
125 fillNumber(&FrameN[2], "Width", "", "%.0f", 0., MAX_PIXELS, 1., 0.);
126 fillNumber(&FrameN[3], "Height", "", "%.0f", 0., MAX_PIXELS, 1., 0.);
127 fillNumberVector(&FrameNP, FrameN, NARRAY(FrameN), mydev, "FRAME", "Frame", IMAGE_GROUP, IP_RW, 60, IPS_IDLE);
129 fillNumber(&BinningN[0], "HOR_BIN", "X", "%0.f", 1., MAXHBIN, 1., 1.);
130 fillNumber(&BinningN[1], "VER_BIN", "Y", "%0.f", 1., MAXVBIN, 1., 1.);
131 fillNumberVector(&BinningNP, BinningN, NARRAY(BinningN), mydev, "BINNING", "Binning", IMAGE_GROUP, IP_RW, 60, IPS_IDLE);
133 fillNumber(&ExposeTimeN[0], "EXPOSE_S", "Duration (s)", "%5.2f", 0., 36000., 0.5, 1.);
134 fillNumberVector(&ExposeTimeNP, ExposeTimeN, NARRAY(ExposeTimeN), mydev, "EXPOSE_DURATION", "Expose", EXPOSE_GROUP, IP_RW, 60, IPS_IDLE);
136 fillNumber(&TemperatureN[0], "TEMPERATURE", "Temperature", "%+06.2f", MIN_CCD_TEMP, MAX_CCD_TEMP, 0.2, 0.);
137 fillNumberVector(&TemperatureNP, TemperatureN, NARRAY(TemperatureN), mydev, "CCD_TEMPERATURE", "Expose", EXPOSE_GROUP, IP_RW, 60, IPS_IDLE);
139 // We need to setup the BLOB (Binary Large Object) below. Using this property, we can send FITS to our client
140 strcpy(imageB.name, "CCD1");
141 strcpy(imageB.label, "Feed");
142 strcpy(imageB.format, "");
143 imageB.blob = 0;
144 imageB.bloblen = 0;
145 imageB.size = 0;
146 imageB.bvp = 0;
147 imageB.aux0 = 0;
148 imageB.aux1 = 0;
149 imageB.aux2 = 0;
151 strcpy(imageBP.device, mydev);
152 strcpy(imageBP.name, "Video");
153 strcpy(imageBP.label, "Video");
154 strcpy(imageBP.group, COMM_GROUP);
155 strcpy(imageBP.timestamp, "");
156 imageBP.p = IP_RO;
157 imageBP.timeout = 0;
158 imageBP.s = IPS_IDLE;
159 imageBP.bp = &imageB;
160 imageBP.nbp = 1;
161 imageBP.aux = 0;
165 void SBIGCam::ISGetProperties(const char */*dev*/)
168 /* COMM_GROUP */
169 IDDefSwitch(&PowerSP, NULL);
170 IDDefBLOB(&imageBP, NULL);
172 /* Expose */
173 IDDefSwitch(&FrameTypeSP, NULL);
174 IDDefNumber(&ExposeTimeNP, NULL);
175 IDDefNumber(&TemperatureNP, NULL);
177 /* Image Group */
178 IDDefNumber(&FrameNP, NULL);
179 IDDefNumber(&BinningNP, NULL);
183 void SBIGCam::ISNewSwitch (const char */*dev*/, const char *name, ISState *states, char *names[], int n)
186 /* Connection */
187 if (!strcmp (name, PowerSP.name))
189 IUResetSwitches(&PowerSP);
190 IUUpdateSwitches(&PowerSP, states, names, n);
191 connectCCD();
192 return;
195 /* Frame Type */
196 if (!strcmp(FrameTypeSP.name, name))
198 if (checkPowerS(&FrameTypeSP))
199 return;
201 IUResetSwitches(&FrameTypeSP);
202 IUUpdateSwitches(&FrameTypeSP, states, names, n);
203 FrameTypeSP.s = IPS_OK;
204 IDSetSwitch(&FrameTypeSP, NULL);
206 return;
211 void SBIGCam::ISNewText (const char */*dev*/, const char */*name*/, char **/*texts[]*/, char **/*names[]*/, int /*n*/)
216 void SBIGCam::ISNewNumber (const char */*dev*/, const char *name, double values[], char *names[], int n)
218 /* Exposure time */
219 if (!strcmp (ExposeTimeNP.name, name))
221 if (checkPowerN(&ExposeTimeNP))
222 return;
224 if (ExposeTimeNP.s == IPS_BUSY)
226 ExposeTimeNP.s = IPS_IDLE;
227 ExposeTimeN[0].value = 0;
229 IDSetNumber(&ExposeTimeNP, "Exposure cancelled.");
230 IDLog("Exposure Cancelled.\n");
231 return;
234 ExposeTimeNP.s = IPS_IDLE;
236 IUUpdateNumbers(&ExposeTimeNP, values, names, n);
238 IDLog("Exposure Time (ms) is: %g\n", ExposeTimeN[0].value);
240 handleExposure(NULL);
241 return;
244 if (!strcmp(TemperatureNP.name, name))
246 if (checkPowerN(&TemperatureNP))
247 return;
249 TemperatureNP.s = IPS_IDLE;
251 if (values[0] < MIN_CCD_TEMP || values[0] > MAX_CCD_TEMP)
253 IDSetNumber(&TemperatureNP, "Error: valid range of temperature is from %d to %d", MIN_CCD_TEMP, MAX_CCD_TEMP);
254 return;
257 targetTemp = values[0];
259 // JM: Below, tell SBIG to update to temperature to targetTemp
260 // e.g. cam->setTemp(targetTemp);
263 // Now we set property to busy and poll in ISPoll for CCD temp
264 TemperatureNP.s = IPS_BUSY;
266 IDSetNumber(&TemperatureNP, "Setting CCD temperature to %+06.2f C", values[0]);
267 IDLog("Setting CCD temperature to %+06.2f C\n", values[0]);
268 return;
271 // Subframing
272 if (!strcmp(FrameNP.name, name))
274 if (checkPowerN(&FrameNP))
275 return;
277 FrameNP.s = IPS_OK;
278 IUUpdateNumbers(&FrameNP, values, names, n);
280 // JM: Below, we setup the frame size we want to take exposure of
281 // The way it is done depends on the camera API. Example below
282 //cam->m_StartX = (int) FrameN[0].value;
283 //cam->m_StartY = (int) FrameN[1].value;
284 //cam->m_Width = (int) FrameN[2].value;
285 //cam->m_Height = (int) FrameN[3].value;
287 IDSetNumber(&FrameNP, NULL);
289 } /* end FrameNP */
292 // Binning
293 if (!strcmp(BinningNP.name, name))
295 if (checkPowerN(&BinningNP))
296 return;
299 BinningNP.s = IPS_OK;
300 IUUpdateNumbers(&BinningNP, values, names, n);
302 // JM: Below we set the camera binning.
303 // eg:
304 //cam->m_BinX = (int) BinningN[0].value;
305 //cam->m_BinY = (int) BinningN[1].value;
307 IDLog("Binning is: %.0f x %.0f\n", BinningN[0].value, BinningN[1].value);
308 return;
313 void SBIGCam::ISStaticPoll(void *p)
315 if (!((SBIGCam *)p)->isCCDConnected())
317 IEAddTimer (POLLMS, SBIGCam::ISStaticPoll, p);
318 return;
321 ((SBIGCam *) p)->ISPoll();
323 IEAddTimer (POLLMS, SBIGCam::ISStaticPoll, p);
326 void SBIGCam::ISPoll()
328 static int mtc=5;
329 int readStatus=0;
330 double ccdTemp (0);
332 switch (ExposeTimeNP.s)
334 case IPS_IDLE:
335 case IPS_OK:
336 break;
338 case IPS_BUSY:
340 // JM: Here we check the status of the camera (whether it's still capturing an image or has finished that)
341 // ISPoll is called once per second. e.g. below for how we do this for Apogee Cameras
343 readStatus = cam->read_Status();
344 if (readStatus < 0)
346 IDLog("Error in exposure!\n");
347 ExposeTimeNP.s = IPS_IDLE;
348 ExposeTimeN[0].value = 0;
349 IDSetNumber(&ExposeTimeNP, "Error in exposure procedure.");
350 return;
352 else if (readStatus == Camera_Status_ImageReady)
354 ExposeTimeN[0].value = 0;
355 ExposeTimeNP.s = IPS_OK;
356 IDSetNumber(&ExposeTimeNP, "Exposure done, downloading image...");
357 IDLog("Exposure done, downloading image...\n");
358 // grab and save image. Don't forget to call this!
359 grabImage();
360 return;
361 } */
363 ExposeTimeN[0].value --;
364 IDSetNumber(&ExposeTimeNP, NULL);
365 break;
367 case IPS_ALERT:
368 break;
372 switch (TemperatureNP.s)
374 // JM: If we're not setting a new temperature (i.e. state is either ok or idle)
375 // We simply check for the temp every 5 seconds (this is why mtc = 5 and then decremented).
376 case IPS_IDLE:
377 case IPS_OK:
378 mtc--;
380 if (mtc == 0)
382 //TemperatureN[0].value = cam->read_Temperature();
383 IDSetNumber(&TemperatureNP, NULL);
384 mtc = 5;
386 break;
388 // JM: If we're setting a new temperature, we continously check for it here untill we get
389 // a "close enough" value. This close enough value as seen below is the TEMP_THRESHOLD (0.25 C)
390 case IPS_BUSY:
392 // Read in current CCD temp
393 //ccdTemp = cam->read_Temperature();
395 if (fabs(targetTemp - ccdTemp) <= TEMP_THRESHOLD)
396 TemperatureNP.s = IPS_OK;
398 mtc = 1;
399 TemperatureN[0].value = ccdTemp;
400 IDSetNumber(&TemperatureNP, NULL);
401 break;
403 case IPS_ALERT:
404 break;
409 /* Downloads the image from the CCD row by row and store them
410 in a raw file.
411 N.B. No processing is done on the image */
412 void SBIGCam::grabImage()
415 long err;
416 int img_size, fd;
417 char errmsg[1024];
418 char filename[] = "/tmp/fitsXXXXXX";
420 if ((fd = mkstemp(filename)) < 0)
422 IDMessage(mydev, "Error making temporary filename.");
423 IDLog("Error making temporary filename.\n");
424 return;
426 close(fd);
428 // JM: allocate memory for buffer. In most cameras, the bit depth is 16 bit and so we use unsigned short
429 // But change if the bit depth is different
430 img_size = SBIGFrame.width * SBIGFrame.height * sizeof(unsigned short);
432 SBIGFrame.img = (unsigned short *) malloc (img_size);
434 if (SBIGFrame.img == NULL)
436 IDMessage(mydev, "Not enough memory to store image.");
437 IDLog("Not enough memory to store image.\n");
438 return;
441 // JM: Here we fetch the image from the camera
443 /*if (!cam->GetImage( SBIGFrame.img , SBIGFrame.width, SBIGFrame.height ))
445 free(SBIGFrame.img);
446 IDMessage(mydev, "GetImage() failed.");
447 IDLog("GetImage() failed.");
448 return;
451 err = writeFITS(filename, errmsg);
453 if (err)
455 free(SBIGFrame.img);
456 IDMessage(mydev, errmsg, NULL);
457 return;
460 free(SBIGFrame.img);
464 int SBIGCam::writeFITS(char *filename, char errmsg[])
467 FITS_FILE* ofp;
468 int bpp, bpsl, width, height;
469 long nbytes;
470 FITS_HDU_LIST *hdu;
472 ofp = fits_open (filename, "w");
473 if (!ofp)
475 sprintf(errmsg, "Error: cannot open file for writing.");
476 return (-1);
479 // We're assuming bit depth is 16 (unsigned short). Change as desired.
480 width = SBIGFrame.width;
481 height = SBIGFrame.height;
482 bpp = sizeof(unsigned short); /* Bytes per Pixel */
483 bpsl = bpp * SBIGFrame.width; /* Bytes per Line */
484 nbytes = 0;
486 hdu = create_fits_header (ofp, width, height, bpp);
487 if (hdu == NULL)
489 sprintf(errmsg, "Error: creating FITS header failed.");
490 return (-1);
492 if (fits_write_header (ofp, hdu) < 0)
494 sprintf(errmsg, "Error: writing to FITS header failed.");
495 return (-1);
498 // JM: Here we're performing little endian to big endian (network order) conversion
499 // Since FITS is stored in big endian. If the camera provides the image buffer in big endian
500 // then there is no need for this conversion
501 for (int i=0; i < height; i++)
502 for (int j=0 ; j < width; j++)
503 SBIGFrame.img[width * i + j] = getBigEndian( (SBIGFrame.img[width * i + j]) );
505 // The "2" below is for two bytes (16 bit or unsigned short). Again, change as desired.
506 for (int i= 0; i < height ; i++)
508 fwrite(SBIGFrame.img + (i * width), 2, width, ofp->fp);
509 nbytes += bpsl;
512 nbytes = nbytes % FITS_RECORD_SIZE;
513 if (nbytes)
515 while (nbytes++ < FITS_RECORD_SIZE)
516 putc (0, ofp->fp);
519 if (ferror (ofp->fp))
521 sprintf(errmsg, "Error: write error occured");
522 return (-1);
525 fits_close (ofp);
527 /* Success */
528 ExposeTimeNP.s = IPS_OK;
529 IDSetNumber(&ExposeTimeNP, NULL);
530 IDLog("Loading FITS image...\n");
532 uploadFile(filename);
534 return 0;
538 void SBIGCam::uploadFile(char * filename)
541 FILE * fitsFile;
542 unsigned char *fitsData, *compressedData;
543 int r=0;
544 unsigned int i =0, nr = 0;
545 uLongf compressedBytes=0;
546 uLong totalBytes;
547 struct stat stat_p;
549 if ( -1 == stat (filename, &stat_p))
551 IDLog(" Error occoured attempting to stat %s\n", filename);
552 return;
555 totalBytes = stat_p.st_size;
556 fitsData = (unsigned char *) malloc (sizeof(unsigned char) * totalBytes);
557 compressedData = (unsigned char *) malloc (sizeof(unsigned char) * totalBytes + totalBytes / 64 + 16 + 3);
559 if (fitsData == NULL || compressedData == NULL)
561 IDLog("Error! low memory. Unable to initialize fits buffers.\n");
562 return;
565 fitsFile = fopen(filename, "r");
567 if (fitsFile == NULL)
568 return;
570 /* #1 Read file from disk */
571 for (i=0; i < totalBytes; i+= nr)
573 nr = fread(fitsData + i, 1, totalBytes - i, fitsFile);
575 if (nr <= 0)
577 IDLog("Error reading temporary FITS file.\n");
578 return;
582 compressedBytes = sizeof(char) * totalBytes + totalBytes / 64 + 16 + 3;
584 /* #2 Compress it */
585 r = compress2(compressedData, &compressedBytes, fitsData, totalBytes, 9);
586 if (r != Z_OK)
588 /* this should NEVER happen */
589 IDLog("internal error - compression failed: %d\n", r);
590 return;
593 /* #3 Send it */
594 imageB.blob = compressedData;
595 imageB.bloblen = compressedBytes;
596 imageB.size = totalBytes;
597 strcpy(imageB.format, ".fits.z");
598 imageBP.s = IPS_OK;
599 IDSetBLOB (&imageBP, NULL);
601 free (fitsData);
602 free (compressedData);
606 /* Initiates the exposure procedure */
607 void SBIGCam::handleExposure(void */*p*/)
610 int curFrame = getOnSwitch(&FrameTypeSP);
612 switch (curFrame)
614 /* Light frame */
615 case LIGHT_FRAME:
617 /*if (!cam->Expose( (int) ExposeTimeN[0].value, true ))
619 ExposeTimeNP.s = IPS_IDLE;
620 IDSetNumber(&ExposeTimeNP, "Light Camera exposure failed.");
621 IDLog("Light Camera exposure failed.\n");
622 return;
624 break;
626 /* BIAS frame is the same as DARK but with minimum period. i.e. readout from camera electronics.
628 case BIAS_FRAME:
630 /*if (!cam->Expose( 0.05 , false ))
632 ExposeTimeNP.s = IPS_IDLE;
633 IDSetNumber(&ExposeTimeNP, "Bias Camera exposure failed.");
634 IDLog("Bias Camera exposure failed.\n");
635 return;
637 break;
639 /* Dark frame */
640 case DARK_FRAME:
642 /*if (!cam->Expose( (int) ExposeTimeN[0].value , false ))
644 ExposeTimeNP.s = IPS_IDLE;
645 IDSetNumber(&ExposeTimeNP, "Dark Camera exposure failed.");
646 IDLog("Dark Camera exposure failed.\n");
647 return;
649 break;
651 case FLAT_FRAME:
653 /*if (!cam->Expose( (int) ExposeTimeN[0].value , true ))
655 ExposeTimeNP.s = IPS_IDLE;
656 IDSetNumber(&ExposeTimeNP, "Flat Camera exposure failed.");
657 IDLog("Flat Camera exposure failed.\n");
658 return;
660 break;
663 SBIGFrame.frameType = curFrame;
664 SBIGFrame.width = (int) FrameN[2].value;
665 SBIGFrame.height = (int) FrameN[3].value;
666 SBIGFrame.expose = (int) ExposeTimeN[0].value;
667 SBIGFrame.temperature = TemperatureN[0].value;
668 SBIGFrame.binX = (int) BinningN[0].value;
669 SBIGFrame.binY = (int) BinningN[1].value;
671 ExposeTimeNP.s = IPS_BUSY;
673 IDSetNumber(&ExposeTimeNP, "Taking a %g seconds frame...", ExposeTimeN[0].value);
674 IDLog("Taking a frame...\n");
678 /* Retrieves basic data from the CCD upon connection like temperature, array size, firmware..etc */
679 void SBIGCam::getBasicData()
683 // Maximum resolution
685 // JM: Update here basic variables upon connection.
688 FrameN[2].max = cam->m_NumX;
689 FrameN[3].max = cam->m_NumY;
690 IUUpdateMinMax(&FrameNP);
692 // Maximum Bin
693 BinningN[0].max = cam->m_MaxBinX;
694 BinningN[1].max = cam->m_MaxBinX;
695 IUUpdateMinMax(&BinningNP);
697 // Current Temperature
698 TemperatureN[0].value = cam->read_Temperature();
699 IDSetNumber(&TemperatureNP, NULL);*/
703 int SBIGCam::getOnSwitch(ISwitchVectorProperty *sp)
705 for (int i=0; i < sp->nsp ; i++)
707 if (sp->sp[i].s == ISS_ON)
708 return i;
711 return -1;
714 int SBIGCam::checkPowerS(ISwitchVectorProperty *sp)
716 if (PowerSP.s != IPS_OK)
718 if (!strcmp(sp->label, ""))
719 IDMessage (mydev, "Cannot change property %s while the CCD is offline.", sp->name);
720 else
721 IDMessage (mydev, "Cannot change property %s while the CCD is offline.", sp->label);
723 sp->s = IPS_IDLE;
724 IDSetSwitch(sp, NULL);
725 return -1;
728 return 0;
731 int SBIGCam::checkPowerN(INumberVectorProperty *np)
733 if (PowerSP.s != IPS_OK)
735 if (!strcmp(np->label, ""))
736 IDMessage (mydev, "Cannot change property %s while the CCD is offline.", np->name);
737 else
738 IDMessage (mydev, "Cannot change property %s while the CCD is offline.", np->label);
740 np->s = IPS_IDLE;
741 IDSetNumber(np, NULL);
742 return -1;
745 return 0;
748 int SBIGCam::checkPowerT(ITextVectorProperty *tp)
751 if (PowerSP.s != IPS_OK)
753 if (!strcmp(tp->label, ""))
754 IDMessage (mydev, "Cannot change property %s while the CCD is offline.", tp->name);
755 else
756 IDMessage (mydev, "Cannot change property %s while the CCD is offline.", tp->label);
758 tp->s = IPS_IDLE;
759 IDSetText(tp, NULL);
760 return -1;
763 return 0;
767 void SBIGCam::connectCCD()
770 switch (PowerS[0].s)
772 case ISS_ON:
773 if (initCamera())
775 /* Sucess! */
776 PowerS[0].s = ISS_ON;
777 PowerS[1].s = ISS_OFF;
778 PowerSP.s = IPS_OK;
779 IDSetSwitch(&PowerSP, "CCD is online. Retrieving basic data.");
780 IDLog("CCD is online. Retrieving basic data.\n");
781 getBasicData();
784 else
786 PowerSP.s = IPS_IDLE;
787 PowerS[0].s = ISS_OFF;
788 PowerS[1].s = ISS_ON;
789 IDSetSwitch(&PowerSP, "Error: no cameras were detected.");
790 IDLog("Error: no cameras were detected.\n");
791 return;
794 break;
796 case ISS_OFF:
797 PowerS[0].s = ISS_OFF;
798 PowerS[1].s = ISS_ON;
799 PowerSP.s = IPS_IDLE;
800 IDSetSwitch(&PowerSP, "CCD is offline.");
802 // JM: Disconnect cam and clean up
803 // e.g. disConnectSBIG();
804 break;
809 bool SBIGCam::initCamera()
811 // JM: Here, we do all camera initilization stuff and we establish a connection to the camera
812 // If everything goes well, we return true, otherwise false
813 // Please use IDLog(...) to report errors.
815 return false;
818 /* isCCDConnected: return 1 if we have a connection, 0 otherwise */
819 int SBIGCam::isCCDConnected(void)
821 return ((PowerS[0].s == ISS_ON) ? 1 : 0);
824 FITS_HDU_LIST * SBIGCam::create_fits_header (FITS_FILE *ofp, uint width, uint height, uint bpp)
827 FITS_HDU_LIST *hdulist;
829 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];
830 char obsDate[FITS_CARD_SIZE];
832 snprintf(obsDate, FITS_CARD_SIZE, "DATE-OBS= '%s' /Observation Date UTC", timestamp());
834 hdulist = fits_add_hdu (ofp);
835 if (hdulist == NULL) return (NULL);
837 hdulist->used.simple = 1;
838 hdulist->bitpix = 16;
839 hdulist->naxis = 2;
840 hdulist->naxisn[0] = width;
841 hdulist->naxisn[1] = height;
842 hdulist->naxisn[2] = bpp;
843 // JM: Record here the minimum and maximum pixel values
844 /*hdulist->used.datamin = min();
845 hdulist->datamin = min();
846 hdulist->used.datamax = max();
847 hdulist->datamax = max();*/
848 hdulist->used.bzero = 1;
849 hdulist->bzero = 0.0;
850 hdulist->used.bscale = 1;
851 hdulist->bscale = 1.0;
853 snprintf(temp_s, FITS_CARD_SIZE, "CCD-TEMP= %g / degrees celcius", SBIGFrame.temperature);
854 snprintf(expose_s, FITS_CARD_SIZE, "EXPOSURE= %d / milliseconds", SBIGFrame.expose);
855 snprintf(binning_s, FITS_CARD_SIZE, "BINNING = '(%d x %d)'", SBIGFrame.binX, SBIGFrame.binY);
856 //sprintf(pixel_s, "PIX-SIZ = '%.0f microns square'", PixelSizeN[0].value);
857 switch (SBIGFrame.frameType)
859 case LIGHT_FRAME:
860 strcpy(frame_s, "FRAME = 'Light'");
861 break;
862 case BIAS_FRAME:
863 strcpy(frame_s, "FRAME = 'Bias'");
864 break;
865 case FLAT_FRAME:
866 strcpy(frame_s, "FRAME = 'Flat Field'");
867 break;
868 case DARK_FRAME:
869 strcpy(frame_s, "FRAME = 'Dark'");
870 break;
873 fits_add_card (hdulist, frame_s);
874 fits_add_card (hdulist, temp_s);
875 fits_add_card (hdulist, expose_s);
876 //fits_add_card (hdulist, pixel_s);
878 // JM: If there is a way to get the specific model of the camera, use that.
879 fits_add_card (hdulist, "INSTRUME= 'SBIG CCD'");
880 fits_add_card (hdulist, obsDate);
882 return (hdulist);