Instrument Neutral Distributed Interface INDI  2.0.2
lx200ap.cpp
Go to the documentation of this file.
1 /*
2  Astro-Physics INDI driver
3 
4  Copyright (C) 2014 Jasem Mutlaq, Mike Fulbright
5  Copyright (C) 2020 indilib.org, by Markus Wildi
6 
7  Based on INDI Astrophysics Driver by Markus Wildi
8 
9  This library is free software; you can redistribute it and/or
10  modify it under the terms of the GNU Lesser General Public
11  License as published by the Free Software Foundation; either
12  version 2.1 of the License, or (at your option) any later version.
13 
14  This library is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  Lesser General Public License for more details.
18 
19  You should have received a copy of the GNU Lesser General Public
20  License along with this library; if not, write to the Free Software
21  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23 /*
24 2020-08-07, ToDo
25 AP commands not yet implemented for revision >= G
26 
27 Sets the centering rate for the N-S-E-W buttons to xxx Rcxxx#
28 Default command for an equatorial fork mount, which eliminates the meridian flip :FM#
29 Default command for A German equatorial mount that includes the meridian flip :EM#
30 Horizon check during slewing functions :ho# and :hq#
31 */
32 
33 
34 
35 #include "lx200ap.h"
36 
37 #include "indicom.h"
38 #include "lx200driver.h"
39 #include "lx200apdriver.h"
41 
42 #include <libnova/transform.h>
43 
44 #include <cmath>
45 #include <cstring>
46 #include <unistd.h>
47 #include <termios.h>
48 
49 
50 // maximum guide pulse request to send to controller
51 #define MAX_LX200AP_PULSE_LEN 999
52 
53 /* Constructor */
55 {
59 }
60 
62 {
63  return "AstroPhysics";
64 }
65 
67 {
68  Connection::Interface *activeConnection = getActiveConnection();
69  if (!activeConnection->name().compare("CONNECTION_TCP"))
70  {
71  // When using a tcp connection, the GTOCP4 adds trailing LF to response.
72  // this small hack will get rid of them as they are not expected in the driver. and generated
73  // lot of communication errors.
75  }
76  return LX200Generic::Connect();
77 }
78 
80 {
82 
84 
85  IUFillSwitch(&StartUpS[0], "COLD", "Cold", ISS_OFF);
86  IUFillSwitch(&StartUpS[1], "WARM", "Warm", ISS_OFF);
88  ISR_1OFMANY, 0, IPS_IDLE);
89 
90  IUFillNumber(&HourangleCoordsN[0], "HA", "HA H:M:S", "%10.6m", -24., 24., 0., 0.);
91  IUFillNumber(&HourangleCoordsN[1], "DEC", "Dec D:M:S", "%10.6m", -90.0, 90.0, 0., 0.);
92  IUFillNumberVector(&HourangleCoordsNP, HourangleCoordsN, 2, getDeviceName(), "HOURANGLE_COORD", "Hourangle Coords",
94 
95  IUFillNumber(&HorizontalCoordsN[0], "AZ", "Az D:M:S", "%10.6m", 0., 360., 0., 0.);
96  IUFillNumber(&HorizontalCoordsN[1], "ALT", "Alt D:M:S", "%10.6m", -90., 90., 0., 0.);
98  "Horizontal Coords", MAIN_CONTROL_TAB, IP_RW, 120, IPS_IDLE);
99 
100  // Max rate is 999.99999X for the GTOCP4.
101  // Using :RR998.9999# just to be safe. 15.041067*998.99999 = 15026.02578
102  TrackRateN[AXIS_RA].min = -15026.0258;
103  TrackRateN[AXIS_RA].max = 15026.0258;
104  TrackRateN[AXIS_DE].min = -998.9999;
105  TrackRateN[AXIS_DE].max = 998.9999;
106 
107  // Motion speed of axis when pressing NSWE buttons
108  IUFillSwitch(&SlewRateS[0], "1", "Guide", ISS_OFF);
109  IUFillSwitch(&SlewRateS[1], "12", "12x", ISS_OFF);
110  IUFillSwitch(&SlewRateS[2], "64", "64x", ISS_ON);
111  IUFillSwitch(&SlewRateS[3], "600", "600x", ISS_OFF);
112  IUFillSwitch(&SlewRateS[4], "1200", "1200x", ISS_OFF);
113  IUFillSwitchVector(&SlewRateSP, SlewRateS, 5, getDeviceName(), "TELESCOPE_SLEW_RATE", "Slew Rate", MOTION_TAB, IP_RW,
114  ISR_1OFMANY, 0, IPS_IDLE);
115 
116  // Slew speed when performing regular GOTO
117  IUFillSwitch(&APSlewSpeedS[0], "600", "600x", ISS_ON);
118  IUFillSwitch(&APSlewSpeedS[1], "900", "900x", ISS_OFF);
119  IUFillSwitch(&APSlewSpeedS[2], "1200", "1200x", ISS_OFF);
121  0, IPS_IDLE);
122 
123  IUFillSwitch(&SwapS[0], "NS", "North/South", ISS_OFF);
124  IUFillSwitch(&SwapS[1], "EW", "East/West", ISS_OFF);
125  IUFillSwitchVector(&SwapSP, SwapS, 2, getDeviceName(), "SWAP", "Swap buttons", MOTION_TAB, IP_RW, ISR_1OFMANY, 0,
126  IPS_IDLE);
127 
128  //2020-06-02, wildi, I'm not fond of this CMR sync
129  IUFillSwitch(&SyncCMRS[USE_REGULAR_SYNC], ":CM#", ":CM#", ISS_ON);
130  IUFillSwitch(&SyncCMRS[USE_CMR_SYNC], ":CMR#", ":CMR#", ISS_OFF);
132  IPS_IDLE);
133 
134  // guide speed
135  IUFillSwitch(&APGuideSpeedS[0], "0.25", "0.25x", ISS_OFF);
136  IUFillSwitch(&APGuideSpeedS[1], "0.5", "0.50x", ISS_OFF);
137  IUFillSwitch(&APGuideSpeedS[2], "1.0", "1.0x", ISS_ON);
139  0, IPS_IDLE);
140 
141  // Unpark from?
142  IUFillSwitch(&UnparkFromS[0], "Last", "Last Parked", ISS_OFF);
143  IUFillSwitch(&UnparkFromS[1], "Park1", "Park1", ISS_OFF);
144  IUFillSwitch(&UnparkFromS[2], "Park2", "Park2", ISS_ON);
145  IUFillSwitch(&UnparkFromS[3], "Park3", "Park3", ISS_OFF);
146  IUFillSwitch(&UnparkFromS[4], "Park4", "Park4", ISS_OFF);
147  IUFillSwitchVector(&UnparkFromSP, UnparkFromS, 5, getDeviceName(), "UNPARK_FROM", "Unpark From?", MAIN_CONTROL_TAB, IP_RW,
148  ISR_1OFMANY, 0, IPS_IDLE);
149 
150  // park presets
151  IUFillSwitch(&ParkToS[0], "Custom", "Custom", ISS_OFF);
152  IUFillSwitch(&ParkToS[1], "Park1", "Park1", ISS_OFF);
153  IUFillSwitch(&ParkToS[2], "Park2", "Park2", ISS_ON);
154  IUFillSwitch(&ParkToS[3], "Park3", "Park3", ISS_OFF);
155  IUFillSwitch(&ParkToS[4], "Park4", "Park4", ISS_OFF);
157  IPS_IDLE);
158 
159  IUFillText(&VersionT[0], "Version", "Version", "");
160  IUFillTextVector(&VersionTP, VersionT, 1, getDeviceName(), "Firmware", "Firmware", SITE_TAB, IP_RO, 0, IPS_IDLE);
161 
162  // UTC offset
163  IUFillNumber(&APUTCOffsetN[0], "APUTC_OFFSET", "AP UTC offset", "%8.5f", 0.0, 24.0, 0.0, 0.);
164  IUFillNumberVector(&APUTCOffsetNP, APUTCOffsetN, 1, getDeviceName(), "APUTC_OFFSET", "AP UTC offset", SITE_TAB,
165  IP_RW, 60, IPS_OK);
166  // sidereal time, ToDO move define where it belongs to
167  IUFillNumber(&APSiderealTimeN[0], "AP_SIDEREAL_TIME", "AP sidereal time", "%10.6m", 0.0, 24.0, 0.0, 0.0);
168  IUFillNumberVector(&APSiderealTimeNP, APSiderealTimeN, 1, getDeviceName(), "AP_SIDEREAL_TIME", "ap sidereal time", SITE_TAB,
169  IP_RO, 60, IPS_OK);
170 
171  //SetParkDataType(PARK_AZ_ALT);
172 
173  return true;
174 }
175 
177 {
179 
181 
182  // MSF 2018/04/10 - disable this behavior for now - we want to have
183  // UnparkFromSP to always start out as "Last Parked" for safety
184  // load config to get unpark from position user wants BEFORE we connect to mount
185  //if (!isConnected())
186  //{
187  // LOG_DEBUG("Loading unpark from location from config file");
188  // loadConfig(true, UnparkFromSP.name);
189  //}
190 
191  if (isConnected())
192  {
200  }
201 }
202 
204 {
206 
208 
209  if (isConnected())
210  {
211  deleteProperty("TELESCOPE_PIER_SIDE");
212  if (firmwareVersion < MCV_G)
213  {
216  }
219  /* Motion group */
228 
229 #ifdef no
230  if(!InitPark())
231  {
232  LOG_WARN("No valid park position found or ParkData.xml missing");
233  }
234  else
235  {
236  // wildi: too early, no time, site information
237  //loadConfig(true, ParkToSP.name);
238  //loadConfig(true, UnparkFromSP.name);
239  SetParked(true);
240  }
241 
242  // 2020-05-30, wildi, this is the wrong place to load config data,
243  // see celestron driver.
244  // The RA/Dec values are 0,90 at startup and should not be
245  // displayed until they are set in UnPark()
246  // load in config value for park to and initialize park position
247  loadConfig(true, ParkToSP.name);
248  ParkPosition parkPos = static_cast<ParkPosition>(IUFindOnSwitchIndex(&ParkToSP));
249  LOGF_DEBUG("park position = %d", parkPos);
250 
251  // setup location
252  double longitude = -1000, latitude = -1000;
253  // Get value from config file if it exists.
254  IUGetConfigNumber(getDeviceName(), "GEOGRAPHIC_COORD", "LONG", &longitude);
255  IUGetConfigNumber(getDeviceName(), "GEOGRAPHIC_COORD", "LAT", &latitude);
256  if (longitude != -1000 && latitude != -1000)
257  updateLocation(latitude, longitude, 0);
258 
259  //2020-05-30, wildi, LocationN will be overwritten
260  // initialize park position
261  if (InitPark())
262  {
263  SetAxis1ParkDefault(LocationN[LOCATION_LATITUDE].value >= 0 ? 0 : 180);
265  }
266  else
267  {
268  // Otherwise, we set all parking data to default in case no parking data is found.
269  SetAxis1Park(LocationN[LOCATION_LATITUDE].value >= 0 ? 0 : 180);
271 
272  SetAxis1ParkDefault(LocationN[LOCATION_LATITUDE].value >= 0 ? 0 : 180);
274  }
275 
276  // override with predefined position if selected
277  if (parkPos != PARK_CUSTOM)
278  {
279  double parkAz, parkAlt;
280  if (calcParkPosition(parkPos, &parkAlt, &parkAz))
281  {
282  SetAxis1Park(parkAz);
283  SetAxis2Park(parkAlt);
284  LOGF_DEBUG("Set park position %d to az=%f alt=%f", parkPos, parkAz, parkAlt);
285  }
286  else
287  {
288  LOGF_ERROR("Unable to set park position %d!!", parkPos);
289  }
290  }
291 #endif
292  }
293  else
294  {
305  }
306 
307  return true;
308 }
309 
310 bool LX200AstroPhysics::getFirmwareVersion()
311 {
312  bool success;
313  char rev[8];
314  char versionString[128];
315 
316  success = false;
317 
318  if (isSimulation())
319  strncpy(versionString, "VCP4-P01-01", 128);
320  else
321  getAPVersionNumber(PortFD, versionString);
322 
323  VersionTP.s = IPS_OK;
324  IUSaveText(&VersionT[0], versionString);
325  IDSetText(&VersionTP, nullptr);
326 
327  // Check controller version
328  // example "VCP4-P01-01" for CP4 or newer
329  // single or double letter like "T" or "V1" for CP3 and older
330 
331  // CP4
332  // 2020-06-02, wildi, ToDo unify all versions
333  if (strstr(versionString, "VCP4"))
334  {
335  firmwareVersion = MCV_V;
336  servoType = GTOCP4;
337  strcpy(rev, "V");
338  success = true;
339  }
340  else if (strlen(versionString) == 1 || strlen(versionString) == 2)
341  {
342  // Check earlier versions
343  // FIXME could probably use better range checking in case we get a letter like 'Z' that doesn't map to anything!
344  int typeIndex = VersionT[0].text[0] - 'D';
345  if (typeIndex >= 0)
346  {
347  firmwareVersion = static_cast<ControllerVersion>(typeIndex);
348  LOGF_DEBUG("Firmware version index: %d", typeIndex);
349  if (firmwareVersion < MCV_G)
350  servoType = GTOCP2;
351  else
352  servoType = GTOCP3;
353 
354  strncpy(rev, versionString, 8);
355 
356  success = true;
357  }
358  else
359  {
360  LOGF_WARN("unknown AP controller version %s", VersionT[0].text);
361  }
362  }
363 
364  if (success)
365  {
366  LOGF_INFO("Servo Box Controller: GTOCP%d.", servoType);
367  LOGF_INFO("Firmware Version: '%s' - %s", rev, versionString + 5);
368  }
369 
370  return success;
371 }
372 
373 #ifdef no
374 bool LX200AstroPhysics::initMount()
375 {
376  // Make sure that the mount is setup according to the properties
377  int err = 0;
378  if (!IsMountInitialized(&mountInitialized))
379  {
380  LOG_ERROR("Error determining if mount is initialized!");
381  return false;
382  }
383  if (!IsMountParked(&mountParked))
384  {
385  return false;
386  }
387  // 2020-03-17, wildi, AP unpark after the mount is initialized
388  // and more importantly, do not do it twice
389  if (!mountInitialized)
390  {
391  LOG_DEBUG("Mount is not yet initialized. Initializing it...");
392  if (!isSimulation())
393  {
394  // This is how to init the mount in case RA/DE are missing.
395  // :PO#
396  if (APUnParkMount(PortFD) < 0)
397  {
398  LOG_ERROR("UnParking Failed.");
399  return false;
400  }
401 
402  // Stop :Q#
403  abortSlew(PortFD);
404  }
405  }
406 
407  mountInitialized = true;
408 
409 
410  // Astrophysics mount is always unparked on startup
411  // In this driver, unpark only sets the tracking ON.
412  // APParkMount() is NOT called as this function, despite its name, is only used for initialization purposes.
413  // 2020-05-22, wildi, that might be too early
414  UnPark();
415  // On most mounts SlewRateS defines the MoveTo AND Slew (GOTO) speeds
416  // lx200ap is different - some of the MoveTo speeds are not VALID
417  // Slew speeds so we have to keep two lists.
418  //
419  // SlewRateS is used as the MoveTo speed
421  {
422  LOGF_ERROR("Error setting center (MoveTo) rate (%d).", err);
423  return false;
424  }
425 
426  SlewRateSP.s = IPS_OK;
427  IDSetSwitch(&SlewRateSP, nullptr);
428 
429  // APSlewSpeedsS defines the Slew (GOTO) speeds valid on the AP mounts
431  {
432  LOGF_ERROR("Error setting slew to rate (%d).", err);
433  return false;
434  }
435 
437  IDSetSwitch(&APSlewSpeedSP, nullptr);
438 
439  return true;
440 }
441 #endif
442 
443 /**************************************************************************************
444 **
445 ***************************************************************************************/
446 bool LX200AstroPhysics::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
447 {
448  if (strcmp(getDeviceName(), dev))
449  return false;
450 
451  if (!strcmp(name, APUTCOffsetNP.name))
452  {
453  if (IUUpdateNumber(&APUTCOffsetNP, values, names, n) < 0)
454  return false;
455 
456  float mdelay;
457  int err;
458 
459  mdelay = APUTCOffsetN[0].value;
460 
461  if (!isSimulation() && (err = setAPUTCOffset(PortFD, mdelay) < 0))
462  {
463  LOGF_ERROR("Error setting UTC offset (%d).", err);
464  return false;
465  }
466 
468  IDSetNumber(&APUTCOffsetNP, nullptr);
469 
470  return true;
471  }
472  if (!strcmp(name, HourangleCoordsNP.name))
473  {
474 
475  if (IUUpdateNumber(&HourangleCoordsNP, values, names, n) < 0)
476  return false;
477 
478  double lng = LocationN[LOCATION_LONGITUDE].value;
479  double lst = get_local_sidereal_time(lng);
480  double ra = lst - HourangleCoordsN[0].value;
481  double dec = HourangleCoordsN[1].value;
482  bool success = false;
483  if ((ISS_ON == IUFindSwitch(&CoordSP, "TRACK")->s) || (ISS_ON == IUFindSwitch(&CoordSP, "SLEW")->s))
484  {
485  success = Goto(ra, dec);
486  }
487  else
488  {
489  success = Sync(ra, dec);
490  }
491  if (success)
492  {
494  }
495  else
496  {
498  }
499  IDSetNumber(&HourangleCoordsNP, nullptr);
500  return true;
501  }
502 
503  return LX200Generic::ISNewNumber(dev, name, values, names, n);
504 }
505 
506 bool LX200AstroPhysics::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
507 {
508  int err = 0;
509 
510  // ignore if not ours //
511  if (strcmp(getDeviceName(), dev))
512  return false;
513 
514  // Reintroduce cold/warm start from lx200ap.cpp in order to check if a valid park position is available.
515  // AP's startup sequence is not mandatory with the exception of :PO.
516  // Button Startup is defined outside "connected" state only to make it more visible to the user.
517  if (!strcmp(name, StartUpSP.name))
518  {
519  if (!isConnected())
520  {
522  LOG_ERROR("Connect first before reading mount's park configuration");
523  IDSetSwitch(&StartUpSP, nullptr);
524  return false;
525  }
526  int switch_nr;
527 
528  IUUpdateSwitch(&StartUpSP, states, names, n);
529 
530  if (StartUpSP.s == IPS_OK)
531  {
532  LOG_INFO("Mount's park configuration already read");
533  StartUpSP.s = IPS_OK;
534  IDSetSwitch(&StartUpSP, nullptr);
535  return true;
536  }
537  // rely on INDI's logic
538  if (!(TimeTP.s == IPS_OK && LocationNP.s == IPS_OK))
539  {
541  LOGF_ERROR("Time is %s ok and location is %s ok must be set before mount initialization is invoked.",
542  TimeTP.s == IPS_OK ? "" : "not ", LocationNP.s == IPS_OK ? "" : "not " );
543  IDSetSwitch(&StartUpSP, nullptr);
544  return false;
545  }
546 
547  // check only the status of the ParkData.xml, do not set ParkSP
548  bool parkDataValid = (LoadParkData() == nullptr);
549  if (parkDataValid && isParked())
550  {
551  if (!loadConfig(true, UnparkFromSP.name))
552  {
553  LOG_DEBUG("could not load config data for UnparkFromSP.name");
554  }
555  if (!loadConfig(true, ParkToSP.name))
556  {
557  LOG_DEBUG("could not load config data for ParkTo.name");
558  }
559 
560  if(UnparkFromS[PARK_LAST].s == ISS_ON)
561  {
562  LOGF_INFO("Driver's config 'Unpark From ?' is set to 'Last Parked': will unpark from Alt=%f Az=%f", GetAxis2Park(),
563  GetAxis1Park());
564  }
565  // forcing mount being parked from INDI's perspective
566  SetParked(true);
567  }
568  else
569  {
570  if (StartUpSP.sp[0].s == ISS_ON)
571  {
572  LOG_WARN("Set 'Park To' and 'Unpark From ?' manually and save dirver's config and park data");
573  SetParked(true);
574 
575  StartUpSP.s = IPS_OK;
576  IDSetSwitch(&StartUpSP, nullptr);
577  }
578  else
579  {
580  LOG_WARN("No ParkData.xml or parkstatus is false, do a cold start");
582  IDSetSwitch(&StartUpSP, nullptr);
583  }
584  return false;
585  }
586 
587  if (isSimulation())
588  {
589  SlewRateSP.s = IPS_OK;
590  IDSetSwitch(&SlewRateSP, nullptr);
591 
593  IDSetSwitch(&APSlewSpeedSP, nullptr);
594 
595  IUSaveText(&VersionT[0], "1.0");
596  VersionTP.s = IPS_OK;
597  IDSetText(&VersionTP, nullptr);
598 
599  StartUpSP.s = IPS_OK;
600  IDSetSwitch(&StartUpSP, "Mount initialized.");
601 
602  return true;
603 
604  }
605  // Make sure that the mount is setup according to the properties
606  switch_nr = IUFindOnSwitchIndex(&TrackModeSP);
607 
608  if ( (err = selectAPTrackingMode(PortFD, switch_nr)) < 0)
609  {
610  LOGF_ERROR("StartUpSP: Error setting tracking mode (%d).", err);
611  return false;
612  }
613 
614  //TrackState = (switch_nr != AP_TRACKING_OFF) ? SCOPE_TRACKING : SCOPE_IDLE;
615 
616  // On most mounts SlewRateS defines the MoveTo AND Slew (GOTO) speeds
617  // lx200ap is different - some of the MoveTo speeds are not VALID
618  // Slew speeds so we have to keep two lists.
619  //
620  // SlewRateS is used as the MoveTo speed
621  switch_nr = IUFindOnSwitchIndex(&SlewRateSP);
622  if ( (err = selectAPMoveToRate(PortFD, switch_nr)) < 0)
623  {
624  LOGF_ERROR("StartUpSP: Error setting move rate (%d).", err);
625  return false;
626  }
627 
628  SlewRateSP.s = IPS_OK;
629  IDSetSwitch(&SlewRateSP, nullptr);
630 
631  // APSlewSpeedsS defines the Slew (GOTO) speeds valid on the AP mounts
632  switch_nr = IUFindOnSwitchIndex(&APSlewSpeedSP);
633  if ( (err = selectAPSlewRate(PortFD, switch_nr)) < 0)
634  {
635  LOGF_ERROR("StartUpSP: Error setting slew to rate (%d).", err);
636  return false;
637  }
639  IDSetSwitch(&APSlewSpeedSP, nullptr);
640 
643 
644  // make a IDSet in order the dome controller is aware of the initial values
647 
649 
650  char versionString[64];
651  getAPVersionNumber(PortFD, versionString);
652  VersionTP.s = IPS_OK;
653  IUSaveText(&VersionT[0], versionString);
654  IDSetText(&VersionTP, nullptr);
655 
656  StartUpSP.s = IPS_OK;
657  IDSetSwitch(&StartUpSP, "Mount initialized.");
658 
659  return true;
660  }
661 
662  // =======================================
663  // Swap Buttons
664  // =======================================
665  if (!strcmp(name, SwapSP.name))
666  {
667  int currentSwap;
668 
670  IUUpdateSwitch(&SwapSP, states, names, n);
671  currentSwap = IUFindOnSwitchIndex(&SwapSP);
672 
673  if ((!isSimulation() && (err = swapAPButtons(PortFD, currentSwap)) < 0))
674  {
675  LOGF_ERROR("Error swapping buttons (%d).", err);
676  return false;
677  }
678 
679  SwapS[0].s = ISS_OFF;
680  SwapS[1].s = ISS_OFF;
681  SwapSP.s = IPS_OK;
682  IDSetSwitch(&SwapSP, nullptr);
683  return true;
684  }
685 
686  // ===========================================================
687  // GOTO ("slew") Speed.
688  // ===========================================================
689  if (!strcmp(name, APSlewSpeedSP.name))
690  {
691  IUUpdateSwitch(&APSlewSpeedSP, states, names, n);
692  int slewRate = IUFindOnSwitchIndex(&APSlewSpeedSP);
693 
694  if (!isSimulation() && (err = selectAPSlewRate(PortFD, slewRate) < 0))
695  {
696  LOGF_ERROR("Error setting move to rate (%d).", err);
697  return false;
698  }
699 
701  IDSetSwitch(&APSlewSpeedSP, nullptr);
702  return true;
703  }
704 
705  // ===========================================================
706  // Guide Speed.
707  // ===========================================================
708  if (!strcmp(name, APGuideSpeedSP.name))
709  {
710  IUUpdateSwitch(&APGuideSpeedSP, states, names, n);
711  int guideRate = IUFindOnSwitchIndex(&APGuideSpeedSP);
712 
713  if (!isSimulation() && (err = selectAPGuideRate(PortFD, guideRate) < 0))
714  {
715  LOGF_ERROR("Error setting guiding to rate (%d).", err);
716  return false;
717  }
718 
720  IDSetSwitch(&APGuideSpeedSP, nullptr);
721  return true;
722  }
723 
724  // =======================================
725  // Choose the appropriate sync command
726  // =======================================
727  if (!strcmp(name, SyncCMRSP.name))
728  {
730  IUUpdateSwitch(&SyncCMRSP, states, names, n);
732  SyncCMRSP.s = IPS_OK;
733  IDSetSwitch(&SyncCMRSP, nullptr);
734  return true;
735  }
736 
737  // =======================================
738  // Choose the PEC playback mode
739  // =======================================
740  if (!strcmp(name, PECStateSP.name))
741  {
743  IUUpdateSwitch(&PECStateSP, states, names, n);
745 
746  int pecstate = IUFindOnSwitchIndex(&PECStateSP);
747 
748  if (!isSimulation() && (err = selectAPPECState(PortFD, pecstate) < 0))
749  {
750  LOGF_ERROR("Error setting PEC state (%d).", err);
751  return false;
752  }
753 
754  PECStateSP.s = IPS_OK;
755  IDSetSwitch(&PECStateSP, nullptr);
756 
757  return true;
758  }
759 
760  // ===========================================================
761  // Unpark from positions
762  // ===========================================================
763  if (!strcmp(name, UnparkFromSP.name))
764  {
765  IUUpdateSwitch(&UnparkFromSP, states, names, n);
766  ParkPosition unparkPos = static_cast<ParkPosition>(IUFindOnSwitchIndex(&UnparkFromSP));
767 
769  if( unparkPos != PARK_LAST)
770  {
771  double unparkAlt, unparkAz;
772  if (!calcParkPosition(unparkPos, &unparkAlt, &unparkAz))
773  {
774  LOG_WARN("Error calculating unpark position!");
776  }
777  else
778  {
779  SetAxis1Park(unparkAz);
780  SetAxis2Park(unparkAlt);
781  // 2020-06-01, wildi, UnPark() relies on it
782  saveConfig(true);
783  }
784  }
785  IDSetSwitch(&UnparkFromSP, nullptr);
786  return true;
787  }
788 
789  // ===========================================================
790  // Switch Park(ed), Unpark(ed)
791  // ===========================================================
792 
793  if (!strcmp(name, ParkSP.name))
794  {
795  if (StartUpSP.s != IPS_OK)
796  {
797  LOG_ERROR("Read driver's config first");
798  return false;
799  }
800  }
801  // ===========================================================
802  // Park To positions
803  // ===========================================================
804  if (!strcmp(name, ParkToSP.name))
805  {
806  IUUpdateSwitch(&ParkToSP, states, names, n);
807  ParkPosition parkPos = static_cast<ParkPosition>(IUFindOnSwitchIndex(&ParkToSP));
808 
809  // override with predefined position if selected
810  if (parkPos != PARK_CUSTOM)
811  {
812  double parkAz, parkAlt;
813  // if (!(TimeTP.s == IPS_OK && LocationNP.s == IPS_OK))
814  // {
815  // LOG_WARN("ParkTo can not calculate park position, latitude, longitude not yet available");
816  // IUResetSwitch(&ParkToSP);
817  // ParkToSP.s = IPS_ALERT;
818  // IDSetSwitch(&ParkToSP, nullptr);
819  // return false;
820  // }
821  if (calcParkPosition(parkPos, &parkAlt, &parkAz))
822  {
823  SetAxis1Park(parkAz);
824  SetAxis2Park(parkAlt);
825  LOGF_INFO("Set predefined park position %d to az=%f alt=%f", parkPos, parkAz, parkAlt);
826  }
827  else
828  {
829  LOGF_ERROR("Unable to set predefined park position %d!!", parkPos);
830  }
831  }
832  else
833  {
834  LOG_WARN("ISNewSwitch: park custom not yet supported");
836  ParkToSP.s = IPS_ALERT;
837  IDSetSwitch(&ParkToSP, nullptr);
838  return false;
839  }
841  ParkToS[(int)parkPos].s = ISS_ON;
842  ParkToSP.s = IPS_OK;
843  IDSetSwitch(&ParkToSP, nullptr);
844  return true;
845  }
846 
847  return LX200Generic::ISNewSwitch(dev, name, states, names, n);
848 }
849 
851 {
852  double lng = LocationN[LOCATION_LONGITUDE].value;
853  double lst = get_local_sidereal_time(lng);
854  // 2020-06-02, wildi, isParked is reserved for the state in ParkData.xml
855  // see method isParked()
856  bool isAPParked = false;
857  IsMountParked(&isAPParked);
858  if (!isAPParked)
859  {
860  double ha = get_local_hour_angle(lst, currentRA);
861 
862  // No need to spam log until we have some actual changes.
863  if (std::fabs(HourangleCoordsN[0].value - ha) > 0.0001 ||
864  std::fabs(HourangleCoordsN[1].value - currentDEC) > 0.0001)
865  {
866  // in case of simulation, the coordinates are set on parking
867  HourangleCoordsN[0].value = ha;
868  HourangleCoordsN[1].value = currentDEC;
870  IDSetNumber(&HourangleCoordsNP, nullptr );
871  }
872  }
873  double val = 0;
874  if ((!isSimulation()) && (getSDTime(PortFD, &val) < 0))
875  {
876  LOG_ERROR("Reading sidereal time failed %d");
877  return false;
878  }
879  if (isSimulation())
880  {
881  val = lst;
882  }
883  APSiderealTimeNP.np[0].value = val;
885  IDSetNumber(&APSiderealTimeNP, nullptr);
886 
887  if (isSimulation())
888  {
889  mountSim();
890  return true;
891  }
892 
893  double val_utc_offset;
894  if (getAPUTCOffset(PortFD, &val_utc_offset) < 0)
895  {
896  LOG_ERROR("Error reading UTC Offset.");
897  return false;
898  }
899  if (getLocalTime24(PortFD, &val) < 0)
900  {
901  LOG_DEBUG("Reading local time failed :GL %d");
902  }
903  if (firmwareVersion >= MCV_G)
904  {
905  char buf[64];
906  if (getCalendarDate(PortFD, buf) < 0)
907  {
908  LOG_DEBUG("Reading calendar day failed :GC");
909  }
910  }
911  if (getLX200Az(PortFD, &val) < 0)
912  {
913  LOG_DEBUG("Reading Az failed :GZ %d");
914  }
915  if (getLX200Alt(PortFD, &val) < 0)
916  {
917  LOG_DEBUG("Reading Alt failed :GA %d");
918  }
919  int ddd = 0;
920  int fmm = 0;
921  double ssf = 0.0;
922  if (getSiteLongitude(PortFD, &ddd, &fmm, &ssf) < 0)
923  {
924  LOG_DEBUG("Reading longitude failed :Gg %d");
925  }
926 
928  {
929  EqNP.s = IPS_ALERT;
930  IDSetNumber(&EqNP, "Error reading RA/DEC.");
931  return false;
932  }
933 
934  if (TrackState == SCOPE_SLEWING)
935  {
936  double dx = lastRA - currentRA;
937  double dy = lastDE - currentDEC;
938 
939  LOGF_DEBUG("Slewing... currentRA: %g dx: %g currentDE: %g dy: %g", currentRA, dx, currentDEC, dy);
940 
941  // Wait until acknowledged
942  if (dx == 0 && dy == 0)
943  {
945  LOG_INFO("Slew is complete. Tracking...");
946  }
947 
948  // Keep try of last values to determine if the mount settled.
949  lastRA = currentRA;
950  lastDE = currentDEC;
951  }
952  else if (TrackState == SCOPE_PARKING)
953  {
954  // new way
955  char parkStatus;
956  char slewStatus;
957  bool slewcomplete = false;
958  double PARKTHRES = 0.1; // max difference from parked position to consider mount PARKED
959  // wildi, downgrade
960  if ((firmwareVersion != MCV_UNKNOWN) && (firmwareVersion >= MCV_T))
961  {
962  if (check_lx200ap_status(PortFD, &parkStatus, &slewStatus) == 0)
963  {
964  LOGF_DEBUG("parkStatus: %c slewStatus: %c", parkStatus, slewStatus);
965 
966  if (slewStatus == '0')
967  slewcomplete = true;
968  }
969  }
970  // old way
971  if (getLX200Az(PortFD, &currentAz) < 0 || getLX200Alt(PortFD, &currentAlt) < 0)
972  {
973  EqNP.s = IPS_ALERT;
974  IDSetNumber(&EqNP, "Error reading Az/Alt.");
975  return false;
976  }
977 
978  double dx = lastAZ - currentAz;
979  double dy = lastAL - currentAlt;
980 
981  LOGF_DEBUG("Parking... currentAz: %g dx: %g currentAlt: %g dy: %g", currentAz, dx, currentAlt, dy);
982 
983  // if for some reason we check slew status BEFORE park motion starts make sure we dont consider park
984  // action complete too early by checking how far from park position we are!
985  if (slewcomplete && (dx > PARKTHRES || dy > PARKTHRES))
986  {
987  LOG_WARN("Parking... slew status indicates mount stopped by dx/dy too far from mount - continuing!");
988 
989  slewcomplete = false;
990  }
991 
992  if (slewcomplete || (dx <= PARKTHRES && dy <= PARKTHRES))
993  {
994  LOG_DEBUG("Parking slew is complete. Asking astrophysics mount to park...");
995 
996  if (APParkMount(PortFD) < 0)
997  {
998  LOG_ERROR("Parking Failed.");
999  return false;
1000  }
1001 
1002  // Turn off tracking.
1003  SetTrackEnabled(false);
1004  SetParked(true);
1005  saveConfig(true);
1006  // wildi: at this point park data can not be saved: LOG_INFO("Please, save park data, disconnect and power off the mount");
1007  LOG_INFO("Please, disconnect and power off the mount");
1008  }
1009 
1010  lastAZ = currentAz;
1011  lastAL = currentAlt;
1012  }
1013 
1015 
1016  syncSideOfPier();
1017 
1018  return true;
1019 }
1020 
1021 #ifdef no
1022 // 2020-06-02, wildi, the init is done in UnPArk()
1023 // experimental function needs testing!!!
1024 bool LX200AstroPhysics::IsMountInitialized(bool *initialized)
1025 {
1026  LOG_DEBUG("IsMountInitialized entry");
1027  if(*initialized)
1028  {
1029  LOG_DEBUG("IsMountInitialized I am initialized");
1030  return true;
1031  }
1032  double ra, dec;
1033  bool raZE, deZE, de90;
1034 
1035  double epscheck = 1e-5; // two doubles this close are considered equal
1036 
1037  if (isSimulation())
1038  {
1040  dec = LocationN[LOCATION_LATITUDE].value > 0 ? 90 : -90;
1041  }
1042  else if (getLX200RA(PortFD, &ra) || getLX200DEC(PortFD, &dec))
1043  {
1044  return false;
1045  }
1046  LOGF_DEBUG("IsMountInitialized: RA: %f - DEC: %f", ra, dec);
1047 
1048  raZE = (fabs(ra) < epscheck);
1049  deZE = (fabs(dec) < epscheck);
1050  de90 = (fabs(dec - 90) < epscheck);
1051 
1052  LOGF_DEBUG("IsMountInitialized: raZE: %s - deZE: %s - de90: %s, (raZE && deZE): %s, (raZE && de90): %s",
1053  raZE ? "true" : "false", deZE ? "true" : "false", de90 ? "true" : "false", (raZE && deZE) ? "true" : "false", (raZE
1054  && de90) ? "true" : "false");
1055 
1056  // RA is zero and DEC is zero or 90
1057  // then mount is not initialized and we need to initialized it.
1058  //if ( (raZE && deZE) || (raZE && de90))
1059  if ( (raZE || deZE || de90))
1060  {
1061  LOG_WARN("IsMountInitialized: Mount is not yet initialized.");
1062  *initialized = false;
1063  return true;
1064  }
1065 
1066  // mount is initialized
1067  LOG_INFO("IsMountInitialized: Mount is initialized.");
1068  *initialized = true;
1069 #ifdef no
1070  LOGF_DEBUG("IsMountInitialized: Mount is initialized, loc: %s time: %s mount: %s", locationUpdated ? "true" : "false",
1071  timeUpdated ? "true" : "false", mountInitialized ? "true" : "false");
1072 #endif
1073 
1074  return true;
1075 }
1076 #endif
1077 // experimental function needs testing!!!
1078 bool LX200AstroPhysics::IsMountParked(bool *isAPParked)
1079 {
1080  // 2020-06-02, wildi, ToDo unify for GTOCPX
1081  if (isSimulation())
1082  {
1083  // 2030-05-30, if Unparked is selected, this condition is not met
1084  *isAPParked = (ParkS[0].s == ISS_ON);
1085  return true;
1086  }
1087  // check for newer
1088  if ((firmwareVersion != MCV_UNKNOWN) && (firmwareVersion >= MCV_T))
1089  {
1090  // try one method
1091  return getMountStatus(isAPParked);
1092  }
1093  const struct timespec timeout = {0, 250000000L};
1094  double ra1, ra2;
1095  // fallback for older controllers
1096  if (getLX200RA(PortFD, &ra1))
1097  return false;
1098 
1099  // wait 250ms
1100  nanosleep(&timeout, nullptr);
1101 
1102  if (getLX200RA(PortFD, &ra2))
1103  return false;
1104 
1105  // if within an arcsec then assume RA is constant
1106  if (fabs(ra1 - ra2) < (1.0 / (15.0 * 3600.0)))
1107  {
1108  *isAPParked = false;
1109  return true;
1110  }
1111  else
1112  {
1113  *isAPParked = true;
1114  return true;
1115  }
1116 
1117  // can't determine
1118  LOG_ERROR("IsMountParked: park status undefined");
1119  return false;
1120 }
1121 
1122 bool LX200AstroPhysics::getMountStatus(bool *isAPParked)
1123 {
1124  char parkStatus;
1125  char slewStatus;
1126 
1127  if (check_lx200ap_status(PortFD, &parkStatus, &slewStatus) == 0)
1128  {
1129  LOGF_DEBUG("parkStatus: %c", parkStatus);
1130 
1131  *isAPParked = (parkStatus == 'P');
1132  return true;
1133  }
1134 
1135  return false;
1136 }
1137 
1138 bool LX200AstroPhysics::Goto(double r, double d)
1139 {
1140  const struct timespec timeout = {0, 100000000L};
1141 
1142  targetRA = r;
1143  targetDEC = d;
1144 
1145  char RAStr[64], DecStr[64];
1146  fs_sexa(RAStr, targetRA, 2, 3600);
1147  fs_sexa(DecStr, targetDEC, 2, 3600);
1148 
1149  // If moving, let's stop it first.
1150  if (EqNP.s == IPS_BUSY)
1151  {
1152  if (!isSimulation() && abortSlew(PortFD) < 0)
1153  {
1154  AbortSP.s = IPS_ALERT;
1155  IDSetSwitch(&AbortSP, "Abort slew failed.");
1156  return false;
1157  }
1158 
1159  AbortSP.s = IPS_OK;
1160  EqNP.s = IPS_IDLE;
1161  IDSetSwitch(&AbortSP, "Slew aborted.");
1162  IDSetNumber(&EqNP, nullptr);
1163 
1165  {
1168  EqNP.s = IPS_IDLE;
1171  IDSetSwitch(&MovementNSSP, nullptr);
1172  IDSetSwitch(&MovementWESP, nullptr);
1173  }
1174 
1175  // sleep for 100 mseconds
1176  nanosleep(&timeout, nullptr);
1177  }
1178 
1179  if (!isSimulation())
1180  {
1182  {
1183  EqNP.s = IPS_ALERT;
1184  IDSetNumber(&EqNP, "Error setting RA/DEC.");
1185  return false;
1186  }
1187 
1188  int err = 0;
1189 
1190  /* Slew reads the '0', that is not the end of the slew */
1191  if ((err = Slew(PortFD)))
1192  {
1193  EqNP.s = IPS_ALERT;
1194  IDSetNumber(&EqNP, "Error Slewing to JNow RA %s - DEC %s\n", RAStr, DecStr);
1195  slewError(err);
1196  return false;
1197  }
1198 #ifdef no
1199  motionCommanded = true;
1200 #endif
1201  lastRA = targetRA;
1202  lastDE = targetDEC;
1203  }
1204 
1206  //EqNP.s = IPS_BUSY;
1207 
1208  LOGF_INFO("Slewing to RA: %s - DEC: %s", RAStr, DecStr);
1209  return true;
1210 }
1211 
1212 
1214 {
1215  // if (IUFindOnSwitchIndex(&SlewRateSP) == index)
1216  // {
1217  // LOGF_DEBUG("updateAPSlewRate: slew rate %d already chosen so ignoring.", index);
1218  // return true;
1219  // }
1220 
1221  if (!isSimulation() && selectAPCenterRate(PortFD, index) < 0)
1222  {
1224  IDSetSwitch(&SlewRateSP, "Error setting slew mode.");
1225  return false;
1226  }
1227 
1229  SlewRateS[index].s = ISS_ON;
1230  SlewRateSP.s = IPS_OK;
1231  IDSetSwitch(&SlewRateSP, nullptr);
1232  return true;
1233 }
1234 
1235 
1236 // AP mounts handle guide commands differently enough from the "generic" LX200 we need to override some
1237 // functions related to the GuiderInterface
1238 
1240 {
1241  // If we're using pulse command, then MovementXXX should NOT be active at all.
1243  {
1244  LOG_ERROR("Cannot pulse guide while manually in motion. Stop first.");
1245  return IPS_ALERT;
1246  }
1247 
1248  if (GuideNSTID)
1249  {
1251  GuideNSTID = 0;
1252  }
1253 
1255  {
1256  LOGF_DEBUG("GuideNorth using SendPulseCmd() for duration %d", ms);
1258  GuideNSTID = IEAddTimer(static_cast<int>(ms), pulseGuideTimeoutHelperNS, this);
1259  }
1260  else
1261  {
1262  LOGF_DEBUG("GuideNorth using simulated pulse for duration %d", ms);
1263 
1264  if (rememberSlewRate == -1)
1265  rememberSlewRate = IUFindOnSwitchIndex(&SlewRateSP);
1266 
1267  // Set slew to guiding
1269 
1270  // Set to dummy value to that MoveNS does not reset slew rate to rememberSlewRate
1271  GuideNSTID = 1;
1272 
1273  ISState states[] = { ISS_ON, ISS_OFF };
1274  const char *names[] = { MovementNSS[DIRECTION_NORTH].name, MovementNSS[DIRECTION_SOUTH].name};
1275  ISNewSwitch(MovementNSSP.device, MovementNSSP.name, states, const_cast<char **>(names), 2);
1276  GuideNSTID = IEAddTimer(static_cast<int>(ms), simulGuideTimeoutHelperNS, this);
1277  }
1278 
1279  return IPS_BUSY;
1280 }
1281 
1283 {
1284  // If we're using pulse command, then MovementXXX should NOT be active at all.
1286  {
1287  LOG_ERROR("Cannot pulse guide while manually in motion. Stop first.");
1288  return IPS_ALERT;
1289  }
1290 
1291  if (GuideNSTID)
1292  {
1294  GuideNSTID = 0;
1295  }
1296 
1298  {
1300  GuideNSTID = IEAddTimer(static_cast<int>(ms), pulseGuideTimeoutHelperNS, this);
1301  }
1302  else
1303  {
1304  LOGF_DEBUG("GuideSouth using simulated pulse for duration %d", ms);
1305 
1306  if (rememberSlewRate == -1)
1307  rememberSlewRate = IUFindOnSwitchIndex(&SlewRateSP);
1308 
1309  // Set slew to guiding
1311 
1312  // Set to dummy value to that MoveNS does not reset slew rate to rememberSlewRate
1313  GuideNSTID = 1;
1314 
1315  ISState states[] = { ISS_OFF, ISS_ON };
1316  const char *names[] = { MovementNSS[DIRECTION_NORTH].name, MovementNSS[DIRECTION_SOUTH].name};
1317  ISNewSwitch(MovementNSSP.device, MovementNSSP.name, states, const_cast<char **>(names), 2);
1318  GuideNSTID = IEAddTimer(static_cast<int>(ms), simulGuideTimeoutHelperNS, this);
1319  }
1320 
1321  return IPS_BUSY;
1322 }
1323 
1325 {
1326  // If we're using pulse command, then MovementXXX should NOT be active at all.
1328  {
1329  LOG_ERROR("Cannot pulse guide while manually in motion. Stop first.");
1330  return IPS_ALERT;
1331  }
1332 
1333  if (GuideWETID)
1334  {
1336  GuideWETID = 0;
1337  }
1338 
1340  {
1341  SendPulseCmd(LX200_EAST, ms);
1342  GuideWETID = IEAddTimer(static_cast<int>(ms), pulseGuideTimeoutHelperWE, this);
1343  }
1344  else
1345  {
1346  LOGF_DEBUG("GuideEast using simulated pulse for duration %d", ms);
1347 
1348  if (rememberSlewRate == -1)
1349  rememberSlewRate = IUFindOnSwitchIndex(&SlewRateSP);
1350 
1351  // Set slew to guiding
1353 
1354  // Set to dummy value to that MoveWE does not reset slew rate to rememberSlewRate
1355  GuideWETID = 1;
1356 
1357  ISState states[] = { ISS_OFF, ISS_ON };
1358  const char *names[] = { MovementWES[DIRECTION_WEST].name, MovementWES[DIRECTION_EAST].name};
1359  ISNewSwitch(MovementWESP.device, MovementWESP.name, states, const_cast<char **>(names), 2);
1360  GuideWETID = IEAddTimer(static_cast<int>(ms), simulGuideTimeoutHelperWE, this);
1361  }
1362 
1363  return IPS_BUSY;
1364 }
1365 
1367 {
1368  // If we're using pulse command, then MovementXXX should NOT be active at all.
1370  {
1371  LOG_ERROR("Cannot pulse guide while manually in motion. Stop first.");
1372  return IPS_ALERT;
1373  }
1374 
1375  if (GuideWETID)
1376  {
1378  GuideWETID = 0;
1379  }
1380 
1382  {
1383  SendPulseCmd(LX200_WEST, ms);
1384  GuideWETID = IEAddTimer(static_cast<int>(ms), pulseGuideTimeoutHelperWE, this);
1385  }
1386  else
1387  {
1388  LOGF_DEBUG("GuideWest using simulated pulse for duration %d", ms);
1389 
1390  if (rememberSlewRate == -1)
1391  rememberSlewRate = IUFindOnSwitchIndex(&SlewRateSP);
1392 
1393  // Set slew to guiding
1395 
1396  // Set to dummy value to that MoveWE does not reset slew rate to rememberSlewRate
1397  GuideWETID = 1;
1398 
1399  ISState states[] = { ISS_ON, ISS_OFF };
1400  const char *names[] = { MovementWES[DIRECTION_WEST].name, MovementWES[DIRECTION_EAST].name};
1401  ISNewSwitch(MovementWESP.device, MovementWESP.name, states, const_cast<char **>(names), 2);
1402  GuideWETID = IEAddTimer(static_cast<int>(ms), simulGuideTimeoutHelperWE, this);
1403  }
1404 
1405  return IPS_BUSY;
1406 }
1407 
1409 {
1410  static_cast<LX200AstroPhysics *>(p)->AstroPhysicsGuideTimeoutNS(false);
1411 }
1412 
1414 {
1415  static_cast<LX200AstroPhysics *>(p)->AstroPhysicsGuideTimeoutWE(false);
1416 }
1417 
1419 {
1420  static_cast<LX200AstroPhysics *>(p)->AstroPhysicsGuideTimeoutNS(true);
1421 }
1422 
1424 {
1425  static_cast<LX200AstroPhysics *>(p)->AstroPhysicsGuideTimeoutWE(true);
1426 }
1427 
1429 {
1430  LOGF_DEBUG("AstroPhysicsGuideTimeoutWE() pulse guide simul = %d", simul);
1431 
1432  if (simul == true)
1433  {
1434  ISState states[] = { ISS_OFF, ISS_OFF };
1435  const char *names[] = { MovementWES[DIRECTION_WEST].name, MovementWES[DIRECTION_EAST].name};
1436  ISNewSwitch(MovementWESP.device, MovementWESP.name, states, const_cast<char **>(names), 2);
1437  }
1438 
1439  GuideWENP.np[DIRECTION_WEST].value = 0;
1440  GuideWENP.np[DIRECTION_EAST].value = 0;
1441  GuideWENP.s = IPS_IDLE;
1442  GuideWETID = 0;
1443  IDSetNumber(&GuideWENP, nullptr);
1444 }
1445 
1447 {
1448  LOGF_DEBUG("AstroPhysicsGuideTimeoutNS() pulse guide simul = %d", simul);
1449 
1450  if (simul == true)
1451  {
1452  ISState states[] = { ISS_OFF, ISS_OFF };
1453  const char *names[] = { MovementNSS[DIRECTION_NORTH].name, MovementNSS[DIRECTION_SOUTH].name};
1454  ISNewSwitch(MovementNSSP.device, MovementNSSP.name, states, const_cast<char **>(names), 2);
1455  }
1456 
1457  GuideNSNP.np[0].value = 0;
1458  GuideNSNP.np[1].value = 0;
1459  GuideNSNP.s = IPS_IDLE;
1460  GuideNSTID = 0;
1461  IDSetNumber(&GuideNSNP, nullptr);
1462 }
1463 
1464 int LX200AstroPhysics::SendPulseCmd(int8_t direction, uint32_t duration_msec)
1465 {
1466  return APSendPulseCmd(PortFD, direction, duration_msec);
1467 }
1468 
1470 {
1471  if (isSimulation())
1472  {
1473  LOG_INFO("Simulated Astrophysics is online. Retrieving basic data...");
1474  getFirmwareVersion();
1475  return true;
1476  }
1477 
1478  int err = 0;
1479 
1480  if ((err = setAPClearBuffer(PortFD)) < 0)
1481  {
1482  LOGF_ERROR("Error clearing the buffer (%d): %s", err, strerror(err));
1483  return false;
1484  }
1485 
1486  if (setAPBackLashCompensation(PortFD, 0, 0, 0) < 0)
1487  {
1488  // It seems we need to send it twice before it works!
1489  if ((err = setAPBackLashCompensation(PortFD, 0, 0, 0)) < 0)
1490  {
1491  LOGF_ERROR("Error setting back lash compensation (%d): %s.", err, strerror(err));
1492  // wildi, downgrade
1493  //return false;
1494  }
1495  }
1496 
1497  // get firmware version
1498  bool rc = false;
1499 
1500  rc = getFirmwareVersion();
1501 #ifdef no
1502  // see if firmware is 'V' or not
1503  if (!rc || firmwareVersion == MCV_UNKNOWN || firmwareVersion < MCV_V)
1504  {
1505  LOG_ERROR("Firmware version is not 'V' - too old to use the experimental driver!");
1506  // wildi, downgrade
1507  //return false;
1508  }
1509  else
1510  {
1511  LOG_INFO("Firmware level 'V' detected - driver loaded.");
1512  }
1513 #endif
1514  if(!rc || firmwareVersion == MCV_UNKNOWN)
1515  {
1516  LOG_ERROR("Firmware detection failed or is unknown");
1517  return false;
1518 
1519  }
1520  if(firmwareVersion == MCV_V)
1521  {
1522  LOG_INFO("Firmware level 'V' detected - driver loaded.");
1523  }
1524  else if(firmwareVersion == MCV_D)
1525  {
1526  LOG_INFO("Firmware level 'D' detected - driver loaded.");
1527  }
1528  // do not track until mount is umparked
1529  if ((err = selectAPTrackingMode(PortFD, AP_TRACKING_OFF)) < 0)
1530  {
1531  LOGF_ERROR("Handshake: Error setting tracking mode to zero (%d).", err);
1532  return false;
1533 
1534  }
1535  else
1536  {
1537  LOG_INFO("Stopped tracking");
1538  }
1539 
1540  // Detect and set fomat. It should be LONG.
1541  return (checkLX200EquatorialFormat(PortFD) == 0);
1542 }
1543 
1545 {
1546 #ifdef no
1547  timeUpdated = false;
1548  locationUpdated = false;
1549  mountInitialized = false;
1550 #endif
1552  StartUpS[0].s = ISS_OFF;
1553  StartUpSP.s = IPS_IDLE;
1554  IDSetSwitch(&StartUpSP, nullptr);
1555 
1556  return LX200Generic::Disconnect();
1557 }
1558 bool LX200AstroPhysics::Sync(double ra, double dec)
1559 {
1560  char syncString[256] = ""; // simulation needs UTF-8
1561 
1562  int syncType = IUFindOnSwitchIndex(&SyncCMRSP);
1563  if (!isSimulation())
1564  {
1565  if (setAPObjectRA(PortFD, ra) < 0 || setAPObjectDEC(PortFD, dec) < 0)
1566  {
1567  EqNP.s = IPS_ALERT;
1568  IDSetNumber(&EqNP, "Error setting RA/DEC. Unable to Sync.");
1569  return false;
1570  }
1571  bool syncOK = true;
1572 
1573  switch (syncType)
1574  {
1575  case USE_REGULAR_SYNC:
1576 
1577  if (::Sync(PortFD, syncString) < 0)
1578  syncOK = false;
1579  break;
1580 
1581  case USE_CMR_SYNC:
1582  if (APSyncCMR(PortFD, syncString) < 0)
1583  syncOK = false;
1584  break;
1585 
1586  default:
1587  break;
1588  }
1589 
1590  if (!syncOK)
1591  {
1592  EqNP.s = IPS_ALERT;
1593  IDSetNumber(&EqNP, "Synchronization failed.");
1594  return false;
1595  }
1596 
1597  }
1598 
1599  currentRA = ra;
1600  currentDEC = dec;
1601  LOGF_DEBUG("%s Synchronization successful %s", (syncType == USE_REGULAR_SYNC ? "CM" : "CMR"), syncString);
1602 
1603  EqNP.s = IPS_OK;
1604 
1606 
1607  return true;
1608 }
1609 
1610 bool LX200AstroPhysics::updateTime(ln_date *utc, double utc_offset)
1611 {
1612 
1613  LOG_DEBUG("LX200AstroPhysics::updateTime entry");
1614  // 2020-06-02, wildi, ToDo, time obtained from KStars differs up to a couple
1615  // of 5 seconds from system time.
1616  struct ln_zonedate ltm;
1617 
1618  ln_date_to_zonedate(utc, &ltm, utc_offset * 3600.0);
1619 
1620  JD = ln_get_julian_day(utc);
1621 
1622  LOGF_DEBUG("New JD is %f, local time: %d, %d, %d, utc offset: %f", JD, ltm.hours, ltm.minutes, (int)ltm.seconds,
1623  utc_offset);
1624 
1625  // Set Local Time
1626  if (isSimulation() == false && setLocalTime(PortFD, ltm.hours, ltm.minutes, (int)ltm.seconds) < 0)
1627  {
1628  LOG_ERROR("Error setting local time.");
1629  return false;
1630  }
1631 
1632  LOGF_DEBUG("Set Local Time %02d:%02d:%02d is successful.", ltm.hours, ltm.minutes,
1633  (int)ltm.seconds);
1634 
1635  if (isSimulation() == false && setCalenderDate(PortFD, ltm.days, ltm.months, ltm.years) < 0)
1636  {
1637  LOG_ERROR("Error setting local date.");
1638  return false;
1639  }
1640 
1641  LOGF_DEBUG("Set Local Date %02d/%02d/%02d is successful.", ltm.days, ltm.months, ltm.years);
1642 
1643  // 2020-05-30, wildi, after a very long journey
1644  // AP: TZ (0,12): West, East (-12.,-0), (>12,24)
1645  // Peru, Lima:
1646  //(TX=':Gg#'), RX='+77*01:42#
1647  //(TX=':SG05:00:00#'), RX='1'
1648  // Linux/Windows TZ values: West: -12,0, East 0,12
1649  // AP GTOCPX accepts a converted float including 24.
1650  double ap_utc_offset = - utc_offset;
1651  if (!isSimulation() && setAPUTCOffset(PortFD, ap_utc_offset) < 0)
1652  {
1653  LOG_ERROR("Error setting UTC Offset.");
1654  return false;
1655  }
1656  APUTCOffsetN[0].value = ap_utc_offset ;
1658  IDSetNumber(&APUTCOffsetNP, nullptr);
1659 
1660  LOGF_DEBUG("Set UTC Offset %g as AP UTC Offset %g is successful.", utc_offset, ap_utc_offset);
1661 
1662  LOG_DEBUG("Time updated.");
1663 #ifdef no
1664  // 2020-06-02, wildi, done in UnPark()
1665  timeUpdated = true;
1666  // 2020-05-22, wildi, do it once and at last
1667  if (locationUpdated && timeUpdated && !mountInitialized)
1668  initMount();
1669 #endif
1670  return true;
1671 }
1672 
1673 bool LX200AstroPhysics::updateLocation(double latitude, double longitude, double elevation)
1674 {
1675  INDI_UNUSED(elevation);
1676 
1677  LOG_DEBUG("LX200AstroPhysics::updateLocation entry");
1678 
1679  if ((latitude == 0.) && (longitude == 0.))
1680  {
1681  LOG_DEBUG("updateLocation: latitude, longitude both zero");
1682  return false;
1683  }
1684 
1685  if (!isSimulation() && setAPSiteLongitude(PortFD, 360.0 - longitude) < 0)
1686  {
1687  LOG_ERROR("Error setting site longitude coordinates");
1688  return false;
1689  }
1690 
1691  if (!isSimulation() && setAPSiteLatitude(PortFD, latitude) < 0)
1692  {
1693  LOG_ERROR("Error setting site latitude coordinates");
1694  return false;
1695  }
1696 
1697  char l[32], L[32];
1698  fs_sexa(l, latitude, 3, 3600);
1699  fs_sexa(L, longitude, 4, 3600);
1700 
1701  LOGF_DEBUG("Site location updated to Lat %.32s - Long %.32s, deg: %f, %f", l, L, latitude, longitude);
1702 #ifdef no
1703  // 2020-06-02, wildi, done in UnPark()
1704  locationUpdated = true;
1705 
1706  if (locationUpdated && timeUpdated && !mountInitialized)
1707  {
1708  if (!initMount())
1709  {
1710  return false;
1711  }
1712  }
1713 #endif
1714  return true;
1715 }
1716 
1718 {
1720 
1721  // we use routines from legacy AP driver routines and newer experimental driver routines
1723 
1724 }
1725 
1726 // For most mounts the SetSlewRate() method sets both the MoveTo and Slew (GOTO) speeds.
1727 // For AP mounts these two speeds are handled separately - so SetSlewRate() actually sets the MoveTo speed for AP mounts - confusing!
1728 // ApSetSlew
1730 {
1731  if (!isSimulation() && selectAPCenterRate(PortFD, index) < 0)
1732  {
1733  LOG_ERROR("Error setting slew mode.");
1734  return false;
1735  }
1736 
1737  return true;
1738 }
1739 
1741 {
1742  LOG_DEBUG("Park entry");
1743 
1744  ParkPosition parkPos = static_cast<ParkPosition>(IUFindOnSwitchIndex(&ParkToSP));
1745  double parkAz {90}, parkAlt {0};
1746  if (calcParkPosition(parkPos, &parkAlt, &parkAz))
1747  {
1748  SetAxis1Park(parkAz);
1749  SetAxis2Park(parkAlt);
1750  LOGF_DEBUG("Set park position %d to az=%f alt=%f", parkPos, parkAz, parkAlt);
1751  }
1752  else
1753  {
1754  LOGF_ERROR("Unable to set park position %d!!", parkPos);
1755  }
1756 
1757  char AzStr[16] = {0}, AltStr[16] = {0};
1758  fs_sexa(AzStr, parkAz, 2, 3600);
1759  fs_sexa(AltStr, parkAlt, 2, 3600);
1760  LOGF_INFO("Parking to Az (%s) Alt (%s)...", AzStr, AltStr);
1761 
1762  INDI::IEquatorialCoordinates equatorialCoords {0, 0};
1763  INDI::IHorizontalCoordinates horizontalCoords {parkAz, parkAlt};
1764  INDI::HorizontalToEquatorial(&horizontalCoords, &m_Location, ln_get_julian_from_sys(), &equatorialCoords);
1766  double ha = get_local_hour_angle(lst, equatorialCoords.rightascension);
1767 
1769  HourangleCoordsN[0].value = ha;
1770  HourangleCoordsN[1].value = equatorialCoords.declination;
1771  IDSetNumber(&HourangleCoordsNP, nullptr);
1772 
1773  if (isSimulation())
1774  {
1775  Goto(equatorialCoords.rightascension, equatorialCoords.declination);
1776  }
1777  else
1778  {
1779  if (setAPObjectAZ(PortFD, parkAz) < 0 || setAPObjectAlt(PortFD, parkAlt) < 0)
1780  {
1781  LOG_ERROR("Error setting Az/Alt.");
1782  return false;
1783  }
1784 
1785  int err = 0;
1786 
1787  /* Slew reads the '0', that is not the end of the slew */
1788  if ((err = Slew(PortFD)))
1789  {
1790  LOGF_ERROR("Error Slewing to Az %s - Alt %s", AzStr, AltStr);
1791  slewError(err);
1792  return false;
1793  }
1794 #ifdef no
1795  motionCommanded = true;
1796 #endif
1797  lastAZ = parkAz;
1798  lastAL = parkAlt;
1799  }
1800 
1801  EqNP.s = IPS_BUSY;
1803  LOG_INFO("Parking is in progress...");
1804 
1805  return true;
1806 }
1807 
1808 bool LX200AstroPhysics::calcParkPosition(ParkPosition pos, double *parkAlt, double *parkAz)
1809 {
1810  switch (pos)
1811  {
1812  // last unparked
1813  case PARK_CUSTOM:
1814  LOG_ERROR("Called calcParkPosition with PARK_CUSTOM!");
1815  return false;
1816  break;
1817 
1818  // Park 1
1819  // Northern Hemisphere should be pointing at ALT=0 AZ=0 with scope on WEST side of pier
1820  // Southern Hemisphere should be pointing at ALT=0 AZ=180 with scope on WEST side of pier
1821  case 1:
1822  LOG_INFO("Computing PARK1 position...");
1823  *parkAlt = 0;
1824  *parkAz = LocationN[LOCATION_LATITUDE].value > 0 ? 359.1 : 180.1;
1825  break;
1826 
1827  // Park 2
1828  // Northern Hemisphere should be pointing at ALT=0 AZ=90 with scope pointing EAST
1829  // Southern Hemisphere should be pointing at ALT=0 AZ=90 with scope pointing EAST
1830  case 2:
1831  LOG_INFO("Computing PARK2 position...");
1832  *parkAlt = 0;
1833  *parkAz = 90;
1834  break;
1835 
1836  // Park 3
1837  // Northern Hemisphere should be pointing at ALT=LAT AZ=0 with scope pointing NORTH with CW down
1838  // Southern Hemisphere should be pointing at ALT=LAT AZ=180 with scope pointing SOUTH with CW down
1839  // wildi: the hour angle is undefined if AZ = 0,180 and ALT=LAT is chosen, adding .1 to Az sets PARK3
1840  // as close as possible to to HA = -6 hours (CW down), valid for both hemispheres.
1841  case 3:
1842  *parkAlt = fabs(LocationN[LOCATION_LATITUDE].value);
1843  *parkAz = LocationN[LOCATION_LATITUDE].value > 0 ? 0.1 : 179.9;
1844  LOG_INFO("Computing PARK3 position");
1845  break;
1846 
1847  // Park 4
1848  // Northern Hemisphere should be pointing at ALT=0 AZ=180 with scope on EAST side of pier
1849  // Southern Hemisphere should be pointing at ALT=0 AZ=0 with scope on EAST side of pier
1850  case 4:
1851  LOG_INFO("Computing PARK4 position...");
1852  *parkAlt = 0;
1853  *parkAz = LocationN[LOCATION_LATITUDE].value > 0 ? 180.1 : 359.1;
1854  break;
1855 
1856  default:
1857  LOG_ERROR("Unknown park position!");
1858  return false;
1859  }
1860 
1861  LOGF_DEBUG("calcParkPosition: parkPos=%d parkAlt=%f parkAz=%f", pos, *parkAlt, *parkAz);
1862 
1863  return true;
1864 
1865 }
1866 
1868 {
1869  bool parkDataValid = InitPark();
1870  bool parkDataValid_and_parked = (parkDataValid && isParked());
1871  bool unpark_from_last_config = false;
1872 
1873  unpark_from_last_config = (PARK_LAST == IUFindOnSwitchIndex(&UnparkFromSP));
1874  double unparkAlt, unparkAz;
1875  if (unpark_from_last_config)
1876  {
1877  if (parkDataValid_and_parked)
1878  {
1879  LOG_INFO("UnPark: mount is parked, has valid park data and driver config is set to Last Parked");
1880  unparkAz = GetAxis1Park(); //Az
1881  unparkAlt = GetAxis2Park(); //Alt
1882  }
1883  else
1884  {
1885  LOG_ERROR("UnPark: Missing unpark position!");
1887  IDSetSwitch(&UnparkFromSP, nullptr);
1888  return false;
1889  }
1890  }
1891  else
1892  {
1893  ParkPosition unparkfromPos = static_cast<ParkPosition>(IUFindOnSwitchIndex(&UnparkFromSP));
1894  LOGF_DEBUG("UnPark: park position = %d from current driver", unparkfromPos);
1895  if (!calcParkPosition(unparkfromPos, &unparkAlt, &unparkAz))
1896  {
1897  LOG_ERROR("UnPark: Error calculating unpark position!");
1899  IDSetSwitch(&UnparkFromSP, nullptr);
1900  return false;
1901  }
1902  LOGF_DEBUG("UnPark: parkPos=%d parkAlt=%f parkAz=%f", unparkfromPos, unparkAlt, unparkAz);
1903  }
1904  SetAxis1ParkDefault(unparkAz);
1905  SetAxis2ParkDefault(unparkAlt);
1906  if(!parkDataValid)
1907  {
1908  SetAxis1ParkDefault(unparkAz);
1909  SetAxis2ParkDefault(unparkAlt);
1910  }
1911 
1912  bool isAPParked = true;
1913  if(!isSimulation())
1914  {
1915  if (firmwareVersion == MCV_D)
1916  {
1917  // no :GOS command
1918  // 2020-06-27, wildi, revision D slews without :PO after power cycle
1919  isAPParked = true ;
1920  }
1921  else
1922  {
1923  if (!IsMountParked(&isAPParked))
1924  {
1925  LOG_WARN("UnPark:could not determine AP park status");
1927  IDSetSwitch(&UnparkFromSP, nullptr);
1928  return false;
1929  }
1930  }
1931  if(!isAPParked)
1932  {
1933  LOG_WARN("UnPark: AP mount status: unparked, park first");
1935  IDSetSwitch(&UnparkFromSP, nullptr);
1936  return false;
1937  }
1938  // The AP :PO# should only be used during initilization and not here as indicated by email from Preston on 2017-12-12
1939  // 2020-05-27, wildi, ToDo taking care of above comment later
1940  if (APUnParkMount(PortFD) < 0)
1941  {
1943  ParkS[0].s = ISS_ON;
1944  ParkSP.s = IPS_ALERT;
1945  IDSetSwitch(&ParkSP, nullptr);
1946  LOG_ERROR("UnParking AP mount failed.");
1947  return false;
1948  }
1949 
1950  SetParked(false);
1951  // Stop :Q#
1952  if ( abortSlew(PortFD) < 0)
1953  {
1955  ParkS[0].s = ISS_ON;
1956  ParkSP.s = IPS_ALERT;
1957  IDSetSwitch(&ParkSP, nullptr);
1958  LOG_WARN("Abort motion Failed");
1959  return false;
1960  }
1961  // NO: Enable tracking
1962  //2020-03-17, wildi, was SetTrackEnabled(true);
1963  SetTrackEnabled(false);
1965  }
1966  else
1967  {
1968  SetParked(false);
1969  SetTrackEnabled(false);
1971  }
1972 
1973 
1974  INDI::IEquatorialCoordinates equatorialCoords {0, 0};
1975  INDI::IHorizontalCoordinates horizontalCoords {unparkAz, unparkAlt};
1976  INDI::HorizontalToEquatorial(&horizontalCoords, &m_Location, ln_get_julian_from_sys(), &equatorialCoords);
1977 
1978  char AzStr[16], AltStr[16];
1979  fs_sexa(AzStr, unparkAz, 2, 3600);
1980  fs_sexa(AltStr, unparkAlt, 2, 3600);
1981  char RaStr[16], DecStr[16];
1982  fs_sexa(RaStr, equatorialCoords.rightascension, 2, 3600);
1983  fs_sexa(DecStr, equatorialCoords.declination, 2, 3600);
1984 
1986  double ha = get_local_hour_angle(lst, equatorialCoords.rightascension);
1987  char HaStr[16];
1988  fs_sexa(HaStr, ha, 2, 3600);
1989  LOGF_INFO("UnPark: Current parking position Az (%s) Alt (%s), HA (%s) RA (%s) Dec (%s)", AzStr, AltStr, HaStr,
1990  RaStr, DecStr);
1991 
1993  HourangleCoordsN[0].value = ha;
1994  HourangleCoordsN[1].value = equatorialCoords.declination;
1995  IDSetNumber(&HourangleCoordsNP, nullptr);
1996 
1997  bool success = Sync(equatorialCoords.rightascension, equatorialCoords.declination);
1998  if(!success)
1999  {
2000  LOG_WARN("Could not sync mount");
2001  return false;
2002  }
2003 #ifdef no
2004  if (isSimulation())
2005  {
2006  // does not sync being in simulation
2007  Sync(equatorialPos.rightascension / 15.0, equatorialPos.dec);
2008  }
2009  else
2010  {
2011  // 2020-03-17, wildi, why not sync in RA/dec?
2012  if ((setAPObjectAZ(PortFD, unparkAz) < 0 || (setAPObjectAlt(PortFD, unparkAlt)) < 0))
2013  {
2014  LOG_ERROR("Error setting Az/Alt.");
2015  return false;
2016  }
2017 
2018  char syncString[256];
2019  if (APSyncCM(PortFD, syncString) < 0)
2020  {
2021  LOG_WARN("Sync failed.");
2022  return false;
2023  }
2024  }
2025 #endif
2026 
2028  {
2031  EqNP.s = IPS_IDLE;
2034  IDSetSwitch(&MovementNSSP, nullptr);
2035  IDSetSwitch(&MovementWESP, nullptr);
2036  }
2037  //
2038 #ifdef no
2039  timeUpdated = false;
2040  locationUpdated = false;
2041  //mountInitialized=false;
2042  if (!IsMountParked(&mountParked))
2043  {
2044  return false;
2045  }
2046 #endif
2047  UnparkFromSP.s = IPS_OK;
2048  IDSetSwitch(&UnparkFromSP, nullptr);
2049  // SlewRateS is used as the MoveTo speed
2050  int err;
2052  {
2053  LOGF_ERROR("Error setting center (MoveTo) rate (%d).", err);
2054  return false;
2055  }
2056 
2057  SlewRateSP.s = IPS_OK;
2058  IDSetSwitch(&SlewRateSP, nullptr);
2059 
2060  // APSlewSpeedsS defines the Slew (GOTO) speeds valid on the AP mounts
2062  {
2063  LOGF_ERROR("Error setting slew to rate (%d).", err);
2064  return false;
2065  }
2066 
2068  IDSetSwitch(&APSlewSpeedSP, nullptr);
2069 
2070  LOG_DEBUG("UnPark: Mount unparked successfully");
2071  return true;
2072 }
2073 
2075 {
2077  INDI::IHorizontalCoordinates horizontalCoords {0, 0};
2078  INDI::EquatorialToHorizontal(&equatorialCoords, &m_Location, ln_get_julian_from_sys(), &horizontalCoords);
2079 
2080  double parkAZ = horizontalCoords.azimuth;
2081  double parkAlt = horizontalCoords.altitude;
2082 
2083  char AzStr[16], AltStr[16];
2084  fs_sexa(AzStr, parkAZ, 2, 3600);
2085  fs_sexa(AltStr, parkAlt, 2, 3600);
2086 
2087  LOGF_DEBUG("SetCurrentPark: Setting current parking position to coordinates Az (%s) Alt (%s)", AzStr, AltStr);
2088 
2089  SetAxis1Park(parkAZ);
2090  SetAxis2Park(parkAlt);
2091 
2092  return true;
2093 }
2094 
2096 {
2097  // Az = 0 for North hemisphere, Az = 180 for South
2098  SetAxis1Park(LocationN[LOCATION_LATITUDE].value > 0.1 ? 0 : 179.1);
2099 
2100  // Alt = Latitude
2102 
2103  return true;
2104 }
2105 
2106 void LX200AstroPhysics::syncSideOfPier()
2107 {
2108  const char *cmd = ":pS#";
2109  // Response
2110  char response[16] = { 0 };
2111  int rc = 0, nbytes_read = 0, nbytes_written = 0;
2112 
2113  LOGF_DEBUG("CMD: <%s>", cmd);
2114 
2115  tcflush(PortFD, TCIOFLUSH);
2116 
2117  if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
2118  {
2119  char errmsg[256];
2120  tty_error_msg(rc, errmsg, 256);
2121  LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
2122  return;
2123  }
2124 
2125  // Read Side
2126  if ((rc = tty_read_section(PortFD, response, '#', 3, &nbytes_read)) != TTY_OK)
2127  {
2128  char errmsg[256];
2129  tty_error_msg(rc, errmsg, 256);
2130  LOGF_ERROR("Error reading from device %s (%d)", errmsg, rc);
2131  return;
2132  }
2133 
2134  response[nbytes_read - 1] = '\0';
2135 
2136  tcflush(PortFD, TCIOFLUSH);
2137 
2138  LOGF_DEBUG("RES: <%s>", response);
2139 
2140  if (!strcmp(response, "East"))
2142  else if (!strcmp(response, "West"))
2144  else
2145  LOGF_ERROR("Invalid pier side response from device-> %s", response);
2146 }
2147 
2149 {
2151 
2158 
2159  return true;
2160 }
2161 
2163 {
2164  int err = 0;
2165 
2166  LOGF_DEBUG("LX200AstroPhysics::SetTrackMode(%d)", mode);
2167 
2168  if (mode == TRACK_CUSTOM)
2169  {
2171  {
2172  LOGF_ERROR("Error setting tracking mode (%d).", err);
2173  return false;
2174  }
2175 
2176  return SetTrackRate(TrackRateN[AXIS_RA].value, TrackRateN[AXIS_DE].value);
2177  }
2178 
2179  if (!isSimulation() && (err = selectAPTrackingMode(PortFD, mode)) < 0)
2180  {
2181  LOGF_ERROR("Error setting tracking mode (%d).", err);
2182  return false;
2183  }
2184 
2185  return true;
2186 }
2187 
2189 {
2190  bool rc;
2191 
2192  LOGF_DEBUG("LX200AstroPhysics::SetTrackEnabled(%d)", enabled);
2193 
2195 
2196  LOGF_DEBUG("LX200AstroPhysics::SetTrackMode() returned %d", rc);
2197 
2198  return rc;
2199 }
2200 
2201 bool LX200AstroPhysics::SetTrackRate(double raRate, double deRate)
2202 {
2203  // Convert to arcsecs/s to AP sidereal multiplier
2204  /*
2205  :RR0.0000# = normal sidereal tracking in RA - similar to :RT2#
2206  :RR+1.0000# = 1 + normal sidereal = 2X sidereal
2207  :RR+9.0000# = 9 + normal sidereal = 10X sidereal
2208  :RR-1.0000# = normal sidereal - 1 = 0 or Stop - similar to :RT9#
2209  :RR-11.0000# = normal sidereal - 11 = -10X sidereal (East at 10X)
2210 
2211  :RD0.0000# = normal zero rate for Dec.
2212  :RD5.0000# = 5 + normal zero rate = 5X sidereal clockwise from above - equivalent to South
2213  :RD-5.0000# = normal zero rate - 5 = 5X sidereal counter-clockwise from above - equivalent to North
2214  */
2215 
2216  double APRARate = (raRate - TRACKRATE_SIDEREAL) / TRACKRATE_SIDEREAL;
2217  double APDERate = deRate / TRACKRATE_SIDEREAL;
2218 
2219  if (!isSimulation())
2220  {
2221  if (setAPRATrackRate(PortFD, APRARate) < 0 || setAPDETrackRate(PortFD, APDERate) < 0)
2222  return false;
2223  }
2224 
2225  return true;
2226 }
2227 
2229 {
2230  if (isSimulation())
2231  {
2232  *offset = 3;
2233  return true;
2234  }
2235 
2236  return (getAPUTCOffset(PortFD, offset) == 0);
2237 }
2238 
2240 {
2241  // If we are not guiding and we need to restore slew rate, then let's restore it.
2242  if (command == MOTION_START && GuideNSTID == 0 && rememberSlewRate >= 0)
2243  {
2244  ISState states[] = { ISS_OFF, ISS_OFF, ISS_OFF, ISS_OFF };
2245  states[rememberSlewRate] = ISS_ON;
2246  const char *names[] = { SlewRateS[0].name, SlewRateS[1].name,
2247  SlewRateS[2].name, SlewRateS[3].name
2248  };
2249  ISNewSwitch(SlewRateSP.device, SlewRateSP.name, states, const_cast<char **>(names), 4);
2250  rememberSlewRate = -1;
2251  }
2252 
2253  bool rc = LX200Generic::MoveNS(dir, command);
2254 #ifdef no
2255  if (command == MOTION_START)
2256  motionCommanded = true;
2257 #endif
2258 
2259  return rc;
2260 }
2261 
2263 {
2264  // If we are not guiding and we need to restore slew rate, then let's restore it.
2265  if (command == MOTION_START && GuideWETID == 0 && rememberSlewRate >= 0)
2266  {
2267  ISState states[] = { ISS_OFF, ISS_OFF, ISS_OFF, ISS_OFF };
2268  states[rememberSlewRate] = ISS_ON;
2269  const char *names[] = { SlewRateS[0].name, SlewRateS[1].name,
2270  SlewRateS[2].name, SlewRateS[3].name
2271  };
2272  ISNewSwitch(SlewRateSP.device, SlewRateSP.name, states, const_cast<char **>(names), 4);
2273  rememberSlewRate = -1;
2274  }
2275 
2276  bool rc = LX200Generic::MoveWE(dir, command);
2277 #ifdef no
2278  if (command == MOTION_START)
2279  motionCommanded = true;
2280 #endif
2281  return rc;
2282 }
2283 
2285 {
2286  // restore guide rate
2288 
2289  bool rc = LX200Generic::MoveNS(dir, command);
2290 #ifdef no
2291  if (command == MOTION_START)
2292  motionCommanded = true;
2293 #endif
2294  return rc;
2295 }
2296 
2298 {
2299  // restore guide rate
2301 
2302  bool rc = LX200Generic::MoveWE(dir, command);
2303 #ifdef no
2304  if (command == MOTION_START)
2305  motionCommanded = true;
2306 #endif
2307  return rc;
2308 }
The Interface class is the base class for all INDI connection plugins.
virtual std::string name()=0
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 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
Connection::Interface * getActiveConnection()
virtual bool Connect()
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
INumberVectorProperty GuideNSNP
INumberVectorProperty GuideWENP
INumberVectorProperty TrackRateNP
TelescopeStatus TrackState
ISwitchVectorProperty TrackStateSP
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.
INumberVectorProperty LocationNP
ITextVectorProperty TimeTP
double GetAxis1Park() const
ISwitchVectorProperty CoordSP
double GetAxis2Park() const
bool isParked()
isParked is mount currently parked?
ISwitchVectorProperty PECStateSP
ISwitchVectorProperty TrackModeSP
ISwitchVectorProperty SlewRateSP
ISwitch MovementWES[2]
virtual void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
INumberVectorProperty EqNP
const char * LoadParkData()
ISwitchVectorProperty ParkSP
INumber TrackRateN[2]
IGeographicCoordinates m_Location
uint32_t GetTelescopeCapability() const
GetTelescopeCapability returns the capability of the Telescope.
ISwitch MovementNSS[2]
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...
ISwitch ParkS[2]
ISwitchVectorProperty MovementWESP
void SetAxis2Park(double steps)
SetDEPark Set current DEC/ALT parking position. The data park file (stored in ~/.indi/ParkData....
void SetAxis2ParkDefault(double steps)
SetDEParkDefault Set default DEC/ALT parking position.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
Definition: lx200ap.cpp:79
ISwitchVectorProperty StartUpSP
Definition: lx200ap.h:103
ISwitchVectorProperty APSlewSpeedSP
Definition: lx200ap.h:114
virtual bool UnPark() override
Unpark the telescope if already parked.
Definition: lx200ap.cpp:1867
ISwitchVectorProperty APGuideSpeedSP
Definition: lx200ap.h:124
ISwitch APSlewSpeedS[3]
Definition: lx200ap.h:113
virtual bool SetTrackMode(uint8_t mode) override
SetTrackMode Set active tracking mode. Do not change track state.
Definition: lx200ap.cpp:2162
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
Definition: lx200ap.cpp:1673
static void pulseGuideTimeoutHelperWE(void *p)
Definition: lx200ap.cpp:1413
virtual bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
Definition: lx200ap.cpp:66
virtual bool SetTrackEnabled(bool enabled) override
SetTrackEnabled Engages or disengages mount tracking. If there are no tracking modes available,...
Definition: lx200ap.cpp:2188
INumber APUTCOffsetN[1]
Definition: lx200ap.h:133
virtual bool Goto(double, double) override
Move the scope to the supplied RA and DEC coordinates.
Definition: lx200ap.cpp:1138
virtual bool SetTrackRate(double raRate, double deRate) override
SetTrackRate Set custom tracking rates.
Definition: lx200ap.cpp:2201
INumber HorizontalCoordsN[2]
Definition: lx200ap.h:110
ISwitchVectorProperty SwapSP
Definition: lx200ap.h:117
virtual IPState GuideWest(uint32_t ms) override
Guide west for ms milliseconds. West is defined as RA-.
Definition: lx200ap.cpp:1366
ISwitch APGuideSpeedS[3]
Definition: lx200ap.h:123
virtual bool Park() override
Park the telescope to its home position.
Definition: lx200ap.cpp:1740
void AstroPhysicsGuideTimeoutNS(bool simul)
Definition: lx200ap.cpp:1446
virtual const char * getDefaultName() override
Definition: lx200ap.cpp:61
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
Definition: lx200ap.cpp:2095
ISwitch SwapS[2]
Definition: lx200ap.h:116
ISwitch SyncCMRS[2]
Definition: lx200ap.h:119
virtual bool Sync(double ra, double dec) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
Definition: lx200ap.cpp:1558
virtual IPState GuideEast(uint32_t ms) override
Guide east for ms milliseconds. East is defined as RA+.
Definition: lx200ap.cpp:1324
bool updateAPSlewRate(int index)
Definition: lx200ap.cpp:1213
IText VersionT[1]
Definition: lx200ap.h:135
ISwitchVectorProperty UnparkFromSP
Definition: lx200ap.h:127
virtual int SendPulseCmd(int8_t direction, uint32_t duration_msec) override
Definition: lx200ap.cpp:1464
ISwitch ParkToS[5]
Definition: lx200ap.h:129
static void simulGuideTimeoutHelperWE(void *p)
Definition: lx200ap.cpp:1423
static void pulseGuideTimeoutHelperNS(void *p)
Definition: lx200ap.cpp:1408
ITextVectorProperty VersionTP
Definition: lx200ap.h:136
ISwitch StartUpS[2]
Definition: lx200ap.h:102
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
Definition: lx200ap.cpp:2074
virtual bool GuideNS(INDI_DIR_NS dir, TelescopeMotionCommand command)
Definition: lx200ap.cpp:2284
INumber APSiderealTimeN[1]
Definition: lx200ap.h:107
INumberVectorProperty APUTCOffsetNP
Definition: lx200ap.h:132
virtual bool Handshake() override
perform handshake with device to check communication
Definition: lx200ap.cpp:1469
INumberVectorProperty HorizontalCoordsNP
Definition: lx200ap.h:111
static void simulGuideTimeoutHelperNS(void *p)
Definition: lx200ap.cpp:1418
virtual IPState GuideSouth(uint32_t ms) override
Guide south for ms milliseconds. South is defined as DEC-.
Definition: lx200ap.cpp:1282
ISwitchVectorProperty ParkToSP
Definition: lx200ap.h:130
virtual bool GuideWE(INDI_DIR_WE dir, TelescopeMotionCommand command)
Definition: lx200ap.cpp:2297
virtual bool getUTFOffset(double *offset) override
Definition: lx200ap.cpp:2228
virtual bool Disconnect() override
Disconnect from device.
Definition: lx200ap.cpp:1544
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
Definition: lx200ap.cpp:2262
INumberVectorProperty APSiderealTimeNP
Definition: lx200ap.h:108
virtual bool updateTime(ln_date *utc, double utc_offset) override
Update telescope time, date, and UTC offset.
Definition: lx200ap.cpp:1610
INumber HourangleCoordsN[2]
Definition: lx200ap.h:105
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
Definition: lx200ap.cpp:203
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition: lx200ap.cpp:506
ISwitchVectorProperty SyncCMRSP
Definition: lx200ap.h:120
virtual IPState GuideNorth(uint32_t ms) override
Guide north for ms milliseconds. North is defined as DEC+.
Definition: lx200ap.cpp:1239
void AstroPhysicsGuideTimeoutWE(bool simul)
Definition: lx200ap.cpp:1428
virtual void debugTriggered(bool enable) override
Inform driver that the debug option was triggered. This function is called after setDebug is triggere...
Definition: lx200ap.cpp:1717
INumberVectorProperty HourangleCoordsNP
Definition: lx200ap.h:106
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition: lx200ap.cpp:446
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
Definition: lx200ap.cpp:2239
ISwitch UnparkFromS[5]
Definition: lx200ap.h:126
virtual bool SetSlewRate(int index) override
SetSlewRate Set desired slew rate index.
Definition: lx200ap.cpp:1729
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
Definition: lx200ap.cpp:2148
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: lx200ap.cpp:176
virtual bool ReadScopeStatus() override
Read telescope status.
Definition: lx200ap.cpp:850
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual void slewError(int slewCode)
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
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)
ISwitchVectorProperty UsePulseCmdSP
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.
const char * SITE_TAB
SITE_TAB Where all site information setting 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 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
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
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
void tty_clr_trailing_read_lf(int enabled)
Definition: indicom.c:375
double get_local_hour_angle(double sideral_time, double ra)
get_local_hour_angle Returns local hour angle of an object
Definition: indicom.c:1293
Implementations for common driver routines.
@ TTY_OK
Definition: indicom.h:150
double get_local_sidereal_time(double longitude)
get_local_sidereal_time Returns local sideral time given longitude and system clock.
#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
ISwitch * IUFindSwitch(const ISwitchVectorProperty *svp, const char *name)
Find an ISwitch member in a vector switch property.
Definition: indidevapi.c:76
#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
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_WARN(fmt,...)
Definition: indilogger.h:81
#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
#define MAX_LX200AP_PULSE_LEN
Definition: lx200ap.cpp:51
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 selectAPCenterRate(int fd, int centerIndex)
int APParkMount(int fd)
int selectAPSlewRate(int fd, int slewIndex)
int check_lx200ap_status(int fd, char *parkStatus, char *slewStatus)
int APUnParkMount(int fd)
int selectAPPECState(int fd, int pecstate)
int selectAPGuideRate(int fd, int guideRate)
int setAPObjectAZ(int fd, double az)
int APSyncCM(int fd, char *matchedObject)
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 AP_TRACKING_OFF
Definition: lx200apdriver.h:36
#define AP_TRACKING_SIDEREAL
Definition: lx200apdriver.h:32
int getSiteLongitude(int fd, int *ddd, int *mm, double *ssf)
int checkLX200EquatorialFormat(int fd)
int abortSlew(int fd)
int setCalenderDate(int fd, int dd, int mm, int yy, bool addSpace)
int getCalendarDate(int fd, char *date)
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
@ LX200_WEST
Definition: lx200driver.h:42
@ LX200_SOUTH
Definition: lx200driver.h:44
@ LX200_NORTH
Definition: lx200driver.h:41
@ LX200_EAST
Definition: lx200driver.h:43
#define getLX200Az(fd, x)
Definition: lx200driver.h:125
#define getSDTime(fd, x)
Definition: lx200driver.h:123
#define getLocalTime24(fd, x)
Definition: lx200driver.h:122
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:323
char device[MAXINDIDEVICE]
Definition: indiapi.h:369
char name[MAXINDINAME]
Definition: indiapi.h:371
char name[MAXINDINAME]
Definition: indiapi.h:250