summaryrefslogtreecommitdiffstats
path: root/kstars/kstars/indi/sbigccd.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kstars/kstars/indi/sbigccd.cpp')
-rw-r--r--kstars/kstars/indi/sbigccd.cpp884
1 files changed, 884 insertions, 0 deletions
diff --git a/kstars/kstars/indi/sbigccd.cpp b/kstars/kstars/indi/sbigccd.cpp
new file mode 100644
index 00000000..fb35cf59
--- /dev/null
+++ b/kstars/kstars/indi/sbigccd.cpp
@@ -0,0 +1,884 @@
+#if 0
+ INDI driver for SBIG CCD
+ Copyright (C) 2005 Chris Curran (ccurran AT planetcurran DOT com)
+
+ Based on Apogee PPI driver by Jasem Mutlaq (mutlaqja AT ikarustech DOT 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 "sbigccd.h"
+#include "lilxml.h"
+#include "base64.h"
+
+extern char* me; /* argv[0] */
+SBIGCam *MainCam = NULL; /* Main and only camera */
+
+/* send client definitions of all properties */
+void ISInit()
+{
+ if (MainCam == NULL)
+ MainCam = new SBIGCam();
+}
+
+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*/)
+{
+
+ // We use this if we're receving binary data from the client. Most likely we won't for this driver.
+
+}
+
+SBIGCam::SBIGCam()
+{
+ initProperties();
+ IEAddTimer (POLLMS, SBIGCam::ISStaticPoll, this);
+}
+
+SBIGCam::~SBIGCam()
+{
+
+}
+
+void SBIGCam::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], "EXPOSE_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);
+
+ // We need to setup the BLOB (Binary Large Object) below. Using this property, we can send FITS to our client
+ 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;
+
+}
+
+void SBIGCam::ISGetProperties(const char */*dev*/)
+{
+
+ /* COMM_GROUP */
+ IDDefSwitch(&PowerSP, NULL);
+ IDDefBLOB(&imageBP, NULL);
+
+ /* Expose */
+ IDDefSwitch(&FrameTypeSP, NULL);
+ IDDefNumber(&ExposeTimeNP, NULL);
+ IDDefNumber(&TemperatureNP, NULL);
+
+ /* Image Group */
+ IDDefNumber(&FrameNP, NULL);
+ IDDefNumber(&BinningNP, NULL);
+
+}
+
+void SBIGCam::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;
+ }
+
+}
+
+void SBIGCam::ISNewText (const char */*dev*/, const char */*name*/, char **/*texts[]*/, char **/*names[]*/, int /*n*/)
+{
+
+}
+
+void SBIGCam::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)
+ {
+ 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 (ms) 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];
+
+ // JM: Below, tell SBIG to update to temperature to targetTemp
+ // e.g. cam->setTemp(targetTemp);
+
+
+ // Now we set property to busy and poll in ISPoll for CCD temp
+ 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;
+ }
+
+ // Subframing
+ if (!strcmp(FrameNP.name, name))
+ {
+ if (checkPowerN(&FrameNP))
+ return;
+
+ FrameNP.s = IPS_OK;
+ IUUpdateNumbers(&FrameNP, values, names, n);
+
+ // JM: Below, we setup the frame size we want to take exposure of
+ // The way it is done depends on the camera API. Example below
+ //cam->m_StartX = (int) FrameN[0].value;
+ //cam->m_StartY = (int) FrameN[1].value;
+ //cam->m_Width = (int) FrameN[2].value;
+ //cam->m_Height = (int) FrameN[3].value;
+
+ IDSetNumber(&FrameNP, NULL);
+
+ } /* end FrameNP */
+
+
+ // Binning
+ if (!strcmp(BinningNP.name, name))
+ {
+ if (checkPowerN(&BinningNP))
+ return;
+
+
+ BinningNP.s = IPS_OK;
+ IUUpdateNumbers(&BinningNP, values, names, n);
+
+ // JM: Below we set the camera binning.
+ // eg:
+ //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 SBIGCam::ISStaticPoll(void *p)
+{
+ if (!((SBIGCam *)p)->isCCDConnected())
+ {
+ IEAddTimer (POLLMS, SBIGCam::ISStaticPoll, p);
+ return;
+ }
+
+ ((SBIGCam *) p)->ISPoll();
+
+ IEAddTimer (POLLMS, SBIGCam::ISStaticPoll, p);
+}
+
+void SBIGCam::ISPoll()
+{
+ static int mtc=5;
+ int readStatus=0;
+ double ccdTemp (0);
+
+ switch (ExposeTimeNP.s)
+ {
+ case IPS_IDLE:
+ case IPS_OK:
+ break;
+
+ case IPS_BUSY:
+
+ // JM: Here we check the status of the camera (whether it's still capturing an image or has finished that)
+ // ISPoll is called once per second. e.g. below for how we do this for Apogee Cameras
+ /*
+ readStatus = cam->read_Status();
+ if (readStatus < 0)
+ {
+ IDLog("Error in exposure!\n");
+ ExposeTimeNP.s = IPS_IDLE;
+ ExposeTimeN[0].value = 0;
+ IDSetNumber(&ExposeTimeNP, "Error in exposure procedure.");
+ 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. Don't forget to call this!
+ grabImage();
+ return;
+ } */
+
+ ExposeTimeN[0].value --;
+ IDSetNumber(&ExposeTimeNP, NULL);
+ break;
+
+ case IPS_ALERT:
+ break;
+ }
+
+
+ switch (TemperatureNP.s)
+ {
+ // JM: If we're not setting a new temperature (i.e. state is either ok or idle)
+ // We simply check for the temp every 5 seconds (this is why mtc = 5 and then decremented).
+ case IPS_IDLE:
+ case IPS_OK:
+ mtc--;
+
+ if (mtc == 0)
+ {
+ //TemperatureN[0].value = cam->read_Temperature();
+ IDSetNumber(&TemperatureNP, NULL);
+ mtc = 5;
+ }
+ break;
+
+ // JM: If we're setting a new temperature, we continously check for it here untill we get
+ // a "close enough" value. This close enough value as seen below is the TEMP_THRESHOLD (0.25 C)
+ case IPS_BUSY:
+
+ // Read in current CCD temp
+ //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 SBIGCam::grabImage()
+{
+
+ long err;
+ int img_size, fd;
+ char errmsg[1024];
+ char filename[] = "/tmp/fitsXXXXXX";
+
+ if ((fd = mkstemp(filename)) < 0)
+ {
+ IDMessage(mydev, "Error making temporary filename.");
+ IDLog("Error making temporary filename.\n");
+ return;
+ }
+ close(fd);
+
+ // JM: allocate memory for buffer. In most cameras, the bit depth is 16 bit and so we use unsigned short
+ // But change if the bit depth is different
+ img_size = SBIGFrame.width * SBIGFrame.height * sizeof(unsigned short);
+
+ SBIGFrame.img = (unsigned short *) malloc (img_size);
+
+ if (SBIGFrame.img == NULL)
+ {
+ IDMessage(mydev, "Not enough memory to store image.");
+ IDLog("Not enough memory to store image.\n");
+ return;
+ }
+
+ // JM: Here we fetch the image from the camera
+ //
+ /*if (!cam->GetImage( SBIGFrame.img , SBIGFrame.width, SBIGFrame.height ))
+ {
+ free(SBIGFrame.img);
+ IDMessage(mydev, "GetImage() failed.");
+ IDLog("GetImage() failed.");
+ return;
+ }*/
+
+ err = writeFITS(filename, errmsg);
+
+ if (err)
+ {
+ free(SBIGFrame.img);
+ IDMessage(mydev, errmsg, NULL);
+ return;
+ }
+
+ free(SBIGFrame.img);
+
+}
+
+int SBIGCam::writeFITS(char *filename, char errmsg[])
+{
+
+ FITS_FILE* ofp;
+ int bpp, bpsl, width, height;
+ long nbytes;
+ FITS_HDU_LIST *hdu;
+
+ ofp = fits_open (filename, "w");
+ if (!ofp)
+ {
+ sprintf(errmsg, "Error: cannot open file for writing.");
+ return (-1);
+ }
+
+ // We're assuming bit depth is 16 (unsigned short). Change as desired.
+ width = SBIGFrame.width;
+ height = SBIGFrame.height;
+ bpp = sizeof(unsigned short); /* Bytes per Pixel */
+ bpsl = bpp * SBIGFrame.width; /* Bytes per Line */
+ nbytes = 0;
+
+ 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);
+ }
+
+ // JM: Here we're performing little endian to big endian (network order) conversion
+ // Since FITS is stored in big endian. If the camera provides the image buffer in big endian
+ // then there is no need for this conversion
+ for (int i=0; i < height; i++)
+ for (int j=0 ; j < width; j++)
+ SBIGFrame.img[width * i + j] = getBigEndian( (SBIGFrame.img[width * i + j]) );
+
+ // The "2" below is for two bytes (16 bit or unsigned short). Again, change as desired.
+ for (int i= 0; i < height ; i++)
+ {
+ fwrite(SBIGFrame.img + (i * width), 2, width, ofp->fp);
+ nbytes += bpsl;
+ }
+
+ 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);
+ }
+
+ fits_close (ofp);
+
+ /* Success */
+ ExposeTimeNP.s = IPS_OK;
+ IDSetNumber(&ExposeTimeNP, NULL);
+ IDLog("Loading FITS image...\n");
+
+ uploadFile(filename);
+
+ return 0;
+
+}
+
+void SBIGCam::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;
+
+ 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;
+ }
+
+ fitsFile = fopen(filename, "r");
+
+ if (fitsFile == NULL)
+ return;
+
+ /* #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;
+
+ /* #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;
+ }
+
+ /* #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 SBIGCam::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;
+ }
+
+ SBIGFrame.frameType = curFrame;
+ SBIGFrame.width = (int) FrameN[2].value;
+ SBIGFrame.height = (int) FrameN[3].value;
+ SBIGFrame.expose = (int) ExposeTimeN[0].value;
+ SBIGFrame.temperature = TemperatureN[0].value;
+ SBIGFrame.binX = (int) BinningN[0].value;
+ SBIGFrame.binY = (int) BinningN[1].value;
+
+ ExposeTimeNP.s = IPS_BUSY;
+
+ IDSetNumber(&ExposeTimeNP, "Taking a %g seconds frame...", ExposeTimeN[0].value);
+ IDLog("Taking a frame...\n");
+
+}
+
+/* Retrieves basic data from the CCD upon connection like temperature, array size, firmware..etc */
+void SBIGCam::getBasicData()
+{
+
+
+ // Maximum resolution
+
+ // JM: Update here basic variables upon connection.
+
+ /*
+ 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);
+
+ // Current Temperature
+ TemperatureN[0].value = cam->read_Temperature();
+ IDSetNumber(&TemperatureNP, NULL);*/
+
+}
+
+int SBIGCam::getOnSwitch(ISwitchVectorProperty *sp)
+{
+ for (int i=0; i < sp->nsp ; i++)
+ {
+ if (sp->sp[i].s == ISS_ON)
+ return i;
+ }
+
+ return -1;
+}
+
+int SBIGCam::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 SBIGCam::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 SBIGCam::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 SBIGCam::connectCCD()
+{
+
+ 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.");
+
+ // JM: Disconnect cam and clean up
+ // e.g. disConnectSBIG();
+ break;
+ }
+
+}
+
+bool SBIGCam::initCamera()
+{
+ // JM: Here, we do all camera initilization stuff and we establish a connection to the camera
+ // If everything goes well, we return true, otherwise false
+ // Please use IDLog(...) to report errors.
+
+ return false;
+}
+
+/* isCCDConnected: return 1 if we have a connection, 0 otherwise */
+int SBIGCam::isCCDConnected(void)
+{
+ return ((PowerS[0].s == ISS_ON) ? 1 : 0);
+}
+
+FITS_HDU_LIST * SBIGCam::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], pixel_s[FITS_CARD_SIZE], frame_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;
+ // JM: Record here the minimum and maximum pixel values
+ /*hdulist->used.datamin = min();
+ hdulist->datamin = min();
+ hdulist->used.datamax = max();
+ 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", SBIGFrame.temperature);
+ snprintf(expose_s, FITS_CARD_SIZE, "EXPOSURE= %d / milliseconds", SBIGFrame.expose);
+ snprintf(binning_s, FITS_CARD_SIZE, "BINNING = '(%d x %d)'", SBIGFrame.binX, SBIGFrame.binY);
+ //sprintf(pixel_s, "PIX-SIZ = '%.0f microns square'", PixelSizeN[0].value);
+ switch (SBIGFrame.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);
+
+ // JM: If there is a way to get the specific model of the camera, use that.
+ fits_add_card (hdulist, "INSTRUME= 'SBIG CCD'");
+ fits_add_card (hdulist, obsDate);
+
+ return (hdulist);
+}
+