Instrument Neutral Distributed Interface INDI 2.2.5
lx200telescope.cpp
Go to the documentation of this file.
1/*
2 * Standard LX200 implementation.
3
4 Copyright (C) 2003 - 2018 Jasem Mutlaq ([email protected])
5 Minor changes Copyright (C) 2021 James Lancaster
6
7 This library is free software;
8 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;
11 either
12 version 2.1 of the License, or (at your option) any later version.
13
14 This library is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY;
16 without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 Lesser General Public License for more details.
19
20 You should have received a copy of the GNU Lesser General Public
21 License along with this library;
22 if not, write to the Free Software
23 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110 - 1301 USA
24 */
25
26#include "lx200telescope.h"
27
28#include "indicom.h"
29#include "lx200driver.h"
30
31#include <libnova/sidereal_time.h>
32
33#include <cmath>
34#include <memory>
35#include <cstring>
36#include <unistd.h>
37#include <time.h>
38
39/* Simulation Parameters */
40#define LX200_GENERIC_SLEWRATE 5 /* slew rate, degrees/s */
41#define SIDRATE 0.004178 /* sidereal rate, degrees/s */
42
44{
45}
46
52
54{
55 return getDefaultName();
56}
57
59{
60 return static_cast<const char *>("Standard LX200");
61}
62
63
65{
66 /* Make sure to init parent properties first */
68
69 IUFillSwitch(&AlignmentS[0], "Polar", "", ISS_ON);
70 IUFillSwitch(&AlignmentS[1], "AltAz", "", ISS_OFF);
71 IUFillSwitch(&AlignmentS[2], "Land", "", ISS_OFF);
74
75#if 0
76 IUFillSwitch(&SlewRateS[SLEW_GUIDE], "SLEW_GUIDE", "Guide", ISS_OFF);
77 IUFillSwitch(&SlewRateS[SLEW_CENTERING], "SLEW_CENTERING", "Centering", ISS_OFF);
78 IUFillSwitch(&SlewRateS[SLEW_FIND], "SLEW_FIND", "Find", ISS_OFF);
79 IUFillSwitch(&SlewRateS[SLEW_MAX], "SLEW_MAX", "Max", ISS_ON);
80 IUFillSwitchVector(&SlewRateSP, SlewRateS, 4, getDeviceName(), "TELESCOPE_SLEW_RATE", "Slew Rate", MOTION_TAB, IP_RW,
82#endif
83
84#if 0
85 IUFillSwitch(&TrackModeS[0], "TRACK_SIDEREAL", "Sidereal", ISS_ON);
86 IUFillSwitch(&TrackModeS[1], "TRACK_SOLAR", "Solar", ISS_OFF);
87 IUFillSwitch(&TrackModeS[2], "TRACK_LUNAR", "Lunar", ISS_OFF);
88 IUFillSwitch(&TrackModeS[3], "TRACK_CUSTOM", "Custom", ISS_OFF);
89 IUFillSwitchVector(&TrackModeSP, TrackModeS, 4, getDeviceName(), "TELESCOPE_TRACK_MODE", "Track Mode", MOTION_TAB,
91#endif
92
93 AddTrackMode("TRACK_SIDEREAL", "Sidereal", true);
94 AddTrackMode("TRACK_SOLAR", "Solar");
95 AddTrackMode("TRACK_LUNAR", "Lunar");
96 AddTrackMode("TRACK_CUSTOM", "Custom");
97
99 {
100 IUFillNumber(&TrackFreqN[0], "trackFreq", "Freq", "%g", 55, 65, 0.00001, 60.16427);
101 }
102 else
103 {
104 IUFillNumber(&TrackFreqN[0], "trackFreq", "Freq", "%g", 56.4, 60.1, 0.1, 60.1);
105 }
106 IUFillNumberVector(&TrackFreqNP, TrackFreqN, 1, getDeviceName(), "Tracking Frequency", "", MOTION_TAB, IP_RW, 0,
107 IPS_IDLE);
108
109 IUFillSwitch(&UsePulseCmdS[0], "Off", "Off", ISS_OFF);
110 IUFillSwitch(&UsePulseCmdS[1], "On", "On", ISS_ON);
113
114 int selectedSite = 0;
115 IUGetConfigOnSwitchIndex(getDeviceName(), "Sites", &selectedSite);
116 IUFillSwitch(&SiteS[0], "Site 1", "Site 1", selectedSite == 0 ? ISS_ON : ISS_OFF);
117 IUFillSwitch(&SiteS[1], "Site 2", "Site 2", selectedSite == 1 ? ISS_ON : ISS_OFF);
118 IUFillSwitch(&SiteS[2], "Site 3", "Site 3", selectedSite == 2 ? ISS_ON : ISS_OFF);
119 IUFillSwitch(&SiteS[3], "Site 4", "Site 4", selectedSite == 3 ? ISS_ON : ISS_OFF);
121
122 char siteName[64] = {"NA"};
123 IUGetConfigText(getDeviceName(), "Site Name", "Name", siteName, 64);
124 IUFillText(&SiteNameT[0], "Name", "Name", siteName);
125 IUFillTextVector(&SiteNameTP, SiteNameT, 1, getDeviceName(), "Site Name", "Site Name", SITE_TAB, IP_RW, 0, IPS_IDLE);
126
127
129 {
132
133 // IUFillSwitch(&FocusModeS[0], "FOCUS_HALT", "Halt", ISS_ON);
134 // IUFillSwitch(&FocusModeS[1], "FOCUS_SLOW", "Slow", ISS_OFF);
135 // IUFillSwitch(&FocusModeS[2], "FOCUS_FAST", "Fast", ISS_OFF);
136 // IUFillSwitchVector(&FocusModeSP, FocusModeS, 3, getDeviceName(), "FOCUS_MODE", "Mode", FOCUS_TAB, IP_RW,
137 // ISR_1OFMANY, 0, IPS_IDLE);
138
139 // Classical speeds slow or fast
140 FocusSpeedNP[0].setMin(1);
141 FocusSpeedNP[0].setMax(2);
142 FocusSpeedNP[0].setValue(1);
144 }
145
147
149
150 /* Add debug/simulation/config controls so we may debug driver if necessary */
152
154
155 if (m_Location.longitude > 0)
157 if (m_Location.latitude != 0)
158 currentDEC = m_Location.latitude > 0 ? 90 : -90;
159 return true;
160}
161
163{
164 if (dev != nullptr && strcmp(dev, getDeviceName()) != 0)
165 return;
166
168
169 /*
170 if (isConnected())
171 {
172 if (genericCapability & LX200_HAS_ALIGNMENT_TYPE)
173 defineProperty(&AlignmentSP);
174
175 if (genericCapability & LX200_HAS_TRACKING_FREQ)
176 defineProperty(&TrackFreqNP);
177
178 if (genericCapability & LX200_HAS_PULSE_GUIDING)
179 defineProperty(&UsePulseCmdSP);
180
181 if (genericCapability & LX200_HAS_SITES)
182 {
183 defineProperty(&SiteSP);
184 defineProperty(&SiteNameTP);
185 }
186
187 defineProperty(&GuideNSNP);
188 defineProperty(&GuideWENP);
189
190 if (genericCapability & LX200_HAS_FOCUS)
191 {
192 defineProperty(&FocusMotionSP);
193 defineProperty(&FocusTimerNP);
194 defineProperty(&FocusModeSP);
195 }
196 }
197 */
198}
199
260
262{
263 if (isSimulation())
264 return true;
265
266 return (check_lx200_connection(PortFD) == 0);
267}
268
270{
271 return checkConnection();
272}
273
275{
276 return (::isSlewComplete(PortFD) == 1);
277}
278
280{
281 if (!isConnected())
282 return false;
283
284 if (isSimulation())
285 {
286 mountSim();
287 return true;
288 }
289
290 //if (check_lx200_connection(PortFD))
291 //return false;
292
294 {
295 // Check if LX200 is done slewing
296 if (isSlewComplete())
297 {
298 // Set slew mode to "Centering"
302
304 LOG_INFO("Slew is complete. Tracking...");
305 }
306 }
307 else if (TrackState == SCOPE_PARKING)
308 {
309 if (isSlewComplete())
310 {
311 SetParked(true);
312 }
313 }
314
316 {
318 LOG_ERROR("Error reading RA/DEC.");
319 EqNP.apply();
320 return false;
321 }
322
324
325 return true;
326}
327
328bool LX200Telescope::Goto(double ra, double dec)
329{
330 const struct timespec timeout = {0, 100000000L};
331
332 targetRA = ra;
333 targetDEC = dec;
334 char RAStr[64] = {0}, DecStr[64] = {0};
335 int fracbase = 0;
336
337 switch (getLX200EquatorialFormat())
338 {
340 fracbase = 360000;
341 break;
344 default:
345 fracbase = 3600;
346 break;
347 }
348
349 fs_sexa(RAStr, targetRA, 2, fracbase);
350 fs_sexa(DecStr, targetDEC, 2, fracbase);
351
352 // If moving, let's stop it first.
353 if (EqNP.getState() == IPS_BUSY)
354 {
355 if (!isSimulation() && abortSlew(PortFD) < 0)
356 {
358 LOG_ERROR("Abort slew failed.");
359 AbortSP.apply();
360 return false;
361 }
362
365 LOG_ERROR("Slew aborted.");
366 AbortSP.apply();
367 EqNP.apply();
368
370 {
378 }
379
380 // sleep for 100 mseconds
381 nanosleep(&timeout, nullptr);
382 }
383
384 if (!isSimulation())
385 {
387 {
389 LOG_ERROR("Error setting RA/DEC.");
390 EqNP.apply();
391 return false;
392 }
393
394 int err = 0;
395
396 /* Slew reads the '0', that is not the end of the slew */
397 if ((err = Slew(PortFD)))
398 {
399 LOGF_ERROR("Error Slewing to JNow RA %s - DEC %s", RAStr, DecStr);
400 slewError(err);
401 return false;
402 }
403 }
404
406 //EqNP.s = IPS_BUSY;
407
408 LOGF_INFO("Slewing to RA: %s - DEC: %s", RAStr, DecStr);
409
410 return true;
411}
412
413bool LX200Telescope::Sync(double ra, double dec)
414{
415 char syncString[256] = {0};
416
417 if (!isSimulation() && (setObjectRA(PortFD, ra) < 0 || (setObjectDEC(PortFD, dec)) < 0))
418 {
420 LOG_ERROR("Error setting RA/DEC. Unable to Sync.");
421 EqNP.apply();
422 return false;
423 }
424
425 if (!isSimulation() && ::Sync(PortFD, syncString) < 0)
426 {
428 LOG_ERROR("Synchronization failed.");
429 EqNP.apply();
430 return false;
431 }
432
433 currentRA = ra;
434 currentDEC = dec;
435
436 LOG_INFO("Synchronization successful.");
437
439
441
442 return true;
443}
444
446{
447 const struct timespec timeout = {0, 100000000L};
448 if (!isSimulation())
449 {
450 // If scope is moving, let's stop it first.
451 if (EqNP.getState() == IPS_BUSY)
452 {
453 if (!isSimulation() && abortSlew(PortFD) < 0)
454 {
456 LOG_ERROR("Abort slew failed");
457 AbortSP.apply();
458 return false;
459 }
460
463 LOG_ERROR("Slew aborted.");
464 AbortSP.apply();
465 EqNP.apply();
466
468 {
474
477 }
478
479 // sleep for 100 msec
480 nanosleep(&timeout, nullptr);
481 }
482
483 if (!isSimulation() && slewToPark(PortFD) < 0)
484 {
486 LOG_ERROR("Parking Failed.");
487 ParkSP.apply();
488 return false;
489 }
490 }
491
493 LOG_INFO("Parking telescope in progress...");
494 return true;
495}
496
498{
499 int current_move = (dir == DIRECTION_NORTH) ? LX200_NORTH : LX200_SOUTH;
500
501 switch (command)
502 {
503 case MOTION_START:
504 if (!isSimulation() && MoveTo(PortFD, current_move) < 0)
505 {
506 LOG_ERROR("Error setting N/S motion direction.");
507 return false;
508 }
509 else
510 LOGF_DEBUG("Moving toward %s.",
511 (current_move == LX200_NORTH) ? "North" : "South");
512 break;
513
514 case MOTION_STOP:
515 if (!isSimulation() && HaltMovement(PortFD, current_move) < 0)
516 {
517 LOG_ERROR("Error stopping N/S motion.");
518 return false;
519 }
520 else
521 LOGF_DEBUG("Movement toward %s halted.",
522 (current_move == LX200_NORTH) ? "North" : "South");
523 break;
524 }
525
526 return true;
527}
528
530{
531 int current_move = (dir == DIRECTION_WEST) ? LX200_WEST : LX200_EAST;
532
533 switch (command)
534 {
535 case MOTION_START:
536 if (!isSimulation() && MoveTo(PortFD, current_move) < 0)
537 {
538 LOG_ERROR("Error setting W/E motion direction.");
539 return false;
540 }
541 else
542 LOGF_DEBUG("Moving toward %s.", (current_move == LX200_WEST) ? "West" : "East");
543 break;
544
545 case MOTION_STOP:
546 if (!isSimulation() && HaltMovement(PortFD, current_move) < 0)
547 {
548 LOG_ERROR("Error stopping W/E motion.");
549 return false;
550 }
551 else
552 LOGF_DEBUG("Movement toward %s halted.",
553 (current_move == LX200_WEST) ? "West" : "East");
554 break;
555 }
556
557 return true;
558}
559
561{
562 if (!isSimulation() && abortSlew(PortFD) < 0)
563 {
564 LOG_ERROR("Failed to abort slew.");
565 return false;
566 }
567
569 {
572 GuideWENP[AXIS_RA].setValue(0);
573 GuideWENP[AXIS_DE].setValue(0);
574 GuideNSNP[AXIS_RA].setValue(0);
575 GuideNSNP[AXIS_DE].setValue(0);
576
577 if (GuideNSTID)
578 {
580 GuideNSTID = 0;
581 }
582
583 if (GuideWETID)
584 {
586 GuideNSTID = 0;
587 }
588
589 LOG_INFO("Guide aborted.");
592
593 return true;
594 }
595
596 return true;
597}
598
599bool LX200Telescope::setLocalDate(uint8_t days, uint8_t months, uint16_t years)
600{
601 return (setCalenderDate(PortFD, days, months, years) == 0);
602}
603
604bool LX200Telescope::setLocalTime24(uint8_t hour, uint8_t minute, uint8_t second)
605{
606 return (setLocalTime(PortFD, hour, minute, second) == 0);
607}
608
610{
611 return (::setUTCOffset(PortFD, (offset * -1.0)) == 0);
612}
613
614bool LX200Telescope::updateTime(ln_date *utc, double utc_offset)
615{
616 struct ln_zonedate ltm;
617
618 if (isSimulation())
619 return true;
620
621 ln_date_to_zonedate(utc, &ltm, utc_offset * 3600.0);
622
623 JD = ln_get_julian_day(utc);
624
625 LOGF_DEBUG("New JD is %.2f", JD);
626
627 // Meade defines UTC Offset as the offset ADDED to local time to yield UTC, which
628 // is the opposite of the standard definition of UTC offset!
629 if (setUTCOffset(utc_offset) == false)
630 {
631 LOG_ERROR("Error setting UTC Offset.");
632 return false;
633 }
634
635 // Set Local Time
636 if (setLocalTime24(ltm.hours, ltm.minutes, ltm.seconds) == false)
637 {
638 LOG_ERROR("Error setting local time.");
639 return false;
640 }
641
642 if (setLocalDate(ltm.days, ltm.months, ltm.years) == false)
643 {
644 LOG_ERROR("Error setting local date.");
645 return false;
646 }
647
648 LOG_INFO("Time updated, updating planetary data...");
649 return true;
650}
651
652bool LX200Telescope::updateLocation(double latitude, double longitude, double elevation)
653{
654 INDI_UNUSED(elevation);
655
656 // JM 2021-04-10: MUST convert from INDI longitude to standard longitude.
657 // DO NOT REMOVE
658 if (longitude > 180)
659 longitude = longitude - 360;
660
661 if (!isSimulation())
662 {
663 if (setSiteLongitude(PortFD, longitude) < 0)
664 {
665 LOG_ERROR("Error setting site longitude coordinates");
666 return false;
667 }
668
669 if (setSiteLatitude(PortFD, latitude) < 0)
670 {
671 LOG_ERROR("Error setting site latitude coordinates");
672 return false;
673 }
674 }
675
676 char l[MAXINDINAME] = {0}, L[MAXINDINAME] = {0};
677 fs_sexa(l, latitude, 2, 36000);
678 fs_sexa(L, longitude, 2, 36000);
679
680 // Choose WGS 84, also known as EPSG:4326 for latitude/longitude ordering
681 LOGF_INFO("Site location in the mount updated to Latitude %.12s (%g) Longitude %.12s (%g) (Longitude sign in carthography format)",
682 l, latitude, L, longitude);
683
684 return true;
685}
686
687bool LX200Telescope::ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n)
688{
689 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
690 {
691 if (!strcmp(name, SiteNameTP.name))
692 {
693 if (!isSimulation() && setSiteName(PortFD, texts[0], currentSiteNum) < 0)
694 {
696 IDSetText(&SiteNameTP, "Setting site name");
697 return false;
698 }
699
701 IText *tp = IUFindText(&SiteNameTP, names[0]);
702 IUSaveText(tp, texts[0]);
703 IDSetText(&SiteNameTP, "Site name updated");
704 return true;
705 }
706 }
707
708 return INDI::Telescope::ISNewText(dev, name, texts, names, n);
709}
710
711bool LX200Telescope::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
712{
713 // Check focuser interface
714 if (FI::processNumber(dev, name, values, names, n))
715 return true;
716 // Check guider interface
717 if (GI::processNumber(dev, name, values, names, n))
718 return true;
719
720 // Check rest of LX200Telescope properties
721 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
722 {
723 // Update Frequency
724 if (!strcmp(name, TrackFreqNP.name))
725 {
726 LOGF_DEBUG("Trying to set track freq of: %04.1f", values[0]);
728 {
729 if (!isSimulation() && setPreciseTrackFreq(PortFD, values[0]) < 0)
730 {
732 IDSetNumber(&TrackFreqNP, "Error setting tracking frequency");
733 return false;
734 }
736 TrackFreqNP.np[0].value = values[0];
737 IDSetNumber(&TrackFreqNP, "Tracking frequency set to %8.5f", values[0]);
738 }
739 else
740 {
741 //Normal Tracking Frequency
742 if (!isSimulation() && setTrackFreq(PortFD, values[0]) < 0)
743
744 LOGF_DEBUG("Trying to set track freq of: %f\n", values[0]);
745 if (!isSimulation() && setTrackFreq(PortFD, values[0]) < 0)
746 {
747 LOGF_DEBUG("Trying to set track freq of: %f\n", values[0]);
748 if (!isSimulation() && setTrackFreq(PortFD, values[0]) < 0)
749 {
751 IDSetNumber(&TrackFreqNP, "Error setting tracking frequency");
752 return false;
753 }
755 IDSetNumber(&TrackFreqNP, "Error setting tracking frequency");
756 return false;
757 }
758
760 TrackFreqNP.np[0].value = values[0];
761 IDSetNumber(&TrackFreqNP, "Tracking frequency set to %04.1f", values[0]);
762 }
763
765 {
774 }
775
776 return true;
777 }
778 }
779
780 // if we didn't process it, continue up the chain, let somebody else
781 // give it a shot
782 return INDI::Telescope::ISNewNumber(dev, name, values, names, n);
783}
784
785bool LX200Telescope::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
786{
787 // Focuser Interface
788 if (FI::processSwitch(dev, name, states, names, n))
789 return true;
790
791 if (dev != nullptr && strcmp(dev, getDeviceName()) == 0)
792 {
793 // Alignment
794 if (!strcmp(name, AlignmentSP.name))
795 {
796 if (IUUpdateSwitch(&AlignmentSP, states, names, n) < 0)
797 return false;
798
799 int index = IUFindOnSwitchIndex(&AlignmentSP);
800
801 if (!isSimulation() && setAlignmentMode(PortFD, index) < 0)
802 {
804 IDSetSwitch(&AlignmentSP, "Error setting alignment mode.");
805 return false;
806 }
807
809 IDSetSwitch(&AlignmentSP, nullptr);
810 return true;
811 }
812
813 // Sites
814 if (!strcmp(name, SiteSP.name))
815 {
816 if (IUUpdateSwitch(&SiteSP, states, names, n) < 0)
817 return false;
818
820
822 {
824 IDSetSwitch(&SiteSP, "Error selecting sites.");
825 return false;
826 }
827 char siteName[64] = {0};
828 if (isSimulation())
829 {
830 IUSaveText(&SiteNameTP.tp[0], "Sample Site");
831 }
832 else
833 {
835 IUSaveText(&SiteNameT[0], siteName);
836 }
839
841 SiteSP.s = IPS_OK;
842
843 IDSetText(&SiteNameTP, nullptr);
844 IDSetSwitch(&SiteSP, nullptr);
845
846 return false;
847 }
848
849 // Pulse-Guide command support
850 if (!strcmp(name, UsePulseCmdSP.name))
851 {
853 IUUpdateSwitch(&UsePulseCmdSP, states, names, n);
854
856 IDSetSwitch(&UsePulseCmdSP, nullptr);
858 LOGF_INFO("Pulse guiding is %s.", usePulseCommand ? "enabled" : "disabled");
859 return true;
860 }
861 }
862
863 // Nobody has claimed this, so pass it to the parent
864 return INDI::Telescope::ISNewSwitch(dev, name, states, names, n);
865}
866
868{
869 if (isSimulation())
870 return true;
871
872 bool rc = (selectTrackingMode(PortFD, mode) == 0);
873
874 // Only update tracking frequency if it is defined and not deleted by child classes
875 // Note, that LX200_HAS_PRECISE_TRACKING_FREQ can use the same get function
877 {
878 getTrackFreq(PortFD, &TrackFreqN[0].value);
879 IDSetNumber(&TrackFreqNP, nullptr);
880 }
881 return rc;
882}
883
885{
886 // Convert index to Meade format
887 index = 3 - index;
888
889 if (!isSimulation() && setSlewMode(PortFD, index) < 0)
890 {
891 LOG_ERROR("Error setting slew mode.");
892 return false;
893 }
894
895 return true;
896}
897
899{
900 if (SlewRateSP.findOnSwitchIndex() == index)
901 return true;
902
903 if (!isSimulation() && setSlewMode(PortFD, 3 - index) < 0)
904 {
906 LOG_ERROR("Error setting slew mode.");
908 return false;
909 }
910
912 SlewRateSP[index].setState(ISS_ON);
915 return true;
916}
917
919{
920 static_cast<LX200Telescope *>(p)->updateFocusTimer();
921}
922
924{
925 // switch (FocusTimerNP.s)
926 // {
927 // case IPS_IDLE:
928 // break;
929
930 // case IPS_BUSY:
931 // //if (isDebug())
932 // //IDLog("Focus Timer Value is %g\n", FocusTimerN[0].value);
933
934 // FocusTimerN[0].value -= 50;
935
936 // if (FocusTimerN[0].value <= 0)
937 // {
938 // //if (isDebug())
939 // //IDLog("Focus Timer Expired\n");
940
941 // if (!isSimulation() && setFocuserSpeedMode(PortFD, 0) < 0)
942 // {
943 // FocusModeSP.s = IPS_ALERT;
944 // IDSetSwitch(&FocusModeSP, "Error setting focuser mode.");
945
946 // //if (isDebug())
947 // //IDLog("Error setting focuser mode\n");
948
949 // return;
950 // }
951
952 // FocusMotionSP.setState(IPS_IDLE);
953 // FocusTimerNP.s = IPS_OK;
954 // FocusModeSP.s = IPS_OK;
955
956 // FocusMotionSP.reset();
957 // IUResetSwitch(&FocusModeSP);
958 // FocusModeS[0].s = ISS_ON;
959
960 // IDSetSwitch(&FocusModeSP, nullptr);
961 // FocusMotionSP.apply();
962 // }
963
964 // FocusTimerNP.apply();
965
966 // if (FocusTimerN[0].value > 0)
967 // IEAddTimer(50, LX200Telescope::updateFocusHelper, this);
968 // break;
969
970 // case IPS_OK:
971 // break;
972
973 // case IPS_ALERT:
974 // break;
975 // }
976
977 AbortFocuser();
979 FocusTimerNP[0].setValue(0);
981}
982
984{
985 static struct timeval ltv;
986 struct timeval tv;
987 double dt = 0, da = 0, dx = 0;
988 int nlocked = 0;
989
990 /* update elapsed time since last poll, don't presume exactly POLLMS */
991 gettimeofday(&tv, nullptr);
992
993 if (ltv.tv_sec == 0 && ltv.tv_usec == 0)
994 ltv = tv;
995
996 dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec) / 1e6;
997 ltv = tv;
998 da = LX200_GENERIC_SLEWRATE * dt;
999
1000 /* Process per current state. We check the state of EQUATORIAL_COORDS and act accordingly */
1001 switch (TrackState)
1002 {
1003
1004 case SCOPE_IDLE:
1005 currentRA += (TRACKRATE_SIDEREAL / 3600.0 * dt / 15.);
1006 break;
1007
1008 case SCOPE_TRACKING:
1010 {
1011 case TRACK_SIDEREAL:
1012 da = 0;
1013 dx = 0;
1014 break;
1015
1016 case TRACK_LUNAR:
1017 da = ((TRACKRATE_LUNAR - TRACKRATE_SIDEREAL) / 3600.0 * dt / 15.);
1018 dx = 0;
1019 break;
1020
1021 case TRACK_SOLAR:
1022 da = ((TRACKRATE_SOLAR - TRACKRATE_SIDEREAL) / 3600.0 * dt / 15.);
1023 dx = 0;
1024 break;
1025
1026 case TRACK_CUSTOM:
1027 da = ((TrackRateNP[AXIS_RA].getValue() - TRACKRATE_SIDEREAL) / 3600.0 * dt / 15.);
1028 dx = (TrackRateNP[AXIS_DE].getValue() / 3600.0 * dt);
1029 break;
1030
1031 }
1032
1033 currentRA += da;
1034 currentDEC += dx;
1035 break;
1036
1037 case SCOPE_SLEWING:
1038 case SCOPE_PARKING:
1039 /* slewing - nail it when both within one pulse @ LX200_GENERIC_SLEWRATE */
1040 nlocked = 0;
1041
1042 dx = targetRA - currentRA;
1043
1044 if (fabs(dx) <= da)
1045 {
1047 nlocked++;
1048 }
1049 else if (dx > 0)
1050 currentRA += da / 15.;
1051 else
1052 currentRA -= da / 15.;
1053
1054 dx = targetDEC - currentDEC;
1055 if (fabs(dx) <= da)
1056 {
1058 nlocked++;
1059 }
1060 else if (dx > 0)
1061 currentDEC += da;
1062 else
1063 currentDEC -= da;
1064
1065 if (nlocked == 2)
1066 {
1069 else
1070 SetParked(true);
1071 }
1072
1073 break;
1074
1075 default:
1076 break;
1077 }
1078
1080}
1081
1083{
1084 if (!isSimulation())
1085 {
1087
1089 getAlignment();
1090
1091 // Only check time format if it is not already initialized by the class
1093 {
1094 if (getTimeFormat(PortFD, &timeFormat) < 0)
1095 LOG_ERROR("Failed to retrieve time format from device.");
1096 else
1097 {
1098 timeFormat = (timeFormat == 24) ? LX200_24 : LX200_AM;
1099 // We always do 24 hours
1100 if (timeFormat != LX200_24)
1101 {
1102 // Toggle format and suppress gcc warning
1103 int rc = toggleTimeFormat(PortFD);
1104 INDI_UNUSED(rc);
1105 }
1106 }
1107 }
1108
1110 {
1111 char siteName[64] = {0};
1112 if (getSiteName(PortFD, siteName, currentSiteNum) < 0)
1113 {
1114 LOG_ERROR("Failed to get site name from device");
1115 }
1116 else
1117 {
1118 IUSaveText(&SiteNameT[0], siteName);
1119 IDSetText(&SiteNameTP, nullptr);
1120 }
1121 }
1122
1123
1125 {
1126 if (getTrackFreq(PortFD, &TrackFreqN[0].value) < 0)
1127 LOG_ERROR("Failed to get tracking frequency from device.");
1128 else
1129 IDSetNumber(&TrackFreqNP, nullptr);
1130 }
1131
1132 }
1133
1137 sendScopeTime();
1138}
1139
1141{
1142 if (slewCode == 1)
1143 LOG_ERROR("Object below horizon.");
1144 else if (slewCode == 2)
1145 LOG_ERROR("Object below the minimum elevation limit.");
1146 else
1147 LOGF_ERROR("Slew failed (%d).", slewCode);
1148
1150 EqNP.apply();
1151}
1152
1154{
1155 signed char align = ACK(PortFD);
1156 if (align < 0)
1157 {
1158 IDSetSwitch(&AlignmentSP, "Failed to get telescope alignment.");
1159 return;
1160 }
1161
1162 AlignmentS[0].s = ISS_OFF;
1163 AlignmentS[1].s = ISS_OFF;
1164 AlignmentS[2].s = ISS_OFF;
1165
1166 switch (align)
1167 {
1168 case 'P':
1169 AlignmentS[0].s = ISS_ON;
1170 break;
1171 case 'A':
1172 AlignmentS[1].s = ISS_ON;
1173 break;
1174 case 'L':
1175 AlignmentS[2].s = ISS_ON;
1176 break;
1177 }
1178
1180 IDSetSwitch(&AlignmentSP, nullptr);
1181}
1182
1183bool LX200Telescope::getLocalTime(char *timeString)
1184{
1185 if (isSimulation())
1186 {
1187 time_t now = time (nullptr);
1188 strftime(timeString, MAXINDINAME, "%T", localtime(&now));
1189 }
1190 else
1191 {
1192 double ctime = 0;
1193 int h, m, s;
1194 if (getLocalTime24(PortFD, &ctime) < 0)
1195 return false;
1196 getSexComponents(ctime, &h, &m, &s);
1197 snprintf(timeString, MAXINDINAME, "%02d:%02d:%02d", h, m, s);
1198 }
1199
1200 return true;
1201}
1202
1203bool LX200Telescope::getLocalDate(char *dateString)
1204{
1205 if (isSimulation())
1206 {
1207 time_t now = time (nullptr);
1208 strftime(dateString, MAXINDINAME, "%F", localtime(&now));
1209 }
1210 else
1211 {
1212 if (getCalendarDate(PortFD, dateString) < 0)
1213 return false;
1214 }
1215
1216 return true;
1217}
1218
1220{
1221 if (isSimulation())
1222 {
1223 *offset = 3;
1224 return true;
1225 }
1226
1227 int lx200_utc_offset = 0;
1228 if (getUTCOffset(PortFD, &lx200_utc_offset) < 0)
1229 return false;
1230 // LX200 TimeT Offset is defined at the number of hours added to LOCAL TIME to get TimeT. This is contrary to the normal definition.
1231 *offset = lx200_utc_offset * -1;
1232 return true;
1233}
1234
1236{
1237 char cdate[MAXINDINAME] = {0};
1238 char ctime[MAXINDINAME] = {0};
1239 struct tm ltm;
1240 struct tm utm;
1241
1242 memset(&ltm, 0, sizeof(ltm));
1243 memset(&utm, 0, sizeof(utm));
1244
1245 time_t time_epoch;
1246
1247 double offset = 0;
1248 if (getUTFOffset(&offset) == false)
1249 {
1250 LOG_WARN("Could not obtain UTC offset from mount!");
1251 return false;
1252 }
1253
1254 if (getLocalTime(ctime) == false)
1255 {
1256 LOG_WARN("Could not obtain local time from mount!");
1257 return false;
1258 }
1259
1260 if (getLocalDate(cdate) == false)
1261 {
1262 LOG_WARN("Could not obtain local date from mount!");
1263 return false;
1264 }
1265
1266 // To ISO 8601 format in LOCAL TIME!
1267 char datetime[MAXINDINAME] = {0};
1268 snprintf(datetime, MAXINDINAME, "%sT%s", cdate, ctime);
1269
1270 // Now that date+time are combined, let's get tm representation of it.
1271 if (strptime(datetime, "%FT%T", &ltm) == nullptr)
1272 {
1273 LOGF_WARN("Could not process mount date and time: %s", datetime);
1274 return false;
1275 }
1276
1277 // Assume no daylight savings always
1278 int isdst = 0;
1279
1280 // Try to get whether daylight saving time is toggled.
1281 // By default we assume it's inactive.
1282 // N.B. This only works for LX200 Autostar II so shouldn't be used in base class.
1283 //getDaylightSaving(PortFD, &isdst);
1284
1285 ltm.tm_isdst = isdst;
1286
1287 // A mount without a battery backed clock boots with a placeholder date (e.g. 2000-01-01).
1288 // Do not advertise it as valid time, otherwise clients may adopt it. Leave TIME_UTC idle
1289 // until a client sets the time.
1290 if (ltm.tm_year + 1900 < MIN_VALID_MOUNT_YEAR)
1291 {
1292 LOGF_WARN("Mount clock is not set (%s local). Waiting for time to be set by the client.", datetime);
1294 TimeTP.apply();
1295 return false;
1296 }
1297
1298 // Get local time epoch in UNIX seconds
1299 time_epoch = mktime(&ltm);
1300
1301 // LOCAL to UTC by subtracting offset.
1302 time_epoch -= static_cast<int>(offset * 3600.0);
1303
1304 // Get UTC (we're using localtime_r, but since we shifted time_epoch above by UTCOffset, we should be getting the real UTC time)
1305 localtime_r(&time_epoch, &utm);
1306
1307 // Format it into the final UTC ISO 8601
1308 strftime(cdate, MAXINDINAME, "%Y-%m-%dT%H:%M:%S", &utm);
1309 TimeTP[UTC].setText(cdate);
1310
1311 char utcStr[8] = {0};
1312 snprintf(utcStr, 8, "%.2f", offset);
1313 TimeTP[OFFSET].setText(utcStr);
1314
1315 LOGF_DEBUG("Mount controller UTC Time: %s", TimeTP[UTC].getText());
1316 LOGF_DEBUG("Mount controller UTC Offset: %s", TimeTP[OFFSET].getText());
1317
1318 // Let's send everything to the client
1320 TimeTP.apply();
1321
1322 return true;
1323}
1324
1326{
1327 int lat_dd = 0, lat_mm = 0, long_dd = 0, long_mm = 0;
1328 double lat_ssf = 0.0, long_ssf = 0.0;
1329 char lat_sexagesimal[MAXINDIFORMAT];
1330 char lng_sexagesimal[MAXINDIFORMAT];
1331
1332 if (isSimulation())
1333 {
1334 LocationNP[LOCATION_LATITUDE].setValue(29.5);
1335 LocationNP[LOCATION_LONGITUDE].setValue(48.0);
1336 LocationNP[LOCATION_ELEVATION].setValue(10);
1338 LocationNP.apply( );
1339 return true;
1340 }
1341
1342 if (getSiteLatitude(PortFD, &lat_dd, &lat_mm, &lat_ssf) < 0)
1343 {
1344 LOG_WARN("Failed to get site latitude from device.");
1345 return false;
1346 }
1347 else
1348 {
1349 double value = 0;
1350 snprintf(lat_sexagesimal, MAXINDIFORMAT, "%02d:%02d:%02.1lf", lat_dd, lat_mm, lat_ssf);
1351 f_scansexa(lat_sexagesimal, &value);
1352 LocationNP[LOCATION_LATITUDE].setValue(value);
1353 }
1354
1355 if (getSiteLongitude(PortFD, &long_dd, &long_mm, &long_ssf) < 0)
1356 {
1357 LOG_WARN("Failed to get site longitude from device.");
1358 return false;
1359 }
1360 else
1361 {
1362 double value = 0;
1363 snprintf(lng_sexagesimal, MAXINDIFORMAT, "%02d:%02d:%02.1lf", long_dd, long_mm, long_ssf);
1364 f_scansexa(lng_sexagesimal, &value);
1365 value = range360(value);
1366 LocationNP[LOCATION_LONGITUDE].setValue(value);
1367 fs_sexa(lng_sexagesimal, LocationNP[LOCATION_LONGITUDE].getValue(), 2, 36000);
1368 }
1369
1370 LOGF_INFO("Mount has Latitude %s (%g) Longitude (0 to +360 Eastwards) %s (%g)",
1371 lat_sexagesimal,
1372 LocationNP[LOCATION_LATITUDE].getValue(),
1373 lng_sexagesimal,
1374 LocationNP[LOCATION_LONGITUDE].getValue());
1375
1376 LocationNP.apply();
1377
1378 saveConfig(true, "GEOGRAPHIC_COORD");
1379
1380 return true;
1381}
1382
1384{
1386 {
1387 LOG_ERROR("Cannot guide while slewing or parking in progress. Stop first.");
1388 return IPS_ALERT;
1389 }
1390
1391 // If we're using pulse command, then MovementXXX should NOT be active at all.
1393 {
1394 LOG_ERROR("Cannot pulse guide while manually in motion. Stop first.");
1395 return IPS_ALERT;
1396 }
1397
1398 if (GuideNSTID)
1399 {
1401 GuideNSTID = 0;
1402 }
1403
1404 if (usePulseCommand)
1405 {
1407 }
1408 else
1409 {
1411
1412 ISState states[] = { ISS_ON, ISS_OFF };
1414 ISNewSwitch(MovementNSSP.getDeviceName(), MovementNSSP.getName(), states, const_cast<char **>(names), 2);
1415 }
1416
1418 GuideNSTID = IEAddTimer(static_cast<int>(ms), guideTimeoutHelperNS, this);
1419 return IPS_BUSY;
1420}
1421
1423{
1425 {
1426 LOG_ERROR("Cannot guide while slewing or parking in progress. Stop first.");
1427 return IPS_ALERT;
1428 }
1429
1430 // If we're using pulse command, then MovementXXX should NOT be active at all.
1432 {
1433 LOG_ERROR("Cannot pulse guide while manually in motion. Stop first.");
1434 return IPS_ALERT;
1435 }
1436
1437 if (GuideNSTID)
1438 {
1440 GuideNSTID = 0;
1441 }
1442
1443 if (usePulseCommand)
1444 {
1446 }
1447 else
1448 {
1450
1451 ISState states[] = { ISS_OFF, ISS_ON };
1453 ISNewSwitch(MovementNSSP.getDeviceName(), MovementNSSP.getName(), states, const_cast<char **>(names), 2);
1454 }
1455
1457 GuideNSTID = IEAddTimer(static_cast<int>(ms), guideTimeoutHelperNS, this);
1458 return IPS_BUSY;
1459}
1460
1462{
1464 {
1465 LOG_ERROR("Cannot guide while slewing or parking in progress. Stop first.");
1466 return IPS_ALERT;
1467 }
1468
1469 // If we're using pulse command, then MovementXXX should NOT be active at all.
1471 {
1472 LOG_ERROR("Cannot pulse guide while manually in motion. Stop first.");
1473 return IPS_ALERT;
1474 }
1475
1476 if (GuideWETID)
1477 {
1479 GuideWETID = 0;
1480 }
1481
1482 if (usePulseCommand)
1483 {
1485 }
1486 else
1487 {
1489
1490 ISState states[] = { ISS_OFF, ISS_ON };
1491 const char *names[] = { MovementWESP[DIRECTION_WEST].getName(), MovementWESP[DIRECTION_EAST].getName()};
1492 ISNewSwitch(MovementWESP.getDeviceName(), MovementWESP.getName(), states, const_cast<char **>(names), 2);
1493 }
1494
1496 GuideWETID = IEAddTimer(static_cast<int>(ms), guideTimeoutHelperWE, this);
1497 return IPS_BUSY;
1498}
1499
1501{
1503 {
1504 LOG_ERROR("Cannot guide while slewing or parking in progress. Stop first.");
1505 return IPS_ALERT;
1506 }
1507
1508 // If we're using pulse command, then MovementXXX should NOT be active at all.
1510 {
1511 LOG_ERROR("Cannot pulse guide while manually in motion. Stop first.");
1512 return IPS_ALERT;
1513 }
1514
1515 if (GuideWETID)
1516 {
1518 GuideWETID = 0;
1519 }
1520
1521 if (usePulseCommand)
1522 {
1524 }
1525 else
1526 {
1528
1529 ISState states[] = { ISS_ON, ISS_OFF };
1530 const char *names[] = { MovementWESP[DIRECTION_WEST].getName(), MovementWESP[DIRECTION_EAST].getName()};
1531 ISNewSwitch(MovementWESP.getDeviceName(), MovementWESP.getName(), states, const_cast<char **>(names), 2);
1532 }
1533
1535 GuideWETID = IEAddTimer(static_cast<int>(ms), guideTimeoutHelperWE, this);
1536 return IPS_BUSY;
1537}
1538
1539int LX200Telescope::SendPulseCmd(int8_t direction, uint32_t duration_msec)
1540{
1541 return ::SendPulseCmd(PortFD, direction, duration_msec);
1542}
1543
1545{
1546 static_cast<LX200Telescope *>(p)->guideTimeoutNS();
1547}
1548
1550{
1551 static_cast<LX200Telescope *>(p)->guideTimeoutWE();
1552}
1553
1555{
1556 if (usePulseCommand == false)
1557 {
1558 ISState states[] = { ISS_OFF, ISS_OFF };
1559 const char *names[] = { MovementWESP[DIRECTION_WEST].getName(), MovementWESP[DIRECTION_EAST].getName()};
1560 ISNewSwitch(MovementWESP.getDeviceName(), MovementWESP.getName(), states, const_cast<char **>(names), 2);
1561 }
1562
1563 GuideWENP[DIRECTION_WEST].setValue(0);
1564 GuideWENP[DIRECTION_EAST].setValue(0);
1566 GuideWETID = 0;
1567 GuideWENP.apply();
1568}
1569
1571{
1572 if (usePulseCommand == false)
1573 {
1574 ISState states[] = { ISS_OFF, ISS_OFF };
1576 ISNewSwitch(MovementNSSP.getDeviceName(), MovementNSSP.getName(), states, const_cast<char **>(names), 2);
1577 }
1578
1579 GuideNSNP[0].setValue(0);
1580 GuideNSNP[1].setValue(0);
1582 GuideNSTID = 0;
1583 GuideNSNP.apply();
1584
1585}
1586
1599
1601{
1602 INDI_UNUSED(enabled);
1603 return true;
1604}
1605
1607{
1608 return SetFocuserSpeed(0);
1609}
1610
1611IPState LX200Telescope::MoveFocuser(FocusDirection dir, int speed, uint16_t duration)
1612{
1613 FocusDirection finalDirection = dir;
1614 // Reverse final direction if necessary
1615 if (FocusReverseSP[INDI_ENABLED].getState() == ISS_ON)
1616 finalDirection = (dir == FOCUS_INWARD) ? FOCUS_OUTWARD : FOCUS_INWARD;
1617
1618 SetFocuserSpeed(speed);
1619
1620 setFocuserMotion(PortFD, finalDirection);
1621
1623
1624 return IPS_BUSY;
1625}
1626
1628{
1629 return (setFocuserSpeedMode(PortFD, speed) == 0);
1630}
1631
bool isConnected() const
INDI::PropertyText getText(const char *name) const
virtual bool saveConfig(bool silent=false, const char *property=nullptr)
Save the current properties in a configuration file.
void setDriverInterface(uint32_t value)
setInterface Set driver interface. By default the driver interface is set to GENERAL_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 addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
uint32_t getDriverInterface() const
Provides interface to implement focuser functionality.
INDI::PropertySwitch FocusReverseSP
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
void SetCapability(uint32_t cap)
FI::SetCapability sets the focuser capabilities. All capabilities must be initialized.
INDI::PropertyNumber FocusSpeedNP
INDI::PropertyNumber FocusTimerNP
void initProperties(const char *groupName)
Initialize focuser properties. It is recommended to call this function within initProperties() of you...
bool saveConfigItems(FILE *fp)
saveConfigItems save focuser properties defined in the interface in config file
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Process focus number properties.
bool processSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
Process focus switch properties.
INDI::PropertyNumber GuideNSNP
bool updateProperties()
updateProperties Define or Delete Rotator properties based on the connection status of the base devic...
INDI::PropertyNumber GuideWENP
void initProperties(const char *groupName)
Initialize guider properties. It is recommended to call this function within initProperties() of your...
bool processNumber(const char *dev, const char *name, double values[], char *names[], int n)
Call this function whenever client updates GuideNSNP or GuideWSP properties in the primary device....
void setState(IPState state)
const char * getDeviceName() const
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
const char * getName() const
TelescopeStatus TrackState
virtual bool initProperties() override
Called to initialize basic properties required all the time.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
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 int AddTrackMode(const char *name, const char *label, bool isDefault=false)
AddTrackMode.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual void SetParked(bool isparked)
SetParked Change the mount parking status. The data park file (stored in ~/.indi/ParkData....
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
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.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
ISwitch UsePulseCmdS[2]
virtual bool Sync(double ra, double dec) override
Set the telescope current RA and DEC coordinates to the supplied RA and DEC coordinates.
uint32_t genericCapability
int8_t guide_direction_we
static void guideTimeoutHelperNS(void *p)
INumber TrackFreqN[1]
@ LX200_HAS_PRECISE_TRACKING_FREQ
virtual IPState GuideWest(uint32_t ms) override
Guide west for ms milliseconds. West is defined as RA-.
ITextVectorProperty SiteNameTP
virtual void slewError(int slewCode)
bool updateSlewRate(int index)
virtual bool SetSlewRate(int index) override
SetSlewRate Set desired slew rate index.
static void updateFocusHelper(void *p)
virtual bool getLocalDate(char *dateString)
virtual bool Park() override
Park the telescope to its home position.
virtual bool updateTime(ln_date *utc, double utc_offset) override
Update telescope time, date, and UTC offset.
static constexpr int MIN_VALID_MOUNT_YEAR
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
ISwitchVectorProperty AlignmentSP
virtual bool MoveNS(INDI_DIR_NS dir, TelescopeMotionCommand command) override
Start or Stop the telescope motion in the direction dir.
virtual IPState GuideSouth(uint32_t ms) override
Guide south for ms milliseconds. South is defined as DEC-.
virtual bool getUTFOffset(double *offset)
virtual bool sendScopeLocation()
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
virtual bool isSlewComplete()
virtual bool Abort() override
Abort any telescope motion including tracking if possible.
virtual bool updateProperties() override
Called when connected state changes, to add/remove properties.
virtual const char * getDefaultName() override
static void guideTimeoutHelperWE(void *p)
virtual bool ReadScopeStatus() override
Read telescope status.
virtual bool getLocalTime(char *timeString)
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.
INumberVectorProperty TrackFreqNP
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 ReverseFocuser(bool enabled) override
ReverseFocuser Reverse focuser motion direction.
virtual bool ISNewText(const char *dev, const char *name, char *texts[], char *names[], int n) override
Process the client newSwitch command.
virtual bool checkConnection()
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
virtual bool sendScopeTime()
virtual bool setLocalDate(uint8_t days, uint8_t months, uint16_t years)
virtual bool setLocalTime24(uint8_t hour, uint8_t minute, uint8_t second)
virtual bool AbortFocuser() override
AbortFocuser all focus motion.
virtual IPState GuideEast(uint32_t ms) override
Guide east for ms milliseconds. East is defined as RA+.
virtual void getBasicData()
virtual bool Goto(double ra, double dec) override
Move the scope to the supplied RA and DEC coordinates.
virtual bool setUTCOffset(double offset)
ISwitch AlignmentS[3]
virtual bool SetTrackMode(uint8_t mode) override
SetTrackMode Set active tracking mode. Do not change track state.
virtual IPState GuideNorth(uint32_t ms) override
Guide north for ms milliseconds. North is defined as DEC+.
virtual IPState MoveFocuser(FocusDirection dir, int speed, uint16_t duration) override
MoveFocuser the focuser in a particular direction with a specific speed for a finite duration.
virtual bool SetFocuserSpeed(int speed) override
SetFocuserSpeed Set Focuser speed.
virtual bool Handshake() override
perform handshake with device to check communication
virtual int SendPulseCmd(int8_t direction, uint32_t duration_msec)
ISwitchVectorProperty SiteSP
virtual const char * getDriverName() override
int8_t guide_direction_ns
ISwitchVectorProperty UsePulseCmdSP
const char * GUIDE_TAB
GUIDE_TAB Where all the properties for guiding are located.
const char * MAIN_CONTROL_TAB
MAIN_CONTROL_TAB Where all the primary controls for the device are located.
const char * MOTION_TAB
MOTION_TAB Where all the motion control properties of the device are located.
const char * SITE_TAB
SITE_TAB Where all site information setting are located.
const char * FOCUS_TAB
FOCUS_TAB Where all the properties for focuser are located.
void IERmTimer(int timerid)
Remove the timer with the given timerid, as returned from IEAddTimer() or IEAddPeriodicTimer().
Definition eventloop.c:606
int IEAddTimer(int millisecs, IE_TCF *fp, void *p)
Register a new single-shot timer function, fp, to be called with ud as argument after ms.
Definition eventloop.c:586
INDI::PropertySwitch ParkSP
INDI::PropertySwitch AbortSP
INDI::PropertyNumber TrackRateNP
INDI::PropertySwitch MovementNSSP
INDI::PropertyNumber EqNP
INDI::PropertySwitch SlewRateSP
INDI::PropertySwitch TrackModeSP
INDI::PropertyText TimeTP
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
#define MAXINDIFORMAT
Definition indiapi.h:193
@ IP_RW
Definition indiapi.h:184
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_1OFMANY
Definition indiapi.h:171
#define MAXINDINAME
Definition indiapi.h:189
@ AXIS_DE
@ AXIS_RA
INDI_DIR_WE
@ DIRECTION_EAST
@ DIRECTION_WEST
INDI_DIR_NS
@ DIRECTION_SOUTH
@ DIRECTION_NORTH
int f_scansexa(const char *str0, double *dp)
convert sexagesimal string str AxBxC to double. x can be anything non-numeric. Any missing A,...
Definition indicom.c:194
void getSexComponents(double value, int *d, int *m, int *s)
Definition indicom.c:243
double range360(double r)
range360 Limits an angle to be between 0-360 degrees.
Definition indicom.c:1321
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.
double get_local_sidereal_time(double longitude)
get_local_sidereal_time Returns local sidereal time given longitude and system clock.
void IUSaveConfigSwitch(FILE *fp, const ISwitchVectorProperty *svp)
Add a switch vector property value to the configuration file.
Definition indidevapi.c:33
IText * IUFindText(const ITextVectorProperty *tvp, const char *name)
Find an IText member in a vector text property.
Definition indidevapi.c:265
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 IUGetConfigText(const char *dev, const char *property, const char *member, char *value, int len)
IUGetConfigText Opens configuration file and reads single text property.
Definition indidriver.c:908
int IUGetConfigOnSwitchIndex(const char *dev, const char *property, int *index)
IUGetConfigOnSwitchIndex Opens configuration file and reads single switch property to find ON switch ...
Definition indidriver.c:719
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#define LOGF_WARN(fmt,...)
Definition indilogger.h:81
#define LOG_WARN(txt)
Definition indilogger.h:73
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
#define LOG_INFO(txt)
Definition indilogger.h:74
#define TRACKRATE_SOLAR
Definition indimacros.h:45
#define TRACKRATE_SIDEREAL
Definition indimacros.h:39
#define TRACKRATE_LUNAR
Definition indimacros.h:48
#define getDeviceName
int setFocuserMotion(int fd, int motionType)
int setAlignmentMode(int fd, unsigned int alignMode)
int setSlewMode(int fd, int slewMode)
int getSiteLongitude(int fd, int *ddd, int *mm, double *ssf)
int selectTrackingMode(int fd, int trackMode)
int getSiteName(int fd, char *siteName, int siteNum)
int selectSite(int fd, int siteNum)
int setObjectRA(int fd, double ra, bool addSpace)
int getLX200EquatorialFormat()
int setSiteLongitude(int fd, double CartographicLongitude, bool addSpace)
int setObjectDEC(int fd, double dec, bool addSpace)
int checkLX200EquatorialFormat(int fd)
int abortSlew(int fd)
int setSiteLatitude(int fd, double Lat, bool addSpace)
int setTrackFreq(int fd, double trackF)
int setFocuserSpeedMode(int fd, int speedMode)
int setCalenderDate(int fd, int dd, int mm, int yy, bool addSpace)
int getCalendarDate(int fd, char *date)
int Slew(int fd)
int MoveTo(int fd, int direction)
int getTimeFormat(int fd, int *format)
int getTrackFreq(int fd, double *value)
int setPreciseTrackFreq(int fd, double trackF)
int setSiteName(int fd, char *siteName, int siteNum)
void setLX200Debug(const char *deviceName, unsigned int debug_level)
int check_lx200_connection(int in_fd)
int HaltMovement(int fd, int direction)
int getSiteLatitude(int fd, int *dd, int *mm, double *ssf)
#define getUTCOffset(fd, x)
#define getLX200DEC(fd, x)
@ LX200_AM
Definition lx200driver.h:65
@ LX200_24
Definition lx200driver.h:64
#define getLX200RA(fd, x)
@ LX200_TRACK_MANUAL
@ LX200_WEST
Definition lx200driver.h:42
@ LX200_SOUTH
Definition lx200driver.h:44
@ LX200_NORTH
Definition lx200driver.h:41
@ LX200_EAST
Definition lx200driver.h:43
@ LX200_EQ_LONGER_FORMAT
Definition lx200driver.h:52
@ LX200_EQ_SHORT_FORMAT
Definition lx200driver.h:50
@ LX200_EQ_LONG_FORMAT
Definition lx200driver.h:51
#define getLocalTime24(fd, x)
#define toggleTimeFormat(fd)
#define slewToPark(fd)
#define LX200_GENERIC_SLEWRATE
One text descriptor.
char name[MAXINDINAME]
Definition indiapi.h:321
double value
Definition indiapi.h:288
char name[MAXINDINAME]
Definition indiapi.h:369
char name[MAXINDINAME]
Definition indiapi.h:248
#define ACK
Definition stvdriver.h:35