Instrument Neutral Distributed Interface INDI 2.2.5
celestrongps.cpp
Go to the documentation of this file.
1#if 0
3Copyright (C) 2003 - 2017 Jasem Mutlaq ([email protected])
4
5This library is free software;
6you can redistribute it and / or
7modify it under the terms of the GNU Lesser General Public
8License as published by the Free Software Foundation;
9either
10version 2.1 of the License, or (at your option) any later version.
11
12This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY;
14without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16Lesser General Public License for more details.
17
18You should have received a copy of the GNU Lesser General Public
19License along with this library;
20if not, write to the Free Software
21Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110 - 1301 USA
22
23Version with experimental pulse guide support. GC 04.12.2015
24
25#endif
26
27#include "celestrongps.h"
28
29#include "indicom.h"
30
31#include <libnova/transform.h>
32
33#include <cmath>
34#include <memory>
35#include <cstring>
36#include <unistd.h>
37
38#include <sys/stat.h>
39
40#include "indilogger.h"
41#include "indiutility.h"
42
43//#include <time.h>
44
45// Simulation Parameters
46#define GOTO_RATE 5 // slew rate, degrees/s
47#define SLEW_RATE 0.5 // slew rate, degrees/s
48#define FINE_SLEW_RATE 0.1 // slew rate, degrees/s
49#define GOTO_LIMIT 5.5 // Move at GOTO_RATE until distance from target is GOTO_LIMIT degrees
50#define SLEW_LIMIT 1 // Move at SLEW_LIMIT until distance from target is SLEW_LIMIT degrees
51#define FINE_SLEW_LIMIT 0.5 // Move at FINE_SLEW_RATE until distance from target is FINE_SLEW_LIMIT degrees
52
53#define MOUNTINFO_TAB "Mount Info"
54
55static std::unique_ptr<CelestronGPS> telescope(new CelestronGPS());
56
58{
59 setVersion(3, 6); // update libindi/drivers.xml as well
60
61
62 fwInfo.Version = "Invalid";
63 fwInfo.controllerVersion = 0;
65 fwInfo.isGem = false;
66 fwInfo.hasFocuser = false;
67
68 INDI::Logger::getInstance().addDebugLevel("Scope Verbose", "SCOPE");
69
70 currentRA = 0;
71 currentDEC = 90;
72 currentAZ = 0;
73 currentALT = 0;
74 targetAZ = 0;
75 targetALT = 0;
76
77 // focuser
79
80 focusTrueMax = 0;
81 focusTrueMin = 0xffffffff;
82
83 // Set minimum properties.
84 // ISGetProperties in INDI::Telescope checks for CanGOTO which must be set.
86}
87
88bool CelestronGPS::checkMinVersion(double minVersion, const char *feature, bool debug)
89{
90 if (((fwInfo.controllerVariant == ISSTARSENSE) &&
91 (fwInfo.controllerVersion < MINSTSENSVER)) ||
92 ((fwInfo.controllerVariant == ISNEXSTAR) &&
93 (fwInfo.controllerVersion < minVersion)))
94 {
95 if (debug)
96 LOGF_DEBUG("Firmware v%3.2f does not support %s. Minimum required version is %3.2f",
97 fwInfo.controllerVersion, feature, minVersion);
98 else
99 LOGF_WARN("Firmware v%3.2f does not support %s. Minimum required version is %3.2f",
100 fwInfo.controllerVersion, feature, minVersion);
101
102 return false;
103 }
104 return true;
105}
106
108{
109 return "Celestron GPS";
110}
111
113{
116
117 // Firmware
118 IUFillText(&FirmwareT[FW_MODEL], "Model", "", nullptr);
119 IUFillText(&FirmwareT[FW_VERSION], "HC Version", "", nullptr);
120 IUFillText(&FirmwareT[FW_RA], "Ra Version", "", nullptr);
121 IUFillText(&FirmwareT[FW_DEC], "Dec Version", "", nullptr);
122 IUFillText(&FirmwareT[FW_ISGEM], "Mount Type", "", nullptr);
123 IUFillText(&FirmwareT[FW_CAN_AUX], "Guide Method", "", nullptr);
124 IUFillText(&FirmwareT[FW_HAS_FOC], "Has Focuser", "", nullptr);
125 IUFillTextVector(&FirmwareTP, FirmwareT, 7, getDeviceName(), "Firmware Info", "", MOUNTINFO_TAB, IP_RO, 0,
126 IPS_IDLE);
127
128 // Celestron Track Modes are Off, AltAz, EQ N, EQ S and Ra and Dec (StarSense only)
129 // off is not provided as these are used to set the track mode when tracking is enabled
130 // may be required for set up, value will be read from the mount if possible
131 IUFillSwitchVector(&CelestronTrackModeSP, CelestronTrackModeS, 4, getDeviceName(), "CELESTRON_TRACK_MODE", "Track Mode",
133 IUFillSwitch(&CelestronTrackModeS[0], "MODE_ALTAZ", "Alt Az", ISS_OFF);
134 IUFillSwitch(&CelestronTrackModeS[1], "MODE_EQ_N", "EQ N", ISS_ON);
135 IUFillSwitch(&CelestronTrackModeS[2], "MODE_EQ_S", "EQ S", ISS_OFF);
136 IUFillSwitch(&CelestronTrackModeS[3], "MODE_RA_DEC", "Ra and Dec", ISS_OFF);
137
138 // INDI track modes are sidereal, solar and lunar
139 AddTrackMode("TRACK_SIDEREAL", "Sidereal", true);
140 AddTrackMode("TRACK_SOLAR", "Solar");
141 AddTrackMode("TRACK_LUNAR", "Lunar");
142
143 IUFillSwitch(&UseHibernateS[0], "Enable", "", ISS_OFF);
144 IUFillSwitch(&UseHibernateS[1], "Disable", "", ISS_ON);
147
148 //GUIDE Define "Use Pulse Cmd" property (Switch).
149 // IUFillSwitch(&UsePulseCmdS[0], "Off", "", ISS_OFF);
150 // IUFillSwitch(&UsePulseCmdS[1], "On", "", ISS_ON);
151 // IUFillSwitchVector(&UsePulseCmdSP, UsePulseCmdS, 2, getDeviceName(), "Use Pulse Cmd", "", MAIN_CONTROL_TAB, IP_RW,
152 // ISR_1OFMANY, 0, IPS_IDLE);
153
154 // experimental last align control
155 IUFillSwitchVector(&LastAlignSP, LastAlignS, 1, getDeviceName(), "Align", "Align", MAIN_CONTROL_TAB,
157 IUFillSwitch(&LastAlignS[0], "Align", "Align", ISS_OFF);
158 // maybe a second switch which confirms the align
159
161
162 //GUIDE Initialize guiding properties.
164
169 IUFillNumber(&GuideRateN[AXIS_RA], "GUIDE_RATE_WE", "W/E Rate", "%0.2f", 0, 1, 0.1, GuideRateN[AXIS_RA].value);
170 IUFillNumber(&GuideRateN[AXIS_DE], "GUIDE_RATE_NS", "N/S Rate", "%0.2f", 0, 1, 0.1, GuideRateN[AXIS_DE].value);
171 IUFillNumberVector(&GuideRateNP, GuideRateN, 2, getDeviceName(), "GUIDE_RATE", "Guide Rate x sidereal", GUIDE_TAB, IP_RW, 0,
172 IPS_IDLE);
173
177
178 IUFillSwitch(&PecControlS[PEC_Seek], "PEC_SEEK_INDEX", "Seek Index", ISS_OFF);
179 IUFillSwitch(&PecControlS[PEC_Stop], "PEC_STOP", "Stop", ISS_OFF);
180 IUFillSwitch(&PecControlS[PEC_Playback], "PEC_PLAYBACK", "Playback", ISS_OFF);
181 IUFillSwitch(&PecControlS[PEC_Record], "PEC_RECORD", "Record", ISS_OFF);
182 IUFillSwitchVector(&PecControlSP, PecControlS, 4, getDeviceName(), "PEC_CONTROL", "PEC Control", MOTION_TAB, IP_RW,
183 ISR_ATMOST1, 60, IPS_IDLE);
184
185 IUFillText(&PecInfoT[0], "PEC_STATE", "Pec State", "undefined");
186 IUFillText(&PecInfoT[1], "PEC_INDEX", "Pec Index", " ");
187 IUFillTextVector(&PecInfoTP, PecInfoT, 2, getDeviceName(), "PEC_INFO", "Pec Info", MOTION_TAB, IP_RO, 60, IPS_IDLE);
188
189 // load Pec data from file
190 IUFillText(&PecFileNameT[0], "PEC_FILE_NAME", "File Name", "");
191 IUFillTextVector(&PecFileNameTP, PecFileNameT, 1, getDeviceName(), "PEC_LOAD", "Load PEC", MOTION_TAB, IP_WO, 60, IPS_IDLE);
192
196
197 IUFillSwitch(&DSTSettingS[0], "DST_ENABLED", "Enabled", ISS_OFF);
198 IUFillSwitchVector(&DSTSettingSP, DSTSettingS, 1, getDeviceName(), "DST_STATE", "DST", SITE_TAB, IP_RW, ISR_NOFMANY, 60,
199 IPS_IDLE);
200
202
203 //GUIDE Set guider interface.
205
206 //FocuserInterface
207 //Initial, these will be updated later.
208 FocusRelPosNP[0].setMin(0.);
209 FocusRelPosNP[0].setMax(30000.);
210 FocusRelPosNP[0].setValue(0);
211 FocusRelPosNP[0].setStep(1000);
212 FocusAbsPosNP[0].setMin(0.);
213 FocusAbsPosNP[0].setMax(60000.);
214 FocusAbsPosNP[0].setValue(0);
215 FocusAbsPosNP[0].setStep(1000);
216
217 // Maximum Position Settings, will be read from the hardware
218 FocusMaxPosNP[0].setMax(60000);
219 FocusMaxPosNP[0].setMin(1000);
220 FocusMaxPosNP[0].setValue(60000);
222
223 // Focuser backlash
224 // CR this is a value, positive or negative to define the direction. It is implemented
225 // in the driver.
226
227 FocusBacklashNP[0].setMin(-1000);
228 FocusBacklashNP[0].setMax(1000);
229 FocusBacklashNP[0].setStep(1);
230 FocusBacklashNP[0].setValue(0);
231 // IUFillNumber(&FocusBacklashN[0], "STEPS", "Steps", "%.f", -500., 500, 1., 0.);
232 // IUFillNumberVector(&FocusBacklashNP, FocusBacklashN, 1, getDeviceName(), "FOCUS_BACKLASH", "Backlash",
233 // FOCUS_TAB, IP_RW, 0, IPS_IDLE);
234
235 // Focuser min limit, read from the hardware
236 // IUFillNumber(&FocusMinPosN[0], "FOCUS_MIN_VALUE", "Steps", "%.f", 0, 40000., 1., 0.);
237 // IUFillNumberVector(&FocusMinPosNP, FocusMinPosN, 1, getDeviceName(), "FOCUS_MIN", "Min. Position",
238 // FOCUS_TAB, IP_RO, 0, IPS_IDLE);
239
240 return true;
241}
242
243void CelestronGPS::ISGetProperties(const char *dev)
244{
245 static bool configLoaded = false;
246
247 if (dev != nullptr && strcmp(dev, getDeviceName()) != 0)
248 return;
249
251
254 if (configLoaded == false)
255 {
256 configLoaded = true;
257 loadConfig(true, "Hibernate");
258 }
259}
260
262{
263 if (isConnected())
264 {
266
267 if (driver.get_firmware(&fwInfo))
268 {
269 IUSaveText(&FirmwareT[FW_MODEL], fwInfo.Model.c_str());
270 IUSaveText(&FirmwareT[FW_VERSION], fwInfo.Version.c_str());
271 IUSaveText(&FirmwareT[FW_RA], fwInfo.RAFirmware.c_str());
272 IUSaveText(&FirmwareT[FW_DEC], fwInfo.DEFirmware.c_str());
273 IUSaveText(&FirmwareT[FW_ISGEM], fwInfo.isGem ? "GEM" : "Fork");
274 canAuxGuide = (atof(fwInfo.RAFirmware.c_str()) >= 6.12 && atof(fwInfo.DEFirmware.c_str()) >= 6.12);
275 IUSaveText(&FirmwareT[FW_CAN_AUX], canAuxGuide ? "Mount" : "Time Guide");
276 IUSaveText(&FirmwareT[FW_HAS_FOC], fwInfo.hasFocuser ? "True" : "False");
277
278 if (!fwInfo.isGem)
279 {
282 }
283
284 usePreciseCoords = (checkMinVersion(2.2, "usePreciseCoords"));
285 // set the default switch index, will be updated from the mount if possible
287 }
288 else
289 {
290 fwInfo.Version = "Invalid";
291 LOG_WARN("Failed to retrieve firmware information.");
292 }
293
294 // JM 2018-09-28: According to user reports in this thread:
295 // http://www.indilib.org/forum/mounts/2208-celestron-avx-mount-and-starsense.html
296 // Parking is also supported fine with StarSense
297 if (checkMinVersion(2.3, "park"))
298 cap |= TELESCOPE_CAN_PARK;
299
300 if (checkMinVersion(4.1, "sync"))
301 cap |= TELESCOPE_CAN_SYNC;
302
303 if (checkMinVersion(2.3, "updating time and location settings"))
304 {
306 }
307
308 // changing track mode (aka rate) is only available for equatorial mounts
309
310 // StarSense supports track mode
311 if (checkMinVersion(2.3, "track on/off"))
313 else
314 LOG_WARN("Mount firmware does not support track on off.");
315
316 if (fwInfo.isGem && checkMinVersion(4.15, "Pier Side", true))
318 else
319 LOG_WARN("Mount firmware does not support getting pier side.");
320
321 // Track Mode (t) is only supported for 2.3+
323 if (checkMinVersion(2.3, "track mode"))
324 {
325 if (isSimulation())
326 {
327 if (isParked())
329 else
331 }
332 if (driver.get_track_mode(&ctm))
333 {
334 if (ctm != CTM_OFF)
335 {
336 fwInfo.celestronTrackMode = ctm;
338 CelestronTrackModeS[ctm - 1].s = ISS_ON;
340
341 saveConfig(true, "CELESTRON_TRACK_MODE");
342 LOGF_DEBUG("Celestron mount tracking, mode %s", CelestronTrackModeS[ctm - 1].label);
343 }
344 else
345 {
346 LOG_INFO("Mount tracking is off.");
348 }
349 }
350 else
351 {
352 LOG_DEBUG("get_track_mode failed");
354 }
355
357 }
358
359 // JM 2025.09.19: Celestron Track Mode (EQ-N, EQ-S, ALTAZ) is not related
360 // to TELESCOPE_HAS_TRACK_MODE which is about track *rates* like Lunar, Solar, Sidereal..etc
361 // if (fwInfo.celestronTrackMode != CTM_ALTAZ)
362 // cap |= TELESCOPE_HAS_TRACK_MODE;
363 // else
364 // {
365 // TrackModeSP[TRACK_SIDEREAL].setState(ISS_ON);
366 // LOG_WARN("Mount firmware does not support track mode.");
367 // }
368
370
372
373 if (fwInfo.Version != "Invalid")
375
376 if (InitPark())
377 {
378 // If loading parking data is successful, we just set the default parking values.
379 SetAxis1ParkDefault(LocationNP[LOCATION_LATITUDE].getValue() >= 0 ? 0 : 180);
381 }
382 else
383 {
384 // Otherwise, we set all parking data to default in case no parking data is found.
385 SetAxis1Park(LocationNP[LOCATION_LATITUDE].getValue() >= 0 ? 0 : 180);
387 SetAxis1ParkDefault(LocationNP[LOCATION_LATITUDE].getValue() >= 0 ? 0 : 180);
389 }
390
391 // InitPark sets TrackState to IDLE or PARKED so this is the earliest we can
392 // update TrackState using the current mount properties
393 // Something seems to set IsParked to true, force the correct state if the
394 // mount is tracking
395 if (ctm != CTM_OFF)
396 {
397 SetParked(false);
399 }
400
401 //GUIDE Update properties.
402 // check if the mount type and version supports guiding
403 // Only show the guide information for mounts that
404 // support guiding. That's GEMs and fork mounts in equatorial modes.
405 // well, anything in an equatorial mode
409 {
411 uint8_t rate;
412 if (driver.get_guide_rate(CELESTRON_AXIS::RA_AXIS, &rate))
413 {
414 GuideRateN[AXIS_RA].value = std::min(std::max(static_cast<double>(rate) / 255.0, 0.0), 1.0);
415 LOGF_DEBUG("Get Guide Rate: RA %f", GuideRateN[AXIS_RA].value);
416 if (driver.get_guide_rate(CELESTRON_AXIS::DEC_AXIS, &rate))
417 {
418 GuideRateN[AXIS_DE].value = std::min(std::max(static_cast<double>(rate) / 255.0, 0.0), 1.0);
419 IDSetNumber(&GuideRateNP, nullptr);
420 LOGF_DEBUG("Get Guide Rate: Dec %f", GuideRateN[AXIS_DE].value);
421 }
422 }
423 else
424 LOG_DEBUG("Unable to get guide rates from mount.");
425
427
428 LOG_INFO("Mount supports guiding.");
429 }
430 else
431 LOG_INFO("Mount does not support guiding. Tracking mode must be set in handset to either EQ-North or EQ-South.");
432
433
435
436 // JM 2014-04-14: User (davidw) reported AVX mount serial communication times out issuing "h" command with firmware 5.28
437 // JM 2018-09-27: User (suramara) reports that it works with AVX mount with Star Sense firmware version 1.19
438 //if (fwInfo.controllerVersion >= 2.3 && fwInfo.Model != "AVX" && fwInfo.Model != "CGE Pro")
439 if (checkMinVersion(2.3, "date and time setting"))
440 {
441 double utc_offset;
442 int yy, dd, mm, hh, minute, ss;
443 bool dst;
444 // StarSense doesn't seems to handle the precise time commands
445 bool precise = fwInfo.controllerVersion >= 5.28;
446 if (driver.get_utc_date_time(&utc_offset, &yy, &mm, &dd, &hh, &minute, &ss, &dst, precise))
447 {
448 char isoDateTime[32];
449 char utcOffset[8];
450
451 snprintf(isoDateTime, 32, "%04d-%02d-%02dT%02d:%02d:%02d", yy, mm, dd, hh, minute, ss);
452 snprintf(utcOffset, 8, "%4.2f", utc_offset);
453
454 TimeTP[UTC].setText(isoDateTime);
455 TimeTP[OFFSET].setText(utcOffset);
456
457 defineProperty(&DSTSettingSP);
458 DSTSettingS[0].s = dst ? ISS_ON : ISS_OFF;
459
460 LOGF_INFO("Mount UTC offset: %s. UTC time: %s. DST: %s", utcOffset, isoDateTime, dst ? "On" : "Off");
461 //LOGF_DEBUG("Mount UTC offset is %s. UTC time is %s", utcOffset, isoDateTime);
462
464 TimeTP.apply();
465 IDSetSwitch(&DSTSettingSP, nullptr);
466 }
467 double longitude, latitude;
468 if (driver.get_location(&longitude, &latitude))
469 {
470 LocationNP[LOCATION_LATITUDE].setValue(latitude);
471 LocationNP[LOCATION_LONGITUDE].setValue(longitude);
472 LocationNP[LOCATION_ELEVATION].setValue(0);
474 LOGF_DEBUG("Mount latitude %8.4f longitude %8.4f", latitude, longitude);
475 }
476 }
477 else
478 LOG_WARN("Mount does not support retrieval of date, time and location.");
479
480 // last align is only available for mounts with switches that define the start index position
481 // At present that is only the CGX and CGX-L mounts so the control is only made available for them
482 // comment out this line and rebuild if you want to run with other mounts - at your own risk!
483 if (fwInfo.hasHomeIndex)
484 {
485 defineProperty(&LastAlignSP);
486 }
487
488 // Sometimes users start their mount when it is NOT yet aligned and then try to proceed to use it
489 // So we check issue and issue error if not aligned.
490 checkAlignment();
491
492 // PEC, must have PEC index and be equatorially mounted
493 if (fwInfo.canPec && CelestronTrackModeS[CTM_ALTAZ].s != ISS_OFF)
494 {
499 }
500
501 // handle the focuser
502 if (fwInfo.hasFocuser)
503 {
504 //defineProperty(&FocusBacklashNP);
505 //defineProperty(&FocusMinPosNP);
506
507 if (focusReadLimits())
508 {
510
512 // IDSetNumber(&FocusMinPosNP, nullptr);
513 // focuser move capability is only set if the focus limits are valid
517
518 LOG_INFO("Auxiliary focuser is connected.");
519 }
520 if (!focuserIsCalibrated)
521 {
522 LOG_WARN("Focuser not calibrated, moves will not be allowed");
523 }
525 }
526 }
527 else // not connected
528 {
530
532 //deleteProperty(FocusMinPosNP.name);
533
534 //GUIDE Delete properties.
536
538
539 deleteProperty(LastAlignSP.name);
541
542 deleteProperty(DSTSettingSP.name);
543
547
548 if (fwInfo.Version != "Invalid")
550 }
551
552 return true;
553}
554
555bool CelestronGPS::Goto(double ra, double dec)
556{
557 targetRA = ra;
558 targetDEC = dec;
559
561 {
562 driver.abort();
563 // sleep for 500 mseconds
564 usleep(500000);
565 }
566
567 if (driver.slew_radec(targetRA + SlewOffsetRa, targetDEC, usePreciseCoords) == false)
568 {
569 LOG_ERROR("Failed to slew telescope in RA/DEC.");
570 return false;
571 }
572
574
575 char RAStr[32], DecStr[32];
576 fs_sexa(RAStr, targetRA, 2, 3600);
577 fs_sexa(DecStr, targetDEC, 2, 3600);
578 LOGF_INFO("Slewing to JNOW RA %s - DEC %s SlewOffsetRa %4.1f arcsec", RAStr, DecStr, SlewOffsetRa * 3600 * 15);
579
580 return true;
581}
582
583bool CelestronGPS::Sync(double ra, double dec)
584{
585 if (!checkMinVersion(4.1, "sync"))
586 return false;
587
588 if (driver.sync(ra, dec, usePreciseCoords) == false)
589 {
590 LOG_ERROR("Sync failed.");
591 return false;
592 }
593
594 currentRA = ra;
595 currentDEC = dec;
596
597 char RAStr[32], DecStr[32];
598 fs_sexa(RAStr, targetRA, 2, 3600);
599 fs_sexa(DecStr, targetDEC, 2, 3600);
600 LOGF_INFO("Sync to %s, %s successful.", RAStr, DecStr);
601
602 return true;
603}
604
605/*
606bool CelestronGPS::GotoAzAlt(double az, double alt)
607{
608 if (isSimulation())
609 {
610 INDI::IHorizontalCoordinates horizontalPos;
611 // Libnova south = 0, west = 90, north = 180, east = 270
612 horizontalPos.az = az + 180;
613 if (horizontalPos.az >= 360)
614 horizontalPos.az -= 360;
615 horizontalPos.alt = alt;
616
617 IGeographicCoordinates observer;
618
619 observer.lat = LocationN[LOCATION_LATITUDE].value;
620 observer.lng = LocationN[LOCATION_LONGITUDE].value;
621
622 if (observer.lng > 180)
623 observer.lng -= 360;
624
625 INDI::IEquatorialCoordinates equatorialPos;
626 ln_get_equ_from_hrz(&horizontalPos, &observer, ln_get_julian_from_sys(), &equatorialPos);
627
628 targetRA = equatorialPos.rightascension/15.0;
629 targetDEC = equatorialPos.dec;
630 }
631
632 if (driver.slew_azalt(LocationN[LOCATION_LATITUDE].value, az, alt) == false)
633 {
634 LOG_ERROR("Failed to slew telescope in Az/Alt.");
635 return false;
636 }
637
638 targetAZ = az;
639 targetALT= alt;
640
641 TrackState = SCOPE_SLEWING;
642
643 HorizontalCoordsNP.s = IPS_BUSY;
644
645 char AZStr[16], ALTStr[16];
646 fs_sexa(AZStr, targetAZ, 3, 3600);
647 fs_sexa(ALTStr, targetALT, 2, 3600);
648 LOGF_INFO("Slewing to Az %s - Alt %s", AZStr, ALTStr);
649
650 return true;
651}
652*/
653
655{
657
659 move = (dir == DIRECTION_NORTH) ? CELESTRON_N : CELESTRON_S;
660 else
661 move = (dir == DIRECTION_NORTH) ? CELESTRON_S : CELESTRON_N;
662
664
665 switch (command)
666 {
667 case MOTION_START:
668 if (driver.start_motion(move, rate) == false)
669 {
670 LOG_ERROR("Error setting N/S motion direction.");
671 return false;
672 }
673 else
674 LOGF_INFO("Moving toward %s.", (move == CELESTRON_N) ? "North" : "South");
675 break;
676
677 case MOTION_STOP:
678 if (driver.stop_motion(move) == false)
679 {
680 LOG_ERROR("Error stopping N/S motion.");
681 return false;
682 }
683 else
684 LOGF_INFO("Movement toward %s halted.", (move == CELESTRON_N) ? "North" : "South");
685 break;
686 }
687
688 return true;
689}
690
692{
695
696 switch (command)
697 {
698 case MOTION_START:
699 if (driver.start_motion(move, rate) == false)
700 {
701 LOG_ERROR("Error setting W/E motion direction.");
702 return false;
703 }
704 else
705 LOGF_INFO("Moving toward %s.", (move == CELESTRON_W) ? "West" : "East");
706 break;
707
708 case MOTION_STOP:
709 if (driver.stop_motion(move) == false)
710 {
711 LOG_ERROR("Error stopping W/E motion.");
712 return false;
713 }
714 else
715 LOGF_INFO("Movement toward %s halted.", (move == CELESTRON_W) ? "West" : "East");
716 break;
717 }
718
719 return true;
720}
721
723{
725
726 if (isSimulation())
727 mountSim();
728
729 if (driver.get_radec(&currentRA, &currentDEC, usePreciseCoords) == false)
730 {
731 LOG_ERROR("Failed to read RA/DEC values.");
732 return false;
733 }
734
735 if (HasPierSide())
736 {
737 // read the pier side close to reading the Radec so they should match
738 char sop = '?';
739 char psc = 'u';
740 if (driver.get_pier_side(&sop))
741 {
742 // manage version and hemisphere nonsense
743 // HC versions less than 5.24 reverse the side of pier if the mount
744 // is in the Southern hemisphere. StarSense doesn't
745 if (LocationNP[LOCATION_LATITUDE].getValue() < 0)
746 {
747 if (fwInfo.controllerVersion <= 5.24 && fwInfo.controllerVariant != ISSTARSENSE)
748 {
749 // swap the char reported
750 if (sop == 'E')
751 sop = 'W';
752 else if (sop == 'W')
753 sop = 'E';
754 }
755 }
756 // The Celestron and INDI pointing states are opposite
757 if (sop == 'W')
758 {
759 pierSide = PIER_EAST;
760 psc = 'E';
761 }
762 else if (sop == 'E')
763 {
764 pierSide = PIER_WEST;
765 psc = 'W';
766 }
767 // pier side and Ha don't match at +-90 deg dec
768 if (currentDEC > 89.999 || currentDEC < -89.999)
769 {
770 pierSide = PIER_UNKNOWN;
771 psc = 'U';
772 }
773 }
774
775 LOGF_DEBUG("latitude %g, sop %c, PierSide %c",
776 LocationNP[LOCATION_LATITUDE].getValue(),
777 sop, psc);
778 }
779
780
781 // aligning
782 if (slewToIndex)
783 {
784 bool atIndex;
785 if (!driver.indexreached(&atIndex))
786 {
787 LOG_ERROR("IndexReached Failure");
788 slewToIndex = false;
789 return false;
790 }
791 if (atIndex)
792 {
793 slewToIndex = false;
794 // reached the index position.
795
796 // do an alignment
797 if (!fwInfo.hasHomeIndex)
798 {
799 // put another dire warning here
800 LOG_WARN("This mount does not have index switches, the alignment assumes it is at the index position.");
801 }
802
803 if (!driver.lastalign())
804 {
805 LOG_ERROR("LastAlign failed");
806 return false;
807 }
808
809 LastAlignSP.s = IPS_IDLE;
810 IDSetSwitch(&LastAlignSP, "Align finished");
811
812 bool isAligned;
813 if (!driver.check_aligned(&isAligned))
814 {
815 LOG_WARN("get Alignment Failed!");
816 }
817 else
818 {
819 if (isAligned)
820 LOG_INFO("Mount is aligned");
821 else
822 LOG_WARN("Alignment Failed!");
823 }
824
825 return true;
826 }
827 }
828
829 switch (TrackState)
830 {
831 case SCOPE_SLEWING:
832 // are we done?
833 bool slewing;
834 if (driver.is_slewing(&slewing) && !slewing)
835 {
836 LOG_INFO("Slew complete, tracking...");
837 SetTrackEnabled(true);
838 // update ra offset
839 double raoffset = targetRA - currentRA + SlewOffsetRa;
840 if (raoffset > 0.0 || raoffset < 10.0 / 3600.0)
841 {
842 // average last two values
843 SlewOffsetRa = SlewOffsetRa > 0 ? (SlewOffsetRa + raoffset) / 2 : raoffset;
844
845 LOGF_DEBUG("raoffset %4.1f, SlewOffsetRa %4.1f arcsec", raoffset * 3600 * 15, SlewOffsetRa * 3600 * 15);
846 }
847 }
848 break;
849
850 case SCOPE_PARKING:
851 // are we done?
852 if (driver.is_slewing(&slewing) && !slewing)
853 {
854 if (driver.set_track_mode(CTM_OFF))
855 LOG_DEBUG("Mount tracking is off.");
856
857 SetParked(true);
858 saveConfig(true);
859
860 // Check if we need to hibernate
861 if (UseHibernateS[0].s == ISS_ON)
862 {
863 LOG_INFO("Hibernating mount...");
864 if (driver.hibernate())
865 LOG_INFO("Mount hibernated. Please disconnect now and turn off your mount.");
866 else
867 LOG_ERROR("Hibernating mount failed!");
868 }
869 }
870 break;
871
872 default:
873 break;
874 }
875
876 // update pier side and RaDec close together to minimise the possibility of
877 // a mismatch causing an Ha limit error during a pier flip slew.
878 if (HasPierSide())
879 setPierSide(pierSide);
880 NewRaDec(currentRA, currentDEC);
881
882 // is PEC Handling required
884 {
885 static PEC_STATE lastPecState = PEC_STATE::NotKnown;
886 static size_t lastPecIndex = 1000;
887 static size_t numRecordPoints;
888
889 if (driver.pecState >= PEC_STATE::PEC_INDEXED)
890 {
891 if (numPecBins < 88)
892 {
893 numPecBins = driver.getPecNumBins();
894 }
895 // get and show the current PEC index
896 size_t pecIndex = driver.pecIndex();
897
898 if (pecIndex != lastPecIndex)
899 {
900 LOGF_DEBUG("PEC state %s, index %d", driver.PecStateStr(), pecIndex);
901 IUSaveText(&PecInfoT[1], std::to_string(pecIndex).c_str());
902 IDSetText(&PecInfoTP, nullptr);
903 lastPecIndex = pecIndex;
904
905 // count the PEC records
907 numRecordPoints++;
908 else
909 numRecordPoints = 0;
910 }
911 }
912
913 // update the PEC state
914 if (driver.updatePecState() != lastPecState)
915 {
916 // and handle the change, if there was one
917 LOGF_DEBUG("PEC last state %s, new State %s", driver.PecStateStr(lastPecState), driver.PecStateStr());
918
919 // update the state string
920 IUSaveText(&PecInfoT[0], driver.PecStateStr());
921 IDSetText(&PecInfoTP, nullptr);
922
923 // no need to check both current and last because they must be different
924 switch (lastPecState)
925 {
927 // finished seeking
930 IDSetSwitch(&PecControlSP, nullptr);
931 LOG_INFO("PEC index Seek completed.");
932 break;
934 // finished playback
937 IDSetSwitch(&PecControlSP, nullptr);
938 LOG_INFO("PEC playback finished");
939 break;
941 // finished recording
942 LOGF_DEBUG("PEC record stopped, %d records", numRecordPoints);
943
944 if (numRecordPoints >= numPecBins)
945 {
946 savePecData();
947 }
948
951 LOG_INFO("PEC record finished");
952 IDSetSwitch(&PecControlSP, nullptr);
953
954 break;
955 default:
956 break;
957 }
958 lastPecState = driver.pecState;
959 }
960 }
961
962 // focuser
963 if (fwInfo.hasFocuser)
964 {
965 // Check position
966 double lastPosition = FocusAbsPosNP[0].getValue();
967
968 int absPos = focusTrue2Abs(driver.foc_position());
969 if (absPos >= 0)
970 {
971 FocusAbsPosNP[0].setValue(absPos);
972 // Only update if there is actual change
973 if (fabs(lastPosition - FocusAbsPosNP[0].getValue()) > 1)
975 }
976
978 {
979 // The backlash handling is done here, if the move state
980 // shows that a backlash move has been done then the final move needs to be started
981 // and the states left at IPS_BUSY
982
983 if (!driver.foc_moving())
984 {
985 if (focusBacklashMove)
986 {
987 focusBacklashMove = false;
988 if (driver.foc_move(focusAbs2True(focusAbsPosition)))
989 LOGF_INFO("Focus final move %i", focusAbsPosition);
990 else
991 LOG_INFO("Backlash move failed");
992 }
993 else
994 {
999 LOG_INFO("Focuser reached requested position.");
1000 }
1001 }
1002 }
1003 }
1004
1005 return true;
1006}
1007
1009{
1010 driver.stop_motion(CELESTRON_N);
1011 driver.stop_motion(CELESTRON_S);
1012 driver.stop_motion(CELESTRON_W);
1013 driver.stop_motion(CELESTRON_E);
1014
1015 //GUIDE Abort guide operations.
1017 {
1020 GuideNSNP[0].setValue(0);
1021 GuideNSNP[1].setValue(0);
1022 GuideWENP[0].setValue(0);
1023 GuideWENP[1].setValue(0);
1024
1025 if (GuideNSTID)
1026 {
1028 GuideNSTID = 0;
1029 }
1030
1031 if (GuideWETID)
1032 {
1034 GuideWETID = 0;
1035 }
1036
1037 LOG_INFO("Guide aborted.");
1038 GuideNSNP.apply();
1039 GuideWENP.apply();
1040
1041 return true;
1042 }
1043
1044 return driver.abort();
1045}
1046
1048{
1049 driver.set_device(getDeviceName());
1050 driver.set_port_fd(PortFD);
1051
1052 if (isSimulation())
1053 {
1054 driver.set_simulation(true);
1055 driver.set_sim_slew_rate(SR_5);
1056 driver.set_sim_ra(0);
1057 driver.set_sim_dec(90);
1058 }
1059
1060 if (driver.check_connection() == false)
1061 {
1062 LOG_ERROR("Failed to communicate with the mount, check the logs for details.");
1063 return false;
1064 }
1065
1066 return true;
1067}
1068
1069bool CelestronGPS::ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n)
1070{
1071 if (dev && std::string(getDeviceName()) == dev)
1072 {
1073 // Enable/Disable hibernate
1074 if (name && std::string(name) == UseHibernateSP.name)
1075 {
1076 IUUpdateSwitch(&UseHibernateSP, states, names, n);
1077 if (fwInfo.controllerVersion > 0)
1078 {
1079 if (UseHibernateS[0].s == ISS_ON && (checkMinVersion(4.22, "hibernation", true) == false))
1080 {
1081 UseHibernateS[0].s = ISS_OFF;
1082 UseHibernateS[1].s = ISS_ON;
1084 }
1085 else
1087 }
1088 IDSetSwitch(&UseHibernateSP, nullptr);
1089 return true;
1090 }
1091
1092 // start a last align
1093 // the process is:
1094 // start move to switch position
1095 // wait for the move to finish
1096 // set the time from the PC - maybe
1097 // send a Last Align command "Y"
1098
1099
1100 if (name && std::string(name) == LastAlignSP.name)
1101 {
1102 if (!fwInfo.hasHomeIndex)
1103 {
1104 // put the dire warning here
1105 LOG_WARN("This mount does not have index switches, make sure that it is at the index position.");
1106 }
1107 LOG_DEBUG("Start Align");
1108 // start move to switch positions
1109 if (!driver.startmovetoindex())
1110 {
1111 LastAlignSP.s = IPS_ALERT;
1112 return false;
1113 }
1114 // wait for the move to finish
1115 // done in ReadScopeStatus
1116 slewToIndex = true;
1117 LastAlignSP.s = IPS_BUSY;
1118 IDSetSwitch(&LastAlignSP, "Align in progress");
1119 return true;
1120 }
1121
1122 // handle the PEC commands
1123 if (name && std::string(name) == PecControlSP.name)
1124 {
1125 IUUpdateSwitch(&PecControlSP, states, names, n);
1127
1128 switch(idx)
1129 {
1130 case PEC_Stop:
1131 LOG_DEBUG(" stop PEC record or playback");
1132 bool playback;
1133 if ((playback = driver.pecState == PEC_PLAYBACK) || driver.pecState == PEC_RECORDING)
1134 {
1135 if (playback ? driver.PecPlayback(false) : driver.PecRecord(false))
1136 {
1138 }
1139 else
1140 {
1142 }
1143 }
1144 else
1145 {
1146 LOG_WARN("Incorrect state to stop PEC Playback or Record");
1148 }
1150 break;
1151 case PEC_Playback:
1152 LOG_DEBUG("start PEC Playback");
1153 if (driver.pecState == PEC_STATE::PEC_INDEXED)
1154 {
1155 // start playback
1156 if (driver.PecPlayback(true))
1157 {
1159 LOG_INFO("PEC Playback started");
1160 }
1161 else
1162 {
1164 return false;
1165 }
1166 }
1167 else
1168 {
1169 LOG_WARN("Incorrect state to start PEC Playback");
1170 }
1171 break;
1172 case PEC_Record:
1173 LOG_DEBUG("start PEC record");
1175 {
1176 LOG_WARN("Mount must be Tracking to record PEC");
1177 break;
1178 }
1179 if (driver.pecState == PEC_STATE::PEC_INDEXED)
1180 {
1181 if (driver.PecRecord(true))
1182 {
1184 LOG_INFO("PEC Record started");
1185 }
1186 else
1187 {
1189 return false;
1190 }
1191 }
1192 else
1193 {
1194 LOG_WARN("Incorrect state to start PEC Recording");
1195 }
1196 break;
1197 case PEC_Seek:
1198 LOG_DEBUG("Seek PEC Index");
1199 if (driver.isPecAtIndex(true))
1200 {
1201 LOG_INFO("PEC index already found");
1203 }
1204 else if (driver.pecState == PEC_STATE::PEC_AVAILABLE)
1205 {
1206 // start seek, moves up to 2 degrees in Ra
1207 if (driver.PecSeekIndex())
1208 {
1210 LOG_INFO("Seek PEC index started");
1211 }
1212 else
1213 {
1215 return false;
1216 }
1217 }
1218 break;
1219 }
1220 IDSetSwitch(&PecControlSP, nullptr);
1221 return true;
1222 }
1223
1224 // Focuser
1225 if (strstr(name, "FOCUS"))
1226 {
1227 return FI::processSwitch(dev, name, states, names, n);
1228 }
1229 }
1230
1231 return INDI::Telescope::ISNewSwitch(dev, name, states, names, n);
1232}
1233
1234bool CelestronGPS::ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n)
1235{
1236 // Check focuser interface
1237 if (FI::processNumber(dev, name, values, names, n))
1238 return true;
1239 // Check guider interface
1240 if (GI::processNumber(dev, name, values, names, n))
1241 return true;
1242
1243 if (dev && std::string(dev) == getDeviceName())
1244 {
1245 // Guide Rate
1246 if (strcmp(name, "GUIDE_RATE") == 0)
1247 {
1248 IUUpdateNumber(&GuideRateNP, values, names, n);
1250 IDSetNumber(&GuideRateNP, nullptr);
1251 uint8_t grRa = static_cast<uint8_t>(std::min(GuideRateN[AXIS_RA].value * 256.0, 255.0));
1252 uint8_t grDec = static_cast<uint8_t>(std::min(GuideRateN[AXIS_DE].value * 256.0, 255.0));
1253 //LOGF_DEBUG("Set Guide Rates (0-1x sidereal): Ra %f, Dec %f", GuideRateN[AXIS_RA].value, GuideRateN[AXIS_DE].value);
1254 //LOGF_DEBUG("Set Guide Rates (0-255): Ra %i, Dec %i", grRa, grDec);
1255 LOGF_DEBUG("Set Guide Rates: Ra %f, Dec %f", GuideRateN[AXIS_RA].value, GuideRateN[AXIS_DE].value);
1258 LOG_WARN("Changing guide rates may require recalibration of guiding.");
1259 return true;
1260 }
1261 }
1262
1263 INDI::Telescope::ISNewNumber(dev, name, values, names, n);
1264 return true;
1265}
1266
1267bool CelestronGPS::ISNewText(const char *dev, const char *name, char **texts, char **names, int n)
1268{
1269 // the idea is that pressing "Set" on the PEC_LOAD text will load the data in the file specified in the text
1270 if (dev && std::string(dev) == getDeviceName())
1271 {
1272 LOGF_DEBUG("ISNewText name %s, text %s, names %s, n %d", name, texts[0], names[0], n);
1273
1274 if (name && std::string(name) == "PEC_LOAD")
1275 {
1276
1277 IUUpdateText(&PecFileNameTP, texts, names, n);
1278 IDSetText(&PecFileNameTP, nullptr);
1279
1280 LOGF_DEBUG("PEC Set %s", PecFileNameT[0].text);
1281
1282 PecData pecData;
1283
1284 // load from file
1285 if (!pecData.Load(PecFileNameT[0].text))
1286 {
1287 LOGF_WARN("File %s load failed", PecFileNameT[0].text);
1288 return false;
1289 }
1290 // save to mount
1291 if (!pecData.Save(&driver))
1292 {
1293 LOGF_WARN("PEC Data file %s save to mount failed", PecFileNameT[0].text);
1294 return false;
1295 }
1296 LOGF_INFO("PEC Data file %s sent to mount", PecFileNameT[0].text);
1297 }
1298 }
1299
1300 INDI::Telescope::ISNewText(dev, name, texts, names, n);
1301 return true;
1302}
1303
1304
1306{
1307 // Just update the number
1308 INDI_UNUSED(steps);
1309 return true;
1310}
1311
1313{
1314 static struct timeval ltv;
1315 struct timeval tv;
1316 double dt, dx, da_ra = 0, da_dec = 0;
1317 int nlocked;
1318
1319 // update elapsed time since last poll, don't presume exactly POLLMS
1320 gettimeofday(&tv, nullptr);
1321
1322 if (ltv.tv_sec == 0 && ltv.tv_usec == 0)
1323 ltv = tv;
1324
1325 dt = tv.tv_sec - ltv.tv_sec + (tv.tv_usec - ltv.tv_usec) / 1e6;
1326 ltv = tv;
1327
1328 if (fabs(targetRA - currentRA) * 15. >= GOTO_LIMIT)
1329 da_ra = GOTO_RATE * dt;
1330 else if (fabs(targetRA - currentRA) * 15. >= SLEW_LIMIT)
1331 da_ra = SLEW_RATE * dt;
1332 else
1333 da_ra = FINE_SLEW_RATE * dt;
1334
1335 if (fabs(targetDEC - currentDEC) >= GOTO_LIMIT)
1336 da_dec = GOTO_RATE * dt;
1337 else if (fabs(targetDEC - currentDEC) >= SLEW_LIMIT)
1338 da_dec = SLEW_RATE * dt;
1339 else
1340 da_dec = FINE_SLEW_RATE * dt;
1341
1343 {
1344 int rate = SlewRateSP.findOnSwitchIndex();
1345
1346 switch (rate)
1347 {
1348 case SLEW_GUIDE:
1349 da_ra = FINE_SLEW_RATE * dt * 0.05;
1350 da_dec = FINE_SLEW_RATE * dt * 0.05;
1351 break;
1352
1353 case SLEW_CENTERING:
1354 da_ra = FINE_SLEW_RATE * dt * .1;
1355 da_dec = FINE_SLEW_RATE * dt * .1;
1356 break;
1357
1358 case SLEW_FIND:
1359 da_ra = SLEW_RATE * dt;
1360 da_dec = SLEW_RATE * dt;
1361 break;
1362
1363 default:
1364 da_ra = GOTO_RATE * dt;
1365 da_dec = GOTO_RATE * dt;
1366 break;
1367 }
1368
1369 switch (MovementNSSP.getState())
1370 {
1371 case IPS_BUSY:
1372 if (MovementNSSP[DIRECTION_NORTH].getState() == ISS_ON)
1373 currentDEC += da_dec;
1374 else if (MovementNSSP[DIRECTION_SOUTH].getState() == ISS_ON)
1375 currentDEC -= da_dec;
1376 break;
1377
1378 default:
1379 break;
1380 }
1381
1382 switch (MovementWESP.getState())
1383 {
1384 case IPS_BUSY:
1385 if (MovementWESP[DIRECTION_WEST].getState() == ISS_ON)
1386 currentRA += da_ra / 15.;
1387 else if (MovementWESP[DIRECTION_EAST].getState() == ISS_ON)
1388 currentRA -= da_ra / 15.;
1389 break;
1390
1391 default:
1392 break;
1393 }
1394
1395 driver.set_sim_ra(currentRA);
1396 driver.set_sim_dec(currentDEC);
1397
1399
1400 return;
1401 }
1402
1403 // Process per current state. We check the state of EQUATORIAL_COORDS and act accordingly
1404 switch (TrackState)
1405 {
1406 case SCOPE_IDLE:
1407 currentRA = driver.get_sim_ra() + (TRACKRATE_SIDEREAL / 3600.0 * dt) / 15.0;
1409 break;
1410
1411 case SCOPE_SLEWING:
1412 case SCOPE_PARKING:
1413 // slewing - nail it when both within one pulse @ SLEWRATE
1414 nlocked = 0;
1415
1416 dx = targetRA - currentRA;
1417
1418 // Take shortest path
1419 if (fabs(dx) > 12)
1420 dx *= -1;
1421
1422 if (fabs(dx) <= da_ra)
1423 {
1424 currentRA = targetRA;
1425 nlocked++;
1426 }
1427 else if (dx > 0)
1428 currentRA += da_ra / 15.;
1429 else
1430 currentRA -= da_ra / 15.;
1431
1432 if (currentRA < 0)
1433 currentRA += 24;
1434 else if (currentRA > 24)
1435 currentRA -= 24;
1436
1437 dx = targetDEC - currentDEC;
1438 if (fabs(dx) <= da_dec)
1439 {
1440 currentDEC = targetDEC;
1441 nlocked++;
1442 }
1443 else if (dx > 0)
1444 currentDEC += da_dec;
1445 else
1446 currentDEC -= da_dec;
1447
1448 if (nlocked == 2)
1449 {
1450 driver.set_sim_slewing(false);
1451 }
1452
1453 break;
1454
1455 default:
1456 break;
1457 }
1458
1459 driver.set_sim_ra(currentRA);
1460 driver.set_sim_dec(currentDEC);
1461}
1462
1464{
1465 driver.set_simulation(enable);
1466}
1467
1468// Update Location and time are disabled if the mount is aligned. This is because
1469// changing either will change the mount model because at least the local sidereal time
1470// will be changed. StarSense will set the mount to unaligned but it isn't a good idea even
1471// with the NexStar HCs
1472
1473bool CelestronGPS::updateLocation(double latitude, double longitude, double elevation)
1474{
1475 if (!isConnected())
1476 {
1477 LOG_DEBUG("updateLocation called before we are connected");
1478 return false;
1479 }
1480
1481 if (!checkMinVersion(2.3, "updating location"))
1482 return false;
1483
1484 bool isAligned;
1485 if (!driver.check_aligned(&isAligned))
1486 {
1487 LOG_INFO("Update location - check_aligned failed");
1488 return false;
1489 }
1490
1491 if (isAligned)
1492 {
1493 LOG_INFO("Updating location is not necessary since mount is already aligned.");
1494 return false;
1495 }
1496
1497 LOGF_DEBUG("Update location %8.3f, %8.3f, %4.0f", latitude, longitude, elevation);
1498
1499 return driver.set_location(longitude, latitude);
1500}
1501
1502bool CelestronGPS::updateTime(ln_date *utc, double utc_offset)
1503{
1504 if (!isConnected())
1505 {
1506 LOG_DEBUG("updateTime called before we are connected");
1507 return false;
1508 }
1509
1510 if (!checkMinVersion(2.3, "updating time"))
1511 return false;
1512
1513 // setting time on StarSense seems to make it not aligned
1514 bool isAligned;
1515 if (!driver.check_aligned(&isAligned))
1516 {
1517 LOG_INFO("UpdateTime - check_aligned failed");
1518 return false;
1519 }
1520 if (isAligned)
1521 {
1522 LOG_INFO("Updating time is not necessary since mount is already aligned.");
1523 return false;
1524 }
1525
1526 // starsense HC doesn't seem to support the precise time setting
1527 bool precise = fwInfo.controllerVersion >= 5.28;
1528
1529 bool dst = DSTSettingS[0].s == ISS_ON;
1530
1531 LOGF_DEBUG("Update time: offset %f %s UTC %i-%02i-%02iT%02i:%02i:%02.0f", utc_offset, dst ? "DST" : "", utc->years,
1532 utc->months, utc->days,
1533 utc->hours, utc->minutes, utc->seconds);
1534
1535 return (driver.set_datetime(utc, utc_offset, dst, precise));
1536}
1537
1539{
1540 double parkAZ = GetAxis1Park();
1541 double parkAlt = GetAxis2Park();
1542
1543 char AzStr[16], AltStr[16];
1544 fs_sexa(AzStr, parkAZ, 2, 3600);
1545 fs_sexa(AltStr, parkAlt, 2, 3600);
1546
1547 // unsync is only for NS+ 5.29 or more and not StarSense
1548 if (fwInfo.controllerVersion >= 5.29 && !driver.unsync())
1549 return false;
1550
1551 LOGF_DEBUG("Parking to Az (%s) Alt (%s)...", AzStr, AltStr);
1552
1553 if (driver.slew_azalt(parkAZ, parkAlt, usePreciseCoords))
1554 {
1556 LOG_INFO("Parking is in progress...");
1557 return true;
1558 }
1559
1560 return false;
1561}
1562
1564{
1565 bool parkDataValid = (LoadParkData() == nullptr);
1566 // Check if we need to wake up IF:
1567 // 1. Park data exists in ParkData.xml
1568 // 2. Mount is currently parked
1569 // 3. Hibernate option is enabled
1570 if (parkDataValid && isParked() && UseHibernateS[0].s == ISS_ON)
1571 {
1572 LOG_INFO("Waking up mount...");
1573
1574 if (!driver.wakeup())
1575 {
1576 LOG_ERROR("Waking up mount failed! Make sure mount is powered and connected. "
1577 "Hibernate requires firmware version >= 5.21");
1578 return false;
1579 }
1580 }
1581
1582 // Set tracking mode to whatever it was stored before
1583 SetParked(false);
1584
1585 //loadConfig(true, "TELESCOPE_TRACK_MODE");
1586 // Read Saved Track State from config file
1587 for (size_t i = 0; i < TrackStateSP.count(); i++)
1589
1590 // set the mount tracking state
1591 LOGF_DEBUG("track state %s", TrackStateSP.getLabel());
1593
1594 // reinit PEC
1595 if (driver.pecState >= PEC_STATE::PEC_AVAILABLE)
1596 driver.pecState = PEC_AVAILABLE;
1597
1598 return true;
1599}
1600
1602{
1603 // The Goto Alt-Az and Get Alt-Az menu items have been renamed Goto Axis Postn and Get Axis Postn
1604 // where Postn is an abbreviation for Position. Since this feature doesn't actually refer
1605 // to altitude and azimuth when mounted on a wedge, the new designation is more accurate.
1606 // Source : NexStarHandControlVersion4UsersGuide.pdf
1607
1608 if (driver.get_azalt(&currentAZ, &currentALT, usePreciseCoords) == false)
1609 {
1610 LOG_ERROR("Failed to read AZ/ALT values.");
1611 return false;
1612 }
1613
1614 double parkAZ = currentAZ;
1615 double parkAlt = currentALT;
1616
1617 char AzStr[16], AltStr[16];
1618 fs_sexa(AzStr, parkAZ, 2, 3600);
1619 fs_sexa(AltStr, parkAlt, 2, 3600);
1620
1621 LOGF_DEBUG("Setting current parking position to coordinates Az (%s) Alt (%s)...", AzStr,
1622 AltStr);
1623
1624 SetAxis1Park(parkAZ);
1625 SetAxis2Park(parkAlt);
1626
1627 return true;
1628}
1629
1631{
1632 // The Goto Alt-Az and Get Alt-Az menu items have been renamed Goto Axis Postn and Get Axis Postn
1633 // where Postn is an abbreviation for Position. Since this feature doesn't actually refer
1634 // to altitude and azimuth when mounted on a wedge, the new designation is more accurate.
1635 // Source : NexStarHandControlVersion4UsersGuide.pdf
1636
1637 // By default azimuth 90° ( hemisphere doesn't matter)
1638 SetAxis1Park(90);
1639
1640 // Altitude = 90° (latitude doesn't matter)
1641 SetAxis2Park(90);
1642
1643 return true;
1644}
1645
1647{
1650
1653 IUSaveConfigSwitch(fp, &DSTSettingSP);
1654
1655 // IUSaveConfigNumber(fp, &FocusMinPosNP);
1656
1657 return true;
1658}
1659
1660bool CelestronGPS::setCelestronTrackMode(CELESTRON_TRACK_MODE mode)
1661{
1662 if (driver.set_track_mode(mode))
1663 {
1665 LOGF_DEBUG("Tracking mode set to %i, %s.", mode, CelestronTrackModeS[mode - 1].label);
1666 return true;
1667 }
1668
1669 return false;
1670}
1671
1672//GUIDE Guiding functions.
1673
1674// There have been substantial changes at version 3.3, Nov 2019
1675//
1676// The mount controlled Aux Guide is used if it is available, this is
1677// if the mount firmware version for both axes is 6.12 or better. Other
1678// mounts use a timed guide method.
1679// The mount Aux Guide command has a maximum vulue of 2.55 seconds but if
1680// a longer guide is needed then multiple Aux Guide commands are sent.
1681//
1682// The start guide and stop guide functions use helper functions to avoid
1683// code duplication.
1684
1689
1694
1699
1704
1705// common function to start guiding for all axes.
1707{
1708 // set up direction properties
1709 char dc = 'x';
1710 int* guideTID = &GuideNSTID;
1711 int* ticks = &ticksNS;
1712 uint8_t rate = 50;
1713 int index = 0;
1714
1715 // set up pointers to the various things needed
1716 switch (dirn)
1717 {
1718 case CELESTRON_N:
1719 dc = 'N';
1720 guideTID = &GuideNSTID;
1721 ticks = &ticksNS;
1722 /* Scale guide rates to uint8 in [0..100] for sending to telescopoe, see CelestronDriver::send_pulse() */
1723 rate = guideRateDec = static_cast<uint8_t>(GuideRateN[AXIS_DE].value * 100.0);
1724 break;
1725 case CELESTRON_S:
1726 dc = 'S';
1727 guideTID = &GuideNSTID;
1728 ticks = &ticksNS;
1729 index = 1;
1730 /* Scale guide rates to uint8 in [0..100] for sending to telescopoe, see CelestronDriver::send_pulse() */
1731 rate = guideRateDec = static_cast<uint8_t>(GuideRateN[AXIS_DE].value * 100.0);
1732 break;
1733 case CELESTRON_E:
1734 dc = 'E';
1735 guideTID = &GuideWETID;
1736 ticks = &ticksWE;
1737 /* Scale guide rates to uint8 in [0..100] for sending to telescopoe, see CelestronDriver::send_pulse() */
1738 rate = guideRateRa = static_cast<uint8_t>(GuideRateN[AXIS_RA].value * 100.0);
1739 break;
1740 case CELESTRON_W:
1741 dc = 'W';
1742 guideTID = &GuideWETID;
1743 ticks = &ticksWE;
1744 index = 1;
1745 /* Scale guide rates to uint8 in [0..100] for sending to telescopoe, see CelestronDriver::send_pulse() */
1746 rate = guideRateRa = static_cast<uint8_t>(GuideRateN[AXIS_RA].value * 100.0);
1747 break;
1748 }
1749
1750 LOGF_DEBUG("GUIDE CMD: %c %u ms, %s guide", dc, ms, canAuxGuide ? "Aux" : "Time");
1751
1752 if (!canAuxGuide && (MovementNSSP.getState() == IPS_BUSY || MovementWESP.getState() == IPS_BUSY))
1753 {
1754 LOG_ERROR("Cannot guide while moving.");
1755 return IPS_ALERT;
1756 }
1757
1758 auto directionProperty = dirn > CELESTRON_S ? MovementWESP : MovementNSSP;
1759
1760 // If already moving (no pulse command), then stop movement
1761 if (directionProperty.getState() == IPS_BUSY)
1762 {
1763 LOG_DEBUG("Already moving - stop");
1764 driver.stop_motion(dirn);
1765 }
1766
1767 if (*guideTID)
1768 {
1769 LOGF_DEBUG("Stop timer %c", dc);
1770 IERmTimer(*guideTID);
1771 *guideTID = 0;
1772 }
1773
1774 if (canAuxGuide)
1775 {
1776 // get the number of 10ms hardware ticks
1777 *ticks = ms / 10;
1778
1779 // send the first Aux Guide command,
1780 if (driver.send_pulse(dirn, rate, static_cast<char>(std::min(255, *ticks))) == 0)
1781 {
1782 LOGF_ERROR("send_pulse %c error", dc);
1783 return IPS_ALERT;
1784 }
1785 // decrease ticks and ms values
1786 *ticks -= 255;
1787 ms = ms > 2550 ? 2550 : ms;
1788 }
1789 else
1790 {
1791 directionProperty[index].setState(ISS_ON);
1792 // start movement at HC button rate 1
1793 if (!driver.start_motion(dirn, CELESTRON_SLEW_RATE::SR_1))
1794 {
1795 LOGF_ERROR("StartMotion %c failed", dc);
1796 return IPS_ALERT;
1797 }
1798 *ticks = 0;
1799 }
1800
1801 // Set slew to guiding
1802 SlewRateSP.reset();
1804 SlewRateSP.apply();
1805 // start the guide timeout timer
1806 AddGuideTimer(dirn, static_cast<int>(ms));
1807 return IPS_BUSY;
1808}
1809
1810//GUIDE The timer helper functions.
1812{
1813 static_cast<CelestronGPS *>(p)->guideTimer(CELESTRON_N);
1814}
1815
1817{
1818 static_cast<CelestronGPS *>(p)->guideTimer(CELESTRON_S);
1819}
1820
1822{
1823 static_cast<CelestronGPS *>(p)->guideTimer(CELESTRON_W);
1824}
1825
1827{
1828 static_cast<CelestronGPS *>(p)->guideTimer(CELESTRON_E);
1829}
1830
1831//GUIDE The timer function
1832
1833/* Here I splitted the behaviour depending upon the direction
1834 * of the guide command which generates the timer; this was
1835 * done because the member variable "guide_direction" could
1836 * be modified by a pulse command on the other axis BEFORE
1837 * the calling pulse command is terminated.
1838 */
1839
1841{
1842 int* ticks = &ticksNS;
1843 uint8_t rate = 0;
1844
1845 switch(dirn)
1846 {
1847 case CELESTRON_N:
1848 case CELESTRON_S:
1849 ticks = &ticksNS;
1850 rate = guideRateDec;
1851 break;
1852 case CELESTRON_E:
1853 case CELESTRON_W:
1854 ticks = &ticksWE;
1855 rate = guideRateRa;
1856 break;
1857 }
1858
1859 LOGF_DEBUG("guideTimer dir %c, ticks %i, rate %i", "NSWE"[dirn], *ticks, rate);
1860
1861 if (canAuxGuide)
1862 {
1863 if (driver.get_pulse_status(dirn))
1864 {
1865 // current move not finished, add some more time
1866 AddGuideTimer(dirn, 100);
1867 return;
1868 }
1869 if (*ticks > 0)
1870 {
1871 // do some more guiding and set the timeout
1872 int dt = (*ticks > 255) ? 255 : *ticks;
1873 driver.send_pulse(dirn, rate, static_cast<uint8_t>(dt));
1874 AddGuideTimer(dirn, dt * 10);
1875 *ticks -= 255;
1876 return;
1877 }
1878 // we get here if the axis reports guiding finished and all the ticks have been done
1879 }
1880 else
1881 {
1882 if (!driver.stop_motion(dirn))
1883 LOGF_ERROR("StopMotion failed dir %c", "NSWE"[dirn]);
1884 }
1885
1886 switch(dirn)
1887 {
1888 case CELESTRON_N:
1889 case CELESTRON_S:
1892 GuideNSNP[0].setValue(0);
1893 GuideNSNP[1].setValue(0);
1895 GuideNSTID = 0;
1896 GuideNSNP.apply();
1897 break;
1898 case CELESTRON_E:
1899 case CELESTRON_W:
1902 GuideWENP[0].setValue(0);
1903 GuideWENP[1].setValue(0);
1905 GuideWETID = 0;
1906 GuideWENP.apply();
1907 break;
1908 }
1909 LOGF_DEBUG("Guide %c finished", "NSWE"[dirn]);
1910}
1911
1913{
1914 switch(dirn)
1915 {
1916 case CELESTRON_N:
1918 break;
1919 case CELESTRON_S:
1921 break;
1922 case CELESTRON_E:
1924 break;
1925 case CELESTRON_W:
1927 break;
1928 }
1929}
1930
1931// end of guiding code
1932
1933// the INDI overload, expected to set the track rate
1934// sidereal, solar or lunar and only if the mount is equatorial
1936{
1938
1939 switch (fwInfo.celestronTrackMode)
1940 {
1941 case CTM_OFF:
1942 case CTM_ALTAZ:
1943 case CTM_RADEC:
1944 return false;
1945 case CTM_EQN:
1946 case CTM_EQS:
1947 break;
1948 }
1949
1950 switch (mode)
1951 {
1952 case 0:
1953 rate = CTR_SIDEREAL;
1954 break;
1955 case 1:
1956 rate = CTR_SOLAR;
1957 break;
1958 case 2:
1959 rate = CTR_LUNAR;
1960 break;
1961 default:
1962 return false;
1963 }
1964 return driver.set_track_rate(rate, fwInfo.celestronTrackMode);
1965}
1966
1968{
1969 return setCelestronTrackMode(enabled ? fwInfo.celestronTrackMode : CTM_OFF);
1970 //return setTrackMode(enabled ? static_cast<CELESTRON_TRACK_MODE>(IUFindOnSwitchIndex(&TrackModeSP)+1) : TRACKING_OFF);
1971}
1972
1973void CelestronGPS::checkAlignment()
1974{
1976
1977 bool isAligned;
1978 if (!driver.check_aligned(&isAligned) || !isAligned)
1979 LOG_WARN("Mount is NOT aligned. You must align the mount first before you can use it. Disconnect, align the mount, and reconnect again.");
1980}
1981
1982bool CelestronGPS::savePecData()
1983{
1984 // generate the file name:
1985 // ~/.indi/pec/yyyy-mm-dd/pecData_hh:mm.log
1986
1987 char ts_date[32], ts_time[32];
1988 struct tm *tp;
1989 time_t t;
1990
1991 time(&t);
1992 tp = gmtime(&t);
1993 strftime(ts_date, sizeof(ts_date), "%Y-%m-%d", tp);
1994 strftime(ts_time, sizeof(ts_time), "%H:%M", tp);
1995
1996 char dir[MAXRBUF];
1997 snprintf(dir, MAXRBUF, "%s/PEC_Data/%s", getenv("HOME"), ts_date);
1998
1999 if (INDI::mkpath(dir, 0755) == -1)
2000 {
2001 LOGF_ERROR("Error creating directory %s (%s)", dir, strerror(errno));
2002 return false;
2003 }
2004
2005 char pecFileBuf[MAXRBUF];
2006 snprintf(pecFileBuf, MAXRBUF, "%s/pecData_%s.csv", dir, ts_time);
2007
2008 // show the file name
2009 IUSaveText(&PecFileNameT[0], pecFileBuf);
2010 IDSetText(&PecFileNameTP, nullptr);
2011
2012 // get the PEC data from the mount
2013 PecData pecdata;
2014
2015 if (!pecdata.Load(&driver))
2016 {
2017 LOG_DEBUG("Load PEC from mount failed");
2018 return false;
2019 }
2020 pecdata.RemoveDrift();
2021 // and save it
2022 if (!pecdata.Save(pecFileBuf))
2023 {
2024 LOGF_DEBUG("Save PEC file %s failed", pecFileBuf);
2025 return false;
2026 }
2027 LOGF_INFO("PEC data saved to %s", pecFileBuf);
2028 return true;
2029}
2030
2031// focus control
2033{
2034 uint32_t absPosition = targetTicks;
2035
2036 if (!focuserIsCalibrated)
2037 {
2038 LOG_ERROR("Move is not allowed because the focuser is not calibrated");
2039 return IPS_ALERT;
2040 }
2041
2042 // implement backlash
2043 int delta = static_cast<int>(targetTicks - FocusAbsPosNP[0].getValue());
2044
2045 if ((FocusBacklashNP[0].getValue() < 0 && delta > 0) ||
2046 (FocusBacklashNP[0].getValue() > 0 && delta < 0))
2047 {
2048 focusBacklashMove = true;
2049 focusAbsPosition = absPosition;
2050 absPosition -= FocusBacklashNP[0].getValue();
2051 }
2052
2053 LOGF_INFO("Focus %s move %d", focusBacklashMove ? "backlash" : "direct", absPosition);
2054
2055 if(!driver.foc_move(focusAbs2True(absPosition)))
2056 return IPS_ALERT;
2057
2058 return IPS_BUSY;
2059}
2060
2062{
2063 uint32_t newPosition = 0;
2064
2065 if (dir == FOCUS_INWARD)
2066 newPosition = static_cast<uint32_t>(FocusAbsPosNP[0].getValue()) - ticks;
2067 else
2068 newPosition = static_cast<uint32_t>(FocusAbsPosNP[0].getValue()) + ticks;
2069
2070 // Clamp
2071 newPosition = std::min(static_cast<uint32_t>(FocusAbsPosNP[0].getMax()), newPosition);
2072 return MoveAbsFocuser(newPosition);
2073}
2074
2076{
2077 return driver.foc_abort();
2078}
2079
2080// read the focuser limits from the hardware
2081bool CelestronGPS::focusReadLimits()
2082{
2083 int low = 0, high = 0;
2084 bool valid = driver.foc_limits(&low, &high);
2085
2086 focusTrueMax = high;
2087 focusTrueMin = low;
2088
2089 FocusAbsPosNP[0].setMax(focusTrue2Abs(focusTrueMin));
2090 FocusMaxPosNP[0].setValue(focusTrue2Abs(focusTrueMin));
2093
2096
2097 // FocusMinPosNP[0].setValue(low);
2098 // FocusMinPosNP.s = IPS_OK;
2099 // IDSetNumber(&FocusMinPosNP, nullptr);
2100
2101 focuserIsCalibrated = valid;
2102
2103 LOGF_INFO("Focus Limits: Maximum (%i) Minimum (%i) steps.", high, low);
2104 return valid;
2105}
CELESTRON_DIRECTION
@ CELESTRON_W
@ CELESTRON_N
@ CELESTRON_S
@ CELESTRON_E
@ FW_CAN_AUX
@ FW_MODEL
@ FW_ISGEM
@ FW_VERSION
@ FW_DEC
@ FW_HAS_FOC
@ FW_RA
CELESTRON_SLEW_RATE
@ SR_1
@ SR_5
CELESTRON_TRACK_MODE
@ CTM_EQN
@ CTM_ALTAZ
@ CTM_EQS
@ CTM_OFF
@ CTM_RADEC
#define ISSTARSENSE
#define MINSTSENSVER
CELESTRON_TRACK_RATE
@ CTR_SIDEREAL
@ CTR_SOLAR
@ CTR_LUNAR
PEC_STATE
@ NotKnown
@ PEC_SEEKING
The PEC index is being searched for, goes to PEC_INDEXED when found.
@ PEC_AVAILABLE
PEC is available but inactive, can seek index Seek index is only available command.
@ PEC_PLAYBACK
PEC is being played back, stays in this state until stopped equivalent to TelescopePECState PEC_ON.
@ PEC_RECORDING
PEC is being recorded, goes to PEC_INDEXED when completed.
@ PEC_INDEXED
the PEC index has been found, can go to Playback or Recording this is equivalent to TelescopePECState...
@ DEC_AXIS
@ RA_AXIS
#define ISNEXSTAR
#define SLEW_RATE
#define GOTO_RATE
#define FINE_SLEW_RATE
#define MOUNTINFO_TAB
#define SLEW_LIMIT
#define GOTO_LIMIT
bool slew_radec(double ra, double dec, bool precise)
bool get_azalt(double *az, double *alt, bool precise)
bool set_guide_rate(CELESTRON_AXIS axis, uint8_t rate)
void set_port_fd(int port_fd)
bool indexreached(bool *atIndex)
bool check_aligned(bool *isAligned)
bool set_datetime(struct ln_date *utc, double utc_offset, bool dst=false, bool precise=false)
bool set_location(double longitude, double latitude)
bool stop_motion(CELESTRON_DIRECTION dir)
const char * PecStateStr(PEC_STATE)
void set_sim_track_mode(CELESTRON_TRACK_MODE val)
bool get_firmware(FirmwareInfo *info)
bool sync(double ra, double dec, bool precise)
bool foc_limits(int *low, int *high)
bool PecPlayback(bool start)
size_t send_pulse(CELESTRON_DIRECTION direction, unsigned char rate, unsigned char duration_msec)
bool get_radec(double *ra, double *dec, bool precise)
bool get_track_mode(CELESTRON_TRACK_MODE *mode)
void set_device(const char *name)
void set_simulation(bool enable)
bool set_track_rate(CELESTRON_TRACK_RATE rate, CELESTRON_TRACK_MODE mode)
bool set_track_mode(CELESTRON_TRACK_MODE mode)
bool PecRecord(bool start)
bool isPecAtIndex(bool force=false)
bool get_location(double *longitude, double *latitude)
void set_sim_ra(double ra)
bool get_pulse_status(CELESTRON_DIRECTION direction)
void set_sim_slewing(bool isSlewing)
PEC_STATE updatePecState()
bool get_guide_rate(CELESTRON_AXIS axis, uint8_t *rate)
void set_sim_dec(double dec)
bool slew_azalt(double az, double alt, bool precise)
void set_sim_slew_rate(CELESTRON_SLEW_RATE val)
bool foc_move(uint32_t steps)
bool get_pier_side(char *sop)
bool is_slewing(bool *slewing)
bool get_utc_date_time(double *utc_hours, int *yy, int *mm, int *dd, int *hh, int *minute, int *ss, bool *dst, bool precise)
bool start_motion(CELESTRON_DIRECTION dir, CELESTRON_SLEW_RATE rate)
virtual bool Handshake() override
perform handshake with device to check communication
virtual bool MoveWE(INDI_DIR_WE dir, TelescopeMotionCommand command) override
Move the telescope in the direction dir.
ITextVectorProperty PecFileNameTP
virtual IPState GuideWest(uint32_t ms) override
Guide west for ms milliseconds. West is defined as RA-.
virtual bool initProperties() override
Called to initialize basic properties required all the time.
IPState MoveAbsFocuser(uint32_t targetTicks) override
MoveFocuser the focuser to an absolute 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 Goto(double ra, double dec) override
Move the scope to the supplied RA and DEC coordinates.
virtual bool updateLocation(double latitude, double longitude, double elevation) override
Update telescope location settings.
virtual IPState GuideSouth(uint32_t ms) override
Guide south for ms milliseconds. South is defined as DEC-.
virtual bool SetTrackEnabled(bool enabled) override
SetTrackEnabled Engages or disengages mount tracking. If there are no tracking modes available,...
virtual IPState GuideEast(uint32_t ms) override
Guide east for ms milliseconds. East is defined as RA+.
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
ISwitch UseHibernateS[2]
IText PecInfoT[2]
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
ISwitchVectorProperty PecControlSP
uint8_t guideRateRa
static void guideTimerHelperE(void *p)
static void guideTimerHelperS(void *p)
virtual bool UnPark() override
Unpark the telescope if already parked.
INumber GuideRateN[2]
ISwitch PecControlS[4]
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.
ITextVectorProperty FirmwareTP
void guideTimer(CELESTRON_DIRECTION dirn)
static void guideTimerHelperW(void *p)
IPState Guide(CELESTRON_DIRECTION dirn, uint32_t ms)
void AddGuideTimer(CELESTRON_DIRECTION dirn, int ms)
bool AbortFocuser() override
AbortFocuser all focus motion.
IText FirmwareT[7]
IPState MoveRelFocuser(FocusDirection dir, uint32_t ticks) override
MoveFocuser the focuser to an relative position.
virtual bool SetCurrentPark() override
SetCurrentPark Set current coordinates/encoders value as the desired parking position.
INumberVectorProperty GuideRateNP
virtual const char * getDefaultName() override
ITextVectorProperty PecInfoTP
virtual bool ReadScopeStatus() override
Read telescope status.
virtual bool SetFocuserBacklash(int32_t steps) override
SetFocuserBacklash Set the focuser backlash compensation value.
virtual bool Park() override
Park the telescope to its home position.
virtual IPState GuideNorth(uint32_t ms) override
Guide north for ms milliseconds. North is defined as DEC+.
virtual void simulationTriggered(bool enable) override
Inform driver that the simulation option was triggered. This function is called after setSimulation i...
ISwitchVectorProperty CelestronTrackModeSP
uint8_t guideRateDec
IText PecFileNameT[1]
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
ISwitchVectorProperty UseHibernateSP
virtual bool SetDefaultPark() override
SetDefaultPark Set default coordinates/encoders value as the desired parking position.
virtual bool updateTime(ln_date *utc, double utc_offset) override
Update telescope time, date, and UTC offset.
static void guideTimerHelperN(void *p)
ISwitch CelestronTrackModeS[4]
virtual bool SetTrackMode(uint8_t mode) override
SetTrackMode Set active tracking mode. Do not change track state.
virtual bool ISNewText(const char *dev, const char *name, char **texts, char **names, int n) override
virtual bool Abort() override
Abort any telescope motion including tracking if possible.
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...
Class to provide general functionality of a GPS device.
bool isConnected() 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....
void setVersion(uint16_t vMajor, uint16_t vMinor)
Set driver version information to be defined in DRIVER_INFO property as vMajor.vMinor.
virtual bool loadConfig(bool silent=false, const char *property=nullptr)
Load the last saved configuration file.
virtual bool deleteProperty(const char *propertyName)
Delete a property and unregister it. It will also be deleted from all clients.
void defineProperty(INumberVectorProperty *property)
bool isSimulation() const
void syncDriverInfo()
syncDriverInfo sends the current driver information to the client.
void addAuxControls()
Add Debug, Simulation, and Configuration options to the driver.
uint32_t getDriverInterface() const
Provides interface to implement focuser functionality.
INDI::PropertyNumber FocusRelPosNP
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.
void initProperties(const char *groupName)
Initialize focuser properties. It is recommended to call this function within initProperties() of you...
INDI::PropertyNumber FocusMaxPosNP
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 FocusBacklashNP
INDI::PropertyNumber FocusAbsPosNP
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)
void apply(const char *format,...) const ATTRIBUTE_FORMAT_PRINTF(2
const char * getName() const
const char * getLabel() const
void setPermission(IPerm permission)
TelescopeStatus TrackState
void SetAxis1Park(double value)
SetRAPark Set current RA/AZ parking position. The data park file (stored in ~/.indi/ParkData....
void SetAxis1ParkDefault(double steps)
SetRAPark Set default RA/AZ parking position.
void SetTelescopeCapability(uint32_t cap, uint8_t slewRateCount)
SetTelescopeCapability sets the Telescope capabilities. All capabilities must be initialized.
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.
double GetAxis1Park() const
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...
double GetAxis2Park() const
bool isParked()
isParked is mount currently parked?
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....
const char * LoadParkData()
virtual bool ISNewNumber(const char *dev, const char *name, double values[], char *names[], int n) override
Process the client newNumber command.
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...
virtual bool saveConfigItems(FILE *fp) override
saveConfigItems Save specific properties in the provide config file handler. Child class usually over...
void SetAxis2Park(double steps)
SetDEPark Set current DEC/ALT parking position. The data park file (stored in ~/.indi/ParkData....
virtual bool ISNewSwitch(const char *dev, const char *name, ISState *states, char *names[], int n) override
Process the client newSwitch command.
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.
bool Load(CelestronDriver *driver)
bool Save(const char *filename)
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.
const char * OPTIONS_TAB
OPTIONS_TAB Where all the driver's options are located. Those may include auxiliary controls,...
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
int errno
TelescopePierSide currentPierSide
INDI::PropertySwitch MovementNSSP
INDI::PropertyNumber EqNP
INDI::PropertySwitch TrackStateSP
INDI::PropertySwitch SlewRateSP
INDI::PropertyText TimeTP
INDI::PropertySwitch MountTypeSP
INDI::PropertySwitch MovementWESP
INDI::PropertyNumber LocationNP
#define currentDEC
Definition ieq45.cpp:48
#define currentRA
Definition ieq45.cpp:47
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
@ IP_WO
Definition indiapi.h:183
IPState
Property state.
Definition indiapi.h:158
@ IPS_BUSY
Definition indiapi.h:161
@ IPS_ALERT
Definition indiapi.h:162
@ IPS_IDLE
Definition indiapi.h:159
@ IPS_OK
Definition indiapi.h:160
@ ISR_1OFMANY
Definition indiapi.h:171
@ ISR_NOFMANY
Definition indiapi.h:173
@ ISR_ATMOST1
Definition indiapi.h:172
@ AXIS_DE
@ AXIS_RA
INDI_DIR_WE
@ DIRECTION_EAST
@ DIRECTION_WEST
INDI_DIR_NS
@ DIRECTION_SOUTH
@ DIRECTION_NORTH
double range24(double r)
range24 Limits a number to be between 0-24 range.
Definition indicom.c:1311
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.
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 IUUpdateText(ITextVectorProperty *tvp, char *texts[], char *names[], int n)
Update all text members in a text vector property.
#define MAXRBUF
Definition indidriver.c:52
int IUUpdateNumber(INumberVectorProperty *nvp, double values[], char *names[], int n)
Update all numbers in a number vector property.
void IDSetText(const ITextVectorProperty *tvp, const char *fmt,...)
int IUGetConfigSwitch(const char *dev, const char *property, const char *member, ISState *value)
IUGetConfigSwitch Opens configuration file and reads single switch property.
Definition indidriver.c:657
#define LOGF_INFO(fmt,...)
Definition indilogger.h:82
#define LOG_DEBUG(txt)
Definition indilogger.h:75
#define LOGF_WARN(fmt,...)
Definition indilogger.h:81
#define LOG_WARN(txt)
Definition indilogger.h:73
#define LOGF_DEBUG(fmt,...)
Definition indilogger.h:83
#define LOG_ERROR(txt)
Shorter logging macros. In order to use these macros, the function (or method) "getDeviceName()" must...
Definition indilogger.h:72
#define LOGF_ERROR(fmt,...)
Definition indilogger.h:80
#define LOG_INFO(txt)
Definition indilogger.h:74
#define TRACKRATE_SIDEREAL
Definition indimacros.h:39
#define getDeviceName
int mkpath(std::string s, mode_t mode)
double controllerVersion
std::string Version
std::string RAFirmware
CELESTRON_TRACK_MODE celestronTrackMode
std::string Model
std::string DEFirmware
static Logger & getInstance()
Method to get a reference to the object (i.e., Singleton) It is a static method.
int addDebugLevel(const char *debugLevelName, const char *LoggingLevelName)
Adds a new debugging level to the driver.
char name[MAXINDINAME]
Definition indiapi.h:321
char name[MAXINDINAME]
Definition indiapi.h:369
char name[MAXINDINAME]
Definition indiapi.h:248