00001 #if 0
00002 INDI driver for SBIG CCD
00003 Copyright (C) 2005 Chris Curran (ccurran AT planetcurran DOT com)
00004
00005 Based on Apogee PPI driver by Jasem Mutlaq (mutlaqja AT ikarustech DOT com)
00006
00007 This library is free software; you can redistribute it and/or
00008 modify it under the terms of the GNU Lesser General Public
00009 License as published by the Free Software Foundation; either
00010 version 2.1 of the License, or (at your option) any later version.
00011
00012 This library is distributed in the hope that it will be useful,
00013 but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00015 Lesser General Public License for more details.
00016
00017 You should have received a copy of the GNU Lesser General Public
00018 License along with this library; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00020
00021 #endif
00022
00023 #include <ctype.h>
00024 #include <sys/stat.h>
00025 #include <sys/time.h>
00026 #include <sys/types.h>
00027 #include <sys/socket.h>
00028 #include <netinet/in.h>
00029 #include <netdb.h>
00030 #include <zlib.h>
00031
00032 #include "sbigccd.h"
00033 #include "lilxml.h"
00034 #include "base64.h"
00035
00036 extern char* me;
00037 SBIGCam *MainCam = NULL;
00038
00039
00040 void ISInit()
00041 {
00042 if (MainCam == NULL)
00043 MainCam = new SBIGCam();
00044 }
00045
00046 void ISGetProperties (const char *dev)
00047 {
00048 if (dev && strcmp (mydev, dev))
00049 return;
00050
00051 ISInit();
00052
00053 MainCam->ISGetProperties(dev);
00054 }
00055
00056
00057 void ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n)
00058 {
00059
00060
00061 if (dev && strcmp (dev, mydev))
00062 return;
00063
00064 ISInit();
00065
00066 MainCam->ISNewSwitch(dev, name, states, names, n);
00067 }
00068
00069 void ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n)
00070 {
00071
00072 if (dev && strcmp (mydev, dev))
00073 return;
00074
00075 ISInit();
00076
00077 MainCam->ISNewText(dev, name, texts, names, n);
00078 }
00079
00080
00081 void ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n)
00082 {
00083
00084
00085 if (dev && strcmp (dev, mydev))
00086 return;
00087
00088 ISInit();
00089
00090 MainCam->ISNewNumber(dev, name, values, names, n);
00091 }
00092
00093 void ISNewBLOB (const char *, const char *, int *, char **, char **, char **, int )
00094 {
00095
00096
00097
00098 }
00099
00100 SBIGCam::SBIGCam()
00101 {
00102 initProperties();
00103 IEAddTimer (POLLMS, SBIGCam::ISStaticPoll, this);
00104 }
00105
00106 SBIGCam::~SBIGCam()
00107 {
00108
00109 }
00110
00111 void SBIGCam::initProperties()
00112 {
00113 fillSwitch(&PowerS[0], "CONNECT", "Connect", ISS_OFF);
00114 fillSwitch(&PowerS[1], "DISCONNECT", "Disconnect", ISS_ON);
00115 fillSwitchVector(&PowerSP, PowerS, NARRAY(PowerS), mydev, "CONNECTION", "Connection", COMM_GROUP, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
00116
00117 fillSwitch(&FrameTypeS[0], "FRAME_LIGHT", "Light", ISS_ON);
00118 fillSwitch(&FrameTypeS[1], "FRAME_BIAS", "Bias", ISS_OFF);
00119 fillSwitch(&FrameTypeS[2], "FRAME_DARK", "Dark", ISS_OFF);
00120 fillSwitch(&FrameTypeS[3], "FRAME_FLAT", "Flat Field", ISS_OFF);
00121 fillSwitchVector(&FrameTypeSP, FrameTypeS, NARRAY(FrameTypeS), mydev, "CCD_FRAME_TYPE", "Frame Type", EXPOSE_GROUP, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
00122
00123 fillNumber(&FrameN[0], "X", "X", "%.0f", 0., MAX_PIXELS, 1., 0.);
00124 fillNumber(&FrameN[1], "Y", "Y", "%.0f", 0., MAX_PIXELS, 1., 0.);
00125 fillNumber(&FrameN[2], "WIDTH", "Width", "%.0f", 0., MAX_PIXELS, 1., 0.);
00126 fillNumber(&FrameN[3], "HEIGHT", "Height", "%.0f", 0., MAX_PIXELS, 1., 0.);
00127 fillNumberVector(&FrameNP, FrameN, NARRAY(FrameN), mydev, "CCD_FRAME", "Frame", IMAGE_GROUP, IP_RW, 60, IPS_IDLE);
00128
00129 fillNumber(&BinningN[0], "HOR_BIN", "X", "%0.f", 1., MAXHBIN, 1., 1.);
00130 fillNumber(&BinningN[1], "VER_BIN", "Y", "%0.f", 1., MAXVBIN, 1., 1.);
00131 fillNumberVector(&BinningNP, BinningN, NARRAY(BinningN), mydev, "CCD_BINNING", "Binning", IMAGE_GROUP, IP_RW, 60, IPS_IDLE);
00132
00133 fillNumber(&ExposeTimeN[0], "EXPOSE_DURATION", "Duration (s)", "%5.2f", 0., 36000., 0.5, 1.);
00134 fillNumberVector(&ExposeTimeNP, ExposeTimeN, NARRAY(ExposeTimeN), mydev, "CCD_EXPOSE_DURATION", "Expose", EXPOSE_GROUP, IP_RW, 60, IPS_IDLE);
00135
00136 fillNumber(&TemperatureN[0], "TEMPERATURE", "Temperature", "%+06.2f", MIN_CCD_TEMP, MAX_CCD_TEMP, 0.2, 0.);
00137 fillNumberVector(&TemperatureNP, TemperatureN, NARRAY(TemperatureN), mydev, "CCD_TEMPERATURE", "Expose", EXPOSE_GROUP, IP_RW, 60, IPS_IDLE);
00138
00139
00140 strcpy(imageB.name, "CCD1");
00141 strcpy(imageB.label, "Feed");
00142 strcpy(imageB.format, "");
00143 imageB.blob = 0;
00144 imageB.bloblen = 0;
00145 imageB.size = 0;
00146 imageB.bvp = 0;
00147 imageB.aux0 = 0;
00148 imageB.aux1 = 0;
00149 imageB.aux2 = 0;
00150
00151 strcpy(imageBP.device, mydev);
00152 strcpy(imageBP.name, "Video");
00153 strcpy(imageBP.label, "Video");
00154 strcpy(imageBP.group, COMM_GROUP);
00155 strcpy(imageBP.timestamp, "");
00156 imageBP.p = IP_RO;
00157 imageBP.timeout = 0;
00158 imageBP.s = IPS_IDLE;
00159 imageBP.bp = &imageB;
00160 imageBP.nbp = 1;
00161 imageBP.aux = 0;
00162
00163 }
00164
00165 void SBIGCam::ISGetProperties(const char *)
00166 {
00167
00168
00169 IDDefSwitch(&PowerSP, NULL);
00170 IDDefBLOB(&imageBP, NULL);
00171
00172
00173 IDDefSwitch(&FrameTypeSP, NULL);
00174 IDDefNumber(&ExposeTimeNP, NULL);
00175 IDDefNumber(&TemperatureNP, NULL);
00176
00177
00178 IDDefNumber(&FrameNP, NULL);
00179 IDDefNumber(&BinningNP, NULL);
00180
00181 }
00182
00183 void SBIGCam::ISNewSwitch (const char *, const char *name, ISState *states, char *names[], int n)
00184 {
00185
00186
00187 if (!strcmp (name, PowerSP.name))
00188 {
00189 IUResetSwitches(&PowerSP);
00190 IUUpdateSwitches(&PowerSP, states, names, n);
00191 connectCCD();
00192 return;
00193 }
00194
00195
00196 if (!strcmp(FrameTypeSP.name, name))
00197 {
00198 if (checkPowerS(&FrameTypeSP))
00199 return;
00200
00201 IUResetSwitches(&FrameTypeSP);
00202 IUUpdateSwitches(&FrameTypeSP, states, names, n);
00203 FrameTypeSP.s = IPS_OK;
00204 IDSetSwitch(&FrameTypeSP, NULL);
00205
00206 return;
00207 }
00208
00209 }
00210
00211 void SBIGCam::ISNewText (const char *, const char *, char **, char **, int )
00212 {
00213
00214 }
00215
00216 void SBIGCam::ISNewNumber (const char *, const char *name, double values[], char *names[], int n)
00217 {
00218
00219 if (!strcmp (ExposeTimeNP.name, name))
00220 {
00221 if (checkPowerN(&ExposeTimeNP))
00222 return;
00223
00224 if (ExposeTimeNP.s == IPS_BUSY)
00225 {
00226 ExposeTimeNP.s = IPS_IDLE;
00227 ExposeTimeN[0].value = 0;
00228
00229 IDSetNumber(&ExposeTimeNP, "Exposure cancelled.");
00230 IDLog("Exposure Cancelled.\n");
00231 return;
00232 }
00233
00234 ExposeTimeNP.s = IPS_IDLE;
00235
00236 IUUpdateNumbers(&ExposeTimeNP, values, names, n);
00237
00238 IDLog("Exposure Time (ms) is: %g\n", ExposeTimeN[0].value);
00239
00240 handleExposure(NULL);
00241 return;
00242 }
00243
00244 if (!strcmp(TemperatureNP.name, name))
00245 {
00246 if (checkPowerN(&TemperatureNP))
00247 return;
00248
00249 TemperatureNP.s = IPS_IDLE;
00250
00251 if (values[0] < MIN_CCD_TEMP || values[0] > MAX_CCD_TEMP)
00252 {
00253 IDSetNumber(&TemperatureNP, "Error: valid range of temperature is from %d to %d", MIN_CCD_TEMP, MAX_CCD_TEMP);
00254 return;
00255 }
00256
00257 targetTemp = values[0];
00258
00259
00260
00261
00262
00263
00264 TemperatureNP.s = IPS_BUSY;
00265
00266 IDSetNumber(&TemperatureNP, "Setting CCD temperature to %+06.2f C", values[0]);
00267 IDLog("Setting CCD temperature to %+06.2f C\n", values[0]);
00268 return;
00269 }
00270
00271
00272 if (!strcmp(FrameNP.name, name))
00273 {
00274 if (checkPowerN(&FrameNP))
00275 return;
00276
00277 FrameNP.s = IPS_OK;
00278 IUUpdateNumbers(&FrameNP, values, names, n);
00279
00280
00281
00282
00283
00284
00285
00286
00287 IDSetNumber(&FrameNP, NULL);
00288
00289 }
00290
00291
00292
00293 if (!strcmp(BinningNP.name, name))
00294 {
00295 if (checkPowerN(&BinningNP))
00296 return;
00297
00298
00299 BinningNP.s = IPS_OK;
00300 IUUpdateNumbers(&BinningNP, values, names, n);
00301
00302
00303
00304
00305
00306
00307 IDLog("Binning is: %.0f x %.0f\n", BinningN[0].value, BinningN[1].value);
00308 return;
00309 }
00310 }
00311
00312
00313 void SBIGCam::ISStaticPoll(void *p)
00314 {
00315 if (!((SBIGCam *)p)->isCCDConnected())
00316 {
00317 IEAddTimer (POLLMS, SBIGCam::ISStaticPoll, p);
00318 return;
00319 }
00320
00321 ((SBIGCam *) p)->ISPoll();
00322
00323 IEAddTimer (POLLMS, SBIGCam::ISStaticPoll, p);
00324 }
00325
00326 void SBIGCam::ISPoll()
00327 {
00328 static int mtc=5;
00329 int readStatus=0;
00330 double ccdTemp (0);
00331
00332 switch (ExposeTimeNP.s)
00333 {
00334 case IPS_IDLE:
00335 case IPS_OK:
00336 break;
00337
00338 case IPS_BUSY:
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363 ExposeTimeN[0].value --;
00364 IDSetNumber(&ExposeTimeNP, NULL);
00365 break;
00366
00367 case IPS_ALERT:
00368 break;
00369 }
00370
00371
00372 switch (TemperatureNP.s)
00373 {
00374
00375
00376 case IPS_IDLE:
00377 case IPS_OK:
00378 mtc--;
00379
00380 if (mtc == 0)
00381 {
00382
00383 IDSetNumber(&TemperatureNP, NULL);
00384 mtc = 5;
00385 }
00386 break;
00387
00388
00389
00390 case IPS_BUSY:
00391
00392
00393
00394
00395 if (fabs(targetTemp - ccdTemp) <= TEMP_THRESHOLD)
00396 TemperatureNP.s = IPS_OK;
00397
00398 mtc = 1;
00399 TemperatureN[0].value = ccdTemp;
00400 IDSetNumber(&TemperatureNP, NULL);
00401 break;
00402
00403 case IPS_ALERT:
00404 break;
00405 }
00406
00407 }
00408
00409
00410
00411
00412 void SBIGCam::grabImage()
00413 {
00414
00415 long err;
00416 int img_size, fd;
00417 char errmsg[1024];
00418 char filename[] = "/tmp/fitsXXXXXX";
00419
00420 if ((fd = mkstemp(filename)) < 0)
00421 {
00422 IDMessage(mydev, "Error making temporary filename.");
00423 IDLog("Error making temporary filename.\n");
00424 return;
00425 }
00426 close(fd);
00427
00428
00429
00430 img_size = SBIGFrame.width * SBIGFrame.height * sizeof(unsigned short);
00431
00432 SBIGFrame.img = (unsigned short *) malloc (img_size);
00433
00434 if (SBIGFrame.img == NULL)
00435 {
00436 IDMessage(mydev, "Not enough memory to store image.");
00437 IDLog("Not enough memory to store image.\n");
00438 return;
00439 }
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451 err = writeFITS(filename, errmsg);
00452
00453 if (err)
00454 {
00455 free(SBIGFrame.img);
00456 IDMessage(mydev, errmsg, NULL);
00457 return;
00458 }
00459
00460 free(SBIGFrame.img);
00461
00462 }
00463
00464 int SBIGCam::writeFITS(char *filename, char errmsg[])
00465 {
00466
00467 FITS_FILE* ofp;
00468 int bpp, bpsl, width, height;
00469 long nbytes;
00470 FITS_HDU_LIST *hdu;
00471
00472 ofp = fits_open (filename, "w");
00473 if (!ofp)
00474 {
00475 sprintf(errmsg, "Error: cannot open file for writing.");
00476 return (-1);
00477 }
00478
00479
00480 width = SBIGFrame.width;
00481 height = SBIGFrame.height;
00482 bpp = sizeof(unsigned short);
00483 bpsl = bpp * SBIGFrame.width;
00484 nbytes = 0;
00485
00486 hdu = create_fits_header (ofp, width, height, bpp);
00487 if (hdu == NULL)
00488 {
00489 sprintf(errmsg, "Error: creating FITS header failed.");
00490 return (-1);
00491 }
00492 if (fits_write_header (ofp, hdu) < 0)
00493 {
00494 sprintf(errmsg, "Error: writing to FITS header failed.");
00495 return (-1);
00496 }
00497
00498
00499
00500
00501 for (int i=0; i < height; i++)
00502 for (int j=0 ; j < width; j++)
00503 SBIGFrame.img[width * i + j] = getBigEndian( (SBIGFrame.img[width * i + j]) );
00504
00505
00506 for (int i= 0; i < height ; i++)
00507 {
00508 fwrite(SBIGFrame.img + (i * width), 2, width, ofp->fp);
00509 nbytes += bpsl;
00510 }
00511
00512 nbytes = nbytes % FITS_RECORD_SIZE;
00513 if (nbytes)
00514 {
00515 while (nbytes++ < FITS_RECORD_SIZE)
00516 putc (0, ofp->fp);
00517 }
00518
00519 if (ferror (ofp->fp))
00520 {
00521 sprintf(errmsg, "Error: write error occured");
00522 return (-1);
00523 }
00524
00525 fits_close (ofp);
00526
00527
00528 ExposeTimeNP.s = IPS_OK;
00529 IDSetNumber(&ExposeTimeNP, NULL);
00530 IDLog("Loading FITS image...\n");
00531
00532 uploadFile(filename);
00533
00534 return 0;
00535
00536 }
00537
00538 void SBIGCam::uploadFile(char * filename)
00539 {
00540
00541 FILE * fitsFile;
00542 unsigned char *fitsData, *compressedData;
00543 int r=0;
00544 unsigned int i =0, nr = 0;
00545 uLongf compressedBytes=0;
00546 uLong totalBytes;
00547 struct stat stat_p;
00548
00549 if ( -1 == stat (filename, &stat_p))
00550 {
00551 IDLog(" Error occoured attempting to stat %s\n", filename);
00552 return;
00553 }
00554
00555 totalBytes = stat_p.st_size;
00556 fitsData = (unsigned char *) malloc (sizeof(unsigned char) * totalBytes);
00557 compressedData = (unsigned char *) malloc (sizeof(unsigned char) * totalBytes + totalBytes / 64 + 16 + 3);
00558
00559 if (fitsData == NULL || compressedData == NULL)
00560 {
00561 IDLog("Error! low memory. Unable to initialize fits buffers.\n");
00562 return;
00563 }
00564
00565 fitsFile = fopen(filename, "r");
00566
00567 if (fitsFile == NULL)
00568 return;
00569
00570
00571 for (i=0; i < totalBytes; i+= nr)
00572 {
00573 nr = fread(fitsData + i, 1, totalBytes - i, fitsFile);
00574
00575 if (nr <= 0)
00576 {
00577 IDLog("Error reading temporary FITS file.\n");
00578 return;
00579 }
00580 }
00581
00582 compressedBytes = sizeof(char) * totalBytes + totalBytes / 64 + 16 + 3;
00583
00584
00585 r = compress2(compressedData, &compressedBytes, fitsData, totalBytes, 9);
00586 if (r != Z_OK)
00587 {
00588
00589 IDLog("internal error - compression failed: %d\n", r);
00590 return;
00591 }
00592
00593
00594 imageB.blob = compressedData;
00595 imageB.bloblen = compressedBytes;
00596 imageB.size = totalBytes;
00597 strcpy(imageB.format, ".fits.z");
00598 imageBP.s = IPS_OK;
00599 IDSetBLOB (&imageBP, NULL);
00600
00601 free (fitsData);
00602 free (compressedData);
00603
00604 }
00605
00606
00607 void SBIGCam::handleExposure(void *)
00608 {
00609
00610 int curFrame = getOnSwitch(&FrameTypeSP);
00611
00612 switch (curFrame)
00613 {
00614
00615 case LIGHT_FRAME:
00616
00617
00618
00619
00620
00621
00622
00623
00624 break;
00625
00626
00627
00628 case BIAS_FRAME:
00629
00630
00631
00632
00633
00634
00635
00636
00637 break;
00638
00639
00640 case DARK_FRAME:
00641
00642
00643
00644
00645
00646
00647
00648
00649 break;
00650
00651 case FLAT_FRAME:
00652
00653
00654
00655
00656
00657
00658
00659
00660 break;
00661 }
00662
00663 SBIGFrame.frameType = curFrame;
00664 SBIGFrame.width = (int) FrameN[2].value;
00665 SBIGFrame.height = (int) FrameN[3].value;
00666 SBIGFrame.expose = (int) ExposeTimeN[0].value;
00667 SBIGFrame.temperature = TemperatureN[0].value;
00668 SBIGFrame.binX = (int) BinningN[0].value;
00669 SBIGFrame.binY = (int) BinningN[1].value;
00670
00671 ExposeTimeNP.s = IPS_BUSY;
00672
00673 IDSetNumber(&ExposeTimeNP, "Taking a %g seconds frame...", ExposeTimeN[0].value);
00674 IDLog("Taking a frame...\n");
00675
00676 }
00677
00678
00679 void SBIGCam::getBasicData()
00680 {
00681
00682
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699
00700
00701 }
00702
00703 int SBIGCam::getOnSwitch(ISwitchVectorProperty *sp)
00704 {
00705 for (int i=0; i < sp->nsp ; i++)
00706 {
00707 if (sp->sp[i].s == ISS_ON)
00708 return i;
00709 }
00710
00711 return -1;
00712 }
00713
00714 int SBIGCam::checkPowerS(ISwitchVectorProperty *sp)
00715 {
00716 if (PowerSP.s != IPS_OK)
00717 {
00718 if (!strcmp(sp->label, ""))
00719 IDMessage (mydev, "Cannot change property %s while the CCD is offline.", sp->name);
00720 else
00721 IDMessage (mydev, "Cannot change property %s while the CCD is offline.", sp->label);
00722
00723 sp->s = IPS_IDLE;
00724 IDSetSwitch(sp, NULL);
00725 return -1;
00726 }
00727
00728 return 0;
00729 }
00730
00731 int SBIGCam::checkPowerN(INumberVectorProperty *np)
00732 {
00733 if (PowerSP.s != IPS_OK)
00734 {
00735 if (!strcmp(np->label, ""))
00736 IDMessage (mydev, "Cannot change property %s while the CCD is offline.", np->name);
00737 else
00738 IDMessage (mydev, "Cannot change property %s while the CCD is offline.", np->label);
00739
00740 np->s = IPS_IDLE;
00741 IDSetNumber(np, NULL);
00742 return -1;
00743 }
00744
00745 return 0;
00746 }
00747
00748 int SBIGCam::checkPowerT(ITextVectorProperty *tp)
00749 {
00750
00751 if (PowerSP.s != IPS_OK)
00752 {
00753 if (!strcmp(tp->label, ""))
00754 IDMessage (mydev, "Cannot change property %s while the CCD is offline.", tp->name);
00755 else
00756 IDMessage (mydev, "Cannot change property %s while the CCD is offline.", tp->label);
00757
00758 tp->s = IPS_IDLE;
00759 IDSetText(tp, NULL);
00760 return -1;
00761 }
00762
00763 return 0;
00764
00765 }
00766
00767 void SBIGCam::connectCCD()
00768 {
00769
00770 switch (PowerS[0].s)
00771 {
00772 case ISS_ON:
00773 if (initCamera())
00774 {
00775
00776 PowerS[0].s = ISS_ON;
00777 PowerS[1].s = ISS_OFF;
00778 PowerSP.s = IPS_OK;
00779 IDSetSwitch(&PowerSP, "CCD is online. Retrieving basic data.");
00780 IDLog("CCD is online. Retrieving basic data.\n");
00781 getBasicData();
00782
00783 }
00784 else
00785 {
00786 PowerSP.s = IPS_IDLE;
00787 PowerS[0].s = ISS_OFF;
00788 PowerS[1].s = ISS_ON;
00789 IDSetSwitch(&PowerSP, "Error: no cameras were detected.");
00790 IDLog("Error: no cameras were detected.\n");
00791 return;
00792 }
00793
00794 break;
00795
00796 case ISS_OFF:
00797 PowerS[0].s = ISS_OFF;
00798 PowerS[1].s = ISS_ON;
00799 PowerSP.s = IPS_IDLE;
00800 IDSetSwitch(&PowerSP, "CCD is offline.");
00801
00802
00803
00804 break;
00805 }
00806
00807 }
00808
00809 bool SBIGCam::initCamera()
00810 {
00811
00812
00813
00814
00815 return false;
00816 }
00817
00818
00819 int SBIGCam::isCCDConnected(void)
00820 {
00821 return ((PowerS[0].s == ISS_ON) ? 1 : 0);
00822 }
00823
00824 FITS_HDU_LIST * SBIGCam::create_fits_header (FITS_FILE *ofp, uint width, uint height, uint bpp)
00825 {
00826
00827 FITS_HDU_LIST *hdulist;
00828
00829 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];
00830 char obsDate[FITS_CARD_SIZE];
00831
00832 snprintf(obsDate, FITS_CARD_SIZE, "DATE-OBS= '%s' /Observation Date UTC", timestamp());
00833
00834 hdulist = fits_add_hdu (ofp);
00835 if (hdulist == NULL) return (NULL);
00836
00837 hdulist->used.simple = 1;
00838 hdulist->bitpix = 16;
00839 hdulist->naxis = 2;
00840 hdulist->naxisn[0] = width;
00841 hdulist->naxisn[1] = height;
00842 hdulist->naxisn[2] = bpp;
00843
00844
00845
00846
00847
00848 hdulist->used.bzero = 1;
00849 hdulist->bzero = 0.0;
00850 hdulist->used.bscale = 1;
00851 hdulist->bscale = 1.0;
00852
00853 snprintf(temp_s, FITS_CARD_SIZE, "CCD-TEMP= %g / degrees celcius", SBIGFrame.temperature);
00854 snprintf(expose_s, FITS_CARD_SIZE, "EXPOSURE= %d / milliseconds", SBIGFrame.expose);
00855 snprintf(binning_s, FITS_CARD_SIZE, "BINNING = '(%d x %d)'", SBIGFrame.binX, SBIGFrame.binY);
00856
00857 switch (SBIGFrame.frameType)
00858 {
00859 case LIGHT_FRAME:
00860 strcpy(frame_s, "FRAME = 'Light'");
00861 break;
00862 case BIAS_FRAME:
00863 strcpy(frame_s, "FRAME = 'Bias'");
00864 break;
00865 case FLAT_FRAME:
00866 strcpy(frame_s, "FRAME = 'Flat Field'");
00867 break;
00868 case DARK_FRAME:
00869 strcpy(frame_s, "FRAME = 'Dark'");
00870 break;
00871 }
00872
00873 fits_add_card (hdulist, frame_s);
00874 fits_add_card (hdulist, temp_s);
00875 fits_add_card (hdulist, expose_s);
00876
00877
00878
00879 fits_add_card (hdulist, "INSTRUME= 'SBIG CCD'");
00880 fits_add_card (hdulist, obsDate);
00881
00882 return (hdulist);
00883 }
00884