Instrument Neutral Distributed Interface INDI 2.2.5
simple_telescope_simulator.cpp
Go to the documentation of this file.
1/*
2 INDI Developers Manual
3 Tutorial #7
4
5 "Simple telescope simulator"
6
7 We construct a most basic (and useless) device driver to illustrate INDI.
8
9 Refer to README, which contains instruction on how to build this driver, and use it
10 with an INDI-compatible client.
11
12*/
13
15
16#include "indicom.h"
17
18#include <memory>
19
20using namespace INDI::AlignmentSubsystem;
21
22// We declare an auto pointer to ScopeSim.
23std::unique_ptr<ScopeSim> telescope_sim(new ScopeSim());
24
26 DBG_SIMULATOR(INDI::Logger::getInstance().addDebugLevel("Simulator Verbose", "SIMULATOR"))
27{
28}
29
30bool ScopeSim::Abort()
31{
33 {
37 }
38
40 {
44 }
45
46 if (EqNP.getState() == IPS_BUSY)
47 {
49 EqNP.apply();
50 }
51
53
54 AxisStatusRA = AxisStatusDEC = STOPPED; // This marvelous inertia free scope can be stopped instantly!
55
57 AbortSP.reset();
58 AbortSP.apply();
59 LOG_INFO("Telescope aborted.");
60
61 return true;
62}
63
65{
66 return true;
67}
68
70{
72 return true;
73}
74
76{
77 return true;
78}
79
80const char *ScopeSim::getDefaultName()
81{
82 return (const char *)"Simple Telescope Simulator";
83}
84
85bool ScopeSim::Goto(double ra, double dec)
86{
87 DEBUGF(DBG_SIMULATOR, "Goto - Celestial reference frame target right ascension %lf(%lf) declination %lf",
88 ra * 360.0 / 24.0, ra, dec);
89
90 if (CoordSP.isSwitchOn("TRACK"))
91 {
92 char RAStr[32], DecStr[32];
93 fs_sexa(RAStr, ra, 2, 3600);
94 fs_sexa(DecStr, dec, 2, 3600);
95 CurrentTrackingTarget.rightascension = ra;
96 CurrentTrackingTarget.declination = dec;
97 DEBUG(DBG_SIMULATOR, "Goto - tracking requested");
98 }
99
100 // Call the alignment subsystem to translate the celestial reference frame coordinate
101 // into a telescope reference frame coordinate
104
105 if (TransformCelestialToTelescopeJD(ra, dec, ln_get_julian_from_sys(), TDV))
106 {
107 // The alignment subsystem has successfully transformed my coordinate
109 }
110 else
111 {
112 // The alignment subsystem cannot transform the coordinate.
113 // Try some simple rotations using the stored observatory position if any
114
115 INDI::IEquatorialCoordinates EquatorialCoordinates { ra, dec };
116 INDI::EquatorialToHorizontal(&EquatorialCoordinates, &m_Location, ln_get_julian_from_sys(), &AltAz);
119 {
120 case ZENITH:
121 break;
122
124 // Rotate the TDV coordinate system clockwise (negative) around the y axis by 90 minus
125 // the (positive)observatory latitude. The vector itself is rotated anticlockwise
127 break;
128
130 // Rotate the TDV coordinate system anticlockwise (positive) around the y axis by 90 plus
131 // the (negative)observatory latitude. The vector itself is rotated clockwise
133 break;
134 }
136 }
137
138 // My altitude encoder runs -90 to +90
139 if ((AltAz.altitude > 90.0) || (AltAz.altitude < -90.0))
140 {
141 DEBUG(DBG_SIMULATOR, "Goto - Altitude out of range");
142 // This should not happen
143 return false;
144 }
145
146 // My polar encoder runs 0 to +360
147 if ((AltAz.azimuth > 360.0) || (AltAz.azimuth < -360.0))
148 {
149 DEBUG(DBG_SIMULATOR, "Goto - Azimuth out of range");
150 // This should not happen
151 return false;
152 }
153
154 if (AltAz.azimuth < 0.0)
155 {
156 DEBUG(DBG_SIMULATOR, "Goto - Azimuth negative");
157 AltAz.azimuth = 360.0 + AltAz.azimuth;
158 }
159
160 DEBUGF(DBG_SIMULATOR, "Goto - Scope reference frame target altitude %lf azimuth %lf", AltAz.altitude,
161 AltAz.azimuth);
162
163 GotoTargetMicrostepsDEC = int(AltAz.altitude * MICROSTEPS_PER_DEGREE);
164 if (GotoTargetMicrostepsDEC == CurrentEncoderMicrostepsDEC)
165 AxisStatusDEC = STOPPED;
166 else
167 {
168 if (GotoTargetMicrostepsDEC > CurrentEncoderMicrostepsDEC)
169 AxisDirectionDEC = FORWARD;
170 else
171 AxisDirectionDEC = REVERSE;
172 AxisStatusDEC = SLEWING_TO;
173 }
174 GotoTargetMicrostepsRA = int(AltAz.azimuth * MICROSTEPS_PER_DEGREE);
175 if (GotoTargetMicrostepsRA == CurrentEncoderMicrostepsRA)
176 AxisStatusRA = STOPPED;
177 else
178 {
179 if (GotoTargetMicrostepsRA > CurrentEncoderMicrostepsRA)
180 AxisDirectionRA = (GotoTargetMicrostepsRA - CurrentEncoderMicrostepsRA) < MICROSTEPS_PER_REVOLUTION / 2.0 ?
181 FORWARD :
182 REVERSE;
183 else
184 AxisDirectionRA = (CurrentEncoderMicrostepsRA - GotoTargetMicrostepsRA) < MICROSTEPS_PER_REVOLUTION / 2.0 ?
185 REVERSE :
186 FORWARD;
187 AxisStatusRA = SLEWING_TO;
188 }
189
191
192 return true;
193}
194
196{
197 /* Make sure to init parent properties first */
199
201
202 /* Add debug controls so we may debug driver if necessary */
204
205 // Add alignment properties
207
208 return true;
209}
210
211bool ScopeSim::ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[],
212 char *formats[], char *names[], int n)
213{
214 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
215 {
216 // Process alignment properties
217 ProcessAlignmentBLOBProperties(this, name, sizes, blobsizes, blobs, formats, names, n);
218 }
219 // Pass it up the chain
220 return INDI::Telescope::ISNewBLOB(dev, name, sizes, blobsizes, blobs, formats, names, n);
221}
222
223bool ScopeSim::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
224{
225 // first check if it's for our device
226
227 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
228 {
229 // Process alignment properties
230 ProcessAlignmentNumberProperties(this, name, values, names, n);
231 }
232
233 // if we didn't process it, continue up the chain, let somebody else
234 // give it a shot
235 return INDI::Telescope::ISNewNumber(dev, name, values, names, n);
236}
237
238bool ScopeSim::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
239{
240 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
241 {
242 // Process alignment properties
243 ProcessAlignmentSwitchProperties(this, name, states, names, n);
244 }
245
246 // Nobody has claimed this, so, ignore it
247 return INDI::Telescope::ISNewSwitch(dev, name, states, names, n);
248}
249
250bool ScopeSim::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
251{
252 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
253 {
254 // Process alignment properties
255 ProcessAlignmentTextProperties(this, name, texts, names, n);
256 }
257 // Pass it up the chain
258 return INDI::Telescope::ISNewText(dev, name, texts, names, n);
259}
260
261bool ScopeSim::MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command)
262{
263 AxisDirection axisDir = (dir == DIRECTION_NORTH) ? FORWARD : REVERSE;
264 AxisStatus axisStat = (command == MOTION_START) ? SLEWING : STOPPED;
265
266 AxisSlewRateDEC = DEFAULT_SLEW_RATE;
267 AxisDirectionDEC = axisDir;
268 AxisStatusDEC = axisStat;
269
270 return true;
271}
272
273bool ScopeSim::MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command)
274{
275 AxisDirection axisDir = (dir == DIRECTION_WEST) ? FORWARD : REVERSE;
276 AxisStatus axisStat = (command == MOTION_START) ? SLEWING : STOPPED;
277
278 AxisSlewRateRA = DEFAULT_SLEW_RATE;
279 AxisDirectionRA = axisDir;
280 AxisStatusRA = axisStat;
281
282 return true;
283}
284
286{
287 INDI::IHorizontalCoordinates AltAz { CurrentEncoderMicrostepsRA / MICROSTEPS_PER_DEGREE, CurrentEncoderMicrostepsDEC / MICROSTEPS_PER_DEGREE };
289 double RightAscension, Declination;
290 if (!TransformTelescopeToCelestialJD(TDV, RightAscension, Declination, ln_get_julian_from_sys()))
291 {
292 if (TraceThisTick)
293 DEBUG(DBG_SIMULATOR, "ReadScopeStatus - TransformTelescopeToCelestial failed");
294
295 TelescopeDirectionVector RotatedTDV(TDV);
296
298 {
299 case ZENITH:
300 if (TraceThisTick)
301 DEBUG(DBG_SIMULATOR, "ReadScopeStatus - ApproximateMountAlignment ZENITH");
302 break;
303
305 if (TraceThisTick)
306 DEBUG(DBG_SIMULATOR, "ReadScopeStatus - ApproximateMountAlignment NORTH_CELESTIAL_POLE");
307 // Rotate the TDV coordinate system anticlockwise (positive) around the y axis by 90 minus
308 // the (positive)observatory latitude. The vector itself is rotated clockwise
309 RotatedTDV.RotateAroundY(90.0 - m_Location.latitude);
311 break;
312
314 if (TraceThisTick)
315 DEBUG(DBG_SIMULATOR, "ReadScopeStatus - ApproximateMountAlignment SOUTH_CELESTIAL_POLE");
316 // Rotate the TDV coordinate system clockwise (negative) around the y axis by 90 plus
317 // the (negative)observatory latitude. The vector itself is rotated anticlockwise
318 RotatedTDV.RotateAroundY(-90.0 - m_Location.latitude);
320 break;
321 }
322
323 INDI::IEquatorialCoordinates EquatorialCoordinates;
324 INDI::HorizontalToEquatorial(&AltAz, &m_Location, ln_get_julian_from_sys(), &EquatorialCoordinates);
325 // libnova works in decimal degrees
326 RightAscension = EquatorialCoordinates.rightascension;
327 Declination = EquatorialCoordinates.declination;
328 }
329
330 if (TraceThisTick)
331 DEBUGF(DBG_SIMULATOR, "ReadScopeStatus - RA %lf hours DEC %lf degrees", RightAscension, Declination);
332
333 NewRaDec(RightAscension, Declination);
334
335 return true;
336}
337
338bool ScopeSim::Sync(double ra, double dec)
339{
341 AlignmentDatabaseEntry NewEntry;
342
343 AltAz.altitude = double(CurrentEncoderMicrostepsDEC) / MICROSTEPS_PER_DEGREE;
344 AltAz.azimuth = double(CurrentEncoderMicrostepsRA) / MICROSTEPS_PER_DEGREE;
345
346 NewEntry.ObservationJulianDate = ln_get_julian_from_sys();
347 NewEntry.RightAscension = ra;
348 NewEntry.Declination = dec;
350 NewEntry.PrivateDataSize = 0;
351
352 if (!CheckForDuplicateSyncPoint(NewEntry))
353 {
354 GetAlignmentDatabase().push_back(NewEntry);
355
356 // Tell the client about size change
357 UpdateSize();
358
359 // Tell the math plugin to reinitialise
360 Initialise(this);
361
362 return true;
363 }
364 return false;
365}
366
368{
369 TraceThisTickCount++;
370 if (60 == TraceThisTickCount)
371 {
372 TraceThisTick = true;
373 TraceThisTickCount = 0;
374 }
375 // Simulate mount movement
376
377 static struct timeval ltv
378 {
379 0, 0
380 }; // previous system time
381 struct timeval tv
382 {
383 0, 0
384 }; // new system time
385 double dt; // Elapsed time in seconds since last tick
386
387 gettimeofday(&tv, nullptr);
388
389 if (ltv.tv_sec == 0 && ltv.tv_usec == 0)
390 ltv = tv;
391
392 dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec) / 1e6;
393 ltv = tv;
394
395 // RA axis
396 long SlewSteps = dt * AxisSlewRateRA;
397 bool CompleteRevolution = SlewSteps >= MICROSTEPS_PER_REVOLUTION;
398 SlewSteps = SlewSteps % MICROSTEPS_PER_REVOLUTION; // Just in case ;-)
399
400 switch (AxisStatusRA)
401 {
402 case STOPPED:
403 // Do nothing
404 break;
405
406 case SLEWING:
407 {
408 DEBUGF(DBG_SIMULATOR,
409 "TimerHit Slewing - RA Current Encoder %ld SlewSteps %ld Direction %d Target %ld Status %d",
410 CurrentEncoderMicrostepsRA, SlewSteps, AxisDirectionRA, GotoTargetMicrostepsRA, AxisStatusRA);
411
412 // Update the encoder
413 if (FORWARD == AxisDirectionRA)
414 CurrentEncoderMicrostepsRA += SlewSteps;
415 else
416 CurrentEncoderMicrostepsRA -= SlewSteps;
417 if (CurrentEncoderMicrostepsRA < 0)
418 CurrentEncoderMicrostepsRA += MICROSTEPS_PER_REVOLUTION;
419 else if (CurrentEncoderMicrostepsRA >= MICROSTEPS_PER_REVOLUTION)
420 CurrentEncoderMicrostepsRA -= MICROSTEPS_PER_REVOLUTION;
421
422 DEBUGF(DBG_SIMULATOR, "TimerHit Slewing - RA New Encoder %d New Status %d", CurrentEncoderMicrostepsRA,
423 AxisStatusRA);
424 break;
425 }
426
427 case SLEWING_TO:
428 {
429 DEBUGF(DBG_SIMULATOR,
430 "TimerHit SlewingTo - RA Current Encoder %ld SlewSteps %ld Direction %d Target %ld Status %d",
431 CurrentEncoderMicrostepsRA, SlewSteps, AxisDirectionRA, GotoTargetMicrostepsRA, AxisStatusRA);
432
433 long OldEncoder = CurrentEncoderMicrostepsRA;
434 // Update the encoder
435 if (FORWARD == AxisDirectionRA)
436 CurrentEncoderMicrostepsRA += SlewSteps;
437 else
438 CurrentEncoderMicrostepsRA -= SlewSteps;
439 if (CurrentEncoderMicrostepsRA < 0)
440 CurrentEncoderMicrostepsRA += MICROSTEPS_PER_REVOLUTION;
441 else if (CurrentEncoderMicrostepsRA >= MICROSTEPS_PER_REVOLUTION)
442 CurrentEncoderMicrostepsRA -= MICROSTEPS_PER_REVOLUTION;
443
444 if (CompleteRevolution)
445 {
446 // Must have found the target
447 AxisStatusRA = STOPPED;
448 CurrentEncoderMicrostepsRA = GotoTargetMicrostepsRA;
449 }
450 else
451 {
452 bool FoundTarget = false;
453 if (FORWARD == AxisDirectionRA)
454 {
455 if (CurrentEncoderMicrostepsRA < OldEncoder)
456 {
457 // Two ranges to search
458 if ((GotoTargetMicrostepsRA >= OldEncoder) &&
459 (GotoTargetMicrostepsRA <= MICROSTEPS_PER_REVOLUTION))
460 FoundTarget = true;
461 else if ((GotoTargetMicrostepsRA >= 0) &&
462 (GotoTargetMicrostepsRA <= CurrentEncoderMicrostepsRA))
463 FoundTarget = true;
464 }
465 else if ((GotoTargetMicrostepsRA >= OldEncoder) &&
466 (GotoTargetMicrostepsRA <= CurrentEncoderMicrostepsRA))
467 FoundTarget = true;
468 }
469 else
470 {
471 if (CurrentEncoderMicrostepsRA > OldEncoder)
472 {
473 // Two ranges to search
474 if ((GotoTargetMicrostepsRA >= 0) && (GotoTargetMicrostepsRA <= OldEncoder))
475 FoundTarget = true;
476 else if ((GotoTargetMicrostepsRA >= CurrentEncoderMicrostepsRA) &&
477 (GotoTargetMicrostepsRA <= MICROSTEPS_PER_REVOLUTION))
478 FoundTarget = true;
479 }
480 else if ((GotoTargetMicrostepsRA >= CurrentEncoderMicrostepsRA) &&
481 (GotoTargetMicrostepsRA <= OldEncoder))
482 FoundTarget = true;
483 }
484 if (FoundTarget)
485 {
486 AxisStatusRA = STOPPED;
487 CurrentEncoderMicrostepsRA = GotoTargetMicrostepsRA;
488 }
489 }
490 DEBUGF(DBG_SIMULATOR, "TimerHit SlewingTo - RA New Encoder %d New Status %d", CurrentEncoderMicrostepsRA,
491 AxisStatusRA);
492 break;
493 }
494 }
495
496 // DEC axis
497 SlewSteps = dt * AxisSlewRateDEC;
498
499 switch (AxisStatusDEC)
500 {
501 case STOPPED:
502 // Do nothing
503 break;
504
505 case SLEWING:
506 {
507 DEBUGF(DBG_SIMULATOR,
508 "TimerHit Slewing - DEC Current Encoder %ld SlewSteps %d Direction %ld Target %ld Status %d",
509 CurrentEncoderMicrostepsDEC, SlewSteps, AxisDirectionDEC, GotoTargetMicrostepsDEC, AxisStatusDEC);
510
511 // Update the encoder
512 SlewSteps = SlewSteps % MICROSTEPS_PER_REVOLUTION; // Just in case ;-)
513 if (FORWARD == AxisDirectionDEC)
514 CurrentEncoderMicrostepsDEC += SlewSteps;
515 else
516 CurrentEncoderMicrostepsDEC -= SlewSteps;
517 if (CurrentEncoderMicrostepsDEC > MAX_DEC)
518 {
519 CurrentEncoderMicrostepsDEC = MAX_DEC;
520 AxisStatusDEC = STOPPED; // Hit the buffers
521 DEBUG(DBG_SIMULATOR, "TimerHit - DEC axis hit the buffers at MAX_DEC");
522 }
523 else if (CurrentEncoderMicrostepsDEC < MIN_DEC)
524 {
525 CurrentEncoderMicrostepsDEC = MIN_DEC;
526 AxisStatusDEC = STOPPED; // Hit the buffers
527 DEBUG(DBG_SIMULATOR, "TimerHit - DEC axis hit the buffers at MIN_DEC");
528 }
529
530 DEBUGF(DBG_SIMULATOR, "TimerHit Slewing - DEC New Encoder %d New Status %d", CurrentEncoderMicrostepsDEC,
531 AxisStatusDEC);
532 break;
533 }
534
535 case SLEWING_TO:
536 {
537 DEBUGF(DBG_SIMULATOR,
538 "TimerHit SlewingTo - DEC Current Encoder %ld SlewSteps %d Direction %ld Target %ld Status %d",
539 CurrentEncoderMicrostepsDEC, SlewSteps, AxisDirectionDEC, GotoTargetMicrostepsDEC, AxisStatusDEC);
540
541 // Calculate steps to target
542 int StepsToTarget;
543 if (FORWARD == AxisDirectionDEC)
544 {
545 if (CurrentEncoderMicrostepsDEC <= GotoTargetMicrostepsDEC)
546 StepsToTarget = GotoTargetMicrostepsDEC - CurrentEncoderMicrostepsDEC;
547 else
548 StepsToTarget = CurrentEncoderMicrostepsDEC - GotoTargetMicrostepsDEC;
549 }
550 else
551 {
552 // Axis in reverse
553 if (CurrentEncoderMicrostepsDEC >= GotoTargetMicrostepsDEC)
554 StepsToTarget = CurrentEncoderMicrostepsDEC - GotoTargetMicrostepsDEC;
555 else
556 StepsToTarget = GotoTargetMicrostepsDEC - CurrentEncoderMicrostepsDEC;
557 }
558 if (StepsToTarget <= SlewSteps)
559 {
560 // Target was hit this tick
561 AxisStatusDEC = STOPPED;
562 CurrentEncoderMicrostepsDEC = GotoTargetMicrostepsDEC;
563 }
564 else
565 {
566 if (FORWARD == AxisDirectionDEC)
567 CurrentEncoderMicrostepsDEC += SlewSteps;
568 else
569 CurrentEncoderMicrostepsDEC -= SlewSteps;
570 if (CurrentEncoderMicrostepsDEC < 0)
571 CurrentEncoderMicrostepsDEC += MICROSTEPS_PER_REVOLUTION;
572 else if (CurrentEncoderMicrostepsDEC >= MICROSTEPS_PER_REVOLUTION)
573 CurrentEncoderMicrostepsDEC -= MICROSTEPS_PER_REVOLUTION;
574 }
575
576 DEBUGF(DBG_SIMULATOR, "TimerHit SlewingTo - DEC New Encoder %d New Status %d", CurrentEncoderMicrostepsDEC,
577 AxisStatusDEC);
578 break;
579 }
580 }
581
582 INDI::Telescope::TimerHit(); // This will call ReadScopeStatus
583
584 // OK I have updated the celestial reference frame RA/DEC in ReadScopeStatus
585 // Now handle the tracking state
586 switch (TrackState)
587 {
588 case SCOPE_SLEWING:
589 if ((STOPPED == AxisStatusRA) && (STOPPED == AxisStatusDEC))
590 {
591 if (CoordSP.isSwitchOn("TRACK"))
592 {
593 // Goto has finished start tracking
594 DEBUG(DBG_SIMULATOR, "TimerHit - Goto finished start tracking");
596 // Fall through to tracking case
597 }
598 else
599 {
601 break;
602 }
603 }
604 break;
605
606 case SCOPE_TRACKING:
607 {
608 // Continue or start tracking
609 // Calculate where the mount needs to be in POLLMS time
610 // POLLMS is hardcoded to be one second
611 // TODO may need to make this longer to get a meaningful result
612 double JulianOffset = 1.0 / (24.0 * 60 * 60);
614 INDI::IHorizontalCoordinates AltAz { 0, 0 };
615
616 if (TransformCelestialToTelescopeJD(CurrentTrackingTarget.rightascension,
617 CurrentTrackingTarget.declination,
618 ln_get_julian_from_sys() + JulianOffset, TDV))
620 else
621 {
622
623 INDI::IEquatorialCoordinates EquatorialCoordinates { 0, 0 };
624 EquatorialCoordinates.rightascension = CurrentTrackingTarget.rightascension;
625 EquatorialCoordinates.declination = CurrentTrackingTarget.declination;
626 INDI::EquatorialToHorizontal(&EquatorialCoordinates, &m_Location, ln_get_julian_from_sys() + JulianOffset, &AltAz);
627 INDI::EquatorialToHorizontal(&EquatorialCoordinates, &m_Location, ln_get_julian_from_sys() + JulianOffset,
628 &AltAz);
629
630 }
631
632 // My altitude encoder runs -90 to +90
633 if ((AltAz.altitude > 90.0) || (AltAz.altitude < -90.0))
634 {
635 DEBUG(DBG_SIMULATOR, "TimerHit tracking - Altitude out of range");
636 // This should not happen
637 return;
638 }
639
640 // My polar encoder runs 0 to +360
641 if ((AltAz.azimuth > 360.0) || (AltAz.azimuth < -360.0))
642 {
643 DEBUG(DBG_SIMULATOR, "TimerHit tracking - Azimuth out of range");
644 // This should not happen
645 return;
646 }
647
648 if (AltAz.azimuth < 0.0)
649 {
650 DEBUG(DBG_SIMULATOR, "TimerHit tracking - Azimuth negative");
651 AltAz.azimuth = 360.0 + AltAz.azimuth;
652 }
653
654 long AltitudeOffsetMicrosteps = int(AltAz.altitude * MICROSTEPS_PER_DEGREE - CurrentEncoderMicrostepsDEC);
655 long AzimuthOffsetMicrosteps = int(AltAz.azimuth * MICROSTEPS_PER_DEGREE - CurrentEncoderMicrostepsRA);
656
657 DEBUGF(DBG_SIMULATOR, "TimerHit - Tracking AltitudeOffsetMicrosteps %d AzimuthOffsetMicrosteps %d",
658 AltitudeOffsetMicrosteps, AzimuthOffsetMicrosteps);
659
660 if (0 != AzimuthOffsetMicrosteps)
661 {
662 // Calculate the slewing rates needed to reach that position
663 // at the correct time. This is simple as interval is one second
664 if (AzimuthOffsetMicrosteps > 0)
665 {
666 if (AzimuthOffsetMicrosteps < MICROSTEPS_PER_REVOLUTION / 2.0)
667 {
668 // Forward
669 AxisDirectionRA = FORWARD;
670 AxisSlewRateRA = AzimuthOffsetMicrosteps;
671 }
672 else
673 {
674 // Reverse
675 AxisDirectionRA = REVERSE;
676 AxisSlewRateRA = MICROSTEPS_PER_REVOLUTION - AzimuthOffsetMicrosteps;
677 }
678 }
679 else
680 {
681 AzimuthOffsetMicrosteps = std::abs(AzimuthOffsetMicrosteps);
682 if (AzimuthOffsetMicrosteps < MICROSTEPS_PER_REVOLUTION / 2.0)
683 {
684 // Forward
685 AxisDirectionRA = REVERSE;
686 AxisSlewRateRA = AzimuthOffsetMicrosteps;
687 }
688 else
689 {
690 // Reverse
691 AxisDirectionRA = FORWARD;
692 AxisSlewRateRA = MICROSTEPS_PER_REVOLUTION - AzimuthOffsetMicrosteps;
693 }
694 }
695 AxisSlewRateRA = std::abs(AzimuthOffsetMicrosteps);
696 AxisDirectionRA = AzimuthOffsetMicrosteps > 0 ? FORWARD : REVERSE; // !!!! BEWARE INERTIA FREE MOUNT
697 AxisStatusRA = SLEWING;
698 DEBUGF(DBG_SIMULATOR, "TimerHit - Tracking AxisSlewRateRA %lf AxisDirectionRA %d", AxisSlewRateRA,
699 AxisDirectionRA);
700 }
701 else
702 {
703 // Nothing to do - stop the axis
704 AxisStatusRA = STOPPED; // !!!! BEWARE INERTIA FREE MOUNT
705 DEBUG(DBG_SIMULATOR, "TimerHit - Tracking nothing to do stopping RA axis");
706 }
707
708 if (0 != AltitudeOffsetMicrosteps)
709 {
710 // Calculate the slewing rates needed to reach that position
711 // at the correct time.
712 AxisSlewRateDEC = std::abs(AltitudeOffsetMicrosteps);
713 AxisDirectionDEC = AltitudeOffsetMicrosteps > 0 ? FORWARD : REVERSE; // !!!! BEWARE INERTIA FREE MOUNT
714 AxisStatusDEC = SLEWING;
715 DEBUGF(DBG_SIMULATOR, "TimerHit - Tracking AxisSlewRateDEC %lf AxisDirectionDEC %d", AxisSlewRateDEC,
716 AxisDirectionDEC);
717 }
718 else
719 {
720 // Nothing to do - stop the axis
721 AxisStatusDEC = STOPPED; // !!!! BEWARE INERTIA FREE MOUNT
722 DEBUG(DBG_SIMULATOR, "TimerHit - Tracking nothing to do stopping DEC axis");
723 }
724
725 break;
726 }
727
728 default:
729 break;
730 }
731
732 TraceThisTick = false;
733}
734
735bool ScopeSim::updateLocation(double latitude, double longitude, double elevation)
736{
737 UpdateLocation(latitude, longitude, elevation);
738 return true;
739}
void ProcessAlignmentNumberProperties(Telescope *pTelescope, const char *name, double values[], char *names[], int n)
Call this function from within the ISNewNumber processing path. The function will handle any alignmen...
void ProcessAlignmentSwitchProperties(Telescope *pTelescope, const char *name, ISState *states, char *names[], int n)
Call this function from within the ISNewSwitch processing path. The function will handle any alignmen...
void InitAlignmentProperties(Telescope *pTelescope)
Initialize alignment subsystem properties. It is recommended to call this function within initPropert...
void ProcessAlignmentBLOBProperties(Telescope *pTelescope, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n)
Call this function from within the ISNewBlob processing path. The function will handle any alignment ...
void ProcessAlignmentTextProperties(Telescope *pTelescope, const char *name, char *texts[], char *names[], int n)
Call this function from within the ISNewText processing path. The function will handle any alignment ...
bool CheckForDuplicateSyncPoint(const AlignmentDatabaseEntry &CandidateEntry, double Tolerance=0.1) const
Check if a entry already exists in the database.
AlignmentDatabaseType & GetAlignmentDatabase()
Get a reference to the in memory database.
void UpdateLocation(double latitude, double longitude, double elevation)
Call this function from within the updateLocation processing path.
void UpdateSize()
Call this function when the number of entries in the database changes.
bool TransformTelescopeToCelestialJD(const TelescopeDirectionVector &ApparentTelescopeDirectionVector, double &RightAscension, double &Declination, double JulianDate)
TransformTelescopeToCelestialJD Transforms Mount Coords to Celestial (Sky) Coordinates.
bool TransformCelestialToTelescopeJD(double RightAscension, double Declination, double JulianDate, TelescopeDirectionVector &ApparentTelescopeDirectionVector)
TransformCelestialToTelescopeJD Transforms Celestial (Sky) Coords to Mount Coordinates.
bool Initialise(InMemoryDatabase *pInMemoryDatabase)
Initialise or re-initialise the math plugin. Re-reading the in memory database as necessary.
MountAlignment_t GetApproximateMountAlignment()
Get the approximate alignment of the mount.
const TelescopeDirectionVector TelescopeDirectionVectorFromAltitudeAzimuth(INDI::IHorizontalCoordinates HorizontalCoordinates)
Calculates a normalised direction vector from the supplied altitude and azimuth.
void AltitudeAzimuthFromTelescopeDirectionVector(const TelescopeDirectionVector TelescopeDirectionVector, INDI::IHorizontalCoordinates &HorizontalCoordinates)
Calculates an altitude and azimuth from the supplied normalised direction vector and declination.
uint32_t getCurrentPollingPeriod() const
getCurrentPollingPeriod Return the current polling period.
virtual bool ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n)
Process the client newBLOB command.
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 isSwitchOn(const std::string &name) const
TelescopeStatus TrackState
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual void TimerHit() override
Called when setTimer() time is up.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
IGeographicCoordinates m_Location
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
The ScopeSim class provides a simple mount simulator of an equatorial mount.
virtual bool ReadScopeStatus() override
Read telescope status.
virtual bool Abort() override
Abort any telescope motion including tracking if possible.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual const char * getDefaultName() override
virtual bool Disconnect() override
Disconnect from device.
virtual bool Sync(double ra, double dec) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
virtual bool Goto(double, double) override
Move the scope to the supplied RA and DEC coordinates.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
void TimerHit() override
Called when setTimer() time is up.
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
bool ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[], char *formats[], char *names[], int n) override
Process the client newBLOB command.
INDI::PropertySwitch AbortSP
INDI::PropertySwitch MovementNSSP
INDI::PropertyNumber EqNP
INDI::PropertySwitch MovementWESP
INDI::PropertySwitch CoordSP
double ra
double dec
ISState
Switch state.
Definition indiapi.h:148
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
INDI_DIR_WE
@ DIRECTION_WEST
INDI_DIR_NS
@ DIRECTION_NORTH
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:130
Implementations for common driver routines.
#define DEBUG(priority, msg)
Macro to print log messages. Example of usage of the Logger: DEBUG(DBG_DEBUG, "hello " << "world");.
Definition indilogger.h:56
#define LOG_INFO(txt)
Definition indilogger.h:74
#define DEBUGF(priority, msg,...)
Definition indilogger.h:57
#define getDeviceName
Namespace to encapsulate the INDI Alignment Subsystem classes. For more information see "INDI Alignme...
Namespace to encapsulate INDI client, drivers, and mediator classes.
void EquatorialToHorizontal(IEquatorialCoordinates *object, IGeographicCoordinates *observer, double JD, IHorizontalCoordinates *position)
EquatorialToHorizontal Calculate horizontal coordinates from equatorial coordinates.
Definition libastro.cpp:141
void HorizontalToEquatorial(IHorizontalCoordinates *object, IGeographicCoordinates *observer, double JD, IEquatorialCoordinates *position)
HorizontalToEquatorial Calculate Equatorial EOD Coordinates from horizontal coordinates.
Definition libastro.cpp:161
std::unique_ptr< ScopeSim > telescope_sim(new ScopeSim())
Entry in the in memory alignment database.
Definition Common.h:152
double RightAscension
Right ascension in decimal hours. N.B. libnova works in decimal degrees so conversion is always neede...
Definition Common.h:190
TelescopeDirectionVector TelescopeDirection
Normalised vector giving telescope pointing direction. This is referred to elsewhere as the "apparent...
Definition Common.h:197
double Declination
Declination in decimal degrees.
Definition Common.h:193
int PrivateDataSize
This size in bytes of any private data.
Definition Common.h:203
Holds a normalised direction vector (direction cosines)
Definition Common.h:69
void RotateAroundY(double Angle)
Rotate the reference frame around the Y axis. This has the affect of rotating the vector itself in th...
Definition Common.cpp:18
The Logger class is a simple logger to log messages to file and INDI clients. This is the implementat...