Instrument Neutral Distributed Interface INDI  1.2.0
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 <sys/stat.h>
31 #include <zlib.h>
32 #include <errno.h>
33 #include <dirent.h>
34 
35 #include <libnova.h>
36 #include <fitsio.h>
37 
38 #ifdef __linux__
39 #include "webcam/v4l2_record/stream_recorder.h"
40 #else
41 class StreamRecorder {};
42 #endif
43 
44 const char *IMAGE_SETTINGS_TAB = "Image Settings";
45 const char *IMAGE_INFO_TAB = "Image Info";
46 const char *GUIDE_HEAD_TAB = "Guider Head";
47 const char *GUIDE_CONTROL_TAB = "Guider Control";
48 const char *RAPIDGUIDE_TAB = "Rapid Guide";
49 const char *WCS_TAB = "WCS";
50 
51 CCDChip::CCDChip()
52 {
53  SendCompressed=false;
54  Interlaced=false;
55 
56  RawFrame= (uint8_t *) malloc(sizeof(uint8_t)); // Seed for realloc
57  RawFrameSize=0;
58 
59  BPP = 8;
60  BinX = BinY = 1;
61  NAxis = 2;
62 
63  BinFrame = NULL;
64 
65  strncpy(imageExtention, "fits", MAXINDIBLOBFMT);
66 
67  FrameType=LIGHT_FRAME;
68  lastRapidX = lastRapidY = -1;
69 }
70 
71 CCDChip::~CCDChip()
72 {
73  free(RawFrame);
74  RawFrameSize=0;
75  RawFrame=NULL;
76  free (BinFrame);
77 }
78 
79 void CCDChip::setFrameType(CCD_FRAME type)
80 {
81  FrameType=type;
82 }
83 
84 void CCDChip::setResolution(int x, int y)
85 {
86  XRes = x;
87  YRes = y;
88 
89  ImagePixelSizeN[0].value=x;
90  ImagePixelSizeN[1].value=y;
91 
92  IDSetNumber(&ImagePixelSizeNP, NULL);
93 
94  ImageFrameN[FRAME_X].min = 0;
95  ImageFrameN[FRAME_X].max = x-1;
96  ImageFrameN[FRAME_Y].min = 0;
97  ImageFrameN[FRAME_Y].max = y-1;
98 
99  ImageFrameN[FRAME_W].min = 1;
100  ImageFrameN[FRAME_W].max = x;
101  ImageFrameN[FRAME_H].max = 1;
102  ImageFrameN[FRAME_H].max = y;
103  IUUpdateMinMax(&ImageFrameNP);
104 }
105 
106 void CCDChip::setFrame(int subx, int suby, int subw, int subh)
107 {
108  SubX = subx;
109  SubY = suby;
110  SubW = subw;
111  SubH = subh;
112 
113  ImageFrameN[FRAME_X].value = SubX;
114  ImageFrameN[FRAME_Y].value = SubY;
115  ImageFrameN[FRAME_W].value = SubW;
116  ImageFrameN[FRAME_H].value = SubH;
117 
118  IDSetNumber(&ImageFrameNP, NULL);
119 }
120 
121 void CCDChip::setBin(int hor, int ver)
122 {
123  BinX = hor;
124  BinY = ver;
125 
126  ImageBinN[BIN_W].value = BinX;
127  ImageBinN[BIN_H].value = BinY;
128 
129  IDSetNumber(&ImageBinNP, NULL);
130 }
131 
132 
133 void CCDChip::setMinMaxStep(const char *property, const char *element, double min, double max, double step, bool sendToClient)
134 {
135  INumberVectorProperty *nvp = NULL;
136 
137  if (!strcmp(property, ImageExposureNP.name))
138  nvp = &ImageExposureNP;
139  else if (!strcmp(property, ImageFrameNP.name))
140  nvp = &ImageFrameNP;
141  else if (!strcmp(property, ImageBinNP.name))
142  nvp = &ImageBinNP;
143  else if (!strcmp(property, ImagePixelSizeNP.name))
144  nvp = &ImagePixelSizeNP;
145  else if (!strcmp(property, RapidGuideDataNP.name))
146  nvp = &RapidGuideDataNP;
147 
148  INumber *np = IUFindNumber(nvp, element);
149  if (np)
150  {
151  np->min = min;
152  np->max = max;
153  np->step = step;
154 
155  if (sendToClient)
156  IUUpdateMinMax(nvp);
157  }
158 
159 }
160 
161 void CCDChip::setPixelSize(float x, float y)
162 {
163  PixelSizex = x;
164  PixelSizey = y;
165 
166  ImagePixelSizeN[2].value=x;
167  ImagePixelSizeN[3].value=x;
168  ImagePixelSizeN[4].value=y;
169 
170  IDSetNumber(&ImagePixelSizeNP, NULL);
171 
172 }
173 
174 void CCDChip::setBPP(int bbp)
175 {
176  BPP = bbp;
177 
178  ImagePixelSizeN[5].value = BPP;
179 
180  IDSetNumber(&ImagePixelSizeNP, NULL);
181 }
182 
183 void CCDChip::setFrameBufferSize(int nbuf, bool allocMem)
184 {
185  if (nbuf == RawFrameSize)
186  return;
187 
188  RawFrameSize = nbuf;
189 
190  if (allocMem == false)
191  return;
192 
193  RawFrame = (uint8_t *) realloc(RawFrame, nbuf * sizeof(uint8_t));
194 
195  if (BinFrame)
196  BinFrame = (uint8_t *) realloc(BinFrame, nbuf * sizeof(uint8_t));
197 }
198 
199 void CCDChip::setExposureLeft(double duration)
200 {
201  ImageExposureN[0].value = duration;
202 
203  IDSetNumber(&ImageExposureNP, NULL);
204 }
205 
206 void CCDChip::setExposureDuration(double duration)
207 {
208  exposureDuration = duration;
209  gettimeofday(&startExposureTime,NULL);
210 }
211 
212 const char *CCDChip::getFrameTypeName(CCD_FRAME fType)
213 {
214  return FrameTypeS[fType].name;
215 }
216 
218 {
219  static char ts[32];
220  struct tm *tp;
221  time_t t = (time_t) startExposureTime.tv_sec;
222 
223  //time (&t);
224  tp = gmtime (&t);
225  strftime (ts, sizeof(ts), "%Y-%m-%dT%H:%M:%S", tp);
226  return (ts);
227 }
228 
229 void CCDChip::setInterlaced(bool intr)
230 {
231  Interlaced = intr;
232 }
233 
235 {
236  ImageExposureNP.s = IPS_ALERT;
237  IDSetNumber(&ImageExposureNP, NULL);
238 }
239 
240 int CCDChip::getNAxis() const
241 {
242  return NAxis;
243 }
244 
245 void CCDChip::setNAxis(int value)
246 {
247  NAxis = value;
248 }
249 
250 void CCDChip::setImageExtension(const char *ext)
251 {
252  strncpy(imageExtention, ext, MAXINDIBLOBFMT);
253 }
254 
256 {
257  if (BinX == 1)
258  return;
259 
260  // Jasem: Keep full frame shadow in memory to enhance performance and just swap frame pointers after operation is complete
261  if (BinFrame == NULL)
262  BinFrame = (uint8_t*) malloc(RawFrameSize);
263 
264  memset(BinFrame, 0, RawFrameSize);
265 
266  switch (getBPP())
267  {
268  case 8:
269  {
270  uint8_t *bin_buf = BinFrame;
271  uint8_t val;
272  for (int i=0; i < SubH; i+= BinX)
273  for (int j=0; j < SubW; j+= BinX)
274  {
275  for (int k=0; k < BinX; k++)
276  {
277  for (int l=0; l < BinX; l++)
278  {
279  val = *(RawFrame + j + (i+k) * SubW + l);
280  if (val + *bin_buf > UINT8_MAX)
281  *bin_buf = UINT8_MAX;
282  else
283  *bin_buf += val;
284  }
285  }
286  bin_buf++;
287  }
288  }
289  break;
290 
291  case 16:
292  {
293  uint16_t *bin_buf = (uint16_t*) BinFrame;
294  uint16_t *RawFrame16 = (uint16_t*) RawFrame;
295  uint16_t val;
296  for (int i=0; i < SubH; i+= BinX)
297  for (int j=0; j < SubW; j+= BinX)
298  {
299  for (int k=0; k < BinX; k++)
300  {
301  for (int l=0; l < BinX; l++)
302  {
303  val = *(RawFrame16 + j + (i+k) * SubW + l);
304  if (val + *bin_buf > UINT16_MAX)
305  *bin_buf = UINT16_MAX;
306  else
307  *bin_buf += val;
308  }
309  }
310  bin_buf++;
311  }
312  }
313  break;
314 
315  default:
316  return;
317 
318  }
319 
320  // Swap frame pointers
321  uint8_t *rawFramePointer = RawFrame;
322  RawFrame = BinFrame;
323  // We just memset it next time we use it
324  BinFrame = rawFramePointer;
325 }
326 
327 INDI::CCD::CCD()
328 {
329  //ctor
330  capability = 0;
331 
332  InExposure=false;
333  InGuideExposure=false;
334  RapidGuideEnabled=false;
335  GuiderRapidGuideEnabled=false;
336  ValidCCDRotation=false;
337 
338  AutoLoop=false;
339  SendImage=false;
340  ShowMarker=false;
341  GuiderAutoLoop=false;
342  GuiderSendImage=false;
343  GuiderShowMarker=false;
344 
345  ExposureTime = 0.0;
346  GuiderExposureTime = 0.0;
347  CurrentFilterSlot=-1;
348 
349  RA=-1000;
350  Dec=-1000;
351  Aperture=FocalLength=-1;
352 
353  streamer = NULL;
354 }
355 
356 INDI::CCD::~CCD()
357 {
358  delete (streamer);
359 }
360 
361 void INDI::CCD::SetCCDCapability(uint32_t cap)
362 {
363  capability = cap;
364 
365  if (HasGuideHead())
366  setDriverInterface(getDriverInterface() | GUIDER_INTERFACE);
367  else
368  setDriverInterface(getDriverInterface() & ~GUIDER_INTERFACE);
369 
370 #ifdef __linux__
371  if (HasStreaming() && streamer == NULL)
372  {
373  delete (streamer);
374  streamer = new StreamRecorder(this);
375  streamer->initProperties();
376  }
377 #endif
378 
379 }
380 
382 {
383  DefaultDevice::initProperties(); // let the base class flesh in what it wants
384 
385  // CCD Temperature
386  IUFillNumber(&TemperatureN[0], "CCD_TEMPERATURE_VALUE", "Temperature (C)", "%5.2f", -50.0, 50.0, 0., 0.);
387  IUFillNumberVector(&TemperatureNP, TemperatureN, 1, getDeviceName(), "CCD_TEMPERATURE", "Temperature", MAIN_CONTROL_TAB, IP_RW, 60, IPS_IDLE);
388 
389  // PRIMARY CCD Init
390 
391  IUFillNumber(&PrimaryCCD.ImageFrameN[0],"X","Left ","%4.0f",0,1392.0,0,0);
392  IUFillNumber(&PrimaryCCD.ImageFrameN[1],"Y","Top","%4.0f",0,1040,0,0);
393  IUFillNumber(&PrimaryCCD.ImageFrameN[2],"WIDTH","Width","%4.0f",0,1392.0,0,1392.0);
394  IUFillNumber(&PrimaryCCD.ImageFrameN[3],"HEIGHT","Height","%4.0f",0,1392,0,1392.0);
395  IUFillNumberVector(&PrimaryCCD.ImageFrameNP,PrimaryCCD.ImageFrameN,4,getDeviceName(),"CCD_FRAME","Frame",IMAGE_SETTINGS_TAB,IP_RW,60,IPS_IDLE);
396 
397  IUFillSwitch(&PrimaryCCD.FrameTypeS[0],"FRAME_LIGHT","Light",ISS_ON);
398  IUFillSwitch(&PrimaryCCD.FrameTypeS[1],"FRAME_BIAS","Bias",ISS_OFF);
399  IUFillSwitch(&PrimaryCCD.FrameTypeS[2],"FRAME_DARK","Dark",ISS_OFF);
400  IUFillSwitch(&PrimaryCCD.FrameTypeS[3],"FRAME_FLAT","Flat",ISS_OFF);
401  IUFillSwitchVector(&PrimaryCCD.FrameTypeSP,PrimaryCCD.FrameTypeS,4,getDeviceName(),"CCD_FRAME_TYPE","Frame Type",IMAGE_SETTINGS_TAB,IP_RW,ISR_1OFMANY,60,IPS_IDLE);
402 
403  IUFillNumber(&PrimaryCCD.ImageExposureN[0],"CCD_EXPOSURE_VALUE","Duration (s)","%5.2f",0.01,3600,1.0,1.0);
404  IUFillNumberVector(&PrimaryCCD.ImageExposureNP,PrimaryCCD.ImageExposureN,1,getDeviceName(),"CCD_EXPOSURE","Expose",MAIN_CONTROL_TAB,IP_RW,60,IPS_IDLE);
405 
406  IUFillSwitch(&PrimaryCCD.AbortExposureS[0],"ABORT","Abort",ISS_OFF);
407  IUFillSwitchVector(&PrimaryCCD.AbortExposureSP,PrimaryCCD.AbortExposureS,1,getDeviceName(),"CCD_ABORT_EXPOSURE","Expose Abort",MAIN_CONTROL_TAB,IP_RW,ISR_ATMOST1,60,IPS_IDLE);
408 
409  IUFillNumber(&PrimaryCCD.ImageBinN[0],"HOR_BIN","X","%2.0f",1,4,1,1);
410  IUFillNumber(&PrimaryCCD.ImageBinN[1],"VER_BIN","Y","%2.0f",1,4,1,1);
411  IUFillNumberVector(&PrimaryCCD.ImageBinNP,PrimaryCCD.ImageBinN,2,getDeviceName(),"CCD_BINNING","Binning",IMAGE_SETTINGS_TAB,IP_RW,60,IPS_IDLE);
412 
413  IUFillNumber(&PrimaryCCD.ImagePixelSizeN[0],"CCD_MAX_X","Resolution x","%4.0f",1,16000,0,1392.0);
414  IUFillNumber(&PrimaryCCD.ImagePixelSizeN[1],"CCD_MAX_Y","Resolution y","%4.0f",1,16000,0,1392.0);
415  IUFillNumber(&PrimaryCCD.ImagePixelSizeN[2],"CCD_PIXEL_SIZE","Pixel size (um)","%5.2f",1,40,0,6.45);
416  IUFillNumber(&PrimaryCCD.ImagePixelSizeN[3],"CCD_PIXEL_SIZE_X","Pixel size X","%5.2f",1,40,0,6.45);
417  IUFillNumber(&PrimaryCCD.ImagePixelSizeN[4],"CCD_PIXEL_SIZE_Y","Pixel size Y","%5.2f",1,40,0,6.45);
418  IUFillNumber(&PrimaryCCD.ImagePixelSizeN[5],"CCD_BITSPERPIXEL","Bits per pixel","%3.0f",8,64,0,8);
419  IUFillNumberVector(&PrimaryCCD.ImagePixelSizeNP,PrimaryCCD.ImagePixelSizeN,6,getDeviceName(),"CCD_INFO","CCD Information",IMAGE_INFO_TAB,IP_RO,60,IPS_IDLE);
420 
421  IUFillSwitch(&PrimaryCCD.CompressS[0],"CCD_COMPRESS","Compress",ISS_OFF);
422  IUFillSwitch(&PrimaryCCD.CompressS[1],"CCD_RAW","Raw",ISS_ON);
423  IUFillSwitchVector(&PrimaryCCD.CompressSP,PrimaryCCD.CompressS,2,getDeviceName(),"CCD_COMPRESSION","Image",IMAGE_SETTINGS_TAB,IP_RW,ISR_1OFMANY,60,IPS_IDLE);
424  PrimaryCCD.SendCompressed = false;
425 
426  IUFillBLOB(&PrimaryCCD.FitsB,"CCD1","Image","");
427  IUFillBLOBVector(&PrimaryCCD.FitsBP,&PrimaryCCD.FitsB,1,getDeviceName(),"CCD1","Image Data",IMAGE_INFO_TAB,IP_RO,60,IPS_IDLE);
428 
429  IUFillSwitch(&PrimaryCCD.RapidGuideS[0], "ENABLE", "Enable", ISS_OFF);
430  IUFillSwitch(&PrimaryCCD.RapidGuideS[1], "DISABLE", "Disable", ISS_ON);
431  IUFillSwitchVector(&PrimaryCCD.RapidGuideSP, PrimaryCCD.RapidGuideS, 2, getDeviceName(), "CCD_RAPID_GUIDE", "Rapid Guide", OPTIONS_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
432 
433  IUFillSwitch(&PrimaryCCD.RapidGuideSetupS[0], "AUTO_LOOP", "Auto loop", ISS_ON);
434  IUFillSwitch(&PrimaryCCD.RapidGuideSetupS[1], "SEND_IMAGE", "Send image", ISS_OFF);
435  IUFillSwitch(&PrimaryCCD.RapidGuideSetupS[2], "SHOW_MARKER", "Show marker", ISS_OFF);
436  IUFillSwitchVector(&PrimaryCCD.RapidGuideSetupSP, PrimaryCCD.RapidGuideSetupS, 3, getDeviceName(), "CCD_RAPID_GUIDE_SETUP", "Rapid Guide Setup", RAPIDGUIDE_TAB, IP_RW, ISR_NOFMANY, 0, IPS_IDLE);
437 
438  IUFillNumber(&PrimaryCCD.RapidGuideDataN[0],"GUIDESTAR_X","Guide star position X","%5.2f",0,1024,0,0);
439  IUFillNumber(&PrimaryCCD.RapidGuideDataN[1],"GUIDESTAR_Y","Guide star position Y","%5.2f",0,1024,0,0);
440  IUFillNumber(&PrimaryCCD.RapidGuideDataN[2],"GUIDESTAR_FIT","Guide star fit","%5.2f",0,1024,0,0);
441  IUFillNumberVector(&PrimaryCCD.RapidGuideDataNP,PrimaryCCD.RapidGuideDataN,3,getDeviceName(),"CCD_RAPID_GUIDE_DATA","Rapid Guide Data",RAPIDGUIDE_TAB,IP_RO,60,IPS_IDLE);
442 
443  // Reset Frame Settings
444  IUFillSwitch(&PrimaryCCD.ResetS[0], "RESET", "Reset", ISS_OFF);
445  IUFillSwitchVector(&PrimaryCCD.ResetSP, PrimaryCCD.ResetS, 1, getDeviceName(), "CCD_FRAME_RESET", "Frame Values", IMAGE_SETTINGS_TAB, IP_WO, ISR_1OFMANY, 0, IPS_IDLE);
446 
447  // GUIDER CCD Init
448 
449  IUFillNumber(&GuideCCD.ImageFrameN[0],"X","Left ","%4.0f",0,1392.0,0,0);
450  IUFillNumber(&GuideCCD.ImageFrameN[1],"Y","Top","%4.0f",0,1040,0,0);
451  IUFillNumber(&GuideCCD.ImageFrameN[2],"WIDTH","Width","%4.0f",0,1392.0,0,1392.0);
452  IUFillNumber(&GuideCCD.ImageFrameN[3],"HEIGHT","Height","%4.0f",0,1040,0,1040);
453  IUFillNumberVector(&GuideCCD.ImageFrameNP,GuideCCD.ImageFrameN,4,getDeviceName(),"GUIDER_FRAME","Frame",GUIDE_HEAD_TAB,IP_RW,60,IPS_IDLE);
454 
455  IUFillNumber(&GuideCCD.ImageBinN[0],"HOR_BIN","X","%2.0f",1,4,1,1);
456  IUFillNumber(&GuideCCD.ImageBinN[1],"VER_BIN","Y","%2.0f",1,4,1,1);
457  IUFillNumberVector(&GuideCCD.ImageBinNP,GuideCCD.ImageBinN,2,getDeviceName(),"GUIDER_BINNING","Binning",GUIDE_HEAD_TAB,IP_RW,60,IPS_IDLE);
458 
459  IUFillNumber(&GuideCCD.ImagePixelSizeN[0],"CCD_MAX_X","Resolution x","%4.0f",1,16000,0,1392.0);
460  IUFillNumber(&GuideCCD.ImagePixelSizeN[1],"CCD_MAX_Y","Resolution y","%4.0f",1,16000,0,1392.0);
461  IUFillNumber(&GuideCCD.ImagePixelSizeN[2],"CCD_PIXEL_SIZE","Pixel size (um)","%5.2f",1,40,0,6.45);
462  IUFillNumber(&GuideCCD.ImagePixelSizeN[3],"CCD_PIXEL_SIZE_X","Pixel size X","%5.2f",1,40,0,6.45);
463  IUFillNumber(&GuideCCD.ImagePixelSizeN[4],"CCD_PIXEL_SIZE_Y","Pixel size Y","%5.2f",1,40,0,6.45);
464  IUFillNumber(&GuideCCD.ImagePixelSizeN[5],"CCD_BITSPERPIXEL","Bits per pixel","%3.0f",8,64,0,8);
465  IUFillNumberVector(&GuideCCD.ImagePixelSizeNP,GuideCCD.ImagePixelSizeN,6,getDeviceName(),"GUIDER_INFO", "Guide Info",IMAGE_INFO_TAB,IP_RO,60,IPS_IDLE);
466 
467  IUFillSwitch(&GuideCCD.FrameTypeS[0],"FRAME_LIGHT","Light",ISS_ON);
468  IUFillSwitch(&GuideCCD.FrameTypeS[1],"FRAME_BIAS","Bias",ISS_OFF);
469  IUFillSwitch(&GuideCCD.FrameTypeS[2],"FRAME_DARK","Dark",ISS_OFF);
470  IUFillSwitch(&GuideCCD.FrameTypeS[3],"FRAME_FLAT","Flat",ISS_OFF);
471  IUFillSwitchVector(&GuideCCD.FrameTypeSP,GuideCCD.FrameTypeS,4,getDeviceName(),"GUIDER_FRAME_TYPE","Frame Type",GUIDE_HEAD_TAB,IP_RW,ISR_1OFMANY,60,IPS_IDLE);
472 
473  IUFillNumber(&GuideCCD.ImageExposureN[0],"GUIDER_EXPOSURE_VALUE","Duration (s)","%5.2f",0.01,3600,1.0,1.0);
474  IUFillNumberVector(&GuideCCD.ImageExposureNP,GuideCCD.ImageExposureN,1,getDeviceName(),"GUIDER_EXPOSURE","Guide Head",MAIN_CONTROL_TAB,IP_RW,60,IPS_IDLE);
475 
476  IUFillSwitch(&GuideCCD.AbortExposureS[0],"ABORT","Abort",ISS_OFF);
477  IUFillSwitchVector(&GuideCCD.AbortExposureSP,GuideCCD.AbortExposureS,1,getDeviceName(),"GUIDER_ABORT_EXPOSURE","Guide Abort",MAIN_CONTROL_TAB,IP_RW,ISR_ATMOST1,60,IPS_IDLE);
478 
479  IUFillSwitch(&GuideCCD.CompressS[0],"GUIDER_COMPRESS","Compress",ISS_OFF);
480  IUFillSwitch(&GuideCCD.CompressS[1],"GUIDER_RAW","Raw",ISS_ON);
481  IUFillSwitchVector(&GuideCCD.CompressSP,GuideCCD.CompressS,2,getDeviceName(),"GUIDER_COMPRESSION","Image",GUIDE_HEAD_TAB,IP_RW,ISR_1OFMANY,60,IPS_IDLE);
482  GuideCCD.SendCompressed = false;
483 
484  IUFillBLOB(&GuideCCD.FitsB,"CCD2","Guider Image","");
485  IUFillBLOBVector(&GuideCCD.FitsBP,&GuideCCD.FitsB,1,getDeviceName(),"CCD2","Image Data",IMAGE_INFO_TAB,IP_RO,60,IPS_IDLE);
486 
487  IUFillSwitch(&GuideCCD.RapidGuideS[0], "ENABLE", "Enable", ISS_OFF);
488  IUFillSwitch(&GuideCCD.RapidGuideS[1], "DISABLE", "Disable", ISS_ON);
489  IUFillSwitchVector(&GuideCCD.RapidGuideSP, GuideCCD.RapidGuideS, 2, getDeviceName(), "GUIDER_RAPID_GUIDE", "Guider Head Rapid Guide", OPTIONS_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
490 
491  IUFillSwitch(&GuideCCD.RapidGuideSetupS[0], "AUTO_LOOP", "Auto loop", ISS_ON);
492  IUFillSwitch(&GuideCCD.RapidGuideSetupS[1], "SEND_IMAGE", "Send image", ISS_OFF);
493  IUFillSwitch(&GuideCCD.RapidGuideSetupS[2], "SHOW_MARKER", "Show marker", ISS_OFF);
494  IUFillSwitchVector(&GuideCCD.RapidGuideSetupSP, GuideCCD.RapidGuideSetupS, 3, getDeviceName(), "GUIDER_RAPID_GUIDE_SETUP", "Rapid Guide Setup", RAPIDGUIDE_TAB, IP_RW, ISR_NOFMANY, 0, IPS_IDLE);
495 
496  IUFillNumber(&GuideCCD.RapidGuideDataN[0],"GUIDESTAR_X","Guide star position X","%5.2f",0,1024,0,0);
497  IUFillNumber(&GuideCCD.RapidGuideDataN[1],"GUIDESTAR_Y","Guide star position Y","%5.2f",0,1024,0,0);
498  IUFillNumber(&GuideCCD.RapidGuideDataN[2],"GUIDESTAR_FIT","Guide star fit","%5.2f",0,1024,0,0);
499  IUFillNumberVector(&GuideCCD.RapidGuideDataNP,GuideCCD.RapidGuideDataN,3,getDeviceName(),"GUIDER_RAPID_GUIDE_DATA","Rapid Guide Data",RAPIDGUIDE_TAB,IP_RO,60,IPS_IDLE);
500 
501  // CCD Class Init
502 
503  IUFillText(&BayerT[0],"CFA_OFFSET_X","X Offset","0");
504  IUFillText(&BayerT[1],"CFA_OFFSET_Y","Y Offset","0");
505  IUFillText(&BayerT[2],"CFA_TYPE","Filter",NULL);
506  IUFillTextVector(&BayerTP,BayerT,3,getDeviceName(),"CCD_CFA","Bayer Info",IMAGE_INFO_TAB,IP_RW,60,IPS_IDLE);
507 
508  IUFillSwitch(&UploadS[0], "UPLOAD_CLIENT", "Client", ISS_ON);
509  IUFillSwitch(&UploadS[1], "UPLOAD_LOCAL", "Local", ISS_OFF);
510  IUFillSwitch(&UploadS[2], "UPLOAD_BOTH", "Both", ISS_OFF);
511  IUFillSwitchVector(&UploadSP, UploadS, 3, getDeviceName(), "UPLOAD_MODE", "Upload", OPTIONS_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
512 
513  IUFillText(&UploadSettingsT[0],"UPLOAD_DIR","Dir","");
514  IUFillText(&UploadSettingsT[1],"UPLOAD_PREFIX","Prefix","IMAGE_XXX");
515  IUFillTextVector(&UploadSettingsTP,UploadSettingsT,2,getDeviceName(),"UPLOAD_SETTINGS","Upload Settings",OPTIONS_TAB,IP_RW,60,IPS_IDLE);
516 
517  IUFillText(&ActiveDeviceT[0],"ACTIVE_TELESCOPE","Telescope","Telescope Simulator");
518  IUFillText(&ActiveDeviceT[1],"ACTIVE_FOCUSER","Focuser","Focuser Simulator");
519  IUFillText(&ActiveDeviceT[2],"ACTIVE_FILTER","Filter","CCD Simulator");
520  IUFillTextVector(&ActiveDeviceTP,ActiveDeviceT,3,getDeviceName(),"ACTIVE_DEVICES","Snoop devices",OPTIONS_TAB,IP_RW,60,IPS_IDLE);
521 
522  IUFillSwitch(&WorldCoordS[0], "WCS_ENABLE", "Enable", ISS_OFF);
523  IUFillSwitch(&WorldCoordS[1], "WCS_DISABLE", "Disable", ISS_ON);
524  IUFillSwitchVector(&WorldCoordSP, WorldCoordS, 2, getDeviceName(), "WCS_CONTROL", "WCS", WCS_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
525 
526  IUFillSwitch(&TelescopeTypeS[0], "TELESCOPE_PRIMARY", "Primary", ISS_ON);
527  IUFillSwitch(&TelescopeTypeS[1], "TELESCOPE_GUIDE", "Guide", ISS_OFF);
528  IUFillSwitchVector(&TelescopeTypeSP, TelescopeTypeS, 2, getDeviceName(), "TELESCOPE_TYPE", "Telescope", WCS_TAB, IP_RW, ISR_1OFMANY, 0, IPS_IDLE);
529 
530  IUFillNumber(&CCDRotationN[0],"CCD_ROTATION_VALUE","Rotation","%g",-360,360,1,0);
531  IUFillNumberVector(&CCDRotationNP,CCDRotationN,1,getDeviceName(),"CCD_ROTATION","CCD FOV", WCS_TAB,IP_RW,60,IPS_IDLE);
532 
533  IUFillNumber(&EqN[0],"RA","Ra (hh:mm:ss)","%010.6m",0,24,0,0);
534  IUFillNumber(&EqN[1],"DEC","Dec (dd:mm:ss)","%010.6m",-90,90,0,0);
535  IUFillNumberVector(&EqNP,EqN,2,ActiveDeviceT[0].text,"EQUATORIAL_EOD_COORD","EQ Coord","Main Control",IP_RW,60,IPS_IDLE);
536 
537  IDSnoopDevice(ActiveDeviceT[0].text,"EQUATORIAL_EOD_COORD");
538  IDSnoopDevice(ActiveDeviceT[0].text,"TELESCOPE_INFO");
539  IDSnoopDevice(ActiveDeviceT[2].text,"FILTER_SLOT");
540  IDSnoopDevice(ActiveDeviceT[2].text,"FILTER_NAME");
541 
542  // Guider Interface
543  initGuiderProperties(getDeviceName(), GUIDE_CONTROL_TAB);
544 
545  setDriverInterface(CCD_INTERFACE|GUIDER_INTERFACE);
546 
547  return true;
548 }
549 
550 void INDI::CCD::ISGetProperties (const char *dev)
551 {
553 
554  defineText(&ActiveDeviceTP);
555  loadConfig(true, "ACTIVE_DEVICES");
556 
557  // Streamer
558  #ifdef __linux__
559  if (HasStreaming())
560  streamer->ISGetProperties(dev);
561  #endif
562 
563  return;
564 }
565 
567 {
568  //IDLog("INDI::CCD UpdateProperties isConnected returns %d %d\n",isConnected(),Connected);
569  if(isConnected())
570  {
571  defineNumber(&PrimaryCCD.ImageExposureNP);
572 
573  if (CanAbort())
574  defineSwitch(&PrimaryCCD.AbortExposureSP);
575  if (CanSubFrame() == false)
576  PrimaryCCD.ImageFrameNP.p = IP_RO;
577 
578  defineNumber(&PrimaryCCD.ImageFrameNP);
579  if (CanBin())
580  defineNumber(&PrimaryCCD.ImageBinNP);
581 
582  if(HasGuideHead())
583  {
584  defineNumber(&GuideCCD.ImageExposureNP);
585  if (CanAbort())
586  defineSwitch(&GuideCCD.AbortExposureSP);
587  if (CanSubFrame() == false)
588  GuideCCD.ImageFrameNP.p = IP_RO;
589  defineNumber(&GuideCCD.ImageFrameNP);
590  }
591 
592  if (HasCooler())
593  defineNumber(&TemperatureNP);
594 
595  defineNumber(&PrimaryCCD.ImagePixelSizeNP);
596  if(HasGuideHead())
597  {
598  defineNumber(&GuideCCD.ImagePixelSizeNP);
599  if (CanBin())
600  defineNumber(&GuideCCD.ImageBinNP);
601  }
602  defineSwitch(&PrimaryCCD.CompressSP);
603  defineBLOB(&PrimaryCCD.FitsBP);
604  if(HasGuideHead())
605  {
606  defineSwitch(&GuideCCD.CompressSP);
607  defineBLOB(&GuideCCD.FitsBP);
608  }
609  if(HasST4Port())
610  {
611  defineNumber(&GuideNSNP);
612  defineNumber(&GuideWENP);
613  }
614  defineSwitch(&PrimaryCCD.FrameTypeSP);
615 
616  if (CanBin() || CanSubFrame())
617  defineSwitch(&PrimaryCCD.ResetSP);
618 
619  if (HasGuideHead())
620  defineSwitch(&GuideCCD.FrameTypeSP);
621 
622  if (HasBayer())
623  defineText(&BayerTP);
624 
625  defineSwitch(&PrimaryCCD.RapidGuideSP);
626 
627  if (HasGuideHead())
628  defineSwitch(&GuideCCD.RapidGuideSP);
629 
630  if (RapidGuideEnabled)
631  {
632  defineSwitch(&PrimaryCCD.RapidGuideSetupSP);
633  defineNumber(&PrimaryCCD.RapidGuideDataNP);
634  }
635  if (GuiderRapidGuideEnabled)
636  {
637  defineSwitch(&GuideCCD.RapidGuideSetupSP);
638  defineNumber(&GuideCCD.RapidGuideDataNP);
639  }
640  defineSwitch(&WorldCoordSP);
641  defineSwitch(&UploadSP);
642 
643  if (UploadSettingsT[0].text == NULL)
644  IUSaveText(&UploadSettingsT[0], getenv("HOME"));
645  defineText(&UploadSettingsTP);
646  }
647  else
648  {
649  deleteProperty(PrimaryCCD.ImageFrameNP.name);
650  deleteProperty(PrimaryCCD.ImagePixelSizeNP.name);
651 
652  if (CanBin())
653  deleteProperty(PrimaryCCD.ImageBinNP.name);
654 
655  deleteProperty(PrimaryCCD.ImageExposureNP.name);
656  if (CanAbort())
657  deleteProperty(PrimaryCCD.AbortExposureSP.name);
658  deleteProperty(PrimaryCCD.FitsBP.name);
659  deleteProperty(PrimaryCCD.CompressSP.name);
660  deleteProperty(PrimaryCCD.RapidGuideSP.name);
661  if (RapidGuideEnabled)
662  {
663  deleteProperty(PrimaryCCD.RapidGuideSetupSP.name);
664  deleteProperty(PrimaryCCD.RapidGuideDataNP.name);
665  }
666  if(HasGuideHead())
667  {
668  deleteProperty(GuideCCD.ImageExposureNP.name);
669  if (CanAbort())
670  deleteProperty(GuideCCD.AbortExposureSP.name);
671  deleteProperty(GuideCCD.ImageFrameNP.name);
672  deleteProperty(GuideCCD.ImagePixelSizeNP.name);
673 
674  deleteProperty(GuideCCD.FitsBP.name);
675  if (CanBin())
676  deleteProperty(GuideCCD.ImageBinNP.name);
677  deleteProperty(GuideCCD.CompressSP.name);
678  deleteProperty(GuideCCD.FrameTypeSP.name);
679  deleteProperty(GuideCCD.RapidGuideSP.name);
680  if (GuiderRapidGuideEnabled)
681  {
682  deleteProperty(GuideCCD.RapidGuideSetupSP.name);
683  deleteProperty(GuideCCD.RapidGuideDataNP.name);
684  }
685  }
686  if (HasCooler())
687  deleteProperty(TemperatureNP.name);
688  if(HasST4Port())
689  {
690  deleteProperty(GuideNSNP.name);
691  deleteProperty(GuideWENP.name);
692  }
693  deleteProperty(PrimaryCCD.FrameTypeSP.name);
694  if (CanBin() || CanSubFrame())
695  deleteProperty(PrimaryCCD.ResetSP.name);
696  if (HasBayer())
697  deleteProperty(BayerTP.name);
698  if (WorldCoordS[0].s == ISS_ON)
699  {
700  deleteProperty(TelescopeTypeSP.name);
701  deleteProperty(CCDRotationNP.name);
702  }
703  deleteProperty(WorldCoordSP.name);
704  deleteProperty(UploadSP.name);
705  deleteProperty(UploadSettingsTP.name);
706  }
707 
708  // Streamer
709  #ifdef __linux__
710  if (HasStreaming())
711  streamer->updateProperties();
712  #endif
713 
714  return true;
715 }
716 
717 bool INDI::CCD::ISSnoopDevice (XMLEle *root)
718 {
719  XMLEle *ep=NULL;
720  const char *propName = findXMLAttValu(root, "name");
721 
722  if(IUSnoopNumber(root,&EqNP)==0)
723  {
724  float newra,newdec;
725  newra=EqN[0].value;
726  newdec=EqN[1].value;
727  if((newra != RA)||(newdec != Dec))
728  {
729  //IDLog("RA %4.2f Dec %4.2f Snooped RA %4.2f Dec %4.2f\n",RA,Dec,newra,newdec);
730  RA=newra;
731  Dec=newdec;
732  }
733  }
734  else if (!strcmp(propName, "TELESCOPE_INFO"))
735  {
736  for (ep = nextXMLEle(root, 1) ; ep != NULL ; ep = nextXMLEle(root, 0))
737  {
738  const char *name = findXMLAttValu(ep, "name");
739 
740  if (!strcmp(name, "TELESCOPE_APERTURE"))
741  {
742  if (TelescopeTypeS[0].s == ISS_ON)
743  Aperture = atof(pcdataXMLEle(ep));
744  }
745  else if (!strcmp(name, "TELESCOPE_FOCAL_LENGTH"))
746  {
747  if (TelescopeTypeS[0].s == ISS_ON)
748  FocalLength = atof(pcdataXMLEle(ep));
749  }
750  else if (!strcmp(name, "GUIDER_APERTURE"))
751  {
752  if (TelescopeTypeS[1].s == ISS_ON)
753  Aperture = atof(pcdataXMLEle(ep));
754  }
755  else if (!strcmp(name, "GUIDER_FOCAL_LENGTH"))
756  {
757  if (TelescopeTypeS[1].s == ISS_ON)
758  FocalLength = atof(pcdataXMLEle(ep));
759  }
760  }
761  }
762  else if (!strcmp(propName, "FILTER_NAME"))
763  {
764  FilterNames.clear();
765 
766  for (ep = nextXMLEle(root, 1) ; ep != NULL ; ep = nextXMLEle(root, 0))
767  FilterNames.push_back(pcdataXMLEle(ep));
768  }
769  else if (!strcmp(propName, "FILTER_SLOT"))
770  {
771  CurrentFilterSlot=-1;
772  for (ep = nextXMLEle(root, 1) ; ep != NULL ; ep = nextXMLEle(root, 0))
773  CurrentFilterSlot = atoi(pcdataXMLEle(ep));
774  }
775 
777  }
778 
779 bool INDI::CCD::ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n)
780 {
781  // first check if it's for our device
782  if(strcmp(dev,getDeviceName())==0)
783  {
784  // This is for our device
785  // Now lets see if it's something we process here
786  if(!strcmp(name,ActiveDeviceTP.name))
787  {
788  int rc;
789  ActiveDeviceTP.s=IPS_OK;
790  rc=IUUpdateText(&ActiveDeviceTP,texts,names,n);
791  // Update client display
792  IDSetText(&ActiveDeviceTP,NULL);
793  //saveConfig();
794 
795  // Update the property name!
796  strncpy(EqNP.device, ActiveDeviceT[0].text, MAXINDIDEVICE);
797  IDSnoopDevice(ActiveDeviceT[0].text,"EQUATORIAL_EOD_COORD");
798  IDSnoopDevice(ActiveDeviceT[0].text,"TELESCOPE_INFO");
799  IDSnoopDevice(ActiveDeviceT[2].text,"FILTER_SLOT");
800  IDSnoopDevice(ActiveDeviceT[2].text,"FILTER_NAME");
801 
802  // Tell children active devices was updated.
803  activeDevicesUpdated();
804 
805  // We processed this one, so, tell the world we did it
806  return true;
807  }
808 
809  if (!strcmp(name, BayerTP.name))
810  {
811  IUUpdateText(&BayerTP, texts, names, n);
812  BayerTP.s = IPS_OK;
813  IDSetText(&BayerTP, NULL);
814  return true;
815  }
816 
817  if (!strcmp(name, UploadSettingsTP.name))
818  {
819  IUUpdateText(&UploadSettingsTP, texts, names, n);
820  IDSetText(&UploadSettingsTP, NULL);
821  return true;
822  }
823 
824  }
825 
826  // Streamer
827  #ifdef __linux__
828  if (HasStreaming())
829  streamer->ISNewText(dev,name,texts,names,n);
830  #endif
831 
832  return INDI::DefaultDevice::ISNewText(dev,name,texts,names,n);
833 }
834 
835 bool INDI::CCD::ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n)
836 {
837  // first check if it's for our device
838  //IDLog("INDI::CCD::ISNewNumber %s\n",name);
839  if(strcmp(dev,getDeviceName())==0)
840  {
841  // This is for our device
842  // Now lets see if it's something we process here
843  if(strcmp(name,"CCD_EXPOSURE")==0)
844  {
845  if (values[0] < PrimaryCCD.ImageExposureN[0].min || values[0] > PrimaryCCD.ImageExposureN[0].max)
846  {
847  DEBUGF(INDI::Logger::DBG_ERROR, "Requested exposure value (%g) seconds out of bounds [%g,%g].", values[0], PrimaryCCD.ImageExposureN[0].min, PrimaryCCD.ImageExposureN[0].max);
848  PrimaryCCD.ImageExposureNP.s=IPS_ALERT;
849  IDSetNumber(&PrimaryCCD.ImageExposureNP,NULL);
850  return false;
851  }
852 
853  PrimaryCCD.ImageExposureN[0].value = ExposureTime = values[0];
854 
855  if (PrimaryCCD.ImageExposureNP.s==IPS_BUSY)
856  AbortExposure();
857 
858  if (StartExposure(ExposureTime))
859  PrimaryCCD.ImageExposureNP.s=IPS_BUSY;
860  else
861  PrimaryCCD.ImageExposureNP.s=IPS_ALERT;
862  IDSetNumber(&PrimaryCCD.ImageExposureNP,NULL);
863  return true;
864  }
865 
866  if(strcmp(name,"GUIDER_EXPOSURE")==0)
867  {
868  if (values[0] < GuideCCD.ImageExposureN[0].min || values[0] > GuideCCD.ImageExposureN[0].max)
869  {
870  DEBUGF(INDI::Logger::DBG_ERROR, "Requested guide exposure value (%g) seconds out of bounds [%g,%g].", values[0], GuideCCD.ImageExposureN[0].min, GuideCCD.ImageExposureN[0].max);
871  GuideCCD.ImageExposureNP.s=IPS_ALERT;
872  IDSetNumber(&GuideCCD.ImageExposureNP,NULL);
873  return false;
874  }
875 
876  GuideCCD.ImageExposureN[0].value = GuiderExposureTime = values[0];
877  GuideCCD.ImageExposureNP.s=IPS_BUSY;
878  if (StartGuideExposure(GuiderExposureTime))
879  GuideCCD.ImageExposureNP.s=IPS_BUSY;
880  else
881  GuideCCD.ImageExposureNP.s=IPS_ALERT;
882  IDSetNumber(&GuideCCD.ImageExposureNP,NULL);
883  return true;
884  }
885 
886  if(strcmp(name,"CCD_BINNING")==0)
887  {
888  // We are being asked to set camera binning
889  INumber *np = IUFindNumber(&PrimaryCCD.ImageBinNP, names[0]);
890  if (np == NULL)
891  {
892  PrimaryCCD.ImageBinNP.s = IPS_ALERT;
893  IDSetNumber (&PrimaryCCD.ImageBinNP, NULL);
894  return false;
895  }
896 
897  int binx,biny;
898  if (!strcmp(np->name, "HOR_BIN"))
899  {
900  binx = values[0];
901  biny = values[1];
902  }
903  else
904  {
905  binx = values[1];
906  biny = values[0];
907  }
908 
909  if (UpdateCCDBin(binx, biny))
910  {
911  IUUpdateNumber(&PrimaryCCD.ImageBinNP,values,names,n);
912  PrimaryCCD.ImageBinNP.s=IPS_OK;
913 
914  }
915  else
916  PrimaryCCD.ImageBinNP.s = IPS_ALERT;
917 
918  IDSetNumber (&PrimaryCCD.ImageBinNP, NULL);
919 
920  return true;
921 
922  }
923 
924  if(strcmp(name,"GUIDER_BINNING")==0)
925  {
926  // We are being asked to set camera binning
927  INumber *np = IUFindNumber(&GuideCCD.ImageBinNP, names[0]);
928  if (np == NULL)
929  {
930  GuideCCD.ImageBinNP.s = IPS_ALERT;
931  IDSetNumber (&GuideCCD.ImageBinNP, NULL);
932  return false;
933  }
934 
935  int binx,biny;
936  if (!strcmp(np->name, "HOR_BIN"))
937  {
938  binx = values[0];
939  biny = values[1];
940  }
941  else
942  {
943  binx = values[1];
944  biny = values[0];
945  }
946 
947  if (UpdateGuiderBin(binx, biny))
948  {
949  IUUpdateNumber(&GuideCCD.ImageBinNP,values,names,n);
950  GuideCCD.ImageBinNP.s=IPS_OK;
951 
952  }
953  else
954  GuideCCD.ImageBinNP.s = IPS_ALERT;
955 
956  IDSetNumber (&GuideCCD.ImageBinNP, NULL);
957 
958  return true;
959 
960 
961  }
962 
963  if(strcmp(name,"CCD_FRAME")==0)
964  {
965  // We are being asked to set CCD Frame
966  if (IUUpdateNumber(&PrimaryCCD.ImageFrameNP,values,names,n) < 0)
967  return false;
968 
969  PrimaryCCD.ImageFrameNP.s=IPS_OK;
970 
971  DEBUGF(Logger::DBG_DEBUG, "Requested CCD Frame is (%3.0f,%3.0f) (%3.0f x %3.0f)", values[0],values[1],values[2],values[3]);
972 
973  if (UpdateCCDFrame(PrimaryCCD.ImageFrameN[0].value, PrimaryCCD.ImageFrameN[1].value, PrimaryCCD.ImageFrameN[2].value,
974  PrimaryCCD.ImageFrameN[3].value) == false)
975  PrimaryCCD.ImageFrameNP.s = IPS_ALERT;
976 
977  IDSetNumber(&PrimaryCCD.ImageFrameNP, NULL);
978  return true;
979  }
980 
981  if(strcmp(name,"GUIDER_FRAME")==0)
982  {
983  // We are being asked to set guide frame
984  if (IUUpdateNumber(&GuideCCD.ImageFrameNP,values,names,n) < 0)
985  return false;
986 
987  GuideCCD.ImageFrameNP.s=IPS_OK;
988 
989  DEBUGF(Logger::DBG_DEBUG, "Requested Guide Frame is %4.0f,%4.0f %4.0f x %4.0f",
990  values[0],values[1],values[2],values[4]);
991 
992  if (UpdateGuiderFrame(GuideCCD.ImageFrameN[0].value, GuideCCD.ImageFrameN[1].value, GuideCCD.ImageFrameN[2].value,
993  GuideCCD.ImageFrameN[3].value) == false)
994  GuideCCD.ImageFrameNP.s = IPS_ALERT;
995 
996  IDSetNumber(&GuideCCD.ImageFrameNP, NULL);
997 
998  return true;
999  }
1000 
1001  if(strcmp(name,"CCD_GUIDESTAR")==0)
1002  {
1003  PrimaryCCD.RapidGuideDataNP.s=IPS_OK;
1004  IUUpdateNumber(&PrimaryCCD.RapidGuideDataNP,values,names,n);
1005  IDSetNumber(&PrimaryCCD.RapidGuideDataNP, NULL);
1006  return true;
1007  }
1008 
1009  if(strcmp(name,"GUIDER_GUIDESTAR")==0)
1010  {
1011  GuideCCD.RapidGuideDataNP.s=IPS_OK;
1012  IUUpdateNumber(&GuideCCD.RapidGuideDataNP,values,names,n);
1013  IDSetNumber(&GuideCCD.RapidGuideDataNP, NULL);
1014  return true;
1015  }
1016 
1017  if (!strcmp(name,GuideNSNP.name) || !strcmp(name,GuideWENP.name))
1018  {
1019  processGuiderProperties(name, values, names, n);
1020  return true;
1021  }
1022 
1023  // CCD TEMPERATURE:
1024  if(!strcmp(name, TemperatureNP.name))
1025  {
1026 
1027  if(values[0] < TemperatureN[0].min || values[0] > TemperatureN[0].max)
1028  {
1029  TemperatureNP.s = IPS_ALERT;
1030  DEBUGF(INDI::Logger::DBG_ERROR, "Error: Bad temperature value! Range is [%.1f, %.1f] [C].",
1031  TemperatureN[0].min, TemperatureN[0].max);
1032  IDSetNumber(&TemperatureNP, NULL);
1033  return false;
1034 
1035  }
1036 
1037  int rc= SetTemperature(values[0]);
1038 
1039  if (rc == 0)
1040  TemperatureNP.s = IPS_BUSY;
1041  else if (rc == 1)
1042  TemperatureNP.s = IPS_OK;
1043  else
1044  TemperatureNP.s = IPS_ALERT;
1045 
1046  IDSetNumber(&TemperatureNP, NULL);
1047  return true;
1048  }
1049 
1050  // Primary CCD Info
1051  if (!strcmp(name, PrimaryCCD.ImagePixelSizeNP.name))
1052  {
1053  IUUpdateNumber(&PrimaryCCD.ImagePixelSizeNP, values, names, n);
1054  PrimaryCCD.ImagePixelSizeNP.s = IPS_OK;
1055  SetCCDParams(PrimaryCCD.ImagePixelSizeNP.np[0].value, PrimaryCCD.ImagePixelSizeNP.np[1].value, PrimaryCCD.getBPP(), PrimaryCCD.ImagePixelSizeNP.np[2].value, PrimaryCCD.ImagePixelSizeNP.np[3].value);
1056  IDSetNumber(&PrimaryCCD.ImagePixelSizeNP, NULL);
1057  return true;
1058  }
1059 
1060  // Guide CCD Info
1061  if (!strcmp(name, GuideCCD.ImagePixelSizeNP.name))
1062  {
1063  IUUpdateNumber(&GuideCCD.ImagePixelSizeNP, values, names, n);
1064  GuideCCD.ImagePixelSizeNP.s = IPS_OK;
1065  SetGuiderParams(GuideCCD.ImagePixelSizeNP.np[0].value, GuideCCD.ImagePixelSizeNP.np[1].value, GuideCCD.getBPP(), GuideCCD.ImagePixelSizeNP.np[2].value, GuideCCD.ImagePixelSizeNP.np[3].value);
1066  IDSetNumber(&GuideCCD.ImagePixelSizeNP, NULL);
1067  return true;
1068  }
1069 
1070  // CCD Rotation
1071  if (!strcmp(name, CCDRotationNP.name))
1072  {
1073  IUUpdateNumber(&CCDRotationNP, values, names, n);
1074  CCDRotationNP.s = IPS_OK;
1075  IDSetNumber(&CCDRotationNP, NULL);
1076  ValidCCDRotation=true;
1077 
1078  DEBUGF(INDI::Logger::DBG_SESSION, "CCD FOV rotation updated to %g degrees.", CCDRotationN[0].value);
1079 
1080  return true;
1081  }
1082  }
1083 
1084  // Streamer
1085  #ifdef __linux__
1086  if (HasStreaming())
1087  streamer->ISNewNumber(dev,name,values,names,n);
1088  #endif
1089 
1090  return DefaultDevice::ISNewNumber(dev,name,values,names,n);
1091 }
1092 
1093 bool INDI::CCD::ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n)
1094 {
1095  if(strcmp(dev,getDeviceName())==0)
1096  {
1097  if (!strcmp(name, UploadSP.name))
1098  {
1099  IUUpdateSwitch(&UploadSP, states, names, n);
1100  UploadSP.s = IPS_OK;
1101  IDSetSwitch(&UploadSP, NULL);
1102 
1103  if (UploadS[0].s == ISS_ON)
1104  DEBUG(INDI::Logger::DBG_SESSION, "Upload settings set to client only.");
1105  else if (UploadS[1].s == ISS_ON)
1106  DEBUG(INDI::Logger::DBG_SESSION, "Upload settings set to local only.");
1107  else
1108  DEBUG(INDI::Logger::DBG_SESSION, "Upload settings set to client and local.");
1109  return true;
1110  }
1111 
1112  if (!strcmp(name, TelescopeTypeSP.name))
1113  {
1114  IUUpdateSwitch(&TelescopeTypeSP, states, names, n);
1115  TelescopeTypeSP.s = IPS_OK;
1116  IDSetSwitch(&TelescopeTypeSP, NULL);
1117  return true;
1118  }
1119 
1120  if (!strcmp(name, WorldCoordSP.name))
1121  {
1122  IUUpdateSwitch(&WorldCoordSP, states, names, n);
1123  WorldCoordSP.s = IPS_OK;
1124 
1125  if (WorldCoordS[0].s == ISS_ON)
1126  {
1127  DEBUG(INDI::Logger::DBG_WARNING, "World Coordinate System is enabled. CCD rotation must be set either manually or by solving the image before proceeding to capture any frames, otherwise the WCS information may be invalid.");
1128  defineSwitch(&TelescopeTypeSP);
1129  defineNumber(&CCDRotationNP);
1130  }
1131  else
1132  {
1133  deleteProperty(TelescopeTypeSP.name);
1134  deleteProperty(CCDRotationNP.name);
1135  }
1136 
1137  ValidCCDRotation=false;
1138  IDSetSwitch(&WorldCoordSP, NULL);
1139  }
1140 
1141  // Reset
1142  if(strcmp(name,PrimaryCCD.ResetSP.name)==0)
1143  {
1144  IUResetSwitch(&PrimaryCCD.ResetSP);
1145  PrimaryCCD.ResetSP.s = IPS_OK;
1146  if (CanBin())
1147  UpdateCCDBin(1,1);
1148  if (CanSubFrame())
1149  UpdateCCDFrame(0,0, PrimaryCCD.getXRes(), PrimaryCCD.getYRes());
1150 
1151  IDSetSwitch(&PrimaryCCD.ResetSP, NULL);
1152  return true;
1153  }
1154 
1155  if(strcmp(name,PrimaryCCD.AbortExposureSP.name)==0)
1156  {
1157  IUResetSwitch(&PrimaryCCD.AbortExposureSP);
1158 
1159  if (AbortExposure())
1160  {
1161  PrimaryCCD.AbortExposureSP.s = IPS_OK;
1162  PrimaryCCD.ImageExposureNP.s = IPS_IDLE;
1163  PrimaryCCD.ImageExposureN[0].value = 0;
1164  }
1165  else
1166  {
1167  PrimaryCCD.AbortExposureSP.s = IPS_ALERT;
1168  PrimaryCCD.ImageExposureNP.s = IPS_ALERT;
1169  }
1170 
1171  IDSetSwitch(&PrimaryCCD.AbortExposureSP, NULL);
1172  IDSetNumber(&PrimaryCCD.ImageExposureNP, NULL);
1173 
1174  return true;
1175  }
1176 
1177  if(strcmp(name,GuideCCD.AbortExposureSP.name)==0)
1178  {
1179  IUResetSwitch(&GuideCCD.AbortExposureSP);
1180 
1181  if (AbortGuideExposure())
1182  {
1183  GuideCCD.AbortExposureSP.s = IPS_OK;
1184  GuideCCD.ImageExposureNP.s = IPS_IDLE;
1185  GuideCCD.ImageExposureN[0].value = 0;
1186  }
1187  else
1188  {
1189  GuideCCD.AbortExposureSP.s = IPS_ALERT;
1190  GuideCCD.ImageExposureNP.s = IPS_ALERT;
1191  }
1192 
1193  IDSetSwitch(&GuideCCD.AbortExposureSP, NULL);
1194  IDSetNumber(&GuideCCD.ImageExposureNP, NULL);
1195 
1196  return true;
1197  }
1198 
1199  if(strcmp(name,PrimaryCCD.CompressSP.name)==0)
1200  {
1201 
1202  IUUpdateSwitch(&PrimaryCCD.CompressSP,states,names,n);
1203  PrimaryCCD.CompressSP.s = IPS_OK;
1204  IDSetSwitch(&PrimaryCCD.CompressSP,NULL);
1205 
1206  if(PrimaryCCD.CompressS[0].s==ISS_ON )
1207  {
1208  PrimaryCCD.SendCompressed=true;
1209  } else
1210  {
1211  PrimaryCCD.SendCompressed=false;
1212  }
1213  return true;
1214  }
1215 
1216  if(strcmp(name,GuideCCD.CompressSP.name)==0)
1217  {
1218 
1219  IUUpdateSwitch(&GuideCCD.CompressSP,states,names,n);
1220  GuideCCD.CompressSP.s = IPS_OK;
1221  IDSetSwitch(&GuideCCD.CompressSP,NULL);
1222 
1223  if(GuideCCD.CompressS[0].s==ISS_ON )
1224  {
1225  GuideCCD.SendCompressed=true;
1226  } else
1227  {
1228  GuideCCD.SendCompressed=false;
1229  }
1230  return true;
1231  }
1232 
1233  if(strcmp(name,PrimaryCCD.FrameTypeSP.name)==0)
1234  {
1235  IUUpdateSwitch(&PrimaryCCD.FrameTypeSP,states,names,n);
1236  PrimaryCCD.FrameTypeSP.s=IPS_OK;
1237  if(PrimaryCCD.FrameTypeS[0].s==ISS_ON)
1238  PrimaryCCD.setFrameType(CCDChip::LIGHT_FRAME);
1239  else if(PrimaryCCD.FrameTypeS[1].s==ISS_ON)
1240  {
1241  PrimaryCCD.setFrameType(CCDChip::BIAS_FRAME);
1242  if (HasShutter() == false)
1243  DEBUG(INDI::Logger::DBG_WARNING, "The CCD does not have a shutter. Cover the camera in order to take a bias frame.");
1244  }
1245  else if(PrimaryCCD.FrameTypeS[2].s==ISS_ON)
1246  {
1247  PrimaryCCD.setFrameType(CCDChip::DARK_FRAME);
1248  if (HasShutter() == false)
1249  DEBUG(INDI::Logger::DBG_WARNING, "The CCD does not have a shutter. Cover the camera in order to take a dark frame.");
1250  }
1251  else if(PrimaryCCD.FrameTypeS[3].s==ISS_ON)
1252  PrimaryCCD.setFrameType(CCDChip::FLAT_FRAME);
1253 
1254  if (UpdateCCDFrameType(PrimaryCCD.getFrameType()) == false)
1255  PrimaryCCD.FrameTypeSP.s = IPS_ALERT;
1256 
1257  IDSetSwitch(&PrimaryCCD.FrameTypeSP,NULL);
1258 
1259  return true;
1260  }
1261 
1262  if(strcmp(name,GuideCCD.FrameTypeSP.name)==0)
1263  {
1264  // Compression Update
1265  IUUpdateSwitch(&GuideCCD.FrameTypeSP,states,names,n);
1266  GuideCCD.FrameTypeSP.s=IPS_OK;
1267  if(GuideCCD.FrameTypeS[0].s==ISS_ON)
1268  GuideCCD.setFrameType(CCDChip::LIGHT_FRAME);
1269  else if(GuideCCD.FrameTypeS[1].s==ISS_ON)
1270  {
1271  GuideCCD.setFrameType(CCDChip::BIAS_FRAME);
1272  if (HasShutter() == false)
1273  DEBUG(INDI::Logger::DBG_WARNING, "The CCD does not have a shutter. Cover the camera in order to take a bias frame.");
1274  }
1275  else if(GuideCCD.FrameTypeS[2].s==ISS_ON)
1276  {
1277  GuideCCD.setFrameType(CCDChip::DARK_FRAME);
1278  if (HasShutter() == false)
1279  DEBUG(INDI::Logger::DBG_WARNING, "The CCD does not have a shutter. Cover the camera in order to take a dark frame.");
1280  }
1281  else if(GuideCCD.FrameTypeS[3].s==ISS_ON)
1282  GuideCCD.setFrameType(CCDChip::FLAT_FRAME);
1283 
1284  if (UpdateGuiderFrameType(GuideCCD.getFrameType()) == false)
1285  GuideCCD.FrameTypeSP.s = IPS_ALERT;
1286 
1287  IDSetSwitch(&GuideCCD.FrameTypeSP,NULL);
1288 
1289  return true;
1290  }
1291 
1292 
1293  if (strcmp(name, PrimaryCCD.RapidGuideSP.name)==0)
1294  {
1295  IUUpdateSwitch(&PrimaryCCD.RapidGuideSP, states, names, n);
1296  PrimaryCCD.RapidGuideSP.s=IPS_OK;
1297  RapidGuideEnabled=(PrimaryCCD.RapidGuideS[0].s==ISS_ON);
1298 
1299  if (RapidGuideEnabled) {
1300  defineSwitch(&PrimaryCCD.RapidGuideSetupSP);
1301  defineNumber(&PrimaryCCD.RapidGuideDataNP);
1302  }
1303  else {
1304  deleteProperty(PrimaryCCD.RapidGuideSetupSP.name);
1305  deleteProperty(PrimaryCCD.RapidGuideDataNP.name);
1306  }
1307 
1308  IDSetSwitch(&PrimaryCCD.RapidGuideSP,NULL);
1309  return true;
1310  }
1311 
1312  if (strcmp(name, GuideCCD.RapidGuideSP.name)==0)
1313  {
1314  IUUpdateSwitch(&GuideCCD.RapidGuideSP, states, names, n);
1315  GuideCCD.RapidGuideSP.s=IPS_OK;
1316  GuiderRapidGuideEnabled=(GuideCCD.RapidGuideS[0].s==ISS_ON);
1317 
1318  if (GuiderRapidGuideEnabled) {
1319  defineSwitch(&GuideCCD.RapidGuideSetupSP);
1320  defineNumber(&GuideCCD.RapidGuideDataNP);
1321  }
1322  else {
1323  deleteProperty(GuideCCD.RapidGuideSetupSP.name);
1324  deleteProperty(GuideCCD.RapidGuideDataNP.name);
1325  }
1326 
1327  IDSetSwitch(&GuideCCD.RapidGuideSP,NULL);
1328  return true;
1329  }
1330 
1331  if (strcmp(name, PrimaryCCD.RapidGuideSetupSP.name)==0)
1332  {
1333  IUUpdateSwitch(&PrimaryCCD.RapidGuideSetupSP, states, names, n);
1334  PrimaryCCD.RapidGuideSetupSP.s=IPS_OK;
1335 
1336  AutoLoop=(PrimaryCCD.RapidGuideSetupS[0].s==ISS_ON);
1337  SendImage=(PrimaryCCD.RapidGuideSetupS[1].s==ISS_ON);
1338  ShowMarker=(PrimaryCCD.RapidGuideSetupS[2].s==ISS_ON);
1339 
1340  IDSetSwitch(&PrimaryCCD.RapidGuideSetupSP,NULL);
1341  return true;
1342  }
1343 
1344  if (strcmp(name, GuideCCD.RapidGuideSetupSP.name)==0)
1345  {
1346  IUUpdateSwitch(&GuideCCD.RapidGuideSetupSP, states, names, n);
1347  GuideCCD.RapidGuideSetupSP.s=IPS_OK;
1348 
1349  GuiderAutoLoop=(GuideCCD.RapidGuideSetupS[0].s==ISS_ON);
1350  GuiderSendImage=(GuideCCD.RapidGuideSetupS[1].s==ISS_ON);
1351  GuiderShowMarker=(GuideCCD.RapidGuideSetupS[2].s==ISS_ON);
1352 
1353  IDSetSwitch(&GuideCCD.RapidGuideSetupSP,NULL);
1354  return true;
1355  }
1356  }
1357 
1358  // Streamer
1359  #ifdef __linux__
1360  if (HasStreaming())
1361  streamer->ISNewSwitch(dev, name, states, names, n);
1362  #endif
1363 
1364  return DefaultDevice::ISNewSwitch(dev, name, states, names, n);
1365 }
1366 
1367 int INDI::CCD::SetTemperature(double temperature)
1368 {
1369  INDI_UNUSED(temperature);
1370  DEBUGF(INDI::Logger::DBG_WARNING, "INDI::CCD::SetTemperature %4.2f - Should never get here", temperature);
1371  return -1;
1372 }
1373 
1374 bool INDI::CCD::StartExposure(float duration)
1375 {
1376  DEBUGF(INDI::Logger::DBG_WARNING, "INDI::CCD::StartExposure %4.2f - Should never get here",duration);
1377  return false;
1378 }
1379 
1380 bool INDI::CCD::StartGuideExposure(float duration)
1381 {
1382  DEBUGF(INDI::Logger::DBG_WARNING, "INDI::CCD::StartGuide Exposure %4.2f - Should never get here",duration);
1383  return false;
1384 }
1385 
1387 {
1388  DEBUG(INDI::Logger::DBG_WARNING, "INDI::CCD::AbortExposure - Should never get here");
1389  return false;
1390 }
1391 
1393 {
1394  DEBUG(INDI::Logger::DBG_WARNING, "INDI::CCD::AbortGuideExposure - Should never get here");
1395  return false;
1396 }
1397 
1398 bool INDI::CCD::UpdateCCDFrame(int x, int y, int w, int h)
1399 {
1400  // Just set value, unless HW layer overrides this and performs its own processing
1401  PrimaryCCD.setFrame(x, y, w, h);
1402  return true;
1403 }
1404 
1405 bool INDI::CCD::UpdateGuiderFrame(int x, int y, int w, int h)
1406 {
1407  GuideCCD.setFrame(x,y, w,h);
1408  return true;
1409 }
1410 
1411 bool INDI::CCD::UpdateCCDBin(int hor, int ver)
1412 {
1413  // Just set value, unless HW layer overrides this and performs its own processing
1414  PrimaryCCD.setBin(hor,ver);
1415  return true;
1416 }
1417 
1418 bool INDI::CCD::UpdateGuiderBin(int hor, int ver)
1419 {
1420  // Just set value, unless HW layer overrides this and performs its own processing
1421  GuideCCD.setBin(hor, ver);
1422  return true;
1423 }
1424 
1425 bool INDI::CCD::UpdateCCDFrameType(CCDChip::CCD_FRAME fType)
1426 {
1427  INDI_UNUSED(fType);
1428  // Child classes can override this
1429  return true;
1430 }
1431 
1432 bool INDI::CCD::UpdateGuiderFrameType(CCDChip::CCD_FRAME fType)
1433 {
1434  INDI_UNUSED(fType);
1435  // Child classes can override this
1436  return true;
1437 }
1438 
1439 void INDI::CCD::addFITSKeywords(fitsfile *fptr, CCDChip *targetChip)
1440 {
1441  int status=0;
1442  char frame_s[32];
1443  char dev_name[32];
1444  char exp_start[32];
1445  double min_val, max_val;
1446  double exposureDuration;
1447  double pixSize1,pixSize2;
1448  unsigned int xbin, ybin;
1449 
1450  setlocale(LC_NUMERIC,"C");
1451 
1452  if (targetChip->getNAxis() == 2)
1453  getMinMax(&min_val, &max_val, targetChip);
1454 
1455  xbin = targetChip->getBinX();
1456  ybin = targetChip->getBinY();
1457 
1458  char myDevice[MAXINDIDEVICE];
1459  strncpy(myDevice, getDeviceName(), MAXINDIDEVICE);
1460  fits_update_key_s(fptr, TSTRING, "INSTRUME", myDevice, "CCD Name" , &status);
1461 
1462  switch (targetChip->getFrameType())
1463  {
1464  case CCDChip::LIGHT_FRAME:
1465  strcpy(frame_s, "Light");
1466  break;
1467  case CCDChip::BIAS_FRAME:
1468  strcpy(frame_s, "Bias");
1469  break;
1470  case CCDChip::FLAT_FRAME:
1471  strcpy(frame_s, "Flat Field");
1472  break;
1473  case CCDChip::DARK_FRAME:
1474  strcpy(frame_s, "Dark");
1475  break;
1476  }
1477 
1478  exposureDuration = targetChip->getExposureDuration();
1479 
1480  pixSize1 = targetChip->getPixelSizeX();
1481  pixSize2 = targetChip->getPixelSizeY();
1482 
1483  strncpy(dev_name, getDeviceName(), 32);
1484  strncpy(exp_start, targetChip->getExposureStartTime(), 32);
1485 
1486  fits_update_key_s(fptr, TDOUBLE, "EXPTIME", &(exposureDuration), "Total Exposure Time (s)", &status);
1487 
1488  if(targetChip->getFrameType() == CCDChip::DARK_FRAME)
1489  fits_update_key_s(fptr, TDOUBLE, "DARKTIME", &(exposureDuration), "Total Exposure Time (s)", &status);
1490 
1491  if (HasCooler())
1492  fits_update_key_s(fptr, TDOUBLE, "CCD-TEMP" , &(TemperatureN[0].value), "CCD Temperature (Celcius)", &status);
1493 
1494  fits_update_key_s(fptr, TDOUBLE, "PIXSIZE1", &(pixSize1), "Pixel Size 1 (microns)", &status);
1495  fits_update_key_s(fptr, TDOUBLE, "PIXSIZE2", &(pixSize2), "Pixel Size 2 (microns)", &status);
1496  fits_update_key_s(fptr, TUINT, "XBINNING", &(xbin) , "Binning factor in width", &status);
1497  fits_update_key_s(fptr, TUINT, "YBINNING", &(ybin), "Binning factor in height", &status);
1498  fits_update_key_s(fptr, TSTRING, "FRAME", frame_s, "Frame Type", &status);
1499  if (CurrentFilterSlot != -1 && CurrentFilterSlot <= FilterNames.size())
1500  {
1501  char filter[32];
1502  strncpy(filter, FilterNames.at(CurrentFilterSlot-1).c_str(), 32);
1503  fits_update_key_s(fptr, TSTRING, "FILTER", filter, "Filter", &status);
1504  }
1505 
1506  if (targetChip->getNAxis() == 2)
1507  {
1508  fits_update_key_s(fptr, TDOUBLE, "DATAMIN", &min_val, "Minimum value", &status);
1509  fits_update_key_s(fptr, TDOUBLE, "DATAMAX", &max_val, "Maximum value", &status);
1510  }
1511 
1512  if (HasBayer() && targetChip->getNAxis() == 2)
1513  {
1514  unsigned int bayer_offset_x = atoi(BayerT[0].text);
1515  unsigned int bayer_offset_y = atoi(BayerT[1].text);
1516 
1517  fits_update_key_s(fptr, TUINT, "XBAYROFF", &bayer_offset_x, "X offset of Bayer array", &status);
1518  fits_update_key_s(fptr, TUINT, "YBAYROFF", &bayer_offset_y, "Y offset of Bayer array", &status);
1519  fits_update_key_s(fptr, TSTRING, "BAYERPAT", BayerT[2].text, "Bayer color pattern", &status);
1520  }
1521 
1522  if (FocalLength != -1)
1523  fits_update_key_s(fptr, TDOUBLE, "FOCALLEN", &FocalLength, "Focal Length (mm)", &status);
1524 
1525  if (targetChip->getFrameType() == CCDChip::LIGHT_FRAME && RA != -1000 && Dec != -1000)
1526  {
1527  ln_equ_posn epochPos, J2000Pos;
1528  epochPos.ra = RA*15.0;
1529  epochPos.dec = Dec;
1530 
1531  // Convert from JNow to J2000
1532  //TODO use exp_start instead of julian from system
1533  ln_get_equ_prec2(&epochPos, ln_get_julian_from_sys(), JD2000, &J2000Pos);
1534 
1535  double raJ2000 = J2000Pos.ra/15.0;
1536  double decJ2000 = J2000Pos.dec;
1537 
1538  fits_update_key_s(fptr, TDOUBLE, "OBJCTRA", &raJ2000, "Object RA", &status);
1539  fits_update_key_s(fptr, TDOUBLE, "OBJCTDEC", &decJ2000, "Object DEC", &status);
1540 
1541  int epoch = 2000;
1542 
1543  //fits_update_key_s(fptr, TINT, "EPOCH", &epoch, "Epoch", &status);
1544  fits_update_key_s(fptr, TINT, "EQUINOX", &epoch, "Equinox", &status);
1545 
1546 
1547  // Add WCS Info
1548  if (WorldCoordS[0].s == ISS_ON && ValidCCDRotation && FocalLength != -1)
1549  {
1550  raJ2000 *= 15;
1551  fits_update_key_s(fptr, TDOUBLE, "CRVAL1", &raJ2000, "CRVAL1", &status);
1552  fits_update_key_s(fptr, TDOUBLE, "CRVAL2", &decJ2000, "CRVAL1", &status);
1553 
1554  char radecsys[8] = "FK5";
1555  char ctype1[16] = "RA---TAN";
1556  char ctype2[16] = "DEC--TAN";
1557 
1558  fits_update_key_s(fptr, TSTRING, "RADECSYS", radecsys, "RADECSYS", &status);
1559  fits_update_key_s(fptr, TSTRING, "CTYPE1", ctype1, "CTYPE1", &status);
1560  fits_update_key_s(fptr, TSTRING, "CTYPE2", ctype2, "CTYPE2", &status);
1561 
1562  double crpix1 = targetChip->getSubW()/targetChip->getBinX()/2.0;
1563  double crpix2 = targetChip->getSubH()/targetChip->getBinY()/2.0;
1564 
1565  fits_update_key_s(fptr, TDOUBLE, "CRPIX1", &crpix1, "CRPIX1", &status);
1566  fits_update_key_s(fptr, TDOUBLE, "CRPIX2", &crpix2, "CRPIX2", &status);
1567 
1568  double secpix1 = pixSize1 / FocalLength * 206.3 * targetChip->getBinX();
1569  double secpix2 = pixSize2 / FocalLength * 206.3 * targetChip->getBinY();
1570 
1571  //double secpix1 = pixSize1 / FocalLength * 206.3;
1572  //double secpix2 = pixSize2 / FocalLength * 206.3;
1573 
1574  fits_update_key_s(fptr, TDOUBLE, "SECPIX1", &secpix1, "SECPIX1", &status);
1575  fits_update_key_s(fptr, TDOUBLE, "SECPIX2", &secpix2, "SECPIX2", &status);
1576 
1577  double degpix1 = secpix1 / 3600.0;
1578  double degpix2 = secpix2 / 3600.0;
1579 
1580  fits_update_key_s(fptr, TDOUBLE, "CDELT1", &degpix1, "CDELT1", &status);
1581  fits_update_key_s(fptr, TDOUBLE, "CDELT2", &degpix2, "CDELT2", &status);
1582 
1583  // Rotation is CW, we need to convert it to CCW per CROTA1 definition
1584  double rotation = 360 - CCDRotationN[0].value;
1585  if (rotation > 360)
1586  rotation -= 360;
1587 
1588  fits_update_key_s(fptr, TDOUBLE, "CROTA1", &rotation, "CROTA1", &status);
1589  fits_update_key_s(fptr, TDOUBLE, "CROTA2", &rotation, "CROTA2", &status);
1590 
1591  /*double cd[4];
1592  cd[0] = degpix1;
1593  cd[1] = 0;
1594  cd[2] = 0;
1595  cd[3] = degpix2;
1596 
1597  fits_update_key_s(fptr, TDOUBLE, "CD1_1", &cd[0], "CD1_1", &status);
1598  fits_update_key_s(fptr, TDOUBLE, "CD1_2", &cd[1], "CD1_2", &status);
1599  fits_update_key_s(fptr, TDOUBLE, "CD2_1", &cd[2], "CD2_1", &status);
1600  fits_update_key_s(fptr, TDOUBLE, "CD2_2", &cd[3], "CD2_2", &status);*/
1601 
1602  }
1603 
1604  }
1605 
1606  fits_update_key_s(fptr, TSTRING, "DATE-OBS", exp_start, "UTC start date of observation", &status);
1607  fits_write_comment(fptr, "Generated by INDI", &status);
1608 
1609  setlocale(LC_NUMERIC,"");
1610 
1611 }
1612 
1613 void INDI::CCD::fits_update_key_s(fitsfile* fptr, int type, std::string name, void* p, std::string explanation, int* status)
1614 {
1615  // this function is for removing warnings about deprecated string conversion to char* (from arg 5)
1616  fits_update_key(fptr,type,name.c_str(),p, const_cast<char*>(explanation.c_str()), status);
1617 }
1618 
1620 {
1621  bool sendImage = (UploadS[0].s == ISS_ON || UploadS[2].s == ISS_ON);
1622  bool saveImage = (UploadS[1].s == ISS_ON || UploadS[2].s == ISS_ON);
1623  bool showMarker = false;
1624  bool autoLoop = false;
1625  bool sendData = false;
1626 
1627  if (RapidGuideEnabled && targetChip == &PrimaryCCD && (PrimaryCCD.getBPP() == 16 || PrimaryCCD.getBPP() == 8))
1628  {
1629  autoLoop = AutoLoop;
1630  sendImage = SendImage;
1631  showMarker = ShowMarker;
1632  sendData = true;
1633  saveImage = false;
1634  }
1635 
1636  if (GuiderRapidGuideEnabled && targetChip == &GuideCCD && (GuideCCD.getBPP() == 16 || PrimaryCCD.getBPP() == 8))
1637  {
1638  autoLoop = GuiderAutoLoop;
1639  sendImage = GuiderSendImage;
1640  showMarker = GuiderShowMarker;
1641  sendData = true;
1642  saveImage = false;
1643  }
1644 
1645  if (sendData)
1646  {
1647  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;
1648  targetChip->RapidGuideDataNP.s=IPS_BUSY;
1649  int width = targetChip->getSubW() / targetChip->getBinX();
1650  int height = targetChip->getSubH() / targetChip->getBinY();
1651  void *src = (unsigned short *) targetChip->getFrameBuffer();
1652  int i0, i1, i2, i3, i4, i5, i6, i7, i8;
1653  int ix = 0, iy = 0;
1654  int xM4;
1655  double average, fit, bestFit = 0;
1656  int minx = 4;
1657  int maxx = width -4;
1658  int miny = 4;
1659  int maxy = height -4;
1660  if (targetChip->lastRapidX > 0 && targetChip->lastRapidY > 0) {
1661  minx = std::max(targetChip->lastRapidX - 20, 4);
1662  maxx = std::min(targetChip->lastRapidX + 20, width - 4);
1663  miny = std::max(targetChip->lastRapidY - 20, 4);
1664  maxy = std::min(targetChip->lastRapidY + 20, height -4);
1665  }
1666  if (targetChip->getBPP() == 16) {
1667  unsigned short *p;
1668  for (int x = minx; x < maxx; x++)
1669  for (int y = miny; y < maxy; y++) {
1670  i0 = i1 = i2 = i3 = i4 = i5 = i6 = i7 = i8 = 0;
1671  xM4 = x - 4;
1672  p = (unsigned short *)src + (y - 4) * width + xM4; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++;
1673  p = (unsigned short *)src + (y - 3) * width + xM4; i8 += *p++; i8 += *p++; i8 += *p++; i7 += *p++; i6 += *p++; i7 += *p++; i8 += *p++; i8 += *p++; i8 += *p++;
1674  p = (unsigned short *)src + (y - 2) * width + xM4; i8 += *p++; i8 += *p++; i5 += *p++; i4 += *p++; i3 += *p++; i4 += *p++; i5 += *p++; i8 += *p++; i8 += *p++;
1675  p = (unsigned short *)src + (y - 1) * width + xM4; i8 += *p++; i7 += *p++; i4 += *p++; i2 += *p++; i1 += *p++; i2 += *p++; i4 += *p++; i8 += *p++; i8 += *p++;
1676  p = (unsigned short *)src + (y + 0) * width + xM4; i8 += *p++; i6 += *p++; i3 += *p++; i1 += *p++; i0 += *p++; i1 += *p++; i3 += *p++; i6 += *p++; i8 += *p++;
1677  p = (unsigned short *)src + (y + 1) * width + xM4; i8 += *p++; i7 += *p++; i4 += *p++; i2 += *p++; i1 += *p++; i2 += *p++; i4 += *p++; i8 += *p++; i8 += *p++;
1678  p = (unsigned short *)src + (y + 2) * width + xM4; i8 += *p++; i8 += *p++; i5 += *p++; i4 += *p++; i3 += *p++; i4 += *p++; i5 += *p++; i8 += *p++; i8 += *p++;
1679  p = (unsigned short *)src + (y + 3) * width + xM4; i8 += *p++; i8 += *p++; i8 += *p++; i7 += *p++; i6 += *p++; i7 += *p++; i8 += *p++; i8 += *p++; i8 += *p++;
1680  p = (unsigned short *)src + (y + 4) * width + xM4; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++;
1681  average = (i0 + i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8) / 85.0;
1682  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);
1683  if (bestFit < fit) {
1684  bestFit = fit;
1685  ix = x;
1686  iy = y;
1687  }
1688  }
1689  } else {
1690  unsigned char *p;
1691  for (int x = minx; x < maxx; x++)
1692  for (int y = miny; y < maxy; y++) {
1693  i0 = i1 = i2 = i3 = i4 = i5 = i6 = i7 = i8 = 0;
1694  xM4 = x - 4;
1695  p = (unsigned char *)src + (y - 4) * width + xM4; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++;
1696  p = (unsigned char *)src + (y - 3) * width + xM4; i8 += *p++; i8 += *p++; i8 += *p++; i7 += *p++; i6 += *p++; i7 += *p++; i8 += *p++; i8 += *p++; i8 += *p++;
1697  p = (unsigned char *)src + (y - 2) * width + xM4; i8 += *p++; i8 += *p++; i5 += *p++; i4 += *p++; i3 += *p++; i4 += *p++; i5 += *p++; i8 += *p++; i8 += *p++;
1698  p = (unsigned char *)src + (y - 1) * width + xM4; i8 += *p++; i7 += *p++; i4 += *p++; i2 += *p++; i1 += *p++; i2 += *p++; i4 += *p++; i8 += *p++; i8 += *p++;
1699  p = (unsigned char *)src + (y + 0) * width + xM4; i8 += *p++; i6 += *p++; i3 += *p++; i1 += *p++; i0 += *p++; i1 += *p++; i3 += *p++; i6 += *p++; i8 += *p++;
1700  p = (unsigned char *)src + (y + 1) * width + xM4; i8 += *p++; i7 += *p++; i4 += *p++; i2 += *p++; i1 += *p++; i2 += *p++; i4 += *p++; i8 += *p++; i8 += *p++;
1701  p = (unsigned char *)src + (y + 2) * width + xM4; i8 += *p++; i8 += *p++; i5 += *p++; i4 += *p++; i3 += *p++; i4 += *p++; i5 += *p++; i8 += *p++; i8 += *p++;
1702  p = (unsigned char *)src + (y + 3) * width + xM4; i8 += *p++; i8 += *p++; i8 += *p++; i7 += *p++; i6 += *p++; i7 += *p++; i8 += *p++; i8 += *p++; i8 += *p++;
1703  p = (unsigned char *)src + (y + 4) * width + xM4; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++; i8 += *p++;
1704  average = (i0 + i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8) / 85.0;
1705  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);
1706  if (bestFit < fit) {
1707  bestFit = fit;
1708  ix = x;
1709  iy = y;
1710  }
1711  }
1712  }
1713 
1714  targetChip->RapidGuideDataN[0].value = ix;
1715  targetChip->RapidGuideDataN[1].value = iy;
1716  targetChip->RapidGuideDataN[2].value = bestFit;
1717  targetChip->lastRapidX = ix;
1718  targetChip->lastRapidY = iy;
1719  if (bestFit > 50) {
1720  int sumX = 0;
1721  int sumY = 0;
1722  int total = 0;
1723  int max = 0;
1724  int noiseThreshold = 0;
1725 
1726  if (targetChip->getBPP() == 16) {
1727  unsigned short *p;
1728  for (int y = iy - 4; y <= iy + 4; y++) {
1729  p = (unsigned short *)src + y * width + ix - 4;
1730  for (int x = ix - 4; x <= ix + 4; x++) {
1731  int w = *p++;
1732  noiseThreshold += w;
1733  if (w > max)
1734  max = w;
1735  }
1736  }
1737  noiseThreshold = (noiseThreshold/81+max)/2; // set threshold between peak and average
1738  for (int y = iy - 4; y <= iy + 4; y++) {
1739  p = (unsigned short *)src + y * width + ix - 4;
1740  for (int x = ix - 4; x <= ix + 4; x++) {
1741  int w = *p++;
1742  if (w < noiseThreshold)
1743  w = 0;
1744  sumX += x * w;
1745  sumY += y * w;
1746  total += w;
1747  }
1748  }
1749  } else {
1750  unsigned char *p;
1751  for (int y = iy - 4; y <= iy + 4; y++) {
1752  p = (unsigned char *)src + y * width + ix - 4;
1753  for (int x = ix - 4; x <= ix + 4; x++) {
1754  int w = *p++;
1755  noiseThreshold += w;
1756  if (w > max)
1757  max = w;
1758  }
1759  }
1760  noiseThreshold = (noiseThreshold/81+max)/2; // set threshold between peak and average
1761  for (int y = iy - 4; y <= iy + 4; y++) {
1762  p = (unsigned char *)src + y * width + ix - 4;
1763  for (int x = ix - 4; x <= ix + 4; x++) {
1764  int w = *p++;
1765  if (w < noiseThreshold)
1766  w = 0;
1767  sumX += x * w;
1768  sumY += y * w;
1769  total += w;
1770  }
1771  }
1772  }
1773 
1774  if (total > 0)
1775  {
1776  targetChip->RapidGuideDataN[0].value = ((double)sumX)/total;
1777  targetChip->RapidGuideDataN[1].value = ((double)sumY)/total;
1778  targetChip->RapidGuideDataNP.s=IPS_OK;
1779 
1780  DEBUGF(INDI::Logger::DBG_DEBUG, "Guide Star X: %g Y: %g FIT: %g", targetChip->RapidGuideDataN[0].value, targetChip->RapidGuideDataN[1].value,
1781  targetChip->RapidGuideDataN[2].value);
1782  }
1783  else
1784  {
1785  targetChip->RapidGuideDataNP.s=IPS_ALERT;
1786  targetChip->lastRapidX = targetChip->lastRapidY = -1;
1787  }
1788  }
1789  else
1790  {
1791  targetChip->RapidGuideDataNP.s=IPS_ALERT;
1792  targetChip->lastRapidX = targetChip->lastRapidY = -1;
1793  }
1794  IDSetNumber(&targetChip->RapidGuideDataNP,NULL);
1795 
1796  if (showMarker)
1797  {
1798  int xmin = std::max(ix - 10, 0);
1799  int xmax = std::min(ix + 10, width - 1);
1800  int ymin = std::max(iy - 10, 0);
1801  int ymax = std::min(iy + 10, height - 1);
1802 
1803  //fprintf(stderr, "%d %d %d %d\n", xmin, xmax, ymin, ymax);
1804 
1805  if (targetChip->getBPP() == 16) {
1806  unsigned short *p;
1807  if (ymin > 0)
1808  {
1809  p = (unsigned short *)src + ymin * width + xmin;
1810  for (int x = xmin; x <= xmax; x++)
1811  *p++ = 50000;
1812  }
1813 
1814  if (xmin > 0)
1815  {
1816  for (int y = ymin; y<= ymax; y++)
1817  {
1818  *((unsigned short *)src + y * width + xmin) = 50000;
1819  }
1820  }
1821 
1822  if (xmax < width - 1)
1823  {
1824  for (int y = ymin; y<= ymax; y++)
1825  {
1826  *((unsigned short *)src + y * width + xmax) = 50000;
1827  }
1828  }
1829 
1830  if (ymax < height -1)
1831  {
1832  p = (unsigned short *)src + ymax * width + xmin;
1833  for (int x = xmin; x <= xmax; x++)
1834  *p++ = 50000;
1835  }
1836  } else {
1837  unsigned char *p;
1838  if (ymin > 0)
1839  {
1840  p = (unsigned char *)src + ymin * width + xmin;
1841  for (int x = xmin; x <= xmax; x++)
1842  *p++ = 255;
1843  }
1844 
1845  if (xmin > 0)
1846  {
1847  for (int y = ymin; y<= ymax; y++)
1848  {
1849  *((unsigned char *)src + y * width + xmin) = 255;
1850  }
1851  }
1852 
1853  if (xmax < width - 1)
1854  {
1855  for (int y = ymin; y<= ymax; y++)
1856  {
1857  *((unsigned char *)src + y * width + xmax) = 255;
1858  }
1859  }
1860 
1861  if (ymax < height -1)
1862  {
1863  p = (unsigned char *)src + ymax * width + xmin;
1864  for (int x = xmin; x <= xmax; x++)
1865  *p++ = 255;
1866  }
1867 
1868  }
1869  }
1870  }
1871 
1872  if (sendImage || saveImage)
1873  {
1874  if (!strcmp(targetChip->getImageExtension(), "fits"))
1875  {
1876  void *memptr;
1877  size_t memsize;
1878  int img_type=0;
1879  int byte_type=0;
1880  int status=0;
1881  long naxis=targetChip->getNAxis();
1882  long naxes[naxis];
1883  int nelements=0;
1884  std::string bit_depth;
1885 
1886  fitsfile *fptr=NULL;
1887 
1888  naxes[0]=targetChip->getSubW()/targetChip->getBinX();
1889  naxes[1]=targetChip->getSubH()/targetChip->getBinY();
1890 
1891  switch (targetChip->getBPP())
1892  {
1893  case 8:
1894  byte_type = TBYTE;
1895  img_type = BYTE_IMG;
1896  bit_depth = "8 bits per pixel";
1897  break;
1898 
1899  case 16:
1900  byte_type = TUSHORT;
1901  img_type = USHORT_IMG;
1902  bit_depth = "16 bits per pixel";
1903  break;
1904 
1905  case 32:
1906  byte_type = TULONG;
1907  img_type = ULONG_IMG;
1908  bit_depth = "32 bits per pixel";
1909  break;
1910 
1911  default:
1912  DEBUGF(Logger::DBG_WARNING, "Unsupported bits per pixel value %d\n", targetChip->getBPP() );
1913  return false;
1914  break;
1915  }
1916 
1917  nelements = naxes[0] * naxes[1];
1918  if (naxis== 3)
1919  {
1920  nelements *= 3;
1921  naxes[2] = 3;
1922  }
1923 
1924  /*DEBUGF(Logger::DBG_DEBUG, "Exposure complete. Image Depth: %s. Width: %d Height: %d nelements: %d", bit_depth.c_str(), naxes[0],
1925  naxes[1], nelements);*/
1926 
1927  // Now we have to send fits format data to the client
1928  memsize=5760;
1929  memptr=malloc(memsize);
1930  if(!memptr)
1931  {
1932  IDLog("Error: failed to allocate memory: %lu\n",(unsigned long)memsize);
1933  }
1934 
1935  fits_create_memfile(&fptr,&memptr,&memsize,2880,realloc,&status);
1936 
1937  if(status)
1938  {
1939  IDLog("Error: Failed to create FITS image\n");
1940  fits_report_error(stderr, status); /* print out any error messages */
1941  return false;
1942  }
1943 
1944  fits_create_img(fptr, img_type , naxis, naxes, &status);
1945 
1946  if (status)
1947  {
1948  IDLog("Error: Failed to create FITS image\n");
1949  fits_report_error(stderr, status); /* print out any error messages */
1950  return false;
1951  }
1952 
1953  addFITSKeywords(fptr, targetChip);
1954 
1955  fits_write_img(fptr,byte_type,1,nelements,targetChip->getFrameBuffer(),&status);
1956 
1957  if (status)
1958  {
1959  IDLog("Error: Failed to write FITS image\n");
1960  fits_report_error(stderr, status); /* print out any error messages */
1961  return false;
1962  }
1963 
1964  fits_close_file(fptr,&status);
1965 
1966  uploadFile(targetChip, memptr, memsize, sendImage, saveImage);
1967 
1968  free(memptr);
1969  }
1970  else
1971  {
1972  uploadFile(targetChip, targetChip->getFrameBuffer(), targetChip->getFrameBufferSize(), sendImage, saveImage);
1973  }
1974 
1975 
1976  }
1977 
1978  targetChip->ImageExposureNP.s=IPS_OK;
1979  IDSetNumber(&targetChip->ImageExposureNP,NULL);
1980 
1981  if (autoLoop)
1982  {
1983  if (targetChip == &PrimaryCCD)
1984  {
1985  PrimaryCCD.ImageExposureN[0].value = ExposureTime;
1986  PrimaryCCD.ImageExposureNP.s=IPS_BUSY;
1987  if (StartExposure(ExposureTime))
1988  PrimaryCCD.ImageExposureNP.s=IPS_BUSY;
1989  else
1990  {
1991  DEBUG(INDI::Logger::DBG_DEBUG, "Autoloop: Primary CCD Exposure Error!");
1992  PrimaryCCD.ImageExposureNP.s=IPS_ALERT;
1993  }
1994 
1995  IDSetNumber(&PrimaryCCD.ImageExposureNP,NULL);
1996  }
1997  else
1998  {
1999  GuideCCD.ImageExposureN[0].value = GuiderExposureTime;
2000  GuideCCD.ImageExposureNP.s=IPS_BUSY;
2001  if (StartGuideExposure(GuiderExposureTime))
2002  GuideCCD.ImageExposureNP.s=IPS_BUSY;
2003  else
2004  {
2005  DEBUG(INDI::Logger::DBG_DEBUG, "Autoloop: Guide CCD Exposure Error!");
2006  GuideCCD.ImageExposureNP.s=IPS_ALERT;
2007  }
2008 
2009  IDSetNumber(&GuideCCD.ImageExposureNP,NULL);
2010  }
2011  }
2012 
2013  return true;
2014 }
2015 
2016 bool INDI::CCD::uploadFile(CCDChip * targetChip, const void *fitsData, size_t totalBytes, bool sendImage, bool saveImage)
2017 {
2018  unsigned char *compressedData = NULL;
2019  uLongf compressedBytes=0;
2020 
2021  if (saveImage)
2022  {
2023  targetChip->FitsB.blob=(unsigned char *)fitsData;
2024  targetChip->FitsB.bloblen=totalBytes;
2025  snprintf(targetChip->FitsB.format, MAXINDIBLOBFMT, ".%s", targetChip->getImageExtension());
2026 
2027  FILE *fp = NULL;
2028  char imageFileName[MAXRBUF];
2029  std::string prefix = UploadSettingsT[1].text;
2030  int maxIndex = getFileIndex(UploadSettingsT[0].text, UploadSettingsT[1].text, targetChip->FitsB.format);
2031 
2032  if (maxIndex < 0)
2033  {
2034  DEBUGF(INDI::Logger::DBG_ERROR, "Error iterating directory %s. %s", UploadSettingsT[0].text, strerror(errno));
2035  return false;
2036  }
2037 
2038  if (maxIndex > 0)
2039  {
2040  char indexString[4];
2041  snprintf(indexString, 4, "%03d", maxIndex);
2042  std::string prefixIndex = indexString;
2043  prefix.replace(prefix.find("XXX"), 3, prefixIndex);
2044  }
2045 
2046  snprintf(imageFileName, MAXRBUF, "%s/%s%s", UploadSettingsT[0].text, prefix.c_str(), targetChip->FitsB.format);
2047  fp = fopen(imageFileName, "w");
2048  if (fp == NULL)
2049  {
2050  DEBUGF(INDI::Logger::DBG_ERROR, "Unable to save image file (%s). %s", imageFileName, strerror(errno));
2051  return false;
2052  }
2053 
2054  int n=0;
2055  for (int nr=0; nr < (int) targetChip->FitsB.bloblen; nr += n)
2056  n = fwrite( (static_cast<char *>(targetChip->FitsB.blob) + nr), 1, targetChip->FitsB.bloblen - nr, fp);
2057 
2058  DEBUGF(INDI::Logger::DBG_SESSION, "Image saved to %s", imageFileName);
2059  fclose(fp);
2060  }
2061 
2062  if (targetChip->SendCompressed)
2063  {
2064  compressedBytes = sizeof(char) * totalBytes + totalBytes / 64 + 16 + 3;
2065  compressedData = (unsigned char *) malloc (compressedBytes);
2066 
2067  if (fitsData == NULL || compressedData == NULL)
2068  {
2069  if (compressedData)
2070  free(compressedData);
2071  DEBUG(INDI::Logger::DBG_ERROR, "Error: Ran out of memory compressing image");
2072  return false;
2073  }
2074 
2075  int r = compress2(compressedData, &compressedBytes, (const Bytef*)fitsData, totalBytes, 9);
2076  if (r != Z_OK)
2077  {
2078  /* this should NEVER happen */
2079  DEBUG(INDI::Logger::DBG_ERROR, "Error: Failed to compress image");
2080  return false;
2081  }
2082 
2083  targetChip->FitsB.blob=compressedData;
2084  targetChip->FitsB.bloblen=compressedBytes;
2085  snprintf(targetChip->FitsB.format, MAXINDIBLOBFMT, ".%s.z", targetChip->getImageExtension());
2086  } else
2087  {
2088  targetChip->FitsB.blob=(unsigned char *)fitsData;
2089  targetChip->FitsB.bloblen=totalBytes;
2090  snprintf(targetChip->FitsB.format, MAXINDIBLOBFMT, ".%s", targetChip->getImageExtension());
2091  }
2092 
2093  targetChip->FitsB.size = totalBytes;
2094  targetChip->FitsBP.s=IPS_OK;
2095 
2096  if (sendImage)
2097  IDSetBLOB(&targetChip->FitsBP,NULL);
2098 
2099  if (compressedData)
2100  free (compressedData);
2101 
2102  return true;
2103 }
2104 
2105 void INDI::CCD::SetCCDParams(int x,int y,int bpp,float xf,float yf)
2106 {
2107  PrimaryCCD.setResolution(x, y);
2108  PrimaryCCD.setFrame(0, 0, x, y);
2109  if (CanBin())
2110  PrimaryCCD.setBin(1,1);
2111  PrimaryCCD.setPixelSize(xf, yf);
2112  PrimaryCCD.setBPP(bpp);
2113 
2114 }
2115 
2116 void INDI::CCD::SetGuiderParams(int x,int y,int bpp,float xf,float yf)
2117 {
2118  capability |= CCD_HAS_GUIDE_HEAD;
2119 
2120  GuideCCD.setResolution(x, y);
2121  GuideCCD.setFrame(0, 0, x, y);
2122  GuideCCD.setPixelSize(xf, yf);
2123  GuideCCD.setBPP(bpp);
2124 
2125 }
2126 
2128 {
2129  IUSaveConfigText(fp, &ActiveDeviceTP);
2130  IUSaveConfigSwitch(fp, &UploadSP);
2131  IUSaveConfigText(fp, &UploadSettingsTP);
2132  //IUSaveConfigSwitch(fp, &WorldCoordSP);
2133  IUSaveConfigSwitch(fp, &TelescopeTypeSP);
2134 
2135  //if (ValidCCDRotation)
2136  // IUSaveConfigNumber(fp, &CCDRotationNP);
2137 
2138  IUSaveConfigSwitch(fp, &PrimaryCCD.CompressSP);
2139 
2140  if (HasGuideHead())
2141  IUSaveConfigSwitch(fp, &GuideCCD.CompressSP);
2142 
2143  if (CanSubFrame())
2144  IUSaveConfigNumber(fp, &PrimaryCCD.ImageFrameNP);
2145 
2146  if (CanBin())
2147  IUSaveConfigNumber(fp, &PrimaryCCD.ImageBinNP);
2148 
2149  if (HasBayer())
2150  IUSaveConfigText(fp, &BayerTP);
2151 
2152  return true;
2153 }
2154 
2156 {
2157  INDI_UNUSED(ms);
2158  DEBUG(INDI::Logger::DBG_ERROR, "The CCD does not support guiding.");
2159  return IPS_ALERT;
2160 }
2161 
2163 {
2164  INDI_UNUSED(ms);
2165  DEBUG(INDI::Logger::DBG_ERROR, "The CCD does not support guiding.");
2166  return IPS_ALERT;
2167 }
2168 
2170 {
2171  INDI_UNUSED(ms);
2172  DEBUG(INDI::Logger::DBG_ERROR, "The CCD does not support guiding.");
2173  return IPS_ALERT;
2174 }
2175 
2177 {
2178  INDI_UNUSED(ms);
2179  DEBUG(INDI::Logger::DBG_ERROR, "The CCD does not support guiding.");
2180  return IPS_ALERT;
2181 }
2182 
2183 void INDI::CCD::getMinMax(double *min, double *max, CCDChip *targetChip)
2184 {
2185  int ind=0, i, j;
2186  int imageHeight = targetChip->getSubH() / targetChip->getBinY();
2187  int imageWidth = targetChip->getSubW() / targetChip->getBinX();
2188  double lmin, lmax;
2189 
2190  switch (targetChip->getBPP())
2191  {
2192  case 8:
2193  {
2194  unsigned char *imageBuffer = (unsigned char *) targetChip->getFrameBuffer();
2195  lmin = lmax = imageBuffer[0];
2196 
2197 
2198  for (i= 0; i < imageHeight ; i++)
2199  for (j= 0; j < imageWidth; j++)
2200  {
2201  ind = (i * imageWidth) + j;
2202  if (imageBuffer[ind] < lmin) lmin = imageBuffer[ind];
2203  else if (imageBuffer[ind] > lmax) lmax = imageBuffer[ind];
2204  }
2205 
2206  }
2207  break;
2208 
2209  case 16:
2210  {
2211  unsigned short *imageBuffer = (unsigned short* ) targetChip->getFrameBuffer();
2212  lmin = lmax = imageBuffer[0];
2213 
2214  for (i= 0; i < imageHeight ; i++)
2215  for (j= 0; j < imageWidth; j++)
2216  {
2217  ind = (i * imageWidth) + j;
2218  if (imageBuffer[ind] < lmin) lmin = imageBuffer[ind];
2219  else if (imageBuffer[ind] > lmax) lmax = imageBuffer[ind];
2220  }
2221 
2222  }
2223  break;
2224 
2225  case 32:
2226  {
2227  unsigned int *imageBuffer = (unsigned int* ) targetChip->getFrameBuffer();
2228  lmin = lmax = imageBuffer[0];
2229 
2230  for (i= 0; i < imageHeight ; i++)
2231  for (j= 0; j < imageWidth; j++)
2232  {
2233  ind = (i * imageWidth) + j;
2234  if (imageBuffer[ind] < lmin) lmin = imageBuffer[ind];
2235  else if (imageBuffer[ind] > lmax) lmax = imageBuffer[ind];
2236 
2237  }
2238 
2239  }
2240  break;
2241 
2242  }
2243  *min = lmin;
2244  *max = lmax;
2245 }
2246 
2247 int INDI::CCD::getFileIndex(const char *dir, const char *prefix, const char *ext)
2248 {
2249  DIR *dpdf;
2250  struct dirent *epdf;
2251  std::vector<std::string> files = std::vector<std::string>();
2252 
2253  std::string prefixIndex = prefix;
2254  if (prefixIndex.find("XXX") == std::string::npos)
2255  return 0;
2256 
2257  std::string prefixSearch = prefix;
2258  prefixSearch.replace(prefixSearch.find("XXX"), 3, "");
2259 
2260  // Create directory if does not exist
2261  struct stat st = {0};
2262  if (stat(dir, &st) == -1)
2263  {
2264  DEBUGF(INDI::Logger::DBG_DEBUG, "Creating directory %s...", dir);
2265  if (mkdir(dir, 0755) == -1)
2266  DEBUGF(INDI::Logger::DBG_ERROR, "Error creating directory %s (%s)", dir, strerror(errno));
2267  }
2268 
2269  dpdf = opendir(dir);
2270  if (dpdf != NULL)
2271  {
2272  while (epdf = readdir(dpdf))
2273  {
2274  if (strstr(epdf->d_name, prefixSearch.c_str()))
2275  files.push_back(epdf->d_name);
2276  }
2277  }
2278  else
2279  return -1;
2280 
2281  int maxIndex=0;
2282 
2283  std::string filterIndex = "%d";
2284  prefixIndex.replace(prefixIndex.find("XXX"), 3, filterIndex);
2285  char filter[MAXRBUF];
2286  snprintf(filter, MAXRBUF, "%s%s", prefixIndex.c_str(), ext);
2287  for (int i=0; i < files.size(); i++)
2288  {
2289  int index=-1;
2290  sscanf(files.at(i).c_str(), filter, &index);
2291  if (index > maxIndex)
2292  maxIndex=index;
2293  }
2294 
2295  return (maxIndex+1);
2296 
2297 }
2298 
2299 void INDI::CCD::GuideComplete(INDI_EQ_AXIS axis)
2300 {
2302 }
2303 
2305 {
2306  DEBUG(INDI::Logger::DBG_ERROR, "Streaming is not supported.");
2307  return false;
2308 }
2309 
2311 {
2312  DEBUG(INDI::Logger::DBG_ERROR, "Streaming is not supported.");
2313  return false;
2314 }
virtual bool updateProperties()
updateProperties is called whenever there is a change in the CONNECTION status of the driver...
Definition: indiccd.cpp:566
void IUSaveConfigText(FILE *fp, const ITextVectorProperty *tvp)
Add a text vector property value to the configuration file.
Definition: indidriver.c:1359
void setPixelSize(float x, float y)
setPixelSize Set CCD Chip pixel size
Definition: indiccd.cpp:161
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition: indicom.c:1223
int getSubH()
getSubH Get the height of the frame
Definition: indiccd.h:92
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indicom.c:1213
void setFrameBufferSize(int nbuf, bool allocMem=true)
setFrameBufferSize Set desired frame buffer size. The function will allocate memory accordingly...
Definition: indiccd.cpp:183
void setBin(int hor, int ver)
setBin Set CCD Chip binnig
Definition: indiccd.cpp:121
void setFrameType(CCD_FRAME type)
setFrameType Set desired frame type for next exposure.
Definition: indiccd.cpp:79
virtual void addFITSKeywords(fitsfile *fptr, CCDChip *targetChip)
Add FITS keywords to a fits file.
Definition: indiccd.cpp:1439
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process the client newNumber command.
Definition: indiccd.cpp:835
virtual bool UpdateCCDFrame(int x, int y, int w, int h)
INDI::CCD calls this function when CCD Frame dimension needs to be updated in the hardware...
Definition: indiccd.cpp:1398
virtual bool StartExposure(float duration)
Start exposing primary CCD chip.
Definition: indiccd.cpp:1374
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector&#39;s auxiliary elements will be set to NULL...
Definition: indidriver.c:515
void setExposureDuration(double duration)
setExposureDuration Set desired CCD frame exposure duration for next exposure. You must call this fun...
Definition: indiccd.cpp:206
void setExposureLeft(double duration)
setExposureLeft Update exposure time left. Inform the client of the new exposure time left value...
Definition: indiccd.cpp:199
char format[MAXINDIBLOBFMT]
Definition: indiapi.h:376
char * pcdataXMLEle(XMLEle *ep)
Return the pcdata of an XML element.
Definition: lilxml.c:394
void IDSetText(const ITextVectorProperty *t, const char *msg,...)
Tell client to update an existing text vector property.
Definition: indidriver.c:1712
void IDSetSwitch(const ISwitchVectorProperty *s, const char *msg,...)
Tell client to update an existing switch vector property.
Definition: indidriver.c:1792
double step
Definition: indiapi.h:243
IPState
Property state.
Definition: indiapi.h:129
void setImageExtension(const char *ext)
setImageExtension Set image exntension
Definition: indiccd.cpp:250
virtual IPState GuideEast(float ms)
Guide easward for ms milliseconds.
Definition: indiccd.cpp:2169
void setResolution(int x, int y)
setResolution set CCD Chip resolution
Definition: indiccd.cpp:84
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:221
const char * getFrameTypeName(CCD_FRAME fType)
getFrameTypeName returns CCD Frame type name
Definition: indiccd.cpp:212
int IUUpdateText(ITextVectorProperty *tvp, char *texts[], char *names[], int n)
Update all text members in a text vector property.
Definition: indidriver.c:312
One number descriptor.
Definition: indiapi.h:238
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
Definition: indidriver.c:275
virtual bool StartGuideExposure(float duration)
Start exposing guide CCD chip.
Definition: indiccd.cpp:1380
Definition: indiapi.h:153
double getExposureDuration()
getExposureDuration Get requested exposure duration for the CCD chip in seconds.
Definition: indiccd.h:140
Definition: indiapi.h:152
int getNAxis() const
Definition: indiccd.cpp:240
int size
Definition: indiapi.h:382
void IUUpdateMinMax(const INumberVectorProperty *nvp)
Function to update the min and max elements of a number in the client.
Definition: indidriver.c:1920
Definition: indiapi.h:154
virtual int SetTemperature(double temperature)
Set CCD temperature.
Definition: indiccd.cpp:1367
int getFrameBufferSize()
getFrameBufferSize Get allocated frame buffer size to hold the CCD image frame.
Definition: indiccd.h:128
void binFrame()
binFrame Perform softwre binning on the CCD frame. Only use this function if hardware binning is not ...
Definition: indiccd.cpp:255
float getPixelSizeY()
getPixelSizeY Get vertical pixel size in microns.
Definition: indiccd.h:116
virtual bool UpdateGuiderFrameType(CCDChip::CCD_FRAME fType)
INDI::CCD calls this function when Guide frame type is updated by the client.
Definition: indiccd.cpp:1432
float getPixelSizeX()
getPixelSizeX Get horizontal pixel size in microns.
Definition: indiccd.h:110
void IDSetNumber(const INumberVectorProperty *n, const char *msg,...)
Tell client to update an existing number vector property.
Definition: indidriver.c:1752
void setExposureFailed()
setExposureFailed Alert the client that the exposure failed.
Definition: indiccd.cpp:234
virtual void SetCCDParams(int x, int y, int bpp, float xf, float yf)
Setup CCD paramters for primary CCD. Child classes call this function to update CCD paramaters...
Definition: indiccd.cpp:2105
int bloblen
Definition: indiapi.h:380
void setMinMaxStep(const char *property, const char *element, double min, double max, double step, bool sendToClient=true)
setMinMaxStep for a number property element
Definition: indiccd.cpp:133
void ISNewNumber(const char *dev, const char *name, double *doubles, char *names[], int n)
Update the value of an existing number vector property.
int IUSnoopNumber(XMLEle *root, INumberVectorProperty *nvp)
Update a snooped number vector property from the given XML root element.
Definition: indidriver.c:591
void IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text&#39;s auxiliary elements will be set to NULL...
Definition: indidriver.c:431
void ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int num)
Update the value of an existing switch vector property.
Definition: dome.cpp:38
void IUFillBLOB(IBLOB *bp, const char *name, const char *label, const char *format)
Assign attributes for a BLOB property. The BLOB&#39;s data and auxiliary elements will be set to NULL...
Definition: indidriver.c:451
virtual bool initProperties()
Initilize properties initial state and value. The child class must implement this function...
Definition: indiccd.cpp:381
void setNAxis(int value)
setNAxis Set FITS number of axis
Definition: indiccd.cpp:245
The CCDChip class provides functionality of a CCD Chip within a CCD.
Definition: indiccd.h:46
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indidriver.c:1340
virtual bool UpdateCCDFrameType(CCDChip::CCD_FRAME fType)
INDI::CCD calls this function when CCD frame type needs to be updated in the hardware.
Definition: indiccd.cpp:1425
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch&#39;s auxiliary elements will be set to NULL...
Definition: indidriver.c:379
void setFrame(int subx, int suby, int subw, int subh)
setFrame Set desired frame resolutoin for an exposure.
Definition: indiccd.cpp:106
void setInterlaced(bool intr)
setInterlaced Set whether the CCD chip is interlaced or not?
Definition: indiccd.cpp:229
uint8_t * getFrameBuffer()
getFrameBuffer Get raw frame buffer of the CCD chip.
Definition: indiccd.h:152
void * blob
Definition: indiapi.h:378
void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number&#39;s auxiliary elements will be set to NULL...
Definition: indidriver.c:410
char * getImageExtension()
Definition: indiccd.h:304
virtual bool StopStreaming()
StopStreaming Stop live video streaming.
Definition: indiccd.cpp:2310
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indidriver.c:1377
INumber * IUFindNumber(const INumberVectorProperty *nvp, const char *name)
Find an INumber member in a number text property.
Definition: indicom.c:1135
void IDSnoopDevice(const char *snooped_device, const char *snooped_property)
Function a Driver calls to snoop on another Device. Snooped messages will then arrive via ISSnoopDevi...
Definition: indidriver.c:132
void ISGetProperties(const char *dev)
Get Device Properties.
Definition: dome.cpp:33
Number vector property descriptor.
Definition: indiapi.h:266
void IDSetBLOB(const IBLOBVectorProperty *b, const char *msg,...)
Tell client to update an existing BLOB vector property.
Definition: indidriver.c:1869
int getBinX()
getBinX Get horizontal binning of the CCD chip.
Definition: indiccd.h:98
double min
Definition: indiapi.h:242
virtual void SetGuiderParams(int x, int y, int bpp, float xf, float yf)
Setup CCD paramters for guide head CCD. Child classes call this function to update CCD paramaters...
Definition: indiccd.cpp:2116
int getSubW()
getSubW Get the width of the frame
Definition: indiccd.h:86
virtual bool UpdateCCDBin(int hor, int ver)
INDI::CCD calls this function when CCD Binning needs to be updated in the hardware. Derived classes should implement this function.
Definition: indiccd.cpp:1411
virtual bool ISSnoopDevice(XMLEle *root)
Process a snoop event from INDI server. This function is called when a snooped property is updated in...
virtual IPState GuideWest(float ms)
Guide westward for ms milliseconds.
Definition: indiccd.cpp:2176
void IDLog(const char *msg,...)
Function Drivers call to log a message locally.
Definition: indicom.c:230
const char * getExposureStartTime()
getExposureStartTime
Definition: indiccd.cpp:217
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process the client newSwitch command.
void IUFillBLOBVector(IBLOBVectorProperty *bvp, IBLOB *bp, int nbp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a BLOB vector property. The vector&#39;s auxiliary elements will be set to NULL...
Definition: indidriver.c:559
virtual bool StartStreaming()
StartStreaming Start live video streaming.
Definition: indiccd.cpp:2304
virtual bool ISSnoopDevice(XMLEle *root)
Process a snoop event from INDI server. This function is called when a snooped property is updated in...
Definition: indiccd.cpp:717
CCD_FRAME getFrameType()
getFrameType
Definition: indiccd.h:178
ISState
Switch state.
Definition: indiapi.h:120
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector&#39;s auxiliary elements will be set to NULL...
Definition: indidriver.c:472
const char * findXMLAttValu(XMLEle *ep, const char *name)
Find an XML element&#39;s attribute value.
Definition: lilxml.c:439
virtual bool AbortExposure()
Abort ongoing exposure.
Definition: indiccd.cpp:1386
void GuideComplete(INDI_EQ_AXIS axis)
Call GuideComplete once the guiding pulse is complete.
Definition: indiccd.cpp:2299
int getBinY()
getBinY Get vertical binning of the CCD chip.
Definition: indiccd.h:104
void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector&#39;s auxiliary elements will be set to NULL...
Definition: indidriver.c:538
void setBPP(int bpp)
setBPP Set depth of CCD chip.
Definition: indiccd.cpp:174
XMLEle * nextXMLEle(XMLEle *ep, int init)
Iterate an XML element for a list of nesetd XML elements.
Definition: lilxml.c:338
void fits_update_key_s(fitsfile *fptr, int type, std::string name, void *p, std::string explanation, int *status)
Definition: indiccd.cpp:1613
virtual IPState GuideSouth(float ms)
Guide southward for ms milliseconds.
Definition: indiccd.cpp:2162
virtual bool saveConfigItems(FILE *fp)
saveConfigItems Save configuration items in XML file.
Definition: indiccd.cpp:2127
virtual IPState GuideNorth(float ms)
Guide northward for ms milliseconds.
Definition: indiccd.cpp:2155
virtual bool AbortGuideExposure()
Abort ongoing exposure.
Definition: indiccd.cpp:1392
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
Process the client newSwitch command.
Definition: indiccd.cpp:779
virtual void GuideComplete(INDI_EQ_AXIS axis)
Call GuideComplete once the guiding pulse is complete.
virtual bool UpdateGuiderBin(int hor, int ver)
INDI::CCD calls this function when Guide head binning is updated by the client. Derived classes shoul...
Definition: indiccd.cpp:1418
virtual void ISGetProperties(const char *dev)
define the driver&#39;s properties to the client. Usually, only a minumum set of properties are defined t...
Definition: indiccd.cpp:550
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process the client newSwitch command.
Definition: indiccd.cpp:1093
virtual bool UpdateGuiderFrame(int x, int y, int w, int h)
INDI::CCD calls this function when Guide head frame dimension is updated by the client. Derived classes should implement this function.
Definition: indiccd.cpp:1405
int getBPP()
getBPP Get CCD Chip depth (bits per pixel).
Definition: indiccd.h:122
void SetCCDCapability(uint32_t cap)
SetCCDCapability Set the CCD capabilities. Al fields must be initilized.
Definition: indiccd.cpp:361
virtual bool ExposureComplete(CCDChip *targetChip)
Uploads target Chip exposed buffer as FITS to the client. Dervied classes should class this functon w...
Definition: indiccd.cpp:1619
double value
Definition: indiapi.h:244