Instrument Neutral Distributed Interface INDI  2.0.2
pmc8.cpp
Go to the documentation of this file.
1 /*
2  INDI Explore Scientific PMC8 driver
3 
4  Copyright (C) 2017 Michael Fulbright
5 
6  Additional contributors:
7  Thomas Olson, Copyright (C) 2019
8  Karl Rees, Copyright (C) 2019-2023
9  Martin Ruiz, Copyright (C) 2023
10 
11  Based on IEQPro driver.
12 
13  This library is free software; you can redistribute it and/or
14  modify it under the terms of the GNU Lesser General Public
15  License as published by the Free Software Foundation; either
16  version 2.1 of the License, or (at your option) any later version.
17 
18  This library is distributed in the hope that it will be useful,
19  but WITHOUT ANY WARRANTY; without even the implied warranty of
20  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21  Lesser General Public License for more details.
22 
23  You should have received a copy of the GNU Lesser General Public
24  License along with this library; if not, write to the Free Software
25  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
26 */
27 /* Experimental Mount selector switch G11 vs EXOS2 by Thomas Olson
28  *
29  */
30 
31 #include "pmc8.h"
32 
33 #include <indicom.h>
36 
37 #include <libnova/sidereal_time.h>
38 
39 #include <memory>
40 
41 #include <math.h>
42 #include <string.h>
43 
44 /* Simulation Parameters */
45 #define SLEWRATE 3 /* slew rate, degrees/s */
46 
47 #define MOUNTINFO_TAB "Mount Info"
48 
49 #define PMC8_DEFAULT_PORT 54372
50 #define PMC8_DEFAULT_IP_ADDRESS "192.168.47.1"
51 #define PMC8_TRACKING_AUTODETECT_INTERVAL 10
52 #define PMC8_VERSION_MAJOR 0
53 #define PMC8_VERSION_MINOR 5
54 
55 static std::unique_ptr<PMC8> scope(new PMC8());
56 
57 /* Constructor */
59 {
60  currentRA = ln_get_apparent_sidereal_time(ln_get_julian_from_sys());
61  if (LocationN[LOCATION_LATITUDE].value < 0)
62  currentDEC = -90;
63  else
64  currentDEC=90;
65 
66  DBG_SCOPE = INDI::Logger::getInstance().addDebugLevel("Scope Verbose", "SCOPE");
67 
71  9);
72 
74 }
75 
77 {
78 }
79 
80 const char *PMC8::getDefaultName()
81 {
82  return "PMC8";
83 }
84 
86 {
88 
89  // Serial Cable Connection Type
90  // Letting them choose standard cable can speed up connection time significantly
91  IUFillSwitch(&SerialCableTypeS[0], "SERIAL_CABLE_AUTO", "Auto", ISS_ON);
92  IUFillSwitch(&SerialCableTypeS[1], "SERIAL_CABLE_INVERTED", "Inverted", ISS_OFF);
93  IUFillSwitch(&SerialCableTypeS[2], "SERIAL_CABLE_STANDARD", "Standard", ISS_OFF);
94  IUFillSwitchVector(&SerialCableTypeSP, SerialCableTypeS, 3, getDeviceName(), "SERIAL_CABLE_TYPE", "Serial Cable",
96 
97  // Mount Type
98  IUFillSwitch(&MountTypeS[MOUNT_G11], "MOUNT_G11", "G11", ISS_OFF);
99  IUFillSwitch(&MountTypeS[MOUNT_EXOS2], "MOUNT_EXOS2", "EXOS2", ISS_OFF);
100  IUFillSwitch(&MountTypeS[MOUNT_iEXOS100], "MOUNT_iEXOS100", "iEXOS100", ISS_OFF);
101  IUFillSwitchVector(&MountTypeSP, MountTypeS, 3, getDeviceName(), "MOUNT_TYPE", "Mount Type", CONNECTION_TAB, IP_RW,
102  ISR_1OFMANY, 0, IPS_IDLE);
103 
104 
105  /* Tracking Mode */
106  // order is important, since driver assumes solar = 1, lunar = 2
107  AddTrackMode("TRACK_SIDEREAL", "Sidereal", true);
108  AddTrackMode("TRACK_SOLAR", "Solar");
109  AddTrackMode("TRACK_LUNAR", "Lunar");
110  //AddTrackMode("TRACK_KING", "King"); // King appears to be effectively the same as Solar, at least for EXOS-2, and a bit of pain to implement with auto-detection
111  AddTrackMode("TRACK_CUSTOM", "Custom");
112 
113  // Set TrackRate limits
114  /*TrackRateN[AXIS_RA].min = -PMC8_MAX_TRACK_RATE;
115  TrackRateN[AXIS_RA].max = PMC8_MAX_TRACK_RATE;
116  TrackRateN[AXIS_DE].min = -0.01;
117  TrackRateN[AXIS_DE].max = 0.01;*/
118 
119  // what to do after goto operation
120  IUFillSwitch(&PostGotoS[0], "GOTO_START_TRACKING", "Start / Resume Tracking", ISS_ON);
121  IUFillSwitch(&PostGotoS[1], "GOTO_RESUME_PREVIOUS", "Previous State", ISS_OFF);
122  IUFillSwitch(&PostGotoS[2], "GOTO_STOP_TRACKING", "No Tracking", ISS_OFF);
123  IUFillSwitchVector(&PostGotoSP, PostGotoS, 3, getDeviceName(), "POST_GOTO_SETTINGS", "Post Goto", MOTION_TAB, IP_RW,
124  ISR_1OFMANY, 0, IPS_IDLE);
125 
126  // relabel move speeds
127  strcpy(SlewRateSP.sp[0].label, "4x");
128  strcpy(SlewRateSP.sp[1].label, "8x");
129  strcpy(SlewRateSP.sp[2].label, "16x");
130  strcpy(SlewRateSP.sp[3].label, "32x");
131  strcpy(SlewRateSP.sp[4].label, "64x");
132  strcpy(SlewRateSP.sp[5].label, "128x");
133  strcpy(SlewRateSP.sp[6].label, "256x");
134  strcpy(SlewRateSP.sp[7].label, "512x");
135  strcpy(SlewRateSP.sp[8].label, "833x");
136 
137  // settings for ramping up/down when moving
138  IUFillNumber(&RampN[0], "RAMP_INTERVAL", "Interval (ms)", "%g", 20, 1000, 5, 200);
139  IUFillNumber(&RampN[1], "RAMP_BASESTEP", "Base Step", "%g", 1, 256, 1, 4);
140  IUFillNumber(&RampN[2], "RAMP_FACTOR", "Factor", "%g", 1.0, 2.0, 0.1, 1.4);
141  IUFillNumberVector(&RampNP, RampN, 3, getDeviceName(), "RAMP_SETTINGS", "Move Ramp", MOTION_TAB, IP_RW, 0, IPS_IDLE);
142 
143  /* How fast do we guide compared to sidereal rate */
144  IUFillNumber(&GuideRateN[0], "GUIDE_RATE_RA", "RA (x Sidereal)", "%g", 0.1, 1.0, 0.1, 0.4);
145  IUFillNumber(&GuideRateN[1], "GUIDE_RATE_DE", "DEC (x Sidereal)", "%g", 0.1, 1.0, 0.1, 0.4);
146  IUFillNumberVector(&GuideRateNP, GuideRateN, 2, getDeviceName(), "GUIDE_RATE", "Guide Rate", GUIDE_TAB, IP_RW, 0, IPS_IDLE);
147  IUFillNumber(&LegacyGuideRateN[0], "LEGACY_GUIDE_RATE", "x Sidereal", "%g", 0.1, 1.0, 0.1, 0.4);
148  IUFillNumberVector(&LegacyGuideRateNP, LegacyGuideRateN, 1, getDeviceName(), "LEGACY_GUIDE_RATE", "Guide Rate", GUIDE_TAB,
149  IP_RW, 0, IPS_IDLE);
150 
152 
154 
155  // Driver does not support custom parking yet.
157 
158  addAuxControls();
159 
161 
162  IUFillText(&FirmwareT[0], "Version", "Version", "");
163  IUFillTextVector(&FirmwareTP, FirmwareT, 1, getDeviceName(), "Firmware", "Firmware", MAIN_CONTROL_TAB, IP_RO, 0, IPS_IDLE);
164 
166 
167  return true;
168 }
169 
171 {
173 
174  if (isConnected())
175  {
176  getStartupData();
177 
178  defineProperty(&PostGotoSP);
179  loadConfig(true, PostGotoSP.name);
180 
181  defineProperty(&RampNP);
182  loadConfig(true, RampNP.name);
183 
184  if (firmwareInfo.IsRev2Compliant)
185  {
186  defineProperty(&GuideRateNP);
187  }
188  else
189  {
190  defineProperty(&LegacyGuideRateNP);
191  }
192 
195 
196  defineProperty(&FirmwareTP);
197 
198  // do not support park position
201  }
202  else
203  {
204  deleteProperty(PostGotoSP.name);
205 
208 
209  if (firmwareInfo.IsRev2Compliant)
210  {
211  deleteProperty(GuideRateNP.name);
212  }
213  else
214  {
215  deleteProperty(LegacyGuideRateNP.name);
216  }
217 
218  deleteProperty(FirmwareTP.name);
219 
220  deleteProperty(RampNP.name);
221  }
222 
223  return true;
224 }
225 
226 void PMC8::getStartupData()
227 {
228  LOG_DEBUG("Getting firmware data...");
229  if (get_pmc8_firmware(PortFD, &firmwareInfo))
230  {
231  const char *c;
232 
233  FirmwareTP.s = IPS_OK;
234  c = firmwareInfo.MainBoardFirmware.c_str();
235  LOGF_INFO("firmware = %s.", c);
236 
237  // not sure if there's really a point to the mount switch anymore if we know the mount from the firmware - perhaps remove as newer firmware becomes standard?
238  // populate mount type switch in interface from firmware if possible
239  if (firmwareInfo.MountType == MOUNT_EXOS2)
240  {
241  MountTypeS[MOUNT_EXOS2].s = ISS_ON;
242  LOG_INFO("Detected mount type as Exos2.");
243  }
244  else if (firmwareInfo.MountType == MOUNT_G11)
245  {
246  MountTypeS[MOUNT_G11].s = ISS_ON;
247  LOG_INFO("Detected mount type as G11.");
248  }
249  else if (firmwareInfo.MountType == MOUNT_iEXOS100)
250  {
251  MountTypeS[MOUNT_iEXOS100].s = ISS_ON;
252  LOG_INFO("Detected mount type as iExos100.");
253  }
254  else
255  {
256  LOG_INFO("Cannot detect mount type--perhaps this is older firmware?");
257  if (strstr(getDeviceName(), "EXOS2"))
258  {
259  MountTypeS[MOUNT_EXOS2].s = ISS_ON;
260  LOG_INFO("Guessing mount is EXOS2 from device name.");
261  }
262  else if (strstr(getDeviceName(), "iEXOS100"))
263  {
264  MountTypeS[MOUNT_iEXOS100].s = ISS_ON;
265  LOG_INFO("Guessing mount is iEXOS100 from device name.");
266  }
267  else
268  {
269  MountTypeS[MOUNT_G11].s = ISS_ON;
270  LOG_INFO("Guessing mount is G11.");
271  }
272  }
273  MountTypeSP.s = IPS_OK;
274  IDSetSwitch(&MountTypeSP, nullptr);
275 
276  IUSaveText(&FirmwareT[0], c);
277  IDSetText(&FirmwareTP, nullptr);
278  }
279 
280  // get SRF values
281  if (firmwareInfo.IsRev2Compliant)
282  {
283  double rate = 0.4;
285  {
286  GuideRateN[0].value = rate;
287  GuideRateNP.s = IPS_OK;
288  IDSetNumber(&GuideRateNP, nullptr);
289  }
291  {
292  GuideRateN[1].value = rate;
293  GuideRateNP.s = IPS_OK;
294  IDSetNumber(&GuideRateNP, nullptr);
295  }
296  }
297 
298  // PMC8 doesn't store location permanently so read from config and set
299  // Convert to INDI standard longitude (0 to 360 Eastward)
300  double longitude = LocationN[LOCATION_LONGITUDE].value;
301  double latitude = LocationN[LOCATION_LATITUDE].value;
302  if (latitude < 0)
303  currentDEC = -90;
304  else
305  currentDEC = 90;
306 
307 
308  // must also keep "low level" aware of position to convert motor counts to RA/DEC
309  set_pmc8_location(latitude, longitude);
310 
311  // seems like best place to put a warning that will be seen in log window of EKOS/etc
312  LOG_INFO("The PMC-Eight driver is in BETA development currently.");
313  LOG_INFO("Be prepared to intervene if something unexpected occurs.");
314 
315 #if 0
316  // FIXEME - Need to handle southern hemisphere for DEC?
317  double HA = ln_get_apparent_sidereal_time(ln_get_julian_from_sys());
318  double DEC = CurrentDEC;
319 
320  // currently only park at motor position (0, 0)
321  if (InitPark())
322  {
323  // If loading parking data is successful, we just set the default parking values.
325  SetAxis2ParkDefault(DEC);
326  }
327  else
328  {
329  // Otherwise, we set all parking data to default in case no parking data is found.
330  SetAxis1Park(HA);
331  SetAxis2Park(DEC);
333  SetAxis2ParkDefault(DEC);
334  }
335 #endif
336 
337 #if 0
338  // FIXME - Need to implement simulation functionality
339  if (isSimulation())
340  {
341  if (isParked())
343  else
345  }
346 #endif
347 }
348 
349 bool PMC8::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
350 {
351  if (!strcmp(dev, getDeviceName()))
352  {
353  // Guiding Rate
354  if (!strcmp(name, RampNP.name))
355  {
356  IUUpdateNumber(&RampNP, values, names, n);
357  RampNP.s = IPS_OK;
358  IDSetNumber(&RampNP, nullptr);
359 
360  return true;
361  }
362  if (!strcmp(name, LegacyGuideRateNP.name))
363  {
364  IUUpdateNumber(&GuideRateNP, values, names, n);
365 
366  if (set_pmc8_guide_rate(PortFD, PMC8_AXIS_RA, LegacyGuideRateN[0].value))
367  LegacyGuideRateNP.s = IPS_OK;
368  else
369  LegacyGuideRateNP.s = IPS_ALERT;
370 
371  IDSetNumber(&LegacyGuideRateNP, nullptr);
372 
373  return true;
374  }
375  if (!strcmp(name, GuideRateNP.name))
376  {
377  IUUpdateNumber(&GuideRateNP, values, names, n);
378 
379  if (set_pmc8_guide_rate(PortFD, PMC8_AXIS_RA, GuideRateN[0].value) &&
380  set_pmc8_guide_rate(PortFD, PMC8_AXIS_DEC, GuideRateN[1].value))
381  GuideRateNP.s = IPS_OK;
382  else
383  GuideRateNP.s = IPS_ALERT;
384 
385  IDSetNumber(&GuideRateNP, nullptr);
386 
387  return true;
388  }
389  if (!strcmp(name, GuideNSNP.name) || !strcmp(name, GuideWENP.name))
390  {
391  processGuiderProperties(name, values, names, n);
392  return true;
393  }
394  // Track Rate - auto change to custom track rate when setting
395  if (!strcmp(name, TrackRateNP.name))
396  {
399  TrackModeSP.s = IPS_OK;
400  IDSetSwitch(&TrackModeSP, nullptr);
401  return true;
402  }
403  }
404 
405  return INDI::Telescope::ISNewNumber(dev, name, values, names, n);
406 }
407 
408 void PMC8::ISGetProperties(const char *dev)
409 {
411  defineProperty(&MountTypeSP);
412  defineProperty(&SerialCableTypeSP);
413  loadConfig(true,SerialCableTypeSP.name);
414 
415  // set default connection parameters
416  // unfortunately, the only way I've found to set these is after calling ISGetProperties on base class
420 
421  // reload config here, even though it was already loaded in call to base class
422  // since defaults may have overridden saved properties
423  loadConfig(false,nullptr);
424 }
425 
426 bool PMC8::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
427 {
428  if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
429  {
430  if (strcmp(name, MountTypeSP.name) == 0)
431  {
432  IUUpdateSwitch(&MountTypeSP, states, names, n);
433  int currentMountIndex = IUFindOnSwitchIndex(&MountTypeSP);
434  LOGF_INFO("Selected mount is %s", MountTypeS[currentMountIndex].label);
435 
436  //right now, this lets the user override the parameters for the detected mount. Perhaps we should prevent the user from doing so?
437  set_pmc8_mountParameters(currentMountIndex);
438  MountTypeSP.s = IPS_OK;
439  IDSetSwitch(&MountTypeSP, nullptr);
440  return true;
441  }
442  if (strcmp(name, SerialCableTypeSP.name) == 0)
443  {
444  IUUpdateSwitch(&SerialCableTypeSP, states, names, n);
445  SerialCableTypeSP.s = IPS_OK;
446  IDSetSwitch(&SerialCableTypeSP, nullptr);
447  return true;
448  }
449  if (strcmp(name, PostGotoSP.name) == 0)
450  {
451  IUUpdateSwitch(&PostGotoSP, states, names, n);
452  // for v2 firmware, if halt after goto is selected, tell driver to use ESPt2
453  set_pmc8_goto_resume(!((IUFindOnSwitchIndex(&PostGotoSP) == 2) && firmwareInfo.IsRev2Compliant));
454  PostGotoSP.s = IPS_OK;
455  IDSetSwitch(&PostGotoSP, nullptr);
456  return true;
457  }
458  }
459 
460  return INDI::Telescope::ISNewSwitch(dev, name, states, names, n);
461 }
462 
464 {
465  bool rc = false;
466 
467  // try to disconnect and reconnect if reconnect flag is set
468 
470  {
471  int rc = Disconnect();
472  if (rc) setConnected(false);
473  rc = Connect();
474  if (rc) setConnected(true, IPS_OK);
475  return false;
476  }
477 
478  if (isSimulation())
479  mountSim();
480 
481  // avoid unnecessary status calls to mount while pulse guiding so we don't lock up the mount for 40+ ms right when it needs to start/stop
482  if (isPulsingNS || isPulsingWE) return true;
483 
484  bool slewing = false;
485 
486  switch (TrackState)
487  {
488  case SCOPE_SLEWING:
489  // are we done?
490  // check slew state
491  rc = get_pmc8_is_scope_slewing(PortFD, slewing);
492  if (!rc)
493  {
494  LOG_ERROR("PMC8::ReadScopeStatus() - unable to check slew state");
495  }
496  else
497  {
498  if (slewing == false)
499  {
500  if ((IUFindOnSwitchIndex(&PostGotoSP) == 0) ||
501  ((IUFindOnSwitchIndex(&PostGotoSP) == 1) && (RememberTrackState == SCOPE_TRACKING)))
502  {
503  LOG_INFO("Slew complete, tracking...");
506 
507  // Don't want to restart tracking after goto with v2 firmware, since mount does automatically
508  // and we might detect that slewing has stopped before it fully settles
509  if (!firmwareInfo.IsRev2Compliant)
510  {
511  if (!SetTrackEnabled(true))
512  {
513  LOG_ERROR("slew complete - unable to enable tracking");
514  return false;
515  }
516  }
517  }
518  else
519  {
520  LOG_INFO("Slew complete.");
522  }
523  }
524  }
525 
526  break;
527 
528  case SCOPE_PARKING:
529  // are we done?
530  // check slew state
531  rc = get_pmc8_is_scope_slewing(PortFD, slewing);
532  if (!rc)
533  {
534  LOG_ERROR("PMC8::ReadScopeStatus() - unable to check slew state");
535  }
536  else
537  {
538  if (slewing == false)
539  {
541  LOG_DEBUG("Mount tracking is off.");
542 
543  SetParked(true);
544 
545  saveConfig(true);
546  }
547  }
548  break;
549 
550  case SCOPE_IDLE:
551  //periodically check to see if we've entered tracking state (e.g. at startup or from other client)
552  if (!trackingPollCounter--)
553  {
554 
555  trackingPollCounter = PMC8_TRACKING_AUTODETECT_INTERVAL;
556 
557  // make sure we aren't moving manually to avoid false positives
558  if (moveInfoDEC.state == PMC8_MOVE_INACTIVE && moveInfoRA.state == PMC8_MOVE_INACTIVE)
559  {
560 
561  double track_rate;
562  uint8_t track_mode;
563 
564  rc = get_pmc8_tracking_data(PortFD, track_rate, track_mode);
565 
566  if (rc && ((int)track_rate > 0) && ((int)track_rate <= PMC8_MAX_TRACK_RATE))
567  {
569  TrackModeS[convertFromPMC8TrackMode(track_mode)].s = ISS_ON;
570  TrackModeSP.s = IPS_OK;
571  IDSetSwitch(&TrackModeSP, nullptr);
573  LOGF_INFO("Mount has started tracking at %f arcsec / sec", track_rate);
575  TrackRateN[AXIS_RA].value = track_rate;
576  IDSetNumber(&TrackRateNP, nullptr);
577  }
578  }
579  }
580  break;
581 
582  case SCOPE_TRACKING:
583  //periodically check to see if we've stopped tracking or changed speed (e.g. from other client)
584  if (!trackingPollCounter--)
585  {
586  trackingPollCounter = PMC8_TRACKING_AUTODETECT_INTERVAL;
587 
588  // make sure we aren't moving manually to avoid false positives
589  if (moveInfoDEC.state == PMC8_MOVE_INACTIVE && moveInfoRA.state == PMC8_MOVE_INACTIVE)
590  {
591 
592  double track_rate;
593  uint8_t track_mode;
594 
595  rc = get_pmc8_tracking_data(PortFD, track_rate, track_mode);
596 
597  if (rc && ((int)track_rate == 0))
598  {
599  LOG_INFO("Mount appears to have stopped tracking");
601  }
602  else if (rc && ((int)track_rate <= PMC8_MAX_TRACK_RATE))
603  {
604  if (TrackModeS[convertFromPMC8TrackMode(track_mode)].s != ISS_ON)
605  {
607  TrackModeS[convertFromPMC8TrackMode(track_mode)].s = ISS_ON;
608  IDSetSwitch(&TrackModeSP, nullptr);
609  }
610  if (TrackRateN[AXIS_RA].value != track_rate)
611  {
614  TrackRateN[AXIS_RA].value = track_rate;
615  IDSetNumber(&TrackRateNP, nullptr);
616  LOGF_INFO("Mount now tracking at %f arcsec / sec", track_rate);
617  }
618  }
619  }
620  }
621 
622  default:
623  break;
624  }
625 
626  rc = get_pmc8_coords(PortFD, currentRA, currentDEC);
627 
628  if (rc)
629  NewRaDec(currentRA, currentDEC);
630 
631  return rc;
632 }
633 
634 bool PMC8::Goto(double r, double d)
635 {
636  if (isPulsingNS ||
637  isPulsingWE ||
638  moveInfoDEC.state != PMC8_MOVE_INACTIVE ||
639  moveInfoRA.state != PMC8_MOVE_INACTIVE ||
640  (TrackState == SCOPE_SLEWING && !firmwareInfo.IsRev2Compliant))
641  {
642  LOG_ERROR("Cannot slew while moving or guiding. Please stop moving or guiding first");
643  return false;
644  }
645  else if (TrackState == SCOPE_SLEWING)
646  {
647  targetRA = r;
648  targetDEC = d;
650  //Supposedly the goto should abort in 2s, but we'll give it a little bit more time just in case
652  LOG_INFO("Goto called while already slewing. Stopping slew and will try goto again in 2.5 seconds");
653  return true;
654  }
655 
656  // start tracking if we're idle, so mount will track at correct rate post-goto
658  if ((TrackState != SCOPE_TRACKING) && (IUFindOnSwitchIndex(&PostGotoSP) == 0) && firmwareInfo.IsRev2Compliant)
659  {
660  SetTrackEnabled(true);
661  }
662  else if (IUFindOnSwitchIndex(&PostGotoSP) == 2)
663  {
665  }
666 
667  char RAStr[64] = {0}, DecStr[64] = {0};
668 
669  targetRA = r;
670  targetDEC = d;
671 
672  fs_sexa(RAStr, targetRA, 2, 3600);
673  fs_sexa(DecStr, targetDEC, 2, 3600);
674 
675  LOGF_DEBUG("Slewing to RA: %s - DEC: %s", RAStr, DecStr);
676 
677  if (slew_pmc8(PortFD, r, d) == false)
678  {
679  LOG_ERROR("Failed to slew.");
680  return false;
681  }
682 
684 
685  return true;
686 }
687 
688 bool PMC8::Sync(double ra, double dec)
689 {
690 
691  targetRA = ra;
692  targetDEC = dec;
693  char RAStr[64] = {0}, DecStr[64] = {0};
694 
695  fs_sexa(RAStr, targetRA, 2, 3600);
696  fs_sexa(DecStr, targetDEC, 2, 3600);
697 
698  LOGF_DEBUG("Syncing to RA: %s - DEC: %s", RAStr, DecStr);
699 
700  if (sync_pmc8(PortFD, ra, dec) == false)
701  {
702  LOG_ERROR("Failed to sync.");
703  }
704 
705  EqNP.s = IPS_OK;
706 
707  currentRA = ra;
708  currentDEC = dec;
709 
710  NewRaDec(currentRA, currentDEC);
711 
712  return true;
713 }
714 
716 {
717  //static_cast<PMC8*>(p)->TrackState = static_cast<PMC8*>(p)->RememberTrackState;
718  static_cast<PMC8*>(p)->Goto(static_cast<PMC8*>(p)->targetRA,static_cast<PMC8*>(p)->targetDEC);
719 }
720 
722 {
723  //GUIDE Abort guide operations.
724  if (GuideNSNP.s == IPS_BUSY || GuideWENP.s == IPS_BUSY)
725  {
727  GuideNSN[0].value = GuideNSN[1].value = 0.0;
728  GuideWEN[0].value = GuideWEN[1].value = 0.0;
729 
730  if (GuideNSTID)
731  {
733  GuideNSTID = 0;
734  }
735 
736  if (GuideWETID)
737  {
739  GuideNSTID = 0;
740  }
741 
742  LOG_INFO("Guide aborted.");
743  IDSetNumber(&GuideNSNP, nullptr);
744  IDSetNumber(&GuideWENP, nullptr);
745  return true;
746  }
747 
748 
749  //GOTO Abort slew operations.
750  if (TrackState == SCOPE_SLEWING)
751  {
753  //It will take about 2s to abort; we'll rely on ReadScopeStatus to detect when that occurs
754  LOG_INFO("Goto aborted.");
755  return true;
756  }
757 
758  //MOVE Abort move operations.
759  if ((moveInfoDEC.state == PMC8_MOVE_ACTIVE) || (moveInfoRA.state == PMC8_MOVE_ACTIVE))
760  {
761  if (moveInfoDEC.state == PMC8_MOVE_ACTIVE)
762  {
763  MoveNS((INDI_DIR_NS)moveInfoDEC.moveDir, MOTION_STOP);
764  }
765  if (moveInfoRA.state == PMC8_MOVE_ACTIVE)
766  {
767  MoveWE((INDI_DIR_WE)moveInfoRA.moveDir, MOTION_STOP);
768  }
769  LOG_INFO("Move aborted.");
770  return true;
771  }
772 
773  LOG_INFO("Abort called--stopping all motion.");
774  if (abort_pmc8(PortFD))
775  {
777  return true;
778  }
779  else return false;
780 }
781 
783 {
784 #if 0
785  // FIXME - Currently only support parking at motor position (0, 0)
786  targetRA = GetAxis1Park();
787  targetDEC = GetAxis2Park();
788  if (set_pmc8_radec(PortFD, r, d) == false)
789  {
790  LOG_ERROR("Error setting RA/DEC.");
791  return false;
792  }
793 #endif
794 
795  //if we're already parking, no need to do anything
796  if (TrackState == SCOPE_PARKING)
797  {
798  return true;
799  }
800 
801  if (park_pmc8(PortFD))
802  {
804  LOG_INFO("Telescope parking in progress to motor position (0, 0)");
805  return true;
806  }
807  else
808  {
809  return false;
810  }
811 }
812 
814 {
815  if (unpark_pmc8(PortFD))
816  {
817  SetParked(false);
819  return true;
820  }
821  else
822  {
823  return false;
824  }
825 }
826 
828 {
829  if (isSimulation())
830  {
833  set_pmc8_sim_move_rate(64 * 15);
834  // set_pmc8_sim_hemisphere(HEMI_NORTH);
835  }
836 
839  {
840  if (IUFindOnSwitchIndex(&SerialCableTypeSP) == 1) conn = PMC8_SERIAL_INVERTED;
841  if (IUFindOnSwitchIndex(&SerialCableTypeSP) == 2) conn = PMC8_SERIAL_STANDARD;
842  }
843  else
844  {
845  conn = PMC8_ETHERNET;
846  }
847 
848  return check_pmc8_connection(PortFD, conn);
849 }
850 
851 bool PMC8::updateTime(ln_date *utc, double utc_offset)
852 {
853  // mark unused
854  INDI_UNUSED(utc);
855  INDI_UNUSED(utc_offset);
856 
857  LOG_ERROR("PMC8::updateTime() not implemented!");
858  return false;
859 
860 }
861 
862 bool PMC8::updateLocation(double latitude, double longitude, double elevation)
863 {
864  INDI_UNUSED(elevation);
865 
866  if (longitude > 180)
867  longitude -= 360;
868 
869  // experimental support for Southern Hemisphere!
870  if (latitude < 0)
871  {
872  LOG_WARN("Southern Hemisphere support still experimental!");
873  //return false;
874  }
875 
876  // must also keep "low level" aware of position to convert motor counts to RA/DEC
877  set_pmc8_location(latitude, longitude);
878 
879  char l[32] = {0}, L[32] = {0};
880  fs_sexa(l, latitude, 3, 3600);
881  fs_sexa(L, longitude, 4, 3600);
882 
883  LOGF_INFO("Site location updated to Lat %.32s - Long %.32s", l, L);
884 
885  return true;
886 }
887 
888 void PMC8::debugTriggered(bool enable)
889 {
890  set_pmc8_debug(enable);
891 }
892 
893 void PMC8::simulationTriggered(bool enable)
894 {
895  set_pmc8_simulation(enable);
896 }
897 
899 {
900  int mode = IUFindOnSwitchIndex(&SlewRateSP);
901  if (mode >= 8) return PMC8_MAX_MOVE_RATE;
902  return 4 * pow(2, mode) * 15;
903 }
904 
905 
907 {
908 
909  PMC8MoveInfo *moveInfo = ((dir == PMC8_N) | (dir == PMC8_S)) ? &moveInfoDEC : &moveInfoRA;
910 
911  if (moveInfo->state != PMC8_MOVE_RAMPING)
912  {
913  return false; //shouldn't be here
914  LOG_ERROR("Ramp function called while not in ramp state");
915  }
916 
917  int newrate = moveInfo->rampLastStep;
918 
919  if (moveInfo->rampDir == PMC8_RAMP_UP)
920  {
921  newrate += RampN[1].value * pow(RampN[2].value, moveInfo->rampIteration++) * 15;
922  }
923  else
924  {
925  newrate -= RampN[1].value * pow(RampN[2].value, --moveInfo->rampIteration) * 15;
926  }
927 
928  int adjrate = newrate;
929 
930  //check to see if we're done
931  if (newrate >= moveInfo->targetRate)
932  {
933  adjrate = moveInfo->targetRate;
934  moveInfo->state = PMC8_MOVE_ACTIVE;
935  }
936  else if (newrate <= 0)
937  {
938  adjrate = 0;
939  moveInfo->state = PMC8_MOVE_INACTIVE;
940  //restore tracking if we're at 0
941  if ((dir == PMC8_E) || (dir == PMC8_W))
942  {
943  if (TrackState == SCOPE_TRACKING)
944  {
945  if (!SetTrackEnabled(true))
946  {
947  LOG_ERROR("slew complete - unable to enable tracking");
948  return false;
949  }
950  }
951 
952  return true;
953  }
954  }
955 
956  //adjust for current tracking rate
957  if (dir == PMC8_E) adjrate += round(TrackRateN[AXIS_RA].value);
958  else if (dir == PMC8_W) adjrate -= round(TrackRateN[AXIS_RA].value);
959 
960  LOGF_EXTRA3("Ramping: mount dir %d, ramping dir %d, iteration %d, step to %d", dir, moveInfo->rampDir,
961  moveInfo->rampIteration, adjrate);
962 
963  if (!set_pmc8_move_rate_axis(PortFD, dir, adjrate))
964  {
965  LOGF_ERROR("Error ramping move rate: mount dir %d, ramping dir %d, iteration %d, step to %d", dir, moveInfo->rampDir,
966  moveInfo->rampIteration, adjrate);
967  moveInfo->state = PMC8_MOVE_INACTIVE;
968  return false;
969  }
970 
971  moveInfo->rampLastStep = newrate;
972 
973  return true;
974 }
975 
976 //MOVE The timer helper functions.
978 {
979  PMC8* pmc8 = static_cast<PMC8*>(p);
980  if (pmc8->ramp_movement(PMC8_N) && (pmc8->moveInfoDEC.state == PMC8_MOVE_RAMPING))
981  pmc8->moveInfoDEC.timer = IEAddTimer(pmc8->RampN[0].value, rampTimeoutHelperN, p);
982 }
984 {
985  PMC8* pmc8 = static_cast<PMC8*>(p);
986  if (pmc8->ramp_movement(PMC8_S) && (pmc8->moveInfoDEC.state == PMC8_MOVE_RAMPING))
987  pmc8->moveInfoDEC.timer = IEAddTimer(pmc8->RampN[0].value, rampTimeoutHelperS, p);
988 }
990 {
991  PMC8* pmc8 = static_cast<PMC8*>(p);
992  if (pmc8->ramp_movement(PMC8_W) && (pmc8->moveInfoRA.state == PMC8_MOVE_RAMPING))
993  pmc8->moveInfoRA.timer = IEAddTimer(pmc8->RampN[0].value, rampTimeoutHelperW, p);
994 }
996 {
997  PMC8* pmc8 = static_cast<PMC8*>(p);
998  if (pmc8->ramp_movement(PMC8_E) && (pmc8->moveInfoRA.state == PMC8_MOVE_RAMPING))
999  pmc8->moveInfoRA.timer = IEAddTimer(pmc8->RampN[0].value, rampTimeoutHelperE, p);
1000 }
1001 
1002 
1004 {
1005  if (TrackState == SCOPE_PARKED)
1006  {
1007  LOG_ERROR("Please unpark the mount before issuing any motion commands.");
1008  return false;
1009  }
1010  if (TrackState == SCOPE_SLEWING)
1011  {
1012  LOG_ERROR("Mount is slewing. Wait to issue move command until goto completes.");
1013  return false;
1014  }
1015  if ((moveInfoDEC.state == PMC8_MOVE_ACTIVE) && (moveInfoDEC.moveDir != dir))
1016  {
1017  LOG_ERROR("Mount received command to move in opposite direction before stopping. This shouldn't happen.");
1018  return false;
1019  }
1020 
1021  // read desired move rate
1022  int currentIndex = IUFindOnSwitchIndex(&SlewRateSP);
1023  LOGF_DEBUG("MoveNS at slew index %d", currentIndex);
1024 
1025  switch (command)
1026  {
1027  case MOTION_START:
1028  moveInfoDEC.rampDir = PMC8_RAMP_UP;
1029  moveInfoDEC.targetRate = getSlewRate();
1030  // if we're still ramping down, we can bypass resetting the state and adding a timer
1031  // but we do need to make sure it's the same direction first (if not, kill our previous timer)
1032  if (moveInfoDEC.state == PMC8_MOVE_RAMPING)
1033  {
1034  if (moveInfoDEC.moveDir == dir) return true;
1035  IERmTimer(moveInfoDEC.timer);
1036  LOG_WARN("Started moving other direction before ramp down completed. This *may* cause mechanical problems with mount. It is adviseable to wait for axis movement to settle before switching directions.");
1037  }
1038  moveInfoDEC.moveDir = dir;
1039  moveInfoDEC.state = PMC8_MOVE_RAMPING;
1040  moveInfoDEC.rampIteration = 0;
1041  moveInfoDEC.rampLastStep = 0;
1042 
1043  LOGF_INFO("Moving toward %s.", (dir == DIRECTION_NORTH) ? "North" : "South");
1044 
1045  break;
1046 
1047  case MOTION_STOP:
1048  // if we've already started moving other direction, no need to stop
1049  if (moveInfoDEC.moveDir != dir)
1050  {
1051  LOGF_DEBUG("Stop command issued for direction %d, but we're not moving that way", dir);
1052  return false;
1053  }
1054 
1055  moveInfoDEC.rampDir = PMC8_RAMP_DOWN;
1056  // if we're still ramping up, we can bypass adding a timer
1057  if (moveInfoDEC.state == PMC8_MOVE_RAMPING) return true;
1058  moveInfoDEC.state = PMC8_MOVE_RAMPING;
1059 
1060  LOGF_INFO("%s motion stopping.", (dir == DIRECTION_NORTH) ? "North" : "South");
1061 
1062  break;
1063  }
1064 
1065  if (dir == DIRECTION_NORTH)
1066  rampTimeoutHelperN(this);
1067  else
1068  rampTimeoutHelperS(this);
1069 
1070  return true;
1071 }
1072 
1073 
1075 {
1076  if (TrackState == SCOPE_PARKED)
1077  {
1078  LOG_ERROR("Please unpark the mount before issuing any motion commands.");
1079  return false;
1080  }
1081  if (TrackState == SCOPE_SLEWING)
1082  {
1083  LOG_ERROR("Mount is already slewing. Wait to issue move command until done slewing.");
1084  return false;
1085  }
1086  if ((moveInfoRA.state == PMC8_MOVE_ACTIVE) && (moveInfoRA.moveDir != dir))
1087  {
1088  LOG_ERROR("Mount received command to move in opposite direction before stopping. This shouldn't happen.");
1089  return false;
1090  }
1091 
1092  // read desired move rate
1093  int currentIndex = IUFindOnSwitchIndex(&SlewRateSP);
1094  LOGF_DEBUG("MoveWE at slew index %d", currentIndex);
1095 
1096  switch (command)
1097  {
1098  case MOTION_START:
1099  moveInfoRA.rampDir = PMC8_RAMP_UP;
1100  moveInfoRA.targetRate = getSlewRate();
1101  // if we're still ramping down, we can bypass resetting the state and adding a timer
1102  // but we do need to make sure it's the same direction first (if not, kill our previous timer)
1103  if (moveInfoRA.state == PMC8_MOVE_RAMPING)
1104  {
1105  if (moveInfoRA.moveDir == dir) return true;
1106  IERmTimer(moveInfoRA.timer);
1107  LOG_WARN("Started moving other direction before ramp down completed. This *may* cause mechanical problems with mount. It is adviseable to wait for axis movement to settle before switching directions.");
1108  }
1109  moveInfoRA.moveDir = dir;
1110  moveInfoRA.state = PMC8_MOVE_RAMPING;
1111  moveInfoRA.rampIteration = 0;
1112  moveInfoRA.rampLastStep = 0;
1113 
1114  LOGF_INFO("Moving toward %s.", (dir == DIRECTION_WEST) ? "West" : "East");
1115 
1116  break;
1117 
1118  case MOTION_STOP:
1119  // if we've already started moving other direction, no need to stop
1120  if (moveInfoRA.moveDir != dir)
1121  {
1122  LOGF_DEBUG("Stop command issued for direction %d, but we're not moving that way", dir);
1123  return false;
1124  }
1125 
1126  moveInfoRA.rampDir = PMC8_RAMP_DOWN;
1127  // if we're still ramping up, we can bypass adding a timer
1128  if (moveInfoRA.state == PMC8_MOVE_RAMPING) return true;
1129  moveInfoRA.state = PMC8_MOVE_RAMPING;
1130 
1131  LOGF_INFO("%s motion stopping.", (dir == DIRECTION_WEST) ? "West" : "East");
1132 
1133  break;
1134  }
1135 
1136  if (dir == DIRECTION_EAST)
1137  rampTimeoutHelperE(this);
1138  else
1139  rampTimeoutHelperW(this);
1140 
1141  return true;
1142 }
1143 
1145 {
1146  IPState ret = IPS_IDLE;
1147  long timetaken_us = 0;
1148  int timeremain_ms = 0;
1149 
1150  //only guide if tracking
1151  if (TrackState == SCOPE_TRACKING)
1152  {
1153 
1154  // If already moving, then stop movement
1155  if (MovementNSSP.s == IPS_BUSY)
1156  {
1157  int dir = IUFindOnSwitchIndex(&MovementNSSP);
1159  }
1160 
1161  if (GuideNSTID)
1162  {
1164  GuideNSTID = 0;
1165  }
1166 
1167  isPulsingNS = true;
1168  start_pmc8_guide(PortFD, PMC8_N, (int)ms, timetaken_us, 0);
1169 
1170  timeremain_ms = (int)(ms - ((float)timetaken_us) / 1000.0);
1171 
1172  if (timeremain_ms < 0)
1173  timeremain_ms = 0;
1174 
1175  ret = IPS_BUSY;
1176  }
1177  else
1178  {
1179  LOG_INFO("Mount not tracking--cannot guide.");
1180  }
1181  GuideNSTID = IEAddTimer(timeremain_ms, guideTimeoutHelperN, this);
1182  return ret;
1183 }
1184 
1186 {
1187  IPState ret = IPS_IDLE;
1188  long timetaken_us = 0;
1189  int timeremain_ms = 0;
1190 
1191  //only guide if tracking
1192  if (TrackState == SCOPE_TRACKING)
1193  {
1194 
1195  // If already moving, then stop movement
1196  if (MovementNSSP.s == IPS_BUSY)
1197  {
1198  int dir = IUFindOnSwitchIndex(&MovementNSSP);
1200  }
1201 
1202  if (GuideNSTID)
1203  {
1205  GuideNSTID = 0;
1206  }
1207 
1208  isPulsingNS = true;
1209  start_pmc8_guide(PortFD, PMC8_S, (int)ms, timetaken_us, 0);
1210 
1211  timeremain_ms = (int)(ms - ((float)timetaken_us) / 1000.0);
1212 
1213  if (timeremain_ms < 0)
1214  timeremain_ms = 0;
1215 
1216  ret = IPS_BUSY;
1217  }
1218  else
1219  {
1220  LOG_INFO("Mount not tracking--cannot guide.");
1221  }
1222  GuideNSTID = IEAddTimer(timeremain_ms, guideTimeoutHelperS, this);
1223  return ret;
1224 }
1225 
1227 {
1228  IPState ret = IPS_IDLE;
1229  long timetaken_us = 0;
1230  int timeremain_ms = 0;
1231 
1232  //only guide if tracking
1233  if (TrackState == SCOPE_TRACKING)
1234  {
1235 
1236  // If already moving (no pulse command), then stop movement
1237  if (MovementWESP.s == IPS_BUSY)
1238  {
1239  int dir = IUFindOnSwitchIndex(&MovementWESP);
1241  }
1242 
1243  if (GuideWETID)
1244  {
1246  GuideWETID = 0;
1247  }
1248 
1249  isPulsingWE = true;
1250 
1251  start_pmc8_guide(PortFD, PMC8_E, (int)ms, timetaken_us, TrackRateN[AXIS_RA].value);
1252 
1253  timeremain_ms = (int)(ms - ((float)timetaken_us) / 1000.0);
1254 
1255  if (timeremain_ms < 0)
1256  timeremain_ms = 0;
1257 
1258  ret = IPS_BUSY;
1259  }
1260  else
1261  {
1262  LOG_INFO("Mount not tracking--cannot guide.");
1263  }
1264  GuideWETID = IEAddTimer(timeremain_ms, guideTimeoutHelperE, this);
1265  return ret;
1266 }
1267 
1269 {
1270  IPState ret = IPS_IDLE;
1271  long timetaken_us = 0;
1272  int timeremain_ms = 0;
1273 
1274  //only guide if tracking
1275  if (TrackState == SCOPE_TRACKING)
1276  {
1277 
1278  // If already moving (no pulse command), then stop movement
1279  if (MovementWESP.s == IPS_BUSY)
1280  {
1281  int dir = IUFindOnSwitchIndex(&MovementWESP);
1283  }
1284 
1285  if (GuideWETID)
1286  {
1288  GuideWETID = 0;
1289  }
1290 
1291  isPulsingWE = true;
1292  start_pmc8_guide(PortFD, PMC8_W, (int)ms, timetaken_us, TrackRateN[AXIS_RA].value);
1293 
1294  timeremain_ms = (int)(ms - ((float)timetaken_us) / 1000.0);
1295 
1296  if (timeremain_ms < 0)
1297  timeremain_ms = 0;
1298 
1299  ret = IPS_BUSY;
1300  }
1301  else
1302  {
1303  LOG_INFO("Mount not tracking--cannot guide.");
1304  }
1305  GuideWETID = IEAddTimer(timeremain_ms, guideTimeoutHelperW, this);
1306  return ret;
1307 }
1308 
1310 {
1311  // end previous pulse command
1312  stop_pmc8_guide(PortFD, calldir);
1313 
1314  if (calldir == PMC8_N || calldir == PMC8_S)
1315  {
1316  isPulsingNS = false;
1317  GuideNSNP.np[0].value = 0;
1318  GuideNSNP.np[1].value = 0;
1319  GuideNSNP.s = IPS_IDLE;
1320  GuideNSTID = 0;
1321  IDSetNumber(&GuideNSNP, nullptr);
1322  }
1323  if (calldir == PMC8_W || calldir == PMC8_E)
1324  {
1325  isPulsingWE = false;
1326  GuideWENP.np[0].value = 0;
1327  GuideWENP.np[1].value = 0;
1328  GuideWENP.s = IPS_IDLE;
1329  GuideWETID = 0;
1330  IDSetNumber(&GuideWENP, nullptr);
1331  }
1332 
1333  LOG_DEBUG("GUIDE CMD COMPLETED");
1334 }
1335 
1336 //GUIDE The timer helper functions.
1338 {
1339  static_cast<PMC8*>(p)->guideTimeout(PMC8_N);
1340 }
1342 {
1343  static_cast<PMC8*>(p)->guideTimeout(PMC8_S);
1344 }
1346 {
1347  static_cast<PMC8*>(p)->guideTimeout(PMC8_W);
1348 }
1350 {
1351  static_cast<PMC8*>(p)->guideTimeout(PMC8_E);
1352 }
1353 
1354 bool PMC8::SetSlewRate(int index)
1355 {
1356 
1357  INDI_UNUSED(index);
1358 
1359  // slew rate is rate for MoveEW/MOVENE commands - not for GOTOs!!!
1360 
1361  // just return true - we will check SlewRateSP when we do actually moves
1362  return true;
1363 }
1364 
1366 {
1368 
1369  IUSaveConfigSwitch(fp, &SerialCableTypeSP);
1370  IUSaveConfigSwitch(fp, &MountTypeSP);
1371  IUSaveConfigNumber(fp, &RampNP);
1372  IUSaveConfigNumber(fp, &LegacyGuideRateNP);
1373  IUSaveConfigSwitch(fp, &PostGotoSP);
1374 
1375  return true;
1376 }
1377 
1379 {
1380  static struct timeval ltv;
1381  struct timeval tv;
1382  double dt, da, dx;
1383  int nlocked;
1384 
1385  /* update elapsed time since last poll, don't presume exactly POLLMS */
1386  gettimeofday(&tv, nullptr);
1387 
1388  if (ltv.tv_sec == 0 && ltv.tv_usec == 0)
1389  ltv = tv;
1390 
1391  dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec) / 1e6;
1392  ltv = tv;
1393  da = SLEWRATE * dt;
1394 
1395  /* Process per current state. We check the state of EQUATORIAL_COORDS and act acoordingly */
1396  switch (TrackState)
1397  {
1398  case SCOPE_IDLE:
1399  currentRA += (TrackRateN[AXIS_RA].value / 3600.0 * dt) / 15.0;
1401  break;
1402 
1403  case SCOPE_TRACKING:
1404  if (TrackModeS[1].s == ISS_ON)
1405  {
1406  currentRA += ( ((TRACKRATE_SIDEREAL / 3600.0) - (TrackRateN[AXIS_RA].value / 3600.0)) * dt) / 15.0;
1407  currentDEC += ( (TrackRateN[AXIS_DE].value / 3600.0) * dt);
1408  }
1409  break;
1410 
1411  case SCOPE_SLEWING:
1412  case SCOPE_PARKING:
1413  /* slewing - nail it when both within one pulse @ SLEWRATE */
1414  nlocked = 0;
1415 
1416  dx = targetRA - currentRA;
1417 
1418  // Take shortest path
1419  if (fabs(dx) > 12)
1420  dx *= -1;
1421 
1422  if (fabs(dx) <= da)
1423  {
1424  currentRA = targetRA;
1425  nlocked++;
1426  }
1427  else if (dx > 0)
1428  currentRA += da / 15.;
1429  else
1430  currentRA -= da / 15.;
1431 
1432  if (currentRA < 0)
1433  currentRA += 24;
1434  else if (currentRA > 24)
1435  currentRA -= 24;
1436 
1437  dx = targetDEC - currentDEC;
1438  if (fabs(dx) <= da)
1439  {
1440  currentDEC = targetDEC;
1441  nlocked++;
1442  }
1443  else if (dx > 0)
1444  currentDEC += da;
1445  else
1446  currentDEC -= da;
1447 
1448  if (nlocked == 2)
1449  {
1450  if (TrackState == SCOPE_SLEWING)
1452  else
1454  }
1455 
1456  break;
1457 
1458  case SCOPE_PARKED:
1459  // setting system status to parked will automatically
1460  // set the simulated RA/DEC to park position so reread
1463 
1464  break;
1465 
1466  default:
1467  break;
1468  }
1469 
1472 }
1473 
1474 #if 0
1475 // PMC8 only parks to motor position (0, 0) currently
1476 bool PMC8::SetCurrentPark()
1477 {
1480 
1481  return true;
1482 }
1483 
1484 bool PMC8::SetDefaultPark()
1485 {
1486  // By default set RA to HA
1487  SetAxis1Park(ln_get_apparent_sidereal_time(ln_get_julian_from_sys()));
1488 
1489  // Set DEC to 90 or -90 depending on the hemisphere
1490  // SetAxis2Park((HemisphereS[HEMI_NORTH].s == ISS_ON) ? 90 : -90);
1491  SetAxis2Park(90);
1492 
1493  return true;
1494 }
1495 #else
1497 {
1498  LOG_ERROR("PPMC8::SetCurrentPark() not implemented!");
1499  return false;
1500 }
1501 
1503 {
1504  LOG_ERROR("PMC8::SetDefaultPark() not implemented!");
1505  return false;
1506 }
1507 #endif
1508 
1509 uint8_t PMC8::convertToPMC8TrackMode(uint8_t mode)
1510 {
1511  switch (mode)
1512  {
1513  case TRACK_SIDEREAL:
1514  return PMC8_TRACK_SIDEREAL;
1515  break;
1516  case TRACK_LUNAR:
1517  return PMC8_TRACK_LUNAR;
1518  break;
1519  case TRACK_SOLAR:
1520  return PMC8_TRACK_SOLAR;
1521  break;
1522  case TRACK_CUSTOM:
1523  return PMC8_TRACK_CUSTOM;
1524  break;
1525  default:
1526  return PMC8_TRACK_UNDEFINED;
1527  }
1528 }
1529 
1530 uint8_t PMC8::convertFromPMC8TrackMode(uint8_t mode)
1531 {
1532  switch (mode)
1533  {
1534  case PMC8_TRACK_SIDEREAL:
1535  return TRACK_SIDEREAL;
1536  break;
1537  case PMC8_TRACK_LUNAR:
1538  return TRACK_LUNAR;
1539  break;
1540  case PMC8_TRACK_SOLAR:
1541  return TRACK_SOLAR;
1542  break;
1543  default:
1544  return TRACK_CUSTOM;
1545  }
1546 }
1547 
1548 bool PMC8::SetTrackMode(uint8_t mode)
1549 {
1550  uint8_t pmc8_mode;
1551 
1552  LOGF_DEBUG("PMC8::SetTrackMode called mode=%d", mode);
1553 
1554  pmc8_mode = convertToPMC8TrackMode(mode);
1555 
1556  if (pmc8_mode == PMC8_TRACK_UNDEFINED)
1557  {
1558  LOGF_ERROR("PMC8::SetTrackMode mode=%d not supported!", mode);
1559  return false;
1560  }
1561 
1562  if (pmc8_mode == PMC8_TRACK_CUSTOM)
1563  {
1565  {
1566  return true;
1567  }
1568  }
1569  else
1570  {
1571  if (set_pmc8_track_mode(PortFD, pmc8_mode))
1572  return true;
1573  }
1574 
1575  return false;
1576 }
1577 
1578 bool PMC8::SetTrackRate(double raRate, double deRate)
1579 {
1580  static bool deRateWarning = true;
1581  double pmc8RARate;
1582 
1583  LOGF_INFO("Custom tracking rate set: raRate=%f deRate=%f", raRate, deRate);
1584 
1585  // for now just send rate
1586  pmc8RARate = raRate;
1587 
1588  if (deRate != 0 && deRateWarning)
1589  {
1590  // Only send warning once per session
1591  deRateWarning = false;
1592  LOG_WARN("Custom Declination tracking rate is not implemented yet.");
1593  }
1594 
1595  if (set_pmc8_ra_tracking(PortFD, pmc8RARate))
1596  return true;
1597 
1598  LOG_ERROR("PMC8::SetTrackRate not implemented!");
1599  return false;
1600 }
1601 
1602 bool PMC8::SetTrackEnabled(bool enabled)
1603 {
1604 
1605  LOGF_DEBUG("PMC8::SetTrackEnabled called enabled=%d", enabled);
1606 
1607  // need to determine current tracking mode and start tracking
1608  if (enabled)
1609  {
1611  {
1612  LOG_ERROR("PMC8::SetTrackEnabled - unable to enable tracking");
1613  return false;
1614  }
1615  }
1616  else
1617  {
1618  bool rc;
1619 
1621  if (!rc)
1622  {
1623  LOG_ERROR("PMC8::SetTrackEnabled - unable to set RA track rate to 0");
1624  return false;
1625  }
1626 
1627  // currently only support tracking rate in RA
1628  // rc=set_pmc8_custom_dec_track_rate(PortFD, 0);
1629  // if (!rc)
1630  // {
1631  // LOG_ERROR("PMC8::SetTrackREnabled - unable to set DEC track rate to 0");
1632  // return false;
1633  // }
1634  }
1635 
1636  return true;
1637 }
1638 
void setDefaultBaudRate(BaudRate newRate)
setDefaultBaudRate Set default baud rate. The default baud rate is 9600 unless otherwise changed by t...
void setDefaultHost(const char *addressHost)
void setDefaultPort(uint32_t addressPort)
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
virtual bool Disconnect()
Disconnect from device.
virtual void setConnected(bool status, IPState state=IPS_OK, const char *msg=nullptr)
Set connection switch status in the client.
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool loadConfig(bool silent=false, const char *property=nullptr)
Load the last saved configuration file.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
bool isSimulation() const
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
void setDriverInterface(uint16_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_DEVICE....
Connection::Interface * getActiveConnection()
virtual bool Connect()
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
uint16_t getDriverInterface() const
INumberVectorProperty GuideNSNP
void initGuiderProperties(const char *deviceName, const char *groupName)
Initilize guider properties. It is recommended to call this function within initProperties() of your ...
INumberVectorProperty GuideWENP
void processGuiderProperties(const char *name, double values[], char *names[], int n)
Call this function whenever client updates GuideNSNP or GuideWSP properties in the primary device....
INumberVectorProperty TrackRateNP
TelescopeStatus TrackState
ISwitchVectorProperty TrackStateSP
ISwitchVectorProperty ParkOptionSP
void SetAxis1Park(double value)
SetRAPark Set current RA/AZ parking position. The data park file (stored in ~/.indi/ParkData....
ISwitchVectorProperty MovementNSSP
void SetAxis1ParkDefault(double steps)
SetRAPark Set default RA/AZ parking position.
void SetTelescopeCapability(uint32_t cap, uint8_t slewRateCount)
SetTelescopeCapability sets the Telescope capabilities. All capabilities must be initialized.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
double GetAxis1Park() const
INumberVectorProperty ParkPositionNP
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...
double GetAxis2Park() const
bool isParked()
isParked is mount currently parked?
virtual int AddTrackMode(const char *name, const char *label, bool isDefault=false)
AddTrackMode.
ISwitchVectorProperty TrackModeSP
ISwitchVectorProperty SlewRateSP
Connection::Serial * serialConnection
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
Connection::TCP * tcpConnection
INumberVectorProperty EqNP
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
INumber TrackRateN[2]
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
INumber LocationN[3]
ISwitch * TrackModeS
bool InitPark()
InitPark Loads parking data (stored in ~/.indi/ParkData.xml) that contains parking status and parking...
ISwitchVectorProperty MovementWESP
TelescopeStatus RememberTrackState
RememberTrackState Remember last state of Track State to fall back to in case of errors or aborts.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
void SetAxis2Park(double steps)
SetDEPark Set current DEC/ALT parking position. The data park file (stored in ~/.indi/ParkData....
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
void SetParkDataType(TelescopeParkData type)
setParkDataType Sets the type of parking data stored in the park data file and presented to the user.
void SetAxis2ParkDefault(double steps)
SetDEParkDefault Set default DEC/ALT parking position.
Definition: pmc8.h:49
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: pmc8.cpp:349
static void guideTimeoutHelperE(void *p)
Definition: pmc8.cpp:1349
virtual void simulationTriggered(bool enable) override
Inform driver that the simulation option was triggered. This function is called after setSimulation i...
Definition: pmc8.cpp:893
virtual bool SetSlewRate(int index) override
SetSlewRate Set desired slew rate index.
Definition: pmc8.cpp:1354
bool ramp_movement(PMC8_DIRECTION calldir)
Definition: pmc8.cpp:906
virtual bool Handshake() override
perform handshake with device to check communication
Definition: pmc8.cpp:827
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
Definition: pmc8.cpp:1502
PMC8()
Definition: pmc8.cpp:58
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
Definition: pmc8.cpp:1365
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
Definition: pmc8.cpp:1003
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
Definition: pmc8.cpp:170
static void rampTimeoutHelperN(void *p)
Definition: pmc8.cpp:977
virtual bool Sync(double ra, double dec) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
Definition: pmc8.cpp:688
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: pmc8.cpp:408
~PMC8() override
Definition: pmc8.cpp:76
virtual bool updateTime(ln_date *utc, double utc_offset) override
Update telescope time, date, and UTC offset.
Definition: pmc8.cpp:851
static void AbortGotoTimeoutHelper(void *p)
Definition: pmc8.cpp:715
virtual const char * getDefaultName() override
Definition: pmc8.cpp:80
virtual bool SetTrackEnabled(bool enabled) override
SetTrackEnabled Engages or disengages mount tracking. If there are no tracking modes available,...
Definition: pmc8.cpp:1602
static void rampTimeoutHelperS(void *p)
Definition: pmc8.cpp:983
virtual IPState GuideNorth(uint32_t ms) override
Guide north for ms milliseconds. North is defined as DEC+.
Definition: pmc8.cpp:1144
virtual bool ReadScopeStatus() override
Read telescope status.
Definition: pmc8.cpp:463
int GuideNSTID
Definition: pmc8.h:124
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
Definition: pmc8.cpp:1074
static void rampTimeoutHelperE(void *p)
Definition: pmc8.cpp:995
virtual bool Park() override
Park the telescope to its home position.
Definition: pmc8.cpp:782
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
Definition: pmc8.cpp:1496
virtual void debugTriggered(bool enable) override
Inform driver that the debug option was triggered. This function is called after setDebug is triggere...
Definition: pmc8.cpp:888
virtual bool SetTrackRate(double raRate, double deRate) override
SetTrackRate Set custom tracking rates.
Definition: pmc8.cpp:1578
virtual IPState GuideWest(uint32_t ms) override
Guide west for ms milliseconds. West is defined as RA-.
Definition: pmc8.cpp:1268
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
Definition: pmc8.cpp:862
void mountSim()
Definition: pmc8.cpp:1378
static void guideTimeoutHelperS(void *p)
Definition: pmc8.cpp:1341
static void guideTimeoutHelperW(void *p)
Definition: pmc8.cpp:1345
virtual bool initProperties() override
Called to initialize basic properties required all the time.
Definition: pmc8.cpp:85
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: pmc8.cpp:426
virtual IPState GuideEast(uint32_t ms) override
Guide east for ms milliseconds. East is defined as RA+.
Definition: pmc8.cpp:1226
static void guideTimeoutHelperN(void *p)
Definition: pmc8.cpp:1337
virtual bool Abort() override
Abort any telescope motion including tracking if possible.
Definition: pmc8.cpp:721
void guideTimeout(PMC8_DIRECTION calldir)
Definition: pmc8.cpp:1309
virtual bool Goto(double, double) override
Move the scope to the supplied RA and DEC coordinates.
Definition: pmc8.cpp:634
virtual bool UnPark() override
Unpark the telescope if already parked.
Definition: pmc8.cpp:813
static void rampTimeoutHelperW(void *p)
Definition: pmc8.cpp:989
virtual IPState GuideSouth(uint32_t ms) override
Guide south for ms milliseconds. South is defined as DEC-.
Definition: pmc8.cpp:1185
int GuideWETID
Definition: pmc8.h:125
virtual bool SetTrackMode(uint8_t mode) override
SetTrackMode Set active tracking mode. Do not change track state.
Definition: pmc8.cpp:1548
int getSlewRate()
Definition: pmc8.cpp:898
const char * GUIDE_TAB
GUIDE_TAB Where all the properties for guiding are located.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
const char * MOTION_TAB
MOTION_TAB Where all the motion control properties of the device are located.
void IERmTimer(int timerid)
Remove the timer with the given timerid, as returned from IEAddTimer() or IEAddPeriodicTimer().
Definition: eventloop.c:602
int IEAddTimer(int millisecs, IE_TCF *fp, void *p)
Register a new single-shot timer function, fp, to be called with ud as argument after ms.
Definition: eventloop.c:582
#define currentDEC
Definition: ieq45.cpp:48
#define currentRA
Definition: ieq45.cpp:47
double ra
void set_sim_system_status(IEQ_SYSTEM_STATUS value)
double dec
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_OFF
Definition: indiapi.h:151
@ ISS_ON
Definition: indiapi.h:152
@ IP_RW
Definition: indiapi.h:186
@ IP_RO
Definition: indiapi.h:184
IPState
Property state.
Definition: indiapi.h:160
@ IPS_BUSY
Definition: indiapi.h:163
@ IPS_ALERT
Definition: indiapi.h:164
@ IPS_IDLE
Definition: indiapi.h:161
@ IPS_OK
Definition: indiapi.h:162
@ ISR_1OFMANY
Definition: indiapi.h:173
@ AXIS_DE
Definition: indibasetypes.h:36
@ AXIS_RA
Definition: indibasetypes.h:35
INDI_DIR_WE
Definition: indibasetypes.h:55
@ DIRECTION_EAST
Definition: indibasetypes.h:57
@ DIRECTION_WEST
Definition: indibasetypes.h:56
INDI_DIR_NS
Definition: indibasetypes.h:48
@ DIRECTION_SOUTH
Definition: indibasetypes.h:50
@ DIRECTION_NORTH
Definition: indibasetypes.h:49
double range24(double r)
range24 Limits a number to be between 0-24 range.
Definition: indicom.c:1235
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.
#define TRACKRATE_SIDEREAL
Definition: indicom.h:55
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition: indidevapi.c:25
void IUFillNumberVector(INumberVectorProperty *nvp, INumber *np, int nnp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a number vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:272
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition: indidevapi.c:128
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition: indidevapi.c:148
void IUFillTextVector(ITextVectorProperty *tvp, IText *tp, int ntp, const char *dev, const char *name, const char *label, const char *group, IPerm p, double timeout, IPState s)
Assign attributes for a text vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:291
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition: indidevapi.c:36
void IUSaveConfigNumber(FILE *fp, const INumberVectorProperty *nvp)
Add a number vector property value to the configuration file.
Definition: indidevapi.c:15
void IUFillSwitch(ISwitch *sp, const char *name, const char *label, ISState s)
Assign attributes for a switch property. The switch's auxiliary elements will be set to NULL.
Definition: indidevapi.c:158
void IUFillText(IText *tp, const char *name, const char *label, const char *initialText)
Assign attributes for a text property. The text's auxiliary elements will be set to NULL.
Definition: indidevapi.c:198
void IUFillNumber(INumber *np, const char *name, const char *label, const char *format, double min, double max, double step, double value)
Assign attributes for a number property. The number's auxiliary elements will be set to NULL.
Definition: indidevapi.c:180
void IUFillSwitchVector(ISwitchVectorProperty *svp, ISwitch *sp, int nsp, const char *dev, const char *name, const char *label, const char *group, IPerm p, ISRule r, double timeout, IPState s)
Assign attributes for a switch vector property. The vector's auxiliary elements will be set to NULL.
Definition: indidevapi.c:235
#define INDI_UNUSED(x)
Definition: indidevapi.h:131
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
Definition: indidriver.c:1308
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
Definition: indidriver.c:1211
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
Definition: indidriver.c:1231
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
Definition: indidriver.c:1362
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
Definition: indidriver.c:1191
#define LOGF_INFO(fmt,...)
Definition: indilogger.h:82
#define LOG_DEBUG(txt)
Definition: indilogger.h:75
#define LOGF_EXTRA3(fmt,...)
Definition: indilogger.h:86
#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 LOGF_ERROR(fmt,...)
Definition: indilogger.h:80
#define LOG_INFO(txt)
Definition: indilogger.h:74
const char * CONNECTION_TAB
@ ST_STOPPED
Definition: ieqdriverbase.h:33
@ ST_PARKED
Definition: ieqdriverbase.h:39
#define PMC8_TRACKING_AUTODETECT_INTERVAL
Definition: pmc8.cpp:51
#define PMC8_DEFAULT_IP_ADDRESS
Definition: pmc8.cpp:50
#define PMC8_VERSION_MINOR
Definition: pmc8.cpp:53
#define SLEWRATE
Definition: pmc8.cpp:45
#define PMC8_DEFAULT_PORT
Definition: pmc8.cpp:49
#define PMC8_VERSION_MAJOR
Definition: pmc8.cpp:52
@ PMC8_MOVE_INACTIVE
Definition: pmc8.h:33
@ PMC8_MOVE_RAMPING
Definition: pmc8.h:33
@ PMC8_MOVE_ACTIVE
Definition: pmc8.h:33
@ PMC8_RAMP_UP
Definition: pmc8.h:34
@ PMC8_RAMP_DOWN
Definition: pmc8.h:34
bool get_pmc8_guide_rate(int fd, PMC8_AXIS axis, double &rate)
void set_pmc8_location(double latitude, double longitude)
Definition: pmc8driver.cpp:243
bool set_pmc8_custom_ra_track_rate(int fd, double rate)
bool start_pmc8_guide(int fd, PMC8_DIRECTION gdir, int ms, long &timetaken_us, double ratehint)
void set_pmc8_sim_system_status(PMC8_SYSTEM_STATUS value)
Definition: pmc8driver.cpp:253
void set_pmc8_device(const char *name)
Definition: pmc8driver.cpp:238
bool park_pmc8(int fd)
bool stop_pmc8_guide(int fd, PMC8_DIRECTION gdir)
void set_pmc8_debug(bool enable)
Definition: pmc8driver.cpp:226
bool sync_pmc8(int fd, double ra, double dec)
bool abort_pmc8_goto(int fd)
bool slew_pmc8(int fd, double ra, double dec)
bool get_pmc8_is_scope_slewing(int fd, bool &isslew)
Definition: pmc8driver.cpp:712
bool set_pmc8_track_mode(int fd, uint8_t mode)
Definition: pmc8driver.cpp:996
bool set_pmc8_radec(int fd, double ra, double dec)
void set_pmc8_mountParameters(int index)
Definition: pmc8driver.cpp:204
bool set_pmc8_guide_rate(int fd, PMC8_AXIS axis, double rate)
void set_pmc8_simulation(bool enable)
Definition: pmc8driver.cpp:231
void set_pmc8_goto_resume(bool resume)
bool get_pmc8_firmware(int fd, FirmwareInfo *info)
Definition: pmc8driver.cpp:538
bool get_pmc8_coords(int fd, double &ra, double &dec)
bool abort_pmc8(int fd)
bool get_pmc8_reconnect_flag()
bool get_pmc8_tracking_data(int fd, double &rate, uint8_t &mode)
Definition: pmc8driver.cpp:834
bool check_pmc8_connection(int fd, PMC8_CONNECTION_TYPE connection)
Definition: pmc8driver.cpp:297
bool unpark_pmc8(int fd)
void set_pmc8_sim_move_rate(int value)
Definition: pmc8driver.cpp:282
bool stop_pmc8_tracking_motion(int fd)
Definition: pmc8driver.cpp:771
void set_pmc8_sim_track_rate(PMC8_TRACK_RATE value)
Definition: pmc8driver.cpp:277
void set_pmc8_sim_ra(double ra)
Definition: pmc8driver.cpp:287
bool set_pmc8_ra_tracking(int fd, double rate)
void set_pmc8_sim_dec(double dec)
Definition: pmc8driver.cpp:292
bool set_pmc8_move_rate_axis(int fd, PMC8_DIRECTION dir, int reqrate)
Definition: pmc8driver.cpp:745
PMC8_CONNECTION_TYPE
Definition: pmc8driver.h:64
@ PMC8_SERIAL_AUTO
Definition: pmc8driver.h:64
@ PMC8_ETHERNET
Definition: pmc8driver.h:64
@ PMC8_SERIAL_STANDARD
Definition: pmc8driver.h:64
@ PMC8_SERIAL_INVERTED
Definition: pmc8driver.h:64
#define PMC8_MAX_MOVE_RATE
Definition: pmc8driver.h:40
@ MOUNT_G11
Definition: pmc8driver.h:62
@ MOUNT_iEXOS100
Definition: pmc8driver.h:62
@ MOUNT_EXOS2
Definition: pmc8driver.h:62
@ PMC8_TRACK_CUSTOM
Definition: pmc8driver.h:55
@ PMC8_TRACK_SIDEREAL
Definition: pmc8driver.h:55
@ PMC8_TRACK_LUNAR
Definition: pmc8driver.h:55
@ PMC8_TRACK_SOLAR
Definition: pmc8driver.h:55
@ PMC8_TRACK_UNDEFINED
Definition: pmc8driver.h:55
@ ST_TRACKING
Definition: pmc8driver.h:45
@ PMC8_AXIS_DEC
Definition: pmc8driver.h:59
@ PMC8_AXIS_RA
Definition: pmc8driver.h:59
PMC8_DIRECTION
Definition: pmc8driver.h:60
@ PMC8_N
Definition: pmc8driver.h:60
@ PMC8_W
Definition: pmc8driver.h:60
@ PMC8_S
Definition: pmc8driver.h:60
@ PMC8_E
Definition: pmc8driver.h:60
#define PMC8_MAX_TRACK_RATE
Definition: pmc8driver.h:37
PMC8_MOUNT_TYPES MountType
Definition: pmc8driver.h:77
std::string MainBoardFirmware
bool IsRev2Compliant
Definition: pmc8driver.h:78
static Logger & getInstance()
Method to get a reference to the object (i.e., Singleton) It is a static method.
Definition: indilogger.cpp:339
int addDebugLevel(const char *debugLevelName, const char *LoggingLevelName)
Adds a new debugging level to the driver.
Definition: indilogger.cpp:72
PMC8_RAMP_DIRECTION rampDir
Definition: pmc8.h:43
int targetRate
Definition: pmc8.h:40
int rampLastStep
Definition: pmc8.h:42
int timer
Definition: pmc8.h:44
PMC8_MOVE_STATE state
Definition: pmc8.h:38
uint8_t moveDir
Definition: pmc8.h:39
int rampIteration
Definition: pmc8.h:41
char name[MAXINDINAME]
Definition: indiapi.h:323
char name[MAXINDINAME]
Definition: indiapi.h:371
char name[MAXINDINAME]
Definition: indiapi.h:250
double round(double value, int decimal_places)