Instrument Neutral Distributed Interface INDI 2.2.5
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/*
242020-08-07, ToDo
25AP commands not yet implemented for revision >= G
26
27Sets the centering rate for the N-S-E-W buttons to xxx Rcxxx#
28Default command for an equatorial fork mount, which eliminates the meridian flip :FM#
29Default command for A German equatorial mount that includes the meridian flip :EM#
30Horizon 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 */
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);
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,
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,
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);
264 SetAxis2ParkDefault(LocationN[LOCATION_LATITUDE].value);
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);
270 SetAxis1ParkDefault(LocationN[LOCATION_LATITUDE].value);
271
272 SetAxis1ParkDefault(LocationN[LOCATION_LATITUDE].value >= 0 ? 0 : 180);
273 SetAxis2ParkDefault(LocationN[LOCATION_LATITUDE].value);
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
310bool 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
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 snprintf(rev, sizeof(rev), "%s", versionString);
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
374bool 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#
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***************************************************************************************/
446bool 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 }
500 return true;
501 }
502
503 return LX200Generic::ISNewNumber(dev, name, values, names, n);
504}
505
506bool 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");
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
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
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");
597 IDSetText(&VersionTP, nullptr);
598
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
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);
653 IUSaveText(&VersionT[0], versionString);
654 IDSetText(&VersionTP, nullptr);
655
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);
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");
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
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!!!
1024bool 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 {
1039 ra = get_local_sidereal_time(LocationN[LOCATION_LONGITUDE].value);
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!!!
1078bool 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
1122bool 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
1138bool 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
1164 if (MovementNSSP.s == IPS_BUSY || MovementWESP.s == IPS_BUSY)
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 {
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 {
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
1422
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;
1442 GuideWETID = 0;
1443 GuideWENP.apply();
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[0].setValue(0);
1458 GuideNSNP[1].setValue(0);
1460 GuideNSTID = 0;
1461 GuideNSNP.apply();
1462}
1463
1464int 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
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;
1554 IDSetSwitch(&StartUpSP, nullptr);
1555
1556 return LX200Generic::Disconnect();
1557}
1558bool 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
1610bool 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
1673bool 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
1808bool 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
2027 if (MovementNSSP.s == IPS_BUSY || MovementWESP.s == IPS_BUSY)
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
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
2101 SetAxis2Park(fabs(LocationN[LOCATION_LATITUDE].value));
2102
2103 return true;
2104}
2105
2106void 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
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
2201bool 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
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...
INDI::PropertyNumber GuideNSNP
INDI::PropertyNumber GuideWENP
void setState(IPState state)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
TelescopeStatus TrackState
void SetAxis1Park(double value)
SetRAPark Set current RA/AZ parking position. The data park file (stored in ~/.indi/ParkData....
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
bool isParked()
isParked is mount currently parked?
virtual void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
const char * LoadParkData()
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.
void setPierSide(TelescopePierSide side)
bool InitPark()
InitPark Loads parking data (stored in ~/.indi/ParkData.xml) that contains parking status and parking...
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:606
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:586
INDI::PropertySwitch ParkSP
INDI::PropertySwitch AbortSP
INDI::PropertyNumber TrackRateNP
INDI::PropertySwitch MovementNSSP
INDI::PropertyNumber EqNP
INDI::PropertySwitch TrackStateSP
INDI::PropertySwitch PECStateSP
INDI::PropertySwitch SlewRateSP
INDI::PropertySwitch TrackModeSP
INDI::PropertyText TimeTP
INDI::PropertySwitch MovementWESP
INDI::PropertySwitch CoordSP
INDI::PropertyNumber LocationNP
#define setLocalTime(fd, x, y, z)
double ra
double dec
ISState
Switch state.
Definition indiapi.h:148
@ ISS_OFF
Definition indiapi.h:149
@ ISS_ON
Definition indiapi.h:150
@ IP_RW
Definition indiapi.h:184
@ IP_RO
Definition indiapi.h:182
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_1OFMANY
Definition indiapi.h:171
@ AXIS_DE
@ AXIS_RA
INDI_DIR_WE
@ DIRECTION_EAST
@ DIRECTION_WEST
INDI_DIR_NS
@ DIRECTION_SOUTH
@ DIRECTION_NORTH
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:653
int tty_write(int fd, const char *buf, int nbytes, int *nbytes_written)
Writes a buffer to fd.
Definition indicom.c:418
void tty_error_msg(int err_code, char *err_msg, int err_msg_len)
Retrieve the tty error message.
Definition indicom.c:1243
int fs_sexa(char *out, double a, int w, int fracbase)
Converts a sexagesimal number to a string. sprint the variable a in sexagesimal format into out[].
Definition indicom.c:130
void tty_clr_trailing_read_lf(int enabled)
Definition indicom.c:369
double get_local_hour_angle(double sideral_time, double ra)
get_local_hour_angle Returns local hour angle of an object
Definition indicom.c:1379
Implementations for common driver routines.
@ TTY_OK
Definition indicom.h:54
double get_local_sidereal_time(double longitude)
get_local_sidereal_time Returns local sidereal time given longitude and system clock.
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition indidevapi.c:33
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:490
int IUFindOnSwitchIndex(const ISwitchVectorProperty *svp)
Returns the index of first ON switch it finds in the vector switch property.
Definition indidevapi.c:337
void IUResetSwitch(ISwitchVectorProperty *svp)
Reset all switches in a switch vector property to OFF.
Definition indidevapi.c:366
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:509
void IUSaveText(IText *tp, const char *newtext)
Function to reliably save new text in a IText.
Definition indidevapi.c:44
ISwitch * IUFindSwitch(const ISwitchVectorProperty *svp, const char *name)
Find an ISwitch member in a vector switch property.
Definition indidevapi.c:285
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:376
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:416
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:398
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:453
#define INDI_UNUSED(x)
Definition indidevapi.h:132
int IUUpdateSwitch(ISwitchVectorProperty *svp, ISState *states, char *names[], int n)
Update all switches in a switch vector property.
void IDSetNumber(const INumberVectorProperty *nvp, const char *fmt,...)
void IDSetSwitch(const ISwitchVectorProperty *svp, const char *fmt,...)
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:846
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#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 TRACKRATE_SIDEREAL
Definition indimacros.h:39
#define getDeviceName
#define MAX_LX200AP_PULSE_LEN
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)
#define getAPVersionNumber(fd, x)
#define setAPClearBuffer(fd)
#define AP_TRACKING_OFF
#define AP_TRACKING_SIDEREAL
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)
@ LX200_24
Definition lx200driver.h:64
#define getLX200RA(fd, x)
#define getLX200Alt(fd, x)
@ 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)
#define getSDTime(fd, x)
#define getLocalTime24(fd, x)
void EquatorialToHorizontal(IEquatorialCoordinates *object, IGeographicCoordinates *observer, double JD, IHorizontalCoordinates *position)
EquatorialToHorizontal Calculate horizontal coordinates from equatorial coordinates.
Definition libastro.cpp:141
void HorizontalToEquatorial(IHorizontalCoordinates *object, IGeographicCoordinates *observer, double JD, IEquatorialCoordinates *position)
HorizontalToEquatorial Calculate Equatorial EOD Coordinates from horizontal coordinates.
Definition libastro.cpp:161
uint8_t cmd[4]
Definition pwc-ioctl.h:2
char name[MAXINDINAME]
Definition indiapi.h:321
double value
Definition indiapi.h:288
char name[MAXINDINAME]
Definition indiapi.h:369
ISState s
Definition indiapi.h:353
char name[MAXINDINAME]
Definition indiapi.h:248