summaryrefslogtreecommitdiffstats
path: root/kstars/kstars/indi/apogee_ppi.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kstars/kstars/indi/apogee_ppi.cpp')
-rw-r--r--kstars/kstars/indi/apogee_ppi.cpp1524
1 files changed, 1524 insertions, 0 deletions
diff --git a/kstars/kstars/indi/apogee_ppi.cpp b/kstars/kstars/indi/apogee_ppi.cpp
new file mode 100644
index 00000000..9898018f
--- /dev/null
+++ b/kstars/kstars/indi/apogee_ppi.cpp
@@ -0,0 +1,1524 @@
+#if 0
+ FLI CCD
+ INDI Interface for Apogee PPI
+ Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+#endif
+
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <zlib.h>
+
+#include "apogee_ppi.h"
+#include "lilxml.h"
+#include "base64.h"
+
+static void ISPoll(void *);
+
+extern char* me; /* argv[0] */
+ApogeeCam *MainCam = NULL; /* Main and only camera */
+
+/* send client definitions of all properties */
+void ISInit()
+{
+ if (MainCam == NULL)
+ {
+ MainCam = new ApogeeCam();
+ IEAddTimer (POLLMS, ISPoll, NULL);
+ }
+}
+
+void ISGetProperties (const char *dev)
+{
+ if (dev && strcmp (mydev, dev))
+ return;
+
+ ISInit();
+
+ MainCam->ISGetProperties(dev);
+}
+
+
+void ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n)
+{
+
+ /* ignore if not ours */
+ if (dev && strcmp (dev, mydev))
+ return;
+
+ ISInit();
+
+ MainCam->ISNewSwitch(dev, name, states, names, n);
+}
+
+void ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n)
+{
+ /* ignore if not ours */
+ if (dev && strcmp (mydev, dev))
+ return;
+
+ ISInit();
+
+ MainCam->ISNewText(dev, name, texts, names, n);
+}
+
+
+void ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n)
+{
+
+ /* ignore if not ours */
+ if (dev && strcmp (dev, mydev))
+ return;
+
+ ISInit();
+
+ MainCam->ISNewNumber(dev, name, values, names, n);
+}
+
+void ISNewBLOB (const char */*dev*/, const char */*name*/, int */*sizes[]*/, char **/*blobs[]*/, char **/*formats[]*/, char **/*names[]*/, int /*n*/)
+{
+
+}
+
+void ISPoll(void *)
+{
+ MainCam->ISPoll();
+ IEAddTimer (POLLMS, ISPoll, NULL);
+}
+
+
+ApogeeCam::ApogeeCam()
+{
+ ApogeeModelS = NULL;
+
+ initProperties();
+
+}
+
+ApogeeCam::~ApogeeCam()
+{
+
+}
+
+void ApogeeCam::initProperties()
+{
+ fillSwitch(&PowerS[0], "CONNECT", "Connect", ISS_OFF);
+ fillSwitch(&PowerS[1], "DISCONNECT", "Disconnect", ISS_ON);
+ fillSwitchVector(&PowerSP, PowerS, NARRAY(PowerS), mydev, "CONNECTION", "Connection", COMM_GROUP, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
+
+ fillSwitch(&FrameTypeS[0], "FRAME_LIGHT", "Light", ISS_ON);
+ fillSwitch(&FrameTypeS[1], "FRAME_BIAS", "Bias", ISS_OFF);
+ fillSwitch(&FrameTypeS[2], "FRAME_DARK", "Dark", ISS_OFF);
+ fillSwitch(&FrameTypeS[3], "FRAME_FLAT", "Flat Field", ISS_OFF);
+ fillSwitchVector(&FrameTypeSP, FrameTypeS, NARRAY(FrameTypeS), mydev, "CCD_FRAME_TYPE", "Frame Type", EXPOSE_GROUP, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
+
+ fillNumber(&FrameN[0], "X", "X", "%.0f", 0., MAX_PIXELS, 1., 0.);
+ fillNumber(&FrameN[1], "Y", "Y", "%.0f", 0., MAX_PIXELS, 1., 0.);
+ fillNumber(&FrameN[2], "WIDTH", "Width", "%.0f", 0., MAX_PIXELS, 1., 0.);
+ fillNumber(&FrameN[3], "HEIGHT", "Height", "%.0f", 0., MAX_PIXELS, 1., 0.);
+ fillNumberVector(&FrameNP, FrameN, NARRAY(FrameN), mydev, "CCD_FRAME", "Frame", IMAGE_GROUP, IP_RW, 60, IPS_IDLE);
+
+ fillNumber(&BinningN[0], "HOR_BIN", "X", "%0.f", 1., MAXHBIN, 1., 1.);
+ fillNumber(&BinningN[1], "VER_BIN", "Y", "%0.f", 1., MAXVBIN, 1., 1.);
+ fillNumberVector(&BinningNP, BinningN, NARRAY(BinningN), mydev, "CCD_BINNING", "Binning", IMAGE_GROUP, IP_RW, 60, IPS_IDLE);
+
+ fillNumber(&ExposeTimeN[0], "DURATION", "Duration (s)", "%5.2f", 0., 36000., 0.5, 1.);
+ fillNumberVector(&ExposeTimeNP, ExposeTimeN, NARRAY(ExposeTimeN), mydev, "CCD_EXPOSE_DURATION", "Expose", EXPOSE_GROUP, IP_RW, 60, IPS_IDLE);
+
+ fillNumber(&TemperatureN[0], "TEMPERATURE", "Temperature", "%+06.2f", MIN_CCD_TEMP, MAX_CCD_TEMP, 0.2, 0.);
+ fillNumberVector(&TemperatureNP, TemperatureN, NARRAY(TemperatureN), mydev, "CCD_TEMPERATURE", "Expose", EXPOSE_GROUP, IP_RW, 60, IPS_IDLE);
+
+ strcpy(imageB.name, "CCD1");
+ strcpy(imageB.label, "Feed");
+ strcpy(imageB.format, "");
+ imageB.blob = 0;
+ imageB.bloblen = 0;
+ imageB.size = 0;
+ imageB.bvp = 0;
+ imageB.aux0 = 0;
+ imageB.aux1 = 0;
+ imageB.aux2 = 0;
+
+ strcpy(imageBP.device, mydev);
+ strcpy(imageBP.name, "Video");
+ strcpy(imageBP.label, "Video");
+ strcpy(imageBP.group, COMM_GROUP);
+ strcpy(imageBP.timestamp, "");
+ imageBP.p = IP_RO;
+ imageBP.timeout = 0;
+ imageBP.s = IPS_IDLE;
+ imageBP.bp = &imageB;
+ imageBP.nbp = 1;
+ imageBP.aux = 0;
+
+ //loadXMLModel();
+}
+
+bool ApogeeCam::loadXMLModel()
+{
+ LilXML *XMLParser = newLilXML();
+ XMLEle *root = NULL, *camera = NULL;
+ XMLAtt *modelName;
+ FILE *modelSpecFile = NULL;
+ char errmsg[1024];
+ int ncams = 0;
+
+ //IDLog("Top dir is "TOP_DATADIR, NULL);
+ modelSpecFile = fopen(TOP_DATADIR"/apogee_caminfo.xml", "r");
+ //modelSpecFile = fopen("/opt/kde3/share/apps/kstars/apogee_caminfo.xml", "r");
+ if (modelSpecFile == NULL)
+ {
+ IDLog("Error: Unable to open file apogee_caminfo.xml\n");
+ IDMessage(mydev, "Error: Unable to open file apogee_caminfo.xml");
+ return false;
+ }
+
+ root = readXMLFile(modelSpecFile, XMLParser, errmsg);
+ if (root == NULL)
+ {
+ IDLog("Error: Unable to process apogee_caminfo.xml. %s\n", errmsg);
+ IDMessage(mydev, "Error: Unable to process apogee_caminfo.xml. %s\n", errmsg);
+ fclose(modelSpecFile);
+ delLilXML(XMLParser);
+ return false;
+ }
+
+ for (camera = nextXMLEle (root, 1); camera != NULL; camera = nextXMLEle (root, 0))
+ {
+ modelName = findXMLAtt(camera, "model");
+ if (modelName == NULL)
+ continue;
+
+ ApogeeModelS = (ApogeeModelS == NULL) ? (ISwitch *) malloc (sizeof(ISwitch))
+ : (ISwitch *) realloc(ApogeeModelS, sizeof(ISwitch) * (ncams + 1));
+
+ snprintf(ApogeeModelS[ncams].name, MAXINDINAME, "Model%d", ncams);
+ strcpy(ApogeeModelS[ncams].label, valuXMLAtt(modelName));
+ ApogeeModelS[ncams].s = (ncams == 0) ? ISS_ON : ISS_OFF;
+ ApogeeModelS[ncams].svp = NULL;
+ ApogeeModelS[ncams].aux = NULL;
+
+ ncams++;
+ }
+
+ fclose(modelSpecFile);
+ delLilXML(XMLParser);
+
+ if (ncams > 0)
+ {
+ fillSwitchVector(&ApogeeModelSP, ApogeeModelS, ncams, mydev, "Model", "", COMM_GROUP, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
+ return true;
+ }
+
+ return false;
+
+}
+
+void ApogeeCam::ISGetProperties(const char */*dev*/)
+{
+
+
+ /* COMM_GROUP */
+ IDDefSwitch(&PowerSP, NULL);
+ if (loadXMLModel())
+ IDDefSwitch(&ApogeeModelSP, NULL);
+ else
+ IDMessage(mydev, "Error: Unable to read camera specifications. Driver is disabled.");
+ IDDefBLOB(&imageBP, NULL);
+
+ /* Expose */
+ IDDefSwitch(&FrameTypeSP, NULL);
+ IDDefNumber(&ExposeTimeNP, NULL);
+ IDDefNumber(&TemperatureNP, NULL);
+
+ /* Image Group */
+ IDDefNumber(&FrameNP, NULL);
+ IDDefNumber(&BinningNP, NULL);
+
+ IDLog("Apogee Driver Debug Enabled\n");
+
+}
+
+void ApogeeCam::ISNewSwitch (const char */*dev*/, const char *name, ISState *states, char *names[], int n)
+{
+
+ /* Connection */
+ if (!strcmp (name, PowerSP.name))
+ {
+ IUResetSwitches(&PowerSP);
+ IUUpdateSwitches(&PowerSP, states, names, n);
+ connectCCD();
+ return;
+ }
+
+ /* Frame Type */
+ if (!strcmp(FrameTypeSP.name, name))
+ {
+ if (checkPowerS(&FrameTypeSP))
+ return;
+
+ IUResetSwitches(&FrameTypeSP);
+ IUUpdateSwitches(&FrameTypeSP, states, names, n);
+ FrameTypeSP.s = IPS_OK;
+ IDSetSwitch(&FrameTypeSP, NULL);
+
+ return;
+ }
+
+ /* Apogee Model */
+ if (!strcmp(ApogeeModelSP.name, name))
+ {
+ IUResetSwitches(&ApogeeModelSP);
+ IUUpdateSwitches(&ApogeeModelSP, states, names, n);
+ ApogeeModelSP.s = IPS_OK;
+ IDSetSwitch(&ApogeeModelSP, NULL);
+ return;
+ }
+
+}
+
+void ApogeeCam::ISNewText (const char */*dev*/, const char */*name*/, char **/*texts[]*/, char **/*names[]*/, int /*n*/)
+{
+
+}
+
+void ApogeeCam::ISNewNumber (const char */*dev*/, const char *name, double values[], char *names[], int n)
+{
+ /* Exposure time */
+ if (!strcmp (ExposeTimeNP.name, name))
+ {
+ if (checkPowerN(&ExposeTimeNP))
+ return;
+
+ if (ExposeTimeNP.s == IPS_BUSY)
+ {
+ cam->Reset();
+ ExposeTimeNP.s = IPS_IDLE;
+ ExposeTimeN[0].value = 0;
+
+ IDSetNumber(&ExposeTimeNP, "Exposure cancelled.");
+ IDLog("Exposure Cancelled.\n");
+ return;
+ }
+
+ ExposeTimeNP.s = IPS_IDLE;
+
+ IUUpdateNumbers(&ExposeTimeNP, values, names, n);
+
+ IDLog("Exposure Time is: %g\n", ExposeTimeN[0].value);
+
+ handleExposure(NULL);
+ return;
+ }
+
+ if (!strcmp(TemperatureNP.name, name))
+ {
+ if (checkPowerN(&TemperatureNP))
+ return;
+
+ TemperatureNP.s = IPS_IDLE;
+
+ if (values[0] < MIN_CCD_TEMP || values[0] > MAX_CCD_TEMP)
+ {
+ IDSetNumber(&TemperatureNP, "Error: valid range of temperature is from %d to %d", MIN_CCD_TEMP, MAX_CCD_TEMP);
+ return;
+ }
+
+ targetTemp = values[0];
+ cam->write_CoolerMode(0);
+ cam->write_CoolerMode(1);
+ cam->write_CoolerSetPoint(targetTemp);
+
+ TemperatureNP.s = IPS_BUSY;
+
+ IDSetNumber(&TemperatureNP, "Setting CCD temperature to %+06.2f C", values[0]);
+ IDLog("Setting CCD temperature to %+06.2f C\n", values[0]);
+ return;
+ }
+
+ if (!strcmp(FrameNP.name, name))
+ {
+ if (checkPowerN(&FrameNP))
+ return;
+
+ FrameNP.s = IPS_OK;
+ IUUpdateNumbers(&FrameNP, values, names, n);
+
+ cam->m_StartX = (int) FrameN[0].value;
+ cam->m_StartY = (int) FrameN[1].value;
+ cam->m_NumX = (int) FrameN[2].value;
+ cam->m_NumY = (int) FrameN[3].value;
+ IDSetNumber(&FrameNP, NULL);
+
+ } /* end FrameNP */
+
+
+ if (!strcmp(BinningNP.name, name))
+ {
+ if (checkPowerN(&BinningNP))
+ return;
+
+
+ BinningNP.s = IPS_OK;
+ IUUpdateNumbers(&BinningNP, values, names, n);
+
+ cam->m_BinX = (int) BinningN[0].value;
+ cam->m_BinY = (int) BinningN[1].value;
+
+ IDLog("Binning is: %.0f x %.0f\n", BinningN[0].value, BinningN[1].value);
+ return;
+ }
+}
+
+
+void ApogeeCam::ISPoll()
+{
+ static int mtc=5;
+ int readStatus=0;
+ double ccdTemp;
+
+ if (!isCCDConnected())
+ return;
+
+ switch (ExposeTimeNP.s)
+ {
+ case IPS_IDLE:
+ case IPS_OK:
+ break;
+
+ case IPS_BUSY:
+
+ readStatus = cam->read_Status();
+ if (readStatus < 0)
+ {
+ IDLog("Error in exposure! Read status: %d\n", readStatus);
+ ExposeTimeNP.s = IPS_ALERT;
+ ExposeTimeN[0].value = 0;
+ IDSetNumber(&ExposeTimeNP, "Error in exposure procedure. Read states: %d", readStatus);
+ return;
+ }
+ else if (readStatus == Camera_Status_ImageReady)
+ {
+ ExposeTimeN[0].value = 0;
+ ExposeTimeNP.s = IPS_OK;
+ IDSetNumber(&ExposeTimeNP, "Exposure done, downloading image...");
+ IDLog("Exposure done, downloading image...\n");
+ /* grab and save image */
+ grabImage();
+ return;
+ }
+
+ ExposeTimeN[0].value --;
+ IDSetNumber(&ExposeTimeNP, NULL);
+ break;
+
+ case IPS_ALERT:
+ break;
+ }
+
+ switch (TemperatureNP.s)
+ {
+ case IPS_IDLE:
+ case IPS_OK:
+ mtc--;
+
+ if (mtc == 0)
+ {
+ TemperatureN[0].value = cam->read_Temperature();
+ IDSetNumber(&TemperatureNP, NULL);
+ mtc = 5;
+ }
+ break;
+
+ case IPS_BUSY:
+
+ ccdTemp = cam->read_Temperature();
+
+ if (fabs(targetTemp - ccdTemp) <= TEMP_THRESHOLD)
+ TemperatureNP.s = IPS_OK;
+
+ mtc = 1;
+ TemperatureN[0].value = ccdTemp;
+ IDSetNumber(&TemperatureNP, NULL);
+ break;
+
+ case IPS_ALERT:
+ break;
+ }
+
+}
+
+/* Downloads the image from the CCD row by row and store them
+ in a raw file.
+ N.B. No processing is done on the image */
+void ApogeeCam::grabImage()
+{
+
+ long err;
+ int img_size, fd;
+ char errmsg[1024];
+ char filename[] = "/tmp/fitsXXXXXX";
+
+ IDLog("In grab Image\n");
+
+ if ((fd = mkstemp(filename)) < 0)
+ {
+ IDMessage(mydev, "Error making temporary filename.");
+ IDLog("Error making temporary filename.\n");
+ return;
+ }
+ close(fd);
+
+ img_size = APGFrame.width * APGFrame.height * sizeof(unsigned short);
+
+ IDLog("Allocating memory buffer. Width: %d - Height: %d\n", APGFrame.width, APGFrame.height);
+
+ APGFrame.img = (unsigned short *) malloc (img_size);
+
+ if (APGFrame.img == NULL)
+ {
+ IDMessage(mydev, "Not enough memory to store image.");
+ IDLog("Not enough memory to store image.\n");
+ return;
+ }
+
+ IDLog("Getting frame buffer from camera...\n");
+ if (!cam->GetImage( APGFrame.img , APGFrame.width, APGFrame.height ))
+ {
+ free(APGFrame.img);
+ IDMessage(mydev, "GetImage() failed.");
+ IDLog("GetImage() failed.");
+ return;
+ }
+
+ IDLog("Done with getting frame buffer, writing FITS file\n");
+
+ err = writeFITS(filename, errmsg);
+
+ if (err)
+ {
+ free(APGFrame.img);
+ IDMessage(mydev, errmsg, NULL);
+ return;
+ }
+
+ free(APGFrame.img);
+
+ IDLog("All good, returning\n");
+
+}
+
+int ApogeeCam::writeFITS(char *filename, char errmsg[])
+{
+
+ FITS_FILE* ofp;
+ int bpp, bpsl, width, height;
+ long nbytes;
+ FITS_HDU_LIST *hdu;
+
+ IDLog("in write FITS, opening filename %s\n", filename);
+
+ ofp = fits_open (filename, "w");
+ if (!ofp)
+ {
+ sprintf(errmsg, "Error: cannot open file for writing.");
+ return (-1);
+ }
+
+ width = APGFrame.width;
+ height = APGFrame.height;
+ bpp = sizeof(unsigned short); /* Bytes per Pixel */
+ bpsl = bpp * APGFrame.width; /* Bytes per Line */
+ nbytes = 0;
+
+ IDLog("Creating FITS header\n");
+ hdu = create_fits_header (ofp, width, height, bpp);
+ if (hdu == NULL)
+ {
+ sprintf(errmsg, "Error: creating FITS header failed.");
+ return (-1);
+ }
+ if (fits_write_header (ofp, hdu) < 0)
+ {
+ sprintf(errmsg, "Error: writing to FITS header failed.");
+ return (-1);
+ }
+
+ IDLog("Converting to BIG Endian\n");
+ for (int i=0; i < height; i++)
+ for (int j=0 ; j < width; j++)
+ APGFrame.img[width * i + j] = getBigEndian( (APGFrame.img[width * i + j]) );
+
+ IDLog("Writing frame to disk\n");
+ for (int i= 0; i < height ; i++)
+ {
+ fwrite(APGFrame.img + (i * width), 2, width, ofp->fp);
+ nbytes += bpsl;
+ }
+
+ IDLog("Calculating nbytes\n");
+ nbytes = nbytes % FITS_RECORD_SIZE;
+ if (nbytes)
+ {
+ while (nbytes++ < FITS_RECORD_SIZE)
+ putc (0, ofp->fp);
+ }
+
+ if (ferror (ofp->fp))
+ {
+ sprintf(errmsg, "Error: write error occured");
+ return (-1);
+ }
+
+ IDLog("Closing ofp\n");
+ fits_close (ofp);
+
+ /* Success */
+ ExposeTimeNP.s = IPS_OK;
+ IDSetNumber(&ExposeTimeNP, NULL);
+ IDLog("Loading FITS image...\n");
+
+ IDLog("Uploading filename\n");
+ uploadFile(filename);
+ IDLog("Uploading done, returning\n");
+
+ return 0;
+
+}
+
+void ApogeeCam::uploadFile(char * filename)
+{
+
+ FILE * fitsFile;
+ unsigned char *fitsData, *compressedData;
+ int r=0;
+ unsigned int i =0, nr = 0;
+ uLongf compressedBytes=0;
+ uLong totalBytes;
+ struct stat stat_p;
+
+ IDLog("in upload file, will stat file now\n");
+
+ if ( -1 == stat (filename, &stat_p))
+ {
+ IDLog(" Error occoured attempting to stat %s\n", filename);
+ return;
+ }
+
+ totalBytes = stat_p.st_size;
+ fitsData = (unsigned char *) malloc (sizeof(unsigned char) * totalBytes);
+ compressedData = (unsigned char *) malloc (sizeof(unsigned char) * totalBytes + totalBytes / 64 + 16 + 3);
+
+ if (fitsData == NULL || compressedData == NULL)
+ {
+ IDLog("Error! low memory. Unable to initialize fits buffers.\n");
+ return;
+ }
+
+ IDLog("opening file\n");
+ fitsFile = fopen(filename, "r");
+
+ if (fitsFile == NULL)
+ return;
+
+ IDLog("Reading file from disk\n");
+ /* #1 Read file from disk */
+ for (i=0; i < totalBytes; i+= nr)
+ {
+ nr = fread(fitsData + i, 1, totalBytes - i, fitsFile);
+
+ if (nr <= 0)
+ {
+ IDLog("Error reading temporary FITS file.\n");
+ return;
+ }
+ }
+
+ compressedBytes = sizeof(char) * totalBytes + totalBytes / 64 + 16 + 3;
+
+ IDLog("Compressing data\n");
+ /* #2 Compress it */
+ r = compress2(compressedData, &compressedBytes, fitsData, totalBytes, 9);
+ if (r != Z_OK)
+ {
+ /* this should NEVER happen */
+ IDLog("internal error - compression failed: %d\n", r);
+ return;
+ }
+
+ IDLog("Sending blob. bloblen %ld - size %ld\n", compressedBytes, totalBytes);
+
+ /* #3 Send it */
+ imageB.blob = compressedData;
+ imageB.bloblen = compressedBytes;
+ imageB.size = totalBytes;
+ strcpy(imageB.format, ".fits.z");
+ imageBP.s = IPS_OK;
+ IDSetBLOB (&imageBP, NULL);
+
+ free (fitsData);
+ free (compressedData);
+
+}
+
+/* Initiates the exposure procedure */
+void ApogeeCam::handleExposure(void */*p*/)
+{
+
+ int curFrame = getOnSwitch(&FrameTypeSP);
+
+ switch (curFrame)
+ {
+ /* Light frame */
+ case LIGHT_FRAME:
+ if (!cam->Expose( (int) ExposeTimeN[0].value, true ))
+ {
+ ExposeTimeNP.s = IPS_IDLE;
+ IDSetNumber(&ExposeTimeNP, "Light Camera exposure failed.");
+ IDLog("Light Camera exposure failed.\n");
+ return;
+ }
+ break;
+
+ /* BIAS frame is the same as DARK but with minimum period. i.e. readout from camera electronics.
+ */
+ case BIAS_FRAME:
+ if (!cam->Expose( 0.05 , false ))
+ {
+ ExposeTimeNP.s = IPS_IDLE;
+ IDSetNumber(&ExposeTimeNP, "Bias Camera exposure failed.");
+ IDLog("Bias Camera exposure failed.\n");
+ return;
+ }
+ break;
+
+ /* Dark frame */
+ case DARK_FRAME:
+ if (!cam->Expose( (int) ExposeTimeN[0].value , false ))
+ {
+ ExposeTimeNP.s = IPS_IDLE;
+ IDSetNumber(&ExposeTimeNP, "Dark Camera exposure failed.");
+ IDLog("Dark Camera exposure failed.\n");
+ return;
+ }
+ break;
+
+ case FLAT_FRAME:
+ if (!cam->Expose( (int) ExposeTimeN[0].value , true ))
+ {
+ ExposeTimeNP.s = IPS_IDLE;
+ IDSetNumber(&ExposeTimeNP, "Flat Camera exposure failed.");
+ IDLog("Flat Camera exposure failed.\n");
+ return;
+ }
+ break;
+ }
+
+ APGFrame.frameType = curFrame;
+ APGFrame.width = (int) FrameN[2].value;
+ APGFrame.height = (int) FrameN[3].value;
+ APGFrame.expose = (int) ExposeTimeN[0].value;
+ APGFrame.temperature = TemperatureN[0].value;
+ APGFrame.binX = (int) BinningN[0].value;
+ APGFrame.binY = (int) BinningN[1].value;
+
+ ExposeTimeNP.s = IPS_BUSY;
+
+ IDSetNumber(&ExposeTimeNP, "Taking a %g seconds frame...", ExposeTimeN[0].value);
+ 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);
+
+}
+
+/* Retrieves basic data from the CCD upon connection like temperature, array size, firmware..etc */
+void ApogeeCam::getBasicData()
+{
+
+
+ // Maximum resolution
+ FrameN[2].max = cam->m_NumX;
+ FrameN[3].max = cam->m_NumY;
+ IUUpdateMinMax(&FrameNP);
+
+ // Maximum Bin
+ BinningN[0].max = cam->m_MaxBinX;
+ BinningN[1].max = cam->m_MaxBinX;
+ IUUpdateMinMax(&BinningNP);
+
+ FrameN[0].value = 0;
+ FrameN[1].value = 0;
+ FrameN[2].min = 0;
+ FrameN[2].max = cam->m_ImgColumns;
+ FrameN[2].value = cam->m_ImgColumns;
+ FrameN[3].min = 0;
+ FrameN[3].max = cam->m_ImgRows;
+ FrameN[3].value = cam->m_ImgRows;
+
+ IUUpdateMinMax(&FrameNP);
+ IDSetNumber(&FrameNP, NULL);
+
+ // Current Temperature
+ TemperatureN[0].value = cam->read_Temperature();
+ IDSetNumber(&TemperatureNP, NULL);
+
+}
+
+int ApogeeCam::getOnSwitch(ISwitchVectorProperty *sp)
+{
+ int i=0;
+ for (i=0; i < sp->nsp ; i++)
+ {
+ /*IDLog("Switch %s is %s\n", sp->sp[i].name, sp->sp[i].s == ISS_ON ? "On" : "Off");*/
+ if (sp->sp[i].s == ISS_ON)
+ return i;
+ }
+
+ return -1;
+}
+
+int ApogeeCam::checkPowerS(ISwitchVectorProperty *sp)
+{
+ if (PowerSP.s != IPS_OK)
+ {
+ if (!strcmp(sp->label, ""))
+ IDMessage (mydev, "Cannot change property %s while the CCD is offline.", sp->name);
+ else
+ IDMessage (mydev, "Cannot change property %s while the CCD is offline.", sp->label);
+
+ sp->s = IPS_IDLE;
+ IDSetSwitch(sp, NULL);
+ return -1;
+ }
+
+ return 0;
+}
+
+int ApogeeCam::checkPowerN(INumberVectorProperty *np)
+{
+ if (PowerSP.s != IPS_OK)
+ {
+ if (!strcmp(np->label, ""))
+ IDMessage (mydev, "Cannot change property %s while the CCD is offline.", np->name);
+ else
+ IDMessage (mydev, "Cannot change property %s while the CCD is offline.", np->label);
+
+ np->s = IPS_IDLE;
+ IDSetNumber(np, NULL);
+ return -1;
+ }
+
+ return 0;
+}
+
+int ApogeeCam::checkPowerT(ITextVectorProperty *tp)
+{
+
+ if (PowerSP.s != IPS_OK)
+ {
+ if (!strcmp(tp->label, ""))
+ IDMessage (mydev, "Cannot change property %s while the CCD is offline.", tp->name);
+ else
+ IDMessage (mydev, "Cannot change property %s while the CCD is offline.", tp->label);
+
+ tp->s = IPS_IDLE;
+ IDSetText(tp, NULL);
+ return -1;
+ }
+
+ return 0;
+
+}
+
+void ApogeeCam::connectCCD()
+{
+
+ /* USB by default {USB, SERIAL, PARALLEL, INET} */
+ switch (PowerS[0].s)
+ {
+ case ISS_ON:
+ if (initCamera())
+ {
+ /* Sucess! */
+ PowerS[0].s = ISS_ON;
+ PowerS[1].s = ISS_OFF;
+ PowerSP.s = IPS_OK;
+ IDSetSwitch(&PowerSP, "CCD is online. Retrieving basic data.");
+ IDLog("CCD is online. Retrieving basic data.\n");
+ getBasicData();
+
+ }
+ else
+ {
+ PowerSP.s = IPS_IDLE;
+ PowerS[0].s = ISS_OFF;
+ PowerS[1].s = ISS_ON;
+ IDSetSwitch(&PowerSP, "Error: no cameras were detected.");
+ IDLog("Error: no cameras were detected.\n");
+ return;
+ }
+
+ break;
+
+ case ISS_OFF:
+ PowerS[0].s = ISS_OFF;
+ PowerS[1].s = ISS_ON;
+ PowerSP.s = IPS_IDLE;
+ IDSetSwitch(&PowerSP, "CCD is offline.");
+ break;
+ }
+
+}
+
+bool ApogeeCam::initCamera()
+{
+ LilXML *XMLParser = newLilXML();
+ XMLEle *root = NULL, *camera = NULL, *ele = NULL;
+ XMLEle *system = NULL, *geometry = NULL, *temp = NULL, *ccd = NULL;
+ XMLAtt *ap;
+ FILE *spFile = NULL;
+ char errmsg[1024];
+
+ spFile = fopen(TOP_DATADIR"/apogee_caminfo.xml", "r");
+ //spFile = fopen("/opt/kde3/share/apps/kstars/apogee_caminfo.xml", "r");
+ if (spFile == NULL)
+ {
+ IDLog("Error: Unable to open file apogee_caminfo.xml\n");
+ IDMessage(mydev, "Error: Unable to open file apogee_caminfo.xml");
+ return false;
+ }
+
+ root = readXMLFile(spFile, XMLParser, errmsg);
+ if (root == NULL)
+ {
+ IDLog("Error: Unable to process apogee_caminfo.xml. %s\n", errmsg);
+ IDMessage(mydev, "Error: Unable to process apogee_caminfo.xml. %s\n", errmsg);
+ fclose(spFile);
+ delLilXML(XMLParser);
+ return false;
+ }
+
+ fclose(spFile);
+
+ // Let's locate which camera to load the configuration for
+ camera = findXMLEle(root, "Apogee_Camera");
+
+ if (camera == NULL)
+ {
+ IDLog("Error: Unable to find Apogee_Camera element.\n");
+ IDMessage(mydev, "Error: Unable to find Apogee_Camera element.");
+ delLilXML(XMLParser);
+ return false;
+ }
+
+ IDLog("Looking for %s - len %d\n", ApogeeModelS[getOnSwitch(&ApogeeModelSP)].label, strlen(ApogeeModelS[getOnSwitch(&ApogeeModelSP)].label));
+
+ ap = findXMLAtt(camera, "model");
+ if (!ap)
+ {
+ IDLog("Error: Unable to find attribute model.\n");
+ IDMessage(mydev, "Error: Unable to find attribute model.");
+ return false;
+ }
+
+ if (strcmp(valuXMLAtt(ap), ApogeeModelS[getOnSwitch(&ApogeeModelSP)].label))
+ {
+ IDLog("Camera %s not found in XML file\n", ApogeeModelS[getOnSwitch(&ApogeeModelSP)].label);
+ IDMessage(mydev, "Camera %s not found in XML file\n", ApogeeModelS[getOnSwitch(&ApogeeModelSP)].label);
+ delLilXML(XMLParser);
+ return false;
+ }
+
+ // Let's get the subsections now
+ system = findXMLEle(camera, "System");
+ geometry = findXMLEle(camera, "Geometry");
+ temp = findXMLEle(camera, "Temp");
+ ccd = findXMLEle(camera, "CCD");
+
+ if (system == NULL)
+ {
+ IDLog("Error: Unable to find System element in camera.\n");
+ IDMessage(mydev, "Error: Unable to find System element in camera.");
+ delLilXML(XMLParser);
+ return false;
+ }
+
+ if (geometry == NULL)
+ {
+ IDLog("Error: Unable to find Geometry element in camera.\n");
+ IDMessage(mydev, "Error: Unable to find Geometry element in camera.");
+ delLilXML(XMLParser);
+ return false;
+ }
+
+ if (temp == NULL)
+ {
+ IDLog("Error: Unable to find Temp element in camera.\n");
+ IDMessage(mydev, "Error: Unable to find Temp element in camera.");
+ delLilXML(XMLParser);
+ return false;
+ }
+
+ if (ccd == NULL)
+ {
+ IDLog("Error: Unable to find CCD element in camera.\n");
+ IDMessage(mydev, "Error: Unable to find CCD element in camera.");
+ delLilXML(XMLParser);
+ return false;
+ }
+
+ cam = new CCameraIO();
+
+ if (cam == NULL)
+ {
+ IDLog("Error: Failed to create CCameraIO object.\n");
+ IDMessage(mydev, "Error: Failed to create CCameraIO object.");
+ delLilXML(XMLParser);
+ return false;
+ }
+
+ int bAddr = 0x378;
+ int val = 0;
+
+ bAddr = hextoi(valuXMLAtt(findXMLAtt(system, "Base"))) & 0xFFF;
+
+ // Rows
+ ap = findXMLAtt(geometry, "Rows");
+ if (!ap)
+ {
+ IDLog("Error: Unable to find attribute Rows.\n");
+ IDMessage(mydev, "Error: Unable to find attribute Rows.");
+ delLilXML(XMLParser);
+ return false;
+ }
+
+ cam->m_Rows = hextoi(valuXMLAtt(ap));
+
+ // Columns
+ ap = findXMLAtt(geometry, "Columns");
+ if (!ap)
+ {
+ IDLog("Error: Unable to find attribute Columns.\n");
+ IDMessage(mydev, "Error: Unable to find attribute Columns.");
+ delLilXML(XMLParser);
+ return false;
+ }
+
+ cam->m_Columns = hextoi(valuXMLAtt(ap));
+
+ // pp_repeat
+ ele = findXMLEle(system, "PP_Repeat");
+ if (!ele)
+ {
+ IDLog("Error: Unable to find element PP_Repeat.\n");
+ IDMessage(mydev, "Error: Unable to find element PP_Repeat.");
+ delLilXML(XMLParser);
+ return false;
+ }
+
+ val = hextoi(pcdataXMLEle(ele));
+ if (val > 0 && val <= 1000)
+ cam->m_PPRepeat = val;
+
+ // Initiate driver
+ if (!cam->InitDriver(0))
+ {
+ IDLog("Error: Failed to Init Driver.\n");
+ IDMessage(mydev, "Error: Failed to Init Driver.");
+ delLilXML(XMLParser);
+ return false;
+ }
+
+ cam->Reset();
+
+ // Cable length
+ ele = findXMLEle(system, "Cable");
+ if (!ele)
+ {
+ IDLog("Error: Unable to find element Cable.\n");
+ IDMessage(mydev, "Error: Unable to find element Cable.");
+ delLilXML(XMLParser);
+ return false;
+ }
+
+ if (!strcmp("Long", pcdataXMLEle(ele)))
+ {
+ cam->write_LongCable( true );
+ IDLog("Cable is long\n");
+ }
+ else
+ {
+ cam->write_LongCable( false );
+ IDLog("Cable is short\n");
+ }
+
+
+ if (!cam->read_Present())
+ {
+ IDLog("Error: read_Present() failed.\n");
+ IDMessage(mydev, "Error: read_Present() failed.");
+ delLilXML(XMLParser);
+ return false;
+}
+
+ // Default settings
+ cam->write_UseTrigger( false );
+ cam->write_ForceShutterOpen( false );
+
+ // High priority
+ ele = findXMLEle(system, "High_Priority");
+ if (ele)
+ {
+ if (!strcmp(pcdataXMLEle(ele), "True"))
+ cam->m_HighPriority = true;
+ else
+ cam->m_HighPriority = false;
+ }
+
+ // Data bits
+ ele = findXMLEle(system, "Data_Bits");
+ if (ele)
+ {
+ val = hextoi(pcdataXMLEle(ele));
+ if (val >= 8 && val <= 18) cam->m_DataBits = val;
+ }
+
+ // Sensor
+ ele = findXMLEle(system, "Sensor");
+ if (ele)
+ {
+ if (!strcmp(pcdataXMLEle(ele), "CCD"))
+ cam->m_SensorType = Camera_SensorType_CCD;
+ else
+ cam->m_SensorType = Camera_SensorType_CMOS;
+ }
+
+ // Mode
+ ele = findXMLEle(system, "Mode");
+ if (ele)
+ {
+ val = hextoi(pcdataXMLEle(ele)) & 0xF;
+ cam->write_Mode( val );
+ IDLog("Mode %d\n", val);
+ }
+ else
+ cam->write_Mode( 0 );
+
+ // Test
+ ele = findXMLEle(system, "Test");
+ if (ele)
+ {
+ val = hextoi(pcdataXMLEle(ele)) & 0xF;
+ cam->write_TestBits( val );
+ IDLog("Test bits %d\n", val);
+ }
+ else
+ cam->write_TestBits( 0 );
+
+ // Test2
+ ele = findXMLEle(system, "Test2");
+ if (ele)
+ {
+ val = hextoi(pcdataXMLEle(ele)) & 0xF;
+ cam->write_Test2Bits( val );
+ IDLog("Test 2 bits %d\n", val);
+ }
+ else
+ cam->write_Test2Bits( 0 );
+
+ // Shutter Speed
+ ele = findXMLEle(system, "Shutter_Speed");
+ if (ele)
+ {
+ cam->m_MaxExposure = 10485.75;
+
+ if (!strcmp(pcdataXMLEle(ele), "Normal"))
+ {
+ cam->m_FastShutter = false;
+ cam->m_MinExposure = 0.01;
+ IDLog("Shutter speed normal\n");
+ }
+ else if ( (!strcmp(pcdataXMLEle(ele), "Fast")) || (!strcmp(pcdataXMLEle(ele), "Dual")) )
+ {
+ cam->m_FastShutter = true;
+ cam->m_MinExposure = 0.001;
+ IDLog("Shutter speed fast\n");
+ }
+ }
+
+ // Shutter Bits
+ ele = findXMLEle(system, "Shutter_Bits");
+ if (ele)
+ {
+ val = hextoi(pcdataXMLEle(ele));
+ cam->m_FastShutterBits_Mode = val & 0x0F;
+ cam->m_FastShutterBits_Test = ( val & 0xF0 ) >> 4;
+ IDLog("Shutter bits %d\n", val);
+ }
+
+ // Max X Bin
+ ele = findXMLEle(system, "MaxBinX");
+ if (ele)
+ {
+ val = hextoi(pcdataXMLEle(ele));
+ if (val >= 1 && val <= MAXHBIN)
+ cam->m_MaxBinX = val;
+ }
+
+ // Max Y Bin
+ ele = findXMLEle(system, "MaxBinY");
+ if (ele)
+ {
+ val = hextoi(pcdataXMLEle(ele));
+ if (val >= 1 && val <= MAXVBIN)
+ cam->m_MaxBinY = val;
+ }
+
+ // Guider Relays
+ ele = findXMLEle(system, "Guider_Relays");
+ if (ele)
+ {
+ if (!strcmp(pcdataXMLEle(ele), "True"))
+ cam->m_GuiderRelays = true;
+ else
+ cam->m_GuiderRelays = false;
+ }
+
+ // Timeout
+ ele = findXMLEle(system, "Timeout");
+ if (ele)
+ {
+ double dval = atof(pcdataXMLEle(ele));
+ if (dval >= 0.0 && dval <= 10000.0) cam->m_Timeout = dval;
+ }
+
+ // BIC
+ ele = findXMLEle(geometry, "BIC");
+ if (ele)
+ {
+ val = hextoi(pcdataXMLEle(ele));
+ if (val >= 1 && val <= MAXCOLUMNS)
+ cam->m_BIC = val;
+ }
+
+ // BIR
+ ele = findXMLEle(geometry, "BIR");
+ if (ele)
+ {
+ val = hextoi(pcdataXMLEle(ele));
+ if (val >= 1 && val <= MAXROWS)
+ cam->m_BIR = val;
+ }
+
+ // SKIPC
+ ele = findXMLEle(geometry, "SKIPC");
+ if (ele)
+ {
+ val = hextoi(pcdataXMLEle(ele));
+ if (val >= 1 && val <= MAXCOLUMNS)
+ cam->m_SkipC = val;
+ }
+
+ // SKIPR
+ ele = findXMLEle(geometry, "SKIPR");
+ if (ele)
+ {
+ val = hextoi(pcdataXMLEle(ele));
+ if (val >= 1 && val <= MAXROWS)
+ cam->m_SkipR = val;
+ }
+
+ // IMG COlS
+ ele = findXMLEle(geometry, "ImgCols");
+ if (ele)
+ {
+ val = hextoi(pcdataXMLEle(ele));
+ if (val >= 1 && val <= MAXTOTALCOLUMNS)
+ cam->m_ImgColumns = val;
+ }
+ else
+ cam->m_ImgColumns = cam->m_Columns - cam->m_BIC - cam->m_SkipC;
+
+ // IMG ROWS
+ ele = findXMLEle(geometry, "ImgRows");
+ if (ele)
+ {
+ val = hextoi(pcdataXMLEle(ele));
+ if (val >= 1 && val <= MAXTOTALROWS)
+ cam->m_ImgRows = val;
+ }
+ else
+ cam->m_ImgRows = cam->m_Rows - cam->m_BIR - cam->m_SkipR;
+
+ // Hor Flush
+ ele = findXMLEle(geometry, "HFlush");
+ if (ele)
+ {
+ val = hextoi(pcdataXMLEle(ele));
+ if (val >= 1 && val <= MAXHBIN)
+ cam->m_HFlush = val;
+ }
+
+ // Ver Flush
+ ele = findXMLEle(geometry, "VFlush");
+ if (ele)
+ {
+ val = hextoi(pcdataXMLEle(ele));
+ if (val >= 1 && val <= MAXVBIN)
+ cam->m_VFlush = val;
+ }
+
+ // Default to full frame
+ cam->m_NumX = cam->m_ImgColumns;
+ cam->m_NumY = cam->m_ImgRows;
+
+ // Temp Control
+ ap = findXMLAtt(temp, "Control");
+ if (ap)
+ {
+ if (!strcmp(valuXMLAtt(ap), "True"))
+ cam->m_TempControl = true;
+ else
+ cam->m_TempControl = false;
+ }
+
+ // Calibration
+ ap = findXMLAtt(temp, "Cal");
+ if (ap)
+ {
+ val = hextoi(valuXMLAtt(ap));
+ if (val >= 1 && val <= 255)
+ cam->m_TempCalibration = val;
+ }
+
+ // Scale
+ ap = findXMLAtt(temp, "Scale");
+ if (ap)
+ {
+ double dval = atof(valuXMLAtt(ap));
+ if (dval >= 1.0 && dval <= 10.0)
+ cam->m_TempScale = dval;
+ }
+
+ // Target
+ ap = findXMLAtt(temp, "Target");
+ if (ap)
+ {
+ double dval = atof(valuXMLAtt(ap));
+ if (dval >= -60.0 && dval <= 40.0)
+ cam->write_CoolerSetPoint( dval );
+ else
+ cam->write_CoolerSetPoint( -10.0 ); // Default
+
+ IDLog("Target: %g\n", dval);
+ }
+
+ // Sensor
+ ap = findXMLAtt(ccd, "Sensor");
+ if (ap)
+ {
+ strncpy (cam->m_Sensor, valuXMLAtt(ap), 255);
+ IDLog("Sensor: %s\n", cam->m_Sensor);
+ }
+
+ // Color
+ ele = findXMLEle(ccd, "Color");
+ if (ele)
+ {
+ if (!strcmp(pcdataXMLEle(ele), "True"))
+ {
+ cam->m_Color = true;
+ IDLog("Color: true\n");
+ }
+ else
+ {
+ cam->m_Color = false;
+ IDLog("Color: false\n");
+ }
+ }
+
+ // Noise
+ ele = findXMLEle(ccd, "Noise");
+ if (ele)
+ cam->m_Noise = atof( pcdataXMLEle(ele) );
+
+ // Noise
+ ele = findXMLEle(ccd, "Gain");
+ if (ele)
+ cam->m_Gain = atof( pcdataXMLEle(ele) );
+
+ // Pixel X Size
+ ele = findXMLEle(ccd, "PixelXSize");
+ if (ele)
+ {
+ cam->m_PixelXSize = atof( pcdataXMLEle(ele) );
+ IDLog("Pixel X Size: %g\n", cam->m_PixelXSize);
+ }
+
+ // Pixel Y Size
+ ele = findXMLEle(ccd, "PixelYSize");
+ if (ele)
+ {
+ cam->m_PixelYSize = atof( pcdataXMLEle(ele) );
+ IDLog("Pixel Y Size: %g\n", cam->m_PixelYSize);
+ }
+
+ // Log all values
+ IDLog("Cam Row: %d - Cam Cols: %d - PP_Repeat %d\n",cam->m_Rows, cam->m_Columns, cam->m_PPRepeat);
+ 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");
+ IDLog("Max X Bin: %d - Max Y Bin: %d - Guider Relays: %s\n", cam->m_MaxBinX, cam->m_MaxBinY, cam->m_GuiderRelays ? "true" : "false");
+ 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);
+ 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);
+
+ delLilXML(XMLParser);
+
+ return true;
+}
+
+/* isCCDConnected: return 1 if we have a connection, 0 otherwise */
+int ApogeeCam::isCCDConnected(void)
+{
+ return ((PowerS[0].s == ISS_ON) ? 1 : 0);
+}
+
+FITS_HDU_LIST * ApogeeCam::create_fits_header (FITS_FILE *ofp, uint width, uint height, uint bpp)
+{
+
+ FITS_HDU_LIST *hdulist;
+
+ 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];
+ char obsDate[FITS_CARD_SIZE];
+
+ snprintf(obsDate, FITS_CARD_SIZE, "DATE-OBS= '%s' /Observation Date UTC", timestamp());
+
+ hdulist = fits_add_hdu (ofp);
+ if (hdulist == NULL) return (NULL);
+
+ hdulist->used.simple = 1;
+ hdulist->bitpix = 16;
+ hdulist->naxis = 2;
+ hdulist->naxisn[0] = width;
+ hdulist->naxisn[1] = height;
+ hdulist->naxisn[2] = bpp;
+ hdulist->used.datamin = 1;
+ hdulist->datamin = min();
+ hdulist->used.datamax = 1;
+ hdulist->datamax = max();
+ hdulist->used.bzero = 1;
+ hdulist->bzero = 0.0;
+ hdulist->used.bscale = 1;
+ hdulist->bscale = 1.0;
+
+ snprintf(temp_s, FITS_CARD_SIZE, "CCD-TEMP= %g / degrees celcius", APGFrame.temperature);
+ snprintf(expose_s, FITS_CARD_SIZE, "EXPOSURE= %d / milliseconds", APGFrame.expose);
+ snprintf(binning_s, FITS_CARD_SIZE, "BINNING = '(%d x %d)'", APGFrame.binX, APGFrame.binY);
+ snprintf(pixel_s, FITS_CARD_SIZE, "PIX-SIZ = '%0.f x %0.f microns square'", cam->m_PixelXSize, cam->m_PixelYSize);
+ switch (APGFrame.frameType)
+ {
+ case LIGHT_FRAME:
+ strcpy(frame_s, "FRAME = 'Light'");
+ break;
+ case BIAS_FRAME:
+ strcpy(frame_s, "FRAME = 'Bias'");
+ break;
+ case FLAT_FRAME:
+ strcpy(frame_s, "FRAME = 'Flat Field'");
+ break;
+ case DARK_FRAME:
+ strcpy(frame_s, "FRAME = 'Dark'");
+ break;
+ }
+
+ fits_add_card (hdulist, frame_s);
+ fits_add_card (hdulist, temp_s);
+ fits_add_card (hdulist, expose_s);
+ fits_add_card (hdulist, pixel_s);
+ fits_add_card (hdulist, "INSTRUME= 'Apogee CCD'");
+ fits_add_card (hdulist, obsDate);
+
+ return (hdulist);
+}
+
+// Convert a string to a decimal or hexadecimal integer
+// Code taken from Dave Mills Apogee driver
+unsigned short ApogeeCam::hextoi(char *instr)
+{
+ unsigned short val, tot = 0;
+ bool IsHEX = false;
+
+ long n = strlen( instr );
+ if ( n > 1 )
+ { // Look for hex format e.g. 8Fh, A3H or 0x5D
+ if ( instr[ n - 1 ] == 'h' || instr[ n - 1 ] == 'H' )
+ IsHEX = true;
+ else if ( *instr == '0' && *(instr+1) == 'x' )
+ {
+ IsHEX = true;
+ instr += 2;
+ }
+ }
+
+ if ( IsHEX )
+ {
+ while (instr && *instr && isxdigit(*instr))
+ {
+ val = *instr++ - '0';
+ if (9 < val)
+ val -= 7;
+ tot <<= 4;
+ tot |= (val & 0x0f);
+ }
+ }
+ else
+ tot = atoi( instr );
+
+ return tot;
+}
+
+double ApogeeCam::min()
+{
+ double lmin = APGFrame.img[0];
+ int ind=0, i, j;
+
+ for (i= 0; i < APGFrame.height ; i++)
+ for (j= 0; j < APGFrame.width; j++)
+ {
+ ind = (i * APGFrame.width) + j;
+ if (APGFrame.img[ind] < lmin) lmin = APGFrame.img[ind];
+ }
+
+ return lmin;
+}
+
+double ApogeeCam::max()
+{
+ double lmax = APGFrame.img[0];
+ int ind=0, i, j;
+
+ for (i= 0; i < APGFrame.height ; i++)
+ for (j= 0; j < APGFrame.width; j++)
+ {
+ ind = (i * APGFrame.width) + j;
+ if (APGFrame.img[ind] > lmax) lmax = APGFrame.img[ind];
+ }
+
+ return lmax;
+}
+