1 | /******************************************************************************* |
---|
2 | Copyright(c) 2010 Gerry Rozema. 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 "ccd_simulator.h" |
---|
19 | |
---|
20 | #include <stdio.h> |
---|
21 | #include <stdlib.h> |
---|
22 | #include <unistd.h> |
---|
23 | #include <math.h> |
---|
24 | #include <string.h> |
---|
25 | |
---|
26 | |
---|
27 | // We declare an auto pointer to ccdsim. |
---|
28 | std::auto_ptr<CCDSim> ccdsim(0); |
---|
29 | |
---|
30 | void ISPoll(void *p); |
---|
31 | |
---|
32 | |
---|
33 | void ISInit() |
---|
34 | { |
---|
35 | static int isInit =0; |
---|
36 | |
---|
37 | if (isInit == 1) |
---|
38 | return; |
---|
39 | |
---|
40 | isInit = 1; |
---|
41 | if(ccdsim.get() == 0) ccdsim.reset(new CCDSim()); |
---|
42 | //IEAddTimer(POLLMS, ISPoll, NULL); |
---|
43 | |
---|
44 | } |
---|
45 | |
---|
46 | void ISGetProperties(const char *dev) |
---|
47 | { |
---|
48 | ISInit(); |
---|
49 | ccdsim->ISGetProperties(dev); |
---|
50 | } |
---|
51 | |
---|
52 | void ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int num) |
---|
53 | { |
---|
54 | ISInit(); |
---|
55 | ccdsim->ISNewSwitch(dev, name, states, names, num); |
---|
56 | } |
---|
57 | |
---|
58 | void ISNewText( const char *dev, const char *name, char *texts[], char *names[], int num) |
---|
59 | { |
---|
60 | ISInit(); |
---|
61 | ccdsim->ISNewText(dev, name, texts, names, num); |
---|
62 | } |
---|
63 | |
---|
64 | void ISNewNumber(const char *dev, const char *name, double values[], char *names[], int num) |
---|
65 | { |
---|
66 | ISInit(); |
---|
67 | ccdsim->ISNewNumber(dev, name, values, names, num); |
---|
68 | } |
---|
69 | |
---|
70 | void ISNewBLOB (const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n) |
---|
71 | { |
---|
72 | INDI_UNUSED(dev); |
---|
73 | INDI_UNUSED(name); |
---|
74 | INDI_UNUSED(sizes); |
---|
75 | INDI_UNUSED(blobsizes); |
---|
76 | INDI_UNUSED(blobs); |
---|
77 | INDI_UNUSED(formats); |
---|
78 | INDI_UNUSED(names); |
---|
79 | INDI_UNUSED(n); |
---|
80 | } |
---|
81 | void ISSnoopDevice (XMLEle *root) |
---|
82 | { |
---|
83 | ISInit(); |
---|
84 | ccdsim->ISSnoopDevice(root); |
---|
85 | } |
---|
86 | |
---|
87 | |
---|
88 | CCDSim::CCDSim() |
---|
89 | { |
---|
90 | //ctor |
---|
91 | testvalue=0; |
---|
92 | AbortGuideFrame=false; |
---|
93 | ShowStarField=true; |
---|
94 | |
---|
95 | HasSt4Port=true; |
---|
96 | HasGuideHead=false; |
---|
97 | |
---|
98 | |
---|
99 | RA=9.95; |
---|
100 | Dec=68.9; |
---|
101 | |
---|
102 | // sxvh9 |
---|
103 | bias=1500; |
---|
104 | maxnoise=20; |
---|
105 | maxval=65000; |
---|
106 | limitingmag=11.5; |
---|
107 | saturationmag=2; |
---|
108 | focallength=1280; // focal length of the telescope in millimeters |
---|
109 | OAGoffset=0; // An oag is offset this much from center of scope position (arcminutes); |
---|
110 | skyglow=40; |
---|
111 | |
---|
112 | seeing=3.5; // fwhm of our stars |
---|
113 | ImageScalex=1.0; // preset with a valid non-zero |
---|
114 | ImageScaley=1.0; |
---|
115 | //focallength=175; // focal length of the telescope in millimeters |
---|
116 | time(&RunStart); |
---|
117 | |
---|
118 | // Our PEPeriod is 8 minutes |
---|
119 | // and we have a 22 arcsecond swing |
---|
120 | PEPeriod=8*60; |
---|
121 | PEMax=11; |
---|
122 | GuideRate=7; // guide rate is 7 arcseconds per second |
---|
123 | TimeFactor=1; |
---|
124 | |
---|
125 | SimulatorSettingsNV = new INumberVectorProperty; |
---|
126 | TimeFactorSV = new ISwitchVectorProperty; |
---|
127 | |
---|
128 | } |
---|
129 | |
---|
130 | bool CCDSim::SetupParms() |
---|
131 | { |
---|
132 | int nbuf; |
---|
133 | SetCCDParams(SimulatorSettingsN[0].value,SimulatorSettingsN[1].value,16,SimulatorSettingsN[2].value,SimulatorSettingsN[3].value); |
---|
134 | // Kwiq |
---|
135 | maxnoise=SimulatorSettingsN[10].value; |
---|
136 | skyglow=SimulatorSettingsN[11].value; |
---|
137 | maxval=SimulatorSettingsN[4].value; |
---|
138 | bias=SimulatorSettingsN[5].value; |
---|
139 | limitingmag=SimulatorSettingsN[7].value; |
---|
140 | saturationmag=SimulatorSettingsN[6].value; |
---|
141 | focallength=SimulatorSettingsN[8].value; // focal length of the telescope in millimeters |
---|
142 | OAGoffset=SimulatorSettingsN[12].value; // An oag is offset this much from center of scope position (arcminutes); |
---|
143 | seeing=SimulatorSettingsN[9].value; // we get real fat stars in this one |
---|
144 | |
---|
145 | nbuf = PrimaryCCD.getXRes() * PrimaryCCD.getYRes() * PrimaryCCD.getBPP(); |
---|
146 | nbuf += 512; |
---|
147 | PrimaryCCD.setFrameBufferSize(nbuf); |
---|
148 | |
---|
149 | return true; |
---|
150 | } |
---|
151 | |
---|
152 | bool CCDSim::Connect() |
---|
153 | { |
---|
154 | |
---|
155 | int nbuf; |
---|
156 | SetupParms(); |
---|
157 | |
---|
158 | if(HasGuideHead) |
---|
159 | { |
---|
160 | SetGuidHeadParams(500,290,8,9.8,12.6); |
---|
161 | nbuf = GuideCCD.getXRes() * GuideCCD.getYRes(); |
---|
162 | GuideCCD.setFrameBufferSize(nbuf); |
---|
163 | } |
---|
164 | |
---|
165 | SetTimer(1000); // start the timer |
---|
166 | return true; |
---|
167 | } |
---|
168 | |
---|
169 | CCDSim::~CCDSim() |
---|
170 | { |
---|
171 | //dtor |
---|
172 | } |
---|
173 | |
---|
174 | const char * CCDSim::getDefaultName() |
---|
175 | { |
---|
176 | return (char *)"CCD Simulator"; |
---|
177 | } |
---|
178 | |
---|
179 | bool CCDSim::initProperties() |
---|
180 | { |
---|
181 | // Most hardware layers wont actually have indi properties defined |
---|
182 | // but the simulators are a special case |
---|
183 | INDI::CCD::initProperties(); |
---|
184 | |
---|
185 | IUFillNumber(&SimulatorSettingsN[0],"SIM_PrimarCCD.getXRes()","CCD X resolution","%4.0f",0,2048,0,1280); |
---|
186 | IUFillNumber(&SimulatorSettingsN[1],"SIM_PrimarCCD.getYRes()","CCD Y resolution","%4.0f",0,2048,0,1024); |
---|
187 | IUFillNumber(&SimulatorSettingsN[2],"SIM_XSIZE","CCD X Pixel Size","%4.2f",0,60,0,5.2); |
---|
188 | IUFillNumber(&SimulatorSettingsN[3],"SIM_YSIZE","CCD Y Pixel Size","%4.2f",0,60,0,5.2); |
---|
189 | IUFillNumber(&SimulatorSettingsN[4],"SIM_MAXVAL","CCD Maximum ADU","%4.0f",0,65000,0,65000); |
---|
190 | IUFillNumber(&SimulatorSettingsN[5],"SIM_BIAS","CCD Bias","%4.0f",0,6000,0,1500); |
---|
191 | IUFillNumber(&SimulatorSettingsN[6],"SIM_SATURATION","Saturation Mag","%4.1f",0,20,0,1.0); |
---|
192 | IUFillNumber(&SimulatorSettingsN[7],"SIM_LIMITINGMAG","Limiting Mag","%4.1f",0,20,0,20); |
---|
193 | IUFillNumber(&SimulatorSettingsN[8],"SIM_FOCALLENGTH","Focal Length","%4.0f",0,60000,0,1000); |
---|
194 | IUFillNumber(&SimulatorSettingsN[9],"SIM_FWHM","FWHM (arcseconds)","%4.2f",0,60,0,3.5); |
---|
195 | IUFillNumber(&SimulatorSettingsN[10],"SIM_NOISE","CCD Noise","%4.0f",0,6000,0,50); |
---|
196 | IUFillNumber(&SimulatorSettingsN[11],"SIM_SKYGLOW","Sky Glow (magnitudes)","%4.1f",0,6000,0,19.5); |
---|
197 | IUFillNumber(&SimulatorSettingsN[12],"SIM_OAGOFFSET","Oag Offset (arminutes)","%4.1f",0,6000,0,0); |
---|
198 | IUFillNumberVector(SimulatorSettingsNV,SimulatorSettingsN,13,deviceName(),"SIMULATOR_SETTINGS","Simulator Settings","Simulator Config",IP_RW,60,IPS_IDLE); |
---|
199 | |
---|
200 | |
---|
201 | IUFillSwitch(&TimeFactorS[0],"1X","Actual Time",ISS_ON); |
---|
202 | IUFillSwitch(&TimeFactorS[1],"10X","10x",ISS_OFF); |
---|
203 | IUFillSwitch(&TimeFactorS[2],"100X","100x",ISS_OFF); |
---|
204 | IUFillSwitchVector(TimeFactorSV,TimeFactorS,3,deviceName(),"ON_TIME_FACTOR","Time Factor","Simulator Config",IP_RW,ISR_1OFMANY,60,IPS_IDLE); |
---|
205 | |
---|
206 | |
---|
207 | //loadConfig(); |
---|
208 | |
---|
209 | return true; |
---|
210 | } |
---|
211 | |
---|
212 | void CCDSim::ISGetProperties (const char *dev) |
---|
213 | { |
---|
214 | // First we let our parent populate |
---|
215 | |
---|
216 | //IDLog("CCDSim IsGetProperties with %s\n",dev); |
---|
217 | INDI::CCD::ISGetProperties(dev); |
---|
218 | |
---|
219 | defineNumber(SimulatorSettingsNV); |
---|
220 | defineSwitch(TimeFactorSV); |
---|
221 | //IDDefText(&ConfigFileTV, NULL); |
---|
222 | //IDDefSwitch(&ConfigSaveRestoreSV, NULL); |
---|
223 | |
---|
224 | |
---|
225 | return; |
---|
226 | } |
---|
227 | |
---|
228 | bool CCDSim::updateProperties() |
---|
229 | { |
---|
230 | IDDefNumber(SimulatorSettingsNV, NULL); |
---|
231 | |
---|
232 | INDI::CCD::updateProperties(); |
---|
233 | return true; |
---|
234 | } |
---|
235 | |
---|
236 | |
---|
237 | bool CCDSim::Disconnect() |
---|
238 | { |
---|
239 | return true; |
---|
240 | } |
---|
241 | |
---|
242 | int CCDSim::StartExposure(float duration) |
---|
243 | { |
---|
244 | // for the simulator, we can just draw the frame now |
---|
245 | // and it will get returned at the right time |
---|
246 | // by the timer routines |
---|
247 | ExposureRequest=duration; |
---|
248 | |
---|
249 | if(InExposure) |
---|
250 | { |
---|
251 | // We are already in an exposure, just change the time |
---|
252 | // and be done with it. |
---|
253 | return 0; |
---|
254 | } |
---|
255 | |
---|
256 | gettimeofday(&ExpStart,NULL); |
---|
257 | // Leave the proper time showing for the draw routines |
---|
258 | DrawCcdFrame(); |
---|
259 | // Now compress the actual wait time |
---|
260 | ExposureRequest=duration*TimeFactor; |
---|
261 | |
---|
262 | InExposure=true; |
---|
263 | return 0; |
---|
264 | } |
---|
265 | |
---|
266 | int CCDSim::StartGuideExposure(float n) |
---|
267 | { |
---|
268 | GuideExposureRequest=n; |
---|
269 | if(InGuideExposure) return 0; |
---|
270 | DrawGuiderFrame(); |
---|
271 | gettimeofday(&GuideExpStart,NULL); |
---|
272 | InGuideExposure=true; |
---|
273 | return 0; |
---|
274 | } |
---|
275 | |
---|
276 | bool CCDSim::AbortGuideExposure() |
---|
277 | { |
---|
278 | //IDLog("Enter AbortGuideExposure\n"); |
---|
279 | if(!InGuideExposure) return true; // no need to abort if we aren't doing one |
---|
280 | AbortGuideFrame=true; |
---|
281 | return true; |
---|
282 | } |
---|
283 | |
---|
284 | float CCDSim::CalcTimeLeft(timeval start,float req) |
---|
285 | { |
---|
286 | double timesince; |
---|
287 | double timeleft; |
---|
288 | struct timeval now; |
---|
289 | gettimeofday(&now,NULL); |
---|
290 | |
---|
291 | timesince=(double)(now.tv_sec * 1000.0 + now.tv_usec/1000) - (double)(start.tv_sec * 1000.0 + start.tv_usec/1000); |
---|
292 | timesince=timesince/1000; |
---|
293 | timeleft=req-timesince; |
---|
294 | return timeleft; |
---|
295 | } |
---|
296 | |
---|
297 | void CCDSim::TimerHit() |
---|
298 | { |
---|
299 | int nexttimer=1000; |
---|
300 | |
---|
301 | if(isConnected() == false) return; // No need to reset timer if we are not connected anymore |
---|
302 | |
---|
303 | if(InExposure) |
---|
304 | { |
---|
305 | float timeleft; |
---|
306 | timeleft=CalcTimeLeft(ExpStart,ExposureRequest); |
---|
307 | |
---|
308 | if (timeleft < 0) |
---|
309 | timeleft = 0; |
---|
310 | |
---|
311 | PrimaryCCD.setExposure(timeleft); |
---|
312 | //ImageExposureN[0].value = timeleft; |
---|
313 | //IDSetNumber(ImageExposureNP, NULL); |
---|
314 | |
---|
315 | if(timeleft < 1.0) |
---|
316 | { |
---|
317 | if(timeleft <= 0.001) |
---|
318 | { |
---|
319 | InExposure=false; |
---|
320 | ExposureComplete(); |
---|
321 | } else |
---|
322 | { |
---|
323 | nexttimer=timeleft*1000; // set a shorter timer |
---|
324 | } |
---|
325 | } |
---|
326 | } |
---|
327 | |
---|
328 | if(InGuideExposure) |
---|
329 | { |
---|
330 | float timeleft; |
---|
331 | timeleft=CalcTimeLeft(GuideExpStart,GuideExposureRequest); |
---|
332 | |
---|
333 | |
---|
334 | if (timeleft < 0) |
---|
335 | timeleft = 0; |
---|
336 | |
---|
337 | //ImageExposureN[0].value = timeleft; |
---|
338 | //IDSetNumber(ImageExposureNP, NULL); |
---|
339 | GuideCCD.setExposure(timeleft); |
---|
340 | |
---|
341 | if(timeleft < 1.0) |
---|
342 | { |
---|
343 | if(timeleft <= 0.001) |
---|
344 | { |
---|
345 | InGuideExposure=false; |
---|
346 | if(!AbortGuideFrame) |
---|
347 | { |
---|
348 | //IDLog("Sending guider frame\n"); |
---|
349 | GuideExposureComplete(); |
---|
350 | if(InGuideExposure) |
---|
351 | { // the call to complete triggered another exposure |
---|
352 | timeleft=CalcTimeLeft(GuideExpStart,GuideExposureRequest); |
---|
353 | if(timeleft <1.0) |
---|
354 | { |
---|
355 | nexttimer=timeleft*1000; |
---|
356 | } |
---|
357 | } |
---|
358 | } else |
---|
359 | { |
---|
360 | IDLog("Not sending guide frame cuz of abort\n"); |
---|
361 | } |
---|
362 | AbortGuideFrame=false; |
---|
363 | } else |
---|
364 | { |
---|
365 | nexttimer=timeleft*1000; // set a shorter timer |
---|
366 | } |
---|
367 | } |
---|
368 | } |
---|
369 | SetTimer(nexttimer); |
---|
370 | return; |
---|
371 | } |
---|
372 | |
---|
373 | int CCDSim::DrawCcdFrame() |
---|
374 | { |
---|
375 | // Ok, lets just put a silly pattern into this |
---|
376 | // CCd frame is 16 bit data |
---|
377 | unsigned short int *ptr; |
---|
378 | unsigned short int val; |
---|
379 | |
---|
380 | ptr=(unsigned short int *) PrimaryCCD.getFrameBuffer(); |
---|
381 | |
---|
382 | if(ShowStarField) |
---|
383 | { |
---|
384 | char gsccmd[250]; |
---|
385 | FILE *pp; |
---|
386 | int stars=0; |
---|
387 | int lines=0; |
---|
388 | int drawn=0; |
---|
389 | int x,y; |
---|
390 | float PEOffset; |
---|
391 | float PESpot; |
---|
392 | double rad; // telescope ra in degrees |
---|
393 | double rar; // telescope ra in radians |
---|
394 | double decr; // telescope dec in radians; |
---|
395 | |
---|
396 | |
---|
397 | double timesince; |
---|
398 | time_t now; |
---|
399 | time(&now); |
---|
400 | |
---|
401 | // Lets figure out where we are on the pe curve |
---|
402 | timesince=difftime(now,RunStart); |
---|
403 | // This is our spot in the curve |
---|
404 | PESpot=timesince/PEPeriod; |
---|
405 | // Now convert to radians |
---|
406 | PESpot=PESpot*2.0*3.14159; |
---|
407 | |
---|
408 | PEOffset=PEMax*sin(PESpot); |
---|
409 | //fprintf(stderr,"PEOffset = %4.2f arcseconds timesince %4.2f\n",PEOffset,timesince); |
---|
410 | PEOffset=PEOffset/3600; // convert to degrees |
---|
411 | //PeOffset=PeOffset/15; // ra is in h:mm |
---|
412 | |
---|
413 | // Start by clearing the frame buffer |
---|
414 | memset(PrimaryCCD.getFrameBuffer(),0,PrimaryCCD.getFrameBufferSize()); |
---|
415 | |
---|
416 | |
---|
417 | // Spin up a set of plate constants that will relate |
---|
418 | // ra/dec of stars, to our fictitious ccd layout |
---|
419 | |
---|
420 | // to account for various rotations etc |
---|
421 | // we should spin up some plate constants here |
---|
422 | // then we can use these constants to rotate and offset |
---|
423 | // the standard co-ordinates on each star for drawing |
---|
424 | // a ccd frame; |
---|
425 | double pa,pb,pc,pd,pe,pf; |
---|
426 | // Since this is a simple ccd, correctly aligned |
---|
427 | // for now we cheat |
---|
428 | // no offset or rotation for and y axis means |
---|
429 | pb=0.0; |
---|
430 | pc=PrimaryCCD.getXRes()/2/PrimaryCCD.getBinX(); |
---|
431 | pd=0.0; |
---|
432 | pf=PrimaryCCD.getYRes()/2/PrimaryCCD.getBinY(); |
---|
433 | // and we do a simple scale for x and y locations |
---|
434 | // based on the focal length and pixel size |
---|
435 | // focal length in mm, pixels in microns |
---|
436 | pa=focallength/PrimaryCCD.getPixelSizeX()*1000/PrimaryCCD.getBinX(); |
---|
437 | pe=focallength/PrimaryCCD.getPixelSizeY()*1000/PrimaryCCD.getBinY(); |
---|
438 | |
---|
439 | //IDLog("Pixels are %4.2f %4.2f pa %6.4f pe %6.4f\n",PixelSizex,PixelSizey,pa,pe); |
---|
440 | |
---|
441 | // these numbers are now pixels per radian |
---|
442 | float Scalex; |
---|
443 | float Scaley; |
---|
444 | Scalex=pa*0.0174532925; // pixels per degree |
---|
445 | Scalex=Scalex/3600.0; // pixels per arcsecond |
---|
446 | Scalex=1.0/Scalex; // arcseconds per pixel |
---|
447 | |
---|
448 | Scaley=pe*0.0174532925; // pixels per degree |
---|
449 | Scaley=Scaley/3600.0; // pixels per arcsecond |
---|
450 | Scaley=1.0/Scaley; // arcseconds per pixel |
---|
451 | //qq=qq/3600; // arcseconds per pixel |
---|
452 | |
---|
453 | //IDLog("Pixel scale is %4.2f x %4.2f\n",Scalex,Scaley); |
---|
454 | ImageScalex=Scalex; |
---|
455 | ImageScaley=Scaley; |
---|
456 | |
---|
457 | // calc this now, we will use it a lot later |
---|
458 | rad=RA*15.0; |
---|
459 | rar=rad*0.0174532925; |
---|
460 | // offsetting the dec by the guide head offset |
---|
461 | float cameradec; |
---|
462 | cameradec=Dec+OAGoffset/60; |
---|
463 | decr=cameradec*0.0174532925; |
---|
464 | |
---|
465 | //fprintf(stderr,"Dec %7.5f cameradec %7.5f CenterOffsetDec %4.4f\n",Dec,cameradec,CenterOffsetDec); |
---|
466 | // now lets calculate the radius we need to fetch |
---|
467 | float radius; |
---|
468 | |
---|
469 | radius=sqrt((Scalex*Scalex*PrimaryCCD.getXRes()/2.0*PrimaryCCD.getXRes()/2.0)+(Scaley*Scaley*PrimaryCCD.getYRes()/2.0*PrimaryCCD.getYRes()/2.0)); |
---|
470 | // we have radius in arcseconds now |
---|
471 | radius=radius/60; // convert to arcminutes |
---|
472 | //fprintf(stderr,"Lookup radius %4.2f\n",radius); |
---|
473 | //radius=radius*2; |
---|
474 | |
---|
475 | // A saturationmag star saturates in one second |
---|
476 | // and a limitingmag produces a one adu level in one second |
---|
477 | // solve for zero point and system gain |
---|
478 | |
---|
479 | k=(saturationmag-limitingmag)/((-2.5*log(maxval))-(-2.5*log(1.0/2.0))); |
---|
480 | z=saturationmag-k*(-2.5*log(maxval)); |
---|
481 | //z=z+saturationmag; |
---|
482 | |
---|
483 | //IDLog("K=%4.2f Z=%4.2f\n",k,z); |
---|
484 | |
---|
485 | // Should probably do some math here to figure out the dimmest |
---|
486 | // star we can see on this exposure |
---|
487 | // and only fetch to that magnitude |
---|
488 | // for now, just use the limiting mag number with some room to spare |
---|
489 | float lookuplimit; |
---|
490 | |
---|
491 | lookuplimit=limitingmag; |
---|
492 | lookuplimit=lookuplimit; |
---|
493 | if(radius > 60) lookuplimit=11; |
---|
494 | |
---|
495 | // if this is a light frame, we need a star field drawn |
---|
496 | if(PrimaryCCD.getFrameType()==CCDChip::LIGHT_FRAME) |
---|
497 | { |
---|
498 | //sprintf(gsccmd,"gsc -c %8.6f %+8.6f -r 120 -m 0 9.1",rad+PEOffset,Dec); |
---|
499 | sprintf(gsccmd,"gsc -c %8.6f %+8.6f -r %4.1f -m 0 %4.2f -n 3000",rad+PEOffset,cameradec,radius,lookuplimit); |
---|
500 | //fprintf(stderr,"gsccmd %s\n",gsccmd); |
---|
501 | pp=popen(gsccmd,"r"); |
---|
502 | if(pp != NULL) { |
---|
503 | char line[256]; |
---|
504 | while(fgets(line,256,pp)!=NULL) |
---|
505 | { |
---|
506 | //fprintf(stderr,"%s",line); |
---|
507 | |
---|
508 | // ok, lets parse this line for specifcs we want |
---|
509 | char id[20]; |
---|
510 | char plate[6]; |
---|
511 | char ob[6]; |
---|
512 | float mag; |
---|
513 | float mage; |
---|
514 | float ra; |
---|
515 | float dec; |
---|
516 | float pose; |
---|
517 | int band; |
---|
518 | float dist; |
---|
519 | int dir; |
---|
520 | int c; |
---|
521 | int rc; |
---|
522 | |
---|
523 | rc=sscanf(line,"%10s %f %f %f %f %f %d %d %4s %2s %f %d", |
---|
524 | id,&ra,&dec,&pose,&mag,&mage,&band,&c,plate,ob,&dist,&dir); |
---|
525 | //fprintf(stderr,"Parsed %d items\n",rc); |
---|
526 | if(rc==12) { |
---|
527 | lines++; |
---|
528 | //if(c==0) { |
---|
529 | stars++; |
---|
530 | //fprintf(stderr,"%s %8.4f %8.4f %5.2f %5.2f %d\n",id,ra,dec,mag,dist,dir); |
---|
531 | |
---|
532 | // Convert the ra/dec to standard co-ordinates |
---|
533 | double sx; // standard co-ords |
---|
534 | double sy; // |
---|
535 | double srar; // star ra in radians |
---|
536 | double sdecr; // star dec in radians; |
---|
537 | double ccdx; |
---|
538 | double ccdy; |
---|
539 | |
---|
540 | //fprintf(stderr,"line %s",line); |
---|
541 | //fprintf(stderr,"parsed %6.5f %6.5f\n",ra,dec); |
---|
542 | |
---|
543 | srar=ra*0.0174532925; |
---|
544 | sdecr=dec*0.0174532925; |
---|
545 | // Handbook of astronomical image processing |
---|
546 | // page 253 |
---|
547 | // equations 9.1 and 9.2 |
---|
548 | // convert ra/dec to standard co-ordinates |
---|
549 | |
---|
550 | sx=cos(decr)*sin(srar-rar)/( cos(decr)*cos(sdecr)*cos(srar-rar)+sin(decr)*sin(sdecr) ); |
---|
551 | sy=(sin(decr)*cos(sdecr)*cos(srar-rar)-cos(decr)*sin(sdecr))/( cos(decr)*cos(sdecr)*cos(srar-rar)+sin(decr)*sin(sdecr) ); |
---|
552 | |
---|
553 | // now convert to microns |
---|
554 | ccdx=pa*sx+pb*sy+pc; |
---|
555 | ccdy=pd*sx+pe*sy+pf; |
---|
556 | |
---|
557 | |
---|
558 | rc=DrawImageStar(mag,ccdx,ccdy); |
---|
559 | drawn+=rc; |
---|
560 | if(rc==1) { |
---|
561 | //fprintf(stderr,"star %s scope %6.4f %6.4f star %6.4f %6.4f ccd %6.2f %6.2f\n",id,rad,Dec,ra,dec,ccdx,ccdy); |
---|
562 | //fprintf(stderr,"star %s ccd %6.2f %6.2f\n",id,ccdx,ccdy); |
---|
563 | } |
---|
564 | } |
---|
565 | } |
---|
566 | pclose(pp); |
---|
567 | } else |
---|
568 | { |
---|
569 | IDMessage(deviceName(),"Error looking up stars, is gsc installed with appropriate environment variables set ??"); |
---|
570 | //fprintf(stderr,"Error doing gsc lookup\n"); |
---|
571 | } |
---|
572 | if(drawn==0) |
---|
573 | { |
---|
574 | IDMessage(deviceName(),"Got no stars, is gsc installed with appropriate environment variables set ??"); |
---|
575 | |
---|
576 | } |
---|
577 | } |
---|
578 | //fprintf(stderr,"Got %d stars from %d lines drew %d\n",stars,lines,drawn); |
---|
579 | |
---|
580 | // now we need to add background sky glow, with vignetting |
---|
581 | // this is essentially the same math as drawing a dim star with |
---|
582 | // fwhm equivalent to the full field of view |
---|
583 | |
---|
584 | CCDChip::CCD_FRAME ftype = PrimaryCCD.getFrameType(); |
---|
585 | |
---|
586 | if((ftype==CCDChip::LIGHT_FRAME)||(ftype==CCDChip::FLAT_FRAME)) |
---|
587 | { |
---|
588 | float skyflux; |
---|
589 | float glow; |
---|
590 | // calculate flux from our zero point and gain values |
---|
591 | glow=skyglow; |
---|
592 | if(ftype==CCDChip::FLAT_FRAME) |
---|
593 | { |
---|
594 | // Assume flats are done with a diffuser |
---|
595 | // in broad daylight, so, the sky magnitude |
---|
596 | // is much brighter than at night |
---|
597 | glow=skyglow/10; |
---|
598 | } |
---|
599 | //fprintf(stderr,"Using glow %4.2f\n",glow); |
---|
600 | skyflux=pow(10,((glow-z)*k/-2.5)); |
---|
601 | // ok, flux represents one second now |
---|
602 | // scale up linearly for exposure time |
---|
603 | skyflux=skyflux*ExposureRequest*PrimaryCCD.getBinX()*PrimaryCCD.getBinY(); |
---|
604 | //IDLog("SkyFlux = %4.2f ExposureRequest %4.2f\n",skyflux,ExposureRequest); |
---|
605 | |
---|
606 | unsigned short *pt; |
---|
607 | |
---|
608 | int nwidth = PrimaryCCD.getXRes()/PrimaryCCD.getBinX(); |
---|
609 | int nheight = PrimaryCCD.getYRes()/PrimaryCCD.getBinY(); |
---|
610 | pt=(unsigned short int *)PrimaryCCD.getFrameBuffer(); |
---|
611 | for(int y=0; y< nheight; y++) { |
---|
612 | for(int x=0; x< nwidth; x++) { |
---|
613 | float dc; // distance from center |
---|
614 | float fp; // flux this pixel; |
---|
615 | float sx,sy; |
---|
616 | float vig; |
---|
617 | |
---|
618 | sx=PrimaryCCD.getXRes()/2/PrimaryCCD.getBinX(); |
---|
619 | sx=sx-x; |
---|
620 | sy=PrimaryCCD.getYRes()/2/PrimaryCCD.getBinY(); |
---|
621 | sy=sy-y; |
---|
622 | |
---|
623 | vig=PrimaryCCD.getXRes()/PrimaryCCD.getBinX(); |
---|
624 | vig=vig*ImageScalex; |
---|
625 | // need to make this account for actual pixel size |
---|
626 | dc=sqrt(sx*sx*ImageScalex*ImageScalex+sy*sy*ImageScaley*ImageScaley); |
---|
627 | // now we have the distance from center, in arcseconds |
---|
628 | // now lets plot a gaussian falloff to the edges |
---|
629 | // |
---|
630 | float fa; |
---|
631 | fa=exp(-2.0*0.7*(dc*dc)/vig/vig); |
---|
632 | // get the current value |
---|
633 | fp=pt[0]; |
---|
634 | // Add the sky glow |
---|
635 | fp+=skyflux; |
---|
636 | // now scale it for the vignetting |
---|
637 | fp=fa*fp; |
---|
638 | // clamp to limits |
---|
639 | if(fp > maxval) fp=maxval; |
---|
640 | // and put it back |
---|
641 | pt[0]=fp; |
---|
642 | pt++; |
---|
643 | |
---|
644 | } |
---|
645 | } |
---|
646 | } |
---|
647 | |
---|
648 | |
---|
649 | // Now we add some bias and read noise |
---|
650 | for(x=0; x<PrimaryCCD.getXRes(); x++) { |
---|
651 | for(y=0; y<PrimaryCCD.getYRes(); y++) { |
---|
652 | int noise; |
---|
653 | |
---|
654 | noise=random(); |
---|
655 | noise=noise%maxnoise; // |
---|
656 | |
---|
657 | AddToPixel(x,y,bias+noise); |
---|
658 | } |
---|
659 | } |
---|
660 | |
---|
661 | |
---|
662 | } else { |
---|
663 | testvalue++; |
---|
664 | if(testvalue > 255) testvalue=0; |
---|
665 | val=testvalue; |
---|
666 | |
---|
667 | int nbuf = PrimaryCCD.getXRes()*PrimaryCCD.getYRes(); |
---|
668 | |
---|
669 | for(int x=0; x<nbuf; x++) |
---|
670 | { |
---|
671 | *ptr=val++; |
---|
672 | ptr++; |
---|
673 | } |
---|
674 | |
---|
675 | } |
---|
676 | return 0; |
---|
677 | } |
---|
678 | |
---|
679 | int CCDSim::DrawGuiderFrame() |
---|
680 | { |
---|
681 | unsigned char *ptr; |
---|
682 | unsigned char val; |
---|
683 | |
---|
684 | ptr=(unsigned char *) GuideCCD.getFrameBuffer(); |
---|
685 | testvalue++; |
---|
686 | if(testvalue > 255) testvalue=0; |
---|
687 | val=testvalue; |
---|
688 | |
---|
689 | int nbuf = GuideCCD.getXRes()*GuideCCD.getYRes(); |
---|
690 | for(int x=0; x< nbuf; x++) |
---|
691 | { |
---|
692 | *ptr=val++; |
---|
693 | ptr++; |
---|
694 | } |
---|
695 | return 0; |
---|
696 | } |
---|
697 | |
---|
698 | int CCDSim::DrawImageStar(float mag,float x,float y) |
---|
699 | { |
---|
700 | //float d; |
---|
701 | //float r; |
---|
702 | int sx,sy; |
---|
703 | int drew=0; |
---|
704 | int boxsizex=5; |
---|
705 | int boxsizey=5; |
---|
706 | float flux; |
---|
707 | |
---|
708 | if((x<0)||(x>PrimaryCCD.getXRes()/PrimaryCCD.getBinX())||(y<0)||(y>PrimaryCCD.getYRes()/PrimaryCCD.getBinY())) |
---|
709 | { |
---|
710 | // this star is not on the ccd frame anyways |
---|
711 | return 0; |
---|
712 | } |
---|
713 | |
---|
714 | |
---|
715 | |
---|
716 | // calculate flux from our zero point and gain values |
---|
717 | flux=pow(10,((mag-z)*k/-2.5)); |
---|
718 | // ok, flux represents one second now |
---|
719 | // scale up linearly for exposure time |
---|
720 | flux=flux*ExposureRequest; |
---|
721 | |
---|
722 | |
---|
723 | float qx; |
---|
724 | // we need a box size that gives a radius at least 3 times fwhm |
---|
725 | qx=seeing/ImageScalex; |
---|
726 | qx=qx*3; |
---|
727 | boxsizex=(int)qx; |
---|
728 | boxsizex++; |
---|
729 | qx=seeing/ImageScaley; |
---|
730 | qx=qx*3; |
---|
731 | boxsizey=(int)qx; |
---|
732 | boxsizey++; |
---|
733 | |
---|
734 | //IDLog("BoxSize %d %d\n",boxsizex,boxsizey); |
---|
735 | |
---|
736 | |
---|
737 | for(sy=-boxsizey; sy<=boxsizey; sy++) { |
---|
738 | for(sx=-boxsizey; sx<=boxsizey; sx++) { |
---|
739 | int rc; |
---|
740 | float dc; // distance from center |
---|
741 | float fp; // flux this pixel; |
---|
742 | |
---|
743 | // need to make this account for actual pixel size |
---|
744 | dc=sqrt(sx*sx*ImageScalex*ImageScalex+sy*sy*ImageScaley*ImageScaley); |
---|
745 | // now we have the distance from center, in arcseconds |
---|
746 | // This should be gaussian, but, for now we'll just go with |
---|
747 | // a simple linear function |
---|
748 | float fa; |
---|
749 | fa=exp(-2.0*0.7*(dc*dc)/seeing/seeing); |
---|
750 | fp=fa*flux*PrimaryCCD.getBinX()*PrimaryCCD.getBinY(); |
---|
751 | if(fp < 0) fp=0; |
---|
752 | |
---|
753 | /* |
---|
754 | if(dc < boxsize) { |
---|
755 | dc=boxsize-dc; |
---|
756 | dc=dc/boxsize; |
---|
757 | fp=dc*flux; |
---|
758 | } else { |
---|
759 | fp=0; |
---|
760 | } |
---|
761 | */ |
---|
762 | rc=AddToPixel(x+sx,y+sy,fp); |
---|
763 | if(rc != 0) drew=1; |
---|
764 | } |
---|
765 | } |
---|
766 | return drew; |
---|
767 | } |
---|
768 | |
---|
769 | int CCDSim::AddToPixel(int x,int y,int val) |
---|
770 | { |
---|
771 | int drew=0; |
---|
772 | if(x >= 0) { |
---|
773 | if(x < PrimaryCCD.getXRes()/PrimaryCCD.getBinX()) { |
---|
774 | if(y >= 0) { |
---|
775 | if(y < PrimaryCCD.getYRes()/PrimaryCCD.getBinY()) { |
---|
776 | unsigned short *pt; |
---|
777 | int newval; |
---|
778 | drew++; |
---|
779 | pt=(unsigned short int *)PrimaryCCD.getFrameBuffer(); |
---|
780 | pt+=(y*PrimaryCCD.getXRes()/PrimaryCCD.getBinX()); |
---|
781 | pt+=x; |
---|
782 | newval=pt[0]; |
---|
783 | newval+=val; |
---|
784 | if(newval > maxval) newval=maxval; |
---|
785 | pt[0]=newval; |
---|
786 | } |
---|
787 | } |
---|
788 | } |
---|
789 | } |
---|
790 | return drew; |
---|
791 | } |
---|
792 | |
---|
793 | bool CCDSim::GuideNorth(float v) |
---|
794 | { |
---|
795 | float c; |
---|
796 | |
---|
797 | c=v*GuideRate; // |
---|
798 | c=c/3600; |
---|
799 | Dec=Dec+c; |
---|
800 | |
---|
801 | return true; |
---|
802 | } |
---|
803 | bool CCDSim::GuideSouth(float v) |
---|
804 | { |
---|
805 | float c; |
---|
806 | |
---|
807 | c=v*GuideRate; // |
---|
808 | c=c/3600; |
---|
809 | Dec=Dec-c; |
---|
810 | |
---|
811 | return true; |
---|
812 | } |
---|
813 | |
---|
814 | bool CCDSim::GuideEast(float v) |
---|
815 | { |
---|
816 | float c; |
---|
817 | |
---|
818 | c=v*GuideRate; |
---|
819 | c=c/3600.0/15.0; |
---|
820 | c=c/(cos(Dec*0.0174532925)); |
---|
821 | RA=RA-c; |
---|
822 | |
---|
823 | return true; |
---|
824 | } |
---|
825 | bool CCDSim::GuideWest(float v) |
---|
826 | { |
---|
827 | float c; |
---|
828 | |
---|
829 | c=v*GuideRate; // |
---|
830 | c=c/3600.0/15.0; |
---|
831 | c=c/(cos(Dec*0.0174532925)); |
---|
832 | RA=RA+c; |
---|
833 | |
---|
834 | return true; |
---|
835 | } |
---|
836 | |
---|
837 | bool CCDSim::ISNewNumber (const char *dev, const char *name, double values[], char *names[], int n) |
---|
838 | { |
---|
839 | // first check if it's for our device |
---|
840 | //IDLog("INDI::CCD::ISNewNumber %s\n",name); |
---|
841 | if(strcmp(dev,deviceName())==0) |
---|
842 | { |
---|
843 | // This is for our device |
---|
844 | // Now lets see if it's something we process here |
---|
845 | |
---|
846 | //IDLog("CCDSim::ISNewNumber %s\n",name); |
---|
847 | if(strcmp(name,"SIMULATOR_SETTINGS")==0) { |
---|
848 | // We are being asked to set camera binning |
---|
849 | SimulatorSettingsNV->s=IPS_OK; |
---|
850 | |
---|
851 | for(int x=0; x<n; x++) { |
---|
852 | //fprintf(stderr,"name %s value %4.2f\n",names[x],values[x]); |
---|
853 | if(values[x] != 0) { |
---|
854 | SimulatorSettingsN[x].value=values[x]; |
---|
855 | } else { |
---|
856 | // We ignore zeros on most of our items |
---|
857 | // because they likely mean it was just a field not |
---|
858 | // actually filled in, but, for the dec offset it is |
---|
859 | // important to keep a zero value |
---|
860 | if(strcmp(names[x],"SIM_OAGOFFSET")==0) { |
---|
861 | SimulatorSettingsN[x].value=values[x]; |
---|
862 | } |
---|
863 | } |
---|
864 | } |
---|
865 | |
---|
866 | // Reset our parameters now |
---|
867 | SetupParms(); |
---|
868 | IDSetNumber(SimulatorSettingsNV,NULL); |
---|
869 | saveConfig(); |
---|
870 | |
---|
871 | //IDLog("Frame set to %4.0f,%4.0f %4.0f x %4.0f\n",CcdFrameN[0].value,CcdFrameN[1].value,CcdFrameN[2].value,CcdFrameN[3].value); |
---|
872 | //seeing=SimulatorSettingsN[0].value; |
---|
873 | return true; |
---|
874 | } |
---|
875 | |
---|
876 | |
---|
877 | } |
---|
878 | // if we didn't process it, continue up the chain, let somebody else |
---|
879 | // give it a shot |
---|
880 | return INDI::CCD::ISNewNumber(dev,name,values,names,n); |
---|
881 | } |
---|
882 | |
---|
883 | |
---|
884 | bool CCDSim::ISNewSwitch (const char *dev, const char *name, ISState *states, char *names[], int n) |
---|
885 | { |
---|
886 | //IDLog("Enter IsNewSwitch for %s\n",name); |
---|
887 | //for(int x=0; x<n; x++) { |
---|
888 | // IDLog("Switch %s %d\n",names[x],states[x]); |
---|
889 | //} |
---|
890 | |
---|
891 | if(strcmp(dev,deviceName())==0) { |
---|
892 | // This one is for us |
---|
893 | |
---|
894 | |
---|
895 | if(strcmp(name,"ON_TIME_FACTOR")==0) { |
---|
896 | |
---|
897 | // client is telling us what to do with co-ordinate requests |
---|
898 | TimeFactorSV->s=IPS_OK; |
---|
899 | IUUpdateSwitch(TimeFactorSV,states,names,n); |
---|
900 | // Update client display |
---|
901 | IDSetSwitch(TimeFactorSV,NULL); |
---|
902 | |
---|
903 | saveConfig(); |
---|
904 | if(TimeFactorS[0].s==ISS_ON ) { |
---|
905 | //IDLog("CCDSim:: Time Factor 1\n"); |
---|
906 | TimeFactor=1; |
---|
907 | } |
---|
908 | if(TimeFactorS[1].s==ISS_ON ) { |
---|
909 | //IDLog("CCDSim:: Time Factor 0.1\n"); |
---|
910 | TimeFactor=0.1; |
---|
911 | } |
---|
912 | if(TimeFactorS[2].s==ISS_ON ) { |
---|
913 | //IDLog("CCDSim:: Time Factor 0.01\n"); |
---|
914 | TimeFactor=0.01; |
---|
915 | } |
---|
916 | |
---|
917 | return true; |
---|
918 | } |
---|
919 | |
---|
920 | } |
---|
921 | |
---|
922 | // Nobody has claimed this, so, ignore it |
---|
923 | return INDI::CCD::ISNewSwitch(dev,name,states,names,n); |
---|
924 | } |
---|
925 | |
---|