Instrument Neutral Distributed Interface INDI 2.2.5
lx200ap_v2.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 Copyright (C) 2022 Hy Murveit
7
8 Based on INDI Astrophysics Driver by Markus Wildi
9
10 This library is free software; you can redistribute it and/or
11 modify it under the terms of the GNU Lesser General Public
12 License as published by the Free Software Foundation; either
13 version 2.1 of the License, or (at your option) any later version.
14
15 This library is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
19
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library; if not, write to the Free Software
22 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
23*/
24/*
252020-08-07, ToDo --wildi
26AP commands not yet implemented for revision >= G
27
28Sets the centering rate for the N-S-E-W buttons to xxx Rcxxx#
29Default command for an equatorial fork mount, which eliminates the meridian flip :FM#
30Default command for A German equatorial mount that includes the meridian flip :EM#
31Horizon check during slewing functions :ho# and :hq#
32*/
33
34
35/***********************************************************************
36 * This file was copied an modified from lx200ap.cpp in Jan 2022.
37 *
38 * This is an update of the Wildi and Fulbright A-P drivers.
39 * It is currently being tested.
40 * You should not use this unless part of the test group.
41***********************************************************************/
42
43#include "lx200ap_v2.h"
44
45#include "indicom.h"
46#include "lx200driver.h"
47#include "lx200apdriver.h"
49
50#include <libnova/transform.h>
51
52#include <cmath>
53#include <cstring>
54#include <unistd.h>
55#include <termios.h>
56#include <regex>
57
58// PEC Recording values
64
65// maximum guide pulse request to send to controller
66#define MAX_LX200AP_PULSE_LEN 999u
67
68// The workaround for long pulses doesn't work!
69// #define DONT_SIMULATE_LONG_PULSES true
70// This didn't work. The driver simply doesn't send pulse
71// commands longer than 999ms since CP3 controllers don't support that.
72
84
86{
87 return "AstroPhysics V2";
88}
89
91{
92 Connection::Interface *activeConnection = getActiveConnection();
93 if (!activeConnection->name().compare("CONNECTION_TCP"))
94 {
95 // When using a tcp connection, the GTOCP4 adds trailing LF to response.
96 // this small hack will get rid of them as they are not expected in the driver. and generated
97 // lot of communication errors.
99 }
100
101 // If ApInitialize fails, probably have to turn some buttons red. Verify!
102 return LX200Generic::Connect() && ApInitialize();
103}
104
106{
108
110
111 IUFillNumber(&HourangleCoordsN[0], "HA", "HA H:M:S", "%10.6m", -24., 24., 0., 0.);
112 IUFillNumber(&HourangleCoordsN[1], "DEC", "Dec D:M:S", "%10.6m", -90.0, 90.0, 0., 0.);
113 IUFillNumberVector(&HourangleCoordsNP, HourangleCoordsN, 2, getDeviceName(), "HOURANGLE_COORD", "Hourangle Coords",
115
116 IUFillNumber(&HorizontalCoordsN[0], "AZ", "Az D:M:S", "%10.6m", 0., 360., 0., 0.);
117 IUFillNumber(&HorizontalCoordsN[1], "ALT", "Alt D:M:S", "%10.6m", -90., 90., 0., 0.);
119 "Horizontal Coords", MAIN_CONTROL_TAB, IP_RW, 120, IPS_IDLE);
120
121 // Max rate is 999.99999X for the GTOCP4.
122 // Using :RR998.9999# just to be safe. 15.041067*998.99999 = 15026.02578
123 TrackRateNP[AXIS_RA].setMin(-15026.0258);
124 TrackRateNP[AXIS_RA].setMax(15026.0258);
125 TrackRateNP[AXIS_DE].setMin(-998.9999);
126 TrackRateNP[AXIS_DE].setMax(998.9999);
127
128 // Rates populated in a different routine since they can change after connect:
129 initRateLabels();
130
131 // Home button for clutch aware mounts with encoders.
132 IUFillSwitch(&HomeAndReSyncS[0], "GO", "Home and ReSync", ISS_OFF);
134 "HomeAndReSync", MAIN_CONTROL_TAB, IP_RW, ISR_ATMOST1, 60, IPS_IDLE);
135
136 // Manual-set-mount-to-parked button for recovering from issues.
137 IUFillSwitch(&ManualSetParkedS[0], "MANUAL SET PARKED", "Manual Set Parked", ISS_OFF);
139 "ManualSetParked", MAIN_CONTROL_TAB, IP_RW, ISR_ATMOST1, 60, IPS_IDLE);
140
141 IUFillSwitch(&SwapS[0], "NS", "North/South", ISS_OFF);
142 IUFillSwitch(&SwapS[1], "EW", "East/West", ISS_OFF);
143 IUFillSwitchVector(&SwapSP, SwapS, 2, getDeviceName(), "SWAP", "Swap buttons", MOTION_TAB, IP_RW, ISR_1OFMANY, 0,
144 IPS_IDLE);
145
146 // guide speed
147 IUFillSwitch(&APGuideSpeedS[0], "0.25", "0.25x", ISS_OFF);
148 IUFillSwitch(&APGuideSpeedS[1], "0.5", "0.50x", ISS_OFF);
149 IUFillSwitch(&APGuideSpeedS[2], "1.0", "1.0x", ISS_ON);
151 0, IPS_IDLE);
152
153 // Unpark from?
154 // Order should be the same as the ParkPosition enum.
155 IUFillSwitch(&UnparkFromS[0], "Last", "Last Parked--recommended!", ISS_ON);
156 IUFillSwitch(&UnparkFromS[1], "Park1", "Park1", ISS_OFF);
157 IUFillSwitch(&UnparkFromS[2], "Park2", "Park2", ISS_OFF);
158 IUFillSwitch(&UnparkFromS[3], "Park3", "Park3", ISS_OFF);
159 IUFillSwitch(&UnparkFromS[4], "Park4", "Park4", ISS_OFF);
160 IUFillSwitchVector(&UnparkFromSP, UnparkFromS, 5, getDeviceName(), "UNPARK_FROM", "Unpark From?", MAIN_CONTROL_TAB, IP_RW,
162
163 // park presets
164 // Order should be the same as the ParkPosition enum.
165 IUFillSwitch(&ParkToS[0], "Custom", "Custom--not implemented", ISS_OFF);
166 IUFillSwitch(&ParkToS[1], "Park1", "Park1", ISS_OFF);
167 IUFillSwitch(&ParkToS[2], "Park2", "Park2", ISS_OFF);
168 IUFillSwitch(&ParkToS[3], "Park3", "Park3", ISS_ON);
169 IUFillSwitch(&ParkToS[4], "Park4", "Park4", ISS_OFF);
170 IUFillSwitch(&ParkToS[5], "Current", "Current Position--not implemented", ISS_OFF);
172 IPS_IDLE);
173
174 IUFillText(&VersionT[0], "Version", "Version", "");
175 IUFillTextVector(&VersionTP, VersionT, 1, getDeviceName(), "Firmware", "Firmware", SITE_TAB, IP_RO, 0, IPS_IDLE);
176
177 // UTC offset
178 IUFillNumber(&APUTCOffsetN[0], "APUTC_OFFSET", "AP UTC offset", "%8.5f", 0.0, 24.0, 0.0, 0.);
179 IUFillNumberVector(&APUTCOffsetNP, APUTCOffsetN, 1, getDeviceName(), "APUTC_OFFSET", "AP UTC offset", SITE_TAB,
180 IP_RW, 60, IPS_OK);
181 // sidereal time, ToDO move define where it belongs to
182 IUFillNumber(&APSiderealTimeN[0], "AP_SIDEREAL_TIME", "AP sidereal time", "%10.6m", 0.0, 24.0, 0.0, 0.0);
183 IUFillNumberVector(&APSiderealTimeNP, APSiderealTimeN, 1, getDeviceName(), "AP_SIDEREAL_TIME", "ap sidereal time", SITE_TAB,
184 IP_RO, 60, IPS_OK);
185
186 // Worm position
187 IUFillNumber(&APWormPositionN[0], "APWormPosition", "AP Worm Position", "%3.0f", 0, 1000, 1, 0);
188 IUFillNumberVector(&APWormPositionNP, APWormPositionN, 1, getDeviceName(), "APWormPosition", "AP Worm Position",
190
191 // PEC State
192 IUFillText(&APPECStateT[0], "APPECState", "AP PEC State", "");
193 IUFillTextVector(&APPECStateTP, APPECStateT, 1, getDeviceName(), "APPECState", "AP PEC State",
195
196 IUFillText(&APMountStatusT[0], "APMountStatus", "AP Mount Status", "");
197 IUFillTextVector(&APMountStatusTP, APMountStatusT, 1, getDeviceName(), "APMountStatus", "AP Mount Status",
199
200 // PEC Record button.
201 IUFillSwitch(&APPECRecordS[AP_PEC_RECORD_OFF], "APPECRecordOFF", "Off", ISS_ON);
202 IUFillSwitch(&APPECRecordS[AP_PEC_RECORD_ON], "APPECRecordON", "Record", ISS_OFF);
203 IUFillSwitchVector(&APPECRecordSP, APPECRecordS, 2, getDeviceName(), "APPECRecord", "Record PEC", MOTION_TAB,
205
207
208 // Without below, it will not write the ParkData.xml file.
209 // However, ParkData.xml is not used.
210 // SetParkDataType(PARK_AZ_ALT);
211
212 return true;
213}
214
215void LX200AstroPhysicsV2::initRateLabels()
216{
217 if (rateTable == AP_RATE_TABLE_DEFAULT) // Legacy, pre P02-01
218 {
219 // Motion speed of axis when pressing NSWE buttons
220 SlewRateSP[0].fill("1", "Guide", ISS_OFF);
221 SlewRateSP[1].fill("12", "12x", ISS_OFF);
222 SlewRateSP[2].fill("64", "64x", ISS_ON);
223 SlewRateSP[3].fill("600", "600x", ISS_OFF);
224 SlewRateSP[4].fill("1200", "1200x", ISS_OFF);
225 SlewRateSP.fill(getDeviceName(), "TELESCOPE_SLEW_RATE", "Slew Rate", MOTION_TAB, IP_RW,
227
228 // Slew speed when performing regular GOTO
229 IUFillSwitch(&APSlewSpeedS[0], "600", "600x", ISS_ON);
230 IUFillSwitch(&APSlewSpeedS[1], "900", "900x", ISS_OFF);
231 IUFillSwitch(&APSlewSpeedS[2], "1200", "1200x", ISS_OFF);
233 0, IPS_IDLE);
234 }
235 else
236 {
237 // This is the rate table straight out of the CPx source. First two numbers
238 // are the highest two center/button rates, and the next three numbers are the
239 // three 'goto' rates. There are 4 sets of rates for 4 different types of mounts.
240 std::string standard_rates[4][5] =
241 {
242 { "600", "1200", "600", "900", "1200" },
243 { "500", "900", "400", "650", "900" },
244 { "400", "600", "300", "450", "600" },
245 { "600", "1200", "600", "1000", "1800" }
246 };
247
248 // The 8 means there are 8 slew/center rates.
251 int i = rateTable;
252 SlewRateSP[0].fill("0.25", "0.25x", ISS_OFF);
253 SlewRateSP[1].fill("0.5", "0.5x", ISS_OFF);
254 SlewRateSP[2].fill("1.0", "1.0x", ISS_OFF);
255 SlewRateSP[3].fill("12", "12x", ISS_OFF);
256 SlewRateSP[4].fill("64", "64x", ISS_ON);
257 SlewRateSP[5].fill("200", "200x", ISS_OFF);
258 SlewRateSP[6].fill(standard_rates[i][0].c_str(), standard_rates[i][0].append("x").c_str(), ISS_OFF);
259 SlewRateSP[7].fill(standard_rates[i][1].c_str(), standard_rates[i][1].append("x").c_str(), ISS_OFF);
260
261 // Slew speed when performing regular GOTO
262 IUFillSwitch(&APSlewSpeedS[0], standard_rates[i][2].c_str(), standard_rates[i][2].append("x").c_str(), ISS_ON);
263 IUFillSwitch(&APSlewSpeedS[1], standard_rates[i][3].c_str(), standard_rates[i][3].append("x").c_str(), ISS_OFF);
264 IUFillSwitch(&APSlewSpeedS[2], standard_rates[i][4].c_str(), standard_rates[i][4].append("x").c_str(), ISS_OFF);
265 }
266}
267
290
333
334bool LX200AstroPhysicsV2::getWormPosition()
335{
336 int position;
337 if (isSimulation())
338 position = 0;
339 else
340 {
341 int res = getAPWormPosition(PortFD, &position);
342 if (res != TTY_OK)
343 {
346 IDSetNumber(&APWormPositionNP, nullptr);
347 return false;
348 }
349 }
350 APWormPositionNP.np[0].value = position;
352 IDSetNumber(&APWormPositionNP, nullptr);
353 return true;
354}
355
356void LX200AstroPhysicsV2::processMountStatus(const char *statusString)
357{
358 const char *mountStatusString = apMountStatus(statusString);
359 IUSaveText(&APMountStatusT[0], mountStatusString);
360 IDSetText(&APMountStatusTP, nullptr);
362}
363
364bool LX200AstroPhysicsV2::getPECState(const char *statusString)
365{
366 int pecState;
367 if (isSimulation())
368 pecState = AP_PEC_OFF;
369 else
370 {
371 if (strlen(statusString) < 10)
372 return false;
373 const char pecChar = statusString[9];
374 switch (pecChar)
375 {
376 case 'O':
377 pecState = AP_PEC_OFF;
378 break;
379 case 'P':
380 pecState = AP_PEC_ON;
381 break;
382 case 'R':
383 pecState = AP_PEC_RECORD;
384 break;
385 case 'E':
386 pecState = AP_PEC_ENCODER;
387 break;
388 default:
389 IUSaveText(&APPECStateT[0], "");
390 IDSetText(&APPECStateTP, nullptr);
392 return false;
393 }
394 }
395 // Set the text status display based on the info from the mount.
396 // Also set the PEC buttons: playback on/off & recording on/off.
397 switch (pecState)
398 {
399 case AP_PEC_OFF:
400 IUSaveText(&APPECStateT[0], "Off");
401
405 IDSetSwitch(&APPECRecordSP, nullptr);
406
408
409 break;
410 case AP_PEC_ON:
411 IUSaveText(&APPECStateT[0], "On");
412
416 IDSetSwitch(&APPECRecordSP, nullptr);
417
419
420 break;
421 case AP_PEC_RECORD:
422 IUSaveText(&APPECStateT[0], "Recording");
423
427 IDSetSwitch(&APPECRecordSP, nullptr);
428
430
431 break;
432 case AP_PEC_ENCODER:
433 IUSaveText(&APPECStateT[0], "Encoder");
434
438 IDSetSwitch(&APPECRecordSP, nullptr);
439
441
442 break;
443 }
444 IDSetText(&APPECStateTP, nullptr);
446
447 return true;
448}
449
450// The version string should be formatted as VCP4-$MAJOR-$MINOR.
451// Could be VCP5 as well. For instance: VCP4-P02-12
452void LX200AstroPhysicsV2::setMajorMinorVersions(char *version)
453{
454 majorVersion = 0;
455 minorVersion = 0;
456
457 const std::string v = version;
458 std::regex rgx(".*-(\\w+)-(\\w+)");
459 std::smatch match;
460
461 if (std::regex_search(v.begin(), v.end(), match, rgx))
462 {
463 std::string major = match.str(1);
464 std::string minor = match.str(2);
465
466 std::string majorStripped = std::regex_replace(major, std::regex(R"([\D])"), "");
467 std::string minorStripped = std::regex_replace(minor, std::regex(R"([\D])"), "");
468
469 if (majorStripped.size() > 0)
470 majorVersion = stoi(majorStripped);
471 if (minorStripped.size() > 0)
472 minorVersion = stoi(minorStripped);
473 }
474}
475
476bool LX200AstroPhysicsV2::getFirmwareVersion()
477{
478 bool success;
479 char versionString[128];
480 majorVersion = 0;
481 minorVersion = 0;
482
483 success = false;
484
485 if (isSimulation())
486 strncpy(versionString, "VCP4-P01-01", 128);
487 else
488 getAPVersionNumber(PortFD, versionString);
489
491 IUSaveText(&VersionT[0], versionString);
492 IDSetText(&VersionTP, nullptr);
493
494 if (strstr(versionString, "VCP4"))
495 {
496 firmwareVersion = MCV_V;
497 servoType = GTOCP4;
498 success = true;
499 setMajorMinorVersions(versionString);
500 }
501 else if (strstr(versionString, "VCP5"))
502
503 {
504 firmwareVersion = MCV_V;
505 servoType = GTOCP5;
506 setMajorMinorVersions(versionString);
507 success = true;
508 }
509 else if (strlen(versionString) == 1 || strlen(versionString) == 2)
510 {
511 // Check earlier versions
512 // FIXME could probably use better range checking in case we get a letter like 'Z' that doesn't map to anything!
513 int typeIndex = VersionT[0].text[0] - 'D';
514 if (typeIndex >= 0)
515 {
516 firmwareVersion = static_cast<ControllerVersion>(typeIndex);
517 LOGF_DEBUG("Firmware version index: %d", typeIndex);
518 if (firmwareVersion < MCV_G)
519 servoType = GTOCP2;
520 else
521 servoType = GTOCP3;
522
523 success = true;
524 }
525 else
526 {
527 LOGF_WARN("unknown AP controller version %s", VersionT[0].text);
528 }
529 }
530
531 if (success)
532 {
533 LOGF_INFO("Servo Box Controller: GTOCP%d.", servoType);
534 LOGF_INFO("Firmware Version: '%s'", versionString);
535 if (majorVersion && minorVersion)
536 LOGF_INFO("Firmware Major Version: %d Minor Version %d", majorVersion, minorVersion);
537 }
538
539 return success;
540}
541
542/**************************************************************************************
543**
544***************************************************************************************/
545bool LX200AstroPhysicsV2::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
546{
547 if (strcmp(getDeviceName(), dev))
548 return false;
549
550 if (!strcmp(name, APUTCOffsetNP.name))
551 {
552 if (IUUpdateNumber(&APUTCOffsetNP, values, names, n) < 0)
553 return false;
554
555 float mdelay;
556 int err;
557
558 mdelay = APUTCOffsetN[0].value;
559
560 if (!isSimulation() && (err = setAPUTCOffset(PortFD, mdelay) < 0))
561 {
562 LOGF_ERROR("Error setting UTC offset (%d).", err);
563 return false;
564 }
565
567 IDSetNumber(&APUTCOffsetNP, nullptr);
568
569 return true;
570 }
571 if (!strcmp(name, HourangleCoordsNP.name))
572 {
573
574 if (IUUpdateNumber(&HourangleCoordsNP, values, names, n) < 0)
575 return false;
576
577 double lng = LocationNP[LOCATION_LONGITUDE].getValue();
578 double lst = get_local_sidereal_time(lng);
579 double ra = lst - HourangleCoordsN[0].value;
580 double dec = HourangleCoordsN[1].value;
581 bool success = false;
582 if (CoordSP.isSwitchOn("TRACK") || CoordSP.isSwitchOn("SLEW"))
583 {
584 success = Goto(ra, dec);
585 }
586 else
587 {
588 success = APSync(ra, dec, true);
589 }
590 if (success)
591 {
593 }
594 else
595 {
597 }
599 return true;
600 }
601
602 return LX200Generic::ISNewNumber(dev, name, values, names, n);
603}
604
605bool LX200AstroPhysicsV2::ApInitialize()
606{
607 if ((firmwareVersion == MCV_UNKNOWN) || (firmwareVersion < MCV_T))
608 {
609 // Can't use this driver
610 LOG_ERROR("This driver requires at least version T firmware");
611 return false;
612 }
613
615 char status_string[256];
616 if (getApStatusString(PortFD, status_string) == TTY_OK)
617 {
618 rateTable = apRateTable(status_string);
619 if ((rateTable >= 0) && (rateTable <= 3))
620 LOGF_INFO("Using Rate Table: %d", rateTable);
621 else
623 }
624 initRateLabels();
625
627
628 // Set location up every time we connect.
629 double longitude = -1000, latitude = -1000;
630 // Get value from config file if it exists.
631 IUGetConfigNumber(getDeviceName(), "GEOGRAPHIC_COORD", "LONG", &longitude);
632 IUGetConfigNumber(getDeviceName(), "GEOGRAPHIC_COORD", "LAT", &latitude);
633 if (longitude != -1000 && latitude != -1000)
634 updateAPLocation(latitude, longitude, 0);
635
636 bool isAPParked = false;
637 if (!IsMountParked(&isAPParked))
638 return false;
639 if (isAPParked)
640 {
641 if (!loadConfig(true, UnparkFromSP.name))
642 LOG_DEBUG("could not load config data for UnparkFromSP.name");
643 if (!loadConfig(true, ParkToSP.name))
644 LOG_DEBUG("could not load config data for ParkTo.name");
646 LOG_INFO("Driver's config 'Unpark From ?' is set to Last Parked");
647 // forcing mount being parked from INDI's perspective
648 LOG_INFO("ApInitialize, parked.");
649 SetParked(true);
650 }
651 else
652 {
653 LOG_INFO("ApInitialize, not parked.");
654 SetParked(false);
655 }
656
657 if (isSimulation())
658 {
661
663 IDSetSwitch(&APSlewSpeedSP, nullptr);
664
665 IUSaveText(&VersionT[0], "1.0");
667 IDSetText(&VersionTP, nullptr);
668
669 return true;
670
671 }
672 // Make sure that the mount is setup according to the properties
673 int switch_nr = TrackModeSP.findOnSwitchIndex();
674
675 int err = 0;
676 if ( (err = selectAPTrackingMode(PortFD, switch_nr)) < 0)
677 {
678 LOGF_ERROR("ApInitialize: Error setting tracking mode (%d).", err);
679 return false;
680 }
681
682 // On most mounts SlewRateS defines the MoveTo AND Slew (GOTO) speeds
683 // lx200ap is different - some of the MoveTo speeds are not VALID
684 // Slew speeds so we have to keep two lists.
685 //
686 // SlewRateS is used as the MoveTo speed
687 switch_nr = SlewRateSP.findOnSwitchIndex();
688 if ((err = selectAPV2CenterRate(PortFD, switch_nr, rateTable)) < 0)
689 {
690 LOGF_ERROR("ApInitialize: Error setting move rate (%d).", err);
691 return false;
692 }
693
696
697 // APSlewSpeedsS defines the Slew (GOTO) speeds valid on the AP mounts
699 if ( (err = selectAPSlewRate(PortFD, switch_nr)) < 0)
700 {
701 LOGF_ERROR("ApInitialize: Error setting slew to rate (%d).", err);
702 return false;
703 }
705 IDSetSwitch(&APSlewSpeedSP, nullptr);
706
709
710 // make a IDSet in order the dome controller is aware of the initial values
713
715
716 char versionString[64];
717 getAPVersionNumber(PortFD, versionString);
719 IUSaveText(&VersionT[0], versionString);
720 IDSetText(&VersionTP, nullptr);
721
722 return true;
723}
724
725bool LX200AstroPhysicsV2::ISNewSwitch(const char *dev, const char *name, ISState * states, char *names[], int n)
726{
727 int err = 0;
728 if (strcmp(getDeviceName(), dev))
729 return false;
730
731 // =======================================
732 // Swap Buttons
733 // =======================================
734 if (!strcmp(name, SwapSP.name))
735 {
736 int currentSwap;
737
739 IUUpdateSwitch(&SwapSP, states, names, n);
740 currentSwap = IUFindOnSwitchIndex(&SwapSP);
741
742 if ((!isSimulation() && (err = swapAPButtons(PortFD, currentSwap)) < 0))
743 {
744 LOGF_ERROR("Error swapping buttons (%d).", err);
745 return false;
746 }
747
748 SwapS[0].s = ISS_OFF;
749 SwapS[1].s = ISS_OFF;
750 SwapSP.s = IPS_OK;
751 IDSetSwitch(&SwapSP, nullptr);
752 return true;
753 }
754
755 // ===========================================================
756 // GOTO ("slew") Speed.
757 // ===========================================================
758 if (!strcmp(name, APSlewSpeedSP.name))
759 {
760 IUUpdateSwitch(&APSlewSpeedSP, states, names, n);
761 int slewRate = IUFindOnSwitchIndex(&APSlewSpeedSP);
762
763 if (!isSimulation() && (err = selectAPSlewRate(PortFD, slewRate) < 0))
764 {
765 LOGF_ERROR("Error setting move to rate (%d).", err);
766 return false;
767 }
768
770 IDSetSwitch(&APSlewSpeedSP, nullptr);
771 return true;
772 }
773
774 // ===========================================================
775 // Guide Speed.
776 // ===========================================================
777 if (!strcmp(name, APGuideSpeedSP.name))
778 {
779 IUUpdateSwitch(&APGuideSpeedSP, states, names, n);
780 int guideRate = IUFindOnSwitchIndex(&APGuideSpeedSP);
781
782 if (!isSimulation() && (err = selectAPGuideRate(PortFD, guideRate) < 0))
783 {
784 LOGF_ERROR("Error setting guiding to rate (%d).", err);
785 return false;
786 }
787
789 IDSetSwitch(&APGuideSpeedSP, nullptr);
790 return true;
791 }
792
793 // =======================================
794 // Choose the PEC playback mode
795 // =======================================
796 if (PECStateSP.isNameMatch(name))
797 {
799 PECStateSP.update(states, names, n);
801
802 int pecstate = PECStateSP.findOnSwitchIndex();
803
804 if (!isSimulation() && (err = selectAPPECState(PortFD, pecstate) < 0))
805 {
806 LOGF_ERROR("Error setting PEC state (%d).", err);
807 return false;
808 }
809
812
813 return true;
814 }
815
816 if (strcmp(name, APPECRecordSP.name) == 0)
817 {
819 IUUpdateSwitch(&APPECRecordSP, states, names, n);
821
822 int recordState = IUFindOnSwitchIndex(&APPECRecordSP);
823
824 // Can't turn recording off.
825 if (recordState == AP_PEC_RECORD_ON)
826 {
828 if (!isSimulation() && (err != 0))
829 {
830 LOGF_ERROR("Error setting PEC state RECORD (%d).", err);
831 return false;
832 }
833 LOG_INFO("Recording PEC");
836 }
837 return true;
838 }
839
840 // ===========================================================
841 // Unpark from positions
842 // ===========================================================
843 if (!strcmp(name, UnparkFromSP.name))
844 {
845 IUUpdateSwitch(&UnparkFromSP, states, names, n);
846 ParkPosition unparkPos = static_cast<ParkPosition>(IUFindOnSwitchIndex(&UnparkFromSP));
847
849 if( unparkPos != PARK_LAST)
850 {
851 double unparkAlt, unparkAz;
852 if (!calcParkPosition(unparkPos, &unparkAlt, &unparkAz))
853 {
854 LOG_WARN("Error calculating unpark position!");
856 }
857 else
858 {
859 // 2020-06-01, wildi, UnPark() relies on it
860 saveConfig(true);
861 }
862 }
863 IDSetSwitch(&UnparkFromSP, nullptr);
864 return true;
865 }
866
867 // ===========================================================
868 // Switch Park(ed), Unpark(ed)
869 // ===========================================================
870
871 if (ParkSP.isNameMatch(name))
872 {
873 }
874
875 // ===========================================================
876 // Park To positions
877 // ===========================================================
878 if (!strcmp(name, ParkToSP.name))
879 {
880 IUUpdateSwitch(&ParkToSP, states, names, n);
881 ParkPosition parkPos = static_cast<ParkPosition>(IUFindOnSwitchIndex(&ParkToSP));
882 if (parkPos != PARK_CUSTOM && parkPos != PARK_CURRENT)
883 {
884 double parkAz, parkAlt;
885 if (calcParkPosition(parkPos, &parkAlt, &parkAz))
886 {
887 LOGF_INFO("Set predefined park position %d to az=%f alt=%f", parkPos, parkAz, parkAlt);
888 }
889 else
890 {
891 LOGF_ERROR("Unable to set predefined park position %d!!", parkPos);
892 }
893 }
894 else
895 {
896 LOG_WARN("ISNewSwitch: park custom/current not supported");
899 IDSetSwitch(&ParkToSP, nullptr);
900 return false;
901 }
903 ParkToS[(int)parkPos].s = ISS_ON;
904 ParkToSP.s = IPS_OK;
905 IDSetSwitch(&ParkToSP, nullptr);
906 return true;
907 }
908
909 if (strcmp(name, ManualSetParkedSP.name) == 0)
910 {
911 // Force the mount to be parked where it is and disconnect.
913 bool alreadyConnected = isConnected();
914 if (!alreadyConnected)
915 {
917 Connection::Interface *activeConnection = getActiveConnection();
918 if (!activeConnection->name().compare("CONNECTION_TCP"))
919 {
920 // When using a tcp connection, the GTOCP4 adds trailing LF to response.
921 // this small hack will get rid of them as they are not expected in the driver. and generated
922 // lot of communication errors.
924 }
926 {
927 LOG_ERROR("Connect failed for Manual Park");
928 return true;
929 }
930 }
931
932 if (parkInternal())
933 saveConfig(true);
934 else
935 LOG_ERROR("ParkInternal failed for Manual Park");
936
937 if (!alreadyConnected)
938 {
939 if (!Disconnect())
940 LOG_ERROR("Disconnect failed for Manual Park");
941 }
943 return true;
944 }
945
946 // ===========================================================
947 // Home and ReSync mount
948 // ===========================================================
949 if (strcmp(name, HomeAndReSyncSP.name) == 0)
950 {
953 IDSetSwitch(&HomeAndReSyncSP, nullptr);
954 return true;
955 }
956
957 return LX200Generic::ISNewSwitch(dev, name, states, names, n);
958}
959
961{
962 switch(state)
963 {
965 static char idleStr[] = "Idle";
966 return idleStr;
968 static char slewStr[] = "Slewing";
969 return slewStr;
971 static char trackStr[] = "Tracking";
972 return trackStr;
974 static char parkingStr[] = "Parking";
975 return parkingStr;
977 static char parkStr[] = "Parked";
978 return parkStr;
979 }
980 static char emptyStr[] = "???";
981 return emptyStr;
982}
983
985{
986 if (!isAPReady())
987 {
988 LOGF_DEBUG("APStatus: Not ready--Checked %s Initialized %s Time updated %s Location Updated %s",
989 apInitializationChecked ? "Y" : "N", apIsInitialized ? "Y" : "N",
990 apTimeInitialized ? "Y" : "N", apLocationInitialized ? "Y" : "N");
991
992 // hope this return doesn't delay the time & location. If it does return true?
993 return false;
994 }
995 double lng = LocationNP[LOCATION_LONGITUDE].getValue();
996 double lst = get_local_sidereal_time(lng);
997 double val = lst;
998 if ((!isSimulation()) && (getSDTime(PortFD, &val) < 0))
999 {
1000 LOG_ERROR("Reading sidereal time failed");
1001 return false;
1002 }
1003 std::string sTimeStr;
1004 if (val >= 0 && val <= 24.0)
1005 sTimeStr = std::to_string(val);
1006 else
1007 {
1008 val = 0;
1009 sTimeStr = "????";
1010 }
1011
1012 APSiderealTimeNP.np[0].value = lst;
1014 IDSetNumber(&APSiderealTimeNP, nullptr);
1015
1016 if (isSimulation())
1017 {
1018 mountSim();
1019 return true;
1020 }
1022 {
1024 LOG_ERROR("Error reading RA/DEC.");
1025 EqNP.apply();
1026 return false;
1027 }
1028
1029 char apStatusString[256];
1030 if (getApStatusString(PortFD, apStatusString) != TTY_OK)
1031 {
1032 LOG_ERROR("Reading AP status failed");
1033 return false;
1034 }
1035
1036 getWormPosition();
1037 getPECState(apStatusString);
1038 processMountStatus(apStatusString);
1039
1040 const bool apParked = apStatusParked(apStatusString);
1041 if (!apParked)
1042 {
1043 double ha = get_local_hour_angle(lst, currentRA);
1044
1045 // No need to spam log until we have some actual changes.
1046 if (std::fabs(HourangleCoordsN[0].value - ha) > 0.00001 ||
1047 std::fabs(HourangleCoordsN[1].value - currentDEC) > 0.00001)
1048 {
1049 // in case of simulation, the coordinates are set on parking
1050 HourangleCoordsN[0].value = ha;
1051 HourangleCoordsN[1].value = currentDEC;
1053 IDSetNumber(&HourangleCoordsNP, nullptr );
1054 }
1055 }
1056 LOGF_DEBUG("APStatus: %s %s stime: %s RA/DEC: %.3f %.3f",
1057 trackStateString(TrackState), apParked ? "Parked" : "Unparked", sTimeStr.c_str(), currentRA, currentDEC);
1058
1060 {
1061 const double dx = fabs(lastRA - currentRA);
1062 const double dy = fabs(lastDE - currentDEC);
1063
1064 LOGF_DEBUG("Slewing... currentRA: %.3f dx: %g currentDE: %.3f dy: %g", currentRA, dx, currentDEC, dy);
1065
1066 // Note, RA won't hit 0 if it's not tracking, because the RA changes when still.
1067 // Dec might, though.
1068 // 0 might work now that I "fixed" slewing...perhaps not when tracking is off.
1069 if (dx < 1e-3 && dy < 1e-3)
1070 {
1072 LOG_INFO("Slew is complete. Tracking...");
1073 }
1074
1075 // Keep try of last values to determine if the mount settled.
1076 lastRA = currentRA;
1077 lastDE = currentDEC;
1078 }
1079 else if (TrackState == SCOPE_PARKING)
1080 {
1081 bool slewcomplete = false;
1082 double PARKTHRES = 0.1; // max difference from parked position to consider mount PARKED
1083
1084 if (!apStatusSlewing(apStatusString))
1085 slewcomplete = true;
1086
1087 // old way
1088 if (getLX200Az(PortFD, &currentAz) < 0 || getLX200Alt(PortFD, &currentAlt) < 0)
1089 {
1091 LOG_ERROR("Error reading Az/Alt.");
1092 EqNP.apply();
1093 return false;
1094 }
1095
1096 const double dx = fabs(lastAZ - currentAz);
1097 const double dy = fabs(lastAL - currentAlt);
1098 LOGF_DEBUG("Parking... currentAz: %g dx: %g currentAlt: %g dy: %g", currentAz, dx, currentAlt, dy);
1099
1100 // if for some reason we check slew status BEFORE park motion starts make sure we dont consider park
1101 // action complete too early by checking how far from park position we are!
1102
1103 if (slewcomplete && (dx > PARKTHRES || dy > PARKTHRES))
1104 {
1105 LOG_WARN("Parking... slew status indicates mount stopped by dx/dy too far from mount - continuing!");
1106 slewcomplete = false;
1107 }
1108
1109 // Not sure why it hedged previously. Require slewcomplete for now. Verify!
1110 // if (slewcomplete || (dx <= PARKTHRES && dy <= PARKTHRES))
1111 if (slewcomplete)
1112 {
1113 LOG_DEBUG("Parking slew is complete. Asking astrophysics mount to park...");
1114 if (!parkInternal())
1115 {
1116 return false;
1117 }
1118 saveConfig(true);
1119 }
1120
1121 lastAZ = currentAz;
1122 lastAL = currentAlt;
1123 }
1124
1126
1127 syncSideOfPier();
1128
1129 return true;
1130}
1131
1132bool LX200AstroPhysicsV2::parkInternal()
1133{
1134 if (APParkMount(PortFD) < 0)
1135 {
1136 LOG_ERROR("Parking Failed.");
1137 return false;
1138 }
1139
1140 SetTrackEnabled(false);
1141 SetParked(true);
1142 return true;
1143}
1144
1145bool LX200AstroPhysicsV2::IsMountParked(bool * isAPParked)
1146{
1147 if (isSimulation())
1148 {
1149 // 2030-05-30, if Unparked is selected, this condition is not met
1150 *isAPParked = (ParkSP[PARK].getState() == ISS_ON);
1151 return true;
1152 }
1153
1154 char parkStatus;
1155 char slewStatus;
1156 if (check_lx200ap_status(PortFD, &parkStatus, &slewStatus) == 0)
1157 {
1158 LOGF_DEBUG("parkStatus: %c", parkStatus);
1159
1160 *isAPParked = (parkStatus == 'P');
1161 return true;
1162 }
1163 return false;
1164}
1165
1166bool LX200AstroPhysicsV2::Goto(double r, double d)
1167{
1168 if (!isAPReady())
1169 return false;
1170
1171 const struct timespec timeout = {0, 100000000L};
1172
1173 targetRA = r;
1174 targetDEC = d;
1175
1176 char RAStr[64], DecStr[64];
1177 fs_sexa(RAStr, targetRA, 2, 3600);
1178 fs_sexa(DecStr, targetDEC, 2, 3600);
1179
1180 // If moving, let's stop it first.
1181 if (EqNP.getState() == IPS_BUSY)
1182 {
1183 if (!isSimulation() && abortSlew(PortFD) < 0)
1184 {
1186 LOG_ERROR("Abort slew failed.");
1187 AbortSP.apply();
1188 return false;
1189 }
1190
1193 LOG_ERROR("Slew aborted.");
1194 AbortSP.apply();
1195 EqNP.apply();
1196
1198 {
1206 }
1207
1208 // sleep for 100 mseconds
1209 nanosleep(&timeout, nullptr);
1210 }
1211
1212 if (!isSimulation())
1213 {
1215 {
1217 LOG_ERROR("Error setting RA/DEC.");
1218 EqNP.apply();
1219 return false;
1220 }
1221
1222 int err = 0;
1223
1224 /* Slew reads the '0', that is not the end of the slew */
1225 if ((err = Slew(PortFD)))
1226 {
1228 LOGF_ERROR("Error Slewing to JNow RA %s - DEC %s\n", RAStr, DecStr);
1229 EqNP.apply();
1230 slewError(err);
1231 return false;
1232 }
1233 lastRA = targetRA;
1234 lastDE = targetDEC;
1235 }
1236
1238 //EqNP.s = IPS_BUSY;
1239
1240 LOGF_INFO("Slewing to RA: %s - DEC: %s", RAStr, DecStr);
1241 return true;
1242}
1243
1244// AP mounts handle guide commands differently enough from the "generic" LX200 we need to override some
1245// functions related to the GuiderInterface
1246
1248{
1249 if (!isAPReady())
1250 return IPS_ALERT;
1251
1252 // If we're using pulse command, then MovementXXX should NOT be active at all.
1254 {
1255 LOG_ERROR("Cannot pulse guide while manually in motion. Stop first.");
1256 return IPS_ALERT;
1257 }
1258
1259 if (GuideNSTID)
1260 {
1262 GuideNSTID = 0;
1263 }
1264
1265 ms = std::clamp(ms, 1u, MAX_LX200AP_PULSE_LEN);
1266
1267 if (usePulseCommand)
1268 {
1270 GuideNSTID = IEAddTimer(static_cast<int>(ms), pulseGuideTimeoutHelperNS, this);
1271 }
1272
1273 return IPS_BUSY;
1274}
1275
1277{
1278 if (!isAPReady())
1279 return IPS_ALERT;
1280
1281 // If we're using pulse command, then MovementXXX should NOT be active at all.
1283 {
1284 LOG_ERROR("Cannot pulse guide while manually in motion. Stop first.");
1285 return IPS_ALERT;
1286 }
1287
1288 if (GuideNSTID)
1289 {
1291 GuideNSTID = 0;
1292 }
1293
1294 // Clamp to within 1 to 9999
1295 ms = std::clamp(ms, 1u, MAX_LX200AP_PULSE_LEN);
1296
1297 if (usePulseCommand)
1298 {
1300 GuideNSTID = IEAddTimer(static_cast<int>(ms), pulseGuideTimeoutHelperNS, this);
1301 }
1302
1303 return IPS_BUSY;
1304}
1305
1307{
1308 if (!isAPReady())
1309 return IPS_ALERT;
1310
1311 // If we're using pulse command, then MovementXXX should NOT be active at all.
1313 {
1314 LOG_ERROR("Cannot pulse guide while manually in motion. Stop first.");
1315 return IPS_ALERT;
1316 }
1317
1318 if (GuideWETID)
1319 {
1321 GuideWETID = 0;
1322 }
1323
1324 // Clamp to within 1 to 9999
1325 ms = std::clamp(ms, 1u, MAX_LX200AP_PULSE_LEN);
1326
1327 if (usePulseCommand)
1328 {
1330 GuideWETID = IEAddTimer(static_cast<int>(ms), pulseGuideTimeoutHelperWE, this);
1331 }
1332
1333 return IPS_BUSY;
1334}
1335
1337{
1338 if (!isAPReady())
1339 return IPS_ALERT;
1340
1341 // If we're using pulse command, then MovementXXX should NOT be active at all.
1343 {
1344 LOG_ERROR("Cannot pulse guide while manually in motion. Stop first.");
1345 return IPS_ALERT;
1346 }
1347
1348 if (GuideWETID)
1349 {
1351 GuideWETID = 0;
1352 }
1353
1354 // Clamp to within 1 to 9999
1355 ms = std::clamp(ms, 1u, MAX_LX200AP_PULSE_LEN);
1356
1357 if (usePulseCommand)
1358 {
1360 GuideWETID = IEAddTimer(static_cast<int>(ms), pulseGuideTimeoutHelperWE, this);
1361 }
1362
1363 return IPS_BUSY;
1364}
1365
1370
1375
1380
1385
1387{
1388 LOGF_DEBUG("AstroPhysicsGuideTimeoutWE() pulse guide simul = %d", simul);
1389
1390 if (simul == true)
1391 {
1392 ISState states[] = { ISS_OFF, ISS_OFF };
1393 const char *names[] = { MovementWESP[DIRECTION_WEST].getName(), MovementWESP[DIRECTION_EAST].getName()};
1394 ISNewSwitch(MovementWESP.getDeviceName(), MovementWESP.getName(), states, const_cast<char **>(names), 2);
1395 }
1396
1397 GuideWENP[DIRECTION_WEST].setValue(0);
1398 GuideWENP[DIRECTION_EAST].setValue(0);
1400 GuideWETID = 0;
1401 GuideWENP.apply();
1402}
1403
1405{
1406 LOGF_DEBUG("AstroPhysicsGuideTimeoutNS() pulse guide simul = %d", simul);
1407
1408 if (simul == true)
1409 {
1410 ISState states[] = { ISS_OFF, ISS_OFF };
1412 ISNewSwitch(MovementNSSP.getDeviceName(), MovementNSSP.getName(), states, const_cast<char **>(names), 2);
1413 }
1414
1415 GuideNSNP[0].setValue(0);
1416 GuideNSNP[1].setValue(0);
1418 GuideNSTID = 0;
1419 GuideNSNP.apply();
1420}
1421
1422int LX200AstroPhysicsV2::SendPulseCmd(int8_t direction, uint32_t duration_msec)
1423{
1424 return APSendPulseCmd(PortFD, direction, duration_msec);
1425}
1426
1428{
1429 if (isSimulation())
1430 {
1431 LOG_INFO("Simulated Astrophysics is online. Retrieving basic data...");
1432 getFirmwareVersion();
1433 return true;
1434 }
1435
1436 int err = 0;
1437
1438 if ((err = setAPClearBuffer(PortFD)) < 0)
1439 {
1440 LOGF_ERROR("Error clearing the buffer (%d): %s", err, strerror(err));
1441 return false;
1442 }
1443 if (!getActiveConnection()->name().compare("CONNECTION_TCP"))
1444 {
1445 LOG_INFO("Setting generic udp format (1)");
1447 }
1448 if (setAPBackLashCompensation(PortFD, 0, 0, 0) < 0)
1449 {
1450 // It seems we need to send it twice before it works!
1451 if ((err = setAPBackLashCompensation(PortFD, 0, 0, 0)) < 0)
1452 {
1453 LOGF_ERROR("Error setting backlash compensation (%d): %s.", err, strerror(err));
1454 }
1455 }
1456
1457 // get firmware version
1458 bool rc = false;
1459
1460 rc = getFirmwareVersion();
1461
1462 if (!rc || (firmwareVersion == MCV_UNKNOWN) || (firmwareVersion < MCV_T))
1463 {
1464 // Can't use this driver at < version T. No way to test.
1465 LOG_ERROR("Firmware detection failed or is unknown. This driver requires at least version T firmware");
1466 return false;
1467 }
1468
1469 // Do not track until mount is umparked
1471 {
1472 LOGF_ERROR("Handshake: Error setting tracking mode to zero (%d).", err);
1473 return false;
1474 }
1475 else
1476 {
1477 LOG_INFO("Stopped tracking");
1478 }
1479
1480 // Check to see if the mount is initialized during handshake.
1481 // If it isn't, we'll later need to set things up, and make sure
1482 // that location and time were sent to it.
1483 apIsInitialized = false;
1484 apInitializationChecked = false;
1485 apLocationInitialized = false;
1486 apTimeInitialized = false;
1487
1488 // Let it fail twice before failing
1489 if ((isAPInitialized(PortFD, &apIsInitialized) != TTY_OK) ||
1490 (isAPInitialized(PortFD, &apIsInitialized) != TTY_OK))
1491 {
1492 return false;
1493 }
1494 apInitializationChecked = true;
1495
1496 // Detect and set format. It should be LONG.
1497 return (checkLX200EquatorialFormat(PortFD) == 0);
1498}
1499
1500bool LX200AstroPhysicsV2::isAPReady()
1501{
1502 if (!apInitializationChecked)
1503 return false;
1504
1505 // AP has passed the initialization check.
1506 if (apIsInitialized)
1507 return true;
1508
1509 // Below is implementing the LastParked Scheme.
1510 // I don't require that PARK_LAST is the unparkFrom scheme.
1511 // If the mount is uninitialized, then trust the mount's PARK_LAST data.
1512 if (apLocationInitialized && apTimeInitialized)
1513 {
1514 bool commWorked = true;
1515 char statusString[256];
1516 if (getApStatusString(PortFD, statusString) != TTY_OK)
1517 {
1518 // Try again
1519 commWorked = getApStatusString(PortFD, statusString) == TTY_OK;
1520 }
1521 if (commWorked)
1522 {
1523 bool isAPParked = apStatusParked(statusString);
1524
1525 // A-P came up uninitialized, but we can now fix.
1526 if (APUnParkMount(PortFD) != TTY_OK)
1527 // Try again if we had a comm failure.
1528 commWorked = APUnParkMount(PortFD) == TTY_OK;
1529
1530 if (commWorked)
1531 {
1532 // The mount should now be "calibrated" and have a correct RA/DEC,
1533 // based on its LastParked position.
1534 bool mountOk = false;
1535 if (isAPInitialized(PortFD, &mountOk) != TTY_OK)
1536 {
1537 // try one more time
1538 commWorked = isAPInitialized(PortFD, &mountOk) == TTY_OK;
1539 }
1540 if (commWorked && mountOk)
1541 {
1542 apIsInitialized = true;
1543
1544 // Put it back into the state we found it.
1545 if (isAPParked)
1546 parkInternal();
1547 else
1548 SetParked(false);
1549 return true;
1550 }
1551 }
1552 }
1553 // If we arrive here, we tried but were unable to initialized the mount.
1554 // If unparkFrom is set to one of the park positions, and we're parked,
1555 // then don't fail, as we will recover on the unpark.
1556 ISState lastParkState;
1557 IUGetConfigSwitch(getDeviceName(), "UNPARK_FROM", "Last", &lastParkState);
1558 if (commWorked && apStatusParked(statusString) && (lastParkState != ISS_ON))
1559 {
1560 apIsInitialized = true;
1561 return true;
1562 }
1563 else
1564 {
1565 LOG_ERROR("Could not initialize mount.");
1566 Disconnect(); // Not sure about this....
1567 return false;
1568 }
1569 }
1570 // Not initialized, but not ready to give up either.
1571 return false;
1572}
1573
1575{
1576 apIsInitialized = false;
1577 apLocationInitialized = false;
1578 apTimeInitialized = false;
1579 apInitializationChecked = false;
1580
1581 return LX200Generic::Disconnect();
1582}
1583
1584bool LX200AstroPhysicsV2::APSync(double ra, double dec, bool recalibrate)
1585{
1586 char syncString[256] = ""; // simulation needs UTF-8
1587
1588 if (!isSimulation())
1589 {
1590 if (setAPObjectRA(PortFD, ra) < 0 || setAPObjectDEC(PortFD, dec) < 0)
1591 {
1593 LOG_ERROR("Error setting RA/DEC. Unable to Sync.");
1594 EqNP.apply();
1595 return false;
1596 }
1597 bool syncOK = true;
1598
1599 if (recalibrate)
1600 {
1601 if (APSyncCMR(PortFD, syncString) < 0)
1602 syncOK = false;
1603 }
1604 else
1605 {
1606 if (APSyncCM(PortFD, syncString) < 0)
1607 syncOK = false;
1608 }
1609
1610 if (!syncOK)
1611 {
1613 LOG_ERROR("Synchronization failed");
1614 EqNP.apply();
1615 return false;
1616 }
1617
1618 }
1619
1620 currentRA = ra;
1621 currentDEC = dec;
1622 LOGF_DEBUG("%s Synchronization successful %s", (recalibrate ? "CMR" : "CM"), syncString);
1623
1625
1627
1628 return true;
1629}
1630
1632{
1633 // The default sync is a "CMR" / "Recalibrate" sync.
1634 return APSync(ra, dec, true);
1635}
1636
1637bool LX200AstroPhysicsV2::updateTime(ln_date * utc, double utc_offset)
1638{
1639 // 2020-06-02, wildi, ToDo, time obtained from KStars differs up to a couple
1640 // of 5 seconds from system time.
1641 struct ln_zonedate ltm;
1642
1643 ln_date_to_zonedate(utc, &ltm, utc_offset * 3600.0);
1644 JD = ln_get_julian_day(utc);
1645 LOGF_DEBUG("New JD is %f, local time: %d, %d, %d, utc offset: %f", JD, ltm.hours, ltm.minutes, (int)ltm.seconds,
1646 utc_offset);
1647
1648 // Set Local Time
1649 if (isSimulation() == false && setLocalTime(PortFD, ltm.hours, ltm.minutes, (int)ltm.seconds) < 0)
1650 {
1651 LOG_ERROR("Error setting local time.");
1652 return false;
1653 }
1654 LOGF_DEBUG("Set Local Time %02d:%02d:%02d is successful.", ltm.hours, ltm.minutes,
1655 (int)ltm.seconds);
1656
1657 if (isSimulation() == false && setCalenderDate(PortFD, ltm.days, ltm.months, ltm.years) < 0)
1658 {
1659 LOG_ERROR("Error setting local date.");
1660 return false;
1661 }
1662 LOGF_DEBUG("Set Local Date %02d/%02d/%02d is successful.", ltm.days, ltm.months, ltm.years);
1663
1664 // 2020-05-30, wildi, after a very long journey
1665 // AP: TZ (0,12): West, East (-12.,-0), (>12,24)
1666 // Peru, Lima:
1667 //(TX=':Gg#'), RX='+77*01:42#
1668 //(TX=':SG05:00:00#'), RX='1'
1669 // Linux/Windows TZ values: West: -12,0, East 0,12
1670 // AP GTOCPX accepts a converted float including 24.
1671 double ap_utc_offset = - utc_offset;
1672 if (!isSimulation() && setAPUTCOffset(PortFD, ap_utc_offset) < 0)
1673 {
1674 LOG_ERROR("Error setting UTC Offset.");
1675 return false;
1676 }
1677 APUTCOffsetN[0].value = ap_utc_offset ;
1679 IDSetNumber(&APUTCOffsetNP, nullptr);
1680
1681 LOGF_DEBUG("Set UTC Offset %g as AP UTC Offset %g is successful.", utc_offset, ap_utc_offset);
1682 apTimeInitialized = true;
1683 return true;
1684}
1685
1686bool LX200AstroPhysicsV2::updateAPLocation(double latitude, double longitude, double elevation)
1687{
1688 INDI_UNUSED(elevation);
1689 LOG_DEBUG("LX200AstroPhysicsV2::updateLocation entry");
1690
1691 if ((latitude == 0.) && (longitude == 0.))
1692 {
1693 LOG_DEBUG("updateLocation: latitude, longitude both zero");
1694 return false;
1695 }
1696
1697 // Why is it 360-longitude? Verify!
1698 double apLongitude = 360 - longitude ;
1699 while (apLongitude < 0)
1700 apLongitude += 360;
1701 while (apLongitude > 360)
1702 apLongitude -= 360;
1703
1704 LOGF_DEBUG("Setting site longitude coordinates, %f %f", longitude, apLongitude);
1705
1706 if (!isSimulation() && setAPSiteLongitude(PortFD, apLongitude) < 0)
1707 {
1708 LOG_ERROR("Error setting site longitude coordinates");
1709 return false;
1710 }
1711
1712 if (!isSimulation() && setAPSiteLatitude(PortFD, latitude) < 0)
1713 {
1714 LOG_ERROR("Error setting site latitude coordinates");
1715 return false;
1716 }
1717
1718 char l[32], L[32];
1719 fs_sexa(l, latitude, 3, 3600);
1720 fs_sexa(L, longitude, 4, 3600);
1721
1722 LOGF_DEBUG("Site location updated to Lat %.32s - Long %.32s, deg: %f, %f", l, L, latitude, longitude);
1723 apLocationInitialized = true;
1724 return true;
1725}
1726
1727bool LX200AstroPhysicsV2::updateLocation(double latitude, double longitude, double elevation)
1728{
1729 if ((latitude == 0.) && (longitude == 0.))
1730 {
1731 LOG_DEBUG("updateLocation: latitude, longitude both zero");
1732 return false;
1733 }
1734 if (!isConnected())
1735 return true;
1736 return updateAPLocation(latitude, longitude, elevation);
1737}
1738
1740{
1742
1743 // we use routines from legacy AP driver routines and newer experimental driver routines
1745
1746}
1747
1748// For most mounts the SetSlewRate() method sets both the MoveTo and Slew (GOTO) speeds.
1749// For AP mounts these two speeds are handled separately - so SetSlewRate() actually sets the MoveTo speed for AP mounts - confusing!
1750// ApSetSlew
1752{
1753 if (!isSimulation() && selectAPV2CenterRate(PortFD, index, rateTable) < 0)
1754 {
1755 LOG_ERROR("Error setting slew mode.");
1756 return false;
1757 }
1758
1759 return true;
1760}
1761
1763{
1764 ParkPosition parkPos = static_cast<ParkPosition>(IUFindOnSwitchIndex(&ParkToSP));
1765 double parkAz {90}, parkAlt {0};
1766 if (parkPos == PARK_CURRENT)
1767 {
1768 LOG_DEBUG("PARK_CURRENT not implemented");
1769 }
1770 else if (calcParkPosition(parkPos, &parkAlt, &parkAz))
1771 {
1772 LOGF_DEBUG("Set park position %d to az=%f alt=%f", parkPos, parkAz, parkAlt);
1773 }
1774 else
1775 {
1776 LOGF_ERROR("Unable to set park position %d!!", parkPos);
1777 }
1778
1779 char AzStr[16] = {0}, AltStr[16] = {0};
1780 fs_sexa(AzStr, parkAz, 2, 3600);
1781 fs_sexa(AltStr, parkAlt, 2, 3600);
1782 LOGF_INFO("Parking to Az (%s) Alt (%s)...", AzStr, AltStr);
1783
1784 INDI::IEquatorialCoordinates equatorialCoords {0, 0};
1785 INDI::IHorizontalCoordinates horizontalCoords {parkAz, parkAlt};
1786 INDI::HorizontalToEquatorial(&horizontalCoords, &m_Location, ln_get_julian_from_sys(), &equatorialCoords);
1788 double ha = get_local_hour_angle(lst, equatorialCoords.rightascension);
1789
1791 HourangleCoordsN[0].value = ha;
1792 HourangleCoordsN[1].value = equatorialCoords.declination;
1793 IDSetNumber(&HourangleCoordsNP, nullptr);
1794
1795 if (isSimulation())
1796 {
1797 Goto(equatorialCoords.rightascension, equatorialCoords.declination);
1798 }
1799 else
1800 {
1801 if (setAPObjectAZ(PortFD, parkAz) < 0 || setAPObjectAlt(PortFD, parkAlt) < 0)
1802 {
1803 LOG_ERROR("Error setting Az/Alt.");
1804 return false;
1805 }
1806
1807 int err = 0;
1808
1809 /* Slew reads the '0', that is not the end of the slew */
1810 if ((err = Slew(PortFD)))
1811 {
1812 LOGF_ERROR("Error Slewing to Az %s - Alt %s", AzStr, AltStr);
1813 slewError(err);
1814 return false;
1815 }
1816 lastAZ = parkAz;
1817 lastAL = parkAlt;
1818 }
1819
1822 LOG_INFO("Parking is in progress...");
1823
1824 return true;
1825}
1826
1827bool LX200AstroPhysicsV2::calcParkPosition(ParkPosition pos, double * parkAlt, double * parkAz)
1828{
1829 switch (pos)
1830 {
1831 // last unparked and park custom share enum 0
1832 case PARK_CUSTOM:
1833 LOG_ERROR("Called calcParkPosition with PARK_CUSTOM or PARK_LAST!");
1834 return false;
1835 break;
1836
1837 case PARK_CURRENT:
1838 LOG_ERROR("Called calcParkPosition with PARK_CURRENT!");
1839 return false;
1840 break;
1841
1842 // Park 1
1843 // Northern Hemisphere should be pointing at ALT=0 AZ=0 with scope on WEST side of pier
1844 // Southern Hemisphere should be pointing at ALT=0 AZ=180 with scope on WEST side of pier
1845 case PARK_PARK1:
1846 LOG_INFO("Computing PARK1 position...");
1847 *parkAlt = 0;
1848 *parkAz = LocationNP[LOCATION_LATITUDE].getValue() > 0 ? 359.1 : 180.1;
1849 break;
1850
1851 // Park 2
1852 // Northern Hemisphere should be pointing at ALT=0 AZ=90 with scope pointing EAST
1853 // Southern Hemisphere should be pointing at ALT=0 AZ=90 with scope pointing EAST
1854 case PARK_PARK2:
1855 LOG_INFO("Computing PARK2 position...");
1856 *parkAlt = 0;
1857 *parkAz = 90;
1858 break;
1859
1860 // Park 3
1861 // Northern Hemisphere should be pointing at ALT=LAT AZ=0 with scope pointing NORTH with CW down
1862 // Southern Hemisphere should be pointing at ALT=LAT AZ=180 with scope pointing SOUTH with CW down
1863 // wildi: the hour angle is undefined if AZ = 0,180 and ALT=LAT is chosen, adding .1 to Az sets PARK3
1864 // as close as possible to to HA = -6 hours (CW down), valid for both hemispheres.
1865 case PARK_PARK3:
1866 *parkAlt = fabs(LocationNP[LOCATION_LATITUDE].getValue());
1867 *parkAz = LocationNP[LOCATION_LATITUDE].getValue() > 0 ? 0.1 : 179.9;
1868 LOG_INFO("Computing PARK3 position");
1869 break;
1870
1871 // Park 4
1872 // Northern Hemisphere should be pointing at ALT=0 AZ=180 with scope on EAST side of pier
1873 // Southern Hemisphere should be pointing at ALT=0 AZ=0 with scope on EAST side of pier
1874 case PARK_PARK4:
1875 LOG_INFO("Computing PARK4 position...");
1876 *parkAlt = 0;
1877 *parkAz = LocationNP[LOCATION_LATITUDE].getValue() > 0 ? 180.1 : 359.1;
1878 break;
1879
1880 default:
1881 LOG_ERROR("Unknown park position!");
1882 return false;
1883 break;
1884 }
1885
1886 LOGF_DEBUG("calcParkPosition: parkPos=%d parkAlt=%f parkAz=%f", pos, *parkAlt, *parkAz);
1887
1888 return true;
1889
1890}
1891
1893{
1894 bool unpark_from_last_config = (PARK_LAST == IUFindOnSwitchIndex(&UnparkFromSP));
1895 double unparkAlt = 0, unparkAz = 0;
1896
1897 if (!unpark_from_last_config)
1898 {
1899 ParkPosition unparkfromPos = static_cast<ParkPosition>(IUFindOnSwitchIndex(&UnparkFromSP));
1900 LOGF_DEBUG("UnPark: park position = %d from current driver", unparkfromPos);
1901 if (!calcParkPosition(unparkfromPos, &unparkAlt, &unparkAz))
1902 {
1903 LOG_ERROR("UnPark: Error calculating unpark position!");
1905 IDSetSwitch(&UnparkFromSP, nullptr);
1906 return false;
1907 }
1908 LOGF_DEBUG("UnPark: parkPos=%d parkAlt=%f parkAz=%f", unparkfromPos, unparkAlt, unparkAz);
1909 }
1910
1911 bool isAPParked = true;
1912 if(!isSimulation())
1913 {
1914 if (!IsMountParked(&isAPParked))
1915 {
1916 LOG_WARN("UnPark:could not determine AP park status");
1918 IDSetSwitch(&UnparkFromSP, nullptr);
1919 return false;
1920 }
1921
1922 if(!isAPParked)
1923 {
1924 LOG_WARN("UnPark: AP mount status: unparked, park first");
1926 IDSetSwitch(&UnparkFromSP, nullptr);
1927 return false;
1928 }
1929
1930 if (APUnParkMount(PortFD) < 0)
1931 {
1932 ParkSP.reset();
1935 ParkSP.apply();
1936 LOG_ERROR("UnParking AP mount failed.");
1937 return false;
1938 }
1939
1940 SetParked(false);
1941 // Stop :Q#
1942 if ( abortSlew(PortFD) < 0)
1943 {
1944 ParkSP.reset();
1947 ParkSP.apply();
1948 LOG_WARN("Abort motion Failed");
1949 return false;
1950 }
1951 SetTrackEnabled(true);
1953 }
1954 else
1955 {
1956 SetParked(false);
1957 SetTrackEnabled(false);
1959 }
1960
1961 if (!unpark_from_last_config)
1962 {
1963 INDI::IEquatorialCoordinates equatorialCoords {0, 0};
1964 INDI::IHorizontalCoordinates horizontalCoords {unparkAz, unparkAlt};
1965 INDI::HorizontalToEquatorial(&horizontalCoords, &m_Location, ln_get_julian_from_sys(), &equatorialCoords);
1966
1967 char AzStr[16], AltStr[16];
1968 fs_sexa(AzStr, unparkAz, 2, 3600);
1969 fs_sexa(AltStr, unparkAlt, 2, 3600);
1970 char RaStr[16], DecStr[16];
1971 fs_sexa(RaStr, equatorialCoords.rightascension, 2, 3600);
1972 fs_sexa(DecStr, equatorialCoords.declination, 2, 3600);
1973
1975 double ha = get_local_hour_angle(lst, equatorialCoords.rightascension);
1976 char HaStr[16];
1977 fs_sexa(HaStr, ha, 2, 3600);
1978 LOGF_INFO("UnPark: Current parking position Az (%s) Alt (%s), HA (%s) RA (%s) Dec (%s)", AzStr, AltStr, HaStr,
1979 RaStr, DecStr);
1980
1982 HourangleCoordsN[0].value = ha;
1983 HourangleCoordsN[1].value = equatorialCoords.declination;
1984 IDSetNumber(&HourangleCoordsNP, nullptr);
1985
1986
1987 // If we are not using PARK_LAST, then we're unparking from a pre-defined position, and this is the
1988 // only time we should use the full :CM "Fully Calibrate" sync command.
1989 bool success = APSync(equatorialCoords.rightascension, equatorialCoords.declination, false);
1990 if(!success)
1991 {
1992 LOG_WARN("Could not sync mount");
1993 return false;
1994 }
1995 else
1996 {
1997 LOGF_INFO("UnPark: Sync'd (:CM) to RA/DEC: %f %f", equatorialCoords.rightascension, equatorialCoords.declination);
1998 }
1999 }
2000
2002 {
2010 }
2011
2013 IDSetSwitch(&UnparkFromSP, nullptr);
2014 // SlewRateS is used as the MoveTo speed
2015 int err;
2016 int switch_nr = SlewRateSP.findOnSwitchIndex();
2017 if (!isSimulation() && (err = selectAPV2CenterRate(PortFD, switch_nr, rateTable)) < 0)
2018 {
2019 LOGF_ERROR("Error setting center (MoveTo) rate (%d).", err);
2020 return false;
2021 }
2022
2024 SlewRateSP.apply();
2025
2026 // APSlewSpeedsS defines the Slew (GOTO) speeds valid on the AP mounts
2027 switch_nr = IUFindOnSwitchIndex(&APSlewSpeedSP);
2028 if (!isSimulation() && (err = selectAPSlewRate(PortFD, switch_nr)) < 0)
2029 {
2030 LOGF_ERROR("Error setting slew to rate (%d).", err);
2031 return false;
2032 }
2033
2035 IDSetSwitch(&APSlewSpeedSP, nullptr);
2036
2037 LOG_DEBUG("UnPark: Mount unparked successfully");
2038
2039 return true;
2040}
2041
2043{
2044 return true;
2045}
2046
2048{
2049 return true;
2050}
2051
2052void LX200AstroPhysicsV2::syncSideOfPier()
2053{
2054 const char *cmd = "#:pS#";
2055 // Response
2056 char response[16] = { 0 };
2057 int rc = 0, nbytes_read = 0, nbytes_written = 0;
2058
2059 LOGF_DEBUG("CMD: <%s>", cmd);
2060
2061 tcflush(PortFD, TCIOFLUSH);
2062
2063 if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
2064 {
2065 char errmsg[256];
2066 tty_error_msg(rc, errmsg, 256);
2067 LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
2068 return;
2069 }
2070
2071 // Read Side
2072 if ((rc = tty_read_section(PortFD, response, '#', 3, &nbytes_read)) != TTY_OK)
2073 {
2074 char errmsg[256];
2075 tty_error_msg(rc, errmsg, 256);
2076 LOGF_ERROR("Error reading from device %s (%d)", errmsg, rc);
2077 return;
2078 }
2079
2080 response[nbytes_read - 1] = '\0';
2081
2082 tcflush(PortFD, TCIOFLUSH);
2083
2084 LOGF_DEBUG("RES: <%s>", response);
2085
2086 if (!strcmp(response, "East"))
2088 else if (!strcmp(response, "West"))
2090 else
2091 LOGF_ERROR("Invalid pier side response from device-> %s", response);
2092}
2093
2106
2108{
2109 int err = 0;
2110
2111 LOGF_DEBUG("LX200AstroPhysicsV2::SetTrackMode(%d)", mode);
2112
2113 if (mode == TRACK_CUSTOM)
2114 {
2116 {
2117 LOGF_ERROR("Error setting tracking mode (%d).", err);
2118 return false;
2119 }
2120
2121 return SetTrackRate(TrackRateNP[AXIS_RA].getValue(), TrackRateNP[AXIS_DE].getValue());
2122 }
2123
2124 if (!isSimulation() && (err = selectAPTrackingMode(PortFD, mode)) < 0)
2125 {
2126 LOGF_ERROR("Error setting tracking mode (%d).", err);
2127 return false;
2128 }
2129
2130 return true;
2131}
2132
2134{
2135 bool rc;
2136
2137 LOGF_DEBUG("LX200AstroPhysicsV2::SetTrackEnabled(%d)", enabled);
2138
2140
2141 LOGF_DEBUG("LX200AstroPhysicsV2::SetTrackMode() returned %d", rc);
2142
2143 return rc;
2144}
2145
2146bool LX200AstroPhysicsV2::SetTrackRate(double raRate, double deRate)
2147{
2148 // Convert to arcsecs/s to AP sidereal multiplier
2149 /*
2150 :RR0.0000# = normal sidereal tracking in RA - similar to :RT2#
2151 :RR+1.0000# = 1 + normal sidereal = 2X sidereal
2152 :RR+9.0000# = 9 + normal sidereal = 10X sidereal
2153 :RR-1.0000# = normal sidereal - 1 = 0 or Stop - similar to :RT9#
2154 :RR-11.0000# = normal sidereal - 11 = -10X sidereal (East at 10X)
2155
2156 :RD0.0000# = normal zero rate for Dec.
2157 :RD5.0000# = 5 + normal zero rate = 5X sidereal clockwise from above - equivalent to South
2158 :RD-5.0000# = normal zero rate - 5 = 5X sidereal counter-clockwise from above - equivalent to North
2159 */
2160
2161 double APRARate = (raRate - TRACKRATE_SIDEREAL) / TRACKRATE_SIDEREAL;
2162 double APDERate = deRate / TRACKRATE_SIDEREAL;
2163
2164 if (!isSimulation())
2165 {
2166 if (setAPRATrackRate(PortFD, APRARate) < 0 || setAPDETrackRate(PortFD, APDERate) < 0)
2167 return false;
2168 }
2169
2170 return true;
2171}
2172
2174{
2175 if (isSimulation())
2176 {
2177 *offset = 3;
2178 return true;
2179 }
2180
2181 return (getAPUTCOffset(PortFD, offset) == 0);
2182}
2183
2185{
2186 // If we are not guiding and we need to restore slew rate, then let's restore it.
2187 if (command == MOTION_START && GuideNSTID == 0 && rememberSlewRate >= 0)
2188 {
2189 ISState states[] = { ISS_OFF, ISS_OFF, ISS_OFF, ISS_OFF };
2190 states[rememberSlewRate] = ISS_ON;
2191 const char *names[] = { SlewRateSP[0].getName(), SlewRateSP[1].getName(),
2193 };
2194 ISNewSwitch(SlewRateSP.getDeviceName(), SlewRateSP.getName(), states, const_cast<char **>(names), 4);
2195 rememberSlewRate = -1;
2196 }
2197
2198 bool rc = LX200Generic::MoveNS(dir, command);
2199 return rc;
2200}
2201
2203{
2204 // If we are not guiding and we need to restore slew rate, then let's restore it.
2205 if (command == MOTION_START && GuideWETID == 0 && rememberSlewRate >= 0)
2206 {
2207 ISState states[] = { ISS_OFF, ISS_OFF, ISS_OFF, ISS_OFF };
2208 states[rememberSlewRate] = ISS_ON;
2209 const char *names[] = { SlewRateSP[0].getName(), SlewRateSP[1].getName(),
2211 };
2212 ISNewSwitch(SlewRateSP.getDeviceName(), SlewRateSP.getName(), states, const_cast<char **>(names), 4);
2213 rememberSlewRate = -1;
2214 }
2215
2216 bool rc = LX200Generic::MoveWE(dir, command);
2217 return rc;
2218}
2219
2221{
2222 if (!isAPReady())
2223 return false;
2224
2225 // restore guide rate
2227
2228 bool rc = LX200Generic::MoveNS(dir, command);
2229 return rc;
2230}
2231
2233{
2234 if (!isAPReady())
2235 return false;
2236
2237 // restore guide rate
2239
2240 bool rc = LX200Generic::MoveWE(dir, command);
2241 return rc;
2242}
2243
The Interface class is the base class for all INDI connection plugins.
virtual std::string name()=0
void setConnectionType(int type)
TODO should be renamed to setDefaultConnectionType.
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.
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool loadConfig(bool silent=false, const char *property=nullptr)
Load the last saved configuration file.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
bool isSimulation() const
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)
const char * getDeviceName() const
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
void save(FILE *f) const
save Save property to configuration file.
const char * getName() const
bool isNameMatch(const char *otherName) const
bool isSwitchOn(const std::string &name) const
bool update(const ISState states[], const char *const names[], int n)
void fill(const char *device, const char *name, const char *label, const char *group, IPerm permission, ISRule rule, double timeout, IPState state)
TelescopeStatus TrackState
void SetTelescopeCapability(uint32_t cap, uint8_t slewRateCount)
SetTelescopeCapability sets the Telescope capabilities. All capabilities must be initialized.
virtual void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
Connection::TCP * tcpConnection
void setPECState(TelescopePECState state)
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)
TelescopePECState getPECState()
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
ITextVectorProperty APMountStatusTP
Definition lx200ap_v2.h:162
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
ISwitchVectorProperty APSlewSpeedSP
Definition lx200ap_v2.h:123
virtual IPState GuideNorth(uint32_t ms) override
Guide north for ms milliseconds. North is defined as DEC+.
INumber HorizontalCoordsN[2]
Definition lx200ap_v2.h:119
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
ISwitch UnparkFromS[5]
Definition lx200ap_v2.h:135
virtual bool Handshake() override
perform handshake with device to check communication
virtual IPState GuideWest(uint32_t ms) override
Guide west for ms milliseconds. West is defined as RA-.
INumberVectorProperty HourangleCoordsNP
Definition lx200ap_v2.h:115
ISwitchVectorProperty ParkToSP
Definition lx200ap_v2.h:139
ISwitch APSlewSpeedS[3]
Definition lx200ap_v2.h:122
void AstroPhysicsGuideTimeoutNS(bool simul)
ITextVectorProperty APPECStateTP
Definition lx200ap_v2.h:159
ITextVectorProperty VersionTP
Definition lx200ap_v2.h:145
ISwitch APPECRecordS[2]
Definition lx200ap_v2.h:165
INumberVectorProperty HorizontalCoordsNP
Definition lx200ap_v2.h:120
virtual bool GuideNS(INDI_DIR_NS dir, TelescopeMotionCommand command)
ISwitch HomeAndReSyncS[1]
Definition lx200ap_v2.h:151
virtual bool Park() override
Park the telescope to its home position.
virtual bool SetTrackMode(uint8_t mode) override
SetTrackMode Set active tracking mode. Do not change track state.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual bool SetTrackEnabled(bool enabled) override
SetTrackEnabled Engages or disengages mount tracking. If there are no tracking modes available,...
virtual void debugTriggered(bool enable) override
Inform driver that the debug option was triggered. This function is called after setDebug is triggere...
virtual bool UnPark() override
Unpark the telescope if already parked.
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
static void pulseGuideTimeoutHelperWE(void *p)
INumberVectorProperty APSiderealTimeNP
Definition lx200ap_v2.h:117
virtual bool getUTFOffset(double *offset) override
INumberVectorProperty APUTCOffsetNP
Definition lx200ap_v2.h:141
INumber APSiderealTimeN[1]
Definition lx200ap_v2.h:116
INumber APUTCOffsetN[1]
Definition lx200ap_v2.h:142
virtual bool Sync(double ra, double dec) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
static void pulseGuideTimeoutHelperNS(void *p)
ISwitch APGuideSpeedS[3]
Definition lx200ap_v2.h:132
virtual int SendPulseCmd(int8_t direction, uint32_t duration_msec) override
virtual bool Goto(double, double) override
Move the scope to the supplied RA and DEC coordinates.
ISwitchVectorProperty SwapSP
Definition lx200ap_v2.h:126
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual IPState GuideEast(uint32_t ms) override
Guide east for ms milliseconds. East is defined as RA+.
ISwitchVectorProperty APGuideSpeedSP
Definition lx200ap_v2.h:133
IText APMountStatusT[1]
Definition lx200ap_v2.h:161
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
virtual bool SetTrackRate(double raRate, double deRate) override
SetTrackRate Set custom tracking rates.
static void simulGuideTimeoutHelperNS(void *p)
ISwitchVectorProperty ManualSetParkedSP
Definition lx200ap_v2.h:147
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual IPState GuideSouth(uint32_t ms) override
Guide south for ms milliseconds. South is defined as DEC-.
APRateTableState rateTable
Definition lx200ap_v2.h:153
ISwitchVectorProperty APPECRecordSP
Definition lx200ap_v2.h:164
ISwitchVectorProperty UnparkFromSP
Definition lx200ap_v2.h:136
virtual bool ReadScopeStatus() override
Read telescope status.
ISwitchVectorProperty HomeAndReSyncSP
Definition lx200ap_v2.h:150
virtual bool Disconnect() override
Disconnect from device.
void AstroPhysicsGuideTimeoutWE(bool simul)
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
virtual bool SetSlewRate(int index) override
SetSlewRate Set desired slew rate index.
INumber HourangleCoordsN[2]
Definition lx200ap_v2.h:114
INumberVectorProperty APWormPositionNP
Definition lx200ap_v2.h:156
virtual bool Connect() override
Connect to the device. INDI::DefaultDevice implementation connects to appropriate connection interfac...
virtual bool GuideWE(INDI_DIR_WE dir, TelescopeMotionCommand command)
virtual bool updateTime(ln_date *utc, double utc_offset) override
Update telescope time, date, and UTC offset.
static void simulGuideTimeoutHelperWE(void *p)
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
ISwitch ManualSetParkedS[1]
Definition lx200ap_v2.h:148
INumber APWormPositionN[1]
Definition lx200ap_v2.h:155
virtual const char * getDefaultName() override
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)
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::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
@ ISR_ATMOST1
Definition indiapi.h:172
@ AXIS_DE
@ AXIS_RA
INDI_DIR_WE
@ DIRECTION_EAST
@ DIRECTION_WEST
INDI_DIR_NS
@ DIRECTION_SOUTH
@ DIRECTION_NORTH
void tty_set_generic_udp_format(int enabled)
Definition indicom.c:364
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
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,...)
int IUGetConfigSwitch(const char *dev, const char *property, const char *member, ISState *value)
IUGetConfigSwitch Opens configuration file and reads single switch property.
Definition indidriver.c:657
#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
APPECRecordingState
@ AP_PEC_RECORD_ON
@ AP_PEC_RECORD_OFF
char * trackStateString(INDI::Telescope::TelescopeStatus state)
#define MAX_LX200AP_PULSE_LEN
bool apStatusParked(const char *statusString)
int setAPUTCOffset(int fd, double hours)
int setAPSiteLatitude(int fd, double Lat)
int getAPWormPosition(int fd, int *position)
void set_lx200ap_name(const char *deviceName, unsigned int debug_level)
int setAPRATrackRate(int fd, double rate)
int apHomeAndSync(int fd)
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 isAPInitialized(int fd, bool *isInitialized)
int APSendPulseCmd(int fd, int direction, int duration_msec)
int APParkMount(int fd)
int selectAPSlewRate(int fd, int slewIndex)
const char * apMountStatus(const char *statusString)
int getApStatusString(int fd, char *statusString)
int check_lx200ap_status(int fd, char *parkStatus, char *slewStatus)
int APUnParkMount(int fd)
bool apCanHome(int fd)
int selectAPPECState(int fd, int pecstate)
int selectAPGuideRate(int fd, int guideRate)
APRateTableState apRateTable(const char *statusString)
int setAPObjectAZ(int fd, double az)
int APSyncCM(int fd, char *matchedObject)
int getAPUTCOffset(int fd, double *value)
int setAPDETrackRate(int fd, double rate)
bool apStatusSlewing(const char *statusString)
int setAPSiteLongitude(int fd, double Long)
int selectAPV2CenterRate(int fd, int centerIndex, APRateTableState rateTable)
int selectAPTrackingMode(int fd, int trackMode)
#define setAPBackLashCompensation(fd, x, y, z)
@ AP_RATE_TABLE_DEFAULT
#define getAPVersionNumber(fd, x)
#define setAPClearBuffer(fd)
#define AP_PEC_OFF
#define AP_PEC_ON
#define AP_PEC_ENCODER
#define AP_TRACKING_OFF
#define AP_PEC_RECORD
#define AP_TRACKING_SIDEREAL
int checkLX200EquatorialFormat(int fd)
int abortSlew(int fd)
int setCalenderDate(int fd, int dd, int mm, int yy, bool addSpace)
int Slew(int fd)
#define getLX200DEC(fd, x)
@ 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)
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
char name[MAXINDINAME]
Definition indiapi.h:248