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