3
INDI Interface for Finger Lakes Instruments CCDs
4
Copyright (C) 2003 Jasem Mutlaq (mutlaqja@ikarustech.com)
6
This library is free software; you can redistribute it and/or
7
modify it under the terms of the GNU Lesser General Public
8
License as published by the Free Software Foundation; either
9
version 2.1 of the License, or (at your option) any later version.
11
This library is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14
Lesser General Public License for more details.
16
You should have received a copy of the GNU Lesser General Public
17
License along with this library; if not, write to the Free Software
18
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
33
#include <sys/types.h>
34
#include <sys/socket.h>
35
#include <netinet/in.h>
42
#include "indidevapi.h"
43
#include "eventloop.h"
47
void getBasicData(void);
49
void handleExposure(void *);
50
void connectCCD(void);
51
void getBasicData(void);
52
void uploadFile(const char* filename);
53
int writeFITS(const char* filename, char errmsg[]);
54
int findcam(flidomain_t domain);
55
int setImageArea(char errmsg[]);
56
int manageDefaults(char errmsg[]);
58
int checkPowerS(ISwitchVectorProperty *sp);
59
int checkPowerN(INumberVectorProperty *np);
60
int checkPowerT(ITextVectorProperty *tp);
61
int getOnSwitch(ISwitchVectorProperty *sp);
62
int isCCDConnected(void);
63
void addFITSKeywords(fitsfile *fptr);
71
#define mydev "FLI CCD"
73
#define COMM_GROUP "Communication"
74
#define EXPOSE_GROUP "Expose"
75
#define IMAGE_GROUP "Image Settings"
76
#define DATA_GROUP "Data Channel"
78
#define MAX_CCD_TEMP 45 /* Max CCD temperature */
79
#define MIN_CCD_TEMP -55 /* Min CCD temperature */
80
#define MAX_X_BIN 16. /* Max Horizontal binning */
81
#define MAX_Y_BIN 16. /* Max Vertical binning */
82
#define MAX_PIXELS 4096 /* Max number of pixels in one dimension */
83
#define POLLMS 1000 /* Polling time (ms) */
84
#define TEMP_THRESHOLD .25 /* Differential temperature threshold (C)*/
85
#define NFLUSHES 1 /* Number of times a CCD array is flushed before an exposure */
87
#define FILENAMESIZ 2048
88
#define LIBVERSIZ 1024
90
#define PIPEBUFSIZ 8192
92
#define TEMPFILE_LEN 16
94
enum FLIFrames { LIGHT_FRAME = 0, BIAS_FRAME, DARK_FRAME, FLAT_FRAME };
107
long Visible_Area[4];
120
/*static int streamTimerID; Stream ID */
122
static flidev_t fli_dev;
123
static cam_t *FLICam;
124
static img_t *FLIImg;
125
static int portSwitchIndex;
127
long int Domains[] = { FLIDOMAIN_USB, FLIDOMAIN_SERIAL, FLIDOMAIN_PARALLEL_PORT, FLIDOMAIN_INET };
131
/* Connect/Disconnect */
132
static ISwitch ConnectS[] = {{"CONNECT" , "Connect" , ISS_OFF, 0, 0},{"DISCONNECT", "Disconnect", ISS_ON, 0, 0}};
133
static ISwitchVectorProperty ConnectSP = { mydev, "CONNECTION" , "Connection", COMM_GROUP, IP_RW, ISR_1OFMANY, 60, IPS_IDLE, ConnectS, NARRAY(ConnectS), "", 0};
136
static ISwitch PortS[] = {{"USB", "", ISS_ON, 0, 0}, {"Serial", "", ISS_OFF, 0, 0}, {"Parallel", "", ISS_OFF, 0, 0}, {"INet", "", ISS_OFF, 0, 0}};
137
static ISwitchVectorProperty PortSP = { mydev, "Port Type", "", COMM_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, PortS, NARRAY(PortS), "", 0};
139
/* Types of Frames */
140
static ISwitch FrameTypeS[] = { {"FRAME_LIGHT", "Light", ISS_ON, 0, 0}, {"FRAME_BIAS", "Bias", ISS_OFF, 0, 0}, {"FRAME_DARK", "Dark", ISS_OFF, 0, 0}, {"FRAME_FLAT", "Flat Field", ISS_OFF, 0, 0}};
141
static ISwitchVectorProperty FrameTypeSP = { mydev, "CCD_FRAME_TYPE", "Frame Type", EXPOSE_GROUP, IP_RW, ISR_1OFMANY, 0, IPS_IDLE, FrameTypeS, NARRAY(FrameTypeS), "", 0};
143
/* Frame coordinates. Full frame is default */
144
static INumber FrameN[] = {
145
{ "X", "X", "%.0f", 0., MAX_PIXELS, 1., 0., 0, 0, 0},
146
{ "Y", "Y", "%.0f", 0., MAX_PIXELS, 1., 0., 0, 0, 0},
147
{ "WIDTH", "Width", "%.0f", 0., MAX_PIXELS, 1., 0., 0, 0, 0},
148
{ "HEIGHT", "Height", "%.0f",0., MAX_PIXELS, 1., 0., 0, 0, 0}};
149
static INumberVectorProperty FrameNP = { mydev, "CCD_FRAME", "Frame", IMAGE_GROUP, IP_RW, 60, IPS_IDLE, FrameN, NARRAY(FrameN), "", 0};
152
static INumber BinningN[] = {
153
{ "HOR_BIN", "X", "%0.f", 1., MAX_X_BIN, 1., 1., 0, 0, 0},
154
{ "VER_BIN", "Y", "%0.f", 1., MAX_Y_BIN, 1., 1., 0, 0, 0}};
155
static INumberVectorProperty BinningNP = { mydev, "CCD_BINNING", "Binning", IMAGE_GROUP, IP_RW, 60, IPS_IDLE, BinningN, NARRAY(BinningN), "", 0};
158
static INumber ExposeTimeWN[] = {{ "CCD_EXPOSURE_VALUE", "Duration (s)", "%5.2f", 0., 36000., .5, 1., 0, 0, 0}};
159
static INumberVectorProperty ExposeTimeWNP = { mydev, "CCD_EXPOSURE_REQUEST", "Expose", EXPOSE_GROUP, IP_WO, 36000, IPS_IDLE, ExposeTimeWN, NARRAY(ExposeTimeWN), "", 0};
161
static INumber ExposeTimeRN[] = {{ "CCD_EXPOSURE_VALUE", "Duration (s)", "%5.2f", 0., 36000., .5, 1., 0, 0, 0}};
162
static INumberVectorProperty ExposeTimeRNP = { mydev, "CCD_EXPOSURE", "Expose", EXPOSE_GROUP, IP_RO, 36000, IPS_IDLE, ExposeTimeRN, NARRAY(ExposeTimeRN), "", 0};
164
/* Temperature control */
165
static INumber TemperatureN[] = { {"CCD_TEMPERATURE_VALUE", "Temperature", "%+06.2f", MIN_CCD_TEMP, MAX_CCD_TEMP, .2, 0., 0, 0, 0}};
166
static INumberVectorProperty TemperatureNP = { mydev, "CCD_TEMPERATURE", "Temperature (C)", EXPOSE_GROUP, IP_RW, 60, IPS_IDLE, TemperatureN, NARRAY(TemperatureN), "", 0};
168
/* Pixel size (µm) */
169
static INumber PixelSizeN[] = {
170
{ "Width", "", "%.0f", 0. , 0., 0., 0., 0, 0, 0},
171
{ "Height", "", "%.0f", 0. , 0., 0., 0., 0, 0, 0}};
172
static INumberVectorProperty PixelSizeNP = { mydev, "Pixel Size (µm)", "", DATA_GROUP, IP_RO, 0, IPS_IDLE, PixelSizeN, NARRAY(PixelSizeN), "", 0};
174
/* BLOB for sending image */
175
static IBLOB imageB = {"FITS_BLOB", "FITS", "", 0, 0, 0, 0, 0, 0, 0};
176
static IBLOBVectorProperty imageBP = {mydev, "CCD_FITS_BLOB", "BLOB", COMM_GROUP,
177
IP_RO, 0, IPS_IDLE, &imageB, 1, "", 0};
179
/* send client definitions of all properties */
187
/* USB by default {USB, SERIAL, PARALLEL, INET} */
190
FLIImg = malloc (sizeof(img_t));
194
IDMessage(mydev, "Error: unable to initialize driver. Low memory.");
195
IDLog("Error: unable to initialize driver. Low memory.");
199
IEAddTimer (POLLMS, ISPoll, NULL);
205
void ISGetProperties (const char *dev)
210
if (dev && strcmp (mydev, dev))
214
IDDefSwitch(&ConnectSP, NULL);
215
IDDefSwitch(&PortSP, NULL);
216
IDDefBLOB(&imageBP, NULL);
219
IDDefSwitch(&FrameTypeSP, NULL);
220
IDDefNumber(&ExposeTimeWNP, NULL);
221
IDDefNumber(&ExposeTimeRNP, NULL);
222
IDDefNumber(&TemperatureNP, NULL);
225
IDDefNumber(&FrameNP, NULL);
226
IDDefNumber(&BinningNP, NULL);
230
void ISNewBLOB (const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n) {}
231
void ISSnoopDevice (XMLEle *root) {}
233
void ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n)
239
/* ignore if not ours */
240
if (dev && strcmp (dev, mydev))
246
if (!strcmp (name, PortSP.name))
249
IUResetSwitch(&PortSP);
250
IUUpdateSwitch(&PortSP, states, names, n);
251
portSwitchIndex = getOnSwitch(&PortSP);
254
IDSetSwitch(&PortSP, NULL);
259
if (!strcmp (name, ConnectSP.name))
261
if (IUUpdateSwitch(&ConnectSP, states, names, n) < 0)
268
if (!strcmp(FrameTypeSP.name, name))
270
if (checkPowerS(&FrameTypeSP))
273
for (i = 0; i < n ; i++)
275
sp = IUFindSwitch(&FrameTypeSP, names[i]);
279
FrameTypeSP.s = IPS_ALERT;
280
IDSetSwitch(&FrameTypeSP, "Unknown error. %s is not a member of %s property.", names[0], name);
284
/* NORMAL, BIAS, or FLAT */
285
if ( (sp == &FrameTypeS[LIGHT_FRAME] || sp == &FrameTypeS[FLAT_FRAME]) && states[i] == ISS_ON)
287
if (sp == &FrameTypeS[LIGHT_FRAME])
288
FLIImg->frameType = LIGHT_FRAME;
290
FLIImg->frameType = FLAT_FRAME;
292
if ((err = FLISetFrameType(fli_dev, FLI_FRAME_TYPE_NORMAL) ))
294
IUResetSwitch(&FrameTypeSP);
295
FrameTypeS[LIGHT_FRAME].s = ISS_ON;
296
FrameTypeSP.s = IPS_ALERT;
297
IDSetSwitch(&FrameTypeSP, "FLISetFrameType() failed. %s.\n", strerror((int)-err));
298
IDLog("FLISetFrameType() failed. %s.\n", strerror((int)-err));
302
IUResetSwitch(&FrameTypeSP);
304
FrameTypeSP.s = IPS_OK;
305
IDSetSwitch(&FrameTypeSP, NULL);
309
else if ( (sp == &FrameTypeS[DARK_FRAME] || sp == &FrameTypeS[BIAS_FRAME]) && states[i] == ISS_ON)
312
if (sp == &FrameTypeS[DARK_FRAME])
313
FLIImg->frameType = DARK_FRAME;
315
FLIImg->frameType = BIAS_FRAME;
317
if ((err = FLISetFrameType(fli_dev, FLI_FRAME_TYPE_DARK) ))
319
IUResetSwitch(&FrameTypeSP);
320
FrameTypeS[LIGHT_FRAME].s = ISS_ON;
321
FrameTypeSP.s = IPS_ALERT;
322
IDSetSwitch(&FrameTypeSP, "FLISetFrameType() failed. %s.\n", strerror((int)-err));
323
IDLog("FLISetFrameType() failed. %s.\n", strerror((int)-err));
327
IUResetSwitch(&FrameTypeSP);
329
FrameTypeSP.s = IPS_OK;
330
IDSetSwitch(&FrameTypeSP, NULL);
341
void ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n)
345
/* ignore if not ours */
346
if (dev && strcmp (mydev, dev))
349
/* suppress warning */
350
n=n; dev=dev; name=name; names=names; texts=texts;
355
void ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n)
360
char errmsg[ERRMSG_SIZE];
362
/* ignore if not ours */
363
if (dev && strcmp (dev, mydev))
369
if (!strcmp (ExposeTimeWNP.name, name))
371
if (checkPowerN(&ExposeTimeWNP))
374
if (ExposeTimeWNP.s == IPS_BUSY)
376
if ( (err = FLICancelExposure(fli_dev)))
378
ExposeTimeWNP.s = IPS_ALERT;
379
IDSetNumber(&ExposeTimeWNP, "FLICancelExposure() failed. %s.", strerror((int)-err));
380
IDLog("FLICancelExposure() failed. %s.\n", strerror((int)-err));
384
ExposeTimeWNP.s = IPS_IDLE;
385
ExposeTimeRNP.s = IPS_IDLE;
386
ExposeTimeRN[0].value = 0;
388
IDSetNumber(&ExposeTimeWNP, "Exposure cancelled.");
389
IDSetNumber(&ExposeTimeRNP, NULL);
390
IDLog("Exposure Cancelled.\n");
394
np = IUFindNumber(&ExposeTimeWNP, names[0]);
398
ExposeTimeWNP.s = IPS_ALERT;
399
IDSetNumber(&ExposeTimeWNP, "Error: %s is not a member of %s property.", names[0], name);
403
np->value = values[0];
404
FLIImg->expose = (int) (values[0] * 1000.);
407
if ( (err = FLISetExposureTime(fli_dev, np->value * 1000.) ))
409
ExposeTimeWNP.s = IPS_ALERT;
410
IDSetNumber(&ExposeTimeWNP, "FLISetExposureTime() failed. %s.\n", strerror((int)-err));
411
IDLog("FLISetExposureTime() failed. %s.\n", strerror((int)-err));
415
IDLog("Exposure Time (ms) is: %g\n", np->value * 1000.);
417
handleExposure(NULL);
422
if (!strcmp(TemperatureNP.name, name))
424
if (checkPowerN(&TemperatureNP))
427
TemperatureNP.s = IPS_IDLE;
429
np = IUFindNumber(&TemperatureNP, names[0]);
433
IDSetNumber(&TemperatureNP, "Unknown error. %s is not a member of %s property.", names[0], name);
437
if (values[0] < MIN_CCD_TEMP || values[0] > MAX_CCD_TEMP)
439
IDSetNumber(&TemperatureNP, "Error: valid range of temperature is from %d to %d", MIN_CCD_TEMP, MAX_CCD_TEMP);
443
if ( (err = FLISetTemperature(fli_dev, values[0])))
445
IDSetNumber(&TemperatureNP, "FLISetTemperature() failed. %s.", strerror((int)-err));
446
IDLog("FLISetTemperature() failed. %s.", strerror((int)-err));
450
FLICam->temperature = values[0];
451
TemperatureNP.s = IPS_BUSY;
453
IDSetNumber(&TemperatureNP, "Setting CCD temperature to %+06.2f C", values[0]);
454
IDLog("Setting CCD temperature to %+06.2f C\n", values[0]);
458
if (!strcmp(FrameNP.name, name))
462
if (checkPowerN(&FrameNP))
465
FrameNP.s = IPS_IDLE;
467
for (i=0; i < n ; i++)
469
np = IUFindNumber(&FrameNP, names[i]);
473
IDSetNumber(&FrameNP, "Unknown error. %s is not a member of %s property.", names[0], name);
478
if (np == &FrameN[0] || np==&FrameN[2])
480
if (values[i] < 0 || values[i] > FLICam->width)
484
np->value = values[i];
487
else if (np == &FrameN[1] || np==&FrameN[3])
489
if (values[i] < 0 || values[i] > FLICam->height)
493
np->value = values[i];
499
IDSetNumber(&FrameNP, "Invalid range. Valid range is (0,0) - (%0d,%0d)", FLICam->width, FLICam->height);
500
IDLog("Invalid range. Valid range is (0,0) - (%0d,%0d)", FLICam->width, FLICam->height);
504
if (setImageArea(errmsg))
506
IDSetNumber(&FrameNP, "%s", errmsg);
512
/* Adjusting image width and height */
513
FLIImg->width = FrameN[2].value;
514
FLIImg->height = FrameN[3].value;
516
IDSetNumber(&FrameNP, NULL);
521
if (!strcmp(BinningNP.name, name))
523
if (checkPowerN(&BinningNP))
526
BinningNP.s = IPS_IDLE;
528
for (i=0 ; i < n ; i++)
530
np = IUFindNumber(&BinningNP, names[i]);
534
IDSetNumber(&BinningNP, "Unknown error. %s is not a member of %s property.", names[0], name);
539
if (np == &BinningN[0])
541
if (values[i] < 1 || values[i] > MAX_X_BIN)
543
IDSetNumber(&BinningNP, "Error: Valid X bin values are from 1 to %g", MAX_X_BIN);
544
IDLog("Error: Valid X bin values are from 1 to %g", MAX_X_BIN);
548
if ( (err = FLISetHBin(fli_dev, values[i])))
550
IDSetNumber(&BinningNP, "FLISetHBin() failed. %s.", strerror((int)-err));
551
IDLog("FLISetHBin() failed. %s.", strerror((int)-err));
555
np->value = values[i];
557
else if (np == &BinningN[1])
559
if (values[i] < 1 || values[i] > MAX_Y_BIN)
561
IDSetNumber(&BinningNP, "Error: Valid Y bin values are from 1 to %g", MAX_Y_BIN);
562
IDLog("Error: Valid X bin values are from 1 to %g", MAX_Y_BIN);
566
if ( (err = FLISetVBin(fli_dev, values[i])))
568
IDSetNumber(&BinningNP, "FLISetVBin() failed. %s.", strerror((int)-err));
569
IDLog("FLISetVBin() failed. %s.", strerror((int)-err));
573
np->value = values[i];
577
if (setImageArea(errmsg))
579
IDSetNumber(&BinningNP, errmsg, NULL);
584
BinningNP.s = IPS_OK;
586
IDLog("Binning is: %.0f x %.0f\n", BinningN[0].value, BinningN[1].value);
588
IDSetNumber(&BinningNP, NULL);
601
if (!isCCDConnected())
603
IEAddTimer (POLLMS, ISPoll, NULL);
607
/*IDLog("In Poll.\n");*/
609
switch (ExposeTimeWNP.s)
618
if ( (err = FLIGetExposureStatus(fli_dev, &timeleft)))
620
ExposeTimeWNP.s = IPS_ALERT;
621
ExposeTimeRN[0].value = 0;
623
IDSetNumber(&ExposeTimeWNP, "FLIGetExposureStatus() failed. %s.", strerror((int)-err));
624
IDSetNumber(&ExposeTimeRNP, NULL);
625
IDLog("FLIGetExposureStatus() failed. %s.\n", strerror((int)-err));
631
ExposeTimeRN[0].value = timeleft / 1000.;
632
IDSetNumber(&ExposeTimeRNP, NULL);
636
/* We're done exposing */
637
/*ExposeTimeWNP.s = IPS_OK;*/
638
ExposeTimeRNP.s = IPS_OK;
639
ExposeTimeRN[0].value = 0;
640
IDSetNumber(&ExposeTimeWNP, "Exposure done, downloading image...");
641
IDSetNumber(&ExposeTimeRNP, NULL);
642
IDLog("Exposure done, downloading image...\n");
644
/* grab and save image */
653
switch (TemperatureNP.s)
657
if ( (err = FLIGetTemperature(fli_dev, &ccdTemp)))
659
TemperatureNP.s = IPS_IDLE;
660
IDSetNumber(&TemperatureNP, "FLIGetTemperature() failed. %s.", strerror((int)-err));
661
IDLog("FLIGetTemperature() failed. %s.", strerror((int)-err));
665
if (fabs(TemperatureN[0].value - ccdTemp) >= TEMP_THRESHOLD)
667
TemperatureN[0].value = ccdTemp;
668
IDSetNumber(&TemperatureNP, NULL);
673
if ((err = FLIGetTemperature(fli_dev, &ccdTemp)))
675
TemperatureNP.s = IPS_ALERT;
676
IDSetNumber(&TemperatureNP, "FLIGetTemperature() failed. %s.", strerror((int)-err));
677
IDLog("FLIGetTemperature() failed. %s.", strerror((int)-err));
681
if (fabs(FLICam->temperature - ccdTemp) <= TEMP_THRESHOLD)
682
TemperatureNP.s = IPS_OK;
684
TemperatureN[0].value = ccdTemp;
685
IDSetNumber(&TemperatureNP, NULL);
694
IEAddTimer (POLLMS, ISPoll, NULL);
697
/* Sets the Image area that the CCD will scan and download.
698
We compensate for binning. */
699
int setImageArea(char errmsg[])
702
long x_1, y_1, x_2, y_2;
705
/* Add the X and Y offsets */
706
x_1 = FrameN[0].value + FLICam->Visible_Area[0];
707
y_1 = FrameN[1].value + FLICam->Visible_Area[1];
709
x_2 = x_1 + (FrameN[2].value / BinningN[0].value);
710
y_2 = y_1 + (FrameN[3].value / BinningN[1].value);
712
if (x_2 > FLICam->Visible_Area[2])
713
x_2 = FLICam->Visible_Area[2];
715
if (y_2 > FLICam->Visible_Area[3])
716
y_2 = FLICam->Visible_Area[3];
718
IDLog("The Final image area is (%ld, %ld), (%ld, %ld)\n", x_1, y_1, x_2, y_2);
720
FLIImg->width = x_2 - x_1;
721
FLIImg->height = y_2 - y_1;
723
if ( (err = FLISetImageArea(fli_dev, x_1, y_1, x_2, y_2) ))
725
snprintf(errmsg, ERRMSG_SIZE, "FLISetImageArea() failed. %s.\n", strerror((int)-err));
733
/* Downloads the image from the CCD row by row and store them
735
N.B. No processing is done on the image */
740
char errmsg[ERRMSG_SIZE];
741
char filename[TEMPFILE_LEN] = "/tmp/fitsXXXXXX";
743
if ((fd = mkstemp(filename)) < 0)
745
IDMessage(mydev, "Error making temporary filename.");
746
IDLog("Error making temporary filename.\n");
751
img_size = FLIImg->width * FLIImg->height * sizeof(unsigned short);
753
FLIImg->img = malloc (img_size);
755
if (FLIImg->img == NULL)
757
IDMessage(mydev, "Not enough memory to store image.");
758
IDLog("Not enough memory to store image.\n");
762
for (i=0; i < FLIImg->height ; i++)
764
if ( (err = FLIGrabRow(fli_dev, &FLIImg->img[i * FLIImg->width], FLIImg->width)))
767
IDMessage(mydev, "FLIGrabRow() failed at row %d. %s.", i, strerror((int)-err));
768
IDLog("FLIGrabRow() failed at row %d. %s.\n", i, strerror((int)-err));
773
IDMessage(mydev, "Download complete.\n");
775
/*err = (ImageFormatS[0].s == ISS_ON) ? writeFITS(FileNameT[0].text, errmsg) : writeRAW(FileNameT[0].text, errmsg);*/
776
err = writeFITS(filename, errmsg);
781
IDMessage(mydev, errmsg, NULL);
790
int writeFITS(const char* filename, char errmsg[])
792
fitsfile *fptr; /* pointer to the FITS file; defined in fitsio.h */
794
long fpixel = 1, naxis = 2, nelements;
796
char filename_rw[TEMPFILE_LEN+1];
798
naxes[0] = FLIImg->width;
799
naxes[1] = FLIImg->height;
801
/* Append ! to file name to over write it.*/
802
snprintf(filename_rw, TEMPFILE_LEN+1, "!%s", filename);
804
status = 0; /* initialize status before calling fitsio routines */
805
fits_create_file(&fptr, filename_rw, &status); /* create new file */
807
/* Create the primary array image (16-bit short integer pixels */
808
fits_create_img(fptr, USHORT_IMG, naxis, naxes, &status);
810
addFITSKeywords(fptr);
812
nelements = naxes[0] * naxes[1]; /* number of pixels to write */
814
/* Write the array of integers to the image */
815
fits_write_img(fptr, TUSHORT, fpixel, nelements, FLIImg->img, &status);
817
fits_close_file(fptr, &status); /* close the file */
819
fits_report_error(stderr, status); /* print out any error messages */
822
ExposeTimeWNP.s = IPS_OK;
823
IDSetNumber(&ExposeTimeWNP, NULL);
824
uploadFile(filename);
830
void addFITSKeywords(fitsfile *fptr)
835
double min_val, max_val;
837
/*pixel_size = (float) PixelSizeN[0].value;
840
temp = (float) TemperatureN[0].value;
841
expose = (float) FLIImg->expose;*/
843
snprintf(binning_s, 32, "(%g x %g)", BinningN[0].value, BinningN[1].value);
845
switch (FLIImg->frameType)
848
strcpy(frame_s, "Light");
851
strcpy(frame_s, "Bias");
854
strcpy(frame_s, "Flat Field");
857
strcpy(frame_s, "Dark");
861
fits_update_key(fptr, TDOUBLE, "CCD-TEMP", &(TemperatureN[0].value), "CCD Temperature (Celcius)", &status);
862
fits_update_key(fptr, TDOUBLE, "EXPOSURE", &(FLIImg->expose), "Total Exposure Time (ms)", &status);
863
fits_update_key(fptr, TDOUBLE, "PIX-SIZ", &(PixelSizeN[0].value), "Pixel Size (microns)", &status);
864
fits_update_key(fptr, TSTRING, "BINNING", binning_s, "Binning HOR x VER", &status);
865
fits_update_key(fptr, TSTRING, "FRAME", frame_s, "Frame Type", &status);
866
fits_update_key(fptr, TDOUBLE, "DATAMIN", &min_val, "Minimum value", &status);
867
fits_update_key(fptr, TDOUBLE, "DATAMAX", &max_val, "Maximum value", &status);
868
fits_update_key(fptr, TSTRING, "INSTRUME", "Finger Lakes Instruments", "CCD Name", &status);
869
fits_write_date(fptr, &status);
873
void uploadFile(const char* filename)
876
unsigned char *fitsData, *compressedData;
878
unsigned int i =0, nr = 0;
879
uLongf compressedBytes=0;
883
if ( -1 == stat (filename, &stat_p))
885
IDLog(" Error occurred attempting to stat file.\n");
889
totalBytes = stat_p.st_size;
890
fitsData = (unsigned char *) malloc (sizeof(unsigned char) * totalBytes);
891
compressedData = (unsigned char *) malloc (sizeof(unsigned char) * totalBytes + totalBytes / 64 + 16 + 3);
893
if (fitsData == NULL || compressedData == NULL)
895
if (fitsData) free(fitsData);
896
if (compressedData) free(compressedData);
897
IDLog("Error! low memory. Unable to initialize fits buffers.\n");
901
fitsFile = fopen(filename, "r");
903
if (fitsFile == NULL)
906
/* #1 Read file from disk */
907
for (i=0; i < totalBytes; i+= nr)
909
nr = fread(fitsData + i, 1, totalBytes - i, fitsFile);
913
IDLog("Error reading temporary FITS file.\n");
919
compressedBytes = sizeof(char) * totalBytes + totalBytes / 64 + 16 + 3;
922
r = compress2(compressedData, &compressedBytes, fitsData, totalBytes, 9);
925
/* this should NEVER happen */
926
IDLog("internal error - compression failed: %d\n", r);
931
imageB.blob = compressedData;
932
imageB.bloblen = compressedBytes;
933
imageB.size = totalBytes;
934
strcpy(imageB.format, ".fits.z");
936
IDSetBLOB (&imageBP, NULL);
939
free (compressedData);
943
/* Initiates the exposure procedure */
944
void handleExposure(void *p)
951
/* BIAS frame is the same as DARK but with minimum period. i.e. readout from camera electronics.
953
if (FLIImg->frameType == BIAS_FRAME)
955
if ((err = FLISetExposureTime(fli_dev, 50)))
957
ExposeTimeWNP.s = IPS_ALERT;
958
IDSetNumber(&ExposeTimeWNP, "FLISetExposureTime() failed. %s.\n", strerror((int)-err));
959
IDLog("FLISetExposureTime() failed. %s.\n", strerror((int)-err));
964
if ((err = FLIExposeFrame(fli_dev)))
966
ExposeTimeWNP.s = IPS_ALERT;
967
IDSetNumber(&ExposeTimeWNP, "FLIExposeFrame() failed. %s.", strerror((int)-err));
968
IDLog("FLIExposeFrame() failed. %s.\n", strerror((int)-err));
972
ExposeTimeWNP.s = IPS_BUSY;
974
IDSetNumber(&ExposeTimeWNP, "Taking a %g seconds frame...", FLIImg->expose / 1000.);
976
IDLog("Taking a frame...\n");
979
/* Retrieves basic data from the CCD upon connection like temperature, array size, firmware..etc */
986
IDLog("In getBasicData()\n");
988
if ((err = FLIGetModel (fli_dev, buff, 2048)))
990
IDMessage(mydev, "FLIGetModel() failed. %s.", strerror((int)-err));
991
IDLog("FLIGetModel() failed. %s.\n", strerror((int)-err));
996
if ( (FLICam->model = malloc (sizeof(char) * 2048)) == NULL)
998
IDMessage(mydev, "malloc() failed.");
999
IDLog("malloc() failed.");
1003
strcpy(FLICam->model, buff);
1006
if (( err = FLIGetHWRevision(fli_dev, &FLICam->HWRevision)))
1008
IDMessage(mydev, "FLIGetHWRevision() failed. %s.", strerror((int)-err));
1009
IDLog("FLIGetHWRevision() failed. %s.\n", strerror((int)-err));
1014
if (( err = FLIGetFWRevision(fli_dev, &FLICam->FWRevision)))
1016
IDMessage(mydev, "FLIGetFWRevision() failed. %s.", strerror((int)-err));
1017
IDLog("FLIGetFWRevision() failed. %s.\n", strerror((int)-err));
1021
if (( err = FLIGetPixelSize(fli_dev, &FLICam->x_pixel_size, &FLICam->y_pixel_size)))
1023
IDMessage(mydev, "FLIGetPixelSize() failed. %s.", strerror((int)-err));
1024
IDLog("FLIGetPixelSize() failed. %s.\n", strerror((int)-err));
1028
FLICam->x_pixel_size *= 1e6;
1029
FLICam->y_pixel_size *= 1e6;
1031
if (( err = FLIGetArrayArea(fli_dev, &FLICam->Array_Area[0], &FLICam->Array_Area[1], &FLICam->Array_Area[2], &FLICam->Array_Area[3])))
1033
IDMessage(mydev, "FLIGetArrayArea() failed. %s.", strerror((int)-err));
1034
IDLog("FLIGetArrayArea() failed. %s.\n", strerror((int)-err));
1038
if (( err = FLIGetVisibleArea( fli_dev, &FLICam->Visible_Area[0], &FLICam->Visible_Area[1], &FLICam->Visible_Area[2], &FLICam->Visible_Area[3])))
1040
IDMessage(mydev, "FLIGetVisibleArea() failed. %s.", strerror((int)-err));
1041
IDLog("FLIGetVisibleArea() failed. %s.\n", strerror((int)-err));
1044
if (( err = FLIGetTemperature(fli_dev, &FLICam->temperature)))
1046
IDMessage(mydev, "FLIGetTemperature() failed. %s.", strerror((int)-err));
1047
IDLog("FLIGetTemperature() failed. %s.\n", strerror((int)-err));
1051
IDLog("The CCD Temperature is %f.\n", FLICam->temperature);
1053
PixelSizeN[0].value = FLICam->x_pixel_size; /* Pixel width (um) */
1054
PixelSizeN[1].value = FLICam->y_pixel_size; /* Pixel height (um) */
1055
TemperatureN[0].value = FLICam->temperature; /* CCD chip temperatre (degrees C) */
1056
FrameN[0].value = 0; /* X */
1057
FrameN[1].value = 0; /* Y */
1058
FrameN[2].value = FLICam->Visible_Area[2] - FLICam->Visible_Area[0]; /* Frame Width */
1059
FrameN[3].value = FLICam->Visible_Area[3] - FLICam->Visible_Area[1]; /* Frame Height */
1061
FLICam->width = FLIImg->width = FrameN[2].value;
1062
FLICam->height = FLIImg->width = FrameN[3].value;
1064
BinningN[0].value = BinningN[1].value = 1;
1066
IDLog("The Camera Width is %d ---- %d\n", (int) FLICam->width, (int) FrameN[2].value);
1067
IDLog("The Camera Height is %d ---- %d\n", (int) FLICam->height, (int) FrameN[3].value);
1069
IDSetNumber(&PixelSizeNP, NULL);
1070
IDSetNumber(&TemperatureNP, NULL);
1071
IDSetNumber(&FrameNP, NULL);
1072
IDSetNumber(&BinningNP, NULL);
1074
IDLog("Exiting getBasicData()\n");
1078
int manageDefaults(char errmsg[])
1083
exposeTimeMS = (int) (ExposeTimeWN[0].value * 1000.);
1085
IDLog("Setting default exposure time of %d ms.\n", exposeTimeMS);
1086
if ( (err = FLISetExposureTime(fli_dev, exposeTimeMS) ))
1088
snprintf(errmsg, ERRMSG_SIZE, "FLISetExposureTime() failed. %s.\n", strerror((int)-err));
1089
IDLog(errmsg, NULL);
1093
/* Default frame type is NORMAL */
1094
if ( (err = FLISetFrameType(fli_dev, FLI_FRAME_TYPE_NORMAL) ))
1096
snprintf(errmsg, ERRMSG_SIZE, "FLISetFrameType() failed. %s.\n", strerror((int)-err));
1097
IDLog(errmsg, NULL);
1101
/* X horizontal binning */
1102
if ( (err = FLISetHBin(fli_dev, BinningN[0].value) ))
1104
snprintf(errmsg, ERRMSG_SIZE, "FLISetBin() failed. %s.\n", strerror((int)-err));
1105
IDLog(errmsg, NULL);
1109
/* Y vertical binning */
1110
if ( (err = FLISetVBin(fli_dev, BinningN[1].value) ))
1112
snprintf(errmsg, ERRMSG_SIZE, "FLISetVBin() failed. %s.\n", strerror((int)-err));
1113
IDLog(errmsg, NULL);
1117
IDLog("Setting default binning %f x %f.\n", BinningN[0].value, BinningN[1].value);
1119
FLISetNFlushes(fli_dev, NFLUSHES);
1121
/* Set image area */
1122
if (setImageArea(errmsg))
1130
int getOnSwitch(ISwitchVectorProperty *sp)
1133
for (i=0; i < sp->nsp ; i++)
1135
/*IDLog("Switch %s is %s\n", sp->sp[i].name, sp->sp[i].s == ISS_ON ? "On" : "Off");*/
1136
if (sp->sp[i].s == ISS_ON)
1143
int checkPowerS(ISwitchVectorProperty *sp)
1145
if (ConnectSP.s != IPS_OK)
1147
if (!strcmp(sp->label, ""))
1148
IDMessage (mydev, "Cannot change property %s while the CCD is offline.", sp->name);
1150
IDMessage (mydev, "Cannot change property %s while the CCD is offline.", sp->label);
1153
IDSetSwitch(sp, NULL);
1160
int checkPowerN(INumberVectorProperty *np)
1162
if (ConnectSP.s != IPS_OK)
1164
if (!strcmp(np->label, ""))
1165
IDMessage (mydev, "Cannot change property %s while the CCD is offline.", np->name);
1167
IDMessage (mydev, "Cannot change property %s while the CCD is offline.", np->label);
1170
IDSetNumber(np, NULL);
1177
int checkPowerT(ITextVectorProperty *tp)
1180
if (ConnectSP.s != IPS_OK)
1182
if (!strcmp(tp->label, ""))
1183
IDMessage (mydev, "Cannot change property %s while the CCD is offline.", tp->name);
1185
IDMessage (mydev, "Cannot change property %s while the CCD is offline.", tp->label);
1188
IDSetText(tp, NULL);
1199
char errmsg[ERRMSG_SIZE];
1201
IDLog ("In ConnectCCD\n");
1203
/* USB by default {USB, SERIAL, PARALLEL, INET} */
1204
switch (ConnectS[0].s)
1207
IDLog("Current portSwitch is %d\n", portSwitchIndex);
1208
IDLog("Attempting to find the camera in domain %ld\n", Domains[portSwitchIndex]);
1209
if (findcam(Domains[portSwitchIndex])) {
1210
ConnectSP.s = IPS_IDLE;
1211
ConnectS[0].s = ISS_OFF;
1212
ConnectS[1].s = ISS_ON;
1213
IDSetSwitch(&ConnectSP, "Error: no cameras were detected.");
1214
IDLog("Error: no cameras were detected.\n");
1218
if ((err = FLIOpen(&fli_dev, FLICam->name, FLIDEVICE_CAMERA | FLICam->domain)))
1220
ConnectSP.s = IPS_IDLE;
1221
ConnectS[0].s = ISS_OFF;
1222
ConnectS[1].s = ISS_ON;
1223
IDSetSwitch(&ConnectSP, "Error: FLIOpen() failed. %s.", strerror( (int) -err));
1224
IDLog("Error: FLIOpen() failed. %s.\n", strerror( (int) -err));
1229
ConnectS[0].s = ISS_ON;
1230
ConnectS[1].s = ISS_OFF;
1231
ConnectSP.s = IPS_OK;
1232
IDSetSwitch(&ConnectSP, "CCD is online. Retrieving basic data.");
1233
IDLog("CCD is online. Retrieving basic data.\n");
1235
if (manageDefaults(errmsg))
1237
IDMessage(mydev, errmsg, NULL);
1238
IDLog("%s", errmsg);
1245
ConnectS[0].s = ISS_OFF;
1246
ConnectS[1].s = ISS_ON;
1247
ConnectSP.s = IPS_IDLE;
1248
if ((err = FLIClose(fli_dev))) {
1249
ConnectSP.s = IPS_IDLE;
1250
ConnectS[0].s = ISS_OFF;
1251
ConnectS[1].s = ISS_ON;
1252
IDSetSwitch(&ConnectSP, "Error: FLIClose() failed. %s.", strerror( (int) -err));
1253
IDLog("Error: FLIClose() failed. %s.\n", strerror( (int) -err));
1256
IDSetSwitch(&ConnectSP, "CCD is offline.");
1261
/* isCCDConnected: return 1 if we have a connection, 0 otherwise */
1262
int isCCDConnected(void)
1264
return ((ConnectS[0].s == ISS_ON) ? 1 : 0);
1267
int findcam(flidomain_t domain)
1272
IDLog("In find Camera, the domain is %ld\n", domain);
1274
if (( err = FLIList(domain | FLIDEVICE_CAMERA, &tmplist)))
1276
IDLog("FLIList() failed. %s\n", strerror((int)-err));
1280
if (tmplist != NULL && tmplist[0] != NULL)
1284
IDLog("Trying to allocate memory to FLICam\n");
1285
if ((FLICam = malloc (sizeof (cam_t))) == NULL)
1287
IDLog("malloc() failed.\n");
1291
for (i = 0; tmplist[i] != NULL; i++)
1295
for (j = 0; tmplist[i][j] != '\0'; j++)
1296
if (tmplist[i][j] == ';')
1298
tmplist[i][j] = '\0';
1303
FLICam->domain = domain;
1307
case FLIDOMAIN_PARALLEL_PORT:
1308
FLICam->dname = strdup("parallel port");
1312
FLICam->dname = strdup("USB");
1315
case FLIDOMAIN_SERIAL:
1316
FLICam->dname = strdup("serial");
1319
case FLIDOMAIN_INET:
1320
FLICam->dname = strdup("inet");
1324
FLICam->dname = strdup("Unknown domain");
1327
FLICam->name = strdup(tmplist[0]);
1329
if ((err = FLIFreeList(tmplist)))
1331
IDLog("FLIFreeList() failed. %s.\n", strerror((int)-err));
1338
if ((err = FLIFreeList(tmplist)))
1340
IDLog("FLIFreeList() failed. %s.\n", strerror((int)-err));
1347
IDLog("Findcam() finished successfully.\n");
1353
double lmin = FLIImg->img[0];
1356
for (i= 0; i < FLIImg->height ; i++)
1357
for (j= 0; j < FLIImg->width; j++)
1359
ind = (i * FLIImg->width) + j;
1360
if (FLIImg->img[ind] < lmin) lmin = FLIImg->img[ind];
1368
double lmax = FLIImg->img[0];
1371
for (i= 0; i < FLIImg->height ; i++)
1372
for (j= 0; j < FLIImg->width; j++)
1374
ind = (i * FLIImg->width) + j;
1375
if (FLIImg->img[ind] > lmax) lmax = FLIImg->img[ind];