Instrument Neutral Distributed Interface INDI  0.9.8
indiccd.cpp
1 /*******************************************************************************
2  Copyright(c) 2010, 2011 Gerry Rozema, Jasem Mutlaq. All rights reserved.
3 
4  Rapid Guide support added by CloudMakers, s. r. o.
5  Copyright(c) 2013 CloudMakers, s. r. o. All rights reserved.
6 
7  Star detection algorithm is based on PHD Guiding by Craig Stark
8  Copyright (c) 2006-2010 Craig Stark. All rights reserved.
9 
10  This library is free software; you can redistribute it and/or
11  modify it under the terms of the GNU Library General Public
12  License version 2 as published by the Free Software Foundation.
13 
14  This library is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  Library General Public License for more details.
18 
19  You should have received a copy of the GNU Library General Public License
20  along with this library; see the file COPYING.LIB. If not, write to
21  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  Boston, MA 02110-1301, USA.
23 *******************************************************************************/
24 
25 #include "indiccd.h"
26 
27 #include <string.h>
28 #include <time.h>
29 #include <sys/time.h>
30 #include <zlib.h>
31 
32 #include <fitsio.h>
33 
34 const char *IMAGE_SETTINGS_TAB = "Image Settings";
35 const char *IMAGE_INFO_TAB = "Image Info";
36 const char *GUIDE_HEAD_TAB = "Guider Head";
37 const char *GUIDE_CONTROL_TAB = "Guider Control";
38 const char *RAPIDGUIDE_TAB = "Rapid Guide";
39 
40 CCDChip::CCDChip()
41 {
42  SendCompressed=false;
43  Interlaced=false;
44 
45  RawFrame= (char *) malloc(sizeof(char)); // Seed for realloc
46  RawFrameSize=0;
47 
48  BPP = 8;
49  BinX = BinY = 1;
50  NAxis = 2;
51 
52  strncpy(imageExtention, "fits", MAXINDIBLOBFMT);
53 
54  FrameType=LIGHT_FRAME;
55 
56  ImageFrameNP = new INumberVectorProperty;
57  AbortExposureSP = new ISwitchVectorProperty;
58  FrameTypeSP = new ISwitchVectorProperty;
59  ImageExposureNP = new INumberVectorProperty;
60  ImageBinNP = new INumberVectorProperty;
61  ImagePixelSizeNP = new INumberVectorProperty;
62  CompressSP = new ISwitchVectorProperty;
63  FitsBP = new IBLOBVectorProperty;
64  RapidGuideSP = new ISwitchVectorProperty;
65  RapidGuideSetupSP = new ISwitchVectorProperty;
66  RapidGuideDataNP = new INumberVectorProperty;
67  lastRapidX = lastRapidY = -1;
68 }
69 
70 CCDChip::~CCDChip()
71 {
72  delete RawFrame;
73  RawFrameSize=0;
74  RawFrame=NULL;
75 
76  delete ImageFrameNP;
77  delete AbortExposureSP;
78  delete FrameTypeSP;
79  delete ImageExposureNP;
80  delete ImageBinNP;
81  delete ImagePixelSizeNP;
82  delete CompressSP;
83  delete FitsBP;
84  delete RapidGuideSP;
85  delete RapidGuideSetupSP;
86  delete RapidGuideDataNP;
87 }
88 
89 void CCDChip::setFrameType(CCD_FRAME type)
90 {
91  FrameType=type;
92 }
93 
94 void CCDChip::setResolution(int x, int y)
95 {
96  XRes = x;
97  YRes = y;
98 
99  ImagePixelSizeN[0].value=x;
100  ImagePixelSizeN[1].value=y;
101 
102  IDSetNumber(ImagePixelSizeNP, NULL);
103 
104  ImageFrameN[FRAME_X].min = 0;
105  ImageFrameN[FRAME_X].max = x-1;
106  ImageFrameN[FRAME_Y].min = 0;
107  ImageFrameN[FRAME_Y].max = y-1;
108 
109  ImageFrameN[FRAME_W].min = 1;
110  ImageFrameN[FRAME_W].max = x;
111  ImageFrameN[FRAME_H].max = 1;
112  ImageFrameN[FRAME_H].max = y;
113  IUUpdateMinMax(ImageFrameNP);
114 }
115 
116 void CCDChip::setFrame(int subx, int suby, int subw, int subh)
117 {
118  SubX = subx;
119  SubY = suby;
120  SubW = subw;
121  SubH = subh;
122 
123  ImageFrameN[FRAME_X].value = SubX;
124  ImageFrameN[FRAME_Y].value = SubY;
125  ImageFrameN[FRAME_W].value = SubW;
126  ImageFrameN[FRAME_H].value = SubH;
127 
128  IDSetNumber(ImageFrameNP, NULL);
129 }
130 
131 void CCDChip::setBin(int hor, int ver)
132 {
133  BinX = hor;
134  BinY = ver;
135 
136  ImageBinN[BIN_W].value = BinX;
137  ImageBinN[BIN_H].value = BinY;
138 
139  IDSetNumber(ImageBinNP, NULL);
140 }
141 
142 
143 void CCDChip::setMaxBin(int max_hor, int max_ver)
144 {
145  ImageBinN[BIN_W].max = max_hor;
146  ImageBinN[BIN_H].max = max_ver;
147 
148  IUUpdateMinMax(ImageBinNP);
149 }
150 
151 void CCDChip::setPixelSize(float x, float y)
152 {
153  PixelSizex = x;
154  PixelSizey = y;
155 
156  ImagePixelSizeN[2].value=x;
157  ImagePixelSizeN[3].value=x;
158  ImagePixelSizeN[4].value=y;
159 
160  IDSetNumber(ImagePixelSizeNP, NULL);
161 
162 }
163 
164 void CCDChip::setBPP(int bbp)
165 {
166  BPP = bbp;
167 
168  ImagePixelSizeN[5].value = BPP;
169 
170  IDSetNumber(ImagePixelSizeNP, NULL);
171 }
172 
173 void CCDChip::setFrameBufferSize(int nbuf, bool allocMem)
174 {
175  if (nbuf == RawFrameSize)
176  return;
177 
178  RawFrameSize = nbuf;
179 
180  if (allocMem == false)
181  return;
182 
183  RawFrame = (char *) realloc(RawFrame, nbuf * sizeof(char));
184 }
185 
186 void CCDChip::setExposureLeft(double duration)
187 {
188  ImageExposureN[0].value = duration;
189 
190  IDSetNumber(ImageExposureNP, NULL);
191 }
192 
193 void CCDChip::setExposureDuration(double duration)
194 {
195  exposureDuration = duration;
196  gettimeofday(&startExposureTime,NULL);
197 }
198 
199 const char *CCDChip::getFrameTypeName(CCD_FRAME fType)
200 {
201  return FrameTypeS[fType].name;
202 }
203 
205 {
206  static char ts[32];
207  struct tm *tp;
208  time_t t = (time_t) startExposureTime.tv_sec;
209 
210  //time (&t);
211  tp = gmtime (&t);
212  strftime (ts, sizeof(ts), "%Y-%m-%dT%H:%M:%S", tp);
213  return (ts);
214 }
215 
216 void CCDChip::setInterlaced(bool intr)
217 {
218  Interlaced = intr;
219 }
220 
222 {
223  ImageExposureNP->s = IPS_ALERT;
224  IDSetNumber(ImageExposureNP, NULL);
225 }
226 
227 int CCDChip::getNAxis() const
228 {
229  return NAxis;
230 }
231 
232 void CCDChip::setNAxis(int value)
233 {
234  NAxis = value;
235 }
236 
237 void CCDChip::setImageExtension(const char *ext)
238 {
239  strncpy(imageExtention, ext, MAXINDINAME);
240 }
241 
242 INDI::CCD::CCD()
243 {
244  //ctor
245  capability.hasGuideHead=false;
246  capability.hasST4Port=false;
247  capability.hasShutter=false;
248  capability.hasCooler=false;
249  capability.canBin=false;
250  capability.canSubFrame=false;
251  capability.canAbort=false;
252 
253  InExposure=false;
254  InGuideExposure=false;
255  RapidGuideEnabled=false;
256  GuiderRapidGuideEnabled=false;
257 
258  AutoLoop=false;
259  SendImage=false;
260  ShowMarker=false;
261  GuiderAutoLoop=false;
262  GuiderSendImage=false;
263  GuiderShowMarker=false;
264 
265  ExposureTime = 0.0;
266  GuiderExposureTime = 0.0;
267 
268  RA=-1000;
269  Dec=-1000;
270  ActiveDeviceTP = new ITextVectorProperty;
271 }
272 
273 INDI::CCD::~CCD()
274 {
275  delete ActiveDeviceTP;
276 }
277 
278 void INDI::CCD::SetCapability(Capability *cap)
279 {
280  capability.canAbort = cap->canAbort;
281  capability.canBin = cap->canBin;
282  capability.canSubFrame = cap->canSubFrame;
283  capability.hasCooler = cap->hasCooler;
284  capability.hasGuideHead = cap->hasGuideHead;
285  capability.hasShutter = cap->hasShutter;
286  capability.hasST4Port = cap->hasST4Port;
287 }
288 
290 {
291  DefaultDevice::initProperties(); // let the base class flesh in what it wants
292 
293  // CCD Temperature
294  IUFillNumber(&TemperatureN[0], "CCD_TEMPERATURE_VALUE", "Temperature (C)", "%5.2f", -50.0, 50.0, 0., 0.);
295  IUFillNumberVector(&TemperatureNP, TemperatureN, 1, getDeviceName(), "CCD_TEMPERATURE", "Temperature", MAIN_CONTROL_TAB, IP_RW, 60, IPS_IDLE);
296 
297  // PRIMARY CCD Init
298 
299  IUFillNumber(&PrimaryCCD.ImageFrameN[0],"X","Left ","%4.0f",0,1392.0,0,0);
300  IUFillNumber(&PrimaryCCD.ImageFrameN[1],"Y","Top","%4.0f",0,1040,0,0);
301  IUFillNumber(&PrimaryCCD.ImageFrameN[2],"WIDTH","Width","%4.0f",0,1392.0,0,1392.0);
302  IUFillNumber(&PrimaryCCD.ImageFrameN[3],"HEIGHT","Height","%4.0f",0,1392,0,1392.0);
303  IUFillNumberVector(PrimaryCCD.ImageFrameNP,PrimaryCCD.ImageFrameN,4,getDeviceName(),"CCD_FRAME","Frame",IMAGE_SETTINGS_TAB,IP_RW,60,IPS_IDLE);
304 
305  IUFillSwitch(&PrimaryCCD.FrameTypeS[0],"FRAME_LIGHT","Light",ISS_ON);
306  IUFillSwitch(&PrimaryCCD.FrameTypeS[1],"FRAME_BIAS","Bias",ISS_OFF);
307  IUFillSwitch(&PrimaryCCD.FrameTypeS[2],"FRAME_DARK","Dark",ISS_OFF);
308  IUFillSwitch(&PrimaryCCD.FrameTypeS[3],"FRAME_FLAT","Flat",ISS_OFF);
309  IUFillSwitchVector(PrimaryCCD.FrameTypeSP,PrimaryCCD.FrameTypeS,4,getDeviceName(),"CCD_FRAME_TYPE","Frame Type",IMAGE_SETTINGS_TAB,IP_RW,ISR_1OFMANY,60,IPS_IDLE);
310 
311  IUFillNumber(&PrimaryCCD.ImageExposureN[0],"CCD_EXPOSURE_VALUE","Duration (s)","%5.2f",0,36000,0,1.0);
312  IUFillNumberVector(PrimaryCCD.ImageExposureNP,PrimaryCCD.ImageExposureN,1,getDeviceName(),"CCD_EXPOSURE","Expose",MAIN_CONTROL_TAB,IP_RW,60,IPS_IDLE);
313 
314  IUFillSwitch(&PrimaryCCD.AbortExposureS[0],"ABORT","Abort",ISS_OFF);
315  IUFillSwitchVector(PrimaryCCD.AbortExposureSP,PrimaryCCD.AbortExposureS,1,getDeviceName(),"CCD_ABORT_EXPOSURE","Expose Abort",MAIN_CONTROL_TAB,IP_RW,ISR_1OFMANY,60,IPS_IDLE);
316 
317  IUFillNumber(&PrimaryCCD.ImageBinN[0],"HOR_BIN","X","%2.0f",1,4,1,1);
318  IUFillNumber(&PrimaryCCD.ImageBinN[1],"VER_BIN","Y","%2.0f",1,4,1,1);
319  IUFillNumberVector(PrimaryCCD.ImageBinNP,PrimaryCCD.ImageBinN,2,getDeviceName(),"CCD_BINNING","Binning",IMAGE_SETTINGS_TAB,IP_RW,60,IPS_IDLE);
320 
321  IUFillNumber(&PrimaryCCD.ImagePixelSizeN[0],"CCD_MAX_X","Resolution x","%4.0f",1,40,0,6.45);
322  IUFillNumber(&PrimaryCCD.ImagePixelSizeN[1],"CCD_MAX_Y","Resolution y","%4.0f",1,40,0,6.45);
323  IUFillNumber(&PrimaryCCD.ImagePixelSizeN[2],"CCD_PIXEL_SIZE","Pixel size (um)","%5.2f",1,40,0,6.45);
324  IUFillNumber(&PrimaryCCD.ImagePixelSizeN[3],"CCD_PIXEL_SIZE_X","Pixel size X","%5.2f",1,40,0,6.45);
325  IUFillNumber(&PrimaryCCD.ImagePixelSizeN[4],"CCD_PIXEL_SIZE_Y","Pixel size Y","%5.2f",1,40,0,6.45);
326  IUFillNumber(&PrimaryCCD.ImagePixelSizeN[5],"CCD_BITSPERPIXEL","Bits per pixel","%3.0f",1,40,0,6.45);
327  IUFillNumberVector(PrimaryCCD.ImagePixelSizeNP,PrimaryCCD.ImagePixelSizeN,6,getDeviceName(),"CCD_INFO","CCD Information",IMAGE_INFO_TAB,IP_RO,60,IPS_IDLE);
328 
329  IUFillSwitch(&PrimaryCCD.CompressS[0],"COMPRESS","Compress",ISS_ON);
330  IUFillSwitch(&PrimaryCCD.CompressS[1],"RAW","Raw",ISS_OFF);
331  IUFillSwitchVector(PrimaryCCD.CompressSP,PrimaryCCD.CompressS,2,getDeviceName(),"CCD_COMPRESSION","Image",IMAGE_SETTINGS_TAB,IP_RW,ISR_1OFMANY,60,IPS_IDLE);
332 
333  IUFillBLOB(&PrimaryCCD.FitsB,"CCD1","Image","");
334  IUFillBLOBVector(PrimaryCCD.FitsBP,&PrimaryCCD.FitsB,1,getDeviceName(),"CCD1","Image Data",IMAGE_INFO_TAB,IP_RO,60,IPS_IDLE);
335 
336  IUFillSwitch(&PrimaryCCD.RapidGuideS[0], "ENABLE", "Enable", ISS_OFF);
337  IUFillSwitch(&PrimaryCCD.RapidGuideS[1], "DISABLE", "Disable", ISS_ON);
338  IUFillSwitchVector(PrimaryCCD.RapidGuideSP, PrimaryCCD.RapidGuideS, 2, getDeviceName(), "CCD_RAPID_GUIDE", "Rapid Guide", OPTIONS_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
339 
340  IUFillSwitch(&PrimaryCCD.RapidGuideSetupS[0], "AUTO_LOOP", "Auto loop", ISS_ON);
341  IUFillSwitch(&PrimaryCCD.RapidGuideSetupS[1], "SEND_IMAGE", "Send image", ISS_OFF);
342  IUFillSwitch(&PrimaryCCD.RapidGuideSetupS[2], "SHOW_MARKER", "Show marker", ISS_OFF);
343  IUFillSwitchVector(PrimaryCCD.RapidGuideSetupSP, PrimaryCCD.RapidGuideSetupS, 3, getDeviceName(), "CCD_RAPID_GUIDE_SETUP", "Rapid Guide Setup", RAPIDGUIDE_TAB, IP_RW, ISR_NOFMANY, 0, IPS_IDLE);
344 
345  IUFillNumber(&PrimaryCCD.RapidGuideDataN[0],"GUIDESTAR_X","Guide star position X","%5.2f",0,1024,0,0);
346  IUFillNumber(&PrimaryCCD.RapidGuideDataN[1],"GUIDESTAR_Y","Guide star position Y","%5.2f",0,1024,0,0);
347  IUFillNumber(&PrimaryCCD.RapidGuideDataN[2],"GUIDESTAR_FIT","Guide star fit","%5.2f",0,1024,0,0);
348  IUFillNumberVector(PrimaryCCD.RapidGuideDataNP,PrimaryCCD.RapidGuideDataN,3,getDeviceName(),"CCD_RAPID_GUIDE_DATA","Rapid Guide Data",RAPIDGUIDE_TAB,IP_RO,60,IPS_IDLE);
349 
350  // GUIDER CCD Init
351 
352  IUFillNumber(&GuideCCD.ImageFrameN[0],"X","Left ","%4.0f",0,1392.0,0,0);
353  IUFillNumber(&GuideCCD.ImageFrameN[1],"Y","Top","%4.0f",0,1040,0,0);
354  IUFillNumber(&GuideCCD.ImageFrameN[2],"WIDTH","Width","%4.0f",0,1392.0,0,1392.0);
355  IUFillNumber(&GuideCCD.ImageFrameN[3],"HEIGHT","Height","%4.0f",0,1040,0,1040);
356  IUFillNumberVector(GuideCCD.ImageFrameNP,GuideCCD.ImageFrameN,4,getDeviceName(),"GUIDER_FRAME","Frame",GUIDE_HEAD_TAB,IP_RW,60,IPS_IDLE);
357 
358  IUFillNumber(&GuideCCD.ImageBinN[0],"HOR_BIN","X","%2.0f",1,4,1,1);
359  IUFillNumber(&GuideCCD.ImageBinN[1],"VER_BIN","Y","%2.0f",1,4,1,1);
360  IUFillNumberVector(GuideCCD.ImageBinNP,GuideCCD.ImageBinN,2,getDeviceName(),"GUIDER_BINNING","Binning",GUIDE_HEAD_TAB,IP_RW,60,IPS_IDLE);
361 
362  IUFillNumber(&GuideCCD.ImagePixelSizeN[0],"CCD_MAX_X","Resolution x","%4.0f",1,40,0,6.45);
363  IUFillNumber(&GuideCCD.ImagePixelSizeN[1],"CCD_MAX_Y","Resolution y","%4.0f",1,40,0,6.45);
364  IUFillNumber(&GuideCCD.ImagePixelSizeN[2],"CCD_PIXEL_SIZE","Pixel size (um)","%5.2f",1,40,0,6.45);
365  IUFillNumber(&GuideCCD.ImagePixelSizeN[3],"CCD_PIXEL_SIZE_X","Pixel size X","%5.2f",1,40,0,6.45);
366  IUFillNumber(&GuideCCD.ImagePixelSizeN[4],"CCD_PIXEL_SIZE_Y","Pixel size Y","%5.2f",1,40,0,6.45);
367  IUFillNumber(&GuideCCD.ImagePixelSizeN[5],"CCD_BITSPERPIXEL","Bits per pixel","%3.0f",1,40,0,6.45);
368  IUFillNumberVector(GuideCCD.ImagePixelSizeNP,GuideCCD.ImagePixelSizeN,6,getDeviceName(),"GUIDER_INFO", "Guide Info",IMAGE_INFO_TAB,IP_RO,60,IPS_IDLE);
369 
370  IUFillSwitch(&GuideCCD.FrameTypeS[0],"FRAME_LIGHT","Light",ISS_ON);
371  IUFillSwitch(&GuideCCD.FrameTypeS[1],"FRAME_BIAS","Bias",ISS_OFF);
372  IUFillSwitch(&GuideCCD.FrameTypeS[2],"FRAME_DARK","Dark",ISS_OFF);
373  IUFillSwitch(&GuideCCD.FrameTypeS[3],"FRAME_FLAT","Flat",ISS_OFF);
374  IUFillSwitchVector(GuideCCD.FrameTypeSP,GuideCCD.FrameTypeS,4,getDeviceName(),"GUIDER_FRAME_TYPE","Frame Type",GUIDE_HEAD_TAB,IP_RW,ISR_1OFMANY,60,IPS_IDLE);
375 
376  IUFillNumber(&GuideCCD.ImageExposureN[0],"GUIDER_EXPOSURE_VALUE","Duration (s)","%5.2f",0,36000,0,1.0);
377  IUFillNumberVector(GuideCCD.ImageExposureNP,GuideCCD.ImageExposureN,1,getDeviceName(),"GUIDER_EXPOSURE","Guide Head",MAIN_CONTROL_TAB,IP_RW,60,IPS_IDLE);
378 
379  IUFillSwitch(&GuideCCD.AbortExposureS[0],"ABORT","Abort",ISS_OFF);
380  IUFillSwitchVector(GuideCCD.AbortExposureSP,GuideCCD.AbortExposureS,1,getDeviceName(),"GUIDER_ABORT_EXPOSURE","Guide Abort",MAIN_CONTROL_TAB,IP_RW,ISR_1OFMANY,60,IPS_IDLE);
381 
382  IUFillSwitch(&GuideCCD.CompressS[0],"GCOMPRESS","Compress",ISS_ON);
383  IUFillSwitch(&GuideCCD.CompressS[1],"GRAW","Raw",ISS_OFF);
384  IUFillSwitchVector(GuideCCD.CompressSP,GuideCCD.CompressS,2,getDeviceName(),"GUIDER_COMPRESSION","Image",GUIDE_HEAD_TAB,IP_RW,ISR_1OFMANY,60,IPS_IDLE);
385 
386  IUFillBLOB(&GuideCCD.FitsB,"CCD2","Guider Image","");
387  IUFillBLOBVector(GuideCCD.FitsBP,&GuideCCD.FitsB,1,getDeviceName(),"CCD2","Image Data",IMAGE_INFO_TAB,IP_RO,60,IPS_IDLE);
388 
389  IUFillSwitch(&GuideCCD.RapidGuideS[0], "ENABLE", "Enable", ISS_OFF);
390  IUFillSwitch(&GuideCCD.RapidGuideS[1], "DISABLE", "Disable", ISS_ON);
391  IUFillSwitchVector(GuideCCD.RapidGuideSP, GuideCCD.RapidGuideS, 2, getDeviceName(), "GUIDER_RAPID_GUIDE", "Guider Head Rapid Guide", OPTIONS_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
392 
393  IUFillSwitch(&GuideCCD.RapidGuideSetupS[0], "AUTO_LOOP", "Auto loop", ISS_ON);
394  IUFillSwitch(&GuideCCD.RapidGuideSetupS[1], "SEND_IMAGE", "Send image", ISS_OFF);
395  IUFillSwitch(&GuideCCD.RapidGuideSetupS[2], "SHOW_MARKER", "Show marker", ISS_OFF);
396  IUFillSwitchVector(GuideCCD.RapidGuideSetupSP, GuideCCD.RapidGuideSetupS, 3, getDeviceName(), "GUIDER_RAPID_GUIDE_SETUP", "Rapid Guide Setup", RAPIDGUIDE_TAB, IP_RW, ISR_NOFMANY, 0, IPS_IDLE);
397 
398  IUFillNumber(&GuideCCD.RapidGuideDataN[0],"GUIDESTAR_X","Guide star position X","%5.2f",0,1024,0,0);
399  IUFillNumber(&GuideCCD.RapidGuideDataN[1],"GUIDESTAR_Y","Guide star position Y","%5.2f",0,1024,0,0);
400  IUFillNumber(&GuideCCD.RapidGuideDataN[2],"GUIDESTAR_FIT","Guide star fit","%5.2f",0,1024,0,0);
401  IUFillNumberVector(GuideCCD.RapidGuideDataNP,GuideCCD.RapidGuideDataN,3,getDeviceName(),"GUIDER_RAPID_GUIDE_DATA","Rapid Guide Data",RAPIDGUIDE_TAB,IP_RO,60,IPS_IDLE);
402 
403  // CCD Class Init
404 
405  IUFillText(&ActiveDeviceT[0],"ACTIVE_TELESCOPE","Telescope","Telescope Simulator");
406  IUFillText(&ActiveDeviceT[1],"ACTIVE_FOCUSER","Focuser","Focuser Simulator");
407  IUFillTextVector(ActiveDeviceTP,ActiveDeviceT,2,getDeviceName(),"ACTIVE_DEVICES","Snoop devices",OPTIONS_TAB,IP_RW,60,IPS_IDLE);
408 
409  IUFillNumber(&EqN[0],"RA","Ra (hh:mm:ss)","%010.6m",0,24,0,0);
410  IUFillNumber(&EqN[1],"DEC","Dec (dd:mm:ss)","%010.6m",-90,90,0,0);
411  IUFillNumberVector(&EqNP,EqN,2,ActiveDeviceT[0].text,"EQUATORIAL_EOD_COORD","EQ Coord","Main Control",IP_RW,60,IPS_IDLE);
412 
413  IDSnoopDevice(ActiveDeviceT[0].text,"EQUATORIAL_EOD_COORD");
414 
415 
416  // Guider Interface
417  initGuiderProperties(getDeviceName(), GUIDE_CONTROL_TAB);
418 
419  return true;
420 }
421 
422 void INDI::CCD::ISGetProperties (const char *dev)
423 {
424  // First we let our parent populate
425 
426  //IDLog("INDI::CCD IsGetProperties with %s\n",dev);
427 
429 
430  return;
431 }
432 
434 {
435  //IDLog("INDI::CCD UpdateProperties isConnected returns %d %d\n",isConnected(),Connected);
436  if(isConnected())
437  {
438  defineNumber(PrimaryCCD.ImageExposureNP);
439 
440  if (capability.canAbort)
441  defineSwitch(PrimaryCCD.AbortExposureSP);
442  if (capability.canSubFrame == false)
443  PrimaryCCD.ImageFrameNP->p = IP_RO;
444 
445  defineNumber(PrimaryCCD.ImageFrameNP);
446  if (capability.canBin)
447  defineNumber(PrimaryCCD.ImageBinNP);
448 
449  if(capability.hasGuideHead)
450  {
451  defineNumber(GuideCCD.ImageExposureNP);
452  if (capability.canAbort)
453  defineSwitch(GuideCCD.AbortExposureSP);
454  if (capability.canSubFrame == false)
455  GuideCCD.ImageFrameNP->p = IP_RO;
456  defineNumber(GuideCCD.ImageFrameNP);
457  }
458 
459  if (capability.hasCooler)
460  defineNumber(&TemperatureNP);
461 
462  defineNumber(PrimaryCCD.ImagePixelSizeNP);
463  if(capability.hasGuideHead)
464  {
465  defineNumber(GuideCCD.ImagePixelSizeNP);
466  if (capability.canBin)
467  defineNumber(GuideCCD.ImageBinNP);
468  }
469  defineSwitch(PrimaryCCD.CompressSP);
470  defineBLOB(PrimaryCCD.FitsBP);
471  if(capability.hasGuideHead)
472  {
473  defineSwitch(GuideCCD.CompressSP);
474  defineBLOB(GuideCCD.FitsBP);
475  }
476  if(capability.hasST4Port)
477  {
478  defineNumber(&GuideNSNP);
479  defineNumber(&GuideWENP);
480  }
481  defineSwitch(PrimaryCCD.FrameTypeSP);
482 
483  if (capability.hasGuideHead)
484  defineSwitch(GuideCCD.FrameTypeSP);
485 
486  defineSwitch(PrimaryCCD.RapidGuideSP);
487 
488  if (capability.hasGuideHead)
489  defineSwitch(GuideCCD.RapidGuideSP);
490 
491  if (RapidGuideEnabled)
492  {
493  defineSwitch(PrimaryCCD.RapidGuideSetupSP);
494  defineNumber(PrimaryCCD.RapidGuideDataNP);
495  }
496  if (GuiderRapidGuideEnabled)
497  {
498  defineSwitch(GuideCCD.RapidGuideSetupSP);
499  defineNumber(GuideCCD.RapidGuideDataNP);
500  }
501  defineText(ActiveDeviceTP);
502  }
503  else
504  {
505  deleteProperty(PrimaryCCD.ImageFrameNP->name);
506  deleteProperty(PrimaryCCD.ImagePixelSizeNP->name);
507 
508  if (capability.canBin)
509  deleteProperty(PrimaryCCD.ImageBinNP->name);
510 
511  deleteProperty(PrimaryCCD.ImageExposureNP->name);
512  if (capability.canAbort)
513  deleteProperty(PrimaryCCD.AbortExposureSP->name);
514  deleteProperty(PrimaryCCD.FitsBP->name);
515  deleteProperty(PrimaryCCD.CompressSP->name);
516  deleteProperty(PrimaryCCD.RapidGuideSP->name);
517  if (RapidGuideEnabled)
518  {
519  deleteProperty(PrimaryCCD.RapidGuideSetupSP->name);
520  deleteProperty(PrimaryCCD.RapidGuideDataNP->name);
521  }
522  if(capability.hasGuideHead)
523  {
524  deleteProperty(GuideCCD.ImageExposureNP->name);
525  if (capability.canAbort)
526  deleteProperty(GuideCCD.AbortExposureSP->name);
527  deleteProperty(GuideCCD.ImageFrameNP->name);
528  deleteProperty(GuideCCD.ImagePixelSizeNP->name);
529 
530  deleteProperty(GuideCCD.FitsBP->name);
531  if (capability.canBin)
532  deleteProperty(GuideCCD.ImageBinNP->name);
533  deleteProperty(GuideCCD.CompressSP->name);
534  deleteProperty(GuideCCD.FrameTypeSP->name);
535  deleteProperty(GuideCCD.RapidGuideSP->name);
536  if (GuiderRapidGuideEnabled)
537  {
538  deleteProperty(GuideCCD.RapidGuideSetupSP->name);
539  deleteProperty(GuideCCD.RapidGuideDataNP->name);
540  }
541  }
542  if (capability.hasCooler)
543  deleteProperty(TemperatureNP.name);
544  if(capability.hasST4Port)
545  {
546  deleteProperty(GuideNSNP.name);
547  deleteProperty(GuideWENP.name);
548  }
549  deleteProperty(PrimaryCCD.FrameTypeSP->name);
550  deleteProperty(ActiveDeviceTP->name);
551  }
552  return true;
553 }
554 
555 bool INDI::CCD::ISSnoopDevice (XMLEle *root)
556 {
557  if(IUSnoopNumber(root,&EqNP)==0)
558  {
559  float newra,newdec;
560  newra=EqN[0].value;
561  newdec=EqN[1].value;
562  if((newra != RA)||(newdec != Dec))
563  {
564  //IDLog("RA %4.2f Dec %4.2f Snooped RA %4.2f Dec %4.2f\n",RA,Dec,newra,newdec);
565  RA=newra;
566  Dec=newdec;
567  }
568  }
569  else
570  {
571  //fprintf(stderr,"Snoop Failed\n");
572  }
573 
575  }
576 
577 bool INDI::CCD::ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n)
578 {
579  // first check if it's for our device
580  if(strcmp(dev,getDeviceName())==0)
581  {
582  // This is for our device
583  // Now lets see if it's something we process here
584  if(strcmp(name,ActiveDeviceTP->name)==0)
585  {
586  int rc;
587  ActiveDeviceTP->s=IPS_OK;
588  rc=IUUpdateText(ActiveDeviceTP,texts,names,n);
589  // Update client display
590  IDSetText(ActiveDeviceTP,NULL);
591  saveConfig();
592 
593  // Update the property name!
594  strncpy(EqNP.device, ActiveDeviceT[0].text, MAXINDIDEVICE);
595  IDSnoopDevice(ActiveDeviceT[0].text,"EQUATORIAL_EOD_COORD");
596 
597  // Tell children active devices was updated.
598  activeDevicesUpdated();
599 
600  // We processed this one, so, tell the world we did it
601  return true;
602  }
603 
604  }
605 
606  return INDI::DefaultDevice::ISNewText(dev,name,texts,names,n);
607 }
608 
609 bool INDI::CCD::ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n)
610 {
611  // first check if it's for our device
612  //IDLog("INDI::CCD::ISNewNumber %s\n",name);
613  if(strcmp(dev,getDeviceName())==0)
614  {
615  // This is for our device
616  // Now lets see if it's something we process here
617  if(strcmp(name,"CCD_EXPOSURE")==0)
618  {
619  PrimaryCCD.ImageExposureN[0].value = ExposureTime = values[0];
620 
621  if (PrimaryCCD.ImageExposureNP->s==IPS_BUSY)
622  AbortExposure();
623 
624  if (StartExposure(ExposureTime))
625  PrimaryCCD.ImageExposureNP->s=IPS_BUSY;
626  else
627  PrimaryCCD.ImageExposureNP->s=IPS_ALERT;
628  IDSetNumber(PrimaryCCD.ImageExposureNP,NULL);
629  return true;
630  }
631 
632  if(strcmp(name,"GUIDER_EXPOSURE")==0)
633  {
634  GuideCCD.ImageExposureN[0].value = GuiderExposureTime = values[0];
635  GuideCCD.ImageExposureNP->s=IPS_BUSY;
636  if (StartGuideExposure(GuiderExposureTime))
637  GuideCCD.ImageExposureNP->s=IPS_BUSY;
638  else
639  GuideCCD.ImageExposureNP->s=IPS_ALERT;
640  IDSetNumber(GuideCCD.ImageExposureNP,NULL);
641  return true;
642  }
643 
644  if(strcmp(name,"CCD_BINNING")==0)
645  {
646  // We are being asked to set camera binning
647  INumber *np = IUFindNumber(PrimaryCCD.ImageBinNP, names[0]);
648  if (np == NULL)
649  {
650  PrimaryCCD.ImageBinNP->s = IPS_ALERT;
651  IDSetNumber (PrimaryCCD.ImageBinNP, NULL);
652  return false;
653  }
654 
655  int binx,biny;
656  if (!strcmp(np->name, "HOR_BIN"))
657  {
658  binx = values[0];
659  biny = values[1];
660  }
661  else
662  {
663  binx = values[1];
664  biny = values[0];
665  }
666 
667  if (UpdateCCDBin(binx, biny))
668  {
669  IUUpdateNumber(PrimaryCCD.ImageBinNP,values,names,n);
670  PrimaryCCD.ImageBinNP->s=IPS_OK;
671 
672  }
673  else
674  PrimaryCCD.ImageBinNP->s = IPS_ALERT;
675 
676  IDSetNumber (PrimaryCCD.ImageBinNP, NULL);
677 
678  return true;
679 
680  }
681 
682  if(strcmp(name,"GUIDER_BINNING")==0)
683  {
684  // We are being asked to set camera binning
685  INumber *np = IUFindNumber(GuideCCD.ImageBinNP, names[0]);
686  if (np == NULL)
687  {
688  GuideCCD.ImageBinNP->s = IPS_ALERT;
689  IDSetNumber (GuideCCD.ImageBinNP, NULL);
690  return false;
691  }
692 
693  int binx,biny;
694  if (!strcmp(np->name, "HOR_BIN"))
695  {
696  binx = values[0];
697  biny = values[1];
698  }
699  else
700  {
701  binx = values[1];
702  biny = values[0];
703  }
704 
705  if (UpdateGuiderBin(binx, biny))
706  {
707  IUUpdateNumber(GuideCCD.ImageBinNP,values,names,n);
708  GuideCCD.ImageBinNP->s=IPS_OK;
709 
710  }
711  else
712  GuideCCD.ImageBinNP->s = IPS_ALERT;
713 
714  IDSetNumber (GuideCCD.ImageBinNP, NULL);
715 
716  return true;
717 
718 
719  }
720 
721  if(strcmp(name,"CCD_FRAME")==0)
722  {
723  // We are being asked to set camera binning
724  PrimaryCCD.ImageFrameNP->s=IPS_OK;
725  IUUpdateNumber(PrimaryCCD.ImageFrameNP,values,names,n);
726 
727  if (UpdateCCDFrame(PrimaryCCD.ImageFrameN[0].value, PrimaryCCD.ImageFrameN[1].value, PrimaryCCD.ImageFrameN[2].value,
728  PrimaryCCD.ImageFrameN[3].value) == false)
729  PrimaryCCD.ImageFrameNP->s = IPS_ALERT;
730 
731  IDSetNumber(PrimaryCCD.ImageFrameNP, NULL);
732  return true;
733  }
734 
735  if(strcmp(name,"GUIDER_FRAME")==0)
736  {
737  // We are being asked to set camera binning
738  GuideCCD.ImageFrameNP->s=IPS_OK;
739  IUUpdateNumber(GuideCCD.ImageFrameNP,values,names,n);
740 
741  DEBUGF(Logger::DBG_DEBUG, "GuiderFrame set to %4.0f,%4.0f %4.0f x %4.0f",
742  GuideCCD.ImageFrameN[0].value,GuideCCD.ImageFrameN[1].value,GuideCCD.ImageFrameN[2].value,GuideCCD.ImageFrameN[3].value);
743 
744  if (UpdateGuiderFrame(GuideCCD.ImageFrameN[0].value, GuideCCD.ImageFrameN[1].value, GuideCCD.ImageFrameN[2].value,
745  GuideCCD.ImageFrameN[3].value) == false)
746  GuideCCD.ImageFrameNP->s = IPS_ALERT;
747 
748  IDSetNumber(GuideCCD.ImageFrameNP, NULL);
749 
750  return true;
751  }
752 
753  if(strcmp(name,"CCD_GUIDESTAR")==0)
754  {
755  PrimaryCCD.RapidGuideDataNP->s=IPS_OK;
756  IUUpdateNumber(PrimaryCCD.RapidGuideDataNP,values,names,n);
757  IDSetNumber(PrimaryCCD.RapidGuideDataNP, NULL);
758  return true;
759  }
760 
761  if(strcmp(name,"GUIDER_GUIDESTAR")==0)
762  {
763  GuideCCD.RapidGuideDataNP->s=IPS_OK;
764  IUUpdateNumber(GuideCCD.RapidGuideDataNP,values,names,n);
765  IDSetNumber(GuideCCD.RapidGuideDataNP, NULL);
766  return true;
767  }
768 
769  if (!strcmp(name,GuideNSNP.name) || !strcmp(name,GuideWENP.name))
770  {
771  processGuiderProperties(name, values, names, n);
772  return true;
773  }
774 
775  // CCD TEMPERATURE:
776  if(!strcmp(name, TemperatureNP.name))
777  {
778 
779  if(values[0] < TemperatureN[0].min || values[0] > TemperatureN[0].max)
780  {
781  TemperatureNP.s = IPS_ALERT;
782  DEBUGF(INDI::Logger::DBG_ERROR, "Error: Bad temperature value! Range is [%.1f, %.1f] [C].",
783  TemperatureN[0].min, TemperatureN[0].max);
784  IDSetNumber(&TemperatureNP, NULL);
785  return false;
786 
787  }
788 
789  int rc= SetTemperature(values[0]);
790 
791  if (rc == 0)
792  TemperatureNP.s = IPS_BUSY;
793  else if (rc == 1)
794  TemperatureNP.s = IPS_OK;
795  else
796  TemperatureNP.s = IPS_ALERT;
797 
798  IDSetNumber(&TemperatureNP, NULL);
799  return true;
800  }
801  }
802  // if we didn't process it, continue up the chain, let somebody else
803  // give it a shot
804  return DefaultDevice::ISNewNumber(dev,name,values,names,n);
805 }
806 
807 bool INDI::CCD::ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n)
808 {
809 
810  if(strcmp(dev,getDeviceName())==0)
811  {
812  if(strcmp(name,PrimaryCCD.AbortExposureSP->name)==0)
813  {
814  IUResetSwitch(PrimaryCCD.AbortExposureSP);
815 
816  if (AbortExposure())
817  {
818  PrimaryCCD.AbortExposureSP->s = IPS_OK;
819  PrimaryCCD.ImageExposureNP->s = IPS_IDLE;
820  PrimaryCCD.ImageExposureN[0].value = 0;
821  }
822  else
823  {
824  PrimaryCCD.AbortExposureSP->s = IPS_ALERT;
825  PrimaryCCD.ImageExposureNP->s = IPS_ALERT;
826  }
827 
828  IDSetSwitch(PrimaryCCD.AbortExposureSP, NULL);
829  IDSetNumber(PrimaryCCD.ImageExposureNP, NULL);
830 
831  return true;
832  }
833 
834  if(strcmp(name,GuideCCD.AbortExposureSP->name)==0)
835  {
836  IUResetSwitch(GuideCCD.AbortExposureSP);
837 
838  if (AbortGuideExposure())
839  {
840  GuideCCD.AbortExposureSP->s = IPS_OK;
841  GuideCCD.ImageExposureNP->s = IPS_IDLE;
842  GuideCCD.ImageExposureN[0].value = 0;
843  }
844  else
845  {
846  GuideCCD.AbortExposureSP->s = IPS_ALERT;
847  GuideCCD.ImageExposureNP->s = IPS_ALERT;
848  }
849 
850  IDSetSwitch(GuideCCD.AbortExposureSP, NULL);
851  IDSetNumber(GuideCCD.ImageExposureNP, NULL);
852 
853  return true;
854  }
855 
856  if(strcmp(name,PrimaryCCD.CompressSP->name)==0)
857  {
858 
859  IUUpdateSwitch(PrimaryCCD.CompressSP,states,names,n);
860  IDSetSwitch(PrimaryCCD.CompressSP,NULL);
861 
862  if(PrimaryCCD.CompressS[0].s==ISS_ON )
863  {
864  PrimaryCCD.SendCompressed=true;
865  } else
866  {
867  PrimaryCCD.SendCompressed=false;
868  }
869  return true;
870  }
871 
872  if(strcmp(name,GuideCCD.CompressSP->name)==0)
873  {
874 
875  IUUpdateSwitch(GuideCCD.CompressSP,states,names,n);
876  IDSetSwitch(GuideCCD.CompressSP,NULL);
877 
878  if(GuideCCD.CompressS[0].s==ISS_ON )
879  {
880  GuideCCD.SendCompressed=true;
881  } else
882  {
883  GuideCCD.SendCompressed=false;
884  }
885  return true;
886  }
887 
888  if(strcmp(name,PrimaryCCD.FrameTypeSP->name)==0)
889  {
890  IUUpdateSwitch(PrimaryCCD.FrameTypeSP,states,names,n);
891  PrimaryCCD.FrameTypeSP->s=IPS_OK;
892  if(PrimaryCCD.FrameTypeS[0].s==ISS_ON)
893  PrimaryCCD.setFrameType(CCDChip::LIGHT_FRAME);
894  else if(PrimaryCCD.FrameTypeS[1].s==ISS_ON)
895  {
896  PrimaryCCD.setFrameType(CCDChip::BIAS_FRAME);
897  if (capability.hasShutter == false)
898  DEBUG(INDI::Logger::DBG_WARNING, "The CCD does not have a shutter. Cover the camera in order to take a bias frame.");
899  }
900  else if(PrimaryCCD.FrameTypeS[2].s==ISS_ON)
901  {
902  PrimaryCCD.setFrameType(CCDChip::DARK_FRAME);
903  if (capability.hasShutter == false)
904  DEBUG(INDI::Logger::DBG_WARNING, "The CCD does not have a shutter. Cover the camera in order to take a dark frame.");
905  }
906  else if(PrimaryCCD.FrameTypeS[3].s==ISS_ON)
907  PrimaryCCD.setFrameType(CCDChip::FLAT_FRAME);
908 
909  if (UpdateCCDFrameType(PrimaryCCD.getFrameType()) == false)
910  PrimaryCCD.FrameTypeSP->s = IPS_ALERT;
911 
912  IDSetSwitch(PrimaryCCD.FrameTypeSP,NULL);
913 
914  return true;
915  }
916 
917  if(strcmp(name,GuideCCD.FrameTypeSP->name)==0)
918  {
919  // Compression Update
920  IUUpdateSwitch(GuideCCD.FrameTypeSP,states,names,n);
921  GuideCCD.FrameTypeSP->s=IPS_OK;
922  if(GuideCCD.FrameTypeS[0].s==ISS_ON)
923  GuideCCD.setFrameType(CCDChip::LIGHT_FRAME);
924  else if(GuideCCD.FrameTypeS[1].s==ISS_ON)
925  {
926  GuideCCD.setFrameType(CCDChip::BIAS_FRAME);
927  if (capability.hasShutter == false)
928  DEBUG(INDI::Logger::DBG_WARNING, "The CCD does not have a shutter. Cover the camera in order to take a bias frame.");
929  }
930  else if(GuideCCD.FrameTypeS[2].s==ISS_ON)
931  {
932  GuideCCD.setFrameType(CCDChip::DARK_FRAME);
933  if (capability.hasShutter == false)
934  DEBUG(INDI::Logger::DBG_WARNING, "The CCD does not have a shutter. Cover the camera in order to take a dark frame.");
935  }
936  else if(GuideCCD.FrameTypeS[3].s==ISS_ON)
937  GuideCCD.setFrameType(CCDChip::FLAT_FRAME);
938 
939  if (UpdateGuiderFrameType(GuideCCD.getFrameType()) == false)
940  GuideCCD.FrameTypeSP->s = IPS_ALERT;
941 
942  IDSetSwitch(GuideCCD.FrameTypeSP,NULL);
943 
944  return true;
945  }
946 
947 
948  if (strcmp(name, PrimaryCCD.RapidGuideSP->name)==0)
949  {
950  IUUpdateSwitch(PrimaryCCD.RapidGuideSP, states, names, n);
951  PrimaryCCD.RapidGuideSP->s=IPS_OK;
952  RapidGuideEnabled=(PrimaryCCD.RapidGuideS[0].s==ISS_ON);
953 
954  if (RapidGuideEnabled) {
955  defineSwitch(PrimaryCCD.RapidGuideSetupSP);
956  defineNumber(PrimaryCCD.RapidGuideDataNP);
957  }
958  else {
959  deleteProperty(PrimaryCCD.RapidGuideSetupSP->name);
960  deleteProperty(PrimaryCCD.RapidGuideDataNP->name);
961  }
962 
963  IDSetSwitch(PrimaryCCD.RapidGuideSP,NULL);
964  return true;
965  }
966 
967  if (strcmp(name, GuideCCD.RapidGuideSP->name)==0)
968  {
969  IUUpdateSwitch(GuideCCD.RapidGuideSP, states, names, n);
970  GuideCCD.RapidGuideSP->s=IPS_OK;
971  GuiderRapidGuideEnabled=(GuideCCD.RapidGuideS[0].s==ISS_ON);
972 
973  if (GuiderRapidGuideEnabled) {
974  defineSwitch(GuideCCD.RapidGuideSetupSP);
975  defineNumber(GuideCCD.RapidGuideDataNP);
976  }
977  else {
978  deleteProperty(GuideCCD.RapidGuideSetupSP->name);
979  deleteProperty(GuideCCD.RapidGuideDataNP->name);
980  }
981 
982  IDSetSwitch(GuideCCD.RapidGuideSP,NULL);
983  return true;
984  }
985 
986  if (strcmp(name, PrimaryCCD.RapidGuideSetupSP->name)==0)
987  {
988  IUUpdateSwitch(PrimaryCCD.RapidGuideSetupSP, states, names, n);
989  PrimaryCCD.RapidGuideSetupSP->s=IPS_OK;
990 
991  AutoLoop=(PrimaryCCD.RapidGuideSetupS[0].s==ISS_ON);
992  SendImage=(PrimaryCCD.RapidGuideSetupS[1].s==ISS_ON);
993  ShowMarker=(PrimaryCCD.RapidGuideSetupS[2].s==ISS_ON);
994 
995  IDSetSwitch(PrimaryCCD.RapidGuideSetupSP,NULL);
996  return true;
997  }
998 
999  if (strcmp(name, GuideCCD.RapidGuideSetupSP->name)==0)
1000  {
1001  IUUpdateSwitch(GuideCCD.RapidGuideSetupSP, states, names, n);
1002  GuideCCD.RapidGuideSetupSP->s=IPS_OK;
1003 
1004  GuiderAutoLoop=(GuideCCD.RapidGuideSetupS[0].s==ISS_ON);
1005  GuiderSendImage=(GuideCCD.RapidGuideSetupS[1].s==ISS_ON);
1006  GuiderShowMarker=(GuideCCD.RapidGuideSetupS[2].s==ISS_ON);
1007 
1008  IDSetSwitch(GuideCCD.RapidGuideSetupSP,NULL);
1009  return true;
1010  }
1011  }
1012 
1013  // let the default driver have a crack at it
1014  return DefaultDevice::ISNewSwitch(dev, name, states, names, n);
1015 }
1016 
1017 int INDI::CCD::SetTemperature(double temperature)
1018 {
1019  INDI_UNUSED(temperature);
1020  DEBUGF(INDI::Logger::DBG_WARNING, "INDI::CCD::SetTemperature %4.2f - Should never get here", temperature);
1021  return -1;
1022 }
1023 
1024 bool INDI::CCD::StartExposure(float duration)
1025 {
1026  DEBUGF(INDI::Logger::DBG_WARNING, "INDI::CCD::StartExposure %4.2f - Should never get here",duration);
1027  return false;
1028 }
1029 
1030 bool INDI::CCD::StartGuideExposure(float duration)
1031 {
1032  DEBUGF(INDI::Logger::DBG_WARNING, "INDI::CCD::StartGuide Exposure %4.2f - Should never get here",duration);
1033  return false;
1034 }
1035 
1037 {
1038  DEBUG(INDI::Logger::DBG_WARNING, "INDI::CCD::AbortExposure - Should never get here");
1039  return false;
1040 }
1041 
1043 {
1044  DEBUG(INDI::Logger::DBG_WARNING, "INDI::CCD::AbortGuideExposure - Should never get here");
1045  return false;
1046 }
1047 
1048 bool INDI::CCD::UpdateCCDFrame(int x, int y, int w, int h)
1049 {
1050  // Just set value, unless HW layer overrides this and performs its own processing
1051  PrimaryCCD.setFrame(x, y, w, h);
1052  return true;
1053 }
1054 
1055 bool INDI::CCD::UpdateGuiderFrame(int x, int y, int w, int h)
1056 {
1057  GuideCCD.setFrame(x,y, w,h);
1058  return true;
1059 }
1060 
1061 bool INDI::CCD::UpdateCCDBin(int hor, int ver)
1062 {
1063  // Just set value, unless HW layer overrides this and performs its own processing
1064  PrimaryCCD.setBin(hor,ver);
1065  return true;
1066 }
1067 
1068 bool INDI::CCD::UpdateGuiderBin(int hor, int ver)
1069 {
1070  // Just set value, unless HW layer overrides this and performs its own processing
1071  GuideCCD.setBin(hor, ver);
1072  return true;
1073 }
1074 
1075 bool INDI::CCD::UpdateCCDFrameType(CCDChip::CCD_FRAME fType)
1076 {
1077  INDI_UNUSED(fType);
1078  // Child classes can override this
1079  return true;
1080 }
1081 
1082 bool INDI::CCD::UpdateGuiderFrameType(CCDChip::CCD_FRAME fType)
1083 {
1084  INDI_UNUSED(fType);
1085  // Child classes can override this
1086  return true;
1087 }
1088 
1089 void INDI::CCD::addFITSKeywords(fitsfile *fptr, CCDChip *targetChip)
1090 {
1091  int status=0;
1092  char frame_s[32];
1093  char dev_name[32];
1094  char exp_start[32];
1095  double min_val, max_val;
1096  double exposureDuration;
1097  double pixSize1,pixSize2;
1098  unsigned int xbin, ybin;
1099 
1100  if (targetChip->getNAxis() == 2)
1101  getMinMax(&min_val, &max_val, targetChip);
1102 
1103  xbin = targetChip->getBinX();
1104  ybin = targetChip->getBinY();
1105 
1106  switch (targetChip->getFrameType())
1107  {
1108  case CCDChip::LIGHT_FRAME:
1109  strcpy(frame_s, "Light");
1110  break;
1111  case CCDChip::BIAS_FRAME:
1112  strcpy(frame_s, "Bias");
1113  break;
1114  case CCDChip::FLAT_FRAME:
1115  strcpy(frame_s, "Flat Field");
1116  break;
1117  case CCDChip::DARK_FRAME:
1118  strcpy(frame_s, "Dark");
1119  break;
1120  }
1121 
1122  exposureDuration = targetChip->getExposureDuration();
1123 
1124  pixSize1 = targetChip->getPixelSizeX();
1125  pixSize2 = targetChip->getPixelSizeY();
1126 
1127  strncpy(dev_name, getDeviceName(), 32);
1128  strncpy(exp_start, targetChip->getExposureStartTime(), 32);
1129 
1130  fits_update_key_s(fptr, TDOUBLE, "EXPTIME", &(exposureDuration), "Total Exposure Time (s)", &status);
1131 
1132  if(targetChip->getFrameType() == CCDChip::DARK_FRAME)
1133  fits_update_key_s(fptr, TDOUBLE, "DARKTIME", &(exposureDuration), "Total Exposure Time (s)", &status);
1134 
1135  fits_update_key_s(fptr, TDOUBLE, "PIXSIZE1", &(pixSize1), "Pixel Size 1 (microns)", &status);
1136  fits_update_key_s(fptr, TDOUBLE, "PIXSIZE2", &(pixSize2), "Pixel Size 2 (microns)", &status);
1137  fits_update_key_s(fptr, TUINT, "XBINNING", &(xbin) , "Binning factor in width", &status);
1138  fits_update_key_s(fptr, TUINT, "YBINNING", &(ybin), "Binning factor in height", &status);
1139  fits_update_key_s(fptr, TSTRING, "FRAME", frame_s, "Frame Type", &status);
1140 
1141  if (targetChip->getNAxis() == 2)
1142  {
1143  fits_update_key_s(fptr, TDOUBLE, "DATAMIN", &min_val, "Minimum value", &status);
1144  fits_update_key_s(fptr, TDOUBLE, "DATAMAX", &max_val, "Maximum value", &status);
1145  }
1146 
1147  if (RA != -1000 && Dec != -1000)
1148  {
1149  fits_update_key_s(fptr, TDOUBLE, "OBJCTRA", &RA, "Object RA", &status);
1150  fits_update_key_s(fptr, TDOUBLE, "OBJCTDEC", &Dec, "Object DEC", &status);
1151  }
1152 
1153  fits_update_key_s(fptr, TSTRING, "INSTRUME", dev_name, "CCD Name", &status);
1154  fits_update_key_s(fptr, TSTRING, "DATE-OBS", exp_start, "UTC start date of observation", &status);
1155 
1156 }
1157 
1158 void INDI::CCD::fits_update_key_s(fitsfile* fptr, int type, std::string name, void* p, std::string explanation, int* status)
1159 {
1160  // this function is for removing warnings about deprecated string conversion to char* (from arg 5)
1161  fits_update_key(fptr,type,name.c_str(),p, const_cast<char*>(explanation.c_str()), status);
1162 }
1163 
1165 {
1166  bool sendImage = true;
1167  bool showMarker = false;
1168  bool autoLoop = false;
1169  bool sendData = false;
1170 
1171  if (RapidGuideEnabled && targetChip == &PrimaryCCD)
1172  {
1173  autoLoop = AutoLoop;
1174  sendImage = SendImage;
1175  showMarker = ShowMarker;
1176  sendData = true;
1177  }
1178 
1179  if (GuiderRapidGuideEnabled && targetChip == &GuideCCD)
1180  {
1181  autoLoop = GuiderAutoLoop;
1182  sendImage = GuiderSendImage;
1183  showMarker = GuiderShowMarker;
1184  sendData = true;
1185  }
1186 
1187  if (sendData)
1188  {
1189  static double P0 = 0.906, P1 = 0.584, P2 = 0.365, P3 = 0.117, P4 = 0.049, P5 = -0.05, P6 = -0.064, P7 = -0.074, P8 = -0.094;
1190  targetChip->RapidGuideDataNP->s=IPS_BUSY;
1191  int width = targetChip->getSubW() / targetChip->getBinX();
1192  int height = targetChip->getSubH() / targetChip->getBinY();
1193  unsigned short *src = (unsigned short *) targetChip->getFrameBuffer();
1194  int i0, i1, i2, i3, i4, i5, i6, i7, i8;
1195  int ix = 0, iy = 0;
1196  int xM4;
1197  unsigned short *p;
1198  double average, fit, bestFit = 0;
1199  int minx = 4;
1200  int maxx = width -4;
1201  int miny = 4;
1202  int maxy = height -4;
1203  if (targetChip->lastRapidX > 0 && targetChip->lastRapidY > 0) {
1204  minx = std::max(targetChip->lastRapidX - 20, 4);
1205  maxx = std::min(targetChip->lastRapidX + 20, width - 4);
1206  miny = std::max(targetChip->lastRapidY - 20, 4);
1207  maxy = std::min(targetChip->lastRapidY + 20, height -4);
1208  }
1209  for (int x = minx; x < maxx; x++)
1210  for (int y = miny; y < maxy; y++)
1211  {
1212  i0 = i1 = i2 = i3 = i4 = i5 = i6 = i7 = i8 = 0;
1213  xM4 = x - 4;
1214  p = src + (y - 4) * width + xM4; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++;
1215  p = src + (y - 3) * width + xM4; i8 += *p++; i8 += *p++; i8 += *p++; i7 += *p++; i6 += *p++; i7 += *p++; i8 += *p++; i8 += *p++; i8 += *p++;
1216  p = src + (y - 2) * width + xM4; i8 += *p++; i8 += *p++; i5 += *p++; i4 += *p++; i3 += *p++; i4 += *p++; i5 += *p++; i8 += *p++; i8 += *p++;
1217  p = src + (y - 1) * width + xM4; i8 += *p++; i7 += *p++; i4 += *p++; i2 += *p++; i1 += *p++; i2 += *p++; i4 += *p++; i8 += *p++; i8 += *p++;
1218  p = src + (y + 0) * width + xM4; i8 += *p++; i6 += *p++; i3 += *p++; i1 += *p++; i0 += *p++; i1 += *p++; i3 += *p++; i6 += *p++; i8 += *p++;
1219  p = src + (y + 1) * width + xM4; i8 += *p++; i7 += *p++; i4 += *p++; i2 += *p++; i1 += *p++; i2 += *p++; i4 += *p++; i8 += *p++; i8 += *p++;
1220  p = src + (y + 2) * width + xM4; i8 += *p++; i8 += *p++; i5 += *p++; i4 += *p++; i3 += *p++; i4 += *p++; i5 += *p++; i8 += *p++; i8 += *p++;
1221  p = src + (y + 3) * width + xM4; i8 += *p++; i8 += *p++; i8 += *p++; i7 += *p++; i6 += *p++; i7 += *p++; i8 += *p++; i8 += *p++; i8 += *p++;
1222  p = src + (y + 4) * width + xM4; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++;
1223  average = (i0 + i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8) / 85.0;
1224  fit = P0 * (i0 - average) + P1 * (i1 - 4 * average) + P2 * (i2 - 4 * average) + P3 * (i3 - 4 * average) + P4 * (i4 - 8 * average) + P5 * (i5 - 4 * average) + P6 * (i6 - 4 * average) + P7 * (i7 - 8 * average) + P8 * (i8 - 48 * average);
1225  if (bestFit < fit)
1226  {
1227  bestFit = fit;
1228  ix = x;
1229  iy = y;
1230  }
1231  }
1232 
1233  targetChip->RapidGuideDataN[0].value = ix;
1234  targetChip->RapidGuideDataN[1].value = iy;
1235  targetChip->RapidGuideDataN[2].value = bestFit;
1236  targetChip->lastRapidX = ix;
1237  targetChip->lastRapidY = iy;
1238  if (bestFit > 50)
1239  {
1240  double sumX = 0;
1241  double sumY = 0;
1242  double total = 0;
1243  for (int y = iy - 4; y <= iy + 4; y++) {
1244  p = src + y * width + ix - 4;
1245  for (int x = ix - 4; x <= ix + 4; x++) {
1246  double w = *p++;
1247  sumX += x * w;
1248  sumY += y * w;
1249  total += w;
1250  }
1251  }
1252  if (total > 0)
1253  {
1254  targetChip->RapidGuideDataN[0].value = sumX/total;
1255  targetChip->RapidGuideDataN[1].value = sumY/total;
1256  targetChip->RapidGuideDataNP->s=IPS_OK;
1257 
1258  DEBUGF(INDI::Logger::DBG_DEBUG, "Guide Star X: %g Y: %g FIT: %g", targetChip->RapidGuideDataN[0].value, targetChip->RapidGuideDataN[1].value,
1259  targetChip->RapidGuideDataN[2].value);
1260  }
1261  else {
1262  targetChip->RapidGuideDataNP->s=IPS_ALERT;
1263  targetChip->lastRapidX = targetChip->lastRapidY = -1;
1264  }
1265  }
1266  else {
1267  targetChip->RapidGuideDataNP->s=IPS_ALERT;
1268  targetChip->lastRapidX = targetChip->lastRapidY = -1;
1269  }
1270  IDSetNumber(targetChip->RapidGuideDataNP,NULL);
1271 
1272  if (showMarker)
1273  {
1274  int xmin = std::max(ix - 10, 0);
1275  int xmax = std::min(ix + 10, width - 1);
1276  int ymin = std::max(iy - 10, 0);
1277  int ymax = std::min(iy + 10, height - 1);
1278 
1279  //fprintf(stderr, "%d %d %d %d\n", xmin, xmax, ymin, ymax);
1280 
1281  if (ymin > 0)
1282  {
1283  p = src + ymin * width + xmin;
1284  for (int x = xmin; x <= xmax; x++)
1285  *p++ = 50000;
1286  }
1287 
1288  if (xmin > 0)
1289  {
1290  for (int y = ymin; y<= ymax; y++)
1291  {
1292  *(src + y * width + xmin) = 50000;
1293  }
1294  }
1295 
1296  if (xmax < width - 1)
1297  {
1298  for (int y = ymin; y<= ymax; y++)
1299  {
1300  *(src + y * width + xmax) = 50000;
1301  }
1302  }
1303 
1304  if (ymax < height -1)
1305  {
1306  p = src + ymax * width + xmin;
1307  for (int x = xmin; x <= xmax; x++)
1308  *p++ = 50000;
1309  }
1310  }
1311  }
1312 
1313  if (sendImage)
1314  {
1315  if (!strcmp(targetChip->getImageExtension(), "fits"))
1316  {
1317  void *memptr;
1318  size_t memsize;
1319  int img_type=0;
1320  int byte_type=0;
1321  int status=0;
1322  long naxis=targetChip->getNAxis();
1323  long naxes[naxis];
1324  int nelements=0;
1325  std::string bit_depth;
1326 
1327  fitsfile *fptr=NULL;
1328 
1329  naxes[0]=targetChip->getSubW()/targetChip->getBinX();
1330  naxes[1]=targetChip->getSubH()/targetChip->getBinY();
1331 
1332  switch (targetChip->getBPP())
1333  {
1334  case 8:
1335  byte_type = TBYTE;
1336  img_type = BYTE_IMG;
1337  bit_depth = "8 bits per pixel";
1338  break;
1339 
1340  case 16:
1341  byte_type = TUSHORT;
1342  img_type = USHORT_IMG;
1343  bit_depth = "16 bits per pixel";
1344  break;
1345 
1346  case 32:
1347  byte_type = TULONG;
1348  img_type = ULONG_IMG;
1349  bit_depth = "32 bits per pixel";
1350  break;
1351 
1352  default:
1353  DEBUGF(Logger::DBG_WARNING, "Unsupported bits per pixel value %d\n", targetChip->getBPP() );
1354  return false;
1355  break;
1356  }
1357 
1358  nelements = naxes[0] * naxes[1];
1359  if (naxis== 3)
1360  {
1361  nelements *= 3;
1362  naxes[2] = 3;
1363  }
1364 
1365  /*DEBUGF(Logger::DBG_DEBUG, "Exposure complete. Image Depth: %s. Width: %d Height: %d nelements: %d", bit_depth.c_str(), naxes[0],
1366  naxes[1], nelements);*/
1367 
1368  // Now we have to send fits format data to the client
1369  memsize=5760;
1370  memptr=malloc(memsize);
1371  if(!memptr)
1372  {
1373  IDLog("Error: failed to allocate memory: %lu\n",(unsigned long)memsize);
1374  }
1375 
1376  fits_create_memfile(&fptr,&memptr,&memsize,2880,realloc,&status);
1377 
1378  if(status)
1379  {
1380  IDLog("Error: Failed to create FITS image\n");
1381  fits_report_error(stderr, status); /* print out any error messages */
1382  return false;
1383  }
1384 
1385  fits_create_img(fptr, img_type , naxis, naxes, &status);
1386 
1387  if (status)
1388  {
1389  IDLog("Error: Failed to create FITS image\n");
1390  fits_report_error(stderr, status); /* print out any error messages */
1391  return false;
1392  }
1393 
1394  addFITSKeywords(fptr, targetChip);
1395 
1396  fits_write_img(fptr,byte_type,1,nelements,targetChip->getFrameBuffer(),&status);
1397 
1398  if (status)
1399  {
1400  IDLog("Error: Failed to write FITS image\n");
1401  fits_report_error(stderr, status); /* print out any error messages */
1402  return false;
1403  }
1404 
1405  fits_close_file(fptr,&status);
1406 
1407  uploadFile(targetChip, memptr, memsize);
1408 
1409  free(memptr);
1410  }
1411  else
1412  {
1413  uploadFile(targetChip, targetChip->getFrameBuffer(), targetChip->getFrameBufferSize());
1414  }
1415 
1416 
1417  }
1418 
1419  targetChip->ImageExposureNP->s=IPS_OK;
1420  IDSetNumber(targetChip->ImageExposureNP,NULL);
1421 
1422  if (autoLoop)
1423  {
1424  if (targetChip == &PrimaryCCD)
1425  {
1426  PrimaryCCD.ImageExposureN[0].value = ExposureTime;
1427  PrimaryCCD.ImageExposureNP->s=IPS_BUSY;
1428  if (StartExposure(ExposureTime))
1429  PrimaryCCD.ImageExposureNP->s=IPS_BUSY;
1430  else
1431  {
1432  DEBUG(INDI::Logger::DBG_DEBUG, "Autoloop: Primary CCD Exposure Error!");
1433  PrimaryCCD.ImageExposureNP->s=IPS_ALERT;
1434  }
1435 
1436  IDSetNumber(PrimaryCCD.ImageExposureNP,NULL);
1437  }
1438  else
1439  {
1440  GuideCCD.ImageExposureN[0].value = GuiderExposureTime;
1441  GuideCCD.ImageExposureNP->s=IPS_BUSY;
1442  if (StartGuideExposure(GuiderExposureTime))
1443  GuideCCD.ImageExposureNP->s=IPS_BUSY;
1444  else
1445  {
1446  DEBUG(INDI::Logger::DBG_DEBUG, "Autoloop: Guide CCD Exposure Error!");
1447  GuideCCD.ImageExposureNP->s=IPS_ALERT;
1448  }
1449 
1450  IDSetNumber(GuideCCD.ImageExposureNP,NULL);
1451  }
1452  }
1453 
1454  return true;
1455 }
1456 
1457 bool INDI::CCD::uploadFile(CCDChip * targetChip, const void *fitsData, size_t totalBytes)
1458 {
1459  unsigned char *compressedData = NULL;
1460  uLongf compressedBytes=0;
1461 
1462  if (targetChip->SendCompressed)
1463  {
1464  compressedBytes = sizeof(char) * totalBytes + totalBytes / 64 + 16 + 3;
1465  compressedData = (unsigned char *) malloc (compressedBytes);
1466 
1467  if (fitsData == NULL || compressedData == NULL)
1468  {
1469  if (compressedData)
1470  free(compressedData);
1471  DEBUG(INDI::Logger::DBG_ERROR, "Error: Ran out of memory compressing image");
1472  return false;
1473  }
1474 
1475  int r = compress2(compressedData, &compressedBytes, (const Bytef*)fitsData, totalBytes, 9);
1476  if (r != Z_OK)
1477  {
1478  /* this should NEVER happen */
1479  DEBUG(INDI::Logger::DBG_ERROR, "Error: Failed to compress image");
1480  return false;
1481  }
1482 
1483  targetChip->FitsB.blob=compressedData;
1484  targetChip->FitsB.bloblen=compressedBytes;
1485  snprintf(targetChip->FitsB.format, MAXINDIBLOBFMT, ".%s.z", targetChip->getImageExtension());
1486  } else
1487  {
1488  targetChip->FitsB.blob=(unsigned char *)fitsData;
1489  targetChip->FitsB.bloblen=totalBytes;
1490  snprintf(targetChip->FitsB.format, MAXINDIBLOBFMT, ".%s", targetChip->getImageExtension());
1491  }
1492 
1493  targetChip->FitsB.size = totalBytes;
1494  targetChip->FitsBP->s=IPS_OK;
1495  IDSetBLOB(targetChip->FitsBP,NULL);
1496 
1497  if (compressedData)
1498  free (compressedData);
1499 
1500  return true;
1501 }
1502 
1503 void INDI::CCD::SetCCDParams(int x,int y,int bpp,float xf,float yf)
1504 {
1505  PrimaryCCD.setResolution(x, y);
1506  PrimaryCCD.setFrame(0, 0, x, y);
1507  PrimaryCCD.setBin(1,1);
1508  PrimaryCCD.setPixelSize(xf, yf);
1509  PrimaryCCD.setBPP(bpp);
1510 
1511 }
1512 
1513 void INDI::CCD::SetGuiderParams(int x,int y,int bpp,float xf,float yf)
1514 {
1515  capability.hasGuideHead=true;
1516 
1517  GuideCCD.setResolution(x, y);
1518  GuideCCD.setFrame(0, 0, x, y);
1519  GuideCCD.setPixelSize(xf, yf);
1520  GuideCCD.setBPP(bpp);
1521 
1522 }
1523 
1524 bool INDI::CCD::saveConfigItems(FILE *fp)
1525 {
1526  IUSaveConfigText(fp, ActiveDeviceTP);
1527 
1528  if (capability.canSubFrame)
1529  IUSaveConfigNumber(fp, PrimaryCCD.ImageFrameNP);
1530 
1531  if (capability.canBin)
1532  IUSaveConfigNumber(fp, PrimaryCCD.ImageBinNP);
1533 
1534  return true;
1535 }
1536 
1538 {
1539  return false;
1540 }
1541 
1543 {
1544  return false;
1545 }
1546 
1547 bool INDI::CCD::GuideEast(float ms)
1548 {
1549  return false;
1550 }
1551 
1552 bool INDI::CCD::GuideWest(float ms)
1553 {
1554  return false;
1555 }
1556 
1557 void INDI::CCD::getMinMax(double *min, double *max, CCDChip *targetChip)
1558 {
1559  int ind=0, i, j;
1560  int imageHeight = targetChip->getSubH() / targetChip->getBinY();
1561  int imageWidth = targetChip->getSubW() / targetChip->getBinX();
1562  double lmin, lmax;
1563 
1564  switch (targetChip->getBPP())
1565  {
1566  case 8:
1567  {
1568  unsigned char *imageBuffer = (unsigned char *) targetChip->getFrameBuffer();
1569  lmin = lmax = imageBuffer[0];
1570 
1571 
1572  for (i= 0; i < imageHeight ; i++)
1573  for (j= 0; j < imageWidth; j++)
1574  {
1575  ind = (i * imageWidth) + j;
1576  if (imageBuffer[ind] < lmin) lmin = imageBuffer[ind];
1577  else if (imageBuffer[ind] > lmax) lmax = imageBuffer[ind];
1578  }
1579 
1580  }
1581  break;
1582 
1583  case 16:
1584  {
1585  unsigned short *imageBuffer = (unsigned short* ) targetChip->getFrameBuffer();
1586  lmin = lmax = imageBuffer[0];
1587 
1588  for (i= 0; i < imageHeight ; i++)
1589  for (j= 0; j < imageWidth; j++)
1590  {
1591  ind = (i * imageWidth) + j;
1592  if (imageBuffer[ind] < lmin) lmin = imageBuffer[ind];
1593  else if (imageBuffer[ind] > lmax) lmax = imageBuffer[ind];
1594  }
1595 
1596  }
1597  break;
1598 
1599  case 32:
1600  {
1601  unsigned int *imageBuffer = (unsigned int* ) targetChip->getFrameBuffer();
1602  lmin = lmax = imageBuffer[0];
1603 
1604  for (i= 0; i < imageHeight ; i++)
1605  for (j= 0; j < imageWidth; j++)
1606  {
1607  ind = (i * imageWidth) + j;
1608  if (imageBuffer[ind] < lmin) lmin = imageBuffer[ind];
1609  else if (imageBuffer[ind] > lmax) lmax = imageBuffer[ind];
1610 
1611  }
1612 
1613  }
1614  break;
1615 
1616  }
1617  *min = lmin;
1618  *max = lmax;
1619 }
1620 
1621