Instrument Neutral Distributed Interface INDI  2.0.2
lx200ap_gtocp2.cpp
Go to the documentation of this file.
1 /*
2  Astro-Physics INDI driver
3 
4  Tailored for GTOCP2
5 
6  Copyright (C) 2018 Jasem Mutlaq
7 
8  This library is free software; you can redistribute it and/or
9  modify it under the terms of the GNU Lesser General Public
10  License as published by the Free Software Foundation; either
11  version 2.1 of the License, or (at your option) any later version.
12 
13  This library is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  Lesser General Public License for more details.
17 
18  You should have received a copy of the GNU Lesser General Public
19  License along with this library; if not, write to the Free Software
20  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 */
22 
23 #include "lx200ap_gtocp2.h"
24 
25 #include "indicom.h"
26 #include "lx200driver.h"
27 #include "lx200apdriver.h"
28 
29 #include <libnova/transform.h>
30 
31 #include <cmath>
32 #include <cstring>
33 #include <unistd.h>
34 #include <termios.h>
35 
36 /* Constructor */
38 {
42 
43  sendLocationOnStartup = false;
44  sendTimeOnStartup = false;
45 }
46 
48 {
49  return (const char *)"AstroPhysics GTOCP2";
50 }
51 
53 {
55 
57 
58  IUFillNumber(&HourangleCoordsN[0], "HA", "HA H:M:S", "%10.6m", 0., 24., 0., 0.);
59  IUFillNumber(&HourangleCoordsN[1], "DEC", "Dec D:M:S", "%10.6m", -90.0, 90.0, 0., 0.);
60  IUFillNumberVector(&HourangleCoordsNP, HourangleCoordsN, 2, getDeviceName(), "HOURANGLE_COORD", "Hourangle Coords",
62 
63  IUFillNumber(&HorizontalCoordsN[0], "AZ", "Az D:M:S", "%10.6m", 0., 360., 0., 0.);
64  IUFillNumber(&HorizontalCoordsN[1], "ALT", "Alt D:M:S", "%10.6m", -90., 90., 0., 0.);
66  "Horizontal Coords", MAIN_CONTROL_TAB, IP_RW, 120, IPS_IDLE);
67 
68 
69  // Max rate is 999.99999X for the GTOCP4.
70  // Using :RR998.9999# just to be safe. 15.041067*998.99999 = 15026.02578
71  TrackRateN[AXIS_RA].min = -15026.0258;
72  TrackRateN[AXIS_RA].max = 15026.0258;
73  TrackRateN[AXIS_DE].min = -998.9999;
74  TrackRateN[AXIS_DE].max = 998.9999;
75 
76  // Motion speed of axis when pressing NSWE buttons
77  IUFillSwitch(&SlewRateS[0], "12", "12x", ISS_OFF);
78  IUFillSwitch(&SlewRateS[1], "64", "64x", ISS_ON);
79  IUFillSwitch(&SlewRateS[2], "600", "600x", ISS_OFF);
80  IUFillSwitch(&SlewRateS[3], "1200", "1200x", ISS_OFF);
81  IUFillSwitchVector(&SlewRateSP, SlewRateS, 4, getDeviceName(), "TELESCOPE_SLEW_RATE", "Slew Rate", MOTION_TAB, IP_RW,
82  ISR_1OFMANY, 0, IPS_IDLE);
83 
84  // Slew speed when performing regular GOTO
85  IUFillSwitch(&APSlewSpeedS[0], "600", "600x", ISS_ON);
86  IUFillSwitch(&APSlewSpeedS[1], "900", "900x", ISS_OFF);
87  IUFillSwitch(&APSlewSpeedS[2], "1200", "1200x", ISS_OFF);
89  0, IPS_IDLE);
90 
91  IUFillSwitch(&SwapS[0], "NS", "North/South", ISS_OFF);
92  IUFillSwitch(&SwapS[1], "EW", "East/West", ISS_OFF);
93  IUFillSwitchVector(&SwapSP, SwapS, 2, getDeviceName(), "SWAP", "Swap buttons", MOTION_TAB, IP_RW, ISR_1OFMANY, 0,
94  IPS_IDLE);
95 
96  IUFillSwitch(&SyncCMRS[USE_REGULAR_SYNC], ":CM#", ":CM#", ISS_ON);
97  IUFillSwitch(&SyncCMRS[USE_CMR_SYNC], ":CMR#", ":CMR#", ISS_OFF);
99  IPS_IDLE);
100 
101  // guide speed
102  IUFillSwitch(&APGuideSpeedS[0], "0.25", "0.25x", ISS_OFF);
103  IUFillSwitch(&APGuideSpeedS[1], "0.5", "0.50x", ISS_ON);
104  IUFillSwitch(&APGuideSpeedS[2], "1.0", "1.0x", ISS_OFF);
106  0, IPS_IDLE);
107 
108  IUFillText(&VersionT[0], "Version", "Version", "");
109  IUFillTextVector(&VersionTP, VersionT, 1, getDeviceName(), "Firmware", "Firmware", MAIN_CONTROL_TAB, IP_RO, 0, IPS_IDLE);
110 
112 
113  return true;
114 }
115 
117 {
119 
120  if (isConnected())
121  {
123 
124  /* Motion group */
129  }
130 }
131 
133 {
135 
136  if (isConnected())
137  {
139 
140  /* Motion group */
145 
146  if (InitPark())
147  {
148  // If loading parking data is successful, we just set the default parking values.
149  SetAxis1ParkDefault(LocationN[LOCATION_LATITUDE].value >= 0 ? 0 : 180);
151  }
152  else
153  {
154  // Otherwise, we set all parking data to default in case no parking data is found.
155  SetAxis1Park(LocationN[LOCATION_LATITUDE].value >= 0 ? 0 : 180);
157 
158  SetAxis1ParkDefault(LocationN[LOCATION_LATITUDE].value >= 0 ? 0 : 180);
160  }
161 
162  double longitude = -1000, latitude = -1000;
163  // Get value from config file if it exists.
164  IUGetConfigNumber(getDeviceName(), "GEOGRAPHIC_COORD", "LONG", &longitude);
165  IUGetConfigNumber(getDeviceName(), "GEOGRAPHIC_COORD", "LAT", &latitude);
166  if (longitude != -1000 && latitude != -1000)
167  updateLocation(latitude, longitude, 0);
168  }
169  else
170  {
176  }
177 
178  return true;
179 }
180 
181 bool LX200AstroPhysicsGTOCP2::initMount()
182 {
183  // Make sure that the mount is setup according to the properties
184  int err = 0;
185 
186  bool raOK = false, deOK = false;
187  if (isSimulation())
188  {
189  raOK = deOK = true;
190  }
191  else
192  {
193  raOK = (getLX200RA(PortFD, &currentRA) == 0);
194  deOK = (getLX200DEC(PortFD, &currentDEC) == 0);
195  }
196 
197  // If we either failed to get coords; OR
198  // RA and DE are zero, then mount is not initialized and we need to initialized it.
199  if ( (raOK == false && deOK == false) || (currentRA == 0 && (currentDEC == 0 || currentDEC == 90)))
200  {
201  LOG_DEBUG("Mount is not yet initialized. Initializing it...");
202 
203  if (isSimulation() == false)
204  {
205  // This is how to init the mount in case RA/DE are missing.
206  // :PO#
207  if (setAPUnPark(PortFD) < 0)
208  {
209  LOG_ERROR("UnParking Failed.");
210  return false;
211  }
212 
213  // Stop :Q#
214  abortSlew(PortFD);
215  }
216  }
217 
218  mountInitialized = true;
219 
220  LOG_DEBUG("Mount is initialized.");
221 
222  // Astrophysics mount is always unparked on startup
223  // In this driver, unpark only sets the tracking ON.
224  // setAPUnPark() is NOT called as this function, despite its name, is only used for initialization purposes.
225  UnPark();
226 
227  // On most mounts SlewRateS defines the MoveTo AND Slew (GOTO) speeds
228  // lx200ap is different - some of the MoveTo speeds are not VALID
229  // Slew speeds so we have to keep two lists.
230  //
231  // SlewRateS is used as the MoveTo speed
232  if (isSimulation() == false && (err = selectAPMoveToRate(PortFD, IUFindOnSwitchIndex(&SlewRateSP))) < 0)
233  {
234  LOGF_ERROR("Error setting move rate (%d).", err);
235  return false;
236  }
237 
238  SlewRateSP.s = IPS_OK;
239  IDSetSwitch(&SlewRateSP, nullptr);
240 
241  // APSlewSpeedsS defines the Slew (GOTO) speeds valid on the AP mounts
242  if (isSimulation() == false && (err = selectAPSlewRate(PortFD, IUFindOnSwitchIndex(&APSlewSpeedSP))) < 0)
243  {
244  LOGF_ERROR("Error setting slew to rate (%d).", err);
245  return false;
246  }
247 
249  IDSetSwitch(&APSlewSpeedSP, nullptr);
250 
251  char versionString[128];
252  if (isSimulation())
253  strncpy(versionString, "E", 128);
254  else
255  getAPVersionNumber(PortFD, versionString);
256 
257  VersionTP.s = IPS_OK;
258  IUSaveText(&VersionT[0], versionString);
259  IDSetText(&VersionTP, nullptr);
260 
261  if (strlen(versionString) != 1)
262  {
263  LOGF_ERROR("Version not supported GTOCP2 driver: %s", versionString);
264  return false;
265  }
266 
267  int typeIndex = VersionT[0].text[0] - 'E';
268  if (typeIndex >= 0)
269  {
270  firmwareVersion = static_cast<ControllerVersion>(typeIndex);
271  LOGF_DEBUG("Firmware version index: %d", typeIndex);
272  LOGF_INFO("Firmware Version: %c", VersionT[0].text[0]);
273  }
274  else
275  {
276  LOGF_ERROR("Invalid version: %s", versionString);
277  return false;
278  }
279 
280  return true;
281 }
282 
283 bool LX200AstroPhysicsGTOCP2::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
284 {
285  int err = 0;
286 
287  // ignore if not ours //
288  if (strcmp(getDeviceName(), dev))
289  return false;
290 
291  // =======================================
292  // Swap Buttons
293  // =======================================
294  if (!strcmp(name, SwapSP.name))
295  {
296  int currentSwap;
297 
299  IUUpdateSwitch(&SwapSP, states, names, n);
300  currentSwap = IUFindOnSwitchIndex(&SwapSP);
301 
302  if ((!isSimulation() && (err = swapAPButtons(PortFD, currentSwap)) < 0))
303  {
304  LOGF_ERROR("Error swapping buttons (%d).", err);
305  return false;
306  }
307 
308  SwapS[0].s = ISS_OFF;
309  SwapS[1].s = ISS_OFF;
310  SwapSP.s = IPS_OK;
311  IDSetSwitch(&SwapSP, nullptr);
312  return true;
313  }
314 
315  // ===========================================================
316  // GOTO ("slew") Speed.
317  // ===========================================================
318  if (!strcmp(name, APSlewSpeedSP.name))
319  {
320  IUUpdateSwitch(&APSlewSpeedSP, states, names, n);
321  int slewRate = IUFindOnSwitchIndex(&APSlewSpeedSP);
322 
323  if (!isSimulation() && (err = selectAPSlewRate(PortFD, slewRate) < 0))
324  {
325  LOGF_ERROR("Error setting move to rate (%d).", err);
326  return false;
327  }
328 
330  IDSetSwitch(&APSlewSpeedSP, nullptr);
331  return true;
332  }
333 
334  // ===========================================================
335  // Guide Speed.
336  // ===========================================================
337  if (!strcmp(name, APGuideSpeedSP.name))
338  {
339  IUUpdateSwitch(&APGuideSpeedSP, states, names, n);
340  int guideRate = IUFindOnSwitchIndex(&APGuideSpeedSP);
341 
342  if (!isSimulation() && (err = selectAPGuideRate(PortFD, guideRate) < 0))
343  {
344  LOGF_ERROR("Error setting guiding to rate (%d).", err);
345  return false;
346  }
347 
349  IDSetSwitch(&APGuideSpeedSP, nullptr);
350  return true;
351  }
352 
353  // =======================================
354  // Choose the appropriate sync command
355  // =======================================
356  if (!strcmp(name, SyncCMRSP.name))
357  {
359  IUUpdateSwitch(&SyncCMRSP, states, names, n);
361  SyncCMRSP.s = IPS_OK;
362  IDSetSwitch(&SyncCMRSP, nullptr);
363  return true;
364  }
365 
366  // =======================================
367  // Choose the PEC playback mode
368  // =======================================
369  if (!strcmp(name, PECStateSP.name))
370  {
372  IUUpdateSwitch(&PECStateSP, states, names, n);
374 
375  int pecstate = IUFindOnSwitchIndex(&PECStateSP);
376 
377  if (!isSimulation() && (err = selectAPPECState(PortFD, pecstate) < 0))
378  {
379  LOGF_ERROR("Error setting PEC state (%d).", err);
380  return false;
381  }
382 
383  PECStateSP.s = IPS_OK;
384  IDSetSwitch(&PECStateSP, nullptr);
385 
386  return true;
387  }
388 
389  return LX200Generic::ISNewSwitch(dev, name, states, names, n);
390 }
391 
393 {
394  if (isSimulation())
395  {
396  mountSim();
397  return true;
398  }
399 
401  {
402  EqNP.s = IPS_ALERT;
403  IDSetNumber(&EqNP, "Error reading RA/DEC.");
404  return false;
405  }
406 
407  if (TrackState == SCOPE_SLEWING)
408  {
409  double dx = lastRA - currentRA;
410  double dy = lastDE - currentDEC;
411 
412  LOGF_DEBUG("Slewing... currentRA: %g dx: %g currentDE: %g dy: %g", currentRA, dx, currentDEC, dy);
413 
414  // Wait until acknowledged
415  if (dx == 0 && dy == 0)
416  {
418  LOG_INFO("Slew is complete. Tracking...");
419  }
420 
421  // Keep try of last values to determine if the mount settled.
422  lastRA = currentRA;
423  lastDE = currentDEC;
424  }
425  else if (TrackState == SCOPE_PARKING)
426  {
427  if (getLX200Az(PortFD, &currentAz) < 0 || getLX200Alt(PortFD, &currentAlt) < 0)
428  {
429  EqNP.s = IPS_ALERT;
430  IDSetNumber(&EqNP, "Error reading Az/Alt.");
431  return false;
432  }
433 
434  double dx = lastAZ - currentAz;
435  double dy = lastAL - currentAlt;
436 
437  LOGF_DEBUG("Parking... currentAz: %g dx: %g currentAlt: %g dy: %g", currentAz, dx, currentAlt, dy);
438 
439  if (dx == 0 && dy == 0)
440  {
441  LOG_DEBUG("Parking slew is complete. Asking astrophysics mount to park...");
442 
443  if (!isSimulation() && setAPPark(PortFD) < 0)
444  {
445  LOG_ERROR("Parking Failed.");
446  return false;
447  }
448 
449  // Turn off tracking.
450  SetTrackEnabled(false);
451 
452  SetParked(true);
453 
454  LOG_INFO("Please disconnect and power off the mount.");
455  }
456 
457  lastAZ = currentAz;
458  lastAL = currentAlt;
459  }
460 
462 
463  syncSideOfPier();
464 
465  return true;
466 }
467 
468 bool LX200AstroPhysicsGTOCP2::Goto(double r, double d)
469 {
470  const struct timespec timeout = {0, 100000000L};
471 
472  targetRA = r;
473  targetDEC = d;
474 
475  char RAStr[64], DecStr[64];
476  fs_sexa(RAStr, targetRA, 2, 3600);
477  fs_sexa(DecStr, targetDEC, 2, 3600);
478 
479  // If moving, let's stop it first.
480  if (EqNP.s == IPS_BUSY)
481  {
482  if (!isSimulation() && abortSlew(PortFD) < 0)
483  {
484  AbortSP.s = IPS_ALERT;
485  IDSetSwitch(&AbortSP, "Abort slew failed.");
486  return false;
487  }
488 
489  AbortSP.s = IPS_OK;
490  EqNP.s = IPS_IDLE;
491  IDSetSwitch(&AbortSP, "Slew aborted.");
492  IDSetNumber(&EqNP, nullptr);
493 
495  {
498  EqNP.s = IPS_IDLE;
501  IDSetSwitch(&MovementNSSP, nullptr);
502  IDSetSwitch(&MovementWESP, nullptr);
503  }
504 
505  // sleep for 100 mseconds
506  nanosleep(&timeout, nullptr);
507  }
508 
509  if (!isSimulation())
510  {
512  {
513  EqNP.s = IPS_ALERT;
514  IDSetNumber(&EqNP, "Error setting RA/DEC.");
515  return false;
516  }
517 
518  int err = 0;
519 
520  /* Slew reads the '0', that is not the end of the slew */
521  if ((err = Slew(PortFD)))
522  {
523  EqNP.s = IPS_ALERT;
524  IDSetNumber(&EqNP, "Error Slewing to JNow RA %s - DEC %s\n", RAStr, DecStr);
525  slewError(err);
526  return false;
527  }
528 
529  motionCommanded = true;
530  lastRA = targetRA;
531  lastDE = targetDEC;
532  }
533 
535  //EqNP.s = IPS_BUSY;
536 
537  LOGF_INFO("Slewing to RA: %s - DEC: %s", RAStr, DecStr);
538  return true;
539 }
540 
541 
542 int LX200AstroPhysicsGTOCP2::SendPulseCmd(int8_t direction, uint32_t duration_msec)
543 {
544  if (firmwareVersion == MCV_E)
546 
547  return APSendPulseCmd(PortFD, direction, duration_msec);
548 }
549 
551 {
552  if (isSimulation())
553  {
554  LOG_INFO("Simulated Astrophysics is online. Retrieving basic data...");
555  return true;
556  }
557 
558  int err = 0;
559 
560  if ((err = setAPClearBuffer(PortFD)) < 0)
561  {
562  LOGF_ERROR("Error clearing the buffer (%d): %s", err, strerror(err));
563  return false;
564  }
565 
566  if ((err = setAPBackLashCompensation(PortFD, 0, 0, 0)) < 0)
567  {
568  // It seems we need to send it twice before it works!
569  if ((err = setAPBackLashCompensation(PortFD, 0, 0, 0)) < 0)
570  {
571  LOGF_ERROR("Error setting back lash compensation (%d): %s.", err, strerror(err));
572  return false;
573  }
574  }
575 
576  // Detect and set fomat. It should be LONG.
577  return (checkLX200EquatorialFormat(PortFD) == 0);
578 }
579 
581 {
582  timeUpdated = false;
583  //locationUpdated = false;
584  mountInitialized = false;
585 
586  return LX200Generic::Disconnect();
587 }
588 
590 {
591  char syncString[256];
592 
593  int syncType = IUFindOnSwitchIndex(&SyncCMRSP);
594 
595  if (!isSimulation())
596  {
597  if (setAPObjectRA(PortFD, ra) < 0 || setAPObjectDEC(PortFD, dec) < 0)
598  {
599  EqNP.s = IPS_ALERT;
600  IDSetNumber(&EqNP, "Error setting RA/DEC. Unable to Sync.");
601  return false;
602  }
603 
604  bool syncOK = true;
605 
606  switch (syncType)
607  {
608  case USE_REGULAR_SYNC:
609  if (::Sync(PortFD, syncString) < 0)
610  syncOK = false;
611  break;
612 
613  case USE_CMR_SYNC:
614  if (APSyncCMR(PortFD, syncString) < 0)
615  syncOK = false;
616  break;
617 
618  default:
619  break;
620  }
621 
622  if (syncOK == false)
623  {
624  EqNP.s = IPS_ALERT;
625  IDSetNumber(&EqNP, "Synchronization failed.");
626  return false;
627  }
628 
629  }
630 
631  currentRA = ra;
632  currentDEC = dec;
633 
634  LOGF_DEBUG("%s Synchronization successful %s", (syncType == USE_REGULAR_SYNC ? "CM" : "CMR"), syncString);
635  LOG_INFO("Synchronization successful.");
636 
637  EqNP.s = IPS_OK;
638 
640 
641  return true;
642 }
643 
644 bool LX200AstroPhysicsGTOCP2::updateTime(ln_date *utc, double utc_offset)
645 {
646  struct ln_zonedate ltm;
647 
648  ln_date_to_zonedate(utc, &ltm, utc_offset * 3600.0);
649 
650  JD = ln_get_julian_day(utc);
651 
652  LOGF_DEBUG("New JD is %.2f", JD);
653 
654  // Set Local Time
655  if (isSimulation() == false && setLocalTime(PortFD, ltm.hours, ltm.minutes, (int)ltm.seconds) < 0)
656  {
657  LOG_ERROR("Error setting local time.");
658  return false;
659  }
660 
661  LOGF_DEBUG("Set Local Time %02d:%02d:%02d is successful.", ltm.hours, ltm.minutes,
662  (int)ltm.seconds);
663 
664  if (isSimulation() == false && setCalenderDate(PortFD, ltm.days, ltm.months, ltm.years) < 0)
665  {
666  LOG_ERROR("Error setting local date.");
667  return false;
668  }
669 
670  LOGF_DEBUG("Set Local Date %02d/%02d/%02d is successful.", ltm.days, ltm.months, ltm.years);
671 
672  if (isSimulation() == false && setAPUTCOffset(PortFD, fabs(utc_offset)) < 0)
673  {
674  LOG_ERROR("Error setting UTC Offset.");
675  return false;
676  }
677 
678  LOGF_DEBUG("Set UTC Offset %g (always positive for AP) is successful.", fabs(utc_offset));
679 
680  LOG_INFO("Time updated.");
681 
682  timeUpdated = true;
683 
684  if (locationUpdated && timeUpdated && mountInitialized == false)
685  initMount();
686 
687  return true;
688 }
689 
690 bool LX200AstroPhysicsGTOCP2::updateLocation(double latitude, double longitude, double elevation)
691 {
692  INDI_UNUSED(elevation);
693 
694  if (!isSimulation() && setAPSiteLongitude(PortFD, 360.0 - longitude) < 0)
695  {
696  LOG_ERROR("Error setting site longitude coordinates");
697  return false;
698  }
699 
700  if (!isSimulation() && setAPSiteLatitude(PortFD, latitude) < 0)
701  {
702  LOG_ERROR("Error setting site latitude coordinates");
703  return false;
704  }
705 
706  char l[32], L[32];
707  fs_sexa(l, latitude, 3, 3600);
708  fs_sexa(L, longitude, 4, 3600);
709 
710  LOGF_INFO("Site location updated to Lat %.32s - Long %.32s", l, L);
711 
712  locationUpdated = true;
713 
714  if (locationUpdated && timeUpdated && mountInitialized == false)
715  initMount();
716 
717  return true;
718 }
719 
721 {
724 }
725 
726 // For most mounts the SetSlewRate() method sets both the MoveTo and Slew (GOTO) speeds.
727 // For AP mounts these two speeds are handled separately - so SetSlewRate() actually sets the MoveTo speed for AP mounts - confusing!
728 // ApSetSlew
730 {
731  if (!isSimulation() && selectAPMoveToRate(PortFD, index) < 0)
732  {
734  IDSetSwitch(&SlewRateSP, "Error setting slew mode.");
735  return false;
736  }
737 
738  SlewRateSP.s = IPS_OK;
739  IDSetSwitch(&SlewRateSP, nullptr);
740  return true;
741 }
742 
744 {
745  double parkAz = GetAxis1Park();
746  double parkAlt = GetAxis2Park();
747 
748  char AzStr[16], AltStr[16];
749  fs_sexa(AzStr, parkAz, 2, 3600);
750  fs_sexa(AltStr, parkAlt, 2, 3600);
751  LOGF_DEBUG("Parking to Az (%s) Alt (%s)...", AzStr, AltStr);
752 
753  if (isSimulation())
754  {
755  INDI::IEquatorialCoordinates equatorialCoords {0, 0};
756  INDI::IHorizontalCoordinates horizontalCoords {parkAz, parkAlt};
757  INDI::HorizontalToEquatorial(&horizontalCoords, &m_Location, ln_get_julian_from_sys(), &equatorialCoords);
758  Goto(equatorialCoords.rightascension, equatorialCoords.declination);
759  }
760  else
761  {
762  if (setAPObjectAZ(PortFD, parkAz) < 0 || setAPObjectAlt(PortFD, parkAlt) < 0)
763  {
764  LOG_ERROR("Error setting Az/Alt.");
765  return false;
766  }
767 
768  int err = 0;
769 
770  /* Slew reads the '0', that is not the end of the slew */
771  if ((err = Slew(PortFD)))
772  {
773  LOGF_ERROR("Error Slewing to Az %s - Alt %s", AzStr, AltStr);
774  slewError(err);
775  return false;
776  }
777 
778  motionCommanded = true;
779  lastAZ = parkAz;
780  lastAL = parkAlt;
781  }
782 
783  EqNP.s = IPS_BUSY;
785  LOG_INFO("Parking is in progress...");
786 
787  return true;
788 }
789 
791 {
792  // The AP :PO# should only be used during initilization and not here as indicated by email from Preston on 2017-12-12
793 
794  // Enable tracking
795  SetTrackEnabled(true);
796 
797  SetParked(false);
798 
799  return true;
800 }
801 
803 {
804  INDI::IHorizontalCoordinates horizontalPos;
806 
807  INDI::EquatorialToHorizontal(&equatorialPos, &m_Location, ln_get_julian_from_sys(), &horizontalPos);
808  double parkAZ = horizontalPos.azimuth;
809  double parkAlt = horizontalPos.altitude;
810 
811  char AzStr[16], AltStr[16];
812  fs_sexa(AzStr, parkAZ, 2, 3600);
813  fs_sexa(AltStr, parkAlt, 2, 3600);
814 
815  LOGF_DEBUG("Setting current parking position to coordinates Az (%s) Alt (%s)", AzStr, AltStr);
816 
817  SetAxis1Park(parkAZ);
818  SetAxis2Park(parkAlt);
819 
820  return true;
821 }
822 
824 {
825  // Az = 0 for North hemisphere
826  SetAxis1Park(LocationN[LOCATION_LATITUDE].value > 0 ? 0 : 180);
827 
828  // Alt = Latitude
830 
831  return true;
832 }
833 
834 void LX200AstroPhysicsGTOCP2::syncSideOfPier()
835 {
836  const char *cmd = ":pS#";
837  // Response
838  char response[16] = { 0 };
839  int rc = 0, nbytes_read = 0, nbytes_written = 0;
840 
841  LOGF_DEBUG("CMD: <%s>", cmd);
842 
843  tcflush(PortFD, TCIOFLUSH);
844 
845  if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
846  {
847  char errmsg[256];
848  tty_error_msg(rc, errmsg, 256);
849  LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
850  return;
851  }
852 
853  // Read Side
854  if ((rc = tty_read_section(PortFD, response, '#', 3, &nbytes_read)) != TTY_OK)
855  {
856  char errmsg[256];
857  tty_error_msg(rc, errmsg, 256);
858  LOGF_ERROR("Error reading from device %s (%d)", errmsg, rc);
859  return;
860  }
861 
862  response[nbytes_read - 1] = '\0';
863 
864  tcflush(PortFD, TCIOFLUSH);
865 
866  LOGF_DEBUG("RES: <%s>", response);
867 
868  if (!strcmp(response, "East"))
870  else if (!strcmp(response, "West"))
872  else
873  LOGF_ERROR("Invalid pier side response from device-> %s", response);
874 
875 }
876 
878 {
880 
884 
885  return true;
886 }
887 
889 {
890  int err = 0;
891 
892  if (mode == TRACK_CUSTOM)
893  {
895  {
896  LOGF_ERROR("Error setting tracking mode (%d).", err);
897  return false;
898  }
899 
900  return SetTrackRate(TrackRateN[AXIS_RA].value, TrackRateN[AXIS_DE].value);
901  }
902 
903  if (!isSimulation() && (err = selectAPTrackingMode(PortFD, mode)) < 0)
904  {
905  LOGF_ERROR("Error setting tracking mode (%d).", err);
906  return false;
907  }
908 
909  return true;
910 }
911 
913 {
915 }
916 
917 bool LX200AstroPhysicsGTOCP2::SetTrackRate(double raRate, double deRate)
918 {
919  // Convert to arcsecs/s to AP sidereal multiplier
920  /*
921  :RR0.0000# = normal sidereal tracking in RA - similar to :RT2#
922  :RR+1.0000# = 1 + normal sidereal = 2X sidereal
923  :RR+9.0000# = 9 + normal sidereal = 10X sidereal
924  :RR-1.0000# = normal sidereal - 1 = 0 or Stop - similar to :RT9#
925  :RR-11.0000# = normal sidereal - 11 = -10X sidereal (East at 10X)
926 
927  :RD0.0000# = normal zero rate for Dec.
928  :RD5.0000# = 5 + normal zero rate = 5X sidereal clockwise from above - equivalent to South
929  :RD-5.0000# = normal zero rate - 5 = 5X sidereal counter-clockwise from above - equivalent to North
930  */
931 
932  double APRARate = (raRate - TRACKRATE_SIDEREAL) / TRACKRATE_SIDEREAL;
933  double APDERate = deRate / TRACKRATE_SIDEREAL;
934 
935  if (!isSimulation())
936  {
937  if (setAPRATrackRate(PortFD, APRARate) < 0 || setAPDETrackRate(PortFD, APDERate) < 0)
938  return false;
939  }
940 
941  return true;
942 }
943 
945 {
946  return (getAPUTCOffset(PortFD, offset) == 0);
947 }
948 
950 {
951  bool rc = LX200Generic::MoveNS(dir, command);
952 
953  if (command == MOTION_START)
954  motionCommanded = true;
955 
956  return rc;
957 }
958 
960 {
961  bool rc = LX200Generic::MoveWE(dir, command);
962 
963  if (command == MOTION_START)
964  motionCommanded = true;
965 
966  return rc;
967 }
968 
970 {
971  LOGF_DEBUG("%s: Motion commanded? %s", __FUNCTION__, motionCommanded ? "True" : "False");
972 
973  // GTOCP2 (Version 'E' and earilar) has a bug that would reset the guide rate to whatever last motion took place
974  // So it must be reset to the user setting in order for guiding to work properly.
975  if (motionCommanded)
976  {
977  LOGF_DEBUG("%s: Issuing select guide rate index: %d", __FUNCTION__, IUFindOnSwitchIndex(&APGuideSpeedSP));
979  motionCommanded = false;
980  }
981 }
bool isConnected() const
Definition: basedevice.cpp:520
const char * getDeviceName() const
Definition: basedevice.cpp:821
virtual bool Disconnect()
Disconnect from device.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
bool isSimulation() const
TelescopeStatus TrackState
void SetAxis1Park(double value)
SetRAPark Set current RA/AZ parking position. The data park file (stored in ~/.indi/ParkData....
ISwitchVectorProperty MovementNSSP
ISwitchVectorProperty AbortSP
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.
double GetAxis1Park() const
double GetAxis2Park() const
ISwitchVectorProperty PECStateSP
ISwitchVectorProperty TrackModeSP
ISwitchVectorProperty SlewRateSP
virtual void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
INumberVectorProperty EqNP
INumber TrackRateN[2]
IGeographicCoordinates m_Location
uint32_t GetTelescopeCapability() const
GetTelescopeCapability returns the capability of the Telescope.
void NewRaDec(double ra, double dec)
The child class calls this function when it has updates.
INumber LocationN[3]
void setPierSide(TelescopePierSide side)
ISwitch * SlewRateS
bool InitPark()
InitPark Loads parking data (stored in ~/.indi/ParkData.xml) that contains parking status and parking...
ISwitchVectorProperty MovementWESP
void SetAxis2Park(double steps)
SetDEPark Set current DEC/ALT parking position. The data park file (stored in ~/.indi/ParkData....
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.
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...
virtual bool SetTrackEnabled(bool enabled) override
SetTrackEnabled Engages or disengages mount tracking. If there are no tracking modes available,...
virtual bool Handshake() override
perform handshake with device to check communication
virtual const char * getDefaultName() override
ITextVectorProperty VersionTP
ISwitchVectorProperty APSlewSpeedSP
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
ISwitchVectorProperty APGuideSpeedSP
INumberVectorProperty HourangleCoordsNP
virtual bool updateTime(ln_date *utc, double utc_offset) override
Update telescope time, date, and UTC offset.
virtual bool SetTrackMode(uint8_t mode) override
SetTrackMode Set active tracking mode. Do not change track state.
virtual bool Disconnect() override
Disconnect from device.
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
ISwitchVectorProperty SyncCMRSP
virtual int SendPulseCmd(int8_t direction, uint32_t duration_msec) override
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
INumberVectorProperty HorizontalCoordsNP
virtual bool getUTFOffset(double *offset) override
virtual void debugTriggered(bool enable) override
Inform driver that the debug option was triggered. This function is called after setDebug is triggere...
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual bool SetTrackRate(double raRate, double deRate) override
SetTrackRate Set custom tracking rates.
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool ReadScopeStatus() override
Read telescope status.
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
virtual bool Park() override
Park the telescope to its home position.
virtual bool Sync(double ra, double dec) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
ISwitchVectorProperty SwapSP
virtual bool SetSlewRate(int index) override
SetSlewRate Set desired slew rate index.
virtual bool UnPark() override
Unpark the telescope if already parked.
virtual bool Goto(double, double) override
Move the scope to the supplied RA and DEC coordinates.
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.
virtual void slewError(int slewCode)
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
bool sendLocationOnStartup
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
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...
virtual void debugTriggered(bool enable) override
Inform driver that the debug option was triggered. This function is called after setDebug is triggere...
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
void setLX200Capability(uint32_t cap)
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.
#define setLocalTime(fd, x, y, z)
Definition: ieq45driver.h:141
double ra
double dec
ISState
Switch state.
Definition: indiapi.h:150
@ ISS_OFF
Definition: indiapi.h:151
@ ISS_ON
Definition: indiapi.h:152
@ IP_RW
Definition: indiapi.h:186
@ IP_RO
Definition: indiapi.h:184
@ 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
INDI_DIR_NS
Definition: indibasetypes.h:48
int tty_read_section(int fd, char *buf, char stop_char, int timeout, int *nbytes_read)
read buffer from terminal with a delimiter
Definition: indicom.c:566
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition: indicom.c:424
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition: indicom.c:1167
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.
@ TTY_OK
Definition: indicom.h:150
#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 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 IUGetConfigNumber(const char *dev, const char *property, const char *member, double *value)
IUGetConfigNumber Opens configuration file and reads single number property.
Definition: indidriver.c:831
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_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
int selectAPMoveToRate(int fd, int moveToIndex)
int setAPUTCOffset(int fd, double hours)
int setAPSiteLatitude(int fd, double Lat)
void set_lx200ap_name(const char *deviceName, unsigned int debug_level)
int setAPRATrackRate(int fd, double rate)
int setAPObjectRA(int fd, double ra)
int APSyncCMR(int fd, char *matchedObject)
int setAPObjectAlt(int fd, double alt)
int swapAPButtons(int fd, int currentSwap)
int setAPObjectDEC(int fd, double dec)
int APSendPulseCmd(int fd, int direction, int duration_msec)
int selectAPSlewRate(int fd, int slewIndex)
int selectAPPECState(int fd, int pecstate)
int selectAPGuideRate(int fd, int guideRate)
int setAPObjectAZ(int fd, double az)
int getAPUTCOffset(int fd, double *value)
int setAPDETrackRate(int fd, double rate)
int setAPSiteLongitude(int fd, double Long)
int selectAPTrackingMode(int fd, int trackMode)
#define setAPBackLashCompensation(fd, x, y, z)
Definition: lx200apdriver.h:30
#define getAPVersionNumber(fd, x)
Definition: lx200apdriver.h:28
#define setAPClearBuffer(fd)
Definition: lx200apdriver.h:29
#define setAPPark(fd)
Definition: lx200apdriver.h:24
#define AP_TRACKING_OFF
Definition: lx200apdriver.h:36
#define setAPUnPark(fd)
Definition: lx200apdriver.h:25
#define AP_TRACKING_SIDEREAL
Definition: lx200apdriver.h:32
int checkLX200EquatorialFormat(int fd)
int abortSlew(int fd)
int setCalenderDate(int fd, int dd, int mm, int yy, bool addSpace)
int Slew(int fd)
#define getLX200DEC(fd, x)
Definition: lx200driver.h:118
@ LX200_24
Definition: lx200driver.h:64
#define getLX200RA(fd, x)
Definition: lx200driver.h:117
#define getLX200Alt(fd, x)
Definition: lx200driver.h:124
#define getLX200Az(fd, x)
Definition: lx200driver.h:125
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
__u8 cmd[4]
Definition: pwc-ioctl.h:2
char name[MAXINDINAME]
Definition: indiapi.h:371
char name[MAXINDINAME]
Definition: indiapi.h:250