Instrument Neutral Distributed Interface INDI  2.0.2
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 
20 using namespace INDI::AlignmentSubsystem;
21 
22 // We declare an auto pointer to ScopeSim.
23 std::unique_ptr<ScopeSim> telescope_sim(new ScopeSim());
24 
26  DBG_SIMULATOR(INDI::Logger::getInstance().addDebugLevel("Simulator Verbose", "SIMULATOR"))
27 {
28 }
29 
30 bool ScopeSim::Abort()
31 {
32  if (MovementNSSP.s == IPS_BUSY)
33  {
36  IDSetSwitch(&MovementNSSP, nullptr);
37  }
38 
39  if (MovementWESP.s == IPS_BUSY)
40  {
43  IDSetSwitch(&MovementWESP, nullptr);
44  }
45 
46  if (EqNP.s == IPS_BUSY)
47  {
48  EqNP.s = IPS_IDLE;
49  IDSetNumber(&EqNP, nullptr);
50  }
51 
53 
54  AxisStatusRA = AxisStatusDEC = STOPPED; // This marvelous inertia free scope can be stopped instantly!
55 
56  AbortSP.s = IPS_OK;
58  IDSetSwitch(&AbortSP, nullptr);
59  LOG_INFO("Telescope aborted.");
60 
61  return true;
62 }
63 
65 {
66  return true;
67 }
68 
69 bool ScopeSim::Connect()
70 {
72  return true;
73 }
74 
76 {
77  return true;
78 }
79 
80 const char *ScopeSim::getDefaultName()
81 {
82  return (const char *)"Simple Telescope Simulator";
83 }
84 
85 bool 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 (ISS_ON == IUFindSwitch(&CoordSP, "TRACK")->s)
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 (TransformCelestialToTelescope(ra, dec, 0.0, 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
126  TDV.RotateAroundY(m_Location.latitude - 90.0);
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
132  TDV.RotateAroundY(m_Location.latitude + 90.0);
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 
200  // Let's simulate it to be an F/10 8" telescope
201  ScopeParametersN[0].value = 203;
202  ScopeParametersN[1].value = 2000;
203  ScopeParametersN[2].value = 203;
204  ScopeParametersN[3].value = 2000;
205 
207 
208  /* Add debug controls so we may debug driver if necessary */
209  addDebugControl();
210 
211  // Add alignment properties
213 
214  return true;
215 }
216 
217 bool ScopeSim::ISNewBLOB(const char *dev, const char *name, int sizes[], int blobsizes[], char *blobs[],
218  char *formats[], char *names[], int n)
219 {
220  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
221  {
222  // Process alignment properties
223  ProcessAlignmentBLOBProperties(this, name, sizes, blobsizes, blobs, formats, names, n);
224  }
225  // Pass it up the chain
226  return INDI::Telescope::ISNewBLOB(dev, name, sizes, blobsizes, blobs, formats, names, n);
227 }
228 
229 bool ScopeSim::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
230 {
231  // first check if it's for our device
232 
233  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
234  {
235  // Process alignment properties
236  ProcessAlignmentNumberProperties(this, name, values, names, n);
237  }
238 
239  // if we didn't process it, continue up the chain, let somebody else
240  // give it a shot
241  return INDI::Telescope::ISNewNumber(dev, name, values, names, n);
242 }
243 
244 bool ScopeSim::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
245 {
246  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
247  {
248  // Process alignment properties
249  ProcessAlignmentSwitchProperties(this, name, states, names, n);
250  }
251 
252  // Nobody has claimed this, so, ignore it
253  return INDI::Telescope::ISNewSwitch(dev, name, states, names, n);
254 }
255 
256 bool ScopeSim::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
257 {
258  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
259  {
260  // Process alignment properties
261  ProcessAlignmentTextProperties(this, name, texts, names, n);
262  }
263  // Pass it up the chain
264  return INDI::Telescope::ISNewText(dev, name, texts, names, n);
265 }
266 
267 bool ScopeSim::MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command)
268 {
269  AxisDirection axisDir = (dir == DIRECTION_NORTH) ? FORWARD : REVERSE;
270  AxisStatus axisStat = (command == MOTION_START) ? SLEWING : STOPPED;
271 
272  AxisSlewRateDEC = DEFAULT_SLEW_RATE;
273  AxisDirectionDEC = axisDir;
274  AxisStatusDEC = axisStat;
275 
276  return true;
277 }
278 
279 bool ScopeSim::MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command)
280 {
281  AxisDirection axisDir = (dir == DIRECTION_WEST) ? FORWARD : REVERSE;
282  AxisStatus axisStat = (command == MOTION_START) ? SLEWING : STOPPED;
283 
284  AxisSlewRateRA = DEFAULT_SLEW_RATE;
285  AxisDirectionRA = axisDir;
286  AxisStatusRA = axisStat;
287 
288  return true;
289 }
290 
292 {
293  INDI::IHorizontalCoordinates AltAz { CurrentEncoderMicrostepsRA / MICROSTEPS_PER_DEGREE, CurrentEncoderMicrostepsDEC / MICROSTEPS_PER_DEGREE };
295  double RightAscension, Declination;
296  if (!TransformTelescopeToCelestial(TDV, RightAscension, Declination))
297  {
298  if (TraceThisTick)
299  DEBUG(DBG_SIMULATOR, "ReadScopeStatus - TransformTelescopeToCelestial failed");
300 
301  TelescopeDirectionVector RotatedTDV(TDV);
302 
304  {
305  case ZENITH:
306  if (TraceThisTick)
307  DEBUG(DBG_SIMULATOR, "ReadScopeStatus - ApproximateMountAlignment ZENITH");
308  break;
309 
311  if (TraceThisTick)
312  DEBUG(DBG_SIMULATOR, "ReadScopeStatus - ApproximateMountAlignment NORTH_CELESTIAL_POLE");
313  // Rotate the TDV coordinate system anticlockwise (positive) around the y axis by 90 minus
314  // the (positive)observatory latitude. The vector itself is rotated clockwise
315  RotatedTDV.RotateAroundY(90.0 - m_Location.latitude);
317  break;
318 
320  if (TraceThisTick)
321  DEBUG(DBG_SIMULATOR, "ReadScopeStatus - ApproximateMountAlignment SOUTH_CELESTIAL_POLE");
322  // Rotate the TDV coordinate system clockwise (negative) around the y axis by 90 plus
323  // the (negative)observatory latitude. The vector itself is rotated anticlockwise
324  RotatedTDV.RotateAroundY(-90.0 - m_Location.latitude);
326  break;
327  }
328 
329  INDI::IEquatorialCoordinates EquatorialCoordinates;
330  INDI::HorizontalToEquatorial(&AltAz, &m_Location, ln_get_julian_from_sys(), &EquatorialCoordinates);
331  // libnova works in decimal degrees
332  RightAscension = EquatorialCoordinates.rightascension;
333  Declination = EquatorialCoordinates.declination;
334  }
335 
336  if (TraceThisTick)
337  DEBUGF(DBG_SIMULATOR, "ReadScopeStatus - RA %lf hours DEC %lf degrees", RightAscension, Declination);
338 
339  NewRaDec(RightAscension, Declination);
340 
341  return true;
342 }
343 
344 bool ScopeSim::Sync(double ra, double dec)
345 {
347  AlignmentDatabaseEntry NewEntry;
348 
349  AltAz.altitude = double(CurrentEncoderMicrostepsDEC) / MICROSTEPS_PER_DEGREE;
350  AltAz.azimuth = double(CurrentEncoderMicrostepsRA) / MICROSTEPS_PER_DEGREE;
351 
352  NewEntry.ObservationJulianDate = ln_get_julian_from_sys();
353  NewEntry.RightAscension = ra;
354  NewEntry.Declination = dec;
356  NewEntry.PrivateDataSize = 0;
357 
358  if (!CheckForDuplicateSyncPoint(NewEntry))
359  {
360  GetAlignmentDatabase().push_back(NewEntry);
361 
362  // Tell the client about size change
363  UpdateSize();
364 
365  // Tell the math plugin to reinitialise
366  Initialise(this);
367 
368  return true;
369  }
370  return false;
371 }
372 
374 {
375  TraceThisTickCount++;
376  if (60 == TraceThisTickCount)
377  {
378  TraceThisTick = true;
379  TraceThisTickCount = 0;
380  }
381  // Simulate mount movement
382 
383  static struct timeval ltv
384  {
385  0, 0
386  }; // previous system time
387  struct timeval tv
388  {
389  0, 0
390  }; // new system time
391  double dt; // Elapsed time in seconds since last tick
392 
393  gettimeofday(&tv, nullptr);
394 
395  if (ltv.tv_sec == 0 && ltv.tv_usec == 0)
396  ltv = tv;
397 
398  dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec) / 1e6;
399  ltv = tv;
400 
401  // RA axis
402  long SlewSteps = dt * AxisSlewRateRA;
403  bool CompleteRevolution = SlewSteps >= MICROSTEPS_PER_REVOLUTION;
404  SlewSteps = SlewSteps % MICROSTEPS_PER_REVOLUTION; // Just in case ;-)
405 
406  switch (AxisStatusRA)
407  {
408  case STOPPED:
409  // Do nothing
410  break;
411 
412  case SLEWING:
413  {
414  DEBUGF(DBG_SIMULATOR,
415  "TimerHit Slewing - RA Current Encoder %ld SlewSteps %ld Direction %d Target %ld Status %d",
416  CurrentEncoderMicrostepsRA, SlewSteps, AxisDirectionRA, GotoTargetMicrostepsRA, AxisStatusRA);
417 
418  // Update the encoder
419  if (FORWARD == AxisDirectionRA)
420  CurrentEncoderMicrostepsRA += SlewSteps;
421  else
422  CurrentEncoderMicrostepsRA -= SlewSteps;
423  if (CurrentEncoderMicrostepsRA < 0)
424  CurrentEncoderMicrostepsRA += MICROSTEPS_PER_REVOLUTION;
425  else if (CurrentEncoderMicrostepsRA >= MICROSTEPS_PER_REVOLUTION)
426  CurrentEncoderMicrostepsRA -= MICROSTEPS_PER_REVOLUTION;
427 
428  DEBUGF(DBG_SIMULATOR, "TimerHit Slewing - RA New Encoder %d New Status %d", CurrentEncoderMicrostepsRA,
429  AxisStatusRA);
430  break;
431  }
432 
433  case SLEWING_TO:
434  {
435  DEBUGF(DBG_SIMULATOR,
436  "TimerHit SlewingTo - RA Current Encoder %ld SlewSteps %ld Direction %d Target %ld Status %d",
437  CurrentEncoderMicrostepsRA, SlewSteps, AxisDirectionRA, GotoTargetMicrostepsRA, AxisStatusRA);
438 
439  long OldEncoder = CurrentEncoderMicrostepsRA;
440  // Update the encoder
441  if (FORWARD == AxisDirectionRA)
442  CurrentEncoderMicrostepsRA += SlewSteps;
443  else
444  CurrentEncoderMicrostepsRA -= SlewSteps;
445  if (CurrentEncoderMicrostepsRA < 0)
446  CurrentEncoderMicrostepsRA += MICROSTEPS_PER_REVOLUTION;
447  else if (CurrentEncoderMicrostepsRA >= MICROSTEPS_PER_REVOLUTION)
448  CurrentEncoderMicrostepsRA -= MICROSTEPS_PER_REVOLUTION;
449 
450  if (CompleteRevolution)
451  {
452  // Must have found the target
453  AxisStatusRA = STOPPED;
454  CurrentEncoderMicrostepsRA = GotoTargetMicrostepsRA;
455  }
456  else
457  {
458  bool FoundTarget = false;
459  if (FORWARD == AxisDirectionRA)
460  {
461  if (CurrentEncoderMicrostepsRA < OldEncoder)
462  {
463  // Two ranges to search
464  if ((GotoTargetMicrostepsRA >= OldEncoder) &&
465  (GotoTargetMicrostepsRA <= MICROSTEPS_PER_REVOLUTION))
466  FoundTarget = true;
467  else if ((GotoTargetMicrostepsRA >= 0) &&
468  (GotoTargetMicrostepsRA <= CurrentEncoderMicrostepsRA))
469  FoundTarget = true;
470  }
471  else if ((GotoTargetMicrostepsRA >= OldEncoder) &&
472  (GotoTargetMicrostepsRA <= CurrentEncoderMicrostepsRA))
473  FoundTarget = true;
474  }
475  else
476  {
477  if (CurrentEncoderMicrostepsRA > OldEncoder)
478  {
479  // Two ranges to search
480  if ((GotoTargetMicrostepsRA >= 0) && (GotoTargetMicrostepsRA <= OldEncoder))
481  FoundTarget = true;
482  else if ((GotoTargetMicrostepsRA >= CurrentEncoderMicrostepsRA) &&
483  (GotoTargetMicrostepsRA <= MICROSTEPS_PER_REVOLUTION))
484  FoundTarget = true;
485  }
486  else if ((GotoTargetMicrostepsRA >= CurrentEncoderMicrostepsRA) &&
487  (GotoTargetMicrostepsRA <= OldEncoder))
488  FoundTarget = true;
489  }
490  if (FoundTarget)
491  {
492  AxisStatusRA = STOPPED;
493  CurrentEncoderMicrostepsRA = GotoTargetMicrostepsRA;
494  }
495  }
496  DEBUGF(DBG_SIMULATOR, "TimerHit SlewingTo - RA New Encoder %d New Status %d", CurrentEncoderMicrostepsRA,
497  AxisStatusRA);
498  break;
499  }
500  }
501 
502  // DEC axis
503  SlewSteps = dt * AxisSlewRateDEC;
504 
505  switch (AxisStatusDEC)
506  {
507  case STOPPED:
508  // Do nothing
509  break;
510 
511  case SLEWING:
512  {
513  DEBUGF(DBG_SIMULATOR,
514  "TimerHit Slewing - DEC Current Encoder %ld SlewSteps %d Direction %ld Target %ld Status %d",
515  CurrentEncoderMicrostepsDEC, SlewSteps, AxisDirectionDEC, GotoTargetMicrostepsDEC, AxisStatusDEC);
516 
517  // Update the encoder
518  SlewSteps = SlewSteps % MICROSTEPS_PER_REVOLUTION; // Just in case ;-)
519  if (FORWARD == AxisDirectionDEC)
520  CurrentEncoderMicrostepsDEC += SlewSteps;
521  else
522  CurrentEncoderMicrostepsDEC -= SlewSteps;
523  if (CurrentEncoderMicrostepsDEC > MAX_DEC)
524  {
525  CurrentEncoderMicrostepsDEC = MAX_DEC;
526  AxisStatusDEC = STOPPED; // Hit the buffers
527  DEBUG(DBG_SIMULATOR, "TimerHit - DEC axis hit the buffers at MAX_DEC");
528  }
529  else if (CurrentEncoderMicrostepsDEC < MIN_DEC)
530  {
531  CurrentEncoderMicrostepsDEC = MIN_DEC;
532  AxisStatusDEC = STOPPED; // Hit the buffers
533  DEBUG(DBG_SIMULATOR, "TimerHit - DEC axis hit the buffers at MIN_DEC");
534  }
535 
536  DEBUGF(DBG_SIMULATOR, "TimerHit Slewing - DEC New Encoder %d New Status %d", CurrentEncoderMicrostepsDEC,
537  AxisStatusDEC);
538  break;
539  }
540 
541  case SLEWING_TO:
542  {
543  DEBUGF(DBG_SIMULATOR,
544  "TimerHit SlewingTo - DEC Current Encoder %ld SlewSteps %d Direction %ld Target %ld Status %d",
545  CurrentEncoderMicrostepsDEC, SlewSteps, AxisDirectionDEC, GotoTargetMicrostepsDEC, AxisStatusDEC);
546 
547  // Calculate steps to target
548  int StepsToTarget;
549  if (FORWARD == AxisDirectionDEC)
550  {
551  if (CurrentEncoderMicrostepsDEC <= GotoTargetMicrostepsDEC)
552  StepsToTarget = GotoTargetMicrostepsDEC - CurrentEncoderMicrostepsDEC;
553  else
554  StepsToTarget = CurrentEncoderMicrostepsDEC - GotoTargetMicrostepsDEC;
555  }
556  else
557  {
558  // Axis in reverse
559  if (CurrentEncoderMicrostepsDEC >= GotoTargetMicrostepsDEC)
560  StepsToTarget = CurrentEncoderMicrostepsDEC - GotoTargetMicrostepsDEC;
561  else
562  StepsToTarget = GotoTargetMicrostepsDEC - CurrentEncoderMicrostepsDEC;
563  }
564  if (StepsToTarget <= SlewSteps)
565  {
566  // Target was hit this tick
567  AxisStatusDEC = STOPPED;
568  CurrentEncoderMicrostepsDEC = GotoTargetMicrostepsDEC;
569  }
570  else
571  {
572  if (FORWARD == AxisDirectionDEC)
573  CurrentEncoderMicrostepsDEC += SlewSteps;
574  else
575  CurrentEncoderMicrostepsDEC -= SlewSteps;
576  if (CurrentEncoderMicrostepsDEC < 0)
577  CurrentEncoderMicrostepsDEC += MICROSTEPS_PER_REVOLUTION;
578  else if (CurrentEncoderMicrostepsDEC >= MICROSTEPS_PER_REVOLUTION)
579  CurrentEncoderMicrostepsDEC -= MICROSTEPS_PER_REVOLUTION;
580  }
581 
582  DEBUGF(DBG_SIMULATOR, "TimerHit SlewingTo - DEC New Encoder %d New Status %d", CurrentEncoderMicrostepsDEC,
583  AxisStatusDEC);
584  break;
585  }
586  }
587 
588  INDI::Telescope::TimerHit(); // This will call ReadScopeStatus
589 
590  // OK I have updated the celestial reference frame RA/DEC in ReadScopeStatus
591  // Now handle the tracking state
592  switch (TrackState)
593  {
594  case SCOPE_SLEWING:
595  if ((STOPPED == AxisStatusRA) && (STOPPED == AxisStatusDEC))
596  {
597  if (ISS_ON == IUFindSwitch(&CoordSP, "TRACK")->s)
598  {
599  // Goto has finished start tracking
600  DEBUG(DBG_SIMULATOR, "TimerHit - Goto finished start tracking");
602  // Fall through to tracking case
603  }
604  else
605  {
607  break;
608  }
609  }
610  else
611  break;
612 
613  case SCOPE_TRACKING:
614  {
615  // Continue or start tracking
616  // Calculate where the mount needs to be in POLLMS time
617  // POLLMS is hardcoded to be one second
618  // TODO may need to make this longer to get a meaningful result
619  double JulianOffset = 1.0 / (24.0 * 60 * 60);
622 
623  if (TransformCelestialToTelescope(CurrentTrackingTarget.rightascension, CurrentTrackingTarget.declination, JulianOffset,
624  TDV))
626  else
627  {
628 
629  INDI::IEquatorialCoordinates EquatorialCoordinates { 0, 0 };
630  EquatorialCoordinates.rightascension = CurrentTrackingTarget.rightascension;
631  EquatorialCoordinates.declination = CurrentTrackingTarget.declination;
632  INDI::EquatorialToHorizontal(&EquatorialCoordinates, &m_Location, ln_get_julian_from_sys() + JulianOffset, &AltAz);
633  INDI::EquatorialToHorizontal(&EquatorialCoordinates, &m_Location, ln_get_julian_from_sys() + JulianOffset,
634  &AltAz);
635 
636  }
637 
638  // My altitude encoder runs -90 to +90
639  if ((AltAz.altitude > 90.0) || (AltAz.altitude < -90.0))
640  {
641  DEBUG(DBG_SIMULATOR, "TimerHit tracking - Altitude out of range");
642  // This should not happen
643  return;
644  }
645 
646  // My polar encoder runs 0 to +360
647  if ((AltAz.azimuth > 360.0) || (AltAz.azimuth < -360.0))
648  {
649  DEBUG(DBG_SIMULATOR, "TimerHit tracking - Azimuth out of range");
650  // This should not happen
651  return;
652  }
653 
654  if (AltAz.azimuth < 0.0)
655  {
656  DEBUG(DBG_SIMULATOR, "TimerHit tracking - Azimuth negative");
657  AltAz.azimuth = 360.0 + AltAz.azimuth;
658  }
659 
660  long AltitudeOffsetMicrosteps = int(AltAz.altitude * MICROSTEPS_PER_DEGREE - CurrentEncoderMicrostepsDEC);
661  long AzimuthOffsetMicrosteps = int(AltAz.azimuth * MICROSTEPS_PER_DEGREE - CurrentEncoderMicrostepsRA);
662 
663  DEBUGF(DBG_SIMULATOR, "TimerHit - Tracking AltitudeOffsetMicrosteps %d AzimuthOffsetMicrosteps %d",
664  AltitudeOffsetMicrosteps, AzimuthOffsetMicrosteps);
665 
666  if (0 != AzimuthOffsetMicrosteps)
667  {
668  // Calculate the slewing rates needed to reach that position
669  // at the correct time. This is simple as interval is one second
670  if (AzimuthOffsetMicrosteps > 0)
671  {
672  if (AzimuthOffsetMicrosteps < MICROSTEPS_PER_REVOLUTION / 2.0)
673  {
674  // Forward
675  AxisDirectionRA = FORWARD;
676  AxisSlewRateRA = AzimuthOffsetMicrosteps;
677  }
678  else
679  {
680  // Reverse
681  AxisDirectionRA = REVERSE;
682  AxisSlewRateRA = MICROSTEPS_PER_REVOLUTION - AzimuthOffsetMicrosteps;
683  }
684  }
685  else
686  {
687  AzimuthOffsetMicrosteps = std::abs(AzimuthOffsetMicrosteps);
688  if (AzimuthOffsetMicrosteps < MICROSTEPS_PER_REVOLUTION / 2.0)
689  {
690  // Forward
691  AxisDirectionRA = REVERSE;
692  AxisSlewRateRA = AzimuthOffsetMicrosteps;
693  }
694  else
695  {
696  // Reverse
697  AxisDirectionRA = FORWARD;
698  AxisSlewRateRA = MICROSTEPS_PER_REVOLUTION - AzimuthOffsetMicrosteps;
699  }
700  }
701  AxisSlewRateRA = std::abs(AzimuthOffsetMicrosteps);
702  AxisDirectionRA = AzimuthOffsetMicrosteps > 0 ? FORWARD : REVERSE; // !!!! BEWARE INERTIA FREE MOUNT
703  AxisStatusRA = SLEWING;
704  DEBUGF(DBG_SIMULATOR, "TimerHit - Tracking AxisSlewRateRA %lf AxisDirectionRA %d", AxisSlewRateRA,
705  AxisDirectionRA);
706  }
707  else
708  {
709  // Nothing to do - stop the axis
710  AxisStatusRA = STOPPED; // !!!! BEWARE INERTIA FREE MOUNT
711  DEBUG(DBG_SIMULATOR, "TimerHit - Tracking nothing to do stopping RA axis");
712  }
713 
714  if (0 != AltitudeOffsetMicrosteps)
715  {
716  // Calculate the slewing rates needed to reach that position
717  // at the correct time.
718  AxisSlewRateDEC = std::abs(AltitudeOffsetMicrosteps);
719  AxisDirectionDEC = AltitudeOffsetMicrosteps > 0 ? FORWARD : REVERSE; // !!!! BEWARE INERTIA FREE MOUNT
720  AxisStatusDEC = SLEWING;
721  DEBUGF(DBG_SIMULATOR, "TimerHit - Tracking AxisSlewRateDEC %lf AxisDirectionDEC %d", AxisSlewRateDEC,
722  AxisDirectionDEC);
723  }
724  else
725  {
726  // Nothing to do - stop the axis
727  AxisStatusDEC = STOPPED; // !!!! BEWARE INERTIA FREE MOUNT
728  DEBUG(DBG_SIMULATOR, "TimerHit - Tracking nothing to do stopping DEC axis");
729  }
730 
731  break;
732  }
733 
734  default:
735  break;
736  }
737 
738  TraceThisTick = false;
739 }
740 
741 bool ScopeSim::updateLocation(double latitude, double longitude, double elevation)
742 {
743  UpdateLocation(latitude, longitude, elevation);
744  return true;
745 }
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)
Initilize alignment subsystem properties. It is recommended to call this function within initProperti...
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 ...
AlignmentDatabaseType & GetAlignmentDatabase()
Get a reference to the in memory database.
bool CheckForDuplicateSyncPoint(const AlignmentDatabaseEntry &CandidateEntry, double Tolerance=0.1) const
Check if a entry already exists in the 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 TransformTelescopeToCelestial(const TelescopeDirectionVector &ApparentTelescopeDirectionVector, double &RightAscension, double &Declination)
TransformTelescopeToCelestial Transforms Mount Coords to Celestial (Sky) Coordinates.
bool TransformCelestialToTelescope(const double RightAscension, const double Declination, double JulianOffset, TelescopeDirectionVector &ApparentTelescopeDirectionVector)
TransformCelestialToTelescope 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 alognment 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.
const char * getDeviceName() const
Definition: basedevice.cpp:821
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.
TelescopeStatus TrackState
ISwitchVectorProperty MovementNSSP
ISwitchVectorProperty AbortSP
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.
ISwitchVectorProperty CoordSP
INumberVectorProperty EqNP
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
INumber ScopeParametersN[4]
IGeographicCoordinates m_Location
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
ISwitchVectorProperty MovementWESP
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.
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.
double ra
double dec
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_ON
Definition: indiapi.h:152
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
INDI_DIR_WE
Definition: indibasetypes.h:55
@ DIRECTION_WEST
Definition: indibasetypes.h:56
INDI_DIR_NS
Definition: indibasetypes.h:48
@ DIRECTION_NORTH
Definition: indibasetypes.h:49
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
Implementations for common driver routines.
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indidevapi.c:148
ISwitch * IUFindSwitch(const ISwitchVectorProperty *svp, const char *name)
Find an ISwitch member in a vector switch property.
Definition: indidevapi.c:76
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
Definition: indidriver.c:1231
#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
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:140
void HorizontalToEquatorial(IHorizontalCoordinates *object, IGeographicCoordinates *observer, double JD, IEquatorialCoordinates *position)
HorizontalToEquatorial Calculate Equatorial EOD Coordinates from horizontal coordinates.
Definition: libastro.cpp:156
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 nomalised 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