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