Instrument Neutral Distributed Interface INDI  0.9.6
indiccd.cpp
1 /*******************************************************************************
2  Copyright(c) 2010, 2011 Gerry Rozema, 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 
19 #include "indiccd.h"
20 
21 #include <string.h>
22 #include <time.h>
23 #include <sys/time.h>
24 #include <zlib.h>
25 
26 #include <fitsio.h>
27 
28 const char *IMAGE_SETTINGS_TAB = "Image Settings";
29 const char *IMAGE_INFO_TAB = "Image Info";
30 const char *GUIDE_HEAD_TAB = "Guide Head";
31 const char *GUIDE_CONTROL_TAB = "Guider Control";
32 
33 CCDChip::CCDChip()
34 {
35  SendCompressed=false;
36  Interlaced=false;
37 
38  RawFrame=NULL;
39  RawFrameSize=0;
40 
41  BinX = BinY = 1;
42 
43  FrameType=LIGHT_FRAME;
44 
45  ImageFrameNP = new INumberVectorProperty;
46  AbortExposureSP = new ISwitchVectorProperty;
47  FrameTypeSP = new ISwitchVectorProperty;
48  ImageExposureNP = new INumberVectorProperty;
49  ImageBinNP = new INumberVectorProperty;
50  ImagePixelSizeNP = new INumberVectorProperty;
51  CompressSP = new ISwitchVectorProperty;
52  FitsBP = new IBLOBVectorProperty;
53 
54 }
55 
56 CCDChip::~CCDChip()
57 {
58  delete RawFrame;
59  RawFrameSize=0;
60  RawFrame=NULL;
61 
62  delete ImageFrameNP;
63  delete AbortExposureSP;
64  delete FrameTypeSP;
65  delete ImageExposureNP;
66  delete ImageBinNP;
67  delete ImagePixelSizeNP;
68  delete CompressSP;
69  delete FitsBP;
70 }
71 
72 int CCDChip::setFrameType(CCD_FRAME t)
73 {
74  //fprintf(stderr,"Set frame type to %d\n",t);
75  FrameType=t;
76  return 0;
77 }
78 
79 void CCDChip::setResolutoin(int x, int y)
80 {
81  XRes = x;
82  YRes = y;
83 
84  ImagePixelSizeN[0].value=x;
85  ImagePixelSizeN[1].value=y;
86 
87  IDSetNumber(ImagePixelSizeNP, NULL);
88 
89  ImageFrameN[FRAME_W].max = x;
90  ImageFrameN[FRAME_H].max = y;
91  IUUpdateMinMax(ImageFrameNP);
92 }
93 
94 void CCDChip::setFrame(int subx, int suby, int subw, int subh)
95 {
96  SubX = subx;
97  SubY = suby;
98  SubW = subw;
99  SubH = subh;
100 
101  ImageFrameN[FRAME_X].value = SubX;
102  ImageFrameN[FRAME_Y].value = SubY;
103  ImageFrameN[FRAME_W].value = SubW;
104  ImageFrameN[FRAME_H].value = SubH;
105 
106  IDSetNumber(ImageFrameNP, NULL);
107 }
108 
109 void CCDChip::setBin(int hor, int ver)
110 {
111  BinX = hor;
112  BinY = ver;
113 
114  ImageBinN[BIN_W].value = BinX;
115  ImageBinN[BIN_H].value = BinY;
116 
117  IDSetNumber(ImageBinNP, NULL);
118 }
119 
120 void CCDChip::setPixelSize(float x, float y)
121 {
122  PixelSizex = x;
123  PixelSizey = y;
124 
125  ImagePixelSizeN[2].value=x;
126  ImagePixelSizeN[3].value=x;
127  ImagePixelSizeN[4].value=y;
128 
129  IDSetNumber(ImagePixelSizeNP, NULL);
130 
131 }
132 
133 void CCDChip::setBPP(int bbp)
134 {
135  BPP = bbp;
136 
137  ImagePixelSizeN[5].value = BPP;
138 
139  IDSetNumber(ImagePixelSizeNP, NULL);
140 }
141 
142 void CCDChip::setFrameBufferSize(int nbuf)
143 {
144  if (nbuf == RawFrameSize)
145  return;
146 
147  if (RawFrame != NULL)
148  delete RawFrame;
149 
150 
151  RawFrameSize = nbuf;
152 
153  RawFrame = new char[nbuf];
154 }
155 
156 void CCDChip::setExposureLeft(double duration)
157 {
158  ImageExposureN[0].value = duration;
159 
160  IDSetNumber(ImageExposureNP, NULL);
161 }
162 
163 void CCDChip::setExposureDuration(double duration)
164 {
165  exposureDuration = duration;
166  gettimeofday(&startExposureTime,NULL);
167 }
168 
169 const char * CCDChip::getExposureStartTime()
170 {
171  static char ts[32];
172  struct tm *tp;
173  time_t t = (time_t) startExposureTime.tv_sec;
174 
175  //time (&t);
176  tp = gmtime (&t);
177  strftime (ts, sizeof(ts), "%Y-%m-%dT%H:%M:%S", tp);
178  return (ts);
179 }
180 
181 void CCDChip::setInterlaced(bool intr)
182 {
183  Interlaced = intr;
184 }
185 
186 void CCDChip::setExposureFailed()
187 {
188  ImageExposureNP->s = IPS_ALERT;
189  IDSetNumber(ImageExposureNP, NULL);
190 }
191 
192 INDI::CCD::CCD()
193 {
194  //ctor
195  HasGuideHead=false;
196  HasSt4Port=false;
197  InExposure=false;
198  ActiveDeviceTP = new ITextVectorProperty;
199 }
200 
201 INDI::CCD::~CCD()
202 {
203  delete ActiveDeviceTP;
204 }
205 
206 bool INDI::CCD::initProperties()
207 {
208  DefaultDevice::initProperties(); // let the base class flesh in what it wants
209 
210  // PRIMARY CCD Init
211 
212  IUFillNumber(&PrimaryCCD.ImageFrameN[0],"X","Left ","%4.0f",0,1392.0,0,0);
213  IUFillNumber(&PrimaryCCD.ImageFrameN[1],"Y","Top","%4.0f",0,1040,0,0);
214  IUFillNumber(&PrimaryCCD.ImageFrameN[2],"WIDTH","Width","%4.0f",0,1392.0,0,1392.0);
215  IUFillNumber(&PrimaryCCD.ImageFrameN[3],"HEIGHT","Height","%4.0f",0,1040,0,1040);
216  IUFillNumberVector(PrimaryCCD.ImageFrameNP,PrimaryCCD.ImageFrameN,4,getDeviceName(),"CCD_FRAME","Frame",IMAGE_SETTINGS_TAB,IP_RW,60,IPS_IDLE);
217 
218  IUFillSwitch(&PrimaryCCD.FrameTypeS[0],"FRAME_LIGHT","Light",ISS_ON);
219  IUFillSwitch(&PrimaryCCD.FrameTypeS[1],"FRAME_BIAS","Bias",ISS_OFF);
220  IUFillSwitch(&PrimaryCCD.FrameTypeS[2],"FRAME_DARK","Dark",ISS_OFF);
221  IUFillSwitch(&PrimaryCCD.FrameTypeS[3],"FRAME_FLAT","Flat",ISS_OFF);
222  IUFillSwitchVector(PrimaryCCD.FrameTypeSP,PrimaryCCD.FrameTypeS,4,getDeviceName(),"CCD_FRAME_TYPE","FrameType",IMAGE_SETTINGS_TAB,IP_RW,ISR_1OFMANY,60,IPS_IDLE);
223 
224  IUFillNumber(&PrimaryCCD.ImageExposureN[0],"CCD_EXPOSURE_VALUE","Duration (s)","%5.2f",0,36000,0,1.0);
225  IUFillNumberVector(PrimaryCCD.ImageExposureNP,PrimaryCCD.ImageExposureN,1,getDeviceName(),"CCD_EXPOSURE","Expose",MAIN_CONTROL_TAB,IP_RW,60,IPS_IDLE);
226 
227  IUFillSwitch(&PrimaryCCD.AbortExposureS[0],"ABORT","Abort",ISS_OFF);
228  IUFillSwitchVector(PrimaryCCD.AbortExposureSP,PrimaryCCD.AbortExposureS,1,getDeviceName(),"CCD_ABORT_EXPOSURE","Expose Abort",MAIN_CONTROL_TAB,IP_RW,ISR_1OFMANY,60,IPS_IDLE);
229 
230  IUFillNumber(&PrimaryCCD.ImageBinN[0],"HOR_BIN","X","%2.0f",1,4,1,1);
231  IUFillNumber(&PrimaryCCD.ImageBinN[1],"VER_BIN","Y","%2.0f",1,4,1,1);
232  IUFillNumberVector(PrimaryCCD.ImageBinNP,PrimaryCCD.ImageBinN,2,getDeviceName(),"CCD_BINNING","Binning",IMAGE_SETTINGS_TAB,IP_RW,60,IPS_IDLE);
233 
234  IUFillNumber(&PrimaryCCD.ImagePixelSizeN[0],"CCD_MAX_X","Resolution x","%4.0f",1,40,0,6.45);
235  IUFillNumber(&PrimaryCCD.ImagePixelSizeN[1],"CCD_MAX_Y","Resolution y","%4.0f",1,40,0,6.45);
236  IUFillNumber(&PrimaryCCD.ImagePixelSizeN[2],"CCD_PIXEL_SIZE","Pixel size (um)","%5.2f",1,40,0,6.45);
237  IUFillNumber(&PrimaryCCD.ImagePixelSizeN[3],"CCD_PIXEL_SIZE_X","Pixel size X","%5.2f",1,40,0,6.45);
238  IUFillNumber(&PrimaryCCD.ImagePixelSizeN[4],"CCD_PIXEL_SIZE_Y","Pixel size Y","%5.2f",1,40,0,6.45);
239  IUFillNumber(&PrimaryCCD.ImagePixelSizeN[5],"CCD_BITSPERPIXEL","Bits per pixel","%3.0f",1,40,0,6.45);
240  IUFillNumberVector(PrimaryCCD.ImagePixelSizeNP,PrimaryCCD.ImagePixelSizeN,6,getDeviceName(),"CCD_INFO","CCD Information",IMAGE_INFO_TAB,IP_RO,60,IPS_IDLE);
241 
242  IUFillSwitch(&PrimaryCCD.CompressS[0],"COMPRESS","Compress",ISS_OFF);
243  IUFillSwitch(&PrimaryCCD.CompressS[1],"RAW","Raw",ISS_ON);
244  IUFillSwitchVector(PrimaryCCD.CompressSP,PrimaryCCD.CompressS,2,getDeviceName(),"COMPRESSION","Image",IMAGE_SETTINGS_TAB,IP_RW,ISR_1OFMANY,60,IPS_IDLE);
245 
246  IUFillBLOB(&PrimaryCCD.FitsB,"CCD1","Image","");
247  IUFillBLOBVector(PrimaryCCD.FitsBP,&PrimaryCCD.FitsB,1,getDeviceName(),"CCD1","Image Data",OPTIONS_TAB,IP_RO,60,IPS_IDLE);
248 
249  // GUIDER CCD Init
250 
251  IUFillNumber(&GuideCCD.ImageFrameN[0],"X","Left ","%4.0f",0,1392.0,0,0);
252  IUFillNumber(&GuideCCD.ImageFrameN[1],"Y","Top","%4.0f",0,1040,0,0);
253  IUFillNumber(&GuideCCD.ImageFrameN[2],"WIDTH","Width","%4.0f",0,1392.0,0,1392.0);
254  IUFillNumber(&GuideCCD.ImageFrameN[3],"HEIGHT","Height","%4.0f",0,1040,0,1040);
255  IUFillNumberVector(GuideCCD.ImageFrameNP,GuideCCD.ImageFrameN,4,getDeviceName(),"GUIDE_FRAME","Frame",GUIDE_HEAD_TAB,IP_RW,60,IPS_IDLE);
256 
257  IUFillNumber(&GuideCCD.ImagePixelSizeN[0],"CCD_MAX_X","Resolution x","%4.0f",1,40,0,6.45);
258  IUFillNumber(&GuideCCD.ImagePixelSizeN[1],"CCD_MAX_Y","Resolution y","%4.0f",1,40,0,6.45);
259  IUFillNumber(&GuideCCD.ImagePixelSizeN[2],"CCD_PIXEL_SIZE","Pixel size (um)","%5.2f",1,40,0,6.45);
260  IUFillNumber(&GuideCCD.ImagePixelSizeN[3],"CCD_PIXEL_SIZE_X","Pixel size X","%5.2f",1,40,0,6.45);
261  IUFillNumber(&GuideCCD.ImagePixelSizeN[4],"CCD_PIXEL_SIZE_Y","Pixel size Y","%5.2f",1,40,0,6.45);
262  IUFillNumber(&GuideCCD.ImagePixelSizeN[5],"CCD_BITSPERPIXEL","Bits per pixel","%3.0f",1,40,0,6.45);
263  IUFillNumberVector(GuideCCD.ImagePixelSizeNP,GuideCCD.ImagePixelSizeN,6,getDeviceName(),"GUIDE_INFO",GUIDE_HEAD_TAB,GUIDE_HEAD_TAB,IP_RO,60,IPS_IDLE);
264 
265  IUFillNumber(&GuideCCD.ImageExposureN[0],"GUIDER_EXPOSURE_VALUE","Duration (s)","%5.2f",0,36000,0,1.0);
266  IUFillNumberVector(GuideCCD.ImageExposureNP,GuideCCD.ImageExposureN,1,getDeviceName(),"GUIDER_EXPOSURE","Guide",MAIN_CONTROL_TAB,IP_RW,60,IPS_IDLE);
267 
268  IUFillSwitch(&GuideCCD.AbortExposureS[0],"ABORT","Abort",ISS_OFF);
269  IUFillSwitchVector(GuideCCD.AbortExposureSP,GuideCCD.AbortExposureS,1,getDeviceName(),"GUIDER_ABORT_EXPOSURE","Guide Abort",MAIN_CONTROL_TAB,IP_RW,ISR_1OFMANY,60,IPS_IDLE);
270 
271  IUFillSwitch(&GuideCCD.CompressS[0],"GCOMPRESS","Compress",ISS_OFF);
272  IUFillSwitch(&GuideCCD.CompressS[1],"GRAW","Raw",ISS_ON);
273  IUFillSwitchVector(GuideCCD.CompressSP,GuideCCD.CompressS,2,getDeviceName(),"GCOMPRESSION","Image",GUIDE_HEAD_TAB,IP_RW,ISR_1OFMANY,60,IPS_IDLE);
274 
275  IUFillBLOB(&GuideCCD.FitsB,"CCD2","Guider Image","");
276  IUFillBLOBVector(GuideCCD.FitsBP,&GuideCCD.FitsB,1,getDeviceName(),"CCD2","Image Data",OPTIONS_TAB,IP_RO,60,IPS_IDLE);
277 
278  // CCD Class Init
279 
280  IUFillText(&ActiveDeviceT[0],"ACTIVE_TELESCOPE","Telescope","Telescope Simulator");
281  IUFillText(&ActiveDeviceT[1],"ACTIVE_FOCUSER","Focuser","Focuser Simulator");
282  IUFillTextVector(ActiveDeviceTP,ActiveDeviceT,2,getDeviceName(),"ACTIVE_DEVICES","Snoop devices",OPTIONS_TAB,IP_RW,60,IPS_IDLE);
283 
284  IUFillNumber(&EqN[0],"RA","Ra (hh:mm:ss)","%010.6m",0,24,0,0);
285  IUFillNumber(&EqN[1],"DEC","Dec (dd:mm:ss)","%010.6m",-90,90,0,0);
286  IUFillNumberVector(&EqNP,EqN,2,ActiveDeviceT[0].text,"EQUATORIAL_COORD","EQ Coord","Main Control",IP_RW,60,IPS_IDLE);
287 
288  IDSnoopDevice(ActiveDeviceT[0].text,"EQUATORIAL_COORD");
289  IDSnoopDevice(ActiveDeviceT[0].text,"TELESCOPE_INFO");
290  IDSnoopDevice(ActiveDeviceT[1].text,"FWHM");
291 
292 
293  // Guider Interface
294  initGuiderProperties(getDeviceName(), GUIDE_CONTROL_TAB);
295 
296  return true;
297 }
298 
299 void INDI::CCD::ISGetProperties (const char *dev)
300 {
301  // First we let our parent populate
302 
303  //IDLog("INDI::CCD IsGetProperties with %s\n",dev);
304 
306 
307  return;
308 }
309 
310 bool INDI::CCD::updateProperties()
311 {
312  //IDLog("INDI::CCD UpdateProperties isConnected returns %d %d\n",isConnected(),Connected);
313  if(isConnected())
314  {
315  defineNumber(PrimaryCCD.ImageExposureNP);
316  defineSwitch(PrimaryCCD.AbortExposureSP);
317  defineNumber(PrimaryCCD.ImageFrameNP);
318  defineNumber(PrimaryCCD.ImageBinNP);
319 
320  if(HasGuideHead)
321  {
322  // IDLog("Sending Guider Stuff\n");
323  defineNumber(GuideCCD.ImageExposureNP);
324  defineSwitch(GuideCCD.AbortExposureSP);
325  defineNumber(GuideCCD.ImageFrameNP);
326  }
327 
328  defineNumber(PrimaryCCD.ImagePixelSizeNP);
329  if(HasGuideHead)
330  {
331  defineNumber(GuideCCD.ImagePixelSizeNP);
332  }
333  defineSwitch(PrimaryCCD.CompressSP);
334  defineBLOB(PrimaryCCD.FitsBP);
335  if(HasGuideHead)
336  {
337  defineSwitch(GuideCCD.CompressSP);
338  defineBLOB(GuideCCD.FitsBP);
339  }
340  if(HasSt4Port)
341  {
342  defineNumber(&GuideNSP);
343  defineNumber(&GuideEWP);
344  }
345  defineSwitch(PrimaryCCD.FrameTypeSP);
346  defineText(ActiveDeviceTP);
347  }
348  else
349  {
350  deleteProperty(PrimaryCCD.ImageFrameNP->name);
351  deleteProperty(PrimaryCCD.ImageBinNP->name);
352  deleteProperty(PrimaryCCD.ImagePixelSizeNP->name);
353  deleteProperty(PrimaryCCD.ImageExposureNP->name);
354  deleteProperty(PrimaryCCD.AbortExposureSP->name);
355  deleteProperty(PrimaryCCD.FitsBP->name);
356  deleteProperty(PrimaryCCD.CompressSP->name);
357  if(HasGuideHead)
358  {
359  deleteProperty(GuideCCD.ImageExposureNP->name);
360  deleteProperty(GuideCCD.AbortExposureSP->name);
361  deleteProperty(GuideCCD.ImageFrameNP->name);
362  deleteProperty(GuideCCD.ImagePixelSizeNP->name);
363  deleteProperty(GuideCCD.FitsBP->name);
364  deleteProperty(GuideCCD.CompressSP->name);
365  }
366  if(HasSt4Port)
367  {
368  deleteProperty(GuideNSP.name);
369  deleteProperty(GuideEWP.name);
370 
371  }
372  deleteProperty(PrimaryCCD.FrameTypeSP->name);
373  deleteProperty(ActiveDeviceTP->name);
374  }
375  return true;
376 }
377 
378 bool INDI::CCD::ISSnoopDevice (XMLEle *root)
379  {
380  //fprintf(stderr," ################# CCDSim handling snoop ##############\n");
381  if(IUSnoopNumber(root,&EqNP)==0)
382  {
383  float newra,newdec;
384  newra=EqN[0].value;
385  newdec=EqN[1].value;
386  if((newra != RA)||(newdec != Dec))
387  {
388  //fprintf(stderr,"RA %4.2f Dec %4.2f Snooped RA %4.2f Dec %4.2f\n",RA,Dec,newra,newdec);
389  RA=newra;
390  Dec=newdec;
391 
392  }
393  }
394  else
395  {
396  //fprintf(stderr,"Snoop Failed\n");
397  }
398 
399  return true;
400  }
401 
402 bool INDI::CCD::ISNewText (const char *dev, const char *name, char *texts[], char *names[], int n)
403 {
404  // Ok, lets see if this is a property wer process
405  //IDLog("IndiTelescope got %d new text items name %s\n",n,name);
406  // first check if it's for our device
407  if(strcmp(dev,getDeviceName())==0)
408  {
409  // This is for our device
410  // Now lets see if it's something we process here
411  if(strcmp(name,ActiveDeviceTP->name)==0)
412  {
413  int rc;
414  //IDLog("calling update text\n");
415  ActiveDeviceTP->s=IPS_OK;
416  rc=IUUpdateText(ActiveDeviceTP,texts,names,n);
417  //IDLog("update text returns %d\n",rc);
418  // Update client display
419  IDSetText(ActiveDeviceTP,NULL);
420  saveConfig();
421  IUFillNumberVector(&EqNP,EqN,2,ActiveDeviceT[0].text,"EQUATORIAL_PEC","EQ PEC",MAIN_CONTROL_TAB,IP_RW,60,IPS_IDLE);
422 
423  IDSnoopDevice(ActiveDeviceT[0].text,"EQUATORIAL_PEC");
424  IDSnoopDevice(ActiveDeviceT[0].text,"TELESCOPE_INFO");
425  IDSnoopDevice(ActiveDeviceT[1].text,"FWHM");
426  // We processed this one, so, tell the world we did it
427  return true;
428  }
429 
430  }
431 
432  return INDI::DefaultDevice::ISNewText(dev,name,texts,names,n);
433 }
434 
435 bool INDI::CCD::ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n)
436 {
437  // first check if it's for our device
438  //IDLog("INDI::CCD::ISNewNumber %s\n",name);
439  if(strcmp(dev,getDeviceName())==0)
440  {
441  // This is for our device
442  // Now lets see if it's something we process here
443  if(strcmp(name,"CCD_EXPOSURE")==0)
444  {
445  float n;
446  int rc;
447 
448  n=values[0];
449 
450  PrimaryCCD.ImageExposureN[0].value=n;
451 
452  if (PrimaryCCD.ImageExposureNP->s==IPS_BUSY)
453  AbortExposure();
454 
455  PrimaryCCD.ImageExposureNP->s=IPS_BUSY;
456  // now we have a new number, this is our requested exposure time
457  // Tell the clients we are busy with this exposure
458 
459  // And now call the physical hardware layer to start the exposure
460  rc=StartExposure(n);
461  switch(rc)
462  {
463  case 0: // normal case, exposure running on timers, callbacks when it's finished
464  PrimaryCCD.ImageExposureNP->s=IPS_BUSY;
465  break;
466  case 1: // Short exposure, it's already done
467  PrimaryCCD.ImageExposureNP->s=IPS_OK;
468  break;
469  case -1: // error condition
470  PrimaryCCD.ImageExposureNP->s=IPS_ALERT;
471  break;
472  }
473  IDSetNumber(PrimaryCCD.ImageExposureNP,NULL);
474  return true;
475  }
476 
477  if(strcmp(name,"GUIDER_EXPOSURE")==0)
478  {
479  float n;
480  int rc;
481 
482  n=values[0];
483 
484  GuideCCD.ImageExposureN[0].value=n;
485  GuideCCD.ImageExposureNP->s=IPS_BUSY;
486  // now we have a new number, this is our requested exposure time
487  // Tell the clients we are busy with this exposure
488 
489  // And now call the physical hardware layer to start the exposure
490  // change of plans, this is just changing exposure time
491  // the start/stop stream buttons do the rest
492  rc=StartGuideExposure(n);
493  //rc=1; // set it to ok
494  switch(rc) {
495  case 0: // normal case, exposure running on timers, callbacks when it's finished
496  GuideCCD.ImageExposureNP->s=IPS_BUSY;
497  break;
498  case 1: // Short exposure, it's already done
499  GuideCCD.ImageExposureNP->s=IPS_OK;
500  break;
501  case -1: // error condition
502  GuideCCD.ImageExposureNP->s=IPS_ALERT;
503  break;
504  }
505  IDSetNumber(GuideCCD.ImageExposureNP,NULL);
506  return true;
507  }
508 
509  if(strcmp(name,"CCD_BINNING")==0)
510  {
511  // We are being asked to set camera binning
512  PrimaryCCD.ImageBinNP->s=IPS_OK;
513  IUUpdateNumber(PrimaryCCD.ImageBinNP,values,names,n);
514 
515 
516  if (updateCCDBin(PrimaryCCD.ImageBinN[0].value, PrimaryCCD.ImageBinN[1].value) == false)
517  {
518  PrimaryCCD.ImageBinNP->s = IPS_ALERT;
519  IDSetNumber (PrimaryCCD.ImageBinNP, NULL);
520  }
521 
522  return true;
523 
524  }
525 
526  if(strcmp(name,"CCD_FRAME")==0)
527  {
528  // We are being asked to set camera binning
529  PrimaryCCD.ImageFrameNP->s=IPS_OK;
530  IUUpdateNumber(PrimaryCCD.ImageFrameNP,values,names,n);
531 
532  if (updateCCDFrame(PrimaryCCD.ImageFrameN[0].value, PrimaryCCD.ImageFrameN[1].value, PrimaryCCD.ImageFrameN[2].value,
533  PrimaryCCD.ImageFrameN[3].value) == false)
534  {
535  PrimaryCCD.ImageFrameNP->s = IPS_ALERT;
536  IDSetNumber(PrimaryCCD.ImageFrameNP, NULL);
537 
538  }
539  return true;
540  }
541 
542  if(strcmp(name,"GUIDER_FRAME")==0)
543  {
544  // We are being asked to set camera binning
545  GuideCCD.ImageFrameNP->s=IPS_OK;
546  IUUpdateNumber(GuideCCD.ImageFrameNP,values,names,n);
547  // Update client display
548  //IDSetNumber(GuiderFrameNP,NULL);
549 
550  if (isDebug())
551  IDLog("GuiderFrame set to %4.0f,%4.0f %4.0f x %4.0f\n",
552  GuideCCD.ImageFrameN[0].value,GuideCCD.ImageFrameN[1].value,GuideCCD.ImageFrameN[2].value,GuideCCD.ImageFrameN[3].value);
553  //GSubX=GuiderFrameN[0].value;
554  //GSubY=GuiderFrameN[1].value;
555  //GSubW=GuiderFrameN[2].value;
556  //GSubH=GuiderFrameN[3].value;
557  GuideCCD.setFrame(GuideCCD.ImageFrameN[0].value, GuideCCD.ImageFrameN[1].value,
558  GuideCCD.ImageFrameN[2].value,GuideCCD.ImageFrameN[3].value);
559 
560  return true;
561  }
562 
563  if (!strcmp(name,GuideNSP.name) || !strcmp(name,GuideEWP.name))
564  {
565  processGuiderProperties(name, values, names, n);
566  return true;
567  }
568 
569  }
570  // if we didn't process it, continue up the chain, let somebody else
571  // give it a shot
572  return DefaultDevice::ISNewNumber(dev,name,values,names,n);
573 }
574 
575 bool INDI::CCD::ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n)
576 {
577 
578  if(strcmp(dev,getDeviceName())==0)
579  {
580 
581 
582  if(strcmp(name,PrimaryCCD.AbortExposureSP->name)==0)
583  {
584  IUResetSwitch(PrimaryCCD.AbortExposureSP);
585 
586  if (AbortExposure())
587  {
588  PrimaryCCD.AbortExposureSP->s = IPS_OK;
589  PrimaryCCD.ImageExposureNP->s = IPS_IDLE;
590  PrimaryCCD.ImageExposureN[0].value = 0;
591  }
592  else
593  {
594  PrimaryCCD.AbortExposureSP->s = IPS_ALERT;
595  PrimaryCCD.ImageExposureNP->s = IPS_ALERT;
596  }
597 
598  IDSetSwitch(PrimaryCCD.AbortExposureSP, NULL);
599  IDSetNumber(PrimaryCCD.ImageExposureNP, NULL);
600 
601  return true;
602  }
603 
604 
605  if(strcmp(name,GuideCCD.AbortExposureSP->name)==0)
606  {
607  IUResetSwitch(GuideCCD.AbortExposureSP);
608 
609  if (AbortGuideExposure())
610  {
611  GuideCCD.AbortExposureSP->s = IPS_OK;
612  GuideCCD.ImageExposureNP->s = IPS_IDLE;
613  GuideCCD.ImageExposureN[0].value = 0;
614  }
615  else
616  {
617  GuideCCD.AbortExposureSP->s = IPS_ALERT;
618  GuideCCD.ImageExposureNP->s = IPS_ALERT;
619  }
620 
621  IDSetSwitch(GuideCCD.AbortExposureSP, NULL);
622  IDSetNumber(GuideCCD.ImageExposureNP, NULL);
623 
624  return true;
625  }
626 
627 
628  if(strcmp(name,PrimaryCCD.CompressSP->name)==0)
629  {
630 
631  IUUpdateSwitch(PrimaryCCD.CompressSP,states,names,n);
632  IDSetSwitch(PrimaryCCD.CompressSP,NULL);
633 
634  if(PrimaryCCD.CompressS[0].s==ISS_ON )
635  {
636  PrimaryCCD.SendCompressed=true;
637  } else
638  {
639  PrimaryCCD.SendCompressed=false;
640  }
641  return true;
642  }
643 
644  if(strcmp(name,GuideCCD.CompressSP->name)==0)
645  {
646 
647  IUUpdateSwitch(GuideCCD.CompressSP,states,names,n);
648  IDSetSwitch(GuideCCD.CompressSP,NULL);
649 
650  if(GuideCCD.CompressS[0].s==ISS_ON )
651  {
652  GuideCCD.SendCompressed=true;
653  } else
654  {
655  GuideCCD.SendCompressed=false;
656  }
657  return true;
658  }
659 
660  if(strcmp(name,PrimaryCCD.FrameTypeSP->name)==0)
661  {
662  // Compression Update
663  IUUpdateSwitch(PrimaryCCD.FrameTypeSP,states,names,n);
664  PrimaryCCD.FrameTypeSP->s=IPS_OK;
665  if(PrimaryCCD.FrameTypeS[0].s==ISS_ON) PrimaryCCD.setFrameType(CCDChip::LIGHT_FRAME);
666  else if(PrimaryCCD.FrameTypeS[1].s==ISS_ON) PrimaryCCD.setFrameType(CCDChip::BIAS_FRAME);
667  else if(PrimaryCCD.FrameTypeS[2].s==ISS_ON) PrimaryCCD.setFrameType(CCDChip::DARK_FRAME);
668  else if(PrimaryCCD.FrameTypeS[3].s==ISS_ON) PrimaryCCD.setFrameType(CCDChip::FLAT_FRAME);
669 
670  if (updateCCDFrameType(PrimaryCCD.getFrameType()) == false)
671  PrimaryCCD.FrameTypeSP->s = IPS_ALERT;
672 
673  IDSetSwitch(PrimaryCCD.FrameTypeSP,NULL);
674 
675  return true;
676  }
677  }
678 
679 
680  // let the default driver have a crack at it
681  return DefaultDevice::ISNewSwitch(dev, name, states, names, n);
682 }
683 
684 
685 int INDI::CCD::StartExposure(float duration)
686 {
687  IDLog("INDI::CCD::StartExposure %4.2f - Should never get here\n",duration);
688  return -1;
689 }
690 
691 int INDI::CCD::StartGuideExposure(float duration)
692 {
693  IDLog("INDI::CCD::StartGuide Exposure %4.2f - Should never get here\n",duration);
694  return -1;
695 }
696 
698 {
699  IDLog("INDI::CCD::AbortExposure - Should never get here\n");
700  return false;
701 }
702 
703 bool INDI::CCD::updateCCDFrame(int x, int y, int w, int h)
704 {
705  // Just set value, unless HW layer overrides this and performs its own processing
706  PrimaryCCD.setFrame(x, y, w, h);
707  return true;
708 }
709 
710 bool INDI::CCD::updateCCDBin(int hor, int ver)
711 {
712  // Just set value, unless HW layer overrides this and performs its own processing
713  PrimaryCCD.setBin(hor, ver);
714  return true;
715 }
716 
717 bool INDI::CCD::updateCCDFrameType(CCDChip::CCD_FRAME fType)
718 {
719  INDI_UNUSED(fType);
720  // Child classes can override this
721  return true;
722 }
723 
724 void INDI::CCD::addFITSKeywords(fitsfile *fptr, CCDChip *targetChip)
725 {
726  int status=0;
727  char frame_s[32];
728  char dev_name[32];
729  char exp_start[32];
730  double min_val, max_val;
731  double exposureDuration;
732  double pixSize1,pixSize2;
733  unsigned int xbin, ybin;
734 
735  getMinMax(&min_val, &max_val, targetChip);
736 
737  xbin = targetChip->getBinX();
738  ybin = targetChip->getBinY();
739 
740 
741  switch (targetChip->getFrameType())
742  {
743  case CCDChip::LIGHT_FRAME:
744  strcpy(frame_s, "Light");
745  break;
746  case CCDChip::BIAS_FRAME:
747  strcpy(frame_s, "Bias");
748  break;
749  case CCDChip::FLAT_FRAME:
750  strcpy(frame_s, "Flat Field");
751  break;
752  case CCDChip::DARK_FRAME:
753  strcpy(frame_s, "Dark");
754  break;
755  }
756 
757  exposureDuration = targetChip->getExposureDuration();
758 
759  pixSize1 = targetChip->getPixelSizeX();
760  pixSize2 = targetChip->getPixelSizeY();
761 
762  strncpy(dev_name, getDeviceName(), 32);
763  strncpy(exp_start, targetChip->getExposureStartTime(), 32);
764 
765  fits_update_key_s(fptr, TDOUBLE, "EXPTIME", &(exposureDuration), "Total Exposure Time (s)", &status);
766 
767  if(targetChip->getFrameType() == CCDChip::DARK_FRAME)
768  fits_update_key_s(fptr, TDOUBLE, "DARKTIME", &(exposureDuration), "Total Exposure Time (s)", &status);
769 
770  fits_update_key_s(fptr, TDOUBLE, "PIXSIZE1", &(pixSize1), "Pixel Size 1 (microns)", &status);
771  fits_update_key_s(fptr, TDOUBLE, "PIXSIZE2", &(pixSize2), "Pixel Size 2 (microns)", &status);
772  fits_update_key_s(fptr, TUINT, "XBINNING", &(xbin) , "Binning factor in width", &status);
773  fits_update_key_s(fptr, TUINT, "YBINNING", &(ybin), "Binning factor in height", &status);
774  fits_update_key_s(fptr, TSTRING, "FRAME", frame_s, "Frame Type", &status);
775  fits_update_key_s(fptr, TDOUBLE, "DATAMIN", &min_val, "Minimum value", &status);
776  fits_update_key_s(fptr, TDOUBLE, "DATAMAX", &max_val, "Maximum value", &status);
777  fits_update_key_s(fptr, TSTRING, "INSTRUME", dev_name, "CCD Name", &status);
778  fits_update_key_s(fptr, TSTRING, "DATE-OBS", exp_start, "UTC start date of observation", &status);
779 
780  //fits_write_date(fptr, &status);
781 }
782 
783 void INDI::CCD::fits_update_key_s(fitsfile* fptr, int type, std::string name, void* p, std::string explanation, int* status)
784 {
785  // this function is for removing warnings about deprecated string conversion to char* (from arg 5)
786  fits_update_key(fptr,type,name.c_str(),p, const_cast<char*>(explanation.c_str()), status);
787 }
788 
789 bool INDI::CCD::ExposureComplete(CCDChip *targetChip)
790 {
791  void *memptr;
792  size_t memsize;
793  int img_type=0;
794  int byte_type=0;
795  int status=0;
796  long naxes[2];
797  long naxis=2;
798  int nelements=0;
799 
800  fitsfile *fptr=NULL;
801 
802  naxes[0]=targetChip->getSubW()/targetChip->getBinX();
803  naxes[1]=targetChip->getSubH()/targetChip->getBinY();
804 
805  switch (targetChip->getBPP())
806  {
807  case 8:
808  byte_type = TBYTE;
809  img_type = BYTE_IMG;
810  break;
811 
812  case 16:
813  byte_type = TUSHORT;
814  img_type = USHORT_IMG;
815  break;
816 
817  case 32:
818  byte_type = TULONG;
819  img_type = ULONG_IMG;
820  break;
821 
822  default:
823  IDLog("Unsupported bits per pixel value %d\n", targetChip->getBPP() );
824  return false;
825  break;
826  }
827 
828 
829  nelements = naxes[0] * naxes[1];
830 
831  // Now we have to send fits format data to the client
832  memsize=5760;
833  memptr=malloc(memsize);
834  if(!memptr)
835  {
836  IDLog("Error: failed to allocate memory: %lu\n",(unsigned long)memsize);
837  }
838 
839  fits_create_memfile(&fptr,&memptr,&memsize,2880,realloc,&status);
840 
841  if(status)
842  {
843  IDLog("Error: Failed to create FITS image\n");
844  fits_report_error(stderr, status); /* print out any error messages */
845  return false;
846  }
847 
848  fits_create_img(fptr, img_type , naxis, naxes, &status);
849 
850  if (status)
851  {
852  IDLog("Error: Failed to create FITS image\n");
853  fits_report_error(stderr, status); /* print out any error messages */
854  return false;
855  }
856 
857  addFITSKeywords(fptr, targetChip);
858 
859  fits_write_img(fptr,byte_type,1,nelements,targetChip->getFrameBuffer(),&status);
860 
861  if (status)
862  {
863  IDLog("Error: Failed to write FITS image\n");
864  fits_report_error(stderr, status); /* print out any error messages */
865  return false;
866  }
867 
868  fits_close_file(fptr,&status);
869 
870  targetChip->ImageExposureNP->s=IPS_OK;
871  IDSetNumber(targetChip->ImageExposureNP,NULL);
872 
873 
874  targetChip->FitsB.blob=memptr;
875  targetChip->FitsB.bloblen=memsize;
876  targetChip->FitsB.size=memsize;
877  strcpy(targetChip->FitsB.format,".fits");
878  targetChip->FitsBP->s=IPS_OK;
879  //IDLog("Enter Uploadfile with %d total sending via %s, and format %s\n",total,targetChip->FitsB.name, targetChip->FitsB.format);
880  IDSetBLOB(targetChip->FitsBP,NULL);
881 
882  free(memptr);
883  return true;
884 }
885 
886 void INDI::CCD::SetCCDParams(int x,int y,int bpp,float xf,float yf)
887 {
888  PrimaryCCD.setResolutoin(x, y);
889  PrimaryCCD.setFrame(0, 0, x, y);
890  PrimaryCCD.setBin(1,1);
891  PrimaryCCD.setPixelSize(xf, yf);
892  PrimaryCCD.setBPP(bpp);
893 
894 }
895 
896 void INDI::CCD::SetGuidHeadParams(int x,int y,int bpp,float xf,float yf)
897 {
898  HasGuideHead=true;
899 
900  GuideCCD.setResolutoin(x, y);
901  GuideCCD.setFrame(0, 0, x, y);
902  GuideCCD.setPixelSize(xf, yf);
903  GuideCCD.setBPP(bpp);
904 
905 }
906 
908 {
909  return false;
910 }
911 
912 bool INDI::CCD::saveConfigItems(FILE *fp)
913 {
914  IUSaveConfigText(fp, ActiveDeviceTP);
915 
916  return true;
917 }
918 
919 bool INDI::CCD::GuideNorth(float ms)
920 {
921  return false;
922 }
923 bool INDI::CCD::GuideSouth(float ms)
924 {
925  return false;
926 }
927 bool INDI::CCD::GuideEast(float ms)
928 {
929  return false;
930 }
931 bool INDI::CCD::GuideWest(float ms)
932 {
933  return false;
934 }
935 
936 void INDI::CCD::getMinMax(double *min, double *max, CCDChip *targetChip)
937 {
938  int ind=0, i, j;
939  int imageHeight = targetChip->getSubH() / targetChip->getBinY();
940  int imageWidth = targetChip->getSubW() / targetChip->getBinX();
941  double lmin, lmax;
942 
943  switch (targetChip->getBPP())
944  {
945  case 8:
946  {
947  unsigned char *imageBuffer = (unsigned char *) targetChip->getFrameBuffer();
948  lmin = lmax = imageBuffer[0];
949 
950 
951  for (i= 0; i < imageHeight ; i++)
952  for (j= 0; j < imageWidth; j++)
953  {
954  ind = (i * imageWidth) + j;
955  if (imageBuffer[ind] < lmin) lmin = imageBuffer[ind];
956  else if (imageBuffer[ind] > lmax) lmax = imageBuffer[ind];
957  }
958 
959  }
960  break;
961 
962  case 16:
963  {
964  unsigned short *imageBuffer = (unsigned short* ) targetChip->getFrameBuffer();
965  lmin = lmax = imageBuffer[0];
966 
967  for (i= 0; i < imageHeight ; i++)
968  for (j= 0; j < imageWidth; j++)
969  {
970  ind = (i * imageWidth) + j;
971  if (imageBuffer[ind] < lmin) lmin = imageBuffer[ind];
972  else if (imageBuffer[ind] > lmax) lmax = imageBuffer[ind];
973  }
974 
975  }
976  break;
977 
978  case 32:
979  {
980  unsigned int *imageBuffer = (unsigned int* ) targetChip->getFrameBuffer();
981  lmin = lmax = imageBuffer[0];
982 
983  for (i= 0; i < imageHeight ; i++)
984  for (j= 0; j < imageWidth; j++)
985  {
986  ind = (i * imageWidth) + j;
987  if (imageBuffer[ind] < lmin) lmin = imageBuffer[ind];
988  else if (imageBuffer[ind] > lmax) lmax = imageBuffer[ind];
989 
990  }
991 
992  }
993  break;
994 
995  }
996  *min = lmin;
997  *max = lmax;
998 }
999 
1000