Kstars

indimount.cpp
1/*
2 SPDX-FileCopyrightText: 2012 Jasem Mutlaq <mutlaqja@ikarustech.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "indimount.h"
8
9#include "ksmessagebox.h"
10#include "driverinfo.h"
11#include "kstars.h"
12#include "Options.h"
13#include "skymap.h"
14#include "skymapcomposite.h"
15#include "ksnotification.h"
16
17#include <KActionCollection>
18#include <KLocalizedString>
19
20#include <QAction>
21#include <qdbusmetatype.h>
22
23#include <indi_debug.h>
24
25// Qt version calming
26#include <qtendl.h>
27
28namespace ISD
29{
30
31const QList<const char *> Mount::mountStates = { I18N_NOOP("Idle"), I18N_NOOP("Moving"), I18N_NOOP("Slewing"),
32 I18N_NOOP("Tracking"), I18N_NOOP("Parking"), I18N_NOOP("Parked"),
33 I18N_NOOP("Error")
34 };
35
36Mount::Mount(GenericDevice *parent) : ConcreteDevice(parent)
37{
38 // Set it for 5 seconds for now as not to spam the display update
39 centerLockTimer.setInterval(5000);
40 centerLockTimer.setSingleShot(true);
41 connect(&centerLockTimer, &QTimer::timeout, this, [this]()
42 {
43 //runCommand(INDI_CENTER_LOCK);
44 centerLock();
45 });
46
47 // Regularly update the coordinates even if no update has been sent from the INDI service
48 updateCoordinatesTimer.setInterval(1000);
49 updateCoordinatesTimer.setSingleShot(false);
50 connect(&updateCoordinatesTimer, &QTimer::timeout, this, [this]()
51 {
52 if (isConnected())
53 {
54 currentCoords.EquatorialToHorizontal(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat());
55 emit newCoords(currentCoords, pierSide(), hourAngle());
56 }
57 });
58
59 qRegisterMetaType<ISD::Mount::Status>("ISD::Mount::Status");
61
62 qRegisterMetaType<ISD::Mount::PierSide>("ISD::Mount::PierSide");
64
65 // Need to delay check for alignment model to upon connection is established since the property is defined BEFORE Telescope class is created.
66 // and therefore no registerProperty is called for these properties since they were already registered _before_ the Telescope
67 // class was created.
68 m_hasAlignmentModel = getProperty("ALIGNMENT_POINTSET_ACTION").isValid() || getProperty("ALIGNLIST").isValid();
69}
70
71void Mount::registerProperty(INDI::Property prop)
72{
73 if (prop.isNameMatch("TELESCOPE_INFO"))
74 {
75 auto ti = prop.getNumber();
76
77 if (!ti)
78 return;
79
80 bool aperture_ok = false, focal_ok = false;
81 double temp = 0;
82
83 auto aperture = ti->findWidgetByName("TELESCOPE_APERTURE");
84 if (aperture && aperture->getValue() <= 0)
85 {
86 if (getDriverInfo()->getAuxInfo().contains("TELESCOPE_APERTURE"))
87 {
88 temp = getDriverInfo()->getAuxInfo().value("TELESCOPE_APERTURE").toDouble(&aperture_ok);
89 if (aperture_ok)
90 {
91 aperture->setValue(temp);
92 auto g_aperture = ti->findWidgetByName("GUIDER_APERTURE");
93 if (g_aperture && g_aperture->getValue() <= 0)
94 g_aperture->setValue(aperture->getValue());
95 }
96 }
97 }
98
99 auto focal_length = ti->findWidgetByName("TELESCOPE_FOCAL_LENGTH");
100 if (focal_length && focal_length->getValue() <= 0)
101 {
102 if (getDriverInfo()->getAuxInfo().contains("TELESCOPE_FOCAL_LENGTH"))
103 {
104 temp = getDriverInfo()->getAuxInfo().value("TELESCOPE_FOCAL_LENGTH").toDouble(&focal_ok);
105 if (focal_ok)
106 {
107 focal_length->setValue(temp);
108 auto g_focal = ti->findWidgetByName("GUIDER_FOCAL_LENGTH");
109 if (g_focal && g_focal->getValue() <= 0)
110 g_focal->setValue(focal_length->getValue());
111 }
112 }
113 }
114
115 if (aperture_ok && focal_ok)
116 sendNewProperty(ti);
117 }
118 else if (prop.isNameMatch("ON_COORD_SET"))
119 {
120 m_canGoto = IUFindSwitch(prop.getSwitch(), "TRACK") != nullptr;
121 m_canSync = IUFindSwitch(prop.getSwitch(), "SYNC") != nullptr;
122 m_canFlip = IUFindSwitch(prop.getSwitch(), "FLIP") != nullptr;
123 }
124 else if (prop.isNameMatch("TELESCOPE_PIER_SIDE"))
125 {
126 auto svp = prop.getSwitch();
127 int currentSide = svp->findOnSwitchIndex();
128 if (currentSide != m_PierSide)
129 {
130 m_PierSide = static_cast<PierSide>(currentSide);
131 emit pierSideChanged(m_PierSide);
132 }
133 }
134 else if (prop.isNameMatch("TELESCOPE_PARK"))
135 updateParkStatus();
136 else if (prop.isNameMatch("TELESCOPE_TRACK_STATE"))
137 m_canControlTrack = true;
138 else if (prop.isNameMatch("TELESCOPE_TRACK_MODE"))
139 {
140 m_hasTrackModes = true;
141 auto svp = prop.getSwitch();
142 for (int i = 0; i < svp->count(); i++)
143 {
144 if (svp->at(i)->isNameMatch("TRACK_SIDEREAL"))
145 TrackMap[TRACK_SIDEREAL] = i;
146 else if (svp->at(i)->isNameMatch("TRACK_SOLAR"))
147 TrackMap[TRACK_SOLAR] = i;
148 else if (svp->at(i)->isNameMatch("TRACK_LUNAR"))
149 TrackMap[TRACK_LUNAR] = i;
150 else if (svp->at(i)->isNameMatch("TRACK_CUSTOM"))
151 TrackMap[TRACK_CUSTOM] = i;
152 }
153 }
154 else if (prop.isNameMatch("TELESCOPE_TRACK_RATE"))
155 m_hasCustomTrackRate = true;
156 else if (prop.isNameMatch("TELESCOPE_ABORT_MOTION"))
157 m_canAbort = true;
158 else if (prop.isNameMatch("TELESCOPE_PARK_OPTION"))
159 m_hasCustomParking = true;
160 else if (prop.isNameMatch("TELESCOPE_SLEW_RATE"))
161 {
162 m_hasSlewRates = true;
163 auto svp = prop.getSwitch();
164 if (svp)
165 {
166 m_slewRates.clear();
167 for (const auto &it : *svp)
168 m_slewRates << it.getLabel();
169 }
170 }
171 else if (prop.isNameMatch("EQUATORIAL_EOD_COORD"))
172 {
173 m_isJ2000 = false;
174 m_hasEquatorialCoordProperty = true;
175 }
176 else if (prop.isNameMatch("SAT_TRACKING_STAT"))
177 {
178 m_canTrackSatellite = true;
179 }
180 else if (prop.isNameMatch("EQUATORIAL_COORD"))
181 {
182 m_isJ2000 = true;
183 m_hasEquatorialCoordProperty = true;
184 }
185}
186
187void Mount::updateJ2000Coordinates(SkyPoint *coords)
188{
189 SkyPoint J2000Coord(coords->ra(), coords->dec());
190 J2000Coord.catalogueCoord(KStars::Instance()->data()->ut().djd());
191 coords->setRA0(J2000Coord.ra());
192 coords->setDec0(J2000Coord.dec());
193}
194
196{
197 emit newTarget(currentCoords);
198 double maxrad = 0.1;
199 currentObject = KStarsData::Instance()->skyComposite()->objectNearest(&currentCoords, maxrad);
200 if (currentObject)
201 emit newTargetName(currentObject->name());
202 // If there is no object, we must clear target as it might give wrong
203 // indication we are still on it.
204 else
206}
207
208void Mount::processNumber(INDI::Property prop)
209{
210 auto nvp = prop.getNumber();
211 if (nvp->isNameMatch("EQUATORIAL_EOD_COORD") || nvp->isNameMatch("EQUATORIAL_COORD"))
212 {
213 auto RA = nvp->findWidgetByName("RA");
214 auto DEC = nvp->findWidgetByName("DEC");
215
216 if (RA == nullptr || DEC == nullptr)
217 return;
218
219 // set both JNow and J2000 coordinates
220 if (isJ2000())
221 {
222 currentCoords.setRA0(RA->value);
223 currentCoords.setDec0(DEC->value);
224 currentCoords.apparentCoord(static_cast<long double>(J2000), KStars::Instance()->data()->ut().djd());
225 }
226 else
227 {
228 currentCoords.setRA(RA->value);
229 currentCoords.setDec(DEC->value);
230 // calculate J2000 coordinates
231 updateJ2000Coordinates(&currentCoords);
232 }
233
234 // calculate horizontal coordinates
235 currentCoords.EquatorialToHorizontal(KStars::Instance()->data()->lst(),
236 KStars::Instance()->data()->geo()->lat());
237 // ensure that coordinates are regularly updated
238 if (! updateCoordinatesTimer.isActive())
239 updateCoordinatesTimer.start();
240
241 auto currentStatus = status(nvp);
242
243 if (nvp->getState() == IPS_BUSY && EqCoordPreviousState != IPS_BUSY)
244 {
245 if (currentStatus == MOUNT_SLEWING)
246 KSNotification::event(QLatin1String("SlewStarted"), i18n("Mount is slewing to target location"), KSNotification::Mount);
248 }
249 else if (EqCoordPreviousState == IPS_BUSY && nvp->getState() == IPS_OK && slewDefined())
250 {
251 if (Options::useExternalSkyMap())
252 {
253 // For external skymaps the only way to determine the target is to take the position where the mount
254 // starts to track
255 updateTarget();
256 }
257 else
258 {
259 // In case that we use KStars as skymap, we intentionally do not communicate the target here, since it
260 // has been set at the beginning of the slew AND we cannot be sure that the position the INDI
261 // mount reports when starting to track is exactly that one where the slew went to.
262 KSNotification::event(QLatin1String("SlewCompleted"), i18n("Mount arrived at target location"), KSNotification::Mount);
263 }
265 }
266
267 EqCoordPreviousState = nvp->getState();
268
269 KStars::Instance()->map()->update();
270 }
271 // JM 2022.03.11 Only process HORIZONTAL_COORD if it was the ONLY source of information
272 // When a driver both sends EQUATORIAL_COORD and HORIZONTAL_COORD, we should prioritize EQUATORIAL_COORD
273 // especially since the conversion from horizontal to equatorial is not as accurate and can result in weird
274 // coordinates near the poles.
275 else if (nvp->isNameMatch("HORIZONTAL_COORD") && m_hasEquatorialCoordProperty == false)
276 {
277 auto Az = nvp->findWidgetByName("AZ");
278 auto Alt = nvp->findWidgetByName("ALT");
279
280 if (Az == nullptr || Alt == nullptr)
281 return;
282
283 currentCoords.setAz(Az->value);
284 currentCoords.setAlt(Alt->value);
285 currentCoords.HorizontalToEquatorial(KStars::Instance()->data()->lst(),
286 KStars::Instance()->data()->geo()->lat());
287
288 // calculate J2000 coordinates
289 updateJ2000Coordinates(&currentCoords);
290
291 // ensure that coordinates are regularly updated
292 if (! updateCoordinatesTimer.isActive())
293 updateCoordinatesTimer.start();
294
295 KStars::Instance()->map()->update();
296 }
297 else if (nvp->isNameMatch("POLLING_PERIOD"))
298 {
299 // set the timer how often the coordinates should be published
300 auto period = nvp->findWidgetByName("PERIOD_MS");
301 if (period != nullptr)
302 updateCoordinatesTimer.setInterval(static_cast<int>(period->getValue()));
303
304 }
305}
306
307void Mount::processSwitch(INDI::Property prop)
308{
309 bool manualMotionChanged = false;
310 auto svp = prop.getSwitch();
311
312 if (svp->isNameMatch("CONNECTION"))
313 {
314 auto conSP = svp->findWidgetByName("CONNECT");
315 if (conSP)
316 {
317 // TODO We must allow for multiple mount drivers to be online, not just one
318 // For the actions taken, the user should be able to specify which mounts shall receive the commands. It could be one
319 // or more. For now, we enable/disable telescope group on the assumption there is only one mount present.
320 if (conSP->getState() == ISS_ON)
321 KStars::Instance()->slotSetTelescopeEnabled(true);
322 else
323 {
324 KStars::Instance()->slotSetTelescopeEnabled(false);
325 centerLockTimer.stop();
326 }
327 }
328 }
329 else if (svp->isNameMatch("TELESCOPE_PARK"))
331 else if (svp->isNameMatch("TELESCOPE_ABORT_MOTION"))
332 {
333 if (svp->s == IPS_OK)
334 {
335 inCustomParking = false;
336 KSNotification::event(QLatin1String("MountAborted"), i18n("Mount motion was aborted"), KSNotification::Mount,
337 KSNotification::Warn);
338 }
339 }
340 else if (svp->isNameMatch("TELESCOPE_PIER_SIDE"))
341 {
343 if (currentSide != m_PierSide)
344 {
345 m_PierSide = static_cast<PierSide>(currentSide);
346 emit pierSideChanged(m_PierSide);
347 }
348 }
349 else if (svp->isNameMatch("TELESCOPE_TRACK_MODE"))
350 {
351 auto sp = svp->findOnSwitch();
352 if (sp)
353 {
354 if (sp->isNameMatch("TRACK_SIDEREAL"))
355 currentTrackMode = TRACK_SIDEREAL;
356 else if (sp->isNameMatch("TRACK_SOLAR"))
357 currentTrackMode = TRACK_SOLAR;
358 else if (sp->isNameMatch("TRACK_LUNAR"))
359 currentTrackMode = TRACK_LUNAR;
360 else
361 currentTrackMode = TRACK_CUSTOM;
362 }
363 }
364 else if (svp->isNameMatch("TELESCOPE_MOTION_NS"))
365 manualMotionChanged = true;
366 else if (svp->isNameMatch("TELESCOPE_MOTION_WE"))
367 manualMotionChanged = true;
368 else if (svp->isNameMatch("TELESCOPE_REVERSE_MOTION"))
369 {
370 emit axisReversed(AXIS_DE, svp->at(0)->getState() == ISS_ON);
371 emit axisReversed(AXIS_RA, svp->at(1)->getState() == ISS_ON);
372 }
373
375 {
376 auto NSCurrentMotion = getSwitch("TELESCOPE_MOTION_NS")->getState();
377 auto WECurrentMotion = getSwitch("TELESCOPE_MOTION_WE")->getState();
378 inCustomParking = false;
379 inManualMotion = (NSCurrentMotion == IPS_BUSY || WECurrentMotion == IPS_BUSY);
380 }
381}
382
383void Mount::processText(INDI::Property prop)
384{
385 auto tvp = prop.getText();
386 if (tvp->isNameMatch("SAT_TLE_TEXT"))
387 {
388 if ((tvp->getState() == IPS_OK) && (m_TLEIsSetForTracking))
389 {
390 auto trajWindow = getText("SAT_PASS_WINDOW");
391 if (!trajWindow)
392 {
393 qCDebug(KSTARS_INDI) << "Property SAT_PASS_WINDOW not found";
394 }
395 else
396 {
397 auto trajStart = trajWindow->findWidgetByName("SAT_PASS_WINDOW_START");
398 auto trajEnd = trajWindow->findWidgetByName("SAT_PASS_WINDOW_END");
399
400 if (!trajStart || !trajEnd)
401 {
402 qCDebug(KSTARS_INDI) << "Start or end in SAT_PASS_WINDOW not found";
403 }
404 else
405 {
406 trajStart->setText(g_satPassStart.toString(Qt::ISODate).toLocal8Bit().data());
407 trajEnd->setText(g_satPassEnd.toString(Qt::ISODate).toLocal8Bit().data());
408
410 m_windowIsSetForTracking = true;
411 }
412 }
413 }
414 }
415 else if (tvp->isNameMatch("SAT_PASS_WINDOW"))
416 {
417 if ((tvp->getState() == IPS_OK) && (m_TLEIsSetForTracking) && (m_windowIsSetForTracking))
418 {
419 auto trackSwitchV = getSwitch("SAT_TRACKING_STAT");
420 if (!trackSwitchV)
421 {
422 qCDebug(KSTARS_INDI) << "Property SAT_TRACKING_STAT not found";
423 }
424 else
425 {
426 auto trackSwitch = trackSwitchV->findWidgetByName("SAT_TRACK");
427 if (trackSwitch)
428 {
429 trackSwitchV->reset();
430 trackSwitch->setState(ISS_ON);
431
433 m_TLEIsSetForTracking = false;
434 m_windowIsSetForTracking = false;
435 }
436 }
437 }
438 }
439}
440
442{
443 auto svp = getSwitch("TELESCOPE_PARK");
444 if (!svp)
445 return;
446
447 auto sp = svp->findWidgetByName("PARK");
448 if (sp)
449 {
450 if (svp->getState() == IPS_ALERT)
451 {
452 // First, inform everyone watch this that an error occurred.
453 emit newParkStatus(PARK_ERROR);
454 // JM 2021-03-08: Reset parking internal state to either PARKED or UNPARKED.
455 // Whatever the current switch is set to
456 m_ParkStatus = (sp->getState() == ISS_ON) ? PARK_PARKED : PARK_UNPARKED;
457 KSNotification::event(QLatin1String("MountParkingFailed"), i18n("Mount parking failed"), KSNotification::Mount,
458 KSNotification::Alert);
459 }
460 else if (svp->getState() == IPS_BUSY && sp->s == ISS_ON && m_ParkStatus != PARK_PARKING)
461 {
462 m_ParkStatus = PARK_PARKING;
463 KSNotification::event(QLatin1String("MountParking"), i18n("Mount parking is in progress"), KSNotification::Mount);
464 currentObject = nullptr;
465
466 emit newParkStatus(m_ParkStatus);
467 }
468 else if (svp->getState() == IPS_BUSY && sp->getState() == ISS_OFF && m_ParkStatus != PARK_UNPARKING)
469 {
470 m_ParkStatus = PARK_UNPARKING;
471 KSNotification::event(QLatin1String("MountUnParking"), i18n("Mount unparking is in progress"), KSNotification::Mount);
472
473 emit newParkStatus(m_ParkStatus);
474 }
475 else if (svp->getState() == IPS_OK && sp->getState() == ISS_ON && m_ParkStatus != PARK_PARKED)
476 {
477 m_ParkStatus = PARK_PARKED;
478 KSNotification::event(QLatin1String("MountParked"), i18n("Mount parked"), KSNotification::Mount);
479 currentObject = nullptr;
480
481 emit newParkStatus(m_ParkStatus);
482
483 QAction *parkAction = KStars::Instance()->actionCollection()->action("telescope_park");
484 if (parkAction)
485 parkAction->setEnabled(false);
486 QAction *unParkAction = KStars::Instance()->actionCollection()->action("telescope_unpark");
487 if (unParkAction)
488 unParkAction->setEnabled(true);
489
490 emit newTarget(currentCoords);
491 }
492 else if ( (svp->getState() == IPS_OK || svp->getState() == IPS_IDLE) && sp->getState() == ISS_OFF
493 && m_ParkStatus != PARK_UNPARKED)
494 {
495 m_ParkStatus = PARK_UNPARKED;
496 KSNotification::event(QLatin1String("MountUnparked"), i18n("Mount unparked"), KSNotification::Mount);
497 currentObject = nullptr;
498
499 emit newParkStatus(m_ParkStatus);
500
501 QAction *parkAction = KStars::Instance()->actionCollection()->action("telescope_park");
502 if (parkAction)
503 parkAction->setEnabled(true);
504 QAction *unParkAction = KStars::Instance()->actionCollection()->action("telescope_unpark");
505 if (unParkAction)
506 unParkAction->setEnabled(false);
507 }
508 }
509}
510bool Mount::canGuide()
511{
512 auto raPulse = getNumber("TELESCOPE_TIMED_GUIDE_WE");
513 auto decPulse = getNumber("TELESCOPE_TIMED_GUIDE_NS");
514
515 return raPulse && decPulse;
516}
517
518bool Mount::canPark()
519{
520 auto parkSP = getSwitch("TELESCOPE_PARK");
521
522 if (!parkSP)
523 return false;
524
525 auto parkSW = parkSP->findWidgetByName("PARK");
526
527 return (parkSW != nullptr);
528}
529
530bool Mount::isSlewing()
531{
532 auto EqProp = getNumber("EQUATORIAL_EOD_COORD");
533
534 if (!EqProp)
535 return false;
536
537 return (EqProp->getState() == IPS_BUSY);
538}
539
540bool Mount::isInMotion()
541{
542 return (isSlewing() || inManualMotion);
543}
544
545bool Mount::doPulse(GuideDirection ra_dir, int ra_msecs, GuideDirection dec_dir, int dec_msecs)
546{
547 if (canGuide() == false)
548 return false;
549
550 bool raOK = doPulse(ra_dir, ra_msecs);
551 bool decOK = doPulse(dec_dir, dec_msecs);
552
553 return raOK && decOK;
554}
555
556bool Mount::doPulse(GuideDirection dir, int msecs)
557{
558 auto raPulse = getNumber("TELESCOPE_TIMED_GUIDE_WE");
559 auto decPulse = getNumber("TELESCOPE_TIMED_GUIDE_NS");
560 INDI::PropertyView<INumber> *npulse = nullptr;
561 INDI::WidgetView<INumber> *dirPulse = nullptr;
562
563 if (!raPulse || !decPulse)
564 return false;
565
566 switch (dir)
567 {
568 case RA_INC_DIR:
569 npulse = raPulse;
570 dirPulse = npulse->findWidgetByName("TIMED_GUIDE_W");
571 break;
572
573 case RA_DEC_DIR:
574 npulse = raPulse;
575 dirPulse = npulse->findWidgetByName("TIMED_GUIDE_E");
576 break;
577
578 case DEC_INC_DIR:
580 dirPulse = npulse->findWidgetByName("TIMED_GUIDE_N");
581 break;
582
583 case DEC_DEC_DIR:
585 dirPulse = npulse->findWidgetByName("TIMED_GUIDE_S");
586 break;
587
588 default:
589 return false;
590 }
591
592 if (!dirPulse)
593 return false;
594
595 dirPulse->setValue(msecs);
596
598
599 return true;
600}
601
602
603void Mount::setCustomParking(SkyPoint * coords)
604{
605 bool rc = false;
606 if (coords == nullptr)
607 rc = sendCoords(KStars::Instance()->map()->clickedPoint());
608 else
609 rc = sendCoords(coords);
610
611 inCustomParking = rc;
612}
613
614void Mount::find()
615{
616 updateJ2000Coordinates(&currentCoords);
617 double maxrad = 1000.0 / Options::zoomFactor();
618 SkyObject *currentObject = KStarsData::Instance()->skyComposite()->objectNearest(&currentCoords, maxrad);
619 KStars::Instance()->map()->setFocusObject(currentObject);
620 KStars::Instance()->map()->setDestination(currentCoords);
621}
622void Mount::centerLock()
623{
624 if (Options::isTracking() == false ||
625 currentCoords.angularDistanceTo(KStars::Instance()->map()->focus()).Degrees() > 0.5)
626 {
627 updateJ2000Coordinates(&currentCoords);
628 KStars::Instance()->map()->setDestination(currentCoords);
629 KStars::Instance()->map()->setFocusPoint(&currentCoords);
630 KStars::Instance()->map()->setFocusObject(nullptr);
631 Options::setIsTracking(true);
632 }
633 centerLockTimer.start();
634}
635
636void Mount::centerUnlock()
637{
638 KStars::Instance()->map()->stopTracking();
639 centerLockTimer.stop();
640}
641
643{
644 INumber *RAEle = nullptr;
645 INumber *DecEle = nullptr;
646 INumber *AzEle = nullptr;
647 INumber *AltEle = nullptr;
648 double currentRA = 0, currentDEC = 0, currentAlt = 0, currentAz = 0;
649 bool useJ2000(false);
650
651 auto EqProp = getNumber("EQUATORIAL_EOD_COORD");
652 if (!EqProp)
653 {
654 // J2000 Property
655 EqProp = getNumber("EQUATORIAL_COORD");
656 if (EqProp)
657 useJ2000 = true;
658 }
659
660 auto HorProp = getNumber("HORIZONTAL_COORD");
661
662 if (EqProp && EqProp->getPermission() == IP_RO)
663 EqProp = nullptr;
664
665 if (HorProp && HorProp->getPermission() == IP_RO)
666 HorProp = nullptr;
667
668 //qDebug() << Q_FUNC_INFO << "Skymap click - RA: " << scope_target->ra().toHMSString() << " DEC: " << scope_target->dec().toDMSString();
669
670 if (EqProp)
671 {
672 RAEle = EqProp->findWidgetByName("RA");
673 if (!RAEle)
674 return false;
675
676 DecEle = EqProp->findWidgetByName("DEC");
677 if (!DecEle)
678 return false;
679
680 //if (useJ2000)
681 //ScopeTarget->apparentCoord( KStars::Instance()->data()->ut().djd(), static_cast<long double>(J2000));
682
683 currentRA = RAEle->value;
684 currentDEC = DecEle->value;
685
686 ScopeTarget->EquatorialToHorizontal(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat());
687 }
688
689 if (HorProp)
690 {
691 AzEle = IUFindNumber(HorProp, "AZ");
692 if (!AzEle)
693 return false;
694 AltEle = IUFindNumber(HorProp, "ALT");
695 if (!AltEle)
696 return false;
697
698 currentAz = AzEle->value;
699 currentAlt = AltEle->value;
700 }
701
702 /* Could not find either properties! */
703 if (EqProp == nullptr && HorProp == nullptr)
704 return false;
705
706 // Function for sending the coordinates to the INDI mount device
707 // via the ClientManager. This helper function translates EKOS objects into INDI commands.
708 auto sendToMountDevice = [ = ]()
709 {
710 // communicate the new target only if a slew will be executed for the given coordinates
711 if (slewDefined())
712 {
714 if (currentObject)
715 emit newTargetName(currentObject->name());
716 // If there is no object, we must clear target as it might give wrong
717 // indication we are still on it.
718 else
720 }
721
722 if (EqProp)
723 {
724 dms ra, de;
725
726 if (useJ2000)
727 {
728 // If we have invalid DEC, then convert coords to J2000
729 if (ScopeTarget->dec0().Degrees() == 180.0)
730 {
731 ScopeTarget->setRA0(ScopeTarget->ra());
732 ScopeTarget->setDec0(ScopeTarget->dec());
733 ScopeTarget->catalogueCoord( KStars::Instance()->data()->ut().djd());
734 ra = ScopeTarget->ra();
735 de = ScopeTarget->dec();
736 }
737 else
738 {
739 ra = ScopeTarget->ra0();
740 de = ScopeTarget->dec0();
741 }
742 }
743 else
744 {
745 ra = ScopeTarget->ra();
746 de = ScopeTarget->dec();
747 }
748
749 RAEle->value = ra.Hours();
750 DecEle->value = de.Degrees();
752
753 qCDebug(KSTARS_INDI) << "ISD:Telescope sending coords RA:" << ra.toHMSString() <<
754 "(" << RAEle->value << ") DE:" << de.toDMSString() <<
755 "(" << DecEle->value << ")";
756
757 RAEle->value = currentRA;
758 DecEle->value = currentDEC;
759 }
760 // Only send Horizontal Coord property if Equatorial is not available.
761 else if (HorProp)
762 {
763 AzEle->value = ScopeTarget->az().Degrees();
764 AltEle->value = ScopeTarget->alt().Degrees();
766 AzEle->value = currentAz;
767 AltEle->value = currentAlt;
768 }
769
770 };
771
772 // Helper function that first checks for the selected target object whether the
773 // tracking modes have to be adapted (special cases moon and sun), explicitely warns before
774 // slewing to the sun and finally (independant whether there exists a target object
775 // for the target coordinates) calls sendToMountDevice
776 auto checkObjectAndSend = [ = ]()
777 {
778 // Search within 0.1 degrees indepdent of zoom level.
779 double maxrad = 0.1;
780 currentObject = KStarsData::Instance()->skyComposite()->objectNearest(ScopeTarget, maxrad);
781 if (currentObject)
782 {
783 auto checkTrackModes = [ = ]()
784 {
785 if (m_hasTrackModes)
786 {
787 // Tracking Moon
788 if (currentObject->type() == SkyObject::MOON)
789 {
790 if (currentTrackMode != TRACK_LUNAR && TrackMap.contains(TRACK_LUNAR))
791 setTrackMode(TrackMap.value(TRACK_LUNAR));
792 }
793 // Tracking Sun
794 else if (currentObject->name() == i18n("Sun"))
795 {
796 if (currentTrackMode != TRACK_SOLAR && TrackMap.contains(TRACK_SOLAR))
797 setTrackMode(TrackMap.value(TRACK_SOLAR));
798 }
799 // If Last track mode was either set to SOLAR or LUNAR but now we are slewing to a different object
800 // then we automatically fallback to sidereal. If the current track mode is CUSTOM or something else, nothing
801 // changes.
802 else if (currentTrackMode == TRACK_SOLAR || currentTrackMode == TRACK_LUNAR)
803 setTrackMode(TRACK_SIDEREAL);
804
805 }
806 };
807
808 // Sun Warning, but don't ask if tracking is already solar.
809 if (currentObject->name() == i18n("Sun") && currentTrackMode != TRACK_SOLAR)
810 {
811 connect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, [ = ]()
812 {
813 KSMessageBox::Instance()->disconnect(this);
816 });
817 connect(KSMessageBox::Instance(), &KSMessageBox::rejected, this, [ = ]()
818 {
819 KSMessageBox::Instance()->disconnect(this);
820 });
821
822 KSMessageBox::Instance()->questionYesNo(
823 i18n("Warning! Looking at the Sun without proper protection can lead to irreversible eye damage!"),
824 i18n("Sun Warning"));
825 }
826 else
827 {
830 }
831 }
832 else
834 };
835
836 // If altitude limits is enabled, then reject motion immediately.
837 double targetAlt = ScopeTarget->altRefracted().Degrees();
838
839 if ((-90 <= minAlt && maxAlt <= 90) && (targetAlt < minAlt || targetAlt > maxAlt))
840 {
841 KSNotification::event(QLatin1String("IndiServerMessage"),
842 i18n("Requested altitude %1 is outside the specified altitude limit boundary (%2,%3).",
843 QString::number(targetAlt, 'g', 3), QString::number(minAlt, 'g', 3),
844 QString::number(maxAlt, 'g', 3)), KSNotification::Mount, KSNotification::Warn);
845 qCInfo(KSTARS_INDI) << "Requested altitude " << QString::number(targetAlt, 'g', 3)
846 << " is outside the specified altitude limit boundary ("
847 << QString::number(minAlt, 'g', 3) << "," << QString::number(maxAlt, 'g', 3) << ").";
848 return false;
849 }
850
851 // If disabled, then check if below horizon and warning the user unless the user previously dismissed it.
852 if (Options::confirmBelowHorizon() && targetAlt < 0 && minAlt == -1)
853 {
854 connect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, [ = ]()
855 {
856 if (minAlt < -90 && +90 < maxAlt)
857 Options::setConfirmBelowHorizon(false);
858 KSMessageBox::Instance()->disconnect(this);
860 });
861 connect(KSMessageBox::Instance(), &KSMessageBox::rejected, this, [ = ]()
862 {
863 KSMessageBox::Instance()->disconnect(this);
864 if (EqProp)
865 {
866 RAEle->value = currentRA;
867 DecEle->value = currentDEC;
868 }
869 if (HorProp)
870 {
871 AzEle->value = currentAz;
872 AltEle->value = currentAlt;
873 }
874 });
875
876 KSMessageBox::Instance()->questionYesNo(i18n("Requested altitude is below the horizon. Are you sure you want to proceed?"),
877 i18n("Telescope Motion"), 15, false);
878 }
879 else
881
882 return true;
883}
884
886{
887 auto motionSP = getSwitch("ON_COORD_SET");
888
889 if (motionSP == nullptr)
890 return false;
891 // A slew will happen if either Track, Slew, or Flip
892 // is selected
893 auto sp = motionSP->findOnSwitch();
894 if(sp != nullptr &&
895 (sp->name == std::string("TRACK") ||
896 sp->name == std::string("SLEW") ||
897 sp->name == std::string("FLIP")))
898 {
899 return true;
900 }
901 else
902 {
903 return false;
904 }
905}
906
907bool Mount::Slew(double ra, double dec, bool flip)
908{
909 SkyPoint target;
910
911 if (m_isJ2000)
912 {
913 target.setRA0(ra);
914 target.setDec0(dec);
915 }
916 else
917 {
918 target.setRA(ra);
919 target.setDec(dec);
920 }
921
922 return Slew(&target, flip);
923}
924
925bool Mount::Slew(SkyPoint * ScopeTarget, bool flip)
926{
927 auto motionSP = getSwitch("ON_COORD_SET");
928
929 if (!motionSP)
930 return false;
931
932 auto slewSW = flip ? motionSP->findWidgetByName("FLIP") : motionSP->findWidgetByName("TRACK");
933
934 if (flip && (!slewSW))
935 slewSW = motionSP->findWidgetByName("TRACK");
936
937 if (!slewSW)
938 slewSW = motionSP->findWidgetByName("SLEW");
939
940 if (!slewSW)
941 return false;
942
943 if (slewSW->getState() != ISS_ON)
944 {
945 motionSP->reset();
946 slewSW->setState(ISS_ON);
948
949 qCDebug(KSTARS_INDI) << "ISD:Telescope: " << slewSW->getName();
950 }
951
952 return sendCoords(ScopeTarget);
953}
954
955bool Mount::Sync(double ra, double dec)
956{
957 SkyPoint target;
958
959 target.setRA(ra);
960 target.setDec(dec);
961
962 return Sync(&target);
963}
964
965bool Mount::Sync(SkyPoint * ScopeTarget)
966{
967 auto motionSP = getSwitch("ON_COORD_SET");
968
969 if (!motionSP)
970 return false;
971
972 auto syncSW = motionSP->findWidgetByName("SYNC");
973
974 if (!syncSW)
975 return false;
976
977 if (syncSW->getState() != ISS_ON)
978 {
979 motionSP->reset();
980 syncSW->setState(ISS_ON);
982
983 qCDebug(KSTARS_INDI) << "ISD:Telescope: Syncing...";
984 }
985
986 return sendCoords(ScopeTarget);
987}
988
989bool Mount::abort()
990{
991 auto motionSP = getSwitch("TELESCOPE_ABORT_MOTION");
992
993 if (!motionSP)
994 return false;
995
996 auto abortSW = motionSP->findWidgetByName("ABORT");
997
998 if (!abortSW)
999 return false;
1000
1001 qCDebug(KSTARS_INDI) << "ISD:Telescope: Aborted." << Qt::endl;
1002
1003 abortSW->setState(ISS_ON);
1005
1006 inCustomParking = false;
1007
1008 return true;
1009}
1010
1011bool Mount::park()
1012{
1013 auto parkSP = getSwitch("TELESCOPE_PARK");
1014
1015 if (!parkSP)
1016 return false;
1017
1018 auto parkSW = parkSP->findWidgetByName("PARK");
1019
1020 if (!parkSW)
1021 return false;
1022
1023 qCDebug(KSTARS_INDI) << "ISD:Telescope: Parking..." << Qt::endl;
1024
1025 parkSP->reset();
1026 parkSW->setState(ISS_ON);
1028
1029 return true;
1030}
1031
1032bool Mount::unpark()
1033{
1034 auto parkSP = getSwitch("TELESCOPE_PARK");
1035
1036 if (!parkSP)
1037 return false;
1038
1039 auto parkSW = parkSP->findWidgetByName("UNPARK");
1040
1041 if (!parkSW)
1042 return false;
1043
1044 qCDebug(KSTARS_INDI) << "ISD:Telescope: UnParking..." << Qt::endl;
1045
1046 parkSP->reset();
1047 parkSW->setState(ISS_ON);
1049
1050 return true;
1051}
1052
1053bool Mount::getEqCoords(double * ra, double * dec)
1054{
1055 auto EqProp = getNumber("EQUATORIAL_EOD_COORD");
1056 if (!EqProp)
1057 {
1058 EqProp = getNumber("EQUATORIAL_COORD");
1059 if (!EqProp)
1060 return false;
1061 }
1062
1063 auto RAEle = EqProp->findWidgetByName("RA");
1064 if (!RAEle)
1065 return false;
1066
1067 auto DecEle = EqProp->findWidgetByName("DEC");
1068 if (!DecEle)
1069 return false;
1070
1071 *ra = RAEle->getValue();
1072 *dec = DecEle->getValue();
1073
1074 return true;
1075}
1076
1077bool Mount::MoveNS(VerticalMotion dir, MotionCommand cmd)
1078{
1079 auto motionSP = getSwitch("TELESCOPE_MOTION_NS");
1080
1081 if (!motionSP)
1082 return false;
1083
1084 auto motionNorth = motionSP->findWidgetByName("MOTION_NORTH");
1085 auto motionSouth = motionSP->findWidgetByName("MOTION_SOUTH");
1086
1087 if (!motionNorth || !motionSouth)
1088 return false;
1089
1090 // If same direction, return
1091 if (dir == MOTION_NORTH && motionNorth->getState() == ((cmd == MOTION_START) ? ISS_ON : ISS_OFF))
1092 return true;
1093
1094 if (dir == MOTION_SOUTH && motionSouth->getState() == ((cmd == MOTION_START) ? ISS_ON : ISS_OFF))
1095 return true;
1096
1097 motionSP->reset();
1098
1099 if (cmd == MOTION_START)
1100 {
1101 if (dir == MOTION_NORTH)
1102 motionNorth->setState(ISS_ON);
1103 else
1104 motionSouth->setState(ISS_ON);
1105 }
1106
1108
1109 return true;
1110}
1111
1112bool Mount::StopWE()
1113{
1114 auto motionSP = getSwitch("TELESCOPE_MOTION_WE");
1115
1116 if (!motionSP)
1117 return false;
1118
1119 motionSP->reset();
1120
1122
1123 return true;
1124}
1125
1126bool Mount::StopNS()
1127{
1128 auto motionSP = getSwitch("TELESCOPE_MOTION_NS");
1129
1130 if (!motionSP)
1131 return false;
1132
1133 motionSP->reset();
1134
1136
1137 return true;
1138}
1139
1140bool Mount::MoveWE(HorizontalMotion dir, MotionCommand cmd)
1141{
1142 auto motionSP = getSwitch("TELESCOPE_MOTION_WE");
1143
1144 if (!motionSP)
1145 return false;
1146
1147 auto motionWest = motionSP->findWidgetByName("MOTION_WEST");
1148 auto motionEast = motionSP->findWidgetByName("MOTION_EAST");
1149
1150 if (!motionWest || !motionEast)
1151 return false;
1152
1153 // If same direction, return
1154 if (dir == MOTION_WEST && motionWest->getState() == ((cmd == MOTION_START) ? ISS_ON : ISS_OFF))
1155 return true;
1156
1157 if (dir == MOTION_EAST && motionEast->getState() == ((cmd == MOTION_START) ? ISS_ON : ISS_OFF))
1158 return true;
1159
1160 motionSP->reset();
1161
1162 if (cmd == MOTION_START)
1163 {
1164 if (dir == MOTION_WEST)
1165 motionWest->setState(ISS_ON);
1166 else
1167 motionEast->setState(ISS_ON);
1168 }
1169
1171
1172 return true;
1173}
1174
1175bool Mount::setSlewRate(int index)
1176{
1177 auto slewRateSP = getSwitch("TELESCOPE_SLEW_RATE");
1178
1179 if (!slewRateSP)
1180 return false;
1181
1182 if (index < 0 || index > slewRateSP->count())
1183 return false;
1184 else if (slewRateSP->findOnSwitchIndex() == index)
1185 return true;
1186
1187 slewRateSP->reset();
1188
1189 slewRateSP->at(index)->setState(ISS_ON);
1190
1192
1193 emit slewRateChanged(index);
1194
1195 return true;
1196}
1197
1198int Mount::getSlewRate() const
1199{
1200 auto slewRateSP = getSwitch("TELESCOPE_SLEW_RATE");
1201
1202 if (!slewRateSP)
1203 return -1;
1204
1205 return slewRateSP->findOnSwitchIndex();
1206}
1207
1208void Mount::setAltLimits(double minAltitude, double maxAltitude)
1209{
1210 minAlt = minAltitude;
1211 maxAlt = maxAltitude;
1212}
1213
1214bool Mount::setAlignmentModelEnabled(bool enable)
1215{
1216 bool wasExecuted = false;
1217
1218 // For INDI Alignment Subsystem
1219 auto alignSwitch = getSwitch("ALIGNMENT_SUBSYSTEM_ACTIVE");
1220 if (alignSwitch)
1221 {
1222 alignSwitch->at(0)->setState(enable ? ISS_ON : ISS_OFF);
1224 wasExecuted = true;
1225 }
1226
1227 // For EQMod Alignment --- Temporary until all drivers switch fully to INDI Alignment Subsystem
1228 alignSwitch = getSwitch("ALIGNMODE");
1229 if (alignSwitch)
1230 {
1231 alignSwitch->reset();
1232 // For now, always set alignment mode to NSTAR on enable.
1233 if (enable)
1234 alignSwitch->at(2)->setState(ISS_ON);
1235 // Otherwise, set to NO ALIGN
1236 else
1237 alignSwitch->at(0)->setState(ISS_ON);
1238
1240 wasExecuted = true;
1241 }
1242
1243 return wasExecuted;
1244}
1245
1247{
1248 auto tleTextVec = getText("SAT_TLE_TEXT");
1249 if (!tleTextVec)
1250 {
1251 qCDebug(KSTARS_INDI) << "Property SAT_TLE_TEXT not found";
1252 return false;
1253 }
1254
1255 auto tleText = tleTextVec->findWidgetByName("TLE");
1256 if (!tleText)
1257 return false;
1258
1259 tleText->setText(tle.toLocal8Bit().data());
1260
1262 m_TLEIsSetForTracking = true;
1263 g_satPassStart = satPassStart;
1264 g_satPassEnd = satPassEnd;
1265 return true;
1266 // See Telescope::processText for the following steps (setting window and switch)
1267}
1268
1269
1270bool Mount::clearParking()
1271{
1272 auto parkSwitch = getSwitch("TELESCOPE_PARK_OPTION");
1273 if (!parkSwitch)
1274 return false;
1275
1276 auto clearParkSW = parkSwitch->findWidgetByName("PARK_PURGE_DATA");
1277 if (!clearParkSW)
1278 return false;
1279
1280 parkSwitch->reset();
1281 clearParkSW->setState(ISS_ON);
1282
1284 return true;
1285}
1286
1287bool Mount::clearAlignmentModel()
1288{
1289 bool wasExecuted = false;
1290
1291 // Note: Should probably use INDI Alignment Subsystem Client API in the future?
1292 auto clearSwitch = getSwitch("ALIGNMENT_POINTSET_ACTION");
1293 auto commitSwitch = getSwitch("ALIGNMENT_POINTSET_COMMIT");
1295 {
1296 clearSwitch->reset();
1297 // ALIGNMENT_POINTSET_ACTION.CLEAR
1298 clearSwitch->at(4)->setState(ISS_ON);
1300 commitSwitch->at(0)->setState(ISS_ON);
1302 wasExecuted = true;
1303 }
1304
1305 // For EQMod Alignment --- Temporary until all drivers switch fully to INDI Alignment Subsystem
1306 clearSwitch = getSwitch("ALIGNLIST");
1307 if (clearSwitch)
1308 {
1309 // ALIGNLISTCLEAR
1310 clearSwitch->reset();
1311 clearSwitch->at(1)->setState(ISS_ON);
1313 wasExecuted = true;
1314 }
1315
1316 return wasExecuted;
1317}
1318
1319Mount::Status Mount::status()
1320{
1321 auto EqProp = getNumber("EQUATORIAL_EOD_COORD");
1322 if (EqProp == nullptr)
1323 {
1324 EqProp = getNumber("EQUATORIAL_COORD");
1325 if (EqProp == nullptr)
1326 return MOUNT_ERROR;
1327 }
1328
1329 return status(EqProp);
1330}
1331
1332const QString Mount::statusString(Mount::Status status, bool translated) const
1333{
1334 switch (status)
1335 {
1336 case ISD::Mount::MOUNT_MOVING:
1337 return (translated ? i18n(mountStates[status]) : mountStates[status]) + QString(" %1").arg(getManualMotionString());
1338 default:
1339 return translated ? i18n(mountStates[status]) : mountStates[status];
1340 }
1341}
1342
1343QString Mount::getManualMotionString() const
1344{
1346
1347 auto movementSP = getSwitch("TELESCOPE_MOTION_NS");
1348 if (movementSP)
1349 {
1350 if (movementSP->at(MOTION_NORTH)->getState() == ISS_ON)
1351 NSMotion = 'N';
1352 else if (movementSP->at(MOTION_SOUTH)->getState() == ISS_ON)
1353 NSMotion = 'S';
1354 }
1355
1356 movementSP = getSwitch("TELESCOPE_MOTION_WE");
1357 if (movementSP)
1358 {
1359 if (movementSP->at(MOTION_WEST)->getState() == ISS_ON)
1360 WEMotion = 'W';
1361 else if (movementSP->at(MOTION_EAST)->getState() == ISS_ON)
1362 WEMotion = 'E';
1363 }
1364
1365 return QString("%1%2").arg(NSMotion, WEMotion);
1366}
1367
1368bool Mount::setTrackEnabled(bool enable)
1369{
1370 auto trackSP = getSwitch("TELESCOPE_TRACK_STATE");
1371 if (!trackSP)
1372 return false;
1373
1374 auto trackON = trackSP->findWidgetByName("TRACK_ON");
1375 auto trackOFF = trackSP->findWidgetByName("TRACK_OFF");
1376
1377 if (!trackON || !trackOFF)
1378 return false;
1379
1380 trackON->setState(enable ? ISS_ON : ISS_OFF);
1381 trackOFF->setState(enable ? ISS_OFF : ISS_ON);
1382
1384
1385 return true;
1386}
1387
1388bool Mount::isTracking()
1389{
1390 return (status() == MOUNT_TRACKING);
1391}
1392
1393bool Mount::setTrackMode(uint8_t index)
1394{
1395 auto trackModeSP = getSwitch("TELESCOPE_TRACK_MODE");
1396 if (!trackModeSP)
1397 return false;
1398
1399 if (index >= trackModeSP->nsp)
1400 return false;
1401
1402 trackModeSP->reset();
1403 trackModeSP->at(index)->setState(ISS_ON);
1404
1406
1407 return true;
1408}
1409
1410bool Mount::getTrackMode(uint8_t &index)
1411{
1412 auto trackModeSP = getSwitch("TELESCOPE_TRACK_MODE");
1413 if (!trackModeSP)
1414 return false;
1415
1416 index = trackModeSP->findOnSwitchIndex();
1417
1418 return true;
1419}
1420
1421bool Mount::setCustomTrackRate(double raRate, double deRate)
1422{
1423 auto trackRateNP = getNumber("TELESCOPE_TRACK_RATE");
1424 if (!trackRateNP)
1425 return false;
1426
1427 auto raRateN = trackRateNP->findWidgetByName("TRACK_RATE_RA");
1428 auto deRateN = trackRateNP->findWidgetByName("TRACK_RATE_DE");
1429
1430 if (!raRateN || !deRateN)
1431 return false;
1432
1433 raRateN->setValue(raRate);
1434 deRateN->setValue(deRate);
1435
1437
1438 return true;
1439}
1440
1441bool Mount::getCustomTrackRate(double &raRate, double &deRate)
1442{
1443 auto trackRateNP = getNumber("TELESCOPE_TRACK_RATE");
1444 if (!trackRateNP)
1445 return false;
1446
1447 auto raRateN = trackRateNP->findWidgetByName("TRACK_RATE_RA");
1448 auto deRateN = trackRateNP->findWidgetByName("TRACK_RATE_DE");
1449
1450 if (!raRateN || !deRateN)
1451 return false;
1452
1453 raRate = raRateN->getValue();
1454 deRate = deRateN->getValue();
1455
1456 return true;
1457
1458}
1459
1460bool Mount::sendParkingOptionCommand(ParkOptionCommand command)
1461{
1462 auto parkOptionsSP = getSwitch("TELESCOPE_PARK_OPTION");
1463 if (!parkOptionsSP)
1464 return false;
1465
1466 parkOptionsSP->reset();
1467 parkOptionsSP->at(command)->setState(ISS_ON);
1469
1470 return true;
1471}
1472
1473Mount::Status Mount::status(INumberVectorProperty * nvp)
1474{
1475 switch (nvp->s)
1476 {
1477 case IPS_IDLE:
1478 if (inManualMotion)
1479 return MOUNT_MOVING;
1480 else if (isParked())
1481 return MOUNT_PARKED;
1482 else
1483 return MOUNT_IDLE;
1484
1485 case IPS_OK:
1486 if (inManualMotion)
1487 return MOUNT_MOVING;
1488 else if (inCustomParking)
1489 {
1490 inCustomParking = false;
1491 // set CURRENT position as the desired parking position
1492 sendParkingOptionCommand(PARK_OPTION_CURRENT);
1493 // Write data to disk
1494 sendParkingOptionCommand(PARK_OPTION_WRITE_DATA);
1495
1496 return MOUNT_TRACKING;
1497 }
1498 else
1499 return MOUNT_TRACKING;
1500
1501 case IPS_BUSY:
1502 {
1503 if (inManualMotion)
1504 return MOUNT_MOVING;
1505
1506 auto parkSP = getSwitch("TELESCOPE_PARK");
1507 if (parkSP && parkSP->getState() == IPS_BUSY)
1508 return MOUNT_PARKING;
1509 else
1510 return MOUNT_SLEWING;
1511 }
1512
1513 case IPS_ALERT:
1514 inCustomParking = false;
1515 return MOUNT_ERROR;
1516 }
1517
1518 return MOUNT_ERROR;
1519}
1520
1522{
1523 dms lst = KStarsData::Instance()->geo()->GSTtoLST(KStarsData::Instance()->clock()->utc().gst());
1524 return dms(lst.Degrees() - currentCoords.ra().Degrees());
1525}
1526
1527bool Mount::isReversed(INDI_EQ_AXIS axis)
1528{
1529 auto reversed = getSwitch("TELESCOPE_REVERSE_MOTION");
1530 if (!reversed)
1531 return false;
1532
1533 return reversed->at(axis == AXIS_DE ? 0 : 1)->getState() == ISS_ON;
1534}
1535
1536bool Mount::setReversedEnabled(INDI_EQ_AXIS axis, bool enabled)
1537{
1538 auto reversed = getSwitch("TELESCOPE_REVERSE_MOTION");
1539 if (!reversed)
1540 return false;
1541
1542 reversed->at(axis == AXIS_DE ? 0 : 1)->setState(enabled ? ISS_ON : ISS_OFF);
1544 return true;
1545}
1546
1548{
1549 updateCoordinatesTimer.stop();
1550 centerLockTimer.stop();
1551}
1552
1553}
1554
1555QDBusArgument &operator<<(QDBusArgument &argument, const ISD::Mount::Status &source)
1556{
1557 argument.beginStructure();
1559 argument.endStructure();
1560 return argument;
1561}
1562
1563const QDBusArgument &operator>>(const QDBusArgument &argument, ISD::Mount::Status &dest)
1564{
1565 int a;
1566 argument.beginStructure();
1567 argument >> a;
1568 argument.endStructure();
1569 dest = static_cast<ISD::Mount::Status>(a);
1570 return argument;
1571}
1572
1573QDBusArgument &operator<<(QDBusArgument &argument, const ISD::Mount::PierSide &source)
1574{
1575 argument.beginStructure();
1577 argument.endStructure();
1578 return argument;
1579}
1580
1581const QDBusArgument &operator>>(const QDBusArgument &argument, ISD::Mount::PierSide &dest)
1582{
1583 int a;
1584 argument.beginStructure();
1585 argument >> a;
1586 argument.endStructure();
1587 dest = static_cast<ISD::Mount::PierSide>(a);
1588 return argument;
1589}
1590
void sendNewProperty(INDI::Property prop)
Send new property command to server.
INDI::PropertyView< IText > * getText(const QString &name) const
INDI::PropertyView< ISwitch > * getSwitch(const QString &name) const
void updateTarget()
updateTarget update target position from {
void newStatus(ISD::Mount::Status status)
Change in the mount status.
bool slewDefined()
Check whether sending new coordinates will result into a slew.
const dms hourAngle() const
Hour angle of the current coordinates.
void updateJ2000Coordinates(SkyPoint *coords)
Helper function to update the J2000 coordinates of a sky point from its JNow coordinates.
bool sendCoords(SkyPoint *ScopeTarget)
Send the coordinates to the mount's INDI driver.
bool setSatelliteTLEandTrack(QString tle, const KStarsDateTime satPassStart, const KStarsDateTime satPassEnd)
Tracks satellite on provided TLE, initial epoch for trajectory calculation and window in minutes.
void updateParkStatus()
updateParkStatus Updating parking status by checking the TELESCOPE_PARK property.
void stopTimers()
stopTimers Stop timers to prevent timing race condition when device is unavailable and timer is still...
void newTarget(SkyPoint &currentCoords)
The mount has finished the slew to a new target.
void newTargetName(const QString &name)
The mount has finished the slew to a new target.
Extension of QDateTime for KStars KStarsDateTime can represent the date/time as a Julian Day,...
static KStars * Instance()
Definition kstars.h:123
Provides all necessary information about an object in the sky: its coordinates, name(s),...
Definition skyobject.h:42
virtual QString name(void) const
Definition skyobject.h:145
int type(void) const
Definition skyobject.h:188
The sky coordinates of a point in the sky.
Definition skypoint.h:45
void apparentCoord(long double jd0, long double jdf)
Computes the apparent coordinates for this SkyPoint for any epoch, accounting for the effects of prec...
Definition skypoint.cpp:700
const CachingDms & dec() const
Definition skypoint.h:269
void setDec(dms d)
Sets Dec, the current Declination.
Definition skypoint.h:169
void setRA(dms &r)
Sets RA, the current Right Ascension.
Definition skypoint.h:144
const CachingDms & ra() const
Definition skypoint.h:263
dms angularDistanceTo(const SkyPoint *sp, double *const positionAngle=nullptr) const
Computes the angular distance between two SkyObjects.
Definition skypoint.cpp:899
void EquatorialToHorizontal(const CachingDms *LST, const CachingDms *lat)
Determine the (Altitude, Azimuth) coordinates of the SkyPoint from its (RA, Dec) coordinates,...
Definition skypoint.cpp:77
void setRA0(dms r)
Sets RA0, the catalog Right Ascension.
Definition skypoint.h:94
void setAlt(dms alt)
Sets Alt, the Altitude.
Definition skypoint.h:194
void HorizontalToEquatorial(const dms *LST, const dms *lat)
Determine the (RA, Dec) coordinates of the SkyPoint from its (Altitude, Azimuth) coordinates,...
Definition skypoint.cpp:143
void setAz(dms az)
Sets Az, the Azimuth.
Definition skypoint.h:230
void setDec0(dms d)
Sets Dec0, the catalog Declination.
Definition skypoint.h:119
An angle, stored as degrees, but expressible in many ways.
Definition dms.h:38
double Hours() const
Definition dms.h:168
const QString toHMSString(const bool machineReadable=false, const bool highPrecision=false) const
Definition dms.cpp:378
const double & Degrees() const
Definition dms.h:141
QString i18n(const char *text, const TYPE &arg...)
ISD is a collection of INDI Standard Devices.
KCALENDARCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &)
GeoCoordinates geo(const QVariant &location)
NETWORKMANAGERQT_EXPORT NetworkManager::Status status()
QCA_EXPORT QVariant getProperty(const QString &name)
char * data()
QString toString(QStringView format, QCalendar cal) const const
void accepted()
void rejected()
bool contains(const Key &key) const const
T value(const Key &key, const T &defaultValue) const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QString arg(Args &&... args) const const
QString number(double n, char format, int precision)
QByteArray toLocal8Bit() const const
QTextStream & dec(QTextStream &stream)
QTextStream & endl(QTextStream &stream)
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void setInterval(int msec)
bool isActive() const const
void start()
void stop()
void timeout()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:19:03 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.