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 <QtDBus/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 // update current status
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);
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 }
264
265 EqCoordPreviousState = nvp->getState();
266
268 }
269 // JM 2022.03.11 Only process HORIZONTAL_COORD if it was the ONLY source of information
270 // When a driver both sends EQUATORIAL_COORD and HORIZONTAL_COORD, we should prioritize EQUATORIAL_COORD
271 // especially since the conversion from horizontal to equatorial is not as accurate and can result in weird
272 // coordinates near the poles.
273 else if (nvp->isNameMatch("HORIZONTAL_COORD") && m_hasEquatorialCoordProperty == false)
274 {
275 auto Az = nvp->findWidgetByName("AZ");
276 auto Alt = nvp->findWidgetByName("ALT");
277
278 if (Az == nullptr || Alt == nullptr)
279 return;
280
281 currentCoords.setAz(Az->value);
282 currentCoords.setAlt(Alt->value);
283 currentCoords.HorizontalToEquatorial(KStars::Instance()->data()->lst(),
284 KStars::Instance()->data()->geo()->lat());
285
286 // calculate J2000 coordinates
287 updateJ2000Coordinates(&currentCoords);
288
289 // ensure that coordinates are regularly updated
290 if (! updateCoordinatesTimer.isActive())
291 updateCoordinatesTimer.start();
292
294 }
295 else if (nvp->isNameMatch("POLLING_PERIOD"))
296 {
297 // set the timer how often the coordinates should be published
298 auto period = nvp->findWidgetByName("PERIOD_MS");
299 if (period != nullptr)
300 updateCoordinatesTimer.setInterval(static_cast<int>(period->getValue()));
301
302 }
303}
304
305void Mount::processSwitch(INDI::Property prop)
306{
307 bool manualMotionChanged = false;
308 auto svp = prop.getSwitch();
309
310 if (svp->isNameMatch("CONNECTION"))
311 {
312 auto conSP = svp->findWidgetByName("CONNECT");
313 if (conSP)
314 {
315 // TODO We must allow for multiple mount drivers to be online, not just one
316 // For the actions taken, the user should be able to specify which mounts shall receive the commands. It could be one
317 // or more. For now, we enable/disable telescope group on the assumption there is only one mount present.
318 if (conSP->getState() == ISS_ON)
320 else
321 {
323 centerLockTimer.stop();
324 }
325 }
326 }
327 else if (svp->isNameMatch("TELESCOPE_PARK"))
329 else if (svp->isNameMatch("TELESCOPE_ABORT_MOTION"))
330 {
331 if (svp->s == IPS_OK)
332 {
333 inCustomParking = false;
334 KSNotification::event(QLatin1String("MountAborted"), i18n("Mount motion was aborted"), KSNotification::Mount,
335 KSNotification::Warn);
336 }
337 }
338 else if (svp->isNameMatch("TELESCOPE_PIER_SIDE"))
339 {
340 int currentSide = IUFindOnSwitchIndex(svp);
341 if (currentSide != m_PierSide)
342 {
343 m_PierSide = static_cast<PierSide>(currentSide);
344 emit pierSideChanged(m_PierSide);
345 }
346 }
347 else if (svp->isNameMatch("TELESCOPE_TRACK_MODE"))
348 {
349 auto sp = svp->findOnSwitch();
350 if (sp)
351 {
352 if (sp->isNameMatch("TRACK_SIDEREAL"))
353 currentTrackMode = TRACK_SIDEREAL;
354 else if (sp->isNameMatch("TRACK_SOLAR"))
355 currentTrackMode = TRACK_SOLAR;
356 else if (sp->isNameMatch("TRACK_LUNAR"))
357 currentTrackMode = TRACK_LUNAR;
358 else
359 currentTrackMode = TRACK_CUSTOM;
360 }
361 }
362 else if (svp->isNameMatch("TELESCOPE_MOTION_NS"))
363 manualMotionChanged = true;
364 else if (svp->isNameMatch("TELESCOPE_MOTION_WE"))
365 manualMotionChanged = true;
366 else if (svp->isNameMatch("TELESCOPE_REVERSE_MOTION"))
367 {
368 emit axisReversed(AXIS_DE, svp->at(0)->getState() == ISS_ON);
369 emit axisReversed(AXIS_RA, svp->at(1)->getState() == ISS_ON);
370 }
371
372 if (manualMotionChanged)
373 {
374 auto NSCurrentMotion = getSwitch("TELESCOPE_MOTION_NS")->getState();
375 auto WECurrentMotion = getSwitch("TELESCOPE_MOTION_WE")->getState();
376 inCustomParking = false;
377 inManualMotion = (NSCurrentMotion == IPS_BUSY || WECurrentMotion == IPS_BUSY);
378 }
379}
380
381void Mount::processText(INDI::Property prop)
382{
383 auto tvp = prop.getText();
384 if (tvp->isNameMatch("SAT_TLE_TEXT"))
385 {
386 if ((tvp->getState() == IPS_OK) && (m_TLEIsSetForTracking))
387 {
388 auto trajWindow = getText("SAT_PASS_WINDOW");
389 if (!trajWindow)
390 {
391 qCDebug(KSTARS_INDI) << "Property SAT_PASS_WINDOW not found";
392 }
393 else
394 {
395 auto trajStart = trajWindow->findWidgetByName("SAT_PASS_WINDOW_START");
396 auto trajEnd = trajWindow->findWidgetByName("SAT_PASS_WINDOW_END");
397
398 if (!trajStart || !trajEnd)
399 {
400 qCDebug(KSTARS_INDI) << "Start or end in SAT_PASS_WINDOW not found";
401 }
402 else
403 {
404 trajStart->setText(g_satPassStart.toString(Qt::ISODate).toLocal8Bit().data());
405 trajEnd->setText(g_satPassEnd.toString(Qt::ISODate).toLocal8Bit().data());
406
407 sendNewProperty(trajWindow);
408 m_windowIsSetForTracking = true;
409 }
410 }
411 }
412 }
413 else if (tvp->isNameMatch("SAT_PASS_WINDOW"))
414 {
415 if ((tvp->getState() == IPS_OK) && (m_TLEIsSetForTracking) && (m_windowIsSetForTracking))
416 {
417 auto trackSwitchV = getSwitch("SAT_TRACKING_STAT");
418 if (!trackSwitchV)
419 {
420 qCDebug(KSTARS_INDI) << "Property SAT_TRACKING_STAT not found";
421 }
422 else
423 {
424 auto trackSwitch = trackSwitchV->findWidgetByName("SAT_TRACK");
425 if (trackSwitch)
426 {
427 trackSwitchV->reset();
428 trackSwitch->setState(ISS_ON);
429
430 sendNewProperty(trackSwitchV);
431 m_TLEIsSetForTracking = false;
432 m_windowIsSetForTracking = false;
433 }
434 }
435 }
436 }
437}
438
440{
441 auto svp = getSwitch("TELESCOPE_PARK");
442 if (!svp)
443 return;
444
445 auto sp = svp->findWidgetByName("PARK");
446 if (sp)
447 {
448 if (svp->getState() == IPS_ALERT)
449 {
450 // First, inform everyone watch this that an error occurred.
451 emit newParkStatus(PARK_ERROR);
452 // JM 2021-03-08: Reset parking internal state to either PARKED or UNPARKED.
453 // Whatever the current switch is set to
454 m_ParkStatus = (sp->getState() == ISS_ON) ? PARK_PARKED : PARK_UNPARKED;
455 KSNotification::event(QLatin1String("MountParkingFailed"), i18n("Mount parking failed"), KSNotification::Mount,
456 KSNotification::Alert);
457 }
458 else if (svp->getState() == IPS_BUSY && sp->s == ISS_ON && m_ParkStatus != PARK_PARKING)
459 {
460 m_ParkStatus = PARK_PARKING;
461 KSNotification::event(QLatin1String("MountParking"), i18n("Mount parking is in progress"), KSNotification::Mount);
462 currentObject = nullptr;
463
464 emit newParkStatus(m_ParkStatus);
465 }
466 else if (svp->getState() == IPS_BUSY && sp->getState() == ISS_OFF && m_ParkStatus != PARK_UNPARKING)
467 {
468 m_ParkStatus = PARK_UNPARKING;
469 KSNotification::event(QLatin1String("MountUnParking"), i18n("Mount unparking is in progress"), KSNotification::Mount);
470
471 emit newParkStatus(m_ParkStatus);
472 }
473 else if (svp->getState() == IPS_OK && sp->getState() == ISS_ON && m_ParkStatus != PARK_PARKED)
474 {
475 m_ParkStatus = PARK_PARKED;
476 KSNotification::event(QLatin1String("MountParked"), i18n("Mount parked"), KSNotification::Mount);
477 currentObject = nullptr;
478
479 emit newParkStatus(m_ParkStatus);
480
481 QAction *parkAction = KStars::Instance()->actionCollection()->action("telescope_park");
482 if (parkAction)
483 parkAction->setEnabled(false);
484 QAction *unParkAction = KStars::Instance()->actionCollection()->action("telescope_unpark");
485 if (unParkAction)
486 unParkAction->setEnabled(true);
487
488 emit newTarget(currentCoords);
489 emit newTargetName(QString());
490 }
491 else if ( (svp->getState() == IPS_OK || svp->getState() == IPS_IDLE) && sp->getState() == ISS_OFF
492 && m_ParkStatus != PARK_UNPARKED)
493 {
494 m_ParkStatus = PARK_UNPARKED;
495 KSNotification::event(QLatin1String("MountUnparked"), i18n("Mount unparked"), KSNotification::Mount);
496 currentObject = nullptr;
497
498 emit newParkStatus(m_ParkStatus);
499
500 QAction *parkAction = KStars::Instance()->actionCollection()->action("telescope_park");
501 if (parkAction)
502 parkAction->setEnabled(true);
503 QAction *unParkAction = KStars::Instance()->actionCollection()->action("telescope_unpark");
504 if (unParkAction)
505 unParkAction->setEnabled(false);
506 }
507 }
508}
509bool Mount::canGuide()
510{
511 auto raPulse = getNumber("TELESCOPE_TIMED_GUIDE_WE");
512 auto decPulse = getNumber("TELESCOPE_TIMED_GUIDE_NS");
513
514 return raPulse && decPulse;
515}
516
517bool Mount::canPark()
518{
519 auto parkSP = getSwitch("TELESCOPE_PARK");
520
521 if (!parkSP)
522 return false;
523
524 auto parkSW = parkSP->findWidgetByName("PARK");
525
526 return (parkSW != nullptr);
527}
528
529bool Mount::isSlewing()
530{
531 auto EqProp = getNumber("EQUATORIAL_EOD_COORD");
532
533 if (!EqProp)
534 return false;
535
536 return (EqProp->getState() == IPS_BUSY);
537}
538
539bool Mount::isInMotion()
540{
541 return (isSlewing() || inManualMotion);
542}
543
544bool Mount::doPulse(GuideDirection ra_dir, int ra_msecs, GuideDirection dec_dir, int dec_msecs)
545{
546 if (canGuide() == false)
547 return false;
548
549 bool raOK = doPulse(ra_dir, ra_msecs);
550 bool decOK = doPulse(dec_dir, dec_msecs);
551
552 return raOK && decOK;
553}
554
555bool Mount::doPulse(GuideDirection dir, int msecs)
556{
557 auto raPulse = getNumber("TELESCOPE_TIMED_GUIDE_WE");
558 auto decPulse = getNumber("TELESCOPE_TIMED_GUIDE_NS");
559 INDI::PropertyView<INumber> *npulse = nullptr;
560 INDI::WidgetView<INumber> *dirPulse = nullptr;
561
562 if (!raPulse || !decPulse)
563 return false;
564
565 switch (dir)
566 {
567 case RA_INC_DIR:
568 npulse = raPulse;
569 dirPulse = npulse->findWidgetByName("TIMED_GUIDE_W");
570 break;
571
572 case RA_DEC_DIR:
573 npulse = raPulse;
574 dirPulse = npulse->findWidgetByName("TIMED_GUIDE_E");
575 break;
576
577 case DEC_INC_DIR:
578 npulse = decPulse;
579 dirPulse = npulse->findWidgetByName("TIMED_GUIDE_N");
580 break;
581
582 case DEC_DEC_DIR:
583 npulse = decPulse;
584 dirPulse = npulse->findWidgetByName("TIMED_GUIDE_S");
585 break;
586
587 default:
588 return false;
589 }
590
591 if (!dirPulse)
592 return false;
593
594 dirPulse->setValue(msecs);
595
596 sendNewProperty(npulse);
597
598 return true;
599}
600
601
602void Mount::setCustomParking(SkyPoint * coords)
603{
604 bool rc = false;
605 if (coords == nullptr)
606 rc = sendCoords(KStars::Instance()->map()->clickedPoint());
607 else
608 rc = sendCoords(coords);
609
610 inCustomParking = rc;
611}
612
613void Mount::find()
614{
615 updateJ2000Coordinates(&currentCoords);
616 double maxrad = 1000.0 / Options::zoomFactor();
617 SkyObject *currentObject = KStarsData::Instance()->skyComposite()->objectNearest(&currentCoords, maxrad);
618 KStars::Instance()->map()->setFocusObject(currentObject);
619 KStars::Instance()->map()->setDestination(currentCoords);
620}
621void Mount::centerLock()
622{
623 if (Options::isTracking() == false ||
624 currentCoords.angularDistanceTo(KStars::Instance()->map()->focus()).Degrees() > 0.5)
625 {
626 updateJ2000Coordinates(&currentCoords);
627 KStars::Instance()->map()->setDestination(currentCoords);
628 KStars::Instance()->map()->setFocusPoint(&currentCoords);
629 KStars::Instance()->map()->setFocusObject(nullptr);
630 Options::setIsTracking(true);
631 }
632 centerLockTimer.start();
633}
634
635void Mount::centerUnlock()
636{
637 KStars::Instance()->map()->stopTracking();
638 centerLockTimer.stop();
639}
640
641bool Mount::sendCoords(SkyPoint * ScopeTarget)
642{
643 INumber *RAEle = nullptr;
644 INumber *DecEle = nullptr;
645 INumber *AzEle = nullptr;
646 INumber *AltEle = nullptr;
647 double currentRA = 0, currentDEC = 0, currentAlt = 0, currentAz = 0;
648 bool useJ2000(false);
649
650 auto EqProp = getNumber("EQUATORIAL_EOD_COORD");
651 if (!EqProp)
652 {
653 // J2000 Property
654 EqProp = getNumber("EQUATORIAL_COORD");
655 if (EqProp)
656 useJ2000 = true;
657 }
658
659 auto HorProp = getNumber("HORIZONTAL_COORD");
660
661 if (EqProp && EqProp->getPermission() == IP_RO)
662 EqProp = nullptr;
663
664 if (HorProp && HorProp->getPermission() == IP_RO)
665 HorProp = nullptr;
666
667 //qDebug() << Q_FUNC_INFO << "Skymap click - RA: " << scope_target->ra().toHMSString() << " DEC: " << scope_target->dec().toDMSString();
668
669 if (EqProp)
670 {
671 RAEle = EqProp->findWidgetByName("RA");
672 if (!RAEle)
673 return false;
674
675 DecEle = EqProp->findWidgetByName("DEC");
676 if (!DecEle)
677 return false;
678
679 //if (useJ2000)
680 //ScopeTarget->apparentCoord( KStars::Instance()->data()->ut().djd(), static_cast<long double>(J2000));
681
682 currentRA = RAEle->value;
683 currentDEC = DecEle->value;
684
685 ScopeTarget->EquatorialToHorizontal(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat());
686 }
687
688 if (HorProp)
689 {
690 AzEle = IUFindNumber(HorProp, "AZ");
691 if (!AzEle)
692 return false;
693 AltEle = IUFindNumber(HorProp, "ALT");
694 if (!AltEle)
695 return false;
696
697 currentAz = AzEle->value;
698 currentAlt = AltEle->value;
699 }
700
701 /* Could not find either properties! */
702 if (EqProp == nullptr && HorProp == nullptr)
703 return false;
704
705 // Function for sending the coordinates to the INDI mount device
706 // via the ClientManager. This helper function translates EKOS objects into INDI commands.
707 auto sendToMountDevice = [ = ]()
708 {
709 // communicate the new target only if a slew will be executed for the given coordinates
710 if (slewDefined())
711 {
712 emit newTarget(*ScopeTarget);
713 if (currentObject)
714 emit newTargetName(currentObject->name());
715 // If there is no object, we must clear target as it might give wrong
716 // indication we are still on it.
717 else
718 emit newTargetName(QString());
719 }
720
721 if (EqProp)
722 {
723 dms ra, de;
724
725 if (useJ2000)
726 {
727 // If we have invalid DEC, then convert coords to J2000
728 if (ScopeTarget->dec0().Degrees() == 180.0)
729 {
730 ScopeTarget->setRA0(ScopeTarget->ra());
731 ScopeTarget->setDec0(ScopeTarget->dec());
732 ScopeTarget->catalogueCoord( KStars::Instance()->data()->ut().djd());
733 ra = ScopeTarget->ra();
734 de = ScopeTarget->dec();
735 }
736 else
737 {
738 ra = ScopeTarget->ra0();
739 de = ScopeTarget->dec0();
740 }
741 }
742 else
743 {
744 ra = ScopeTarget->ra();
745 de = ScopeTarget->dec();
746 }
747
748 RAEle->value = ra.Hours();
749 DecEle->value = de.Degrees();
750 sendNewProperty(EqProp);
751
752 qCDebug(KSTARS_INDI) << "ISD:Telescope sending coords RA:" << ra.toHMSString() <<
753 "(" << RAEle->value << ") DE:" << de.toDMSString() <<
754 "(" << DecEle->value << ")";
755
756 RAEle->value = currentRA;
757 DecEle->value = currentDEC;
758 }
759 // Only send Horizontal Coord property if Equatorial is not available.
760 else if (HorProp)
761 {
762 AzEle->value = ScopeTarget->az().Degrees();
763 AltEle->value = ScopeTarget->alt().Degrees();
764 sendNewProperty(HorProp);
765 AzEle->value = currentAz;
766 AltEle->value = currentAlt;
767 }
768
769 };
770
771 // Helper function that first checks for the selected target object whether the
772 // tracking modes have to be adapted (special cases moon and sun), explicitely warns before
773 // slewing to the sun and finally (independant whether there exists a target object
774 // for the target coordinates) calls sendToMountDevice
775 auto checkObjectAndSend = [ = ]()
776 {
777 // Search within 0.1 degrees indepdent of zoom level.
778 double maxrad = 0.1;
779 currentObject = KStarsData::Instance()->skyComposite()->objectNearest(ScopeTarget, maxrad);
780 if (currentObject)
781 {
782 auto checkTrackModes = [ = ]()
783 {
784 if (m_hasTrackModes)
785 {
786 // Tracking Moon
787 if (currentObject->type() == SkyObject::MOON)
788 {
789 if (currentTrackMode != TRACK_LUNAR && TrackMap.contains(TRACK_LUNAR))
790 setTrackMode(TrackMap.value(TRACK_LUNAR));
791 }
792 // Tracking Sun
793 else if (currentObject->name() == i18n("Sun"))
794 {
795 if (currentTrackMode != TRACK_SOLAR && TrackMap.contains(TRACK_SOLAR))
796 setTrackMode(TrackMap.value(TRACK_SOLAR));
797 }
798 // If Last track mode was either set to SOLAR or LUNAR but now we are slewing to a different object
799 // then we automatically fallback to sidereal. If the current track mode is CUSTOM or something else, nothing
800 // changes.
801 else if (currentTrackMode == TRACK_SOLAR || currentTrackMode == TRACK_LUNAR)
802 setTrackMode(TRACK_SIDEREAL);
803
804 }
805 };
806
807 // Sun Warning, but don't ask if tracking is already solar.
808 if (currentObject->name() == i18n("Sun") && currentTrackMode != TRACK_SOLAR)
809 {
810 connect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, [ = ]()
811 {
812 KSMessageBox::Instance()->disconnect(this);
813 checkTrackModes();
814 sendToMountDevice();
815 });
816 connect(KSMessageBox::Instance(), &KSMessageBox::rejected, this, [ = ]()
817 {
818 KSMessageBox::Instance()->disconnect(this);
819 });
820
821 KSMessageBox::Instance()->questionYesNo(
822 i18n("Warning! Looking at the Sun without proper protection can lead to irreversible eye damage!"),
823 i18n("Sun Warning"));
824 }
825 else
826 {
827 checkTrackModes();
828 sendToMountDevice();
829 }
830 }
831 else
832 sendToMountDevice();
833 };
834
835 // If altitude limits is enabled, then reject motion immediately.
836 double targetAlt = ScopeTarget->altRefracted().Degrees();
837
838 if ((-90 <= minAlt && maxAlt <= 90) && (targetAlt < minAlt || targetAlt > maxAlt) && !altLimitsTrackingOnly)
839 {
840 KSNotification::event(QLatin1String("IndiServerMessage"),
841 i18n("Requested altitude %1 is outside the specified altitude limit boundary (%2,%3).",
842 QString::number(targetAlt, 'g', 3), QString::number(minAlt, 'g', 3),
843 QString::number(maxAlt, 'g', 3)), KSNotification::Mount, KSNotification::Warn);
844 qCInfo(KSTARS_INDI) << "Requested altitude " << QString::number(targetAlt, 'g', 3)
845 << " is outside the specified altitude limit boundary ("
846 << QString::number(minAlt, 'g', 3) << "," << QString::number(maxAlt, 'g', 3) << ").";
847 return false;
848 }
849
850 // If disabled, then check if below horizon and warning the user unless the user previously dismissed it.
851 if (Options::confirmBelowHorizon() && targetAlt < 0 && minAlt == -1)
852 {
853 connect(KSMessageBox::Instance(), &KSMessageBox::accepted, this, [ = ]()
854 {
855 if (minAlt < -90 && +90 < maxAlt)
856 Options::setConfirmBelowHorizon(false);
857 KSMessageBox::Instance()->disconnect(this);
858 checkObjectAndSend();
859 });
860 connect(KSMessageBox::Instance(), &KSMessageBox::rejected, this, [ = ]()
861 {
862 KSMessageBox::Instance()->disconnect(this);
863 if (EqProp)
864 {
865 RAEle->value = currentRA;
866 DecEle->value = currentDEC;
867 }
868 if (HorProp)
869 {
870 AzEle->value = currentAz;
871 AltEle->value = currentAlt;
872 }
873 });
874
875 KSMessageBox::Instance()->questionYesNo(i18n("Requested altitude is below the horizon. Are you sure you want to proceed?"),
876 i18n("Telescope Motion"), 15, false);
877 }
878 else
879 checkObjectAndSend();
880
881 return true;
882}
883
885{
886 auto motionSP = getSwitch("ON_COORD_SET");
887
888 if (motionSP == nullptr)
889 return false;
890 // A slew will happen if either Track, Slew, or Flip
891 // is selected
892 auto sp = motionSP->findOnSwitch();
893 if(sp != nullptr &&
894 (sp->name == std::string("TRACK") ||
895 sp->name == std::string("SLEW") ||
896 sp->name == std::string("FLIP")))
897 {
898 return true;
899 }
900 else
901 {
902 return false;
903 }
904}
905
906bool Mount::Slew(double ra, double dec, bool flip)
907{
908 SkyPoint target;
909
910 if (m_isJ2000)
911 {
912 target.setRA0(ra);
913 target.setDec0(dec);
914 }
915 else
916 {
917 target.setRA(ra);
918 target.setDec(dec);
919 }
920
921 return Slew(&target, flip);
922}
923
924bool Mount::Slew(SkyPoint * ScopeTarget, bool flip)
925{
926 auto motionSP = getSwitch("ON_COORD_SET");
927
928 if (!motionSP)
929 return false;
930
931 auto slewSW = flip ? motionSP->findWidgetByName("FLIP") : motionSP->findWidgetByName("TRACK");
932
933 if (flip && (!slewSW))
934 slewSW = motionSP->findWidgetByName("TRACK");
935
936 if (!slewSW)
937 slewSW = motionSP->findWidgetByName("SLEW");
938
939 if (!slewSW)
940 return false;
941
942 if (slewSW->getState() != ISS_ON)
943 {
944 motionSP->reset();
945 slewSW->setState(ISS_ON);
946 sendNewProperty(motionSP);
947
948 qCDebug(KSTARS_INDI) << "ISD:Telescope: " << slewSW->getName();
949 }
950
951 return sendCoords(ScopeTarget);
952}
953
954bool Mount::Sync(double ra, double dec)
955{
956 SkyPoint target;
957
958 target.setRA(ra);
959 target.setDec(dec);
960
961 return Sync(&target);
962}
963
964bool Mount::Sync(SkyPoint * ScopeTarget)
965{
966 auto motionSP = getSwitch("ON_COORD_SET");
967
968 if (!motionSP)
969 return false;
970
971 auto syncSW = motionSP->findWidgetByName("SYNC");
972
973 if (!syncSW)
974 return false;
975
976 if (syncSW->getState() != ISS_ON)
977 {
978 motionSP->reset();
979 syncSW->setState(ISS_ON);
980 sendNewProperty(motionSP);
981
982 qCDebug(KSTARS_INDI) << "ISD:Telescope: Syncing...";
983 }
984
985 return sendCoords(ScopeTarget);
986}
987
988bool Mount::abort()
989{
990 auto motionSP = getSwitch("TELESCOPE_ABORT_MOTION");
991
992 if (!motionSP)
993 return false;
994
995 auto abortSW = motionSP->findWidgetByName("ABORT");
996
997 if (!abortSW)
998 return false;
999
1000 qCDebug(KSTARS_INDI) << "ISD:Telescope: Aborted." << Qt::endl;
1001
1002 abortSW->setState(ISS_ON);
1003 sendNewProperty(motionSP);
1004
1005 inCustomParking = false;
1006
1007 return true;
1008}
1009
1010bool Mount::park()
1011{
1012 auto parkSP = getSwitch("TELESCOPE_PARK");
1013
1014 if (!parkSP)
1015 return false;
1016
1017 auto parkSW = parkSP->findWidgetByName("PARK");
1018
1019 if (!parkSW)
1020 return false;
1021
1022 qCDebug(KSTARS_INDI) << "ISD:Telescope: Parking..." << Qt::endl;
1023
1024 parkSP->reset();
1025 parkSW->setState(ISS_ON);
1026 sendNewProperty(parkSP);
1027
1028 return true;
1029}
1030
1031bool Mount::unpark()
1032{
1033 auto parkSP = getSwitch("TELESCOPE_PARK");
1034
1035 if (!parkSP)
1036 return false;
1037
1038 auto parkSW = parkSP->findWidgetByName("UNPARK");
1039
1040 if (!parkSW)
1041 return false;
1042
1043 qCDebug(KSTARS_INDI) << "ISD:Telescope: UnParking..." << Qt::endl;
1044
1045 parkSP->reset();
1046 parkSW->setState(ISS_ON);
1047 sendNewProperty(parkSP);
1048
1049 return true;
1050}
1051
1052bool Mount::getEqCoords(double * ra, double * dec)
1053{
1054 auto EqProp = getNumber("EQUATORIAL_EOD_COORD");
1055 if (!EqProp)
1056 {
1057 EqProp = getNumber("EQUATORIAL_COORD");
1058 if (!EqProp)
1059 return false;
1060 }
1061
1062 auto RAEle = EqProp->findWidgetByName("RA");
1063 if (!RAEle)
1064 return false;
1065
1066 auto DecEle = EqProp->findWidgetByName("DEC");
1067 if (!DecEle)
1068 return false;
1069
1070 *ra = RAEle->getValue();
1071 *dec = DecEle->getValue();
1072
1073 return true;
1074}
1075
1076bool Mount::MoveNS(VerticalMotion dir, MotionCommand cmd)
1077{
1078 auto motionSP = getSwitch("TELESCOPE_MOTION_NS");
1079
1080 if (!motionSP)
1081 return false;
1082
1083 auto motionNorth = motionSP->findWidgetByName("MOTION_NORTH");
1084 auto motionSouth = motionSP->findWidgetByName("MOTION_SOUTH");
1085
1086 if (!motionNorth || !motionSouth)
1087 return false;
1088
1089 // If same direction, return
1090 if (dir == MOTION_NORTH && motionNorth->getState() == ((cmd == MOTION_START) ? ISS_ON : ISS_OFF))
1091 return true;
1092
1093 if (dir == MOTION_SOUTH && motionSouth->getState() == ((cmd == MOTION_START) ? ISS_ON : ISS_OFF))
1094 return true;
1095
1096 motionSP->reset();
1097
1098 if (cmd == MOTION_START)
1099 {
1100 if (dir == MOTION_NORTH)
1101 motionNorth->setState(ISS_ON);
1102 else
1103 motionSouth->setState(ISS_ON);
1104 }
1105
1106 sendNewProperty(motionSP);
1107
1108 return true;
1109}
1110
1111bool Mount::StopWE()
1112{
1113 auto motionSP = getSwitch("TELESCOPE_MOTION_WE");
1114
1115 if (!motionSP)
1116 return false;
1117
1118 motionSP->reset();
1119
1120 sendNewProperty(motionSP);
1121
1122 return true;
1123}
1124
1125bool Mount::StopNS()
1126{
1127 auto motionSP = getSwitch("TELESCOPE_MOTION_NS");
1128
1129 if (!motionSP)
1130 return false;
1131
1132 motionSP->reset();
1133
1134 sendNewProperty(motionSP);
1135
1136 return true;
1137}
1138
1139bool Mount::MoveWE(HorizontalMotion dir, MotionCommand cmd)
1140{
1141 auto motionSP = getSwitch("TELESCOPE_MOTION_WE");
1142
1143 if (!motionSP)
1144 return false;
1145
1146 auto motionWest = motionSP->findWidgetByName("MOTION_WEST");
1147 auto motionEast = motionSP->findWidgetByName("MOTION_EAST");
1148
1149 if (!motionWest || !motionEast)
1150 return false;
1151
1152 // If same direction, return
1153 if (dir == MOTION_WEST && motionWest->getState() == ((cmd == MOTION_START) ? ISS_ON : ISS_OFF))
1154 return true;
1155
1156 if (dir == MOTION_EAST && motionEast->getState() == ((cmd == MOTION_START) ? ISS_ON : ISS_OFF))
1157 return true;
1158
1159 motionSP->reset();
1160
1161 if (cmd == MOTION_START)
1162 {
1163 if (dir == MOTION_WEST)
1164 motionWest->setState(ISS_ON);
1165 else
1166 motionEast->setState(ISS_ON);
1167 }
1168
1169 sendNewProperty(motionSP);
1170
1171 return true;
1172}
1173
1174bool Mount::setSlewRate(int index)
1175{
1176 auto slewRateSP = getSwitch("TELESCOPE_SLEW_RATE");
1177
1178 if (!slewRateSP)
1179 return false;
1180
1181 if (index < 0 || index > slewRateSP->count())
1182 return false;
1183 else if (slewRateSP->findOnSwitchIndex() == index)
1184 return true;
1185
1186 slewRateSP->reset();
1187
1188 slewRateSP->at(index)->setState(ISS_ON);
1189
1190 sendNewProperty(slewRateSP);
1191
1192 emit slewRateChanged(index);
1193
1194 return true;
1195}
1196
1197int Mount::getSlewRate() const
1198{
1199 auto slewRateSP = getSwitch("TELESCOPE_SLEW_RATE");
1200
1201 if (!slewRateSP)
1202 return -1;
1203
1204 return slewRateSP->findOnSwitchIndex();
1205}
1206
1207void Mount::setAltLimits(double minAltitude, double maxAltitude, bool trackingOnly)
1208{
1209 minAlt = minAltitude;
1210 maxAlt = maxAltitude;
1211 altLimitsTrackingOnly = trackingOnly;
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 Status newMountStatus = MOUNT_ERROR;
1477 switch (nvp->s)
1478 {
1479 case IPS_IDLE:
1480 if (inManualMotion)
1481 newMountStatus = MOUNT_MOVING;
1482 else if (isParked())
1483 newMountStatus = MOUNT_PARKED;
1484 else
1485 newMountStatus = MOUNT_IDLE;
1486 break;
1487
1488 case IPS_OK:
1489 if (inManualMotion)
1490 newMountStatus = MOUNT_MOVING;
1491 else if (inCustomParking)
1492 {
1493 inCustomParking = false;
1494 // set CURRENT position as the desired parking position
1495 sendParkingOptionCommand(PARK_OPTION_CURRENT);
1496 // Write data to disk
1497 sendParkingOptionCommand(PARK_OPTION_WRITE_DATA);
1498
1499 newMountStatus = MOUNT_TRACKING;
1500 }
1501 else
1502 newMountStatus = MOUNT_TRACKING;
1503 break;
1504
1505 case IPS_BUSY:
1506 if (inManualMotion)
1507 newMountStatus = MOUNT_MOVING;
1508 else
1509 {
1510 auto parkSP = getSwitch("TELESCOPE_PARK");
1511 if (parkSP && parkSP->getState() == IPS_BUSY)
1512 newMountStatus = MOUNT_PARKING;
1513 else
1514 newMountStatus = MOUNT_SLEWING;
1515 }
1516 break;
1517
1518 case IPS_ALERT:
1519 inCustomParking = false;
1520 newMountStatus = MOUNT_ERROR;
1521 }
1522
1523 if (previousMountStatus != newMountStatus)
1524 emit newStatus(newMountStatus);
1525
1526 previousMountStatus = newMountStatus;
1527 return newMountStatus;
1528}
1529
1530const dms Mount::hourAngle() const
1531{
1532 dms lst = KStarsData::Instance()->geo()->GSTtoLST(KStarsData::Instance()->clock()->utc().gst());
1533 return dms(lst.Degrees() - currentCoords.ra().Degrees());
1534}
1535
1536bool Mount::isReversed(INDI_EQ_AXIS axis)
1537{
1538 auto reversed = getSwitch("TELESCOPE_REVERSE_MOTION");
1539 if (!reversed)
1540 return false;
1541
1542 return reversed->at(axis == AXIS_DE ? 0 : 1)->getState() == ISS_ON;
1543}
1544
1545bool Mount::setReversedEnabled(INDI_EQ_AXIS axis, bool enabled)
1546{
1547 auto reversed = getSwitch("TELESCOPE_REVERSE_MOTION");
1548 if (!reversed)
1549 return false;
1550
1551 reversed->at(axis == AXIS_DE ? 0 : 1)->setState(enabled ? ISS_ON : ISS_OFF);
1552 sendNewProperty(reversed);
1553 return true;
1554}
1555
1557{
1558 updateCoordinatesTimer.stop();
1559 centerLockTimer.stop();
1560}
1561
1562}
1563
1564QDBusArgument &operator<<(QDBusArgument &argument, const ISD::Mount::Status &source)
1565{
1566 argument.beginStructure();
1567 argument << static_cast<int>(source);
1568 argument.endStructure();
1569 return argument;
1570}
1571
1572const QDBusArgument &operator>>(const QDBusArgument &argument, ISD::Mount::Status &dest)
1573{
1574 int a;
1575 argument.beginStructure();
1576 argument >> a;
1577 argument.endStructure();
1578 dest = static_cast<ISD::Mount::Status>(a);
1579 return argument;
1580}
1581
1582QDBusArgument &operator<<(QDBusArgument &argument, const ISD::Mount::PierSide &source)
1583{
1584 argument.beginStructure();
1585 argument << static_cast<int>(source);
1586 argument.endStructure();
1587 return argument;
1588}
1589
1590const QDBusArgument &operator>>(const QDBusArgument &argument, ISD::Mount::PierSide &dest)
1591{
1592 int a;
1593 argument.beginStructure();
1594 argument >> a;
1595 argument.endStructure();
1596 dest = static_cast<ISD::Mount::PierSide>(a);
1597 return argument;
1598}
1599
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:232
SkyMapComposite * skyComposite()
Definition kstarsdata.h:168
SkyMap * map() const
Definition kstars.h:139
static KStars * Instance()
Definition kstars.h:121
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:980
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:146
int type(void) const
Definition skyobject.h:189
The sky coordinates of a point in the sky.
Definition skypoint.h:45
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
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)
NETWORKMANAGERQT_EXPORT NetworkManager::Status status()
QDebug operator<<(QDebug dbg, const PerceptualColor::MultiSpinBoxSection &value)
QCA_EXPORT QVariant getProperty(const QString &name)
void setEnabled(bool)
char * data()
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 Mon Nov 18 2024 12:16:41 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.