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