Instrument Neutral Distributed Interface INDI 2.2.5
lx200ap_legacy.cpp
Go to the documentation of this file.
1/*
2 Astro-Physics INDI driver
3
4 Copyright (C) 2014 Jasem Mutlaq
5
6 Based on INDI Astrophysics Driver by Markus Wildi
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.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#define MOUNT_TAB "Mount"
37
38/* Constructor */
40{
41 setLX200Capability(LX200_HAS_PULSE_GUIDING);
42 SetTelescopeCapability(GetTelescopeCapability() | TELESCOPE_HAS_PIER_SIDE | TELESCOPE_HAS_PEC | TELESCOPE_CAN_CONTROL_TRACK
43 | TELESCOPE_HAS_TRACK_RATE, 4);
44
45 sendLocationOnStartup = false;
46 sendTimeOnStartup = false;
47}
48
50{
51 return (const char *)"AstroPhysics";
52}
53
55{
57
59
60 IUFillSwitch(&StartUpS[0], "COLD", "Cold", ISS_OFF);
61 IUFillSwitch(&StartUpS[1], "WARM", "Warm", ISS_OFF);
62 IUFillSwitchVector(&StartUpSP, StartUpS, 2, getDeviceName(), "STARTUP", "Mount init.", MAIN_CONTROL_TAB, IP_RW,
64
65 IUFillNumber(&HourangleCoordsN[0], "HA", "HA H:M:S", "%10.6m", 0., 24., 0., 0.);
66 IUFillNumber(&HourangleCoordsN[1], "DEC", "Dec D:M:S", "%10.6m", -90.0, 90.0, 0., 0.);
67 IUFillNumberVector(&HourangleCoordsNP, HourangleCoordsN, 2, getDeviceName(), "HOURANGLE_COORD", "Hourangle Coords",
69
70 IUFillNumber(&HorizontalCoordsN[0], "AZ", "Az D:M:S", "%10.6m", 0., 360., 0., 0.);
71 IUFillNumber(&HorizontalCoordsN[1], "ALT", "Alt D:M:S", "%10.6m", -90., 90., 0., 0.);
73 "Horizontal Coords", MAIN_CONTROL_TAB, IP_RW, 120, IPS_IDLE);
74
75
76 // Max rate is 999.99999X for the GTOCP4.
77 // Using :RR998.9999# just to be safe. 15.041067*998.99999 = 15026.02578
78 TrackRateN[AXIS_RA].min = -15026.0258;
79 TrackRateN[AXIS_RA].max = 15026.0258;
80 TrackRateN[AXIS_DE].min = -998.9999;
81 TrackRateN[AXIS_DE].max = 998.9999;
82
83 // Motion speed of axis when pressing NSWE buttons
84 IUFillSwitch(&SlewRateS[0], "12", "12x", ISS_OFF);
85 IUFillSwitch(&SlewRateS[1], "64", "64x", ISS_ON);
86 IUFillSwitch(&SlewRateS[2], "600", "600x", ISS_OFF);
87 IUFillSwitch(&SlewRateS[3], "1200", "1200x", ISS_OFF);
88 IUFillSwitchVector(&SlewRateSP, SlewRateS, 4, getDeviceName(), "TELESCOPE_SLEW_RATE", "Slew Rate", MOTION_TAB, IP_RW,
90
91 // Slew speed when performing regular GOTO
92 IUFillSwitch(&APSlewSpeedS[0], "600", "600x", ISS_ON);
93 IUFillSwitch(&APSlewSpeedS[1], "900", "900x", ISS_OFF);
94 IUFillSwitch(&APSlewSpeedS[2], "1200", "1200x", ISS_OFF);
96 0, IPS_IDLE);
97
98 IUFillSwitch(&SwapS[0], "NS", "North/South", ISS_OFF);
99 IUFillSwitch(&SwapS[1], "EW", "East/West", ISS_OFF);
100 IUFillSwitchVector(&SwapSP, SwapS, 2, getDeviceName(), "SWAP", "Swap buttons", MOTION_TAB, IP_RW, ISR_1OFMANY, 0,
101 IPS_IDLE);
102
103 IUFillSwitch(&SyncCMRS[USE_REGULAR_SYNC], ":CM#", ":CM#", ISS_OFF);
104 IUFillSwitch(&SyncCMRS[USE_CMR_SYNC], ":CMR#", ":CMR#", ISS_ON);
106 IPS_IDLE);
107
108 // guide speed
109 IUFillSwitch(&APGuideSpeedS[0], "0.25", "0.25x", ISS_OFF);
110 IUFillSwitch(&APGuideSpeedS[1], "0.5", "0.50x", ISS_OFF);
111 IUFillSwitch(&APGuideSpeedS[2], "1.0", "1.0x", ISS_ON);
113 0, IPS_IDLE);
114
115 IUFillText(&VersionT[0], "Number", "", nullptr);
117
118 IUFillText(&DeclinationAxisT[0], "RELHA", "rel. to HA", "undefined");
119 IUFillTextVector(&DeclinationAxisTP, DeclinationAxisT, 1, getDeviceName(), "DECLINATIONAXIS", "Declination axis",
121
122 // Slew threshold
123 IUFillNumber(&SlewAccuracyN[0], "SlewRA", "RA (arcmin)", "%10.6m", 0., 60., 1., 3.0);
124 IUFillNumber(&SlewAccuracyN[1], "SlewDEC", "Dec (arcmin)", "%10.6m", 0., 60., 1., 3.0);
126 IPS_IDLE);
127
129
130 return true;
131}
132
133void LX200AstroPhysics::ISGetProperties(const char *dev)
134{
136
137 /*
138 if (isConnected())
139 {
140 defineProperty(&StartUpSP);
141 defineProperty(&VersionTP);
142
143 //defineProperty(&DeclinationAxisTP);
144
145 // Motion group
146 defineProperty(&APSlewSpeedSP);
147 defineProperty(&SwapSP);
148 defineProperty(&SyncCMRSP);
149 defineProperty(&APGuideSpeedSP);
150 defineProperty(&SlewAccuracyNP);
151
152 LOG_INFO("Please initialize the mount before issuing any command.");
153 }
154 */
155}
156
158{
160
161 if (isConnected())
162 {
165
166 //defineProperty(&DeclinationAxisTP);
167
168 /* Motion group */
174
175 LOG_INFO("Please initialize the mount before issuing any command.");
176 }
177 else
178 {
181 //deleteProperty(DeclinationAxisTP.name);
187 }
188
189 return true;
190}
191
192bool LX200AstroPhysics::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
193{
194 int err = 0;
195
196 // ignore if not ours //
197 if (strcmp(getDeviceName(), dev))
198 return false;
199
200 // ============================================================
201 // Satisfy AP mount initialization, see AP key pad manual p. 76
202 // ============================================================
203 if (!strcmp(name, StartUpSP.name))
204 {
205 int switch_nr;
206
207 IUUpdateSwitch(&StartUpSP, states, names, n);
208
209 if (initStatus == MOUNTNOTINITIALIZED)
210 {
211 if (timeUpdated == false || locationUpdated == false)
212 {
214 LOG_ERROR("Time and location must be set before mount initialization is invoked.");
215 IDSetSwitch(&StartUpSP, nullptr);
216 return false;
217 }
218
219 if (StartUpSP.sp[0].s == ISS_ON) // do it only in case a power on (cold start)
220 {
221 if (setBasicDataPart1() == false)
222 {
224 IDSetSwitch(&StartUpSP, "Cold mount initialization failed.");
225 return false;
226 }
227 }
228
229 initStatus = MOUNTINITIALIZED;
230
231 if (isSimulation())
232 {
233 SlewRateSP.s = IPS_OK;
234 IDSetSwitch(&SlewRateSP, nullptr);
235
237 IDSetSwitch(&APSlewSpeedSP, nullptr);
238
239 IUSaveText(&VersionT[0], "1.0");
241 IDSetText(&VersionTP, nullptr);
242
244 IDSetSwitch(&StartUpSP, "Mount initialized.");
245
246 //currentRA = 0;
247 //currentDEC = 90;
248 }
249 else
250 {
251 // Make sure that the mount is setup according to the properties
252 switch_nr = IUFindOnSwitchIndex(&TrackModeSP);
253
254 if ( (err = selectAPTrackingMode(PortFD, switch_nr)) < 0)
255 {
256 LOGF_ERROR("StartUpSP: Error setting tracking mode (%d).", err);
257 return false;
258 }
259
261
262 // On most mounts SlewRateS defines the MoveTo AND Slew (GOTO) speeds
263 // lx200ap is different - some of the MoveTo speeds are not VALID
264 // Slew speeds so we have to keep two lists.
265 //
266 // SlewRateS is used as the MoveTo speed
267 switch_nr = IUFindOnSwitchIndex(&SlewRateSP);
268 if ( (err = selectAPMoveToRate(PortFD, switch_nr)) < 0)
269 {
270 LOGF_ERROR("StartUpSP: Error setting move rate (%d).", err);
271 return false;
272 }
273
274 SlewRateSP.s = IPS_OK;
275 IDSetSwitch(&SlewRateSP, nullptr);
276
277 // APSlewSpeedsS defines the Slew (GOTO) speeds valid on the AP mounts
279 if ( (err = selectAPSlewRate(PortFD, switch_nr)) < 0)
280 {
281 LOGF_ERROR("StartUpSP: Error setting slew to rate (%d).", err);
282 return false;
283 }
285 IDSetSwitch(&APSlewSpeedSP, nullptr);
286
289
290 // make a IDSet in order the dome controller is aware of the initial values
293
295
296 char versionString[64];
297 getAPVersionNumber(PortFD, versionString);
299 IUSaveText(&VersionT[0], versionString);
300 IDSetText(&VersionTP, nullptr);
301
302 // TODO check controller type here
303 INDI_UNUSED(controllerType);
304 INDI_UNUSED(servoType);
305 //controllerType = ...;
306
308 IDSetSwitch(&StartUpSP, "Mount initialized.");
309
310 }
311 }
312 else
313 {
315 IDSetSwitch(&StartUpSP, "Mount is already initialized.");
316 }
317 return true;
318 }
319
320 // =======================================
321 // Swap Buttons
322 // =======================================
323 if (!strcmp(name, SwapSP.name))
324 {
325 int currentSwap;
326
328 IUUpdateSwitch(&SwapSP, states, names, n);
329 currentSwap = IUFindOnSwitchIndex(&SwapSP);
330
331 if ((!isSimulation() && (err = swapAPButtons(PortFD, currentSwap)) < 0))
332 {
333 LOGF_ERROR("Error swapping buttons (%d).", err);
334 return false;
335 }
336
337 SwapS[0].s = ISS_OFF;
338 SwapS[1].s = ISS_OFF;
339 SwapSP.s = IPS_OK;
340 IDSetSwitch(&SwapSP, nullptr);
341 return true;
342 }
343
344 // ===========================================================
345 // GOTO ("slew") Speed.
346 // ===========================================================
347 if (!strcmp(name, APSlewSpeedSP.name))
348 {
349 IUUpdateSwitch(&APSlewSpeedSP, states, names, n);
350 int slewRate = IUFindOnSwitchIndex(&APSlewSpeedSP);
351
352 if (!isSimulation() && (err = selectAPSlewRate(PortFD, slewRate) < 0))
353 {
354 LOGF_ERROR("Error setting move to rate (%d).", err);
355 return false;
356 }
357
359 IDSetSwitch(&APSlewSpeedSP, nullptr);
360 return true;
361 }
362
363 // ===========================================================
364 // Guide Speed.
365 // ===========================================================
366 if (!strcmp(name, APGuideSpeedSP.name))
367 {
368 IUUpdateSwitch(&APGuideSpeedSP, states, names, n);
369 int guideRate = IUFindOnSwitchIndex(&APGuideSpeedSP);
370
371 if (!isSimulation() && (err = selectAPGuideRate(PortFD, guideRate) < 0))
372 {
373 LOGF_ERROR("Error setting guiding to rate (%d).", err);
374 return false;
375 }
376
378 IDSetSwitch(&APGuideSpeedSP, nullptr);
379 return true;
380 }
381
382 // =======================================
383 // Choose the appropriate sync command
384 // =======================================
385 if (!strcmp(name, SyncCMRSP.name))
386 {
388 IUUpdateSwitch(&SyncCMRSP, states, names, n);
391 IDSetSwitch(&SyncCMRSP, nullptr);
392 return true;
393 }
394
395 // =======================================
396 // Choose the PEC playback mode
397 // =======================================
398 if (!strcmp(name, PECStateSP.name))
399 {
401 IUUpdateSwitch(&PECStateSP, states, names, n);
403
404 int pecstate = IUFindOnSwitchIndex(&PECStateSP);
405
406 if (!isSimulation() && (err = selectAPPECState(PortFD, pecstate) < 0))
407 {
408 LOGF_ERROR("Error setting PEC state (%d).", err);
409 return false;
410 }
411
412 PECStateSP.s = IPS_OK;
413 IDSetSwitch(&PECStateSP, nullptr);
414
415 return true;
416 }
417
418 return LX200Generic::ISNewSwitch(dev, name, states, names, n);
419}
420
421/**************************************************************************************
422**
423***************************************************************************************/
424bool LX200AstroPhysics::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
425{
426 if (strcmp(getDeviceName(), dev))
427 return false;
428
429 // Update slew precision limit
430 if (!strcmp(name, SlewAccuracyNP.name))
431 {
432 if (IUUpdateNumber(&SlewAccuracyNP, values, names, n) < 0)
433 return false;
434
436
437 if (SlewAccuracyN[0].value < 3 || SlewAccuracyN[1].value < 3)
438 IDSetNumber(&SlewAccuracyNP, "Warning: Setting the slew accuracy too low may result in a dead lock");
439
440 IDSetNumber(&SlewAccuracyNP, nullptr);
441 return true;
442 }
443
444 return LX200Generic::ISNewNumber(dev, name, values, names, n);
445}
446
447bool LX200AstroPhysics::isMountInit()
448{
449 return (StartUpSP.s != IPS_IDLE);
450}
451
453{
454 if (!isMountInit())
455 return false;
456
457 if (isSimulation())
458 {
459 mountSim();
460 return true;
461 }
462
464 {
465 EqNP.s = IPS_ALERT;
466 IDSetNumber(&EqNP, "Error reading RA/DEC.");
467 return false;
468 }
469
471 {
472 double dx = targetRA - currentRA;
473 double dy = targetDEC - currentDEC;
474
475 // Wait until acknowledged or within threshold
476 if (fabs(dx) <= (SlewAccuracyN[0].value / (900.0)) && fabs(dy) <= (SlewAccuracyN[1].value / 60.0))
477 {
479 LOG_INFO("Slew is complete. Tracking...");
480 }
481 }
482 else if (TrackState == SCOPE_PARKING)
483 {
484 double currentAlt, currentAz;
485 if (getLX200Az(PortFD, &currentAz) < 0 || getLX200Alt(PortFD, &currentAlt) < 0)
486 {
487 EqNP.s = IPS_ALERT;
488 IDSetNumber(&EqNP, "Error reading Az/Alt.");
489 return false;
490 }
491
492 double dx = GetAxis1Park() - currentAz;
493 double dy = GetAxis2Park() - currentAlt;
494
495 LOGF_DEBUG("Parking... targetAz: %g currentAz: %g dx: %g targetAlt: %g currentAlt: %g dy: %g", GetAxis1Park(),
496 currentAz, dx, GetAxis2Park(), currentAlt, dy);
497
498 if (fabs(dx) <= (SlewAccuracyN[0].value / (60.0)) && fabs(dy) <= (SlewAccuracyN[1].value / 60.0))
499 {
500 LOG_DEBUG("Parking slew is complete. Asking astrophysics mount to park...");
501
502 if (!isSimulation() && setAPPark(PortFD) < 0)
503 {
504 LOG_ERROR("Parking Failed.");
505 return false;
506 }
507
508 SetParked(true);
509 }
510 }
511
513
514 syncSideOfPier();
515
516 return true;
517}
518
519bool LX200AstroPhysics::setBasicDataPart0()
520{
521 int err;
522 //struct ln_date utm;
523 //struct ln_zonedate ltm;
524
525 if (isSimulation() == true)
526 {
527 LOG_INFO("setBasicDataPart0 simulation complete.");
528 return true;
529 }
530
531 if ((err = setAPClearBuffer(PortFD)) < 0)
532 {
533 LOGF_ERROR("Error clearing the buffer (%d): %s", err, strerror(err));
534 return false;
535 }
536
537 if ((err = setAPLongFormat(PortFD)) < 0)
538 {
539 LOGF_ERROR("Error setting long format failed (%d): %s", err, strerror(err));
540 return false;
541 }
542
543 if ((err = setAPBackLashCompensation(PortFD, 0, 0, 0)) < 0)
544 {
545 // It seems we need to send it twice before it works!
546 if ((err = setAPBackLashCompensation(PortFD, 0, 0, 0)) < 0)
547 {
548 LOGF_ERROR("Error setting back lash compensation (%d): %s.", err, strerror(err));
549 return false;
550 }
551 }
552
553 // Detect and set fomat. It should be LONG.
555
556 return true;
557}
558
559bool LX200AstroPhysics::setBasicDataPart1()
560{
561 int err = 0;
562
563 if (InitPark())
564 {
565 // If loading parking data is successful, we just set the default parking values.
566 SetAxis1ParkDefault(LocationN[LOCATION_LATITUDE].value >= 0 ? 0 : 180);
567 SetAxis2ParkDefault(LocationN[LOCATION_LATITUDE].value);
568 }
569 else
570 {
571 // Otherwise, we set all parking data to default in case no parking data is found.
572 SetAxis1Park(LocationN[LOCATION_LATITUDE].value >= 0 ? 0 : 180);
573 SetAxis1ParkDefault(LocationN[LOCATION_LATITUDE].value);
574
575 SetAxis1ParkDefault(LocationN[LOCATION_LATITUDE].value >= 0 ? 0 : 180);
576 SetAxis2ParkDefault(LocationN[LOCATION_LATITUDE].value);
577 }
578
579 // Unpark
580 UnPark();
581
582 // Stop
583 if (!isSimulation() && (err = setAPMotionStop(PortFD)) < 0)
584 {
585 LOGF_ERROR("Stop motion (:Q#) failed, check the mount (%d): %s", err, strerror(err));
586 return false;
587 }
588
589 // AP always track after unpark? Must check
591
592 return true;
593}
594
595bool LX200AstroPhysics::Goto(double r, double d)
596{
597 const struct timespec timeout = {0, 100000000L};
598
599 targetRA = r;
600 targetDEC = d;
601 char RAStr[64], DecStr[64];
602
603 fs_sexa(RAStr, targetRA, 2, 3600);
604 fs_sexa(DecStr, targetDEC, 2, 3600);
605
606 // If moving, let's stop it first.
607 if (EqNP.s == IPS_BUSY)
608 {
609 if (!isSimulation() && abortSlew(PortFD) < 0)
610 {
611 AbortSP.s = IPS_ALERT;
612 IDSetSwitch(&AbortSP, "Abort slew failed.");
613 return false;
614 }
615
616 AbortSP.s = IPS_OK;
617 EqNP.s = IPS_IDLE;
618 IDSetSwitch(&AbortSP, "Slew aborted.");
619 IDSetNumber(&EqNP, nullptr);
620
622 {
625 EqNP.s = IPS_IDLE;
628 IDSetSwitch(&MovementNSSP, nullptr);
629 IDSetSwitch(&MovementWESP, nullptr);
630 }
631
632 // sleep for 100 mseconds
633 nanosleep(&timeout, nullptr);
634 }
635
636 if (!isSimulation())
637 {
639 {
640 EqNP.s = IPS_ALERT;
641 IDSetNumber(&EqNP, "Error setting RA/DEC.");
642 return false;
643 }
644
645 int err = 0;
646
647 /* Slew reads the '0', that is not the end of the slew */
648 if ((err = Slew(PortFD)))
649 {
650 EqNP.s = IPS_ALERT;
651 IDSetNumber(&EqNP, "Error Slewing to JNow RA %s - DEC %s\n", RAStr, DecStr);
652 slewError(err);
653 return false;
654 }
655 }
656
658 //EqNP.s = IPS_BUSY;
659
660 LOGF_INFO("Slewing to RA: %s - DEC: %s", RAStr, DecStr);
661 return true;
662}
663
664
665int LX200AstroPhysics::SendPulseCmd(int8_t direction, uint32_t duration_msec)
666{
667 return APSendPulseCmd(PortFD, direction, duration_msec);
668}
669
670
672{
673 return setBasicDataPart0();
674}
675
677{
678 timeUpdated = false;
679 locationUpdated = false;
680
682}
683
684bool LX200AstroPhysics::Sync(double ra, double dec)
685{
686 char syncString[256];
687
688 int syncType = IUFindOnSwitchIndex(&SyncCMRSP);
689
690 if (!isSimulation())
691 {
692 if (setAPObjectRA(PortFD, ra) < 0 || setAPObjectDEC(PortFD, dec) < 0)
693 {
694 EqNP.s = IPS_ALERT;
695 IDSetNumber(&EqNP, "Error setting RA/DEC. Unable to Sync.");
696 return false;
697 }
698
699 bool syncOK = true;
700
701 switch (syncType)
702 {
703 case USE_REGULAR_SYNC:
704 if (::Sync(PortFD, syncString) < 0)
705 syncOK = false;
706 break;
707
708 case USE_CMR_SYNC:
709 if (APSyncCMR(PortFD, syncString) < 0)
710 syncOK = false;
711 break;
712
713 default:
714 break;
715 }
716
717 if (syncOK == false)
718 {
719 EqNP.s = IPS_ALERT;
720 IDSetNumber(&EqNP, "Synchronization failed.");
721 return false;
722 }
723
724 }
725
726 currentRA = ra;
727 currentDEC = dec;
728
729 LOGF_DEBUG("%s Synchronization successful %s", (syncType == USE_REGULAR_SYNC ? "CM" : "CMR"), syncString);
730 LOG_INFO("Synchronization successful.");
731
732 EqNP.s = IPS_OK;
733
735
736 return true;
737}
738
739bool LX200AstroPhysics::updateTime(ln_date *utc, double utc_offset)
740{
741 struct ln_zonedate ltm;
742
743 if (isSimulation())
744 {
745 timeUpdated = true;
746 return true;
747 }
748
749 ln_date_to_zonedate(utc, &ltm, utc_offset * 3600.0);
750
751 JD = ln_get_julian_day(utc);
752
753 LOGF_DEBUG("New JD is %.2f", JD);
754
755 // Set Local Time
756 if (setLocalTime(PortFD, ltm.hours, ltm.minutes, (int)ltm.seconds) < 0)
757 {
758 LOG_ERROR("Error setting local time.");
759 return false;
760 }
761
762 LOGF_DEBUG("Set Local Time %02d:%02d:%02d is successful.", ltm.hours, ltm.minutes,
763 (int)ltm.seconds);
764
765 if (setCalenderDate(PortFD, ltm.days, ltm.months, ltm.years) < 0)
766 {
767 LOG_ERROR("Error setting local date.");
768 return false;
769 }
770
771 LOGF_DEBUG("Set Local Date %02d/%02d/%02d is successful.", ltm.days, ltm.months, ltm.years);
772
773 if (setAPUTCOffset(PortFD, fabs(utc_offset)) < 0)
774 {
775 LOG_ERROR("Error setting UTC Offset.");
776 return false;
777 }
778
779 LOGF_DEBUG("Set UTC Offset %g (always positive for AP) is successful.", fabs(utc_offset));
780
781 LOG_INFO("Time updated.");
782
783 timeUpdated = true;
784
785 return true;
786}
787
788bool LX200AstroPhysics::updateLocation(double latitude, double longitude, double elevation)
789{
790 INDI_UNUSED(elevation);
791
792 if (isSimulation())
793 {
794 locationUpdated = true;
795 return true;
796 }
797
798 if (!isSimulation() && setAPSiteLongitude(PortFD, 360.0 - longitude) < 0)
799 {
800 LOG_ERROR("Error setting site longitude coordinates");
801 return false;
802 }
803
804 if (!isSimulation() && setAPSiteLatitude(PortFD, latitude) < 0)
805 {
806 LOG_ERROR("Error setting site latitude coordinates");
807 return false;
808 }
809
810 char l[32], L[32];
811 fs_sexa(l, latitude, 3, 3600);
812 fs_sexa(L, longitude, 4, 3600);
813
814 LOGF_INFO("Site location updated to Lat %.32s - Long %.32s", l, L);
815
816 locationUpdated = true;
817
818 return true;
819}
820
821void LX200AstroPhysics::debugTriggered(bool enable)
822{
823 INDI_UNUSED(enable);
826}
827
828
829// For most mounts the SetSlewRate() method sets both the MoveTo and Slew (GOTO) speeds.
830// For AP mounts these two speeds are handled separately - so SetSlewRate() actually sets the MoveTo speed for AP mounts - confusing!
831// ApSetSlew
832bool LX200AstroPhysics::SetSlewRate(int index)
833{
834 if (!isSimulation() && selectAPMoveToRate(PortFD, index) < 0)
835 {
837 IDSetSwitch(&SlewRateSP, "Error setting slew mode.");
838 return false;
839 }
840
841 SlewRateSP.s = IPS_OK;
842 IDSetSwitch(&SlewRateSP, nullptr);
843 return true;
844}
845
847{
848 if (initStatus == MOUNTNOTINITIALIZED)
849 {
850 LOG_WARN("You must initialize the mount before parking.");
851 return false;
852 }
853
854 double parkAz = GetAxis1Park();
855 double parkAlt = GetAxis2Park();
856
857 char AzStr[16], AltStr[16];
858 fs_sexa(AzStr, parkAz, 2, 3600);
859 fs_sexa(AltStr, parkAlt, 2, 3600);
860 LOGF_DEBUG("Parking to Az (%s) Alt (%s)...", AzStr, AltStr);
861
862 if (isSimulation())
863 {
864 INDI::IEquatorialCoordinates equatorialCoords {0, 0};
865 INDI::IHorizontalCoordinates horizontalCoords {parkAz, parkAlt};
866 INDI::HorizontalToEquatorial(&horizontalCoords, &m_Location, ln_get_julian_from_sys(), &equatorialCoords);
867 Goto(equatorialCoords.rightascension, equatorialCoords.declination);
868 }
869 else
870 {
871 if (setAPObjectAZ(PortFD, parkAz) < 0 || setAPObjectAlt(PortFD, parkAlt) < 0)
872 {
873 LOG_ERROR("Error setting Az/Alt.");
874 return false;
875 }
876
877 int err = 0;
878
879 /* Slew reads the '0', that is not the end of the slew */
880 if ((err = Slew(PortFD)))
881 {
882 LOGF_ERROR("Error Slewing to Az %s - Alt %s", AzStr, AltStr);
883 slewError(err);
884 return false;
885 }
886 }
887
888 EqNP.s = IPS_BUSY;
890 LOG_INFO("Parking is in progress...");
891
892 return true;
893}
894
896{
897 // First we unpark astrophysics
898 if (isSimulation() == false)
899 {
900 if (setAPUnPark(PortFD) < 0)
901 {
902 LOG_ERROR("UnParking Failed.");
903 return false;
904 }
905 }
906
907 // Then we sync with to our last stored position
908 double parkAz = GetAxis1Park();
909 double parkAlt = GetAxis2Park();
910
911 char AzStr[16], AltStr[16];
912 fs_sexa(AzStr, parkAz, 2, 3600);
913 fs_sexa(AltStr, parkAlt, 2, 3600);
914 LOGF_DEBUG("Syncing to parked coordinates Az (%s) Alt (%s)...", AzStr, AltStr);
915
916 if (isSimulation())
917 {
918 INDI::IEquatorialCoordinates equatorialCoords {0, 0};
919 INDI::IHorizontalCoordinates horizontalCoords {parkAz, parkAlt};
920 INDI::HorizontalToEquatorial(&horizontalCoords, &m_Location, ln_get_julian_from_sys(), &equatorialCoords);
921
922 currentRA = equatorialCoords.rightascension;
923 currentDEC = equatorialCoords.declination;
924 }
925 else
926 {
927 if (setAPObjectAZ(PortFD, parkAz) < 0 || (setAPObjectAlt(PortFD, parkAlt)) < 0)
928 {
929 LOG_ERROR("Error setting Az/Alt.");
930 return false;
931 }
932
933 char syncString[256];
934 if (APSyncCM(PortFD, syncString) < 0)
935 {
936 LOG_WARN("Sync failed.");
937 return false;
938 }
939 }
940
941 SetParked(false);
942 return true;
943}
944
946{
948 INDI::IHorizontalCoordinates horizontalCoords {0, 0};
949 INDI::EquatorialToHorizontal(&equatorialCoords, &m_Location, ln_get_julian_from_sys(), &horizontalCoords);
950 double parkAZ = horizontalCoords.azimuth;
951 double parkAlt = horizontalCoords.altitude;
952
953 char AzStr[16], AltStr[16];
954 fs_sexa(AzStr, parkAZ, 2, 3600);
955 fs_sexa(AltStr, parkAlt, 2, 3600);
956
957 LOGF_DEBUG("Setting current parking position to coordinates Az (%s) Alt (%s)...", AzStr,
958 AltStr);
959
960 SetAxis1Park(parkAZ);
961 SetAxis2Park(parkAlt);
962
963 return true;
964}
965
967{
968 // Az = 0 for North hemisphere
969 SetAxis1Park(LocationN[LOCATION_LATITUDE].value > 0 ? 0 : 180);
970
971 // Alt = Latitude
972 SetAxis2Park(LocationN[LOCATION_LATITUDE].value);
973
974 return true;
975}
976
977void LX200AstroPhysics::syncSideOfPier()
978{
979 const char *cmd = ":pS#";
980 // Response
981 char response[16] = { 0 };
982 int rc = 0, nbytes_read = 0, nbytes_written = 0;
983
984 LOGF_DEBUG("CMD: <%s>", cmd);
985
986 tcflush(PortFD, TCIOFLUSH);
987
988 if ((rc = tty_write(PortFD, cmd, strlen(cmd), &nbytes_written)) != TTY_OK)
989 {
990 char errmsg[256];
991 tty_error_msg(rc, errmsg, 256);
992 LOGF_ERROR("Error writing to device %s (%d)", errmsg, rc);
993 return;
994 }
995
996 // Read Side
997 if ((rc = tty_read_section(PortFD, response, '#', 3, &nbytes_read)) != TTY_OK)
998 {
999 char errmsg[256];
1000 tty_error_msg(rc, errmsg, 256);
1001 LOGF_ERROR("Error reading from device %s (%d)", errmsg, rc);
1002 return;
1003 }
1004
1005 response[nbytes_read - 1] = '\0';
1006
1007 tcflush(PortFD, TCIOFLUSH);
1008
1009 LOGF_DEBUG("RES: <%s>", response);
1010
1011 if (!strcmp(response, "East"))
1013 else if (!strcmp(response, "West"))
1015 else
1016 LOGF_ERROR("Invalid pier side response from device-> %s", response);
1017
1018}
1019
1021{
1023
1027
1028 return true;
1029}
1030
1031bool LX200AstroPhysics::SetTrackMode(uint8_t mode)
1032{
1033 int err = 0;
1034
1035 if (mode == TRACK_CUSTOM)
1036 {
1038 {
1039 LOGF_ERROR("Error setting tracking mode (%d).", err);
1040 return false;
1041 }
1042
1043 return SetTrackRate(TrackRateN[AXIS_RA].value, TrackRateN[AXIS_DE].value);
1044 }
1045
1046 if (!isSimulation() && (err = selectAPTrackingMode(PortFD, mode)) < 0)
1047 {
1048 LOGF_ERROR("Error setting tracking mode (%d).", err);
1049 return false;
1050 }
1051
1052 return true;
1053}
1054
1055bool LX200AstroPhysics::SetTrackEnabled(bool enabled)
1056{
1058}
1059
1060bool LX200AstroPhysics::SetTrackRate(double raRate, double deRate)
1061{
1062 // Convert to arcsecs/s to AP sidereal multiplier
1063 /*
1064 :RR0.0000# = normal sidereal tracking in RA - similar to :RT2#
1065 :RR+1.0000# = 1 + normal sidereal = 2X sidereal
1066 :RR+9.0000# = 9 + normal sidereal = 10X sidereal
1067 :RR-1.0000# = normal sidereal - 1 = 0 or Stop - similar to :RT9#
1068 :RR-11.0000# = normal sidereal - 11 = -10X sidereal (East at 10X)
1069
1070 :RD0.0000# = normal zero rate for Dec.
1071 :RD5.0000# = 5 + normal zero rate = 5X sidereal clockwise from above - equivalent to South
1072 :RD-5.0000# = normal zero rate - 5 = 5X sidereal counter-clockwise from above - equivalent to North
1073 */
1074
1075 double APRARate = (raRate - TRACKRATE_SIDEREAL) / TRACKRATE_SIDEREAL;
1076 double APDERate = deRate / TRACKRATE_SIDEREAL;
1077
1078 if (!isSimulation())
1079 {
1080 if (setAPRATrackRate(PortFD, APRARate) < 0 || setAPDETrackRate(PortFD, APDERate) < 0)
1081 return false;
1082 }
1083
1084 return true;
1085}
1086
1087bool LX200AstroPhysics::getUTFOffset(double *offset)
1088{
1089 if (isSimulation())
1090 {
1091 *offset = 3;
1092 return true;
1093 }
1094
1095 return (getAPUTCOffset(PortFD, offset) == 0);
1096}
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 bool initProperties() override
Called to initialize basic properties required all the time.
Definition lx200ap.cpp:79
ISwitchVectorProperty StartUpSP
Definition lx200ap.h:103
ISwitchVectorProperty APSlewSpeedSP
Definition lx200ap.h:114
virtual bool UnPark() override
Unpark the telescope if already parked.
Definition lx200ap.cpp:1867
ISwitchVectorProperty APGuideSpeedSP
Definition lx200ap.h:124
ISwitch APSlewSpeedS[3]
Definition lx200ap.h:113
virtual bool SetTrackMode(uint8_t mode) override
SetTrackMode Set active tracking mode. Do not change track state.
Definition lx200ap.cpp:2162
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
Definition lx200ap.cpp:1673
virtual bool SetTrackEnabled(bool enabled) override
SetTrackEnabled Engages or disengages mount tracking. If there are no tracking modes available,...
Definition lx200ap.cpp:2188
virtual bool Goto(double, double) override
Move the scope to the supplied RA and DEC coordinates.
Definition lx200ap.cpp:1138
virtual bool SetTrackRate(double raRate, double deRate) override
SetTrackRate Set custom tracking rates.
Definition lx200ap.cpp:2201
INumber HorizontalCoordsN[2]
Definition lx200ap.h:110
ISwitchVectorProperty SwapSP
Definition lx200ap.h:117
ISwitch APGuideSpeedS[3]
Definition lx200ap.h:123
virtual bool Park() override
Park the telescope to its home position.
Definition lx200ap.cpp:1740
virtual const char * getDefaultName() override
Definition lx200ap.cpp:61
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
Definition lx200ap.cpp:2095
ISwitch SwapS[2]
Definition lx200ap.h:116
ISwitch SyncCMRS[2]
Definition lx200ap.h:119
INumberVectorProperty SlewAccuracyNP
virtual bool Sync(double ra, double dec) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
Definition lx200ap.cpp:1558
IText VersionT[1]
Definition lx200ap.h:135
virtual int SendPulseCmd(int8_t direction, uint32_t duration_msec) override
Definition lx200ap.cpp:1464
ITextVectorProperty VersionTP
Definition lx200ap.h:136
ISwitch StartUpS[2]
Definition lx200ap.h:102
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
Definition lx200ap.cpp:2074
virtual bool Handshake() override
perform handshake with device to check communication
Definition lx200ap.cpp:1469
INumberVectorProperty HorizontalCoordsNP
Definition lx200ap.h:111
virtual bool getUTFOffset(double *offset) override
Definition lx200ap.cpp:2228
INumber SlewAccuracyN[2]
virtual bool Disconnect() override
Disconnect from device.
Definition lx200ap.cpp:1544
virtual bool updateTime(ln_date *utc, double utc_offset) override
Update telescope time, date, and UTC offset.
Definition lx200ap.cpp:1610
INumber HourangleCoordsN[2]
Definition lx200ap.h:105
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
Definition lx200ap.cpp:203
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
Definition lx200ap.cpp:506
ISwitchVectorProperty SyncCMRSP
Definition lx200ap.h:120
virtual void debugTriggered(bool enable) override
Inform driver that the debug option was triggered. This function is called after setDebug is triggere...
Definition lx200ap.cpp:1717
INumberVectorProperty HourangleCoordsNP
Definition lx200ap.h:106
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
Definition lx200ap.cpp:446
ITextVectorProperty DeclinationAxisTP
virtual bool SetSlewRate(int index) override
SetSlewRate Set desired slew rate index.
Definition lx200ap.cpp:1729
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
Definition lx200ap.cpp:2148
virtual void ISGetProperties(const char *dev) override
define the driver's properties to the client. Usually, only a minimum set of properties are defined t...
Definition lx200ap.cpp:176
virtual bool ReadScopeStatus() override
Read telescope status.
Definition lx200ap.cpp:850
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual void slewError(int slewCode)
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
virtual bool 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 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
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 IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#define LOG_DEBUG(txt)
Definition indilogger.h:75
#define LOG_WARN(txt)
Definition indilogger.h:73
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
#define LOG_INFO(txt)
Definition indilogger.h:74
#define TRACKRATE_SIDEREAL
Definition indimacros.h:39
#define getDeviceName
#define MOUNT_TAB
#define MOUNTNOTINITIALIZED
#define MOUNTINITIALIZED
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 APSyncCM(int fd, char *matchedObject)
int getAPUTCOffset(int fd, double *value)
int setAPDETrackRate(int fd, double rate)
int setAPSiteLongitude(int fd, double Long)
int selectAPTrackingMode(int fd, int trackMode)
#define setAPBackLashCompensation(fd, x, y, z)
#define getAPVersionNumber(fd, x)
#define setAPClearBuffer(fd)
#define 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
char name[MAXINDINAME]
Definition indiapi.h:321
char name[MAXINDINAME]
Definition indiapi.h:369
ISState s
Definition indiapi.h:353
char name[MAXINDINAME]
Definition indiapi.h:248