You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdeedu/kstars/kstars/indi/apogee_ppi.cpp

1525 lines
37 KiB

#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/trinity/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 readtqStatus=0;
double ccdTemp;
if (!isCCDConnected())
return;
switch (ExposeTimeNP.s)
{
case IPS_IDLE:
case IPS_OK:
break;
case IPS_BUSY:
readtqStatus = cam->read_tqStatus();
if (readtqStatus < 0)
{
IDLog("Error in exposure! Read status: %d\n", readtqStatus);
ExposeTimeNP.s = IPS_ALERT;
ExposeTimeN[0].value = 0;
IDSetNumber(&ExposeTimeNP, "Error in exposure procedure. Read states: %d", readtqStatus);
return;
}
else if (readtqStatus == 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, *tqgeometry = NULL, *temp = NULL, *ccd = NULL;
XMLAtt *ap;
FILE *spFile = NULL;
char errmsg[1024];
spFile = fopen(TOP_DATADIR"/apogee_caminfo.xml", "r");
//spFile = fopen("/opt/trinity/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");
tqgeometry = 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 (tqgeometry == 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(tqgeometry, "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(tqgeometry, "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(tqgeometry, "BIC");
if (ele)
{
val = hextoi(pcdataXMLEle(ele));
if (val >= 1 && val <= MAXCOLUMNS)
cam->m_BIC = val;
}
// BIR
ele = findXMLEle(tqgeometry, "BIR");
if (ele)
{
val = hextoi(pcdataXMLEle(ele));
if (val >= 1 && val <= MAXROWS)
cam->m_BIR = val;
}
// SKIPC
ele = findXMLEle(tqgeometry, "SKIPC");
if (ele)
{
val = hextoi(pcdataXMLEle(ele));
if (val >= 1 && val <= MAXCOLUMNS)
cam->m_SkipC = val;
}
// SKIPR
ele = findXMLEle(tqgeometry, "SKIPR");
if (ele)
{
val = hextoi(pcdataXMLEle(ele));
if (val >= 1 && val <= MAXROWS)
cam->m_SkipR = val;
}
// IMG COlS
ele = findXMLEle(tqgeometry, "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(tqgeometry, "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(tqgeometry, "HFlush");
if (ele)
{
val = hextoi(pcdataXMLEle(ele));
if (val >= 1 && val <= MAXHBIN)
cam->m_HFlush = val;
}
// Ver Flush
ele = findXMLEle(tqgeometry, "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;
}