Instrument Neutral Distributed Interface INDI  2.0.2
indiccdchip.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright(c) 2019 Jasem Mutlaq. All rights reserved.
3 
4  This library is free software; you can redistribute it and/or
5  modify it under the terms of the GNU Library General Public
6  License version 2 as published by the Free Software Foundation.
7 
8  This library is distributed in the hope that it will be useful,
9  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11  Library General Public License for more details.
12 
13  You should have received a copy of the GNU Library General Public License
14  along with this library; see the file COPYING.LIB. If not, write to
15  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16  Boston, MA 02110-1301, USA.
17 *******************************************************************************/
18 #include "indiccdchip.h"
19 #include "indidevapi.h"
20 #include "locale_compat.h"
21 
22 #include <cstring>
23 #include <ctime>
24 
25 namespace INDI
26 {
27 
29 {
30  strncpy(ImageExtention, "fits", MAXINDIBLOBFMT);
31 }
32 
34 {
35  IDSharedBlobFree(RawFrame);
36  IDSharedBlobFree(BinFrame);
37  IDSharedBlobFree(m_FITSMemoryBlock);
38 }
39 
40 bool CCDChip::openFITSFile(uint32_t size, int &status)
41 {
42  m_FITSMemorySize = size > 2880 ? 2880 : size;
43  m_FITSMemoryBlock = IDSharedBlobAlloc(size);
44  if (m_FITSMemoryBlock == nullptr)
45  {
46  IDLog("Failed to allocate memory for FITS file.");
47  status = MEMORY_ALLOCATION;
48  return false;
49  }
50 
51  fits_create_memfile(&m_FITSFilePointer, &m_FITSMemoryBlock, &m_FITSMemorySize, 2880, IDSharedBlobRealloc, &status);
52  if (status != 0)
53  {
54  IDSharedBlobFree(m_FITSMemoryBlock);
55  m_FITSMemoryBlock = nullptr;
56  }
57 
58  return (status == 0);
59 }
60 
61 bool CCDChip::finishFITSFile(int &status)
62 {
63  fits_flush_file(m_FITSFilePointer, &status);
64  fits_close_file(m_FITSFilePointer, &status);
65  if (status == 0)
66  {
67  m_FITSFilePointer = nullptr;
68  }
69  return (status == 0);
70 }
71 
73 {
74  if (m_FITSFilePointer != nullptr)
75  {
76  int status = 0;
77  // Discard error here, the caller can't expect a valid file anymore at that point
78  fits_close_file(m_FITSFilePointer, &status);
79  m_FITSFilePointer = nullptr;
80  }
81  IDSharedBlobFree(m_FITSMemoryBlock);
82  m_FITSMemoryBlock = nullptr;
83 }
84 
86 {
87  FrameType = type;
88 }
89 
90 void CCDChip::setResolution(uint32_t x, uint32_t y)
91 {
92  XRes = x;
93  YRes = y;
94 
95  ImagePixelSizeN[0].value = x;
96  ImagePixelSizeN[1].value = y;
97 
98  IDSetNumber(&ImagePixelSizeNP, nullptr);
99 
100  ImageFrameN[FRAME_X].min = 0;
101  ImageFrameN[FRAME_X].max = x - 1;
102  ImageFrameN[FRAME_Y].min = 0;
103  ImageFrameN[FRAME_Y].max = y - 1;
104 
105  ImageFrameN[FRAME_W].min = 1;
106  ImageFrameN[FRAME_W].max = x;
107  ImageFrameN[FRAME_H].min = 1;
108  ImageFrameN[FRAME_H].max = y;
109  IUUpdateMinMax(&ImageFrameNP);
110 }
111 
112 void CCDChip::setFrame(uint32_t subx, uint32_t suby, uint32_t subw, uint32_t subh)
113 {
114  SubX = subx;
115  SubY = suby;
116  SubW = subw;
117  SubH = subh;
118 
119  ImageFrameN[FRAME_X].value = SubX;
120  ImageFrameN[FRAME_Y].value = SubY;
121  ImageFrameN[FRAME_W].value = SubW;
122  ImageFrameN[FRAME_H].value = SubH;
123 
124  IDSetNumber(&ImageFrameNP, nullptr);
125 }
126 
127 void CCDChip::setBin(uint8_t hor, uint8_t ver)
128 {
129  BinX = hor;
130  BinY = ver;
131 
132  ImageBinN[BIN_W].value = BinX;
133  ImageBinN[BIN_H].value = BinY;
134 
135  IDSetNumber(&ImageBinNP, nullptr);
136 }
137 
138 void CCDChip::setMinMaxStep(const char *property, const char *element, double min, double max, double step,
139  bool sendToClient)
140 {
141  INumberVectorProperty *nvp = nullptr;
142 
143  if (!strcmp(property, ImageExposureNP.name))
144  nvp = &ImageExposureNP;
145  else if (!strcmp(property, ImageFrameNP.name))
146  nvp = &ImageFrameNP;
147  else if (!strcmp(property, ImageBinNP.name))
148  nvp = &ImageBinNP;
149  else if (!strcmp(property, ImagePixelSizeNP.name))
150  nvp = &ImagePixelSizeNP;
151  // else if (!strcmp(property, RapidGuideDataNP.name))
152  // nvp = &RapidGuideDataNP;
153  else
154  return;
155 
156  INumber *np = IUFindNumber(nvp, element);
157  if (np)
158  {
159  np->min = min;
160  np->max = max;
161  np->step = step;
162 
163  if (sendToClient)
164  IUUpdateMinMax(nvp);
165  }
166 }
167 
168 void CCDChip::setPixelSize(double x, double y)
169 {
170  PixelSizeX = x;
171  PixelSizeY = y;
172 
173  ImagePixelSizeN[2].value = x;
174  ImagePixelSizeN[3].value = x;
175  ImagePixelSizeN[4].value = y;
176 
177  IDSetNumber(&ImagePixelSizeNP, nullptr);
178 }
179 
180 void CCDChip::setBPP(uint8_t bbp)
181 {
182  BitsPerPixel = bbp;
183 
184  ImagePixelSizeN[5].value = BitsPerPixel;
185 
186  IDSetNumber(&ImagePixelSizeNP, nullptr);
187 }
188 
189 void CCDChip::setFrameBufferSize(uint32_t nbuf, bool allocMem)
190 {
191  if (nbuf == RawFrameSize)
192  return;
193 
194  RawFrameSize = nbuf;
195 
196  if (allocMem == false)
197  return;
198 
199  RawFrame = static_cast<uint8_t*>(IDSharedBlobRealloc(RawFrame, RawFrameSize));
200  if (RawFrame == nullptr)
201  RawFrame = static_cast<uint8_t*>(IDSharedBlobAlloc(RawFrameSize));
202 
203  if (BinFrame)
204  {
205  BinFrame = static_cast<uint8_t*>(IDSharedBlobRealloc(BinFrame, RawFrameSize));
206  if (BinFrame == nullptr)
207  BinFrame = static_cast<uint8_t*>(IDSharedBlobAlloc(RawFrameSize));
208  }
209 }
210 
211 void CCDChip::setExposureLeft(double duration)
212 {
213  ImageExposureNP.s = IPS_BUSY;
214  ImageExposureN[0].value = duration;
215  IDSetNumber(&ImageExposureNP, nullptr);
216 }
217 
219 {
220  ImageExposureNP.s = IPS_OK;
221  ImageExposureN[0].value = 0;
222  IDSetNumber(&ImageExposureNP, nullptr);
223 }
224 
225 void CCDChip::setExposureDuration(double duration)
226 {
227  ExposureDuration = duration;
228  gettimeofday(&StartExposureTime, nullptr);
229 }
230 
232 {
233  return FrameTypeS[fType].name;
234 }
235 
237 {
238  static char ts[32];
239 
240  char iso8601[32] = {0};
241  struct tm *tp = nullptr;
242 
243  // Get exposure startup timestamp
244  time_t t = static_cast<time_t>(StartExposureTime.tv_sec);
245 
246  // Get UTC timestamp
247  tp = gmtime(&t);
248 
249  // Format it in ISO8601 format
250  strftime(iso8601, sizeof(iso8601), "%Y-%m-%dT%H:%M:%S", tp);
251 
252  // Add millisecond
253  snprintf(ts, 32, "%s.%03d", iso8601, static_cast<int>(StartExposureTime.tv_usec / 1000.0));
254 
255  return (ts);
256 }
257 
258 //void CCDChip::setInterlaced(bool intr)
259 //{
260 // Interlaced = intr;
261 //}
262 
264 {
265  ImageExposureNP.s = IPS_ALERT;
266  IDSetNumber(&ImageExposureNP, nullptr);
267 }
268 
269 int CCDChip::getNAxis() const
270 {
271  return NAxis;
272 }
273 
274 void CCDChip::setNAxis(int value)
275 {
276  NAxis = value;
277 }
278 
279 void CCDChip::setImageExtension(const char *ext)
280 {
281  strncpy(ImageExtention, ext, MAXINDIBLOBFMT);
282 }
283 
285 {
286  if (BinX == 1)
287  return;
288 
289  // Jasem: Keep full frame shadow in memory to enhance performance and just swap frame pointers after operation is complete
290  if (BinFrame == nullptr)
291  BinFrame = static_cast<uint8_t*>(IDSharedBlobAlloc(RawFrameSize));
292  else
293  {
294  BinFrame = static_cast<uint8_t*>(IDSharedBlobRealloc(BinFrame, RawFrameSize));
295  if (BinFrame == nullptr)
296  BinFrame = static_cast<uint8_t*>(IDSharedBlobAlloc(RawFrameSize));
297  }
298 
299  memset(BinFrame, 0, RawFrameSize);
300 
301  switch (getBPP())
302  {
303  case 8:
304  {
305  uint8_t *bin_buf = BinFrame;
306  // Try to average pixels since in 8bit they get saturated pretty quickly
307  double factor = (BinX * BinX) / 2;
308  double accumulator;
309 
310  for (uint32_t i = 0; i < SubH; i += BinX)
311  for (uint32_t j = 0; j < SubW; j += BinX)
312  {
313  accumulator = 0;
314  for (int k = 0; k < BinX; k++)
315  {
316  for (int l = 0; l < BinX; l++)
317  {
318  accumulator += *(RawFrame + j + (i + k) * SubW + l);
319  }
320  }
321 
322  accumulator /= factor;
323  if (accumulator > UINT8_MAX)
324  *bin_buf = UINT8_MAX;
325  else
326  *bin_buf += static_cast<uint8_t>(accumulator);
327  bin_buf++;
328  }
329  }
330  break;
331 
332  case 16:
333  {
334  uint16_t *bin_buf = reinterpret_cast<uint16_t *>(BinFrame);
335  uint16_t *RawFrame16 = reinterpret_cast<uint16_t *>(RawFrame);
336  uint16_t val;
337 
338  for (uint32_t i = 0; i < SubH; i += BinX)
339  for (uint32_t j = 0; j < SubW; j += BinX)
340  {
341  for (int k = 0; k < BinX; k++)
342  {
343  for (int l = 0; l < BinX; l++)
344  {
345  val = *(RawFrame16 + j + (i + k) * SubW + l);
346  if (val + *bin_buf > UINT16_MAX)
347  *bin_buf = UINT16_MAX;
348  else
349  *bin_buf += val;
350  }
351  }
352  bin_buf++;
353  }
354  }
355  break;
356 
357  default:
358  return;
359  }
360 
361  // Swap frame pointers
362  uint8_t *rawFramePointer = RawFrame;
363  RawFrame = BinFrame;
364  // We just memset it next time we use it
365  BinFrame = rawFramePointer;
366 }
367 
368 
369 // Thx8411:
370 // Binning Bayer frames
371 // Each raw frame pixel is mapped and summed onto the binned frame
372 // The right place of each pixel in the 2x2 Bayer matrix is found by :
373 // (((i/BinX) & 0xFFFFFFFE) + (i & 0x00000001))
374 // and
375 // ((j/BinX) & 0xFFFFFFFE) + (j & 0x00000001)
376 //
378 {
379  if (BinX == 1)
380  return;
381 
382  // Jasem: Keep full frame shadow in memory to enhance performance and just swap frame pointers after operation is complete
383  if (BinFrame == nullptr)
384  BinFrame = static_cast<uint8_t*>(IDSharedBlobAlloc(RawFrameSize));
385  else
386  {
387  BinFrame = static_cast<uint8_t*>(IDSharedBlobRealloc(BinFrame, RawFrameSize));
388  if (BinFrame == nullptr)
389  BinFrame = static_cast<uint8_t*>(IDSharedBlobAlloc(RawFrameSize));
390  }
391 
392  memset(BinFrame, 0, RawFrameSize);
393 
394  switch (getBPP())
395  {
396  // 8 bpp frame
397  case 8:
398  {
399  uint32_t BinFrameOffset;
400  uint32_t val;
401  uint32_t BinW = SubW / BinX;
402  uint8_t BinFactor = BinX * BinY;
403  uint32_t RawOffset = 0;
404  uint32_t BinOffsetH;
405 
406  // for each raw frame row
407  for (uint32_t i = 0; i < SubH; i++)
408  {
409  // find the binned frame row
410  BinOffsetH = (((i / BinY) & 0xFFFFFFFE) + (i & 0x00000001)) * BinW;
411  // for each raw column
412  for (uint32_t j = 0; j < SubW; j++)
413  {
414  // find the proper position in the binned frame
415  BinFrameOffset = BinOffsetH + ((j / BinX) & 0xFFFFFFFE) + (j & 0x00000001);
416  // get the existing value in the binned frame
417  val = BinFrame[BinFrameOffset];
418  // add the new value, averaged and caped
419  val += RawFrame[RawOffset] / BinFactor;
420  if(val > UINT8_MAX)
421  val = UINT8_MAX;
422  // write back into the binned frame
423  BinFrame[BinFrameOffset] = static_cast<uint8_t>(val);
424  // next binned frame pixel
425  RawOffset++;
426  }
427  }
428  }
429  break;
430 
431  // 16 bpp frame
432  case 16:
433  {
434  // works the same as the 8 bits version, without averaging but
435  // mapped onto 16 bits pixel
436  uint16_t *RawFrame16 = reinterpret_cast<uint16_t *>(RawFrame);
437  uint16_t *BinFrame16 = reinterpret_cast<uint16_t *>(BinFrame);
438 
439  uint32_t BinFrameOffset;
440  uint32_t val;
441  uint32_t BinW = SubW / BinX;
442  uint32_t RawOffset = 0;
443 
444  for (uint32_t i = 0; i < SubH; i++)
445  {
446  uint32_t BinOffsetH = (((i / BinY) & 0xFFFFFFFE) + (i & 0x00000001)) * BinW;
447  for (uint32_t j = 0; j < SubW; j++)
448  {
449  BinFrameOffset = BinOffsetH + ((j / BinX) & 0xFFFFFFFE) + (j & 0x00000001);
450  val = BinFrame16[BinFrameOffset];
451  val += RawFrame16[RawOffset];
452  if(val > UINT16_MAX)
453  val = UINT16_MAX;
454  BinFrame16[BinFrameOffset] = (uint16_t)val;
455  RawOffset++;
456  }
457  }
458 
459  }
460  break;
461 
462  default:
463  return;
464  }
465 
466  // Swap frame pointers
467  uint8_t *rawFramePointer = RawFrame;
468  RawFrame = BinFrame;
469  // We just memset it next time we use it
470  BinFrame = rawFramePointer;
471 }
472 
473 }
void setExposureComplete()
setExposureComplete Mark exposure as complete by setting ImageExposure property to IPS_OK
bool finishFITSFile(int &status)
Finish any pending write to fits file.
Definition: indiccdchip.cpp:61
void setExposureDuration(double duration)
setExposureDuration Set desired CCD frame exposure duration for next exposure. You must call this fun...
void setResolution(uint32_t x, uint32_t y)
setResolution set CCD Chip resolution
Definition: indiccdchip.cpp:90
int getBPP() const
getBPP Get CCD Chip depth (bits per pixel).
Definition: indiccdchip.h:167
const char * getExposureStartTime()
getExposureStartTime
void closeFITSFile()
closeFITSFile Close the in-memory FITS File.
Definition: indiccdchip.cpp:72
void setFrame(uint32_t subx, uint32_t suby, uint32_t subw, uint32_t subh)
setFrame Set desired frame resolutoin for an exposure.
void setMinMaxStep(const char *property, const char *element, double min, double max, double step, bool sendToClient=true)
setMinMaxStep for a number property element
void setFrameType(CCD_FRAME type)
setFrameType Set desired frame type for next exposure.
Definition: indiccdchip.cpp:85
void setImageExtension(const char *ext)
setImageExtension Set image exntension
void setPixelSize(double x, double y)
setPixelSize Set CCD Chip pixel size
void setExposureFailed()
setExposureFailed Alert the client that the exposure failed.
void setExposureLeft(double duration)
setExposureLeft Update exposure time left. Inform the client of the new exposure time left value.
const char * getFrameTypeName(CCD_FRAME fType)
getFrameTypeName returns CCD Frame type name
void setFrameBufferSize(uint32_t nbuf, bool allocMem=true)
setFrameBufferSize Set desired frame buffer size. The function will allocate memory accordingly....
void setBin(uint8_t hor, uint8_t ver)
setBin Set CCD Chip binnig
int getNAxis() const
void binBayerFrame()
binBayerFrame Perform software binning on a 2x2 Bayer matrix CCD frame. Only use this function if har...
void binFrame()
binFrame Perform software binning on the CCD frame. Only use this function if hardware binning is not...
void setBPP(uint8_t bpp)
setBPP Set depth of CCD chip.
bool openFITSFile(uint32_t size, int &status)
openFITSFile Allocate memory buffer for internal FITS file structure and open
Definition: indiccdchip.cpp:40
void setNAxis(int value)
setNAxis Set FITS number of axis
double max(void)
double min(void)
#define MAXINDIBLOBFMT
Definition: indiapi.h:196
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_OK
Definition: indiapi.h:162
void IDLog(const char *fmt,...)
Definition: indicom.c:316
INumber * IUFindNumber(const INumberVectorProperty *nvp, const char *name)
Find an INumber member in a number text property.
Definition: indidevapi.c:66
Interface to the reference INDI C API device implementation on the Device Driver side.
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
void IUUpdateMinMax(const INumberVectorProperty *nvp)
Function to update the min and max elements of a number in the client.
Definition: indidriver.c:1296
Namespace to encapsulate INDI client, drivers, and mediator classes.
__le16 type
Definition: pwc-ioctl.h:0
void IDSharedBlobFree(void *ptr)
Definition: sharedblob.c:132
void * IDSharedBlobRealloc(void *ptr, size_t size)
Definition: sharedblob.c:171
void * IDSharedBlobAlloc(size_t size)
Definition: sharedblob.c:70
One number descriptor.
Number vector property descriptor.
Definition: indiapi.h:319
char name[MAXINDINAME]
Definition: indiapi.h:323