Instrument Neutral Distributed Interface INDI 2.2.5
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 "sharedblob.h"
21#include "locale_compat.h"
22
23#include <cstring>
24#include <ctime>
25
26namespace INDI
27{
28
30{
31 strncpy(ImageExtention, "fits", MAXINDIBLOBFMT);
32}
33
35{
36 IDSharedBlobFree(RawFrame);
37 IDSharedBlobFree(BinFrame);
38 IDSharedBlobFree(m_FITSMemoryBlock);
39}
40
41bool CCDChip::openFITSFile(uint32_t size, int &status)
42{
43 m_FITSMemorySize = size > 2880 ? 2880 : size;
44 m_FITSMemoryBlock = IDSharedBlobAlloc(size);
45 if (m_FITSMemoryBlock == nullptr)
46 {
47 IDLog("Failed to allocate memory for FITS file.");
48 status = MEMORY_ALLOCATION;
49 return false;
50 }
51
52 fits_create_memfile(&m_FITSFilePointer, &m_FITSMemoryBlock, &m_FITSMemorySize, 2880, IDSharedBlobRealloc, &status);
53 if (status != 0)
54 {
55 IDSharedBlobFree(m_FITSMemoryBlock);
56 m_FITSMemoryBlock = nullptr;
57 }
58
59 return (status == 0);
60}
61
62bool CCDChip::finishFITSFile(int &status)
63{
64 fits_flush_file(m_FITSFilePointer, &status);
65 fits_close_file(m_FITSFilePointer, &status);
66 if (status == 0)
67 {
68 m_FITSFilePointer = nullptr;
69 }
70 return (status == 0);
71}
72
74{
75 if (m_FITSFilePointer != nullptr)
76 {
77 int status = 0;
78 // Discard error here, the caller can't expect a valid file anymore at that point
79 fits_close_file(m_FITSFilePointer, &status);
80 m_FITSFilePointer = nullptr;
81 }
82 IDSharedBlobFree(m_FITSMemoryBlock);
83 m_FITSMemoryBlock = nullptr;
84}
85
87{
88 FrameType = type;
89}
90
91void CCDChip::setResolution(uint32_t x, uint32_t y)
92{
93 XRes = x;
94 YRes = y;
95
96 ImagePixelSizeNP[0].setValue(x);
97 ImagePixelSizeNP[1].setValue(y);
98
99 ImagePixelSizeNP.apply();
100
101 ImageFrameNP[FRAME_X].setMin(0);
102 ImageFrameNP[FRAME_X].setMax(x - 1);
103 ImageFrameNP[FRAME_Y].setMin(0);
104 ImageFrameNP[FRAME_Y].setMax(y - 1);
105
106 ImageFrameNP[FRAME_W].setMin(1);
107 ImageFrameNP[FRAME_W].setMax(x);
108 ImageFrameNP[FRAME_H].setMin(1);
109 ImageFrameNP[FRAME_H].setMax(y);
110 ImageFrameNP.updateMinMax();
111}
112
113void CCDChip::setFrame(uint32_t subx, uint32_t suby, uint32_t subw, uint32_t subh)
114{
115 SubX = subx;
116 SubY = suby;
117 SubW = subw;
118 SubH = subh;
119
120 ImageFrameNP[FRAME_X].setValue(SubX);
121 ImageFrameNP[FRAME_Y].setValue(SubY);
122 ImageFrameNP[FRAME_W].setValue(SubW);
123 ImageFrameNP[FRAME_H].setValue(SubH);
124
125 ImageFrameNP.apply();
126}
127
128void CCDChip::setBin(uint8_t hor, uint8_t ver)
129{
130 BinX = hor;
131 BinY = ver;
132
133 ImageBinNP[BIN_W].setValue(BinX);
134 ImageBinNP[BIN_H].setValue(BinY);
135
136 ImageBinNP.apply();
137}
138
139void CCDChip::setMinMaxStep(const char *property, const char *element, double min, double max, double step,
140 bool sendToClient)
141{
142 auto updateMinMaxStep = [element, min, max, step, sendToClient](INDI::PropertyNumber & oneProperty)
143 {
144 auto oneElement = oneProperty.findWidgetByName(element);
145 if(oneElement)
146 {
147 oneElement->setMinMax(min, max);
148 oneElement->setStep(step);
149 if(sendToClient)
150 {
151 oneProperty.updateMinMax();
152 }
153 }
154 };
155
156 if (ImageExposureNP.isNameMatch(property))
157 updateMinMaxStep(ImageExposureNP);
158 else if (ImageFrameNP.isNameMatch(property))
159 updateMinMaxStep(ImageFrameNP);
160 else if (ImageBinNP.isNameMatch(property))
161 updateMinMaxStep(ImageBinNP);
162
163 else if (ImagePixelSizeNP.isNameMatch(property))
164 updateMinMaxStep(ImagePixelSizeNP);
165}
166
167void CCDChip::setPixelSize(double x, double y)
168{
169 PixelSizeX = x;
170 PixelSizeY = y;
171
172 ImagePixelSizeNP[2].setValue(x);
173 ImagePixelSizeNP[3].setValue(x);
174 ImagePixelSizeNP[4].setValue(y);
175
176 ImagePixelSizeNP.apply();
177}
178
179void CCDChip::setBPP(uint8_t bbp)
180{
181 BitsPerPixel = bbp;
182
183 ImagePixelSizeNP[5].setValue(BitsPerPixel);
184
185 ImagePixelSizeNP.apply();
186}
187
188void CCDChip::setFrameBufferSize(uint32_t nbuf, bool allocMem)
189{
190 if (nbuf == RawFrameSize)
191 return;
192
193 RawFrameSize = nbuf;
194
195 if (allocMem == false)
196 return;
197
198 RawFrame = static_cast<uint8_t*>(IDSharedBlobRealloc(RawFrame, RawFrameSize));
199 if (RawFrame == nullptr)
200 RawFrame = static_cast<uint8_t * >(IDSharedBlobAlloc(RawFrameSize));
201
202 if (BinFrame)
203 {
204 BinFrame = static_cast<uint8_t*>(IDSharedBlobRealloc(BinFrame, RawFrameSize));
205 if (BinFrame == nullptr)
206 BinFrame = static_cast<uint8_t * >(IDSharedBlobAlloc(RawFrameSize));
207 }
208}
209
210void CCDChip::setExposureLeft(double duration)
211{
212 ImageExposureNP.setState(IPS_BUSY);
213 ImageExposureNP[0].setValue(duration);
214 ImageExposureNP.apply();
215}
216
218{
219 ImageExposureNP.setState(IPS_OK);
220 ImageExposureNP[0].setValue(0);
221 ImageExposureNP.apply();
222}
223
224void CCDChip::setExposureDuration(double duration)
225{
226 ExposureDuration = duration;
227 gettimeofday(&StartExposureTime, nullptr);
228}
229
231{
232 return FrameTypeSP[fType].getName();
233}
234
236{
237 static char ts[32];
238
239 char iso8601[32] = {0};
240 struct tm *tp = nullptr;
241
242 // Get exposure startup timestamp
243 time_t t = static_cast<time_t>(StartExposureTime.tv_sec);
244
245 // Get UTC timestamp
246 tp = gmtime(&t);
247
248 // Format it in ISO8601 format
249 strftime(iso8601, sizeof(iso8601), "%Y-%m-%dT%H:%M:%S", tp);
250
251 // Add millisecond
252 snprintf(ts, 32, "%s.%03d", iso8601, static_cast<int>(StartExposureTime.tv_usec / 1000.0));
253
254 return (ts);
255}
256
257//void CCDChip::setInterlaced(bool intr)
258//{
259// Interlaced = intr;
260//}
261
263{
264 ImageExposureNP.setState(IPS_ALERT);
265 ImageExposureNP.apply();
266}
267
269{
270 return NAxis;
271}
272
273void CCDChip::setNAxis(int value)
274{
275 NAxis = value;
276}
277
278void CCDChip::setImageExtension(const char *ext)
279{
280 snprintf(ImageExtention, MAXINDIBLOBFMT, "%s", ext);
281}
282
284{
285 if (BinX == 1)
286 return;
287
288 // Jasem: Keep full frame shadow in memory to enhance performance and just swap frame pointers after operation is complete
289 if (BinFrame == nullptr)
290 BinFrame = static_cast<uint8_t * >(IDSharedBlobAlloc(RawFrameSize));
291 else
292 {
293 BinFrame = static_cast<uint8_t*>(IDSharedBlobRealloc(BinFrame, RawFrameSize));
294 if (BinFrame == nullptr)
295 BinFrame = static_cast<uint8_t * >(IDSharedBlobAlloc(RawFrameSize));
296 }
297
298 memset(BinFrame, 0, RawFrameSize);
299
300 switch (getBPP())
301 {
302 case 8:
303 {
304 uint8_t *bin_buf = BinFrame;
305 // Try to average pixels since in 8bit they get saturated pretty quickly
306 double factor = (BinX * BinX) / 2;
307 double accumulator;
308
309 for (uint32_t i = 0; i < SubH; i += BinX)
310 for (uint32_t j = 0; j < SubW; j += BinX)
311 {
312 accumulator = 0;
313 for (int k = 0; k < BinX; k++)
314 {
315 for (int l = 0; l < BinX; l++)
316 {
317 accumulator += *(RawFrame + j + (i + k) * SubW + l);
318 }
319 }
320
321 accumulator /= factor;
322 if (accumulator > UINT8_MAX)
323 *bin_buf = UINT8_MAX;
324 else
325 *bin_buf += static_cast<uint8_t>(accumulator);
326 bin_buf++;
327 }
328 }
329 break;
330
331 case 16:
332 {
333 uint16_t *bin_buf = reinterpret_cast<uint16_t *>(BinFrame);
334 uint16_t *RawFrame16 = reinterpret_cast<uint16_t *>(RawFrame);
335 uint16_t val;
336
337 for (uint32_t i = 0; i < SubH; i += BinX)
338 for (uint32_t j = 0; j < SubW; j += BinX)
339 {
340 for (int k = 0; k < BinX; k++)
341 {
342 for (int l = 0; l < BinX; l++)
343 {
344 val = *(RawFrame16 + j + (i + k) * SubW + l);
345 if (val + *bin_buf > UINT16_MAX)
346 *bin_buf = UINT16_MAX;
347 else
348 *bin_buf += val;
349 }
350 }
351 bin_buf++;
352 }
353 }
354 break;
355
356 default:
357 return;
358 }
359
360 // Swap frame pointers
361 uint8_t *rawFramePointer = RawFrame;
362 RawFrame = BinFrame;
363 // We just memset it next time we use it
364 BinFrame = rawFramePointer;
365}
366
367
368// Thx8411:
369// Binning Bayer frames
370// Each raw frame pixel is mapped and summed onto the binned frame
371// The right place of each pixel in the 2x2 Bayer matrix is found by :
372// (((i/BinX) & 0xFFFFFFFE) + (i & 0x00000001))
373// and
374// ((j/BinX) & 0xFFFFFFFE) + (j & 0x00000001)
375//
377{
378 if (BinX == 1)
379 return;
380
381 // Jasem: Keep full frame shadow in memory to enhance performance and just swap frame pointers after operation is complete
382 if (BinFrame == nullptr)
383 BinFrame = static_cast<uint8_t * >(IDSharedBlobAlloc(RawFrameSize));
384 else
385 {
386 BinFrame = static_cast<uint8_t*>(IDSharedBlobRealloc(BinFrame, RawFrameSize));
387 if (BinFrame == nullptr)
388 BinFrame = static_cast<uint8_t * >(IDSharedBlobAlloc(RawFrameSize));
389 }
390
391 memset(BinFrame, 0, RawFrameSize);
392
393 switch (getBPP())
394 {
395 // 8 bpp frame
396 case 8:
397 {
398 uint32_t BinFrameOffset;
399 uint32_t val;
400 uint32_t BinW = SubW / BinX;
401 uint8_t BinFactor = BinX * BinY;
402 uint32_t RawOffset = 0;
403 uint32_t BinOffsetH;
404
405 // for each raw frame row
406 for (uint32_t i = 0; i < SubH; i++)
407 {
408 // find the binned frame row
409 BinOffsetH = (((i / BinY) & 0xFFFFFFFE) + (i & 0x00000001)) * BinW;
410 // for each raw column
411 for (uint32_t j = 0; j < SubW; j++)
412 {
413 // find the proper position in the binned frame
414 BinFrameOffset = BinOffsetH + ((j / BinX) & 0xFFFFFFFE) + (j & 0x00000001);
415 // get the existing value in the binned frame
416 val = BinFrame[BinFrameOffset];
417 // add the new value, averaged and caped
418 val += RawFrame[RawOffset] / BinFactor;
419 if(val > UINT8_MAX)
420 val = UINT8_MAX;
421 // write back into the binned frame
422 BinFrame[BinFrameOffset] = static_cast<uint8_t>(val);
423 // next binned frame pixel
424 RawOffset++;
425 }
426 }
427 }
428 break;
429
430 // 16 bpp frame
431 case 16:
432 {
433 // works the same as the 8 bits version, without averaging but
434 // mapped onto 16 bits pixel
435 uint16_t *RawFrame16 = reinterpret_cast<uint16_t *>(RawFrame);
436 uint16_t *BinFrame16 = reinterpret_cast<uint16_t *>(BinFrame);
437
438 uint32_t BinFrameOffset;
439 uint32_t val;
440 uint32_t BinW = SubW / BinX;
441 uint32_t RawOffset = 0;
442
443 for (uint32_t i = 0; i < SubH; i++)
444 {
445 uint32_t BinOffsetH = (((i / BinY) & 0xFFFFFFFE) + (i & 0x00000001)) * BinW;
446 for (uint32_t j = 0; j < SubW; j++)
447 {
448 BinFrameOffset = BinOffsetH + ((j / BinX) & 0xFFFFFFFE) + (j & 0x00000001);
449 val = BinFrame16[BinFrameOffset];
450 val += RawFrame16[RawOffset];
451 if(val > UINT16_MAX)
452 val = UINT16_MAX;
453 BinFrame16[BinFrameOffset] = (uint16_t)val;
454 RawOffset++;
455 }
456 }
457
458 }
459 break;
460
461 default:
462 return;
463 }
464
465 // Swap frame pointers
466 uint8_t *rawFramePointer = RawFrame;
467 RawFrame = BinFrame;
468 // We just memset it next time we use it
469 BinFrame = rawFramePointer;
470}
471
472}
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.
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
int getBPP() const
getBPP Get CCD Chip depth (bits per pixel).
const char * getExposureStartTime()
getExposureStartTime
void closeFITSFile()
closeFITSFile Close the in-memory FITS File.
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.
void setImageExtension(const char *ext)
setImageExtension Set image extension
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
void setNAxis(int value)
setNAxis Set FITS number of axis
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
const char * getName() const
bool isNameMatch(const char *otherName) const
double max(void)
#define min(a, b)
#define MAXINDIBLOBFMT
Definition indiapi.h:194
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_OK
Definition indiapi.h:160
void IDLog(const char *fmt,...)
Definition indicom.c:305
Interface to the reference INDI C API device implementation on the Device Driver side.
Namespace to encapsulate INDI client, drivers, and mediator classes.
uint16_t type
Definition pwc-ioctl.h:0
void IDSharedBlobFree(void *ptr)
Free a buffer allocated using IDSharedBlobAlloc. Fall back to free for buffer that are not shared blo...
Definition sharedblob.c:139
void * IDSharedBlobRealloc(void *ptr, size_t size)
Adjust the size of a buffer obtained using IDSharedBlobAlloc.
Definition sharedblob.c:186
void * IDSharedBlobAlloc(size_t size)
Allocate a buffer suitable for fast exchange over local links. Warning : the buffer will be sealed (r...
Definition sharedblob.c:73