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