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
19#include <QAction>
20#include <qdbusmetatype.h>
21
22#include <indi_debug.h>
23
24// Qt version calming
25#include <qtendl.h>
26
27namespace ISD
28{
29
30const QList<KLocalizedString> Mount::mountStates = { ki18n("Idle"), ki18n("Moving"), ki18n("Slewing"),
31 ki18n("Tracking"), ki18n("Parking"), ki18n("Parked"),
32 ki18n("Error")
33 };
34
35Mount::Mount(GenericDevice *parent) : ConcreteDevice(parent)
36{
37 // Set it for 5 seconds for now as not to spam the display update
38 centerLockTimer.setInterval(5000);
39 centerLockTimer.setSingleShot(true);
40 connect(&centerLockTimer, &QTimer::timeout, this, [this]()
41 {
42 //runCommand(INDI_CENTER_LOCK);
43 centerLock();
44 });
45
46 // Regularly update the coordinates even if no update has been sent from the INDI service
47 updateCoordinatesTimer.setInterval(1000);
48 updateCoordinatesTimer.setSingleShot(false);
49 connect(&updateCoordinatesTimer, &QTimer::timeout, this, [this]()
50 {
51 if (isConnected())
52 {
53 currentCoords.EquatorialToHorizontal(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat());
54 emit newCoords(currentCoords, pierSide(), hourAngle());
55 }
56 });
57
58 qRegisterMetaType<ISD::Mount::Status>("ISD::Mount::Status");
59 qDBusRegisterMetaType<ISD::Mount::Status>();
60
61 qRegisterMetaType<ISD::Mount::PierSide>("ISD::Mount::PierSide");
62 qDBusRegisterMetaType<ISD::Mount::PierSide>();
63
64 // Need to delay check for alignment model to upon connection is established since the property is defined BEFORE Telescope class is created.
65 // and therefore no registerProperty is called for these properties since they were already registered _before_ the Telescope
66 // class was created.
67 m_hasAlignmentModel = getProperty("ALIGNMENT_POINTSET_ACTION").isValid() || getProperty("ALIGNLIST").isValid();
68}
69
70void Mount::registerProperty(INDI::Property prop)
71{
72 if (prop.isNameMatch("TELESCOPE_INFO"))
73 {
74 auto ti = prop.getNumber();
75
76 if (!ti)
77 return;
78
79 bool aperture_ok = false, focal_ok = false;
80 double temp = 0;
81
82 auto aperture = ti->findWidgetByName("TELESCOPE_APERTURE");
83 if (aperture && aperture->getValue() <= 0)
84 {
85 if (getDriverInfo()->getAuxInfo().contains("TELESCOPE_APERTURE"))
86 {
87 temp = getDriverInfo()->getAuxInfo().value("TELESCOPE_APERTURE").toDouble(&aperture_ok);
88 if (aperture_ok)
89 {
90 aperture->setValue(temp);
91 auto g_aperture = ti->findWidgetByName("GUIDER_APERTURE");
92 if (g_aperture && g_aperture->getValue() <= 0)
93 g_aperture->setValue(aperture->getValue());
94 }
95 }
96 }
97
98 auto focal_length = ti->findWidgetByName("TELESCOPE_FOCAL_LENGTH");
99 if (focal_length && focal_length->getValue() <= 0)
100 {
101 if (getDriverInfo()->getAuxInfo().contains("TELESCOPE_FOCAL_LENGTH"))
102 {
103 temp = getDriverInfo()->getAuxInfo().value("TELESCOPE_FOCAL_LENGTH").toDouble(&focal_ok);
104 if (focal_ok)
105 {
106 focal_length->setValue(temp);
107 auto g_focal = ti->findWidgetByName("GUIDER_FOCAL_LENGTH");
108 if (g_focal && g_focal->getValue() <= 0)
109 g_focal->setValue(focal_length->getValue());
110 }
111 }
112 }
113
114 if (aperture_ok && focal_ok)
115 sendNewProperty(ti);
116 }
117 else if (prop.isNameMatch("ON_COORD_SET"))
118 {
119 m_canGoto = IUFindSwitch(prop.getSwitch(), "TRACK") != nullptr;
120 m_canSync = IUFindSwitch(prop.getSwitch(), "SYNC") != nullptr;
121 m_canFlip = IUFindSwitch(prop.getSwitch(), "FLIP") != nullptr;
122 }
123 else if (prop.isNameMatch("TELESCOPE_PIER_SIDE"))
124 {
125 auto svp = prop.getSwitch();
126 int currentSide = svp->findOnSwitchIndex();
127 if (currentSide != m_PierSide)
128 {
129 m_PierSide = static_cast<PierSide>(currentSide);
130 emit pierSideChanged(m_PierSide);
131 }
132 }
133 else if (prop.isNameMatch("TELESCOPE_PARK"))
134 updateParkStatus();
135 else if (prop.isNameMatch("TELESCOPE_TRACK_STATE"))
136 m_canControlTrack = true;
137 else if (prop.isNameMatch("TELESCOPE_TRACK_MODE"))
138 {
139 m_hasTrackModes = true;
140 auto svp = prop.getSwitch();
141 for (int i = 0; i < svp->count(); i++)
142 {
143 if (svp->at(i)->isNameMatch("TRACK_SIDEREAL"))
144 TrackMap[TRACK_SIDEREAL] = i;
145 else if (svp->at(i)->isNameMatch("TRACK_SOLAR"))
146 TrackMap[TRACK_SOLAR] = i;
147 else if (svp->at(i)->isNameMatch("TRACK_LUNAR"))
148 TrackMap[TRACK_LUNAR] = i;
149 else if (svp->at(i)->isNameMatch("TRACK_CUSTOM"))
150 TrackMap[TRACK_CUSTOM] = i;
151 }
152 }
153 else if (prop.isNameMatch("TELESCOPE_TRACK_RATE"))
154 m_hasCustomTrackRate = true;
155 else if (prop.isNameMatch("TELESCOPE_ABORT_MOTION"))
156 m_canAbort = true;
157 else if (prop.isNameMatch("TELESCOPE_PARK_OPTION"))
158 m_hasCustomParking = true;
159 else if (prop.isNameMatch("TELESCOPE_SLEW_RATE"))
160 {
161 m_hasSlewRates = true;
162 auto svp = prop.getSwitch();
163 if (svp)
164 {
165 m_slewRates.clear();
166 for (const auto &it : *svp)
167 m_slewRates << it.getLabel();
168 }
169 }
170 else if (prop.isNameMatch("EQUATORIAL_EOD_COORD"))
171 {
172 m_isJ2000 = false;
173 m_hasEquatorialCoordProperty = true;
174 }
175 else if (prop.isNameMatch("SAT_TRACKING_STAT"))
176 {
177 m_canTrackSatellite = true;
178 }
179 else if (prop.isNameMatch("EQUATORIAL_COORD"))
180 {
181 m_isJ2000 = true;
182 m_hasEquatorialCoordProperty = true;
183 }
184}
185
186void Mount::updateJ2000Coordinates(SkyPoint *coords)
187{
188 SkyPoint J2000Coord(coords->ra(), coords->dec());
189 J2000Coord.catalogueCoord(KStars::Instance()->data()->ut().djd());
190 coords->setRA0(J2000Coord.ra());
191 coords->setDec0(J2000Coord.dec());
192}
193
195{
196 emit newTarget(currentCoords);
197 double maxrad = 0.1;
198 currentObject = KStarsData::Instance()->skyComposite()->objectNearest(&currentCoords, maxrad);
199 if (currentObject)
200 emit newTargetName(currentObject->name());
201 // If there is no object, we must clear target as it might give wrong
202 // indication we are still on it.
203 else
204 emit newTargetName(QString());
205}
206
207void Mount::processNumber(INDI::Property prop)
208{
209 auto nvp = prop.getNumber();
210 if (nvp->isNameMatch("EQUATORIAL_EOD_COORD") || nvp->isNameMatch("EQUATORIAL_COORD"))
211 {
212 auto RA = nvp->findWidgetByName("RA");
213 auto DEC = nvp->findWidgetByName("DEC");
214
215 if (RA == nullptr || DEC == nullptr)
216 return;
217
218 // set both JNow and J2000 coordinates
219 if (isJ2000())
220 {
221 currentCoords.setRA0(RA->value);
222 currentCoords.setDec0(DEC->value);
223 currentCoords.apparentCoord(static_cast<long double>(J2000), KStars::Instance()->data()->ut().djd());
224 }
225 else
226 {
227 currentCoords.setRA(RA->value);
228 currentCoords.setDec(DEC->value);
229 // calculate J2000 coordinates
230 updateJ2000Coordinates(&currentCoords);
231 }
232
233 // calculate horizontal coordinates
234 currentCoords.EquatorialToHorizontal(KStars::Instance()->data()->lst(),
235 KStars::Instance()->data()->geo()->lat());
236 // ensure that coordinates are regularly updated
237 if (! updateCoordinatesTimer.isActive())
238 updateCoordinatesTimer.start();
239
240 auto currentStatus = status(nvp);
241
242 if (nvp->getState() == IPS_BUSY && EqCoordPreviousState != IPS_BUSY)
243 {
244 if (currentStatus == MOUNT_SLEWING)
245 KSNotification::event(QLatin1String("SlewStarted"), i18n("Mount is slewing to target location"), KSNotification::Mount);
246 emit newStatus(currentStatus);
247 }
248 else if (EqCoordPreviousState == IPS_BUSY && nvp->getState() == IPS_OK && slewDefined())
249 {
250 if (Options::useExternalSkyMap())
251 {
252 // For external skymaps the only way to determine the target is to take the position where the mount
253 // starts to track
254 updateTarget();
255 }
256 else
257 {
258 // In case that we use KStars as skymap, we intentionally do not communicate the target here, since it
259 // has been set at the beginning of the slew AND we cannot be sure that the position the INDI
260 // mount reports when starting to track is exactly that one where the slew went to.
261 KSNotification::event(QLatin1String("SlewCompleted"), i18n("Mount arrived at target location"), KSNotification::Mount);
262 }
263 emit newStatus(currentStatus);
264 }
265
266 EqCoordPreviousState = nvp->getState();
267
269 }
270 // JM 2022.03.11 Only process HORIZONTAL_COORD if it was the ONLY source of information
271 // When a driver both sends EQUATORIAL_COORD and HORIZONTAL_COORD, we should prioritize EQUATORIAL_COORD
272 // especially since the conversion from horizontal to equatorial is not as accurate and can result in weird
273 // coordinates near the poles.
274 else if (nvp->isNameMatch("HORIZONTAL_COORD") && m_hasEquatorialCoordProperty == false)
275 {
276 auto Az = nvp->findWidgetByName("AZ");
277 auto Alt = nvp->findWidgetByName("ALT");
278
279 if (Az == nullptr || Alt == nullptr)
280 return;
281
282 currentCoords.setAz(Az->value);
283 currentCoords.setAlt(Alt->value);
284 currentCoords.HorizontalToEquatorial(KStars::Instance()->data()->lst(),
285 KStars::Instance()->data()->geo()->lat());
286
287 // calculate J2000 coordinates
288 updateJ2000Coordinates(&currentCoords);
289
290 // ensure that coordinates are regularly updated
291 if (! updateCoordinatesTimer.isActive())
292 updateCoordinatesTimer.start();
293
295 }
296 else if (nvp->isNameMatch("POLLING_PERIOD"))
297 {
298 // set the timer how often the coordinates should be published
299 auto period = nvp->findWidgetByName("PERIOD_MS");
300 if (period != nullptr)
301 updateCoordinatesTimer.setInterval(static_cast<int>(period->getValue()));
302
303 }
304}
305
306void Mount::processSwitch(INDI::Property prop)
307{
308 bool manualMotionChanged = false;
309 auto svp = prop.getSwitch();
310
311 if (svp->isNameMatch("CONNECTION"))
312 {
313 auto conSP = svp->findWidgetByName("CONNECT");
314 if (conSP)
315 {
316 // TODO We must allow for multiple mount drivers to be online, not just one
317 // For the actions taken, the user should be able to specify which mounts shall receive the commands. It could be one
318 // or more. For now, we enable/disable telescope group on the assumption there is only one mount present.
319 if (conSP->getState() == ISS_ON)
321 else
322 {
324 centerLockTimer.stop();
325 }
326 }
327 }
328 else if (svp->isNameMatch("TELESCOPE_PARK"))
330 else if (svp->isNameMatch("TELESCOPE_ABORT_MOTION"))
331 {
332 if (svp->s == IPS_OK)
333 {
334 inCustomParking = false;
335 KSNotification::event(QLatin1String("MountAborted"), i18n("Mount motion was aborted"), KSNotification::Mount,
336 KSNotification::Warn);
337 }
338 }
339 else if (svp->isNameMatch("TELESCOPE_PIER_SIDE"))
340 {
341 int currentSide = IUFindOnSwitchIndex(svp);
342 if (currentSide != m_PierSide)
343 {
344 m_PierSide = static_cast<PierSide>(currentSide);
345 emit pierSideChanged(m_PierSide);
346 }
347 }
348 else if (svp->isNameMatch("TELESCOPE_TRACK_MODE"))
349 {
350 auto sp = svp->findOnSwitch();
351 if (sp)
352 {
353 if (sp->isNameMatch("TRACK_SIDEREAL"))
354 currentTrackMode = TRACK_SIDEREAL;
355 else if (sp->isNameMatch("TRACK_SOLAR"))
356 currentTrackMode = TRACK_SOLAR;
357 else if (sp->isNameMatch("TRACK_LUNAR"))
358 currentTrackMode = TRACK_LUNAR;
359 else
360 currentTrackMode = TRACK_CUSTOM;
361 }
362 }
363 else if (svp->isNameMatch("TELESCOPE_MOTION_NS"))
364 manualMotionChanged = true;
365 else if (svp->isNameMatch("TELESCOPE_MOTION_WE"))
366 manualMotionChanged = true;
367 else if (svp->isNameMatch("TELESCOPE_REVERSE_MOTION"))
368 {
369 emit axisReversed(AXIS_DE, svp->at(0)->getState() == ISS_ON);
370 emit axisReversed(AXIS_RA, svp->at(1)->getState() == ISS_ON);
371 }
372
373 if (manualMotionChanged)
374 {
375 auto NSCurrentMotion = getSwitch("TELESCOPE_MOTION_NS")->getState();
376 auto WECurrentMotion = getSwitch("TELESCOPE_MOTION_WE")->getState();
377 inCustomParking = false;
378 inManualMotion = (NSCurrentMotion == IPS_BUSY || WECurrentMotion == IPS_BUSY);
379 }
380}
381
382void Mount::processText(INDI::Property prop)
383{
384 auto tvp = prop.getText();
385 if (tvp->isNameMatch("SAT_TLE_TEXT"))
386 {
387 if ((tvp->getState() == IPS_OK) && (m_TLEIsSetForTracking))
388 {
389 auto trajWindow = getText("SAT_PASS_WINDOW");
390 if (!trajWindow)
391 {
392 qCDebug(KSTARS_INDI) << "Property SAT_PASS_WINDOW not found";
393 }
394 else
395 {
396 auto trajStart = trajWindow->findWidgetByName("SAT_PASS_WINDOW_START");
397 auto trajEnd = trajWindow->findWidgetByName("SAT_PASS_WINDOW_END");
398
399 if (!trajStart || !trajEnd)
400 {
401 qCDebug(KSTARS_INDI) << "Start or end in SAT_PASS_WINDOW not found";
402 }
403 else
404 {
405 trajStart->setText(g_satPassStart.toString(Qt::ISODate).toLocal8Bit().data());
406 trajEnd->setText(g_satPassEnd.toString(Qt::ISODate).toLocal8Bit().data());
407
408 sendNewProperty(trajWindow);
409 m_windowIsSetForTracking = true;
410 }
411 }
412 }
413 }
414 else if (tvp->isNameMatch("SAT_PASS_WINDOW"))
415 {
416 if ((tvp->getState() == IPS_OK) && (m_TLEIsSetForTracking) && (m_windowIsSetForTracking))
417 {
418 auto trackSwitchV = getSwitch("SAT_TRACKING_STAT");
419 if (!trackSwitchV)
420 {
421 qCDebug(KSTARS_INDI) << "Property SAT_TRACKING_STAT not found";
422 }
423 else
424 {
425 auto trackSwitch = trackSwitchV->findWidgetByName("SAT_TRACK");
426 if (trackSwitch)
427 {
428 trackSwitchV->reset();
429 trackSwitch->setState(ISS_ON);
430
431 sendNewProperty(trackSwitchV);
432 m_TLEIsSetForTracking = false;
433 m_windowIsSetForTracking = false;
434 }
435 }
436 }
437 }
438}
439
441{
442 auto svp = getSwitch("TELESCOPE_PARK");
443 if (!svp)
444 return;
445
446 auto sp = svp->findWidgetByName("PARK");
447 if (sp)
448 {
449 if (svp->getState() == IPS_ALERT)
450 {
451 // First, inform everyone watch this that an error occurred.
452 emit newParkStatus(PARK_ERROR);
453 // JM 2021-03-08: Reset parking internal state to either PARKED or UNPARKED.
454 // Whatever the current switch is set to
455 m_ParkStatus = (sp->getState() == ISS_ON) ? PARK_PARKED : PARK_UNPARKED;
456 KSNotification::event(QLatin1String("MountParkingFailed"), i18n("Mount parking failed"), KSNotification::Mount,
457 KSNotification::Alert);
458 }
459 else if (svp->getState() == IPS_BUSY && sp->s == ISS_ON && m_ParkStatus != PARK_PARKING)
460 {
461 m_ParkStatus = PARK_PARKING;
462 KSNotification::event(QLatin1String("MountParking"), i18n("Mount parking is in progress"), KSNotification::Mount);
463 currentObject = nullptr;
464
465 emit newParkStatus(m_ParkStatus);
466 }
467 else if (svp->getState() == IPS_BUSY && sp->getState() == ISS_OFF && m_ParkStatus != PARK_UNPARKING)
468 {
469 m_ParkStatus = PARK_UNPARKING;
470 KSNotification::event(QLatin1String("MountUnParking"), i18n("Mount unparking is in progress"), KSNotification::Mount);
471
472 emit newParkStatus(m_ParkStatus);
473 }
474 else if (svp->getState() == IPS_OK && sp->getState() == ISS_ON && m_ParkStatus != PARK_PARKED)
475 {
476 m_ParkStatus = PARK_PARKED;
477 KSNotification::event(QLatin1String("MountParked"), i18n("Mount parked"), KSNotification::Mount);
478 currentObject = nullptr;
479
480 emit newParkStatus(m_ParkStatus);
481
482 QAction *parkAction = KStars::Instance()->actionCollection()->action("telescope_park");
483 if (parkAction)
484 parkAction->setEnabled(false);
485 QAction *unParkAction = KStars::Instance()->actionCollection()->action("telescope_unpark");
486 if (unParkAction)
487 unParkAction->setEnabled(true);
488
489 emit newTarget(currentCoords);
490 emit newTargetName(QString());
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:
579 npulse = decPulse;
580 dirPulse = npulse->findWidgetByName("TIMED_GUIDE_N");
581 break;
582
583 case DEC_DEC_DIR:
584 npulse = decPulse;
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
597 sendNewProperty(npulse);
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
642bool Mount::sendCoords(SkyPoint * ScopeTarget)
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 {
713 emit newTarget(*ScopeTarget);
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
719 emit newTargetName(QString());
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();
751 sendNewProperty(EqProp);
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();
765 sendNewProperty(HorProp);
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);
814 checkTrackModes();
815 sendToMountDevice();
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 {
828 checkTrackModes();
829 sendToMountDevice();
830 }
831 }
832 else
833 sendToMountDevice();
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);
859 checkObjectAndSend();
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
880 checkObjectAndSend();
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);
947 sendNewProperty(motionSP);
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);
981 sendNewProperty(motionSP);
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);
1004 sendNewProperty(motionSP);
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);
1027 sendNewProperty(parkSP);
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);
1048 sendNewProperty(parkSP);
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
1107 sendNewProperty(motionSP);
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
1121 sendNewProperty(motionSP);
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
1135 sendNewProperty(motionSP);
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
1170 sendNewProperty(motionSP);
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
1191 sendNewProperty(slewRateSP);
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);
1223 sendNewProperty(alignSwitch);
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
1239 sendNewProperty(alignSwitch);
1240 wasExecuted = true;
1241 }
1242
1243 return wasExecuted;
1244}
1245
1246bool Mount::setSatelliteTLEandTrack(QString tle, const KStarsDateTime satPassStart, const KStarsDateTime satPassEnd)
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
1261 sendNewProperty(tleTextVec);
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
1283 sendNewProperty(parkSwitch);
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");
1294 if (clearSwitch && commitSwitch)
1295 {
1296 clearSwitch->reset();
1297 // ALIGNMENT_POINTSET_ACTION.CLEAR
1298 clearSwitch->at(4)->setState(ISS_ON);
1299 sendNewProperty(clearSwitch);
1300 commitSwitch->at(0)->setState(ISS_ON);
1301 sendNewProperty(commitSwitch);
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);
1312 sendNewProperty(clearSwitch);
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 ? mountStates[status].toString() : mountStates[status].untranslatedText() + QString(" %1").arg(
1338 getManualMotionString()));
1339 default:
1340 return translated ? mountStates[status].toString() : mountStates[status].untranslatedText();
1341 }
1342}
1343
1344QString Mount::getManualMotionString() const
1345{
1346 QString NSMotion, WEMotion;
1347
1348 auto movementSP = getSwitch("TELESCOPE_MOTION_NS");
1349 if (movementSP)
1350 {
1351 if (movementSP->at(MOTION_NORTH)->getState() == ISS_ON)
1352 NSMotion = 'N';
1353 else if (movementSP->at(MOTION_SOUTH)->getState() == ISS_ON)
1354 NSMotion = 'S';
1355 }
1356
1357 movementSP = getSwitch("TELESCOPE_MOTION_WE");
1358 if (movementSP)
1359 {
1360 if (movementSP->at(MOTION_WEST)->getState() == ISS_ON)
1361 WEMotion = 'W';
1362 else if (movementSP->at(MOTION_EAST)->getState() == ISS_ON)
1363 WEMotion = 'E';
1364 }
1365
1366 return QString("%1%2").arg(NSMotion, WEMotion);
1367}
1368
1369bool Mount::setTrackEnabled(bool enable)
1370{
1371 auto trackSP = getSwitch("TELESCOPE_TRACK_STATE");
1372 if (!trackSP)
1373 return false;
1374
1375 auto trackON = trackSP->findWidgetByName("TRACK_ON");
1376 auto trackOFF = trackSP->findWidgetByName("TRACK_OFF");
1377
1378 if (!trackON || !trackOFF)
1379 return false;
1380
1381 trackON->setState(enable ? ISS_ON : ISS_OFF);
1382 trackOFF->setState(enable ? ISS_OFF : ISS_ON);
1383
1384 sendNewProperty(trackSP);
1385
1386 return true;
1387}
1388
1389bool Mount::isTracking()
1390{
1391 return (status() == MOUNT_TRACKING);
1392}
1393
1394bool Mount::setTrackMode(uint8_t index)
1395{
1396 auto trackModeSP = getSwitch("TELESCOPE_TRACK_MODE");
1397 if (!trackModeSP)
1398 return false;
1399
1400 if (index >= trackModeSP->nsp)
1401 return false;
1402
1403 trackModeSP->reset();
1404 trackModeSP->at(index)->setState(ISS_ON);
1405
1406 sendNewProperty(trackModeSP);
1407
1408 return true;
1409}
1410
1411bool Mount::getTrackMode(uint8_t &index)
1412{
1413 auto trackModeSP = getSwitch("TELESCOPE_TRACK_MODE");
1414 if (!trackModeSP)
1415 return false;
1416
1417 index = trackModeSP->findOnSwitchIndex();
1418
1419 return true;
1420}
1421
1422bool Mount::setCustomTrackRate(double raRate, double deRate)
1423{
1424 auto trackRateNP = getNumber("TELESCOPE_TRACK_RATE");
1425 if (!trackRateNP)
1426 return false;
1427
1428 auto raRateN = trackRateNP->findWidgetByName("TRACK_RATE_RA");
1429 auto deRateN = trackRateNP->findWidgetByName("TRACK_RATE_DE");
1430
1431 if (!raRateN || !deRateN)
1432 return false;
1433
1434 raRateN->setValue(raRate);
1435 deRateN->setValue(deRate);
1436
1437 sendNewProperty(trackRateNP);
1438
1439 return true;
1440}
1441
1442bool Mount::getCustomTrackRate(double &raRate, double &deRate)
1443{
1444 auto trackRateNP = getNumber("TELESCOPE_TRACK_RATE");
1445 if (!trackRateNP)
1446 return false;
1447
1448 auto raRateN = trackRateNP->findWidgetByName("TRACK_RATE_RA");
1449 auto deRateN = trackRateNP->findWidgetByName("TRACK_RATE_DE");
1450
1451 if (!raRateN || !deRateN)
1452 return false;
1453
1454 raRate = raRateN->getValue();
1455 deRate = deRateN->getValue();
1456
1457 return true;
1458
1459}
1460
1461bool Mount::sendParkingOptionCommand(ParkOptionCommand command)
1462{
1463 auto parkOptionsSP = getSwitch("TELESCOPE_PARK_OPTION");
1464 if (!parkOptionsSP)
1465 return false;
1466
1467 parkOptionsSP->reset();
1468 parkOptionsSP->at(command)->setState(ISS_ON);
1469 sendNewProperty(parkOptionsSP);
1470
1471 return true;
1472}
1473
1474Mount::Status Mount::status(INumberVectorProperty * nvp)
1475{
1476 switch (nvp->s)
1477 {
1478 case IPS_IDLE:
1479 if (inManualMotion)
1480 return MOUNT_MOVING;
1481 else if (isParked())
1482 return MOUNT_PARKED;
1483 else
1484 return MOUNT_IDLE;
1485
1486 case IPS_OK:
1487 if (inManualMotion)
1488 return MOUNT_MOVING;
1489 else if (inCustomParking)
1490 {
1491 inCustomParking = false;
1492 // set CURRENT position as the desired parking position
1493 sendParkingOptionCommand(PARK_OPTION_CURRENT);
1494 // Write data to disk
1495 sendParkingOptionCommand(PARK_OPTION_WRITE_DATA);
1496
1497 return MOUNT_TRACKING;
1498 }
1499 else
1500 return MOUNT_TRACKING;
1501
1502 case IPS_BUSY:
1503 {
1504 if (inManualMotion)
1505 return MOUNT_MOVING;
1506
1507 auto parkSP = getSwitch("TELESCOPE_PARK");
1508 if (parkSP && parkSP->getState() == IPS_BUSY)
1509 return MOUNT_PARKING;
1510 else
1511 return MOUNT_SLEWING;
1512 }
1513
1514 case IPS_ALERT:
1515 inCustomParking = false;
1516 return MOUNT_ERROR;
1517 }
1518
1519 return MOUNT_ERROR;
1520}
1521
1523{
1524 dms lst = KStarsData::Instance()->geo()->GSTtoLST(KStarsData::Instance()->clock()->utc().gst());
1525 return dms(lst.Degrees() - currentCoords.ra().Degrees());
1526}
1527
1528bool Mount::isReversed(INDI_EQ_AXIS axis)
1529{
1530 auto reversed = getSwitch("TELESCOPE_REVERSE_MOTION");
1531 if (!reversed)
1532 return false;
1533
1534 return reversed->at(axis == AXIS_DE ? 0 : 1)->getState() == ISS_ON;
1535}
1536
1537bool Mount::setReversedEnabled(INDI_EQ_AXIS axis, bool enabled)
1538{
1539 auto reversed = getSwitch("TELESCOPE_REVERSE_MOTION");
1540 if (!reversed)
1541 return false;
1542
1543 reversed->at(axis == AXIS_DE ? 0 : 1)->setState(enabled ? ISS_ON : ISS_OFF);
1544 sendNewProperty(reversed);
1545 return true;
1546}
1547
1549{
1550 updateCoordinatesTimer.stop();
1551 centerLockTimer.stop();
1552}
1553
1554}
1555
1556QDBusArgument &operator<<(QDBusArgument &argument, const ISD::Mount::Status &source)
1557{
1558 argument.beginStructure();
1559 argument << static_cast<int>(source);
1560 argument.endStructure();
1561 return argument;
1562}
1563
1564const QDBusArgument &operator>>(const QDBusArgument &argument, ISD::Mount::Status &dest)
1565{
1566 int a;
1567 argument.beginStructure();
1568 argument >> a;
1569 argument.endStructure();
1570 dest = static_cast<ISD::Mount::Status>(a);
1571 return argument;
1572}
1573
1574QDBusArgument &operator<<(QDBusArgument &argument, const ISD::Mount::PierSide &source)
1575{
1576 argument.beginStructure();
1577 argument << static_cast<int>(source);
1578 argument.endStructure();
1579 return argument;
1580}
1581
1582const QDBusArgument &operator>>(const QDBusArgument &argument, ISD::Mount::PierSide &dest)
1583{
1584 int a;
1585 argument.beginStructure();
1586 argument >> a;
1587 argument.endStructure();
1588 dest = static_cast<ISD::Mount::PierSide>(a);
1589 return argument;
1590}
1591
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.
Q_INVOKABLE QAction * action(const QString &name) const
GeoLocation * geo()
Definition kstarsdata.h:230
SkyMapComposite * skyComposite()
Definition kstarsdata.h:166
Extension of QDateTime for KStars KStarsDateTime can represent the date/time as a Julian Day,...
SkyMap * map() const
Definition kstars.h:141
static KStars * Instance()
Definition kstars.h:123
void slotSetTelescopeEnabled(bool enable)
slotSetTelescopeEnabled call when telescope comes online or goes offline.
virtual KActionCollection * actionCollection() const
SkyObject * objectNearest(SkyPoint *p, double &maxrad) override
SkyPoint * focus()
Retrieve the Focus point; the position on the sky at the center of the skymap.
Definition skymap.h:123
void setDestination(const SkyPoint &f)
sets the destination point of the sky map.
Definition skymap.cpp:984
void setFocusObject(SkyObject *o)
Set the FocusObject pointer to the argument.
Definition skymap.cpp:371
void setFocusPoint(SkyPoint *f)
set the FocusPoint; the position that is to be the next Destination.
Definition skymap.h:204
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
const CachingDms & ra0() const
Definition skypoint.h:251
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 altRefracted() const
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
const dms & az() const
Definition skypoint.h:275
void setAlt(dms alt)
Sets Alt, the Altitude.
Definition skypoint.h:194
const dms & alt() const
Definition skypoint.h:281
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
const CachingDms & dec0() const
Definition skypoint.h:257
void setDec0(dms d)
Sets Dec0, the catalog Declination.
Definition skypoint.h:119
SkyPoint catalogueCoord(long double jdf)
Computes the J2000.0 catalogue coordinates for this SkyPoint using the epoch removing aberration,...
Definition skypoint.cpp:710
An angle, stored as degrees, but expressible in many ways.
Definition dms.h:38
double Hours() const
Definition dms.h:168
const QString toDMSString(const bool forceSign=false, const bool machineReadable=false, const bool highPrecision=false) const
Definition dms.cpp:287
const QString toHMSString(const bool machineReadable=false, const bool highPrecision=false) const
Definition dms.cpp:378
const double & Degrees() const
Definition dms.h:141
KLocalizedString KI18N_EXPORT ki18n(const char *text)
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
ISD is a collection of INDI Standard Devices.
KCALENDARCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &)
GeoCoordinates geo(const QVariant &location)
KTEXTEDITOR_EXPORT QDebug operator<<(QDebug s, const MovingCursor &cursor)
NETWORKMANAGERQT_EXPORT NetworkManager::Status status()
QCA_EXPORT QVariant getProperty(const QString &name)
void setEnabled(bool)
char * data()
QString toString(QStringView format, QCalendar cal) const const
void beginStructure()
void endStructure()
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)
bool disconnect(const QMetaObject::Connection &connection)
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)
void reset()
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void setInterval(int msec)
bool isActive() const const
void start()
void stop()
void timeout()
bool isValid() const const
void update()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 19 2024 11:51:34 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.