Instrument Neutral Distributed Interface INDI  2.0.2
guide_simulator.cpp
Go to the documentation of this file.
1 /*******************************************************************************
2  Copyright(c) 2017 Jasem Mutlaq. All rights reserved.
3  Copyright(c) 2010 Gerry Rozema. All rights reserved.
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 "guide_simulator.h"
20 #include "indicom.h"
21 #include "stream/streammanager.h"
22 
23 #include "locale_compat.h"
24 
25 #include <libnova/julian_day.h>
26 #include <libastro.h>
27 
28 #include <cmath>
29 #include <unistd.h>
30 
31 static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
32 static pthread_mutex_t condMutex = PTHREAD_MUTEX_INITIALIZER;
33 
34 // We declare an auto pointer to GuideSim.
35 static std::unique_ptr<GuideSim> ccd(new GuideSim());
36 
38 {
39  currentRA = RA;
40  currentDE = Dec;
41 
42  streamPredicate = 0;
43  terminateThread = false;
44 
45  time(&RunStart);
46 }
47 
48 bool GuideSim::SetupParms()
49 {
50  int nbuf;
51  SetCCDParams(SimulatorSettingsN[0].value, SimulatorSettingsN[1].value, 16, SimulatorSettingsN[2].value,
52  SimulatorSettingsN[3].value);
53 
54  if (HasCooler())
55  {
56  TemperatureN[0].value = 20;
57  IDSetNumber(&TemperatureNP, nullptr);
58  }
59 
60  // Kwiq
61  maxnoise = SimulatorSettingsN[8].value;
62  skyglow = SimulatorSettingsN[9].value;
63  maxval = SimulatorSettingsN[4].value;
64  bias = SimulatorSettingsN[5].value;
65  limitingmag = SimulatorSettingsN[7].value;
66  saturationmag = SimulatorSettingsN[6].value;
67  OAGoffset = SimulatorSettingsN[10].value; // An oag is offset this much from center of scope position (arcminutes);
68  polarError = SimulatorSettingsN[11].value;
69  polarDrift = SimulatorSettingsN[12].value;
70  rotationCW = SimulatorSettingsN[13].value;
71  // Kwiq++
72  king_gamma = SimulatorSettingsN[14].value * 0.0174532925;
73  king_theta = SimulatorSettingsN[15].value * 0.0174532925;
74  TimeFactor = SimulatorSettingsN[16].value;
75 
77  //nbuf += 512;
79 
80  Streamer->setPixelFormat(INDI_MONO, 16);
82 
83  return true;
84 }
85 
87 {
88  streamPredicate = 0;
89  terminateThread = false;
90  pthread_create(&primary_thread, nullptr, &streamVideoHelper, this);
92  return true;
93 }
94 
96 {
97  pthread_mutex_lock(&condMutex);
98  streamPredicate = 1;
99  terminateThread = true;
100  pthread_cond_signal(&cv);
101  pthread_mutex_unlock(&condMutex);
102 
103  return true;
104 }
105 
107 {
108  return "Guide Simulator";
109 }
110 
112 {
113  // Most hardware layers wont actually have indi properties defined
114  // but the simulators are a special case
116 
117  CaptureFormat format = {"INDI_MONO", "Mono", 16, true};
118  addCaptureFormat(format);
119 
120  IUFillNumber(&SimulatorSettingsN[0], "SIM_XRES", "CCD X resolution", "%4.0f", 0, 8192, 0, 1280);
121  IUFillNumber(&SimulatorSettingsN[1], "SIM_YRES", "CCD Y resolution", "%4.0f", 0, 8192, 0, 1024);
122  IUFillNumber(&SimulatorSettingsN[2], "SIM_XSIZE", "CCD X Pixel Size", "%4.2f", 0, 60, 0, 2.4);
123  IUFillNumber(&SimulatorSettingsN[3], "SIM_YSIZE", "CCD Y Pixel Size", "%4.2f", 0, 60, 0, 2.4);
124  IUFillNumber(&SimulatorSettingsN[4], "SIM_MAXVAL", "CCD Maximum ADU", "%4.0f", 0, 65000, 0, 65000);
125  IUFillNumber(&SimulatorSettingsN[5], "SIM_BIAS", "CCD Bias", "%4.0f", 0, 6000, 0, 10);
126  IUFillNumber(&SimulatorSettingsN[6], "SIM_SATURATION", "Saturation Mag", "%4.1f", 0, 20, 0, 1.0);
127  IUFillNumber(&SimulatorSettingsN[7], "SIM_LIMITINGMAG", "Limiting Mag", "%4.1f", 0, 20, 0, 17.0);
128  IUFillNumber(&SimulatorSettingsN[8], "SIM_NOISE", "CCD Noise", "%4.0f", 0, 6000, 0, 10);
129  IUFillNumber(&SimulatorSettingsN[9], "SIM_SKYGLOW", "Sky Glow (magnitudes)", "%4.1f", 0, 6000, 0, 19.5);
130  IUFillNumber(&SimulatorSettingsN[10], "SIM_OAGOFFSET", "Oag Offset (arcminutes)", "%4.1f", 0, 6000, 0, 0);
131  IUFillNumber(&SimulatorSettingsN[11], "SIM_POLAR", "PAE (arcminutes)", "%4.1f", -600, 600, 0,
132  0); /* PAE = Polar Alignment Error */
133  IUFillNumber(&SimulatorSettingsN[12], "SIM_POLARDRIFT", "PAE Drift (minutes)", "%4.1f", 0, 6000, 0, 0);
134  IUFillNumber(&SimulatorSettingsN[13], "SIM_ROTATION", "Rotation CW (degrees)", "%4.1f", -360, 360, 0, 0);
135  IUFillNumber(&SimulatorSettingsN[14], "SIM_KING_GAMMA", "(CP,TCP), deg", "%4.1f", 0, 10, 0, 0);
136  IUFillNumber(&SimulatorSettingsN[15], "SIM_KING_THETA", "hour hangle, deg", "%4.1f", 0, 360, 0, 0);
137  IUFillNumber(&SimulatorSettingsN[16], "SIM_TIME_FACTOR", "Time Factor (x)", "%.2f", 0.01, 100, 0, 1);
138 
139  IUFillNumberVector(&SimulatorSettingsNP, SimulatorSettingsN, 17, getDeviceName(), "SIMULATOR_SETTINGS",
140  "Config", SIMULATOR_TAB, IP_RW, 60, IPS_IDLE);
141 
142  // RGB Simulation
143  IUFillSwitch(&SimulateRgbS[0], "SIMULATE_YES", "Yes", ISS_OFF);
144  IUFillSwitch(&SimulateRgbS[1], "SIMULATE_NO", "No", ISS_ON);
145  IUFillSwitchVector(&SimulateRgbSP, SimulateRgbS, 2, getDeviceName(), "SIMULATE_RGB", "Simulate RGB",
146  SIMULATOR_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
147 
148  IUFillSwitch(&CoolerS[0], "COOLER_ON", "ON", ISS_OFF);
149  IUFillSwitch(&CoolerS[1], "COOLER_OFF", "OFF", ISS_ON);
150  IUFillSwitchVector(&CoolerSP, CoolerS, 2, getDeviceName(), "CCD_COOLER", "Cooler", MAIN_CONTROL_TAB, IP_WO,
151  ISR_1OFMANY, 0, IPS_IDLE);
152 
153  // CCD Gain
154  IUFillNumber(&GainN[0], "GAIN", "Gain", "%.f", 0, 100, 10, 50);
155  IUFillNumberVector(&GainNP, GainN, 1, getDeviceName(), "CCD_GAIN", "Gain", MAIN_CONTROL_TAB, IP_RW, 60, IPS_IDLE);
156 
157  IUFillNumber(&EqPEN[0], "RA_PE", "RA (hh:mm:ss)", "%010.6m", 0, 24, 0, 0);
158  IUFillNumber(&EqPEN[1], "DEC_PE", "DEC (dd:mm:ss)", "%010.6m", -90, 90, 0, 0);
159  IUFillNumberVector(&EqPENP, EqPEN, 2, getDeviceName(), "EQUATORIAL_PE", "EQ PE", SIMULATOR_TAB, IP_RW, 60,
160  IPS_IDLE);
161 
162  // Timeout
163  ToggleTimeoutSP[INDI_ENABLED].fill("INDI_ENABLED", "Enabled", ISS_OFF);
164  ToggleTimeoutSP[INDI_DISABLED].fill("INDI_DISABLED", "Disabled", ISS_ON);
165  ToggleTimeoutSP.fill(getDeviceName(), "CCD_TIMEOUT", "Timeout", SIMULATOR_TAB, IP_RW, ISR_1OFMANY, 60, IPS_IDLE);
166 
167 #ifdef USE_EQUATORIAL_PE
168  IDSnoopDevice(ActiveDeviceT[0].text, "EQUATORIAL_PE");
169 #else
170  IDSnoopDevice(ActiveDeviceT[ACTIVE_TELESCOPE].text, "EQUATORIAL_EOD_COORD");
171 #endif
172 
173  uint32_t cap = 0;
174 
175  cap |= CCD_CAN_ABORT;
176  cap |= CCD_CAN_BIN;
177  cap |= CCD_CAN_SUBFRAME;
178  cap |= CCD_HAS_SHUTTER;
179  cap |= CCD_HAS_ST4_PORT;
180  cap |= CCD_HAS_STREAMING;
181 
182 #ifdef HAVE_WEBSOCKET
183  cap |= CCD_HAS_WEB_SOCKET;
184 #endif
185 
186  SetCCDCapability(cap);
187 
188  // This should be called after the initial SetCCDCapability (above)
189  // as it modifies the capabilities.
190  setRGB(simulateRGB);
191 
192  addDebugControl();
193 
195 
196  return true;
197 }
198 
199 void GuideSim::setRGB(bool onOff)
200 {
201  if (onOff)
202  {
204  IUSaveText(&BayerT[0], "0");
205  IUSaveText(&BayerT[1], "0");
206  IUSaveText(&BayerT[2], "RGGB");
207  }
208  else
209  {
211  }
212 }
213 
214 void GuideSim::ISGetProperties(const char * dev)
215 {
217 
218  defineProperty(&SimulatorSettingsNP);
219  defineProperty(&EqPENP);
220  defineProperty(&SimulateRgbSP);
221  defineProperty(ToggleTimeoutSP);
222 }
223 
225 {
227 
228  if (isConnected())
229  {
230  if (HasCooler())
231  defineProperty(&CoolerSP);
232 
233  defineProperty(&GainNP);
234 
235  SetupParms();
236 
237  if (HasGuideHead())
238  {
239  SetGuiderParams(500, 290, 16, 9.8, 12.6);
241  }
242  }
243  else
244  {
245  if (HasCooler())
246  deleteProperty(CoolerSP.name);
247 
248  deleteProperty(GainNP.name);
249  }
250 
251  return true;
252 }
253 
254 int GuideSim::SetTemperature(double temperature)
255 {
256  TemperatureRequest = temperature;
257  if (fabs(temperature - TemperatureN[0].value) < 0.1)
258  {
259  TemperatureN[0].value = temperature;
260  return 1;
261  }
262 
263  CoolerS[0].s = ISS_ON;
264  CoolerS[1].s = ISS_OFF;
265  CoolerSP.s = IPS_BUSY;
266  IDSetSwitch(&CoolerSP, nullptr);
267  return 0;
268 }
269 
270 bool GuideSim::StartExposure(float duration)
271 {
272  // for the simulator, we can just draw the frame now
273  // and it will get returned at the right time
274  // by the timer routines
275  AbortPrimaryFrame = false;
276  ExposureRequest = duration;
277 
279  gettimeofday(&ExpStart, nullptr);
280  // Leave the proper time showing for the draw routines
282  // Now compress the actual wait time
283  ExposureRequest = duration * TimeFactor;
284  InExposure = true;
285 
286  return true;
287 }
288 
290 {
291  if (!InExposure)
292  return true;
293 
294  AbortPrimaryFrame = true;
295 
296  return true;
297 }
298 
299 float GuideSim::CalcTimeLeft(timeval start, float req)
300 {
301  double timesince;
302  double timeleft;
303  struct timeval now
304  {
305  0, 0
306  };
307  gettimeofday(&now, nullptr);
308 
309  timesince =
310  (double)(now.tv_sec * 1000.0 + now.tv_usec / 1000) - (double)(start.tv_sec * 1000.0 + start.tv_usec / 1000);
311  timesince = timesince / 1000;
312  timeleft = req - timesince;
313  return timeleft;
314 }
315 
317 {
318  uint32_t nextTimer = getCurrentPollingPeriod();
319 
320  // No need to reset timer if we are not connected anymore
321  if (!isConnected())
322  return;
323 
324  if (InExposure && ToggleTimeoutSP.findOnSwitchIndex() == INDI_DISABLED)
325  {
326  if (AbortPrimaryFrame)
327  {
328  InExposure = false;
329  AbortPrimaryFrame = false;
330  }
331  else
332  {
333  float timeleft;
334  timeleft = CalcTimeLeft(ExpStart, ExposureRequest);
335 
336  //IDLog("CCD Exposure left: %g - Requset: %g\n", timeleft, ExposureRequest);
337  if (timeleft < 0)
338  timeleft = 0;
339 
340  PrimaryCCD.setExposureLeft(timeleft);
341 
342  if (timeleft < 1.0)
343  {
344  if (timeleft <= 0.001)
345  {
346  InExposure = false;
349  }
350  else
351  {
352  // set a shorter timer
353  nextTimer = timeleft * 1000;
354  }
355  }
356  }
357  }
358 
359  if (TemperatureNP.s == IPS_BUSY)
360  {
361  if (TemperatureRequest < TemperatureN[0].value)
362  TemperatureN[0].value = std::max(TemperatureRequest, TemperatureN[0].value - 0.5);
363  else
364  TemperatureN[0].value = std::min(TemperatureRequest, TemperatureN[0].value + 0.5);
365 
366 
367  IDSetNumber(&TemperatureNP, nullptr);
368 
369  // Above 20, cooler is off
370  if (TemperatureN[0].value >= 20)
371  {
372  CoolerS[0].s = ISS_OFF;
373  CoolerS[0].s = ISS_ON;
374  CoolerSP.s = IPS_IDLE;
375  IDSetSwitch(&CoolerSP, nullptr);
376  }
377  }
378 
379 
380  SetTimer(nextTimer);
381 }
382 
384 {
385  // CCD frame is 16 bit data
386  double exposure_time;
387 
388  uint16_t * ptr = reinterpret_cast<uint16_t *>(targetChip->getFrameBuffer());
389 
390  if (Streamer->isStreaming())
391  exposure_time = (ExposureRequest < 1) ? (ExposureRequest * 100) : ExposureRequest * 2;
392  else
393  exposure_time = ExposureRequest;
394 
395  exposure_time *= (1 + sqrt(GainN[0].value));
396 
397  auto targetFocalLength = ScopeInfoNP[FocalLength].getValue() > 0 ? ScopeInfoNP[FocalLength].getValue() : snoopedFocalLength;
398 
399  if (ShowStarField)
400  {
401  float PEOffset;
402  float PESpot;
403  float decDrift;
404  double rad; // telescope ra in degrees
405  double rar; // telescope ra in radians
406  double decr; // telescope dec in radians;
407  int nwidth = 0, nheight = 0;
408 
409  double timesince;
410  time_t now;
411  time(&now);
412 
413  // Lets figure out where we are on the pe curve
414  timesince = difftime(now, RunStart);
415  // This is our spot in the curve
416  PESpot = timesince / PEPeriod;
417  // Now convert to radians
418  PESpot = PESpot * 2.0 * 3.14159;
419 
420  PEOffset = PEMax * std::sin(PESpot);
421  PEOffset = PEOffset / 3600; // convert to degrees
422  //PeOffset=PeOffset/15; // ra is in h:mm
423 
424  // Spin up a set of plate constants that will relate
425  // ra/dec of stars, to our fictitious ccd layout
426 
427  // to account for various rotations etc
428  // we should spin up some plate constants here
429  // then we can use these constants to rotate and offset
430  // the standard co-ordinates on each star for drawing
431  // a ccd frame;
432  double pa, pb, pc, pd, pe, pf;
433  // Pixels per radian
434  double pprx, ppry;
435  // Scale in arcsecs per pixel
436  double Scalex;
437  double Scaley;
438  // CCD width in pixels
439  double ccdW = targetChip->getXRes();
440 
441  // Pixels per radian
442  pprx = targetFocalLength / targetChip->getPixelSizeX() * 1000;
443  ppry = targetFocalLength / targetChip->getPixelSizeY() * 1000;
444 
445  // we do a simple scale for x and y locations
446  // based on the focal length and pixel size
447  // focal length in mm, pixels in microns
448  // JM: 2015-03-17: Using a simpler formula, Scalex and Scaley are in arcsecs/pixel
449  Scalex = (targetChip->getPixelSizeX() / targetFocalLength) * 206.3;
450  Scaley = (targetChip->getPixelSizeY() / targetFocalLength) * 206.3;
451 
452 #if 0
453  DEBUGF(
455  "pprx: %g pixels per radian ppry: %g pixels per radian ScaleX: %g arcsecs/pixel ScaleY: %g arcsecs/pixel",
456  pprx, ppry, Scalex, Scaley);
457 #endif
458 
459  double theta = rotationCW + 270;
460  if (theta > 360)
461  theta -= 360;
462  if (pierSide == 1)
463  theta -= 180; // rotate 180 if on East
464  else if (theta < -360)
465  theta += 360;
466 
467  // JM: 2015-03-17: Next we do a rotation assuming CW for angle theta
468  pa = pprx * cos(theta * M_PI / 180.0);
469  pb = ppry * sin(theta * M_PI / 180.0);
470 
471  pd = pprx * -sin(theta * M_PI / 180.0);
472  pe = ppry * cos(theta * M_PI / 180.0);
473 
474  nwidth = targetChip->getXRes();
475  pc = nwidth / 2;
476 
477  nheight = targetChip->getYRes();
478  pf = nheight / 2;
479 
480  ImageScalex = Scalex;
481  ImageScaley = Scaley;
482 
483 #ifdef USE_EQUATORIAL_PE
484  if (!usePE)
485  {
486 #endif
487  currentRA = RA;
488  currentDE = Dec;
489 
490  if (std::isnan(currentRA))
491  {
492  currentRA = 0;
493  currentDE = 0;
494  }
495 
496  INDI::IEquatorialCoordinates epochPos { currentRA, currentDE }, J2000Pos { 0, 0 };
497  // Convert from JNow to J2000
498  INDI::ObservedToJ2000(&epochPos, ln_get_julian_from_sys(), &J2000Pos);
499  currentRA = J2000Pos.rightascension;
500  currentDE = J2000Pos.declination;
501  currentDE += guideNSOffset;
502  currentRA += guideWEOffset;
503 #ifdef USE_EQUATORIAL_PE
504  }
505 #endif
506 
507  // calc this now, we will use it a lot later
508  rad = currentRA * 15.0 + PEOffset;
509  rar = rad * 0.0174532925;
510  // offsetting the dec by the guide head offset
511  float cameradec;
512  cameradec = currentDE + OAGoffset / 60;
513  decr = cameradec * 0.0174532925;
514 
515  decDrift = (polarDrift * polarError * cos(decr)) / 3.81;
516 
517  // Add declination drift, if any.
518  decr += decDrift / 3600.0 * 0.0174532925;
519 
520  // now lets calculate the radius we need to fetch
521  float radius;
522 
523  radius = sqrt((Scalex * Scalex * targetChip->getXRes() / 2.0 * targetChip->getXRes() / 2.0) +
524  (Scaley * Scaley * targetChip->getYRes() / 2.0 * targetChip->getYRes() / 2.0));
525  // we have radius in arcseconds now
526  radius = radius / 60; // convert to arcminutes
527 #if 0
528  LOGF_DEBUG("Lookup radius %4.2f", radius);
529 #endif
530 
531  // A saturationmag star saturates in one second
532  // and a limitingmag produces a one adu level in one second
533  // solve for zero point and system gain
534 
535  k = (saturationmag - limitingmag) / ((-2.5 * log(maxval)) - (-2.5 * log(1.0 / 2.0)));
536  z = saturationmag - k * (-2.5 * log(maxval));
537  //z=z+saturationmag;
538 
539  //IDLog("K=%4.2f Z=%4.2f\n",k,z);
540 
541  // Should probably do some math here to figure out the dimmest
542  // star we can see on this exposure
543  // and only fetch to that magnitude
544  // for now, just use the limiting mag number with some room to spare
545  float lookuplimit = limitingmag;
546 
547  if (radius > 60)
548  lookuplimit = 11;
549 
550  if (king_gamma > 0.)
551  {
552  // wildi, make sure there are always stars, e.g. in case where king_gamma is set to 1 degree.
553  // Otherwise the solver will fail.
554  radius = 60.;
555 
556  // wildi, transform to telescope coordinate system, differential form
557  // see E.S. King based on Chauvenet:
558  // https://ui.adsabs.harvard.edu/link_gateway/1902AnHar..41..153K/ADS_PDF
559  // Currently it is not possible to enable the logging in simulator devices (tested with ccd and telescope)
560  // Replace LOGF_DEBUG by IDLog
561  //IDLog("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", king_gamma); // without variable, macro expansion fails
562  char JnRAStr[64] = {0};
563  fs_sexa(JnRAStr, RA, 2, 360000);
564  char JnDecStr[64] = {0};
565  fs_sexa(JnDecStr, Dec, 2, 360000);
566  // IDLog("Longitude : %8.3f, Latitude : %8.3f\n", this->Longitude, this->Latitude);
567  // IDLog("King gamma : %8.3f, King theta : %8.3f\n", king_gamma / 0.0174532925, king_theta / 0.0174532925);
568  // IDLog("Jnow RA : %11s, dec: %11s\n", JnRAStr, JnDecStr );
569  // IDLog("Jnow RA : %8.3f, Dec : %8.3f\n", RA * 15., Dec);
570  // IDLog("J2000 Pos.ra: %8.3f, Pos.dec: %8.3f\n", J2000Pos.ra, J2000Pos.dec);
571  // Since the catalog is J2000, we are going back in time
572  // tra, tdec are at the center of the projection center for the simulated
573  // images
574  //double J2ra = J2000Pos.ra; // J2000Pos: 0,360, RA: 0,24
575  double J2dec = J2000Pos.declination;
576 
577  //double J2rar = J2ra * 0.0174532925;
578  double J2decr = J2dec * 0.0174532925;
579  double sid = get_local_sidereal_time(this->Longitude);
580  // HA is what is observed, that is Jnow
581  // ToDo check if mean or apparent
582  double JnHAr = get_local_hour_angle(sid, RA) * 15. * 0.0174532925;
583 
584  char sidStr[64] = {0};
585  fs_sexa(sidStr, sid, 2, 3600);
586  char JnHAStr[64] = {0};
587  fs_sexa(JnHAStr, JnHAr / 15. / 0.0174532925, 2, 360000);
588 
589  // IDLog("sid : %s\n", sidStr);
590  // IDLog("Jnow JnHA: %8.3f degree\n", JnHAr / 0.0174532925);
591  // IDLog(" JnHAStr: %11s hms\n", JnHAStr);
592  // king_theta is the HA of the great circle where the HA axis is in.
593  // RA is a right and HA a left handed coordinate system.
594  // apparent or J2000? apparent, since we live now :-)
595 
596  // Transform to the mount coordinate system
597  // remember it is the center of the simulated image
598  double J2_mnt_d_rar = king_gamma * sin(J2decr) * sin(JnHAr - king_theta) / cos(J2decr);
599  double J2_mnt_rar = rar - J2_mnt_d_rar
600  ; // rad = currentRA * 15.0; rar = rad * 0.0174532925; currentRA = J2000Pos.ra / 15.0;
601 
602  // Imagine the HA axis points to HA=0, dec=89deg, then in the mount's coordinate
603  // system a star at true dec = 88 is seen at 89 deg in the mount's system
604  // Or in other words: if one uses the setting circle, that is the mount system,
605  // and set it to 87 deg then the real location is at 88 deg.
606  double J2_mnt_d_decr = king_gamma * cos(JnHAr - king_theta);
607  double J2_mnt_decr = decr + J2_mnt_d_decr
608  ; // decr = cameradec * 0.0174532925; cameradec = currentDE + OAGoffset / 60; currentDE = J2000Pos.dec;
609  // IDLog("raw mod ra : %8.3f, dec: %8.3f (degree)\n", J2_mnt_rar / 0.0174532925, J2_mnt_decr / 0.0174532925 );
610  if (J2_mnt_decr > M_PI / 2.)
611  {
612  J2_mnt_decr = M_PI / 2. - (J2_mnt_decr - M_PI / 2.);
613  J2_mnt_rar -= M_PI;
614  }
615  J2_mnt_rar = fmod(J2_mnt_rar, 2. * M_PI) ;
616  // IDLog("mod sin : %8.3f, cos: %8.3f\n", sin(JnHAr - king_theta), cos(JnHAr - king_theta));
617  // IDLog("mod dra : %8.3f, ddec: %8.3f (degree)\n", J2_mnt_d_rar / 0.0174532925, J2_mnt_d_decr / 0.0174532925 );
618  // IDLog("mod ra : %8.3f, dec: %8.3f (degree)\n", J2_mnt_rar / 0.0174532925, J2_mnt_decr / 0.0174532925 );
619  //IDLog("mod ra : %11s, dec: %11s\n", );
620  char J2RAStr[64] = {0};
621  fs_sexa(J2RAStr, J2_mnt_rar / 15. / 0.0174532925, 2, 360000);
622  char J2DecStr[64] = {0};
623  fs_sexa(J2DecStr, J2_mnt_decr / 0.0174532925, 2, 360000);
624  // IDLog("mod ra : %s, dec: %s\n", J2RAStr, J2DecStr );
625  // IDLog("PEOffset : %10.5f setting it to ZERO\n", PEOffset);
626  PEOffset = 0.;
627  // feed the result to the original variables
628  rar = J2_mnt_rar ;
629  rad = rar / 0.0174532925;
630  decr = J2_mnt_decr;
631  cameradec = decr / 0.0174532925;
632  // IDLog("mod ra rad: %8.3f (degree)\n", rad);
633  }
634  // if this is a light frame, we need a star field drawn
635  INDI::CCDChip::CCD_FRAME ftype = targetChip->getFrameType();
636 
637  std::unique_lock<std::mutex> guard(ccdBufferLock);
638 
639  // Start by clearing the frame buffer
640  memset(targetChip->getFrameBuffer(), 0, targetChip->getFrameBufferSize());
641 
642  if (ftype == INDI::CCDChip::LIGHT_FRAME)
643  {
644  AutoCNumeric locale;
645  char gsccmd[250];
646  FILE * pp;
647  int drawn = 0;
648 
649  sprintf(gsccmd, "gsc -c %8.6f %+8.6f -r %4.1f -m 0 %4.2f -n 3000",
650  range360(rad),
651  rangeDec(cameradec),
652  radius,
653  lookuplimit);
654 
655  if (!Streamer->isStreaming() || (king_gamma > 0.))
656  LOGF_DEBUG("GSC Command: %s", gsccmd);
657 
658  pp = popen(gsccmd, "r");
659  if (pp != nullptr)
660  {
661  char line[256];
662  int stars = 0;
663  int lines = 0;
664 
665  while (fgets(line, 256, pp) != nullptr)
666  {
667  // ok, lets parse this line for specifcs we want
668  char id[20];
669  char plate[6];
670  char ob[6];
671  float mag;
672  float mage;
673  float ra;
674  float dec;
675  float pose;
676  int band;
677  float dist;
678  int dir;
679  int c;
680 
681  int rc = sscanf(line, "%10s %f %f %f %f %f %d %d %4s %2s %f %d", id, &ra, &dec, &pose, &mag, &mage,
682  &band, &c, plate, ob, &dist, &dir);
683  if (rc == 12)
684  {
685  lines++;
686  //if(c==0) {
687  stars++;
688 
689  // Convert the ra/dec to standard co-ordinates
690  double sx; // standard co-ords
691  double sy; //
692  double srar; // star ra in radians
693  double sdecr; // star dec in radians;
694  double ccdx;
695  double ccdy;
696 
697  srar = ra * 0.0174532925;
698  sdecr = dec * 0.0174532925;
699  // Handbook of astronomical image processing
700  // page 253
701  // equations 9.1 and 9.2
702  // convert ra/dec to standard co-ordinates
703 
704  sx = cos(sdecr) * sin(srar - rar) /
705  (cos(decr) * cos(sdecr) * cos(srar - rar) + sin(decr) * sin(sdecr));
706  sy = (sin(decr) * cos(sdecr) * cos(srar - rar) - cos(decr) * sin(sdecr)) /
707  (cos(decr) * cos(sdecr) * cos(srar - rar) + sin(decr) * sin(sdecr));
708 
709  // now convert to pixels
710  ccdx = pa * sx + pb * sy + pc;
711  ccdy = pd * sx + pe * sy + pf;
712 
713  // Invert horizontally
714  ccdx = ccdW - ccdx;
715 
716  rc = DrawImageStar(targetChip, mag, ccdx, ccdy, exposure_time);
717  drawn += rc;
718  if (rc == 1)
719  {
720  //LOGF_DEBUG("star %s scope %6.4f %6.4f star %6.4f %6.4f ccd %6.2f %6.2f",id,rad,decPE,ra,dec,ccdx,ccdy);
721  //LOGF_DEBUG("star %s ccd %6.2f %6.2f",id,ccdx,ccdy);
722  }
723  }
724  }
725  pclose(pp);
726  }
727  else
728  {
729  LOG_ERROR("Error looking up stars, is gsc installed with appropriate environment variables set ??");
730  }
731  if (drawn == 0)
732  {
733  LOG_ERROR("Got no stars, is gsc installed with appropriate environment variables set ??");
734  }
735  }
736  //fprintf(stderr,"Got %d stars from %d lines drew %d\n",stars,lines,drawn);
737 
738  // now we need to add background sky glow, with vignetting
739  // this is essentially the same math as drawing a dim star with
740  // fwhm equivalent to the full field of view
741 
742  if (ftype == INDI::CCDChip::LIGHT_FRAME || ftype == INDI::CCDChip::FLAT_FRAME)
743  {
744  float skyflux;
745  // calculate flux from our zero point and gain values
746  float glow = skyglow;
747 
748  if (ftype == INDI::CCDChip::FLAT_FRAME)
749  {
750  // Assume flats are done with a diffuser
751  // in broad daylight, so, the sky magnitude
752  // is much brighter than at night
753  glow = skyglow / 10;
754  }
755 
756  //fprintf(stderr,"Using glow %4.2f\n",glow);
757 
758  skyflux = pow(10, ((glow - z) * k / -2.5));
759  // ok, flux represents one second now
760  // scale up linearly for exposure time
761  skyflux = skyflux * exposure_time;
762  //IDLog("SkyFlux = %g ExposureRequest %g\n",skyflux,exposure_time);
763 
764  uint16_t * pt = reinterpret_cast<uint16_t *>(targetChip->getFrameBuffer());
765 
766  nheight = targetChip->getSubH();
767  nwidth = targetChip->getSubW();
768 
769  for (int y = 0; y < nheight; y++)
770  {
771  for (int x = 0; x < nwidth; x++)
772  {
773  float dc; // distance from center
774  float fp; // flux this pixel;
775  float sx, sy;
776  float vig;
777 
778  sx = nwidth / 2 - x;
779  sy = nheight / 2 - y;
780 
781  vig = nwidth;
782  vig = vig * ImageScalex;
783  // need to make this account for actual pixel size
784  dc = std::sqrt(sx * sx * ImageScalex * ImageScalex + sy * sy * ImageScaley * ImageScaley);
785  // now we have the distance from center, in arcseconds
786  // now lets plot a gaussian falloff to the edges
787  //
788  float fa;
789  fa = exp(-2.0 * 0.7 * (dc * dc) / vig / vig);
790 
791  // get the current value
792  fp = pt[0];
793 
794  // Add the sky glow
795  fp += skyflux;
796 
797  // now scale it for the vignetting
798  fp = fa * fp;
799 
800  // clamp to limits
801  if (fp > maxval)
802  fp = maxval;
803  if (fp > maxpix)
804  maxpix = fp;
805  if (fp < minpix)
806  minpix = fp;
807  // and put it back
808  pt[0] = fp;
809  pt++;
810  }
811  }
812  }
813 
814  // Now we add some bias and read noise
815  int subX = targetChip->getSubX();
816  int subY = targetChip->getSubY();
817  int subW = targetChip->getSubW() + subX;
818  int subH = targetChip->getSubH() + subY;
819 
820  if (maxnoise > 0)
821  {
822  for (int x = subX; x < subW; x++)
823  {
824  for (int y = subY; y < subH; y++)
825  {
826  int noise;
827 
828  noise = random();
829  noise = noise % maxnoise; //
830 
831  //IDLog("noise is %d\n", noise);
832  AddToPixel(targetChip, x, y, bias + noise);
833  }
834  }
835  }
836  }
837  else
838  {
839  testvalue++;
840  if (testvalue > 255)
841  testvalue = 0;
842  uint16_t val = testvalue;
843 
844  int nbuf = targetChip->getSubW() * targetChip->getSubH();
845 
846  for (int x = 0; x < nbuf; x++)
847  {
848  *ptr = val++;
849  ptr++;
850  }
851  }
852  return 0;
853 }
854 
855 int GuideSim::DrawImageStar(INDI::CCDChip * targetChip, float mag, float x, float y, float exposure_time)
856 {
857  //float d;
858  //float r;
859  int sx, sy;
860  int drew = 0;
861  //int boxsizex = 5;
862  int boxsizey = 5;
863  float flux;
864 
865  int subX = targetChip->getSubX();
866  int subY = targetChip->getSubY();
867  int subW = targetChip->getSubW() + subX;
868  int subH = targetChip->getSubH() + subY;
869 
870  if ((x < subX) || (x > subW || (y < subY) || (y > subH)))
871  {
872  // this star is not on the ccd frame anyways
873  return 0;
874  }
875 
876  // calculate flux from our zero point and gain values
877  flux = pow(10, ((mag - z) * k / -2.5));
878 
879  // ok, flux represents one second now
880  // scale up linearly for exposure time
881  flux = flux * exposure_time;
882 
883  float qx;
884  // we need a box size that gives a radius at least 3 times fwhm
885  qx = seeing / ImageScalex;
886  qx = qx * 3;
887  //boxsizex = (int)qx;
888  //boxsizex++;
889  qx = seeing / ImageScaley;
890  qx = qx * 3;
891  boxsizey = static_cast<int>(qx);
892  boxsizey++;
893 
894  //IDLog("BoxSize %d %d\n",boxsizex,boxsizey);
895 
896  for (sy = -boxsizey; sy <= boxsizey; sy++)
897  {
898  for (sx = -boxsizey; sx <= boxsizey; sx++)
899  {
900  int rc;
901  float dc; // distance from center
902  float fp; // flux this pixel;
903 
904  // need to make this account for actual pixel size
905  dc = std::sqrt(sx * sx * ImageScalex * ImageScalex + sy * sy * ImageScaley * ImageScaley);
906  // now we have the distance from center, in arcseconds
907  // This should be gaussian, but, for now we'll just go with
908  // a simple linear function
909  float fa = exp(-2.0 * 0.7 * (dc * dc) / seeing / seeing);
910 
911  fp = fa * flux;
912 
913  if (fp < 0)
914  fp = 0;
915 
916  rc = AddToPixel(targetChip, x + sx, y + sy, fp);
917  if (rc != 0)
918  drew = 1;
919  }
920  }
921  return drew;
922 }
923 
924 int GuideSim::AddToPixel(INDI::CCDChip * targetChip, int x, int y, int val)
925 {
926  int nwidth = targetChip->getSubW();
927  int nheight = targetChip->getSubH();
928 
929  x -= targetChip->getSubX();
930  y -= targetChip->getSubY();
931 
932  int drew = 0;
933  if (x >= 0)
934  {
935  if (x < nwidth)
936  {
937  if (y >= 0)
938  {
939  if (y < nheight)
940  {
941  unsigned short * pt;
942  int newval;
943  drew++;
944 
945  pt = reinterpret_cast<uint16_t *>(targetChip->getFrameBuffer());
946 
947  pt += (y * nwidth);
948  pt += x;
949  newval = pt[0];
950  newval += val;
951  if (newval > maxval)
952  newval = maxval;
953  if (newval > maxpix)
954  maxpix = newval;
955  if (newval < minpix)
956  minpix = newval;
957  pt[0] = newval;
958  }
959  }
960  }
961  }
962  return drew;
963 }
964 
966 {
967  guideNSOffset += v / 1000.0 * GuideRate / 3600;
968  return IPS_OK;
969 }
970 
972 {
973  guideNSOffset += v / -1000.0 * GuideRate / 3600;
974  return IPS_OK;
975 }
976 
978 {
979  float c = v / 1000.0 * GuideRate;
980  c = c / 3600.0 / 15.0;
981  c = c / (cos(currentDE * 0.0174532925));
982 
983  guideWEOffset += c;
984 
985  return IPS_OK;
986 }
987 
989 {
990  float c = v / -1000.0 * GuideRate;
991  c = c / 3600.0 / 15.0;
992  c = c / (cos(currentDE * 0.0174532925));
993 
994  guideWEOffset += c;
995 
996  return IPS_OK;
997 }
998 
999 bool GuideSim::ISNewNumber(const char * dev, const char * name, double values[], char * names[], int n)
1000 {
1001  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
1002  {
1003 
1004  if (!strcmp(name, GainNP.name))
1005  {
1006  IUUpdateNumber(&GainNP, values, names, n);
1007  GainNP.s = IPS_OK;
1008  IDSetNumber(&GainNP, nullptr);
1009  return true;
1010  }
1011 
1012  if (strcmp(name, "SIMULATOR_SETTINGS") == 0)
1013  {
1014  IUUpdateNumber(&SimulatorSettingsNP, values, names, n);
1015  SimulatorSettingsNP.s = IPS_OK;
1016 
1017  // Reset our parameters now
1018  SetupParms();
1019  IDSetNumber(&SimulatorSettingsNP, nullptr);
1020 
1021  maxnoise = SimulatorSettingsN[8].value;
1022  skyglow = SimulatorSettingsN[9].value;
1023  maxval = SimulatorSettingsN[4].value;
1024  bias = SimulatorSettingsN[5].value;
1025  limitingmag = SimulatorSettingsN[7].value;
1026  saturationmag = SimulatorSettingsN[6].value;
1027  OAGoffset = SimulatorSettingsN[10].value;
1028  polarError = SimulatorSettingsN[11].value;
1029  polarDrift = SimulatorSettingsN[12].value;
1030  rotationCW = SimulatorSettingsN[13].value;
1031  // Kwiq++
1032  king_gamma = SimulatorSettingsN[14].value * 0.0174532925;
1033  king_theta = SimulatorSettingsN[15].value * 0.0174532925;
1034  TimeFactor = SimulatorSettingsN[16].value;
1035 
1036  return true;
1037  }
1038 
1039  // Record PE EQ to simulate different position in the sky than actual mount coordinate
1040  // This can be useful to simulate Periodic Error or cone error or any arbitrary error.
1041  if (!strcmp(name, EqPENP.name))
1042  {
1043  IUUpdateNumber(&EqPENP, values, names, n);
1044  EqPENP.s = IPS_OK;
1045 
1046  INDI::IEquatorialCoordinates epochPos { EqPEN[AXIS_RA].value, EqPEN[AXIS_DE].value }, J2000Pos { 0, 0 };
1047  INDI::ObservedToJ2000(&epochPos, ln_get_julian_from_sys(), &J2000Pos);
1048  currentRA = J2000Pos.rightascension;
1049  currentDE = J2000Pos.declination;
1050  usePE = true;
1051  IDSetNumber(&EqPENP, nullptr);
1052  return true;
1053  }
1054  }
1055 
1056  return INDI::CCD::ISNewNumber(dev, name, values, names, n);
1057 }
1058 
1059 bool GuideSim::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
1060 {
1061  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
1062  {
1063 
1064  if (!strcmp(name, SimulateRgbSP.name))
1065  {
1066  IUUpdateSwitch(&SimulateRgbSP, states, names, n);
1067  int index = IUFindOnSwitchIndex(&SimulateRgbSP);
1068  if (index == -1)
1069  {
1070  SimulateRgbSP.s = IPS_ALERT;
1071  LOG_INFO("Cannot determine whether RGB simulation should be switched on or off.");
1072  IDSetSwitch(&SimulateRgbSP, nullptr);
1073  return false;
1074  }
1075 
1076  simulateRGB = index == 0;
1077  setRGB(simulateRGB);
1078 
1079  SimulateRgbS[0].s = simulateRGB ? ISS_ON : ISS_OFF;
1080  SimulateRgbS[1].s = simulateRGB ? ISS_OFF : ISS_ON;
1081  SimulateRgbSP.s = IPS_OK;
1082  IDSetSwitch(&SimulateRgbSP, nullptr);
1083 
1084  return true;
1085  }
1086 
1087  if (strcmp(name, CoolerSP.name) == 0)
1088  {
1089  IUUpdateSwitch(&CoolerSP, states, names, n);
1090 
1091  if (CoolerS[0].s == ISS_ON)
1092  CoolerSP.s = IPS_BUSY;
1093  else
1094  {
1095  CoolerSP.s = IPS_IDLE;
1096  TemperatureRequest = 20;
1098  }
1099 
1100  IDSetSwitch(&CoolerSP, nullptr);
1101 
1102  return true;
1103  }
1104 
1105  if (ToggleTimeoutSP.isNameMatch(name))
1106  {
1107  ToggleTimeoutSP.update(states, names, n);
1108  ToggleTimeoutSP.setState(IPS_OK);
1109  ToggleTimeoutSP.apply();
1110  return true;
1111  }
1112  }
1113 
1114  // Nobody has claimed this, so, ignore it
1115  return INDI::CCD::ISNewSwitch(dev, name, states, names, n);
1116 }
1117 
1119 {
1120 #ifdef USE_EQUATORIAL_PE
1121  IDSnoopDevice(ActiveDeviceT[0].text, "EQUATORIAL_PE");
1122 #else
1123  IDSnoopDevice(ActiveDeviceT[ACTIVE_TELESCOPE].text, "EQUATORIAL_EOD_COORD");
1124 #endif
1125 }
1126 
1128 {
1129  // We try to snoop EQPEC first, if not found, we snoop regular EQNP
1130 #ifdef USE_EQUATORIAL_PE
1131  const char * propName = findXMLAttValu(root, "name");
1132  if (!strcmp(propName, EqPENP.name))
1133  {
1134  XMLEle * ep = nullptr;
1135  int rc_ra = -1, rc_de = -1;
1136  double newra = 0, newdec = 0;
1137 
1138  for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0))
1139  {
1140  const char * elemName = findXMLAttValu(ep, "name");
1141 
1142  if (!strcmp(elemName, "RA_PE"))
1143  rc_ra = f_scansexa(pcdataXMLEle(ep), &newra);
1144  else if (!strcmp(elemName, "DEC_PE"))
1145  rc_de = f_scansexa(pcdataXMLEle(ep), &newdec);
1146  }
1147 
1148  if (rc_ra == 0 && rc_de == 0 && ((newra != raPE) || (newdec != decPE)))
1149  {
1150  INDI::IEquatorialCoordinates epochPos { 0, 0 }, J2000Pos { 0, 0 };
1151  epochPos.ra = newra * 15.0;
1152  epochPos.dec = newdec;
1153  ln_get_equ_prec2(&epochPos, ln_get_julian_from_sys(), JD2000, &J2000Pos);
1154  raPE = J2000Pos.ra / 15.0;
1155  decPE = J2000Pos.dec;
1156  usePE = true;
1157 
1158  EqPEN[AXIS_RA].value = newra;
1159  EqPEN[AXIS_DE].value = newdec;
1160  IDSetNumber(&EqPENP, nullptr);
1161 
1162  LOGF_DEBUG("raPE %g decPE %g Snooped raPE %g decPE %g", raPE, decPE, newra, newdec);
1163 
1164  return true;
1165  }
1166  }
1167 #endif
1168 
1169  return INDI::CCD::ISSnoopDevice(root);
1170 }
1171 
1173 {
1174  // Save CCD Config
1176 
1177 
1178  // Save CCD Simulator Config
1179  IUSaveConfigNumber(fp, &SimulatorSettingsNP);
1180 
1181  // Gain
1182  IUSaveConfigNumber(fp, &GainNP);
1183 
1184  // RGB
1185  IUSaveConfigSwitch(fp, &SimulateRgbSP);
1186 
1187  return true;
1188 }
1189 
1191 {
1192  ExposureRequest = 1.0 / Streamer->getTargetExposure();
1193  pthread_mutex_lock(&condMutex);
1194  streamPredicate = 1;
1195  pthread_mutex_unlock(&condMutex);
1196  pthread_cond_signal(&cv);
1197 
1198  return true;
1199 }
1200 
1202 {
1203  pthread_mutex_lock(&condMutex);
1204  streamPredicate = 0;
1205  pthread_mutex_unlock(&condMutex);
1206  pthread_cond_signal(&cv);
1207 
1208  return true;
1209 }
1210 
1211 bool GuideSim::UpdateCCDFrame(int x, int y, int w, int h)
1212 {
1213  long bin_width = w / PrimaryCCD.getBinX();
1214  long bin_height = h / PrimaryCCD.getBinY();
1215 
1216  bin_width = bin_width - (bin_width % 2);
1217  bin_height = bin_height - (bin_height % 2);
1218 
1219  Streamer->setSize(bin_width, bin_height);
1220 
1221  return INDI::CCD::UpdateCCDFrame(x, y, w, h);
1222 }
1223 
1224 bool GuideSim::UpdateCCDBin(int hor, int ver)
1225 {
1226  if (hor == 3 || ver == 3)
1227  {
1228  LOG_ERROR("3x3 binning is not supported.");
1229  return false;
1230  }
1231 
1232  long bin_width = PrimaryCCD.getSubW() / hor;
1233  long bin_height = PrimaryCCD.getSubH() / ver;
1234 
1235  bin_width = bin_width - (bin_width % 2);
1236  bin_height = bin_height - (bin_height % 2);
1237 
1238  Streamer->setSize(bin_width, bin_height);
1239 
1240  return INDI::CCD::UpdateCCDBin(hor, ver);
1241 }
1242 
1243 void * GuideSim::streamVideoHelper(void * context)
1244 {
1245  return static_cast<GuideSim *>(context)->streamVideo();
1246 }
1247 
1249 {
1250  auto start = std::chrono::high_resolution_clock::now();
1251  auto finish = std::chrono::high_resolution_clock::now();
1252 
1253  while (true)
1254  {
1255  pthread_mutex_lock(&condMutex);
1256 
1257  while (streamPredicate == 0)
1258  {
1259  pthread_cond_wait(&cv, &condMutex);
1260  ExposureRequest = Streamer->getTargetExposure();
1261  }
1262 
1263  if (terminateThread)
1264  break;
1265 
1266  // release condMutex
1267  pthread_mutex_unlock(&condMutex);
1268 
1269 
1270  // 16 bit
1272 
1273  PrimaryCCD.binFrame();
1274 
1275  finish = std::chrono::high_resolution_clock::now();
1276  std::chrono::duration<double> elapsed = finish - start;
1277 
1278  if (elapsed.count() < ExposureRequest)
1279  usleep(fabs(ExposureRequest - elapsed.count()) * 1e6);
1280 
1281  uint32_t size = PrimaryCCD.getFrameBufferSize() / (PrimaryCCD.getBinX() * PrimaryCCD.getBinY());
1282  Streamer->newFrame(PrimaryCCD.getFrameBuffer(), size);
1283 
1284  start = std::chrono::high_resolution_clock::now();
1285  }
1286 
1287  pthread_mutex_unlock(&condMutex);
1288  return nullptr;
1289 }
1290 
1291 void GuideSim::addFITSKeywords(INDI::CCDChip *targetChip, std::vector<INDI::FITSRecord> &fitsKeywords)
1292 {
1293  INDI::CCD::addFITSKeywords(targetChip, fitsKeywords);
1294 
1295  fitsKeywords.push_back({"GAIN", GainN[0].value, 3, "Gain"});
1296 }
The GuideSim class provides an advanced simulator for a CCD that includes a dedicated on-board guide ...
virtual IPState GuideNorth(uint32_t) override
Guide northward for ms milliseconds.
virtual bool UpdateCCDFrame(int x, int y, int w, int h) override
CCD calls this function when CCD Frame dimension needs to be updated in the hardware....
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save configuration items in XML file.
virtual IPState GuideEast(uint32_t) override
Guide easward for ms milliseconds.
void TimerHit() override
Callback function to be called once SetTimer duration elapses.
virtual bool UpdateCCDBin(int hor, int ver) override
CCD calls this function when CCD Binning needs to be updated in the hardware. Derived classes should ...
int DrawCcdFrame(INDI::CCDChip *targetChip)
virtual void addFITSKeywords(INDI::CCDChip *targetChip, std::vector< INDI::FITSRecord > &fitsKeywords) override
Generate FITS keywords that will be added to FIST/XISF file.
const char * getDefaultName() override
virtual bool ISSnoopDevice(XMLEle *root) override
Process a snoop event from INDI server. This function is called when a snooped property is updated in...
bool Disconnect() override
Disconnect from device.
int AddToPixel(INDI::CCDChip *targetChip, int, int, int)
void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
bool AbortExposure() override
Abort ongoing exposure.
virtual bool StartStreaming() override
StartStreaming Start live video streaming.
virtual bool StopStreaming() override
StopStreaming Stop live video streaming.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
void * streamVideo()
virtual IPState GuideSouth(uint32_t) override
Guide southward for ms milliseconds.
static void * streamVideoHelper(void *context)
bool StartExposure(float duration) override
Start exposing primary CCD chip.
virtual void activeDevicesUpdated() override
activeDevicesUpdated Inform children that ActiveDevices property was updated so they can snoop on the...
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
int DrawImageStar(INDI::CCDChip *targetChip, float, float, float, float ExposureTime)
bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
virtual IPState GuideWest(uint32_t) override
Guide westward for ms milliseconds.
virtual int SetTemperature(double temperature) override
Set CCD temperature.
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
The CCDChip class provides functionality of a CCD Chip within a CCD.
Definition: indiccdchip.h:35
uint8_t * getFrameBuffer()
getFrameBuffer Get raw frame buffer of the CCD chip.
Definition: indiccdchip.h:209
int getSubX() const
getSubX Get the starting left coordinates (X) of the frame.
Definition: indiccdchip.h:95
int getSubY() const
getSubY Get the starting top coordinates (Y) of the frame.
Definition: indiccdchip.h:104
float getPixelSizeY() const
getPixelSizeY Get vertical pixel size in microns.
Definition: indiccdchip.h:158
void setExposureDuration(double duration)
setExposureDuration Set desired CCD frame exposure duration for next exposure. You must call this fun...
int getSubH() const
getSubH Get the height of the frame
Definition: indiccdchip.h:122
int getBPP() const
getBPP Get CCD Chip depth (bits per pixel).
Definition: indiccdchip.h:167
CCD_FRAME getFrameType() const
isInterlaced
Definition: indiccdchip.h:249
int getBinY() const
getBinY Get vertical binning of the CCD chip.
Definition: indiccdchip.h:140
float getPixelSizeX() const
getPixelSizeX Get horizontal pixel size in microns.
Definition: indiccdchip.h:149
int getXRes() const
getXRes Get the horizontal resolution in pixels of the CCD Chip.
Definition: indiccdchip.h:77
int getBinX() const
getBinX Get horizontal binning of the CCD chip.
Definition: indiccdchip.h:131
void setExposureLeft(double duration)
setExposureLeft Update exposure time left. Inform the client of the new exposure time left value.
int getSubW() const
getSubW Get the width of the frame
Definition: indiccdchip.h:113
void setFrameBufferSize(uint32_t nbuf, bool allocMem=true)
setFrameBufferSize Set desired frame buffer size. The function will allocate memory accordingly....
int getFrameBufferSize() const
getFrameBufferSize Get allocated frame buffer size to hold the CCD image frame.
Definition: indiccdchip.h:176
int getYRes() const
Get the vertical resolution in pixels of the CCD Chip.
Definition: indiccdchip.h:86
void binFrame()
binFrame Perform software binning on the CCD frame. Only use this function if hardware binning is not...
double Longitude
Definition: indiccd.h:610
CCDChip PrimaryCCD
Definition: indiccd.h:629
uint32_t GetCCDCapability() const
GetCCDCapability returns the CCD capabilities.
Definition: indiccd.h:170
@ CCD_CAN_SUBFRAME
Definition: indiccd.h:124
@ CCD_HAS_WEB_SOCKET
Definition: indiccd.h:132
@ CCD_CAN_ABORT
Definition: indiccd.h:125
@ CCD_HAS_SHUTTER
Definition: indiccd.h:128
@ CCD_HAS_BAYER
Definition: indiccd.h:130
@ CCD_CAN_BIN
Definition: indiccd.h:123
@ CCD_HAS_STREAMING
Definition: indiccd.h:131
@ CCD_HAS_ST4_PORT
Definition: indiccd.h:127
bool HasGuideHead()
Definition: indiccd.h:208
std::unique_ptr< StreamManager > Streamer
Definition: indiccd.h:627
@ ACTIVE_TELESCOPE
Definition: indiccd.h:667
bool HasCooler()
Definition: indiccd.h:232
IText BayerT[3]
Definition: indiccd.h:694
bool InExposure
Definition: indiccd.h:582
virtual bool UpdateCCDFrame(int x, int y, int w, int h)
CCD calls this function when CCD Frame dimension needs to be updated in the hardware....
Definition: indiccd.cpp:1891
virtual void addFITSKeywords(CCDChip *targetChip, std::vector< FITSRecord > &fitsKeywords)
Generate FITS keywords that will be added to FIST/XISF file.
Definition: indiccd.cpp:1940
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: indiccd.cpp:1451
virtual bool ExposureComplete(CCDChip *targetChip)
Uploads target Chip exposed buffer as FITS to the client. Dervied classes should class this function ...
Definition: indiccd.cpp:2228
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
Definition: indiccd.cpp:516
std::mutex ccdBufferLock
Definition: indiccd.h:620
INDI::PropertyNumber ScopeInfoNP
Definition: indiccd.h:728
INumber TemperatureN[1]
Definition: indiccd.h:678
virtual bool UpdateCCDBin(int hor, int ver)
CCD calls this function when CCD Binning needs to be updated in the hardware. Derived classes should ...
Definition: indiccd.cpp:1904
virtual bool initProperties() override
Initilize properties initial state and value. The child class must implement this function.
Definition: indiccd.cpp:152
int pierSide
Definition: indiccd.h:570
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition: indiccd.cpp:528
virtual void addCaptureFormat(const CaptureFormat &format)
addCaptureFormat Add a supported camera native capture format (e.g. Mono, Bayer8.....
Definition: indiccd.cpp:3033
virtual void SetGuiderParams(int x, int y, int bpp, float xf, float yf)
Setup CCD paramters for guide head CCD. Child classes call this function to update CCD parameters.
Definition: indiccd.cpp:2752
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save configuration items in XML file.
Definition: indiccd.cpp:2762
virtual bool ISSnoopDevice(XMLEle *root) override
Process a snoop event from INDI server. This function is called when a snooped property is updated in...
Definition: indiccd.cpp:728
void SetCCDCapability(uint32_t cap)
SetCCDCapability Set the CCD capabilities. Al fields must be initialized.
Definition: indiccd.cpp:138
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: indiccd.cpp:1070
INumberVectorProperty TemperatureNP
TemperatureNP Camera Temperature in Celcius.
Definition: indiccd.h:677
IText ActiveDeviceT[5]
Definition: indiccd.h:664
CCDChip GuideCCD
Definition: indiccd.h:630
double RA
Definition: indiccd.h:567
virtual void SetCCDParams(int x, int y, int bpp, float xf, float yf)
Setup CCD paramters for primary CCD. Child classes call this function to update CCD parameters.
Definition: indiccd.cpp:2742
@ FocalLength
Definition: indiccd.h:731
double snoopedFocalLength
Definition: indiccd.h:581
double Dec
Definition: indiccd.h:567
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
void setDriverInterface(uint16_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
uint16_t getDriverInterface() const
void addDebugControl()
Add Debug control to the driver.
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
bool isNameMatch(const char *otherName) const
bool update(const ISState states[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
double max(void)
double min(void)
double ra
double dec
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_OFF
Definition: indiapi.h:151
@ ISS_ON
Definition: indiapi.h:152
@ IP_RW
Definition: indiapi.h:186
@ IP_WO
Definition: indiapi.h:185
IPState
Property state.
Definition: indiapi.h:160
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
@ ISR_1OFMANY
Definition: indiapi.h:173
@ AXIS_DE
Definition: indibasetypes.h:36
@ AXIS_RA
Definition: indibasetypes.h:35
@ INDI_MONO
Definition: indibasetypes.h:71
int f_scansexa(const char *str0, double *dp)
convert sexagesimal string str AxBxC to double. x can be anything non-numeric. Any missing A,...
Definition: indicom.c:205
double range360(double r)
range360 Limits an angle to be between 0-360 degrees.
Definition: indicom.c:1245
double rangeDec(double decdegrees)
rangeDec Limits declination value to be in -90 to 90 range.
Definition: indicom.c:1255
int fs_sexa(char *out, double a, int w, int fracbase)
Converts a sexagesimal number to a string. sprint the variable a in sexagesimal format into out[].
Definition: indicom.c:141
double get_local_hour_angle(double sideral_time, double ra)
get_local_hour_angle Returns local hour angle of an object
Definition: indicom.c:1293
Implementations for common driver routines.
double get_local_sidereal_time(double longitude)
get_local_sidereal_time Returns local sideral time given longitude and system clock.
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indidevapi.c:25
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:272
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indidevapi.c:128
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition: indidevapi.c:36
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indidevapi.c:15
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
Definition: indidevapi.c:158
void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
Definition: indidevapi.c:180
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:235
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:1308
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
Definition: indidriver.c:1231
void IDSnoopDevice(const char *snooped_device, const char *snooped_property)
Function a Driver calls to snoop on another Device. Snooped messages will then arrive via ISSnoopDevi...
Definition: indidriver.c:143
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
Definition: indidriver.c:1362
#define LOGF_DEBUG(fmt,...)
Definition: indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition: indilogger.h:72
#define LOG_INFO(txt)
Definition: indilogger.h:74
#define DEBUGF(priority, msg,...)
Definition: indilogger.h:57
const char * findXMLAttValu(XMLEle *ep, const char *name)
Find an XML element's attribute value.
Definition: lilxml.cpp:644
char * pcdataXMLEle(XMLEle *ep)
Return the pcdata of an XML element.
Definition: lilxml.cpp:606
XMLEle * nextXMLEle(XMLEle *ep, int init)
Iterate an XML element for a list of nesetd XML elements.
Definition: lilxml.cpp:555
void ObservedToJ2000(IEquatorialCoordinates *observed, double jd, IEquatorialCoordinates *J2000pos)
ObservedToJ2000 converts an observed position to a J2000 catalogue position removes aberration,...
Definition: libastro.cpp:50
char name[MAXINDINAME]
Definition: indiapi.h:323
char name[MAXINDINAME]
Definition: indiapi.h:371