Instrument Neutral Distributed Interface INDI 2.2.5
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
20/************************************************************************************
21This simulator is useful for simulating the images sent to a guiding program such as the
22Ekos guider or PHD2.
23
24There are many adjustments to the image needing comments. An important one is:
25
26 Seeing: make this reasonably large so that single pixels stars aren't generated and the
27 guider can track sub-pixel. 5 is a good start.
28
29Use the below INDI properties to simulate imperfections in real-world mounts.
30
31 RA drift: Simulates a drift in arcseconds/second of the RA angle, e.g. due to bad tracking
32 or refraction
33 DEC drift: Similar, useful to simulate polar alignment error, etc.
34
35 Periodic error period (secs):
36 Periodic error maxval (arcsecs): These add a sinusoid of the given period, going from
37 -maxval to maxval arcseconds onto the RA.
38
39 Max random RA add (arcsecs):
40 Max random DEC add (arcsecs): These add random RA or DEC offsets each frame to the RA or
41 dec values. The random values are linearly distributed between -value to + value.
42
43Another interesting guide hardware simulation is found in the telescope simulator,
44which can simulate backlash to the guiding pulses. See its Dec Backlash parameter.
45************************************************************************************/
46
47#include "guide_simulator.h"
48#include "indicom.h"
50
51#include <libnova/julian_day.h>
52#include <libastro.h>
53
54#include <cmath>
55#include <unistd.h>
56
57static pthread_cond_t cv = PTHREAD_COND_INITIALIZER;
58static pthread_mutex_t condMutex = PTHREAD_MUTEX_INITIALIZER;
59
60constexpr double DEGREES_TO_RADIANS = 0.0174532925;
61
62// We declare an auto pointer to GuideSim.
63static std::unique_ptr<GuideSim> ccd(new GuideSim());
64
66{
69
70 m_StreamPredicate = 0;
71 m_TerminateThread = false;
72}
73
75{
76 int nbuf;
77 SetCCDParams(SimulatorSettingsNP[SIM_XRES].getValue(), SimulatorSettingsNP[SIM_YRES].getValue(), 16,
78 SimulatorSettingsNP[SIM_XSIZE].getValue(),
79 SimulatorSettingsNP[SIM_YSIZE].getValue());
80
81 // Random number added to each pixel up to this value.
82 m_MaxNoise = SimulatorSettingsNP[SIM_NOISE].getValue();
83 // A "glow" added to all frames, stronger at the center and less so further from the center.
84 m_SkyGlow = SimulatorSettingsNP[SIM_SKYGLOW].getValue();
85 // Clipping ADU value. Nothing is allowed to get brighter.
86 m_MaxVal = SimulatorSettingsNP[SIM_MAXVAL].getValue();
87 // Fixed bias added to each pixel. Useful when negative and half of m_MaxNoise.
88 // This only gets added if m_MaxNoise is > 0.
89 m_Bias = SimulatorSettingsNP[SIM_BIAS].getValue();
90 // A saturation mag star saturates in one second
91 // and a limiting mag produces a one adu level in one second
92 m_LimitingMag = SimulatorSettingsNP[SIM_LIMITINGMAG].getValue();
93 m_SaturationMag = SimulatorSettingsNP[SIM_SATURATION].getValue();
94 // offset the dec (in minutes) by the guide head offset
95 m_OAGoffset = SimulatorSettingsNP[SIM_OAGOFFSET].getValue();
96 // Doesn't make much sense to me.
97 // The dec is offset by (m_PolarError * polarDrift * cos(dec)) / 3.81
98 // This (locally at least) is a constant offset to dec, so won't show
99 // up much in guiding error.
100 m_PolarError = SimulatorSettingsNP[SIM_POLAR].getValue();
101 m_PolarDrift = SimulatorSettingsNP[SIM_POLARDRIFT].getValue();
102 // Kwiq++
103 m_KingGamma = SimulatorSettingsNP[SIM_KING_GAMMA].getValue() * DEGREES_TO_RADIANS;
104 m_KingTheta = SimulatorSettingsNP[SIM_KING_THETA].getValue() * DEGREES_TO_RADIANS;
105 // Reduce the simulator "wait time" for exposures by this factor.
106 // That is, we can say exposure duration = 10s, and m_TimeFactor = 0.05
107 // and the system will simulate a 10s exposure, but it will only take 0.5 seconds.
108 m_TimeFactor = SimulatorSettingsNP[SIM_TIME_FACTOR].getValue();
109 // This is the rotation offset of the simulated camera respective to North.
110 // Because the simulated star field is calculated with their RA/DEC-coordinates
111 // (see DrawCcdFrame()) the origin angle of star field points north. So this value
112 // for EQ mounts normally simulate a certain camera offset and is a constant.
113 // For ALTAZ-mount this variable is altered consecutively by the value of the parallactic
114 // angle (transfered through a signal from KStars/skymapdrawabstract.cpp) and this way used
115 // to simulate the deviation of the camera orientation from N.
116 m_RotationOffset = SimulatorSettingsNP[SIM_ROTATION].getValue();
117 m_Seeing = SimulatorSettingsNP[SIM_SEEING].getValue();
118 m_RaTimeDrift = SimulatorSettingsNP[SIM_RA_DRIFT].getValue();
119 m_DecTimeDrift = SimulatorSettingsNP[SIM_DEC_DRIFT].getValue();
120 m_RaRand = SimulatorSettingsNP[SIM_RA_RAND].getValue();
121 m_DecRand = SimulatorSettingsNP[SIM_DEC_RAND].getValue();
122 m_PEPeriod = SimulatorSettingsNP[SIM_PE_PERIOD].getValue();
123 m_PEMax = SimulatorSettingsNP[SIM_PE_MAX].getValue();
124 m_TemperatureRequest = SimulatorSettingsNP[SIM_TEMPERATURE].getValue();
125 TemperatureNP[0].setValue(m_TemperatureRequest);
126
128 //nbuf += 512;
130
131 Streamer->setPixelFormat(INDI_MONO, 16);
133
134 return true;
135}
136
138{
139 m_StreamPredicate = 0;
140 m_TerminateThread = false;
141 pthread_create(&m_PrimaryThread, nullptr, &streamVideoHelper, this);
143 return true;
144}
145
147{
148 pthread_mutex_lock(&condMutex);
149 m_StreamPredicate = 1;
150 m_TerminateThread = true;
151 pthread_cond_signal(&cv);
152 pthread_mutex_unlock(&condMutex);
153
154 return true;
155}
156
158{
159 return "Guide Simulator";
160}
161
163{
164 // Most hardware layers wont actually have indi properties defined
165 // but the simulators are a special case
167
169
170 CaptureFormat format = {"INDI_MONO", "Mono", 16, true};
171 addCaptureFormat(format);
172
173 SimulatorSettingsNP[SIM_XRES].fill("SIM_XRES", "X resolution", "%4.0f", 0, 8192, 0, 1280);
174 SimulatorSettingsNP[SIM_YRES].fill("SIM_YRES", "Y resolution", "%4.0f", 0, 8192, 0, 1024);
175 SimulatorSettingsNP[SIM_XSIZE].fill("SIM_XSIZE", "X Pixel Size", "%4.2f", 0, 60, 0, 2.4);
176 SimulatorSettingsNP[SIM_YSIZE].fill("SIM_YSIZE", "Y Pixel Size", "%4.2f", 0, 60, 0, 2.4);
177 SimulatorSettingsNP[SIM_MAXVAL].fill("SIM_MAXVAL", "Maximum ADU", "%4.0f", 0, 65000, 0, 65000);
178 SimulatorSettingsNP[SIM_BIAS].fill("SIM_BIAS", "Bias", "%4.0f", 0, 6000, 0, 10);
179 SimulatorSettingsNP[SIM_SATURATION].fill("SIM_SATURATION", "Saturation Mag", "%4.1f", 0, 20, 0, 1.0);
180 SimulatorSettingsNP[SIM_LIMITINGMAG].fill("SIM_LIMITINGMAG", "Limiting Mag", "%4.1f", 0, 20, 0, 17.0);
181 SimulatorSettingsNP[SIM_NOISE].fill("SIM_NOISE", "Noise", "%4.0f", 0, 6000, 0, 10);
182 SimulatorSettingsNP[SIM_SKYGLOW].fill("SIM_SKYGLOW", "Sky Glow (magnitudes)", "%4.1f", 0, 6000, 0, 19.5);
183 SimulatorSettingsNP[SIM_OAGOFFSET].fill("SIM_OAGOFFSET", "Oag Offset (arcminutes)", "%4.1f", 0, 6000, 0, 0);
184 SimulatorSettingsNP[SIM_POLAR].fill("SIM_POLAR", "PAE (arcminutes)", "%4.3f", -600, 600, 0,
185 0); /* PAE = Polar Alignment Error */
186 SimulatorSettingsNP[SIM_POLARDRIFT].fill("SIM_POLARDRIFT", "PAE Drift (minutes)", "%4.3f", 0, 6000, 0, 0);
187 SimulatorSettingsNP[SIM_ROTATION].fill("SIM_ROTATION", "Rotation Offset", "%4.1f", -180, 180, 0, 0);
188 SimulatorSettingsNP[SIM_KING_GAMMA].fill("SIM_KING_GAMMA", "(CP,TCP), deg", "%4.1f", 0, 10, 0, 0);
189 SimulatorSettingsNP[SIM_KING_THETA].fill("SIM_KING_THETA", "hour hangle, deg", "%4.1f", 0, 360, 0, 0);
190 SimulatorSettingsNP[SIM_TIME_FACTOR].fill("SIM_TIME_FACTOR", "Time Factor (x)", "%.2f", 0.01, 100, 0, 1);
191
192 SimulatorSettingsNP[SIM_SEEING].fill("SIM_SEEING", "Seeing (a-s)", "%4.1f", 0, 20, 0, 6);
193 SimulatorSettingsNP[SIM_RA_DRIFT].fill("SIM_RA_DRIFT", "RA drift (a-s/second)", "%5.3f", -2, 2, 0, 0.05);
194 SimulatorSettingsNP[SIM_DEC_DRIFT].fill("SIM_DEC_DRIFT", "DEC drift (a-s/second)", "%5.3f", -2, 2, 0, -0.05);
195 SimulatorSettingsNP[SIM_RA_RAND].fill("SIM_RA_RAND", "Max random RA add (a-s)", "%5.3f", -2, 2, 0, 0.2);
196 SimulatorSettingsNP[SIM_DEC_RAND].fill("SIM_DEC_RAND", "Max random DEC add (a-s)", "%5.3f", -2, 2, 0,
197 0.3);
198 SimulatorSettingsNP[SIM_PE_PERIOD].fill("SIM_PE_PERIOD", "Periodic error period (secs)", "%4.1f", 0, 1000, 0, 120);
199 SimulatorSettingsNP[SIM_PE_MAX].fill("SIM_PE_MAX", "Periodic error maxval (a-s)", "%4.1f", 0, 100, 0, 3);
200 SimulatorSettingsNP[SIM_TEMPERATURE].fill("SIM_TEMPERATURE", "Temperature (°C)", "%4.1f", -100, 100, 0, 25);
201
202 SimulatorSettingsNP.fill(getDeviceName(), "SIMULATOR_SETTINGS",
203 "Config", SIMULATOR_TAB, IP_RW, 60, IPS_IDLE);
204 // load() is important to fill all editfields with saved values also, so ISNewNumber() of one field
205 // doesn't update the other fields of the group with the "old" contents.
206 SimulatorSettingsNP.load();
207 // Sim Tests
208 SimTestsSP[SIM_TESTS_RGB].fill("RGB", "RGB", ISS_OFF);
209 SimTestsSP[SIM_TESTS_CRASH].fill("CRASH", "Crash", ISS_OFF);
210 SimTestsSP[SIM_TESTS_TIMEOUT].fill("TIMEOUT", "Timeout", ISS_OFF);
211 SimTestsSP.fill(getDeviceName(), "SIM_TESTS", "Sim Tests", SIMULATOR_TAB, IP_RW,
212 ISR_NOFMANY, 60, IPS_IDLE);
213
214 // CCD Gain
215 GainNP[0].fill("GAIN", "Gain", "%.f", 0, 100, 10, 50);
216 GainNP.fill(getDeviceName(), "CCD_GAIN", "Gain", MAIN_CONTROL_TAB, IP_RW, 60, IPS_IDLE);
217
218 EqPENP[RA_PE].fill("RA_PE", "RA (hh:mm:ss)", "%010.6m", 0, 24, 0, 0);
219 EqPENP[DEC_PE].fill("DEC_PE", "DEC (dd:mm:ss)", "%010.6m", -90, 90, 0, 0);
220 EqPENP.fill(getDeviceName(), "EQUATORIAL_PE", "EQ PE", SIMULATOR_TAB, IP_RW, 60,
221 IPS_IDLE);
222
223#ifdef USE_EQUATORIAL_PE
224 IDSnoopDevice(ActiveDeviceTP[0].getText(), "EQUATORIAL_PE");
225#else
226 IDSnoopDevice(ActiveDeviceTP[ACTIVE_TELESCOPE].getText(), "EQUATORIAL_EOD_COORD");
227#endif
228
230 TemperatureNP[0].setValue(25);
231
232 uint32_t cap = 0;
233
234 cap |= CCD_CAN_ABORT;
235 cap |= CCD_CAN_BIN;
236 cap |= CCD_CAN_SUBFRAME;
237 cap |= CCD_HAS_SHUTTER;
238 cap |= CCD_HAS_ST4_PORT;
239 cap |= CCD_HAS_STREAMING;
240
241 SetCCDCapability(cap);
242
243 // This should be called after the initial SetCCDCapability (above)
244 // as it modifies the capabilities.
245 setRGB(m_SimulateRGB);
246
248
250
251 return true;
252}
253
254void GuideSim::setRGB(bool onOff)
255{
256 if (onOff)
257 {
259 BayerTP[CFA_OFFSET_X].setText("0");
260 BayerTP[CFA_OFFSET_Y].setText("0");
261 BayerTP[CFA_TYPE].setText("RGGB");
262 }
263 else
264 {
266 }
267}
268
269void GuideSim::ISGetProperties(const char * dev)
270{
272
273 defineProperty(SimulatorSettingsNP);
274 defineProperty(EqPENP);
275 defineProperty(SimTestsSP);
276}
277
279{
281
282 m_Clock.updateProperties(this, isConnected());
283
284 if (isConnected())
285 {
287 defineProperty(GainNP);
288
289 SetupParms();
290
291 if (HasGuideHead())
292 {
293 SetGuiderParams(500, 290, 16, 9.8, 12.6);
295 }
296 }
297 else
298 {
300 deleteProperty(GainNP);
301 }
302
303 return true;
304}
305
306bool GuideSim::StartExposure(float duration)
307{
308 // for the simulator, we can just draw the frame now
309 // and it will get returned at the right time
310 // by the timer routines
311 m_AbortPrimaryFrame = false;
312 m_ExposureRequest = duration;
313
315 gettimeofday(&m_ExpStart, nullptr);
316 // Leave the proper time showing for the draw routines
318 // Now compress the actual wait time
319 m_ExposureRequest = duration * m_TimeFactor;
320 InExposure = true;
321
322 return true;
323}
324
326{
327 if (!InExposure)
328 return true;
329
330 m_AbortPrimaryFrame = true;
331
332 return true;
333}
334
336{
337 uint32_t nextTimer = getCurrentPollingPeriod();
338
339 // No need to reset timer if we are not connected anymore
340 if (!isConnected())
341 return;
342
343 if (InExposure && !m_SimulateTimeout)
344 {
345 if (m_AbortPrimaryFrame)
346 {
347 InExposure = false;
348 m_AbortPrimaryFrame = false;
349 }
350 else
351 {
352 float timeleft;
353 timeleft = CalcTimeLeft(m_ExpStart, m_ExposureRequest);
354
355 //IDLog("CCD Exposure left: %g - Request: %g\n", timeleft, m_ExposureRequest);
356 if (timeleft < 0)
357 timeleft = 0;
358
359 PrimaryCCD.setExposureLeft(timeleft);
360
361 if (timeleft < 1.0)
362 {
363 if (timeleft <= 0.001)
364 {
365 InExposure = false;
368 }
369 else
370 {
371 // set a shorter timer
372 nextTimer = timeleft * 1000;
373 }
374 }
375 }
376 }
377
378 SetTimer(nextTimer);
379}
380
382{
383 double exposure_time;
384
385 uint16_t * ptr = reinterpret_cast<uint16_t *>(targetChip->getFrameBuffer());
386
387 if (Streamer->isStreaming())
388 exposure_time = (m_ExposureRequest < 1) ? (m_ExposureRequest * 100) : m_ExposureRequest * 2;
389 else
390 exposure_time = m_ExposureRequest;
391
392 exposure_time *= (1 + sqrt(GainNP[0].getValue()));
393
394 auto targetFocalLength = ScopeInfoNP[FOCAL_LENGTH].getValue() > 0 ? ScopeInfoNP[FOCAL_LENGTH].getValue() :
396
397 if (m_ShowStarField)
398 {
399 double PEOffset = 0;
400 double rad; // telescope RA in degrees
401 double rar; // telescope RA in radians
402 double decr; // telescope Dec in radians
403
404 time_t now;
405 time(&now);
406 if (!m_RunStartInitialized || difftime(now, m_LastSim) > 30)
407 {
408 m_RunStartInitialized = true;
409 time(&m_RunStart);
410 }
411 m_LastSim = now;
412
413 const double timesince = difftime(now, m_RunStart);
414
415 if (m_PEPeriod != 0 && m_PEMax != 0)
416 {
417 const double PESpot = 2.0 * 3.14159 * timesince / m_PEPeriod;
418 PEOffset = m_PEMax * std::sin(PESpot) / 3600.0;
419 }
420
421 // Camera rotation: manual offset + snooped rotator + pier flip
422 m_RotationOffset = SimulatorSettingsNP[SIM_ROTATION].getValue();
423 double theta = m_RotationOffset;
424 if (!std::isnan(RotatorAngle))
425 theta += RotatorAngle;
426 if (pierSide == 1)
427 theta -= 180;
428 theta = range360(theta);
429 LOGF_DEBUG("Rotator Angle: %f, Camera Rotation: %f", RotatorAngle, theta);
430
431#ifdef USE_EQUATORIAL_PE
432 if (m_UsePE)
433 {
436 }
437 else
438 {
439 static bool s_warnedNoSnoop = false;
440 if (!s_warnedNoSnoop)
441 {
442 LOG_WARN("EQUATORIAL_PE not yet snooped -- rendering at raw EQUATORIAL_EOD_COORD (mount errors not active)");
443 s_warnedNoSnoop = true;
444 }
445#endif
446 m_CurrentRA = RA;
448
449 if (std::isnan(m_CurrentRA))
450 {
451 m_CurrentRA = 0;
452 m_CurrentDEC = 0;
453 }
454
455 INDI::IEquatorialCoordinates epochPos { m_CurrentRA, m_CurrentDEC }, J2000Pos { 0, 0 };
456 INDI::ObservedToJ2000(&epochPos, m_Clock.julianDate(), &J2000Pos);
457 m_CurrentRA = J2000Pos.rightascension;
458 m_CurrentDEC = J2000Pos.declination;
461#ifdef USE_EQUATORIAL_PE
462 }
463#endif
464
465 // Linear drift in degrees (drift rate in arcsec/sec * elapsed seconds / 3600)
466 const float raTDrift = timesince * m_RaTimeDrift / 3600.0f;
467 const float decTDrift = timesince * m_DecTimeDrift / 3600.0f;
468
469 // Random offsets for RA and DEC in degrees
470 double raRandomDrift = 0;
471 double decRandomDrift = 0;
472 constexpr int scale = 1000000;
473 if (m_RaRand > 0)
474 {
475 const int raScale = scale * m_RaRand;
476 raRandomDrift = ((random() % (2 * raScale)) - raScale) / (3600.0 * scale);
477 }
478 if (m_DecRand > 0)
479 {
480 const int decScale = scale * m_DecRand;
481 decRandomDrift = ((random() % (2 * decScale)) - decScale) / (3600.0 * scale);
482 }
483
484 rad = m_CurrentRA * 15.0 + PEOffset + raTDrift + raRandomDrift;
485 rar = rad * DEGREES_TO_RADIANS;
486
487 float cameradec = m_CurrentDEC + m_OAGoffset / 60.0f;
488 decr = cameradec * DEGREES_TO_RADIANS;
489
490 const double decDrift = (m_PolarDrift * m_PolarError * cos(decr)) / 3.81;
491 decr += (decRandomDrift + decTDrift + decDrift / 3600.0) * DEGREES_TO_RADIANS;
492 cameradec = static_cast<float>(decr / DEGREES_TO_RADIANS);
493
494 if (m_KingGamma > 0.)
495 {
496 // King cone-error transform -- see E.S. King (1902AnHar..41..153K)
497 // Differential correction to RA and Dec for alt-az polar alignment error.
498 double J2decr = m_CurrentDEC * DEGREES_TO_RADIANS;
499 double sid = m_Clock.localSiderealTime(this->Longitude);
500 double JnHAr = get_local_hour_angle(sid, RA) * 15.0 * DEGREES_TO_RADIANS;
501
502 double J2_mnt_d_rar = m_KingGamma * sin(J2decr) * sin(JnHAr - m_KingTheta) / cos(J2decr);
503 double J2_mnt_rar = rar - J2_mnt_d_rar;
504
505 double J2_mnt_d_decr = m_KingGamma * cos(JnHAr - m_KingTheta);
506 double J2_mnt_decr = decr + J2_mnt_d_decr;
507
508 if (J2_mnt_decr > M_PI / 2.)
509 {
510 J2_mnt_decr = M_PI / 2. - (J2_mnt_decr - M_PI / 2.);
511 J2_mnt_rar -= M_PI;
512 }
513 J2_mnt_rar = fmod(J2_mnt_rar, 2. * M_PI);
514
515 PEOffset = 0.;
516 rar = J2_mnt_rar;
517 rad = rar / DEGREES_TO_RADIANS;
518 decr = J2_mnt_decr;
519 cameradec = static_cast<float>(decr / DEGREES_TO_RADIANS);
520 }
521
522 // King mode needs a wider GSC search to ensure the plate solver has enough stars.
523 double const kingMinRadius = (m_KingGamma > 0.) ? 60.0 : 0.0;
524
525 INDI::CCDChip::CCD_FRAME ftype = targetChip->getFrameType();
526 bool const isLight = (ftype == INDI::CCDChip::LIGHT_FRAME);
527 bool const isFlat = (ftype == INDI::CCDChip::FLAT_FRAME);
528
529 RenderConfig cfg;
530 cfg.maxVal = m_MaxVal;
531 cfg.limitingMag = m_LimitingMag;
532 cfg.saturationMag = m_SaturationMag;
533 cfg.seeing = m_Seeing;
534 // Guide sim sky glow: no extra boost for light frames (guide images are typically short)
535 cfg.skyGlow = isLight ? m_SkyGlow : m_SkyGlow / 10.0f;
536 m_Renderer.setConfig(cfg);
537
538 std::unique_lock<std::mutex> guard(ccdBufferLock);
539
540 if (isLight || isFlat)
541 {
542 int drawn = m_Renderer.renderFrame(targetChip, rad, cameradec,
543 targetFocalLength, theta,
544 static_cast<float>(exposure_time), isLight,
545 kingMinRadius);
546 if (isLight && drawn < 0)
547 LOG_ERROR("Error launching gsc, is it installed with appropriate environment variables set?");
548 else if (isLight && drawn == 0)
549 LOG_WARN("No stars found in field -- check gsc catalog coverage for this region.");
550 }
551 else
552 {
553 memset(targetChip->getFrameBuffer(), 0, targetChip->getFrameBufferSize());
554 }
555
556 m_Renderer.applyReadoutNoise(targetChip, m_Bias, m_MaxNoise);
557 }
558 else
559 {
560 m_TestValue++;
561 if (m_TestValue > 255)
562 m_TestValue = 0;
563 uint16_t val = m_TestValue;
564
565 int nbuf = targetChip->getSubW() * targetChip->getSubH();
566
567 for (int x = 0; x < nbuf; x++)
568 {
569 *ptr = val++;
570 ptr++;
571 }
572 }
573 return 0;
574}
575
576float GuideSim::CalcTimeLeft(timeval start, float req)
577{
578 struct timeval now
579 {
580 0, 0
581 };
582 gettimeofday(&now, nullptr);
583 double const timesince = (now.tv_sec * 1000.0 + now.tv_usec / 1000.0)
584 - (start.tv_sec * 1000.0 + start.tv_usec / 1000.0);
585 return static_cast<float>(req - timesince / 1000.0);
586}
587
589{
590 m_GuideNSOffset += v / 1000.0 * m_GuideRate / 3600;
591 return IPS_OK;
592}
593
595{
596 m_GuideNSOffset += v / -1000.0 * m_GuideRate / 3600;
597 return IPS_OK;
598}
599
601{
602 float c = v / 1000.0 * m_GuideRate;
603 c = c / 3600.0 / 15.0;
604 c = c / (cos(m_CurrentDEC * DEGREES_TO_RADIANS));
605
606 m_GuideWEOffset += c;
607
608 return IPS_OK;
609}
610
612{
613 float c = v / -1000.0 * m_GuideRate;
614 c = c / 3600.0 / 15.0;
615 c = c / (cos(m_CurrentDEC * DEGREES_TO_RADIANS));
616
617 m_GuideWEOffset += c;
618
619 return IPS_OK;
620}
621
622bool GuideSim::ISNewText(const char * dev, const char * name, char * texts[], char * names[], int n)
623{
624 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0 && m_Clock.processText(name, texts, names, n))
625 return true;
626
627 return INDI::CCD::ISNewText(dev, name, texts, names, n);
628}
629
630bool GuideSim::ISNewNumber(const char * dev, const char * name, double values[], char * names[], int n)
631{
632 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
633 {
634
635 if (GainNP.isNameMatch(name))
636 {
637 GainNP.update(values, names, n);
638 GainNP.setState(IPS_OK);
639 GainNP.apply();
640 return true;
641 }
642
643 if (strcmp(name, "SIMULATOR_SETTINGS") == 0)
644 {
645 SimulatorSettingsNP.update(values, names, n);
646 SimulatorSettingsNP.setState(IPS_OK);
647
648 // Reset our parameters now
649 SetupParms();
650 SimulatorSettingsNP.apply();
651
652 m_MaxNoise = SimulatorSettingsNP[SIM_NOISE].getValue();
653 m_SkyGlow = SimulatorSettingsNP[SIM_SKYGLOW].getValue();
654 m_MaxVal = SimulatorSettingsNP[SIM_MAXVAL].getValue();
655 m_Bias = SimulatorSettingsNP[SIM_BIAS].getValue();
656 m_LimitingMag = SimulatorSettingsNP[SIM_LIMITINGMAG].getValue();
657 m_SaturationMag = SimulatorSettingsNP[SIM_SATURATION].getValue();
658 m_OAGoffset = SimulatorSettingsNP[SIM_OAGOFFSET].getValue();
659 m_PolarError = SimulatorSettingsNP[SIM_POLAR].getValue();
660 m_PolarDrift = SimulatorSettingsNP[SIM_POLARDRIFT].getValue();
661 m_RotationOffset = SimulatorSettingsNP[SIM_ROTATION].getValue();
662 // Kwiq++
663 m_KingGamma = SimulatorSettingsNP[SIM_KING_GAMMA].getValue() * DEGREES_TO_RADIANS;
664 m_KingTheta = SimulatorSettingsNP[SIM_KING_THETA].getValue() * DEGREES_TO_RADIANS;
665 m_TimeFactor = SimulatorSettingsNP[SIM_TIME_FACTOR].getValue();
666
667 m_Seeing = SimulatorSettingsNP[SIM_SEEING].getValue();
668 m_RaTimeDrift = SimulatorSettingsNP[SIM_RA_DRIFT].getValue();
669 m_DecTimeDrift = SimulatorSettingsNP[SIM_DEC_DRIFT].getValue();
670 m_RaRand = SimulatorSettingsNP[SIM_RA_RAND].getValue();
671 m_DecRand = SimulatorSettingsNP[SIM_DEC_RAND].getValue();
672 m_PEPeriod = SimulatorSettingsNP[SIM_PE_PERIOD].getValue();
673 m_PEMax = SimulatorSettingsNP[SIM_PE_MAX].getValue();
674 m_TemperatureRequest = SimulatorSettingsNP[SIM_TEMPERATURE].getValue();
675 TemperatureNP[0].setValue(m_TemperatureRequest);
677
678 return true;
679 }
680
681 // Record PE EQ to simulate different position in the sky than actual mount coordinate
682 // This can be useful to simulate Periodic Error or cone error or any arbitrary error.
683 if (EqPENP.isNameMatch(name))
684 {
685 EqPENP.update(values, names, n);
686 EqPENP.setState(IPS_OK);
687
688 INDI::IEquatorialCoordinates epochPos { EqPENP[AXIS_RA].getValue(), EqPENP[AXIS_DE].getValue() }, J2000Pos { 0, 0 };
689 INDI::ObservedToJ2000(&epochPos, m_Clock.julianDate(), &J2000Pos);
690 m_CurrentRA = J2000Pos.rightascension;
691 m_CurrentDEC = J2000Pos.declination;
692 m_UsePE = true;
693 EqPENP.apply();
694 return true;
695 }
696 }
697
698 return INDI::CCD::ISNewNumber(dev, name, values, names, n);
699}
700
701bool GuideSim::ISNewSwitch(const char * dev, const char * name, ISState * states, char * names[], int n)
702{
703 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
704 {
705
706 if (SimTestsSP.isNameMatch(name))
707 {
708 SimTestsSP.update(states, names, n);
709
710 // RGB
711 if (SimTestsSP[SIM_TESTS_RGB].getState() == ISS_ON)
712 {
713 m_SimulateRGB = true;
714 SimTestsSP[SIM_TESTS_RGB].setState(ISS_ON);
715 }
716 else
717 {
718 m_SimulateRGB = false;
719 SimTestsSP[SIM_TESTS_RGB].setState(ISS_OFF);
720 }
721 setRGB(m_SimulateRGB);
722
723 // Crash
724 if (SimTestsSP[SIM_TESTS_CRASH].getState() == ISS_ON)
725 {
726 abort();
727 }
728
729 // Timeout
730 if (SimTestsSP[SIM_TESTS_TIMEOUT].getState() == ISS_ON)
731 {
732 m_SimulateTimeout = true;
733 SimTestsSP[SIM_TESTS_TIMEOUT].setState(ISS_ON);
734 }
735 else
736 {
737 m_SimulateTimeout = false;
738 SimTestsSP[SIM_TESTS_TIMEOUT].setState(ISS_OFF);
739 }
740
741 SimTestsSP.setState(IPS_OK);
742 SimTestsSP.apply();
743 return true;
744 }
745 }
746
747 // Nobody has claimed this, so, ignore it
748 return INDI::CCD::ISNewSwitch(dev, name, states, names, n);
749}
750
752{
753#ifdef USE_EQUATORIAL_PE
755#endif
756 IDSnoopDevice(ActiveDeviceTP[ACTIVE_TELESCOPE].getText(), "EQUATORIAL_EOD_COORD");
757}
758
760{
761 // We try to snoop EQUATORIAL_PE first (true pointing with mount errors injected);
762 // if not found we fall through to the regular EQUATORIAL_EOD_COORD snoop below.
763#ifdef USE_EQUATORIAL_PE
764 const char * propName = findXMLAttValu(root, "name");
765 if (!strcmp(propName, "EQUATORIAL_PE"))
766 {
767 XMLEle * ep = nullptr;
768 int rc_ra = -1, rc_de = -1;
769 double newra = 0, newdec = 0;
770
771 for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0))
772 {
773 const char * elemName = findXMLAttValu(ep, "name");
774
775 if (!strcmp(elemName, "RA_PE"))
776 rc_ra = f_scansexa(pcdataXMLEle(ep), &newra);
777 else if (!strcmp(elemName, "DEC_PE"))
778 rc_de = f_scansexa(pcdataXMLEle(ep), &newdec);
779 }
780
781 if (rc_ra == 0 && rc_de == 0)
782 {
783 INDI::IEquatorialCoordinates epochPos { newra, newdec }, J2000Pos { 0, 0 };
784 INDI::ObservedToJ2000(&epochPos, m_Clock.julianDate(), &J2000Pos);
785 raPE = J2000Pos.rightascension;
786 decPE = J2000Pos.declination;
787
788 // Only activate PE rendering once the telescope reports a valid (Ok) state.
789 // The initial defNumberVector arrives with state Idle and values 0/0.
790 const char * state = findXMLAttValu(root, "state");
791 if (!m_UsePE && strcmp(state, "Ok") == 0)
792 {
793 LOGF_INFO("EQUATORIAL_PE snoop active: JNow RA %.4f Dec %.4f -> J2000 RA %.4f Dec %.4f", newra, newdec, raPE, decPE);
794 m_UsePE = true;
795 }
796
797 EqPENP[AXIS_RA].setValue(newra);
798 EqPENP[AXIS_DE].setValue(newdec);
799 EqPENP.apply();
800
801 LOGF_DEBUG("Snooped EQUATORIAL_PE JNow RA %g Dec %g -> J2000 RA %g Dec %g", newra, newdec, raPE, decPE);
802
803 return true;
804 }
805 }
806#endif
807
808 return INDI::CCD::ISSnoopDevice(root);
809}
810
812{
813 // Save CCD Config
815
816
817 // Save CCD Simulator Config
818 SimulatorSettingsNP.save(fp);
819
820 // Gain
821 GainNP.save(fp);
822
823
824 return true;
825}
826
828{
829 m_ExposureRequest = 1.0 / Streamer->getTargetExposure();
830 pthread_mutex_lock(&condMutex);
831 m_StreamPredicate = 1;
832 pthread_mutex_unlock(&condMutex);
833 pthread_cond_signal(&cv);
834
835 return true;
836}
837
839{
840 pthread_mutex_lock(&condMutex);
841 m_StreamPredicate = 0;
842 pthread_mutex_unlock(&condMutex);
843 pthread_cond_signal(&cv);
844
845 return true;
846}
847
848bool GuideSim::UpdateCCDFrame(int x, int y, int w, int h)
849{
850 long bin_width = w / PrimaryCCD.getBinX();
851 long bin_height = h / PrimaryCCD.getBinY();
852
853 bin_width = bin_width - (bin_width % 2);
854 bin_height = bin_height - (bin_height % 2);
855
856 Streamer->setSize(bin_width, bin_height);
857
858 return INDI::CCD::UpdateCCDFrame(x, y, w, h);
859}
860
861bool GuideSim::UpdateCCDBin(int hor, int ver)
862{
863 if (hor == 3 || ver == 3)
864 {
865 LOG_ERROR("3x3 binning is not supported.");
866 return false;
867 }
868
869 long bin_width = PrimaryCCD.getSubW() / hor;
870 long bin_height = PrimaryCCD.getSubH() / ver;
871
872 bin_width = bin_width - (bin_width % 2);
873 bin_height = bin_height - (bin_height % 2);
874
875 Streamer->setSize(bin_width, bin_height);
876
877 return INDI::CCD::UpdateCCDBin(hor, ver);
878}
879
880void * GuideSim::streamVideoHelper(void * context)
881{
882 return static_cast<GuideSim *>(context)->streamVideo();
883}
884
886{
887 auto start = std::chrono::high_resolution_clock::now();
888 auto finish = std::chrono::high_resolution_clock::now();
889
890 while (true)
891 {
892 pthread_mutex_lock(&condMutex);
893
894 while (m_StreamPredicate == 0)
895 {
896 pthread_cond_wait(&cv, &condMutex);
897 m_ExposureRequest = Streamer->getTargetExposure();
898 }
899
900 if (m_TerminateThread)
901 break;
902
903 // release condMutex
904 pthread_mutex_unlock(&condMutex);
905
906
907 // 16 bit
909
911
912 finish = std::chrono::high_resolution_clock::now();
913 std::chrono::duration<double> elapsed = finish - start;
914
915 if (elapsed.count() < m_ExposureRequest)
916 usleep(fabs(m_ExposureRequest - elapsed.count()) * 1e6);
917
919 Streamer->newFrame(PrimaryCCD.getFrameBuffer(), size);
920
921 start = std::chrono::high_resolution_clock::now();
922 }
923
924 pthread_mutex_unlock(&condMutex);
925 return nullptr;
926}
927
928void GuideSim::addFITSKeywords(INDI::CCDChip *targetChip, std::vector<INDI::FITSRecord> &fitsKeywords)
929{
930 INDI::CCD::addFITSKeywords(targetChip, fitsKeywords);
931
932 fitsKeywords.push_back({"GAIN", GainNP[0].getValue(), 3, "Gain"});
933}
The GuideSim class provides an advanced simulator for a CCD that includes a dedicated on-board guide ...
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
double m_CurrentDEC
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.
float m_GuideRate
void TimerHit() override
Callback function to be called once SetTimer duration elapses.
float m_GuideNSOffset
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)
double m_CurrentRA
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.
float m_GuideWEOffset
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
Initialize 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.
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.
bool SetupParms()
bool isConnected() const
INDI::PropertyText getText(const char *name) const
The CCDChip class provides functionality of a CCD Chip within a CCD.
Definition indiccdchip.h:39
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
int getBPP() const
getBPP Get CCD Chip depth (bits per pixel).
CCD_FRAME getFrameType() const
isInterlaced
int getBinY() const
getBinY Get vertical binning of the CCD chip.
int getXRes() const
getXRes Get the horizontal resolution in pixels of the CCD Chip.
Definition indiccdchip.h:81
int getBinX() const
getBinX Get horizontal binning of the CCD chip.
uint8_t * getFrameBuffer()
getFrameBuffer Get raw frame buffer of the CCD chip.
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
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.
int getYRes() const
Get the vertical resolution in pixels of the CCD Chip.
Definition indiccdchip.h:90
void binFrame()
binFrame Perform software binning on the CCD frame. Only use this function if hardware binning is not...
double Longitude
Definition indiccd.h:597
CCDChip PrimaryCCD
Definition indiccd.h:621
uint32_t GetCCDCapability() const
GetCCDCapability returns the CCD capabilities.
Definition indiccd.h:161
@ CCD_CAN_SUBFRAME
Definition indiccd.h:116
@ CCD_CAN_ABORT
Definition indiccd.h:117
@ CCD_HAS_SHUTTER
Definition indiccd.h:120
@ CCD_HAS_BAYER
Definition indiccd.h:122
@ CCD_CAN_BIN
Definition indiccd.h:115
@ CCD_HAS_STREAMING
Definition indiccd.h:123
@ CCD_HAS_ST4_PORT
Definition indiccd.h:119
@ ACTIVE_TELESCOPE
Definition indiccd.h:659
bool HasGuideHead()
Definition indiccd.h:199
std::unique_ptr< StreamManager > Streamer
Definition indiccd.h:619
bool InExposure
Definition indiccd.h:566
INDI::PropertyText ActiveDeviceTP
ActiveDeviceTP defines 4 devices the camera driver can listen to (snoop) for properties of interest s...
Definition indiccd.h:656
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:1988
virtual void addFITSKeywords(CCDChip *targetChip, std::vector< FITSRecord > &fitsKeywords)
Generate FITS keywords that will be added to FIST/XISF file.
Definition indiccd.cpp:2055
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition indiccd.cpp:1554
virtual bool ExposureComplete(CCDChip *targetChip)
Uploads target Chip exposed buffer as FITS to the client. Derived classes should class this function ...
Definition indiccd.cpp:2361
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:525
INDI::PropertyNumber TemperatureNP
TemperatureNP Camera Temperature in Celcius.
Definition indiccd.h:669
std::mutex ccdBufferLock
Definition indiccd.h:612
INDI::PropertyNumber ScopeInfoNP
Definition indiccd.h:720
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:2007
virtual bool initProperties() override
Initialize properties initial state and value. The child class must implement this function.
Definition indiccd.cpp:157
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
Definition indiccd.cpp:910
int pierSide
Definition indiccd.h:554
INDI::PropertyText BayerTP
BayerTP Bayer pattern offset and type.
Definition indiccd.h:684
virtual bool updateProperties() override
updateProperties is called whenever there is a change in the CONNECTION status of the driver....
Definition indiccd.cpp:540
virtual void addCaptureFormat(const CaptureFormat &format)
addCaptureFormat Add a supported camera native capture format (e.g. Mono, Bayer8.....
Definition indiccd.cpp:3264
double RotatorAngle
Definition indiccd.h:585
virtual void SetGuiderParams(int x, int y, int bpp, float xf, float yf)
Setup CCD parameters for guide head CCD. Child classes call this function to update CCD parameters.
Definition indiccd.cpp:2901
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save configuration items in XML file.
Definition indiccd.cpp:2914
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:729
void SetCCDCapability(uint32_t cap)
SetCCDCapability Set the CCD capabilities. All fields must be initialized.
Definition indiccd.cpp:140
@ FOCAL_LENGTH
Definition indiccd.h:723
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition indiccd.cpp:1143
CCDChip GuideCCD
Definition indiccd.h:622
@ CFA_OFFSET_Y
Definition indiccd.h:688
@ CFA_OFFSET_X
Definition indiccd.h:687
double RA
Definition indiccd.h:551
virtual void SetCCDParams(int x, int y, int bpp, float xf, float yf)
Setup CCD parameters for primary CCD. Child classes call this function to update CCD parameters.
Definition indiccd.cpp:2888
double snoopedFocalLength
Definition indiccd.h:565
double Dec
Definition indiccd.h:551
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
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.
uint32_t getDriverInterface() const
int SetTimer(uint32_t ms)
Set a timer to call the function TimerHit after ms milliseconds.
void addDebugControl()
Add Debug control to the driver.
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
bool load()
load Attempt to load property values from configuration file.
void save(FILE *f) const
save Save property to configuration file.
bool isNameMatch(const char *otherName) const
void setPermission(IPerm permission)
bool update(const double values[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, double timeout, IPState state)
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)
bool processText(const char *name, char *texts[], char *names[], int n)
void updateProperties(INDI::DefaultDevice *device, bool connected)
double julianDate() const
void initProperties(const char *deviceName, const char *group)
double localSiderealTime(double longitude) const
int renderFrame(INDI::CCDChip *chip, double ra_j2000_deg, double dec_j2000_deg, double focal_length_mm, double rotation_deg, float exposure_s, bool renderStars=true, double minSearchRadiusArcmin=0)
void applyReadoutNoise(INDI::CCDChip *chip, int bias, int maxNoise)
void setConfig(const RenderConfig &cfg)
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
const char * SITE_TAB
SITE_TAB Where all site information setting are located.
#define M_PI
Definition dsp.h:52
constexpr double DEGREES_TO_RADIANS
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
IPState
Property state.
Definition indiapi.h:158
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_NOFMANY
Definition indiapi.h:173
@ AXIS_DE
@ AXIS_RA
@ INDI_MONO
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:194
double range360(double r)
range360 Limits an angle to be between 0-360 degrees.
Definition indicom.c:1321
double get_local_hour_angle(double sideral_time, double ra)
get_local_hour_angle Returns local hour angle of an object
Definition indicom.c:1379
Implementations for common driver routines.
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
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#define LOG_WARN(txt)
Definition indilogger.h:73
#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 getDeviceName
const char * findXMLAttValu(XMLEle *ep, const char *name)
Find an XML element's attribute value.
Definition lilxml.cpp:644
XMLEle * nextXMLEle(XMLEle *ep, int init)
Iterate an XML element for a list of nesetd XML elements.
Definition lilxml.cpp:555
char * pcdataXMLEle(XMLEle *ep)
Return the pcdata of an XML element.
Definition lilxml.cpp:606
void ObservedToJ2000(IEquatorialCoordinates *observed, double jd, IEquatorialCoordinates *J2000pos)
ObservedToJ2000 converts an observed position to a J2000 catalogue position removes aberration,...
Definition libastro.cpp:51
float limitingMag
Definition sky_renderer.h:9
float saturationMag