00001 #if 0
00002 FLI CCD
00003 INDI Interface for Apogee PPI
00004 Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com)
00005
00006 This library is free software; you can redistribute it and/or
00007 modify it under the terms of the GNU Lesser General Public
00008 License as published by the Free Software Foundation; either
00009 version 2.1 of the License, or (at your option) any later version.
00010
00011 This library is distributed in the hope that it will be useful,
00012 but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
00014 Lesser General Public License for more details.
00015
00016 You should have received a copy of the GNU Lesser General Public
00017 License along with this library; if not, write to the Free Software
00018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00019
00020 #endif
00021
00022 #include <ctype.h>
00023 #include <sys/stat.h>
00024 #include <sys/time.h>
00025 #include <sys/types.h>
00026 #include <sys/socket.h>
00027 #include <netinet/in.h>
00028 #include <netdb.h>
00029 #include <zlib.h>
00030
00031 #include "apogee_ppi.h"
00032 #include "lilxml.h"
00033 #include "base64.h"
00034
00035 static void ISPoll(void *);
00036
00037 extern char* me;
00038 ApogeeCam *MainCam = NULL;
00039
00040
00041 void ISInit()
00042 {
00043 if (MainCam == NULL)
00044 {
00045 MainCam = new ApogeeCam();
00046 IEAddTimer (POLLMS, ISPoll, NULL);
00047 }
00048 }
00049
00050 void ISGetProperties (const char *dev)
00051 {
00052 if (dev && strcmp (mydev, dev))
00053 return;
00054
00055 ISInit();
00056
00057 MainCam->ISGetProperties(dev);
00058 }
00059
00060
00061 void ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n)
00062 {
00063
00064
00065 if (dev && strcmp (dev, mydev))
00066 return;
00067
00068 ISInit();
00069
00070 MainCam->ISNewSwitch(dev, name, states, names, n);
00071 }
00072
00073 void ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n)
00074 {
00075
00076 if (dev && strcmp (mydev, dev))
00077 return;
00078
00079 ISInit();
00080
00081 MainCam->ISNewText(dev, name, texts, names, n);
00082 }
00083
00084
00085 void ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n)
00086 {
00087
00088
00089 if (dev && strcmp (dev, mydev))
00090 return;
00091
00092 ISInit();
00093
00094 MainCam->ISNewNumber(dev, name, values, names, n);
00095 }
00096
00097 void ISNewBLOB (const char *, const char *, int *, char **, char **, char **, int )
00098 {
00099
00100 }
00101
00102 void ISPoll(void *)
00103 {
00104 MainCam->ISPoll();
00105 IEAddTimer (POLLMS, ISPoll, NULL);
00106 }
00107
00108
00109 ApogeeCam::ApogeeCam()
00110 {
00111 ApogeeModelS = NULL;
00112
00113 initProperties();
00114
00115 }
00116
00117 ApogeeCam::~ApogeeCam()
00118 {
00119
00120 }
00121
00122 void ApogeeCam::initProperties()
00123 {
00124 fillSwitch(&PowerS[0], "CONNECT", "Connect", ISS_OFF);
00125 fillSwitch(&PowerS[1], "DISCONNECT", "Disconnect", ISS_ON);
00126 fillSwitchVector(&PowerSP, PowerS, NARRAY(PowerS), mydev, "CONNECTION", "Connection", COMM_GROUP, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
00127
00128 fillSwitch(&FrameTypeS[0], "FRAME_LIGHT", "Light", ISS_ON);
00129 fillSwitch(&FrameTypeS[1], "FRAME_BIAS", "Bias", ISS_OFF);
00130 fillSwitch(&FrameTypeS[2], "FRAME_DARK", "Dark", ISS_OFF);
00131 fillSwitch(&FrameTypeS[3], "FRAME_FLAT", "Flat Field", ISS_OFF);
00132 fillSwitchVector(&FrameTypeSP, FrameTypeS, NARRAY(FrameTypeS), mydev, "CCD_FRAME_TYPE", "Frame Type", EXPOSE_GROUP, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
00133
00134 fillNumber(&FrameN[0], "X", "X", "%.0f", 0., MAX_PIXELS, 1., 0.);
00135 fillNumber(&FrameN[1], "Y", "Y", "%.0f", 0., MAX_PIXELS, 1., 0.);
00136 fillNumber(&FrameN[2], "WIDTH", "Width", "%.0f", 0., MAX_PIXELS, 1., 0.);
00137 fillNumber(&FrameN[3], "HEIGHT", "Height", "%.0f", 0., MAX_PIXELS, 1., 0.);
00138 fillNumberVector(&FrameNP, FrameN, NARRAY(FrameN), mydev, "CCD_FRAME", "Frame", IMAGE_GROUP, IP_RW, 60, IPS_IDLE);
00139
00140 fillNumber(&BinningN[0], "HOR_BIN", "X", "%0.f", 1., MAXHBIN, 1., 1.);
00141 fillNumber(&BinningN[1], "VER_BIN", "Y", "%0.f", 1., MAXVBIN, 1., 1.);
00142 fillNumberVector(&BinningNP, BinningN, NARRAY(BinningN), mydev, "CCD_BINNING", "Binning", IMAGE_GROUP, IP_RW, 60, IPS_IDLE);
00143
00144 fillNumber(&ExposeTimeN[0], "DURATION", "Duration (s)", "%5.2f", 0., 36000., 0.5, 1.);
00145 fillNumberVector(&ExposeTimeNP, ExposeTimeN, NARRAY(ExposeTimeN), mydev, "CCD_EXPOSE_DURATION", "Expose", EXPOSE_GROUP, IP_RW, 60, IPS_IDLE);
00146
00147 fillNumber(&TemperatureN[0], "TEMPERATURE", "Temperature", "%+06.2f", MIN_CCD_TEMP, MAX_CCD_TEMP, 0.2, 0.);
00148 fillNumberVector(&TemperatureNP, TemperatureN, NARRAY(TemperatureN), mydev, "CCD_TEMPERATURE", "Expose", EXPOSE_GROUP, IP_RW, 60, IPS_IDLE);
00149
00150 strcpy(imageB.name, "CCD1");
00151 strcpy(imageB.label, "Feed");
00152 strcpy(imageB.format, "");
00153 imageB.blob = 0;
00154 imageB.bloblen = 0;
00155 imageB.size = 0;
00156 imageB.bvp = 0;
00157 imageB.aux0 = 0;
00158 imageB.aux1 = 0;
00159 imageB.aux2 = 0;
00160
00161 strcpy(imageBP.device, mydev);
00162 strcpy(imageBP.name, "Video");
00163 strcpy(imageBP.label, "Video");
00164 strcpy(imageBP.group, COMM_GROUP);
00165 strcpy(imageBP.timestamp, "");
00166 imageBP.p = IP_RO;
00167 imageBP.timeout = 0;
00168 imageBP.s = IPS_IDLE;
00169 imageBP.bp = &imageB;
00170 imageBP.nbp = 1;
00171 imageBP.aux = 0;
00172
00173
00174 }
00175
00176 bool ApogeeCam::loadXMLModel()
00177 {
00178 LilXML *XMLParser = newLilXML();
00179 XMLEle *root = NULL, *camera = NULL;
00180 XMLAtt *modelName;
00181 FILE *modelSpecFile = NULL;
00182 char errmsg[1024];
00183 int ncams = 0;
00184
00185
00186 modelSpecFile = fopen(TOP_DATADIR"/apogee_caminfo.xml", "r");
00187
00188 if (modelSpecFile == NULL)
00189 {
00190 IDLog("Error: Unable to open file apogee_caminfo.xml\n");
00191 IDMessage(mydev, "Error: Unable to open file apogee_caminfo.xml");
00192 return false;
00193 }
00194
00195 root = readXMLFile(modelSpecFile, XMLParser, errmsg);
00196 if (root == NULL)
00197 {
00198 IDLog("Error: Unable to process apogee_caminfo.xml. %s\n", errmsg);
00199 IDMessage(mydev, "Error: Unable to process apogee_caminfo.xml. %s\n", errmsg);
00200 fclose(modelSpecFile);
00201 delLilXML(XMLParser);
00202 return false;
00203 }
00204
00205 for (camera = nextXMLEle (root, 1); camera != NULL; camera = nextXMLEle (root, 0))
00206 {
00207 modelName = findXMLAtt(camera, "model");
00208 if (modelName == NULL)
00209 continue;
00210
00211 ApogeeModelS = (ApogeeModelS == NULL) ? (ISwitch *) malloc (sizeof(ISwitch))
00212 : (ISwitch *) realloc(ApogeeModelS, sizeof(ISwitch) * (ncams + 1));
00213
00214 snprintf(ApogeeModelS[ncams].name, MAXINDINAME, "Model%d", ncams);
00215 strcpy(ApogeeModelS[ncams].label, valuXMLAtt(modelName));
00216 ApogeeModelS[ncams].s = (ncams == 0) ? ISS_ON : ISS_OFF;
00217 ApogeeModelS[ncams].svp = NULL;
00218 ApogeeModelS[ncams].aux = NULL;
00219
00220 ncams++;
00221 }
00222
00223 fclose(modelSpecFile);
00224 delLilXML(XMLParser);
00225
00226 if (ncams > 0)
00227 {
00228 fillSwitchVector(&ApogeeModelSP, ApogeeModelS, ncams, mydev, "Model", "", COMM_GROUP, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
00229 return true;
00230 }
00231
00232 return false;
00233
00234 }
00235
00236 void ApogeeCam::ISGetProperties(const char *)
00237 {
00238
00239
00240
00241 IDDefSwitch(&PowerSP, NULL);
00242 if (loadXMLModel())
00243 IDDefSwitch(&ApogeeModelSP, NULL);
00244 else
00245 IDMessage(mydev, "Error: Unable to read camera specifications. Driver is disabled.");
00246 IDDefBLOB(&imageBP, NULL);
00247
00248
00249 IDDefSwitch(&FrameTypeSP, NULL);
00250 IDDefNumber(&ExposeTimeNP, NULL);
00251 IDDefNumber(&TemperatureNP, NULL);
00252
00253
00254 IDDefNumber(&FrameNP, NULL);
00255 IDDefNumber(&BinningNP, NULL);
00256
00257 IDLog("Apogee Driver Debug Enabled\n");
00258
00259 }
00260
00261 void ApogeeCam::ISNewSwitch (const char *, const char *name, ISState *states, char *names[], int n)
00262 {
00263
00264
00265 if (!strcmp (name, PowerSP.name))
00266 {
00267 IUResetSwitches(&PowerSP);
00268 IUUpdateSwitches(&PowerSP, states, names, n);
00269 connectCCD();
00270 return;
00271 }
00272
00273
00274 if (!strcmp(FrameTypeSP.name, name))
00275 {
00276 if (checkPowerS(&FrameTypeSP))
00277 return;
00278
00279 IUResetSwitches(&FrameTypeSP);
00280 IUUpdateSwitches(&FrameTypeSP, states, names, n);
00281 FrameTypeSP.s = IPS_OK;
00282 IDSetSwitch(&FrameTypeSP, NULL);
00283
00284 return;
00285 }
00286
00287
00288 if (!strcmp(ApogeeModelSP.name, name))
00289 {
00290 IUResetSwitches(&ApogeeModelSP);
00291 IUUpdateSwitches(&ApogeeModelSP, states, names, n);
00292 ApogeeModelSP.s = IPS_OK;
00293 IDSetSwitch(&ApogeeModelSP, NULL);
00294 return;
00295 }
00296
00297 }
00298
00299 void ApogeeCam::ISNewText (const char *, const char *, char **, char **, int )
00300 {
00301
00302 }
00303
00304 void ApogeeCam::ISNewNumber (const char *, const char *name, double values[], char *names[], int n)
00305 {
00306
00307 if (!strcmp (ExposeTimeNP.name, name))
00308 {
00309 if (checkPowerN(&ExposeTimeNP))
00310 return;
00311
00312 if (ExposeTimeNP.s == IPS_BUSY)
00313 {
00314 cam->Reset();
00315 ExposeTimeNP.s = IPS_IDLE;
00316 ExposeTimeN[0].value = 0;
00317
00318 IDSetNumber(&ExposeTimeNP, "Exposure cancelled.");
00319 IDLog("Exposure Cancelled.\n");
00320 return;
00321 }
00322
00323 ExposeTimeNP.s = IPS_IDLE;
00324
00325 IUUpdateNumbers(&ExposeTimeNP, values, names, n);
00326
00327 IDLog("Exposure Time is: %g\n", ExposeTimeN[0].value);
00328
00329 handleExposure(NULL);
00330 return;
00331 }
00332
00333 if (!strcmp(TemperatureNP.name, name))
00334 {
00335 if (checkPowerN(&TemperatureNP))
00336 return;
00337
00338 TemperatureNP.s = IPS_IDLE;
00339
00340 if (values[0] < MIN_CCD_TEMP || values[0] > MAX_CCD_TEMP)
00341 {
00342 IDSetNumber(&TemperatureNP, "Error: valid range of temperature is from %d to %d", MIN_CCD_TEMP, MAX_CCD_TEMP);
00343 return;
00344 }
00345
00346 targetTemp = values[0];
00347 cam->write_CoolerMode(0);
00348 cam->write_CoolerMode(1);
00349 cam->write_CoolerSetPoint(targetTemp);
00350
00351 TemperatureNP.s = IPS_BUSY;
00352
00353 IDSetNumber(&TemperatureNP, "Setting CCD temperature to %+06.2f C", values[0]);
00354 IDLog("Setting CCD temperature to %+06.2f C\n", values[0]);
00355 return;
00356 }
00357
00358 if (!strcmp(FrameNP.name, name))
00359 {
00360 if (checkPowerN(&FrameNP))
00361 return;
00362
00363 FrameNP.s = IPS_OK;
00364 IUUpdateNumbers(&FrameNP, values, names, n);
00365
00366 cam->m_StartX = (int) FrameN[0].value;
00367 cam->m_StartY = (int) FrameN[1].value;
00368 cam->m_NumX = (int) FrameN[2].value;
00369 cam->m_NumY = (int) FrameN[3].value;
00370 IDSetNumber(&FrameNP, NULL);
00371
00372 }
00373
00374
00375 if (!strcmp(BinningNP.name, name))
00376 {
00377 if (checkPowerN(&BinningNP))
00378 return;
00379
00380
00381 BinningNP.s = IPS_OK;
00382 IUUpdateNumbers(&BinningNP, values, names, n);
00383
00384 cam->m_BinX = (int) BinningN[0].value;
00385 cam->m_BinY = (int) BinningN[1].value;
00386
00387 IDLog("Binning is: %.0f x %.0f\n", BinningN[0].value, BinningN[1].value);
00388 return;
00389 }
00390 }
00391
00392
00393 void ApogeeCam::ISPoll()
00394 {
00395 static int mtc=5;
00396 int readStatus=0;
00397 double ccdTemp;
00398
00399 if (!isCCDConnected())
00400 return;
00401
00402 switch (ExposeTimeNP.s)
00403 {
00404 case IPS_IDLE:
00405 case IPS_OK:
00406 break;
00407
00408 case IPS_BUSY:
00409
00410 readStatus = cam->read_Status();
00411 if (readStatus < 0)
00412 {
00413 IDLog("Error in exposure! Read status: %d\n", readStatus);
00414 ExposeTimeNP.s = IPS_ALERT;
00415 ExposeTimeN[0].value = 0;
00416 IDSetNumber(&ExposeTimeNP, "Error in exposure procedure. Read states: %d", readStatus);
00417 return;
00418 }
00419 else if (readStatus == Camera_Status_ImageReady)
00420 {
00421 ExposeTimeN[0].value = 0;
00422 ExposeTimeNP.s = IPS_OK;
00423 IDSetNumber(&ExposeTimeNP, "Exposure done, downloading image...");
00424 IDLog("Exposure done, downloading image...\n");
00425
00426 grabImage();
00427 return;
00428 }
00429
00430 ExposeTimeN[0].value --;
00431 IDSetNumber(&ExposeTimeNP, NULL);
00432 break;
00433
00434 case IPS_ALERT:
00435 break;
00436 }
00437
00438 switch (TemperatureNP.s)
00439 {
00440 case IPS_IDLE:
00441 case IPS_OK:
00442 mtc--;
00443
00444 if (mtc == 0)
00445 {
00446 TemperatureN[0].value = cam->read_Temperature();
00447 IDSetNumber(&TemperatureNP, NULL);
00448 mtc = 5;
00449 }
00450 break;
00451
00452 case IPS_BUSY:
00453
00454 ccdTemp = cam->read_Temperature();
00455
00456 if (fabs(targetTemp - ccdTemp) <= TEMP_THRESHOLD)
00457 TemperatureNP.s = IPS_OK;
00458
00459 mtc = 1;
00460 TemperatureN[0].value = ccdTemp;
00461 IDSetNumber(&TemperatureNP, NULL);
00462 break;
00463
00464 case IPS_ALERT:
00465 break;
00466 }
00467
00468 }
00469
00470
00471
00472
00473 void ApogeeCam::grabImage()
00474 {
00475
00476 long err;
00477 int img_size, fd;
00478 char errmsg[1024];
00479 char filename[] = "/tmp/fitsXXXXXX";
00480
00481 IDLog("In grab Image\n");
00482
00483 if ((fd = mkstemp(filename)) < 0)
00484 {
00485 IDMessage(mydev, "Error making temporary filename.");
00486 IDLog("Error making temporary filename.\n");
00487 return;
00488 }
00489 close(fd);
00490
00491 img_size = APGFrame.width * APGFrame.height * sizeof(unsigned short);
00492
00493 IDLog("Allocating memory buffer. Width: %d - Height: %d\n", APGFrame.width, APGFrame.height);
00494
00495 APGFrame.img = (unsigned short *) malloc (img_size);
00496
00497 if (APGFrame.img == NULL)
00498 {
00499 IDMessage(mydev, "Not enough memory to store image.");
00500 IDLog("Not enough memory to store image.\n");
00501 return;
00502 }
00503
00504 IDLog("Getting frame buffer from camera...\n");
00505 if (!cam->GetImage( APGFrame.img , APGFrame.width, APGFrame.height ))
00506 {
00507 free(APGFrame.img);
00508 IDMessage(mydev, "GetImage() failed.");
00509 IDLog("GetImage() failed.");
00510 return;
00511 }
00512
00513 IDLog("Done with getting frame buffer, writing FITS file\n");
00514
00515 err = writeFITS(filename, errmsg);
00516
00517 if (err)
00518 {
00519 free(APGFrame.img);
00520 IDMessage(mydev, errmsg, NULL);
00521 return;
00522 }
00523
00524 free(APGFrame.img);
00525
00526 IDLog("All good, returning\n");
00527
00528 }
00529
00530 int ApogeeCam::writeFITS(char *filename, char errmsg[])
00531 {
00532
00533 FITS_FILE* ofp;
00534 int bpp, bpsl, width, height;
00535 long nbytes;
00536 FITS_HDU_LIST *hdu;
00537
00538 IDLog("in write FITS, opening filename %s\n", filename);
00539
00540 ofp = fits_open (filename, "w");
00541 if (!ofp)
00542 {
00543 sprintf(errmsg, "Error: cannot open file for writing.");
00544 return (-1);
00545 }
00546
00547 width = APGFrame.width;
00548 height = APGFrame.height;
00549 bpp = sizeof(unsigned short);
00550 bpsl = bpp * APGFrame.width;
00551 nbytes = 0;
00552
00553 IDLog("Creating FITS header\n");
00554 hdu = create_fits_header (ofp, width, height, bpp);
00555 if (hdu == NULL)
00556 {
00557 sprintf(errmsg, "Error: creating FITS header failed.");
00558 return (-1);
00559 }
00560 if (fits_write_header (ofp, hdu) < 0)
00561 {
00562 sprintf(errmsg, "Error: writing to FITS header failed.");
00563 return (-1);
00564 }
00565
00566 IDLog("Converting to BIG Endian\n");
00567 for (int i=0; i < height; i++)
00568 for (int j=0 ; j < width; j++)
00569 APGFrame.img[width * i + j] = getBigEndian( (APGFrame.img[width * i + j]) );
00570
00571 IDLog("Writing frame to disk\n");
00572 for (int i= 0; i < height ; i++)
00573 {
00574 fwrite(APGFrame.img + (i * width), 2, width, ofp->fp);
00575 nbytes += bpsl;
00576 }
00577
00578 IDLog("Calculating nbytes\n");
00579 nbytes = nbytes % FITS_RECORD_SIZE;
00580 if (nbytes)
00581 {
00582 while (nbytes++ < FITS_RECORD_SIZE)
00583 putc (0, ofp->fp);
00584 }
00585
00586 if (ferror (ofp->fp))
00587 {
00588 sprintf(errmsg, "Error: write error occured");
00589 return (-1);
00590 }
00591
00592 IDLog("Closing ofp\n");
00593 fits_close (ofp);
00594
00595
00596 ExposeTimeNP.s = IPS_OK;
00597 IDSetNumber(&ExposeTimeNP, NULL);
00598 IDLog("Loading FITS image...\n");
00599
00600 IDLog("Uploading filename\n");
00601 uploadFile(filename);
00602 IDLog("Uploading done, returning\n");
00603
00604 return 0;
00605
00606 }
00607
00608 void ApogeeCam::uploadFile(char * filename)
00609 {
00610
00611 FILE * fitsFile;
00612 unsigned char *fitsData, *compressedData;
00613 int r=0;
00614 unsigned int i =0, nr = 0;
00615 uLongf compressedBytes=0;
00616 uLong totalBytes;
00617 struct stat stat_p;
00618
00619 IDLog("in upload file, will stat file now\n");
00620
00621 if ( -1 == stat (filename, &stat_p))
00622 {
00623 IDLog(" Error occoured attempting to stat %s\n", filename);
00624 return;
00625 }
00626
00627 totalBytes = stat_p.st_size;
00628 fitsData = (unsigned char *) malloc (sizeof(unsigned char) * totalBytes);
00629 compressedData = (unsigned char *) malloc (sizeof(unsigned char) * totalBytes + totalBytes / 64 + 16 + 3);
00630
00631 if (fitsData == NULL || compressedData == NULL)
00632 {
00633 IDLog("Error! low memory. Unable to initialize fits buffers.\n");
00634 return;
00635 }
00636
00637 IDLog("opening file\n");
00638 fitsFile = fopen(filename, "r");
00639
00640 if (fitsFile == NULL)
00641 return;
00642
00643 IDLog("Reading file from disk\n");
00644
00645 for (i=0; i < totalBytes; i+= nr)
00646 {
00647 nr = fread(fitsData + i, 1, totalBytes - i, fitsFile);
00648
00649 if (nr <= 0)
00650 {
00651 IDLog("Error reading temporary FITS file.\n");
00652 return;
00653 }
00654 }
00655
00656 compressedBytes = sizeof(char) * totalBytes + totalBytes / 64 + 16 + 3;
00657
00658 IDLog("Compressing data\n");
00659
00660 r = compress2(compressedData, &compressedBytes, fitsData, totalBytes, 9);
00661 if (r != Z_OK)
00662 {
00663
00664 IDLog("internal error - compression failed: %d\n", r);
00665 return;
00666 }
00667
00668 IDLog("Sending blob. bloblen %ld - size %ld\n", compressedBytes, totalBytes);
00669
00670
00671 imageB.blob = compressedData;
00672 imageB.bloblen = compressedBytes;
00673 imageB.size = totalBytes;
00674 strcpy(imageB.format, ".fits.z");
00675 imageBP.s = IPS_OK;
00676 IDSetBLOB (&imageBP, NULL);
00677
00678 free (fitsData);
00679 free (compressedData);
00680
00681 }
00682
00683
00684 void ApogeeCam::handleExposure(void *)
00685 {
00686
00687 int curFrame = getOnSwitch(&FrameTypeSP);
00688
00689 switch (curFrame)
00690 {
00691
00692 case LIGHT_FRAME:
00693 if (!cam->Expose( (int) ExposeTimeN[0].value, true ))
00694 {
00695 ExposeTimeNP.s = IPS_IDLE;
00696 IDSetNumber(&ExposeTimeNP, "Light Camera exposure failed.");
00697 IDLog("Light Camera exposure failed.\n");
00698 return;
00699 }
00700 break;
00701
00702
00703
00704 case BIAS_FRAME:
00705 if (!cam->Expose( 0.05 , false ))
00706 {
00707 ExposeTimeNP.s = IPS_IDLE;
00708 IDSetNumber(&ExposeTimeNP, "Bias Camera exposure failed.");
00709 IDLog("Bias Camera exposure failed.\n");
00710 return;
00711 }
00712 break;
00713
00714
00715 case DARK_FRAME:
00716 if (!cam->Expose( (int) ExposeTimeN[0].value , false ))
00717 {
00718 ExposeTimeNP.s = IPS_IDLE;
00719 IDSetNumber(&ExposeTimeNP, "Dark Camera exposure failed.");
00720 IDLog("Dark Camera exposure failed.\n");
00721 return;
00722 }
00723 break;
00724
00725 case FLAT_FRAME:
00726 if (!cam->Expose( (int) ExposeTimeN[0].value , true ))
00727 {
00728 ExposeTimeNP.s = IPS_IDLE;
00729 IDSetNumber(&ExposeTimeNP, "Flat Camera exposure failed.");
00730 IDLog("Flat Camera exposure failed.\n");
00731 return;
00732 }
00733 break;
00734 }
00735
00736 APGFrame.frameType = curFrame;
00737 APGFrame.width = (int) FrameN[2].value;
00738 APGFrame.height = (int) FrameN[3].value;
00739 APGFrame.expose = (int) ExposeTimeN[0].value;
00740 APGFrame.temperature = TemperatureN[0].value;
00741 APGFrame.binX = (int) BinningN[0].value;
00742 APGFrame.binY = (int) BinningN[1].value;
00743
00744 ExposeTimeNP.s = IPS_BUSY;
00745
00746 IDSetNumber(&ExposeTimeNP, "Taking a %g seconds frame...", ExposeTimeN[0].value);
00747 IDLog("Taking a frame. Width: %d - Height: %d - expose %d - temperature %g - binX %d - binY %d\n", APGFrame.width, APGFrame.height, APGFrame.expose, APGFrame.temperature, APGFrame.binX, APGFrame.binY);
00748
00749 }
00750
00751
00752 void ApogeeCam::getBasicData()
00753 {
00754
00755
00756
00757 FrameN[2].max = cam->m_NumX;
00758 FrameN[3].max = cam->m_NumY;
00759 IUUpdateMinMax(&FrameNP);
00760
00761
00762 BinningN[0].max = cam->m_MaxBinX;
00763 BinningN[1].max = cam->m_MaxBinX;
00764 IUUpdateMinMax(&BinningNP);
00765
00766 FrameN[0].value = 0;
00767 FrameN[1].value = 0;
00768 FrameN[2].min = 0;
00769 FrameN[2].max = cam->m_ImgColumns;
00770 FrameN[2].value = cam->m_ImgColumns;
00771 FrameN[3].min = 0;
00772 FrameN[3].max = cam->m_ImgRows;
00773 FrameN[3].value = cam->m_ImgRows;
00774
00775 IUUpdateMinMax(&FrameNP);
00776 IDSetNumber(&FrameNP, NULL);
00777
00778
00779 TemperatureN[0].value = cam->read_Temperature();
00780 IDSetNumber(&TemperatureNP, NULL);
00781
00782 }
00783
00784 int ApogeeCam::getOnSwitch(ISwitchVectorProperty *sp)
00785 {
00786 int i=0;
00787 for (i=0; i < sp->nsp ; i++)
00788 {
00789
00790 if (sp->sp[i].s == ISS_ON)
00791 return i;
00792 }
00793
00794 return -1;
00795 }
00796
00797 int ApogeeCam::checkPowerS(ISwitchVectorProperty *sp)
00798 {
00799 if (PowerSP.s != IPS_OK)
00800 {
00801 if (!strcmp(sp->label, ""))
00802 IDMessage (mydev, "Cannot change property %s while the CCD is offline.", sp->name);
00803 else
00804 IDMessage (mydev, "Cannot change property %s while the CCD is offline.", sp->label);
00805
00806 sp->s = IPS_IDLE;
00807 IDSetSwitch(sp, NULL);
00808 return -1;
00809 }
00810
00811 return 0;
00812 }
00813
00814 int ApogeeCam::checkPowerN(INumberVectorProperty *np)
00815 {
00816 if (PowerSP.s != IPS_OK)
00817 {
00818 if (!strcmp(np->label, ""))
00819 IDMessage (mydev, "Cannot change property %s while the CCD is offline.", np->name);
00820 else
00821 IDMessage (mydev, "Cannot change property %s while the CCD is offline.", np->label);
00822
00823 np->s = IPS_IDLE;
00824 IDSetNumber(np, NULL);
00825 return -1;
00826 }
00827
00828 return 0;
00829 }
00830
00831 int ApogeeCam::checkPowerT(ITextVectorProperty *tp)
00832 {
00833
00834 if (PowerSP.s != IPS_OK)
00835 {
00836 if (!strcmp(tp->label, ""))
00837 IDMessage (mydev, "Cannot change property %s while the CCD is offline.", tp->name);
00838 else
00839 IDMessage (mydev, "Cannot change property %s while the CCD is offline.", tp->label);
00840
00841 tp->s = IPS_IDLE;
00842 IDSetText(tp, NULL);
00843 return -1;
00844 }
00845
00846 return 0;
00847
00848 }
00849
00850 void ApogeeCam::connectCCD()
00851 {
00852
00853
00854 switch (PowerS[0].s)
00855 {
00856 case ISS_ON:
00857 if (initCamera())
00858 {
00859
00860 PowerS[0].s = ISS_ON;
00861 PowerS[1].s = ISS_OFF;
00862 PowerSP.s = IPS_OK;
00863 IDSetSwitch(&PowerSP, "CCD is online. Retrieving basic data.");
00864 IDLog("CCD is online. Retrieving basic data.\n");
00865 getBasicData();
00866
00867 }
00868 else
00869 {
00870 PowerSP.s = IPS_IDLE;
00871 PowerS[0].s = ISS_OFF;
00872 PowerS[1].s = ISS_ON;
00873 IDSetSwitch(&PowerSP, "Error: no cameras were detected.");
00874 IDLog("Error: no cameras were detected.\n");
00875 return;
00876 }
00877
00878 break;
00879
00880 case ISS_OFF:
00881 PowerS[0].s = ISS_OFF;
00882 PowerS[1].s = ISS_ON;
00883 PowerSP.s = IPS_IDLE;
00884 IDSetSwitch(&PowerSP, "CCD is offline.");
00885 break;
00886 }
00887
00888 }
00889
00890 bool ApogeeCam::initCamera()
00891 {
00892 LilXML *XMLParser = newLilXML();
00893 XMLEle *root = NULL, *camera = NULL, *ele = NULL;
00894 XMLEle *system = NULL, *geometry = NULL, *temp = NULL, *ccd = NULL;
00895 XMLAtt *ap;
00896 FILE *spFile = NULL;
00897 char errmsg[1024];
00898
00899 spFile = fopen(TOP_DATADIR"/apogee_caminfo.xml", "r");
00900
00901 if (spFile == NULL)
00902 {
00903 IDLog("Error: Unable to open file apogee_caminfo.xml\n");
00904 IDMessage(mydev, "Error: Unable to open file apogee_caminfo.xml");
00905 return false;
00906 }
00907
00908 root = readXMLFile(spFile, XMLParser, errmsg);
00909 if (root == NULL)
00910 {
00911 IDLog("Error: Unable to process apogee_caminfo.xml. %s\n", errmsg);
00912 IDMessage(mydev, "Error: Unable to process apogee_caminfo.xml. %s\n", errmsg);
00913 fclose(spFile);
00914 delLilXML(XMLParser);
00915 return false;
00916 }
00917
00918 fclose(spFile);
00919
00920
00921 camera = findXMLEle(root, "Apogee_Camera");
00922
00923 if (camera == NULL)
00924 {
00925 IDLog("Error: Unable to find Apogee_Camera element.\n");
00926 IDMessage(mydev, "Error: Unable to find Apogee_Camera element.");
00927 delLilXML(XMLParser);
00928 return false;
00929 }
00930
00931 IDLog("Looking for %s - len %d\n", ApogeeModelS[getOnSwitch(&ApogeeModelSP)].label, strlen(ApogeeModelS[getOnSwitch(&ApogeeModelSP)].label));
00932
00933 ap = findXMLAtt(camera, "model");
00934 if (!ap)
00935 {
00936 IDLog("Error: Unable to find attribute model.\n");
00937 IDMessage(mydev, "Error: Unable to find attribute model.");
00938 return false;
00939 }
00940
00941 if (strcmp(valuXMLAtt(ap), ApogeeModelS[getOnSwitch(&ApogeeModelSP)].label))
00942 {
00943 IDLog("Camera %s not found in XML file\n", ApogeeModelS[getOnSwitch(&ApogeeModelSP)].label);
00944 IDMessage(mydev, "Camera %s not found in XML file\n", ApogeeModelS[getOnSwitch(&ApogeeModelSP)].label);
00945 delLilXML(XMLParser);
00946 return false;
00947 }
00948
00949
00950 system = findXMLEle(camera, "System");
00951 geometry = findXMLEle(camera, "Geometry");
00952 temp = findXMLEle(camera, "Temp");
00953 ccd = findXMLEle(camera, "CCD");
00954
00955 if (system == NULL)
00956 {
00957 IDLog("Error: Unable to find System element in camera.\n");
00958 IDMessage(mydev, "Error: Unable to find System element in camera.");
00959 delLilXML(XMLParser);
00960 return false;
00961 }
00962
00963 if (geometry == NULL)
00964 {
00965 IDLog("Error: Unable to find Geometry element in camera.\n");
00966 IDMessage(mydev, "Error: Unable to find Geometry element in camera.");
00967 delLilXML(XMLParser);
00968 return false;
00969 }
00970
00971 if (temp == NULL)
00972 {
00973 IDLog("Error: Unable to find Temp element in camera.\n");
00974 IDMessage(mydev, "Error: Unable to find Temp element in camera.");
00975 delLilXML(XMLParser);
00976 return false;
00977 }
00978
00979 if (ccd == NULL)
00980 {
00981 IDLog("Error: Unable to find CCD element in camera.\n");
00982 IDMessage(mydev, "Error: Unable to find CCD element in camera.");
00983 delLilXML(XMLParser);
00984 return false;
00985 }
00986
00987 cam = new CCameraIO();
00988
00989 if (cam == NULL)
00990 {
00991 IDLog("Error: Failed to create CCameraIO object.\n");
00992 IDMessage(mydev, "Error: Failed to create CCameraIO object.");
00993 delLilXML(XMLParser);
00994 return false;
00995 }
00996
00997 int bAddr = 0x378;
00998 int val = 0;
00999
01000 bAddr = hextoi(valuXMLAtt(findXMLAtt(system, "Base"))) & 0xFFF;
01001
01002
01003 ap = findXMLAtt(geometry, "Rows");
01004 if (!ap)
01005 {
01006 IDLog("Error: Unable to find attribute Rows.\n");
01007 IDMessage(mydev, "Error: Unable to find attribute Rows.");
01008 delLilXML(XMLParser);
01009 return false;
01010 }
01011
01012 cam->m_Rows = hextoi(valuXMLAtt(ap));
01013
01014
01015 ap = findXMLAtt(geometry, "Columns");
01016 if (!ap)
01017 {
01018 IDLog("Error: Unable to find attribute Columns.\n");
01019 IDMessage(mydev, "Error: Unable to find attribute Columns.");
01020 delLilXML(XMLParser);
01021 return false;
01022 }
01023
01024 cam->m_Columns = hextoi(valuXMLAtt(ap));
01025
01026
01027 ele = findXMLEle(system, "PP_Repeat");
01028 if (!ele)
01029 {
01030 IDLog("Error: Unable to find element PP_Repeat.\n");
01031 IDMessage(mydev, "Error: Unable to find element PP_Repeat.");
01032 delLilXML(XMLParser);
01033 return false;
01034 }
01035
01036 val = hextoi(pcdataXMLEle(ele));
01037 if (val > 0 && val <= 1000)
01038 cam->m_PPRepeat = val;
01039
01040
01041 if (!cam->InitDriver(0))
01042 {
01043 IDLog("Error: Failed to Init Driver.\n");
01044 IDMessage(mydev, "Error: Failed to Init Driver.");
01045 delLilXML(XMLParser);
01046 return false;
01047 }
01048
01049 cam->Reset();
01050
01051
01052 ele = findXMLEle(system, "Cable");
01053 if (!ele)
01054 {
01055 IDLog("Error: Unable to find element Cable.\n");
01056 IDMessage(mydev, "Error: Unable to find element Cable.");
01057 delLilXML(XMLParser);
01058 return false;
01059 }
01060
01061 if (!strcmp("Long", pcdataXMLEle(ele)))
01062 {
01063 cam->write_LongCable( true );
01064 IDLog("Cable is long\n");
01065 }
01066 else
01067 {
01068 cam->write_LongCable( false );
01069 IDLog("Cable is short\n");
01070 }
01071
01072
01073 if (!cam->read_Present())
01074 {
01075 IDLog("Error: read_Present() failed.\n");
01076 IDMessage(mydev, "Error: read_Present() failed.");
01077 delLilXML(XMLParser);
01078 return false;
01079 }
01080
01081
01082 cam->write_UseTrigger( false );
01083 cam->write_ForceShutterOpen( false );
01084
01085
01086 ele = findXMLEle(system, "High_Priority");
01087 if (ele)
01088 {
01089 if (!strcmp(pcdataXMLEle(ele), "True"))
01090 cam->m_HighPriority = true;
01091 else
01092 cam->m_HighPriority = false;
01093 }
01094
01095
01096 ele = findXMLEle(system, "Data_Bits");
01097 if (ele)
01098 {
01099 val = hextoi(pcdataXMLEle(ele));
01100 if (val >= 8 && val <= 18) cam->m_DataBits = val;
01101 }
01102
01103
01104 ele = findXMLEle(system, "Sensor");
01105 if (ele)
01106 {
01107 if (!strcmp(pcdataXMLEle(ele), "CCD"))
01108 cam->m_SensorType = Camera_SensorType_CCD;
01109 else
01110 cam->m_SensorType = Camera_SensorType_CMOS;
01111 }
01112
01113
01114 ele = findXMLEle(system, "Mode");
01115 if (ele)
01116 {
01117 val = hextoi(pcdataXMLEle(ele)) & 0xF;
01118 cam->write_Mode( val );
01119 IDLog("Mode %d\n", val);
01120 }
01121 else
01122 cam->write_Mode( 0 );
01123
01124
01125 ele = findXMLEle(system, "Test");
01126 if (ele)
01127 {
01128 val = hextoi(pcdataXMLEle(ele)) & 0xF;
01129 cam->write_TestBits( val );
01130 IDLog("Test bits %d\n", val);
01131 }
01132 else
01133 cam->write_TestBits( 0 );
01134
01135
01136 ele = findXMLEle(system, "Test2");
01137 if (ele)
01138 {
01139 val = hextoi(pcdataXMLEle(ele)) & 0xF;
01140 cam->write_Test2Bits( val );
01141 IDLog("Test 2 bits %d\n", val);
01142 }
01143 else
01144 cam->write_Test2Bits( 0 );
01145
01146
01147 ele = findXMLEle(system, "Shutter_Speed");
01148 if (ele)
01149 {
01150 cam->m_MaxExposure = 10485.75;
01151
01152 if (!strcmp(pcdataXMLEle(ele), "Normal"))
01153 {
01154 cam->m_FastShutter = false;
01155 cam->m_MinExposure = 0.01;
01156 IDLog("Shutter speed normal\n");
01157 }
01158 else if ( (!strcmp(pcdataXMLEle(ele), "Fast")) || (!strcmp(pcdataXMLEle(ele), "Dual")) )
01159 {
01160 cam->m_FastShutter = true;
01161 cam->m_MinExposure = 0.001;
01162 IDLog("Shutter speed fast\n");
01163 }
01164 }
01165
01166
01167 ele = findXMLEle(system, "Shutter_Bits");
01168 if (ele)
01169 {
01170 val = hextoi(pcdataXMLEle(ele));
01171 cam->m_FastShutterBits_Mode = val & 0x0F;
01172 cam->m_FastShutterBits_Test = ( val & 0xF0 ) >> 4;
01173 IDLog("Shutter bits %d\n", val);
01174 }
01175
01176
01177 ele = findXMLEle(system, "MaxBinX");
01178 if (ele)
01179 {
01180 val = hextoi(pcdataXMLEle(ele));
01181 if (val >= 1 && val <= MAXHBIN)
01182 cam->m_MaxBinX = val;
01183 }
01184
01185
01186 ele = findXMLEle(system, "MaxBinY");
01187 if (ele)
01188 {
01189 val = hextoi(pcdataXMLEle(ele));
01190 if (val >= 1 && val <= MAXVBIN)
01191 cam->m_MaxBinY = val;
01192 }
01193
01194
01195 ele = findXMLEle(system, "Guider_Relays");
01196 if (ele)
01197 {
01198 if (!strcmp(pcdataXMLEle(ele), "True"))
01199 cam->m_GuiderRelays = true;
01200 else
01201 cam->m_GuiderRelays = false;
01202 }
01203
01204
01205 ele = findXMLEle(system, "Timeout");
01206 if (ele)
01207 {
01208 double dval = atof(pcdataXMLEle(ele));
01209 if (dval >= 0.0 && dval <= 10000.0) cam->m_Timeout = dval;
01210 }
01211
01212
01213 ele = findXMLEle(geometry, "BIC");
01214 if (ele)
01215 {
01216 val = hextoi(pcdataXMLEle(ele));
01217 if (val >= 1 && val <= MAXCOLUMNS)
01218 cam->m_BIC = val;
01219 }
01220
01221
01222 ele = findXMLEle(geometry, "BIR");
01223 if (ele)
01224 {
01225 val = hextoi(pcdataXMLEle(ele));
01226 if (val >= 1 && val <= MAXROWS)
01227 cam->m_BIR = val;
01228 }
01229
01230
01231 ele = findXMLEle(geometry, "SKIPC");
01232 if (ele)
01233 {
01234 val = hextoi(pcdataXMLEle(ele));
01235 if (val >= 1 && val <= MAXCOLUMNS)
01236 cam->m_SkipC = val;
01237 }
01238
01239
01240 ele = findXMLEle(geometry, "SKIPR");
01241 if (ele)
01242 {
01243 val = hextoi(pcdataXMLEle(ele));
01244 if (val >= 1 && val <= MAXROWS)
01245 cam->m_SkipR = val;
01246 }
01247
01248
01249 ele = findXMLEle(geometry, "ImgCols");
01250 if (ele)
01251 {
01252 val = hextoi(pcdataXMLEle(ele));
01253 if (val >= 1 && val <= MAXTOTALCOLUMNS)
01254 cam->m_ImgColumns = val;
01255 }
01256 else
01257 cam->m_ImgColumns = cam->m_Columns - cam->m_BIC - cam->m_SkipC;
01258
01259
01260 ele = findXMLEle(geometry, "ImgRows");
01261 if (ele)
01262 {
01263 val = hextoi(pcdataXMLEle(ele));
01264 if (val >= 1 && val <= MAXTOTALROWS)
01265 cam->m_ImgRows = val;
01266 }
01267 else
01268 cam->m_ImgRows = cam->m_Rows - cam->m_BIR - cam->m_SkipR;
01269
01270
01271 ele = findXMLEle(geometry, "HFlush");
01272 if (ele)
01273 {
01274 val = hextoi(pcdataXMLEle(ele));
01275 if (val >= 1 && val <= MAXHBIN)
01276 cam->m_HFlush = val;
01277 }
01278
01279
01280 ele = findXMLEle(geometry, "VFlush");
01281 if (ele)
01282 {
01283 val = hextoi(pcdataXMLEle(ele));
01284 if (val >= 1 && val <= MAXVBIN)
01285 cam->m_VFlush = val;
01286 }
01287
01288
01289 cam->m_NumX = cam->m_ImgColumns;
01290 cam->m_NumY = cam->m_ImgRows;
01291
01292
01293 ap = findXMLAtt(temp, "Control");
01294 if (ap)
01295 {
01296 if (!strcmp(valuXMLAtt(ap), "True"))
01297 cam->m_TempControl = true;
01298 else
01299 cam->m_TempControl = false;
01300 }
01301
01302
01303 ap = findXMLAtt(temp, "Cal");
01304 if (ap)
01305 {
01306 val = hextoi(valuXMLAtt(ap));
01307 if (val >= 1 && val <= 255)
01308 cam->m_TempCalibration = val;
01309 }
01310
01311
01312 ap = findXMLAtt(temp, "Scale");
01313 if (ap)
01314 {
01315 double dval = atof(valuXMLAtt(ap));
01316 if (dval >= 1.0 && dval <= 10.0)
01317 cam->m_TempScale = dval;
01318 }
01319
01320
01321 ap = findXMLAtt(temp, "Target");
01322 if (ap)
01323 {
01324 double dval = atof(valuXMLAtt(ap));
01325 if (dval >= -60.0 && dval <= 40.0)
01326 cam->write_CoolerSetPoint( dval );
01327 else
01328 cam->write_CoolerSetPoint( -10.0 );
01329
01330 IDLog("Target: %g\n", dval);
01331 }
01332
01333
01334 ap = findXMLAtt(ccd, "Sensor");
01335 if (ap)
01336 {
01337 strncpy (cam->m_Sensor, valuXMLAtt(ap), 255);
01338 IDLog("Sensor: %s\n", cam->m_Sensor);
01339 }
01340
01341
01342 ele = findXMLEle(ccd, "Color");
01343 if (ele)
01344 {
01345 if (!strcmp(pcdataXMLEle(ele), "True"))
01346 {
01347 cam->m_Color = true;
01348 IDLog("Color: true\n");
01349 }
01350 else
01351 {
01352 cam->m_Color = false;
01353 IDLog("Color: false\n");
01354 }
01355 }
01356
01357
01358 ele = findXMLEle(ccd, "Noise");
01359 if (ele)
01360 cam->m_Noise = atof( pcdataXMLEle(ele) );
01361
01362
01363 ele = findXMLEle(ccd, "Gain");
01364 if (ele)
01365 cam->m_Gain = atof( pcdataXMLEle(ele) );
01366
01367
01368 ele = findXMLEle(ccd, "PixelXSize");
01369 if (ele)
01370 {
01371 cam->m_PixelXSize = atof( pcdataXMLEle(ele) );
01372 IDLog("Pixel X Size: %g\n", cam->m_PixelXSize);
01373 }
01374
01375
01376 ele = findXMLEle(ccd, "PixelYSize");
01377 if (ele)
01378 {
01379 cam->m_PixelYSize = atof( pcdataXMLEle(ele) );
01380 IDLog("Pixel Y Size: %g\n", cam->m_PixelYSize);
01381 }
01382
01383
01384 IDLog("Cam Row: %d - Cam Cols: %d - PP_Repeat %d\n",cam->m_Rows, cam->m_Columns, cam->m_PPRepeat);
01385 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");
01386 IDLog("Max X Bin: %d - Max Y Bin: %d - Guider Relays: %s\n", cam->m_MaxBinX, cam->m_MaxBinY, cam->m_GuiderRelays ? "true" : "false");
01387 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);
01388 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);
01389
01390 delLilXML(XMLParser);
01391
01392 return true;
01393 }
01394
01395
01396 int ApogeeCam::isCCDConnected(void)
01397 {
01398 return ((PowerS[0].s == ISS_ON) ? 1 : 0);
01399 }
01400
01401 FITS_HDU_LIST * ApogeeCam::create_fits_header (FITS_FILE *ofp, uint width, uint height, uint bpp)
01402 {
01403
01404 FITS_HDU_LIST *hdulist;
01405
01406 char temp_s[FITS_CARD_SIZE], expose_s[FITS_CARD_SIZE], binning_s[FITS_CARD_SIZE], frame_s[FITS_CARD_SIZE], pixel_s[FITS_CARD_SIZE];
01407 char obsDate[FITS_CARD_SIZE];
01408
01409 snprintf(obsDate, FITS_CARD_SIZE, "DATE-OBS= '%s' /Observation Date UTC", timestamp());
01410
01411 hdulist = fits_add_hdu (ofp);
01412 if (hdulist == NULL) return (NULL);
01413
01414 hdulist->used.simple = 1;
01415 hdulist->bitpix = 16;
01416 hdulist->naxis = 2;
01417 hdulist->naxisn[0] = width;
01418 hdulist->naxisn[1] = height;
01419 hdulist->naxisn[2] = bpp;
01420 hdulist->used.datamin = 1;
01421 hdulist->datamin = min();
01422 hdulist->used.datamax = 1;
01423 hdulist->datamax = max();
01424 hdulist->used.bzero = 1;
01425 hdulist->bzero = 0.0;
01426 hdulist->used.bscale = 1;
01427 hdulist->bscale = 1.0;
01428
01429 snprintf(temp_s, FITS_CARD_SIZE, "CCD-TEMP= %g / degrees celcius", APGFrame.temperature);
01430 snprintf(expose_s, FITS_CARD_SIZE, "EXPOSURE= %d / milliseconds", APGFrame.expose);
01431 snprintf(binning_s, FITS_CARD_SIZE, "BINNING = '(%d x %d)'", APGFrame.binX, APGFrame.binY);
01432 snprintf(pixel_s, FITS_CARD_SIZE, "PIX-SIZ = '%0.f x %0.f microns square'", cam->m_PixelXSize, cam->m_PixelYSize);
01433 switch (APGFrame.frameType)
01434 {
01435 case LIGHT_FRAME:
01436 strcpy(frame_s, "FRAME = 'Light'");
01437 break;
01438 case BIAS_FRAME:
01439 strcpy(frame_s, "FRAME = 'Bias'");
01440 break;
01441 case FLAT_FRAME:
01442 strcpy(frame_s, "FRAME = 'Flat Field'");
01443 break;
01444 case DARK_FRAME:
01445 strcpy(frame_s, "FRAME = 'Dark'");
01446 break;
01447 }
01448
01449 fits_add_card (hdulist, frame_s);
01450 fits_add_card (hdulist, temp_s);
01451 fits_add_card (hdulist, expose_s);
01452 fits_add_card (hdulist, pixel_s);
01453 fits_add_card (hdulist, "INSTRUME= 'Apogee CCD'");
01454 fits_add_card (hdulist, obsDate);
01455
01456 return (hdulist);
01457 }
01458
01459
01460
01461 unsigned short ApogeeCam::hextoi(char *instr)
01462 {
01463 unsigned short val, tot = 0;
01464 bool IsHEX = false;
01465
01466 long n = strlen( instr );
01467 if ( n > 1 )
01468 {
01469 if ( instr[ n - 1 ] == 'h' || instr[ n - 1 ] == 'H' )
01470 IsHEX = true;
01471 else if ( *instr == '0' && *(instr+1) == 'x' )
01472 {
01473 IsHEX = true;
01474 instr += 2;
01475 }
01476 }
01477
01478 if ( IsHEX )
01479 {
01480 while (instr && *instr && isxdigit(*instr))
01481 {
01482 val = *instr++ - '0';
01483 if (9 < val)
01484 val -= 7;
01485 tot <<= 4;
01486 tot |= (val & 0x0f);
01487 }
01488 }
01489 else
01490 tot = atoi( instr );
01491
01492 return tot;
01493 }
01494
01495 double ApogeeCam::min()
01496 {
01497 double lmin = APGFrame.img[0];
01498 int ind=0, i, j;
01499
01500 for (i= 0; i < APGFrame.height ; i++)
01501 for (j= 0; j < APGFrame.width; j++)
01502 {
01503 ind = (i * APGFrame.width) + j;
01504 if (APGFrame.img[ind] < lmin) lmin = APGFrame.img[ind];
01505 }
01506
01507 return lmin;
01508 }
01509
01510 double ApogeeCam::max()
01511 {
01512 double lmax = APGFrame.img[0];
01513 int ind=0, i, j;
01514
01515 for (i= 0; i < APGFrame.height ; i++)
01516 for (j= 0; j < APGFrame.width; j++)
01517 {
01518 ind = (i * APGFrame.width) + j;
01519 if (APGFrame.img[ind] > lmax) lmax = APGFrame.img[ind];
01520 }
01521
01522 return lmax;
01523 }
01524