Kstars

mountmodel.cpp
1/* Ekos Mount MOdel
2 SPDX-FileCopyrightText: 2018 Robert Lancaster
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7#include "mountmodel.h"
8
9#include "align.h"
10#include "kstars.h"
11#include "kstarsdata.h"
12#include "flagcomponent.h"
13#include "ksnotification.h"
14
15#include "skymap.h"
16#include "starobject.h"
17#include "skymapcomposite.h"
18#include "skyobject.h"
19#include "starobject.h"
20#include "dialogs/finddialog.h"
21#include "QProgressIndicator.h"
22
23#include <ekos_align_debug.h>
24
25#define AL_FORMAT_VERSION 1.0
26
27// Qt version calming
28#include <qtendl.h>
29
30namespace Ekos
31{
32MountModel::MountModel(Align *parent) : QDialog(parent)
33{
34 setupUi(this);
35
36 m_AlignInstance = parent;
37
38 setWindowTitle("Mount Model Tool");
39 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
40 alignTable->setColumnWidth(0, 70);
41 alignTable->setColumnWidth(1, 75);
42 alignTable->setColumnWidth(2, 130);
43 alignTable->setColumnWidth(3, 30);
44
45 wizardAlignB->setIcon(
46 QIcon::fromTheme("tools-wizard"));
48
49 clearAllAlignB->setIcon(
50 QIcon::fromTheme("application-exit"));
52
53 removeAlignB->setIcon(QIcon::fromTheme("list-remove"));
55
56 addAlignB->setIcon(QIcon::fromTheme("list-add"));
58
59 findAlignB->setIcon(QIcon::fromTheme("edit-find"));
61
62 alignTable->verticalHeader()->setDragDropOverwriteMode(false);
63 alignTable->verticalHeader()->setSectionsMovable(true);
64 alignTable->verticalHeader()->setDragEnabled(true);
65 alignTable->verticalHeader()->setDragDropMode(QAbstractItemView::InternalMove);
66 connect(alignTable->verticalHeader(), SIGNAL(sectionMoved(int, int, int)), this,
67 SLOT(moveAlignPoint(int, int, int)));
68
69 loadAlignB->setIcon(
70 QIcon::fromTheme("document-open"));
72
73 saveAlignB->setIcon(
74 QIcon::fromTheme("document-save"));
76
77 previewB->setIcon(QIcon::fromTheme("kstars_grid"));
79 previewB->setCheckable(true);
80
81 sortAlignB->setIcon(QIcon::fromTheme("svn-update"));
83
84 stopAlignB->setIcon(
85 QIcon::fromTheme("media-playback-stop"));
87
88 startAlignB->setIcon(
89 QIcon::fromTheme("media-playback-start"));
91
92 connect(wizardAlignB, &QPushButton::clicked, this, &Ekos::MountModel::slotWizardAlignmentPoints);
93 connect(alignTypeBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this,
94 &Ekos::MountModel::alignTypeChanged);
95
96 connect(starListBox, static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentTextChanged), this,
97 &Ekos::MountModel::slotStarSelected);
99 this,
100 &Ekos::MountModel::slotStarSelected);
101
102 connect(loadAlignB, &QPushButton::clicked, this, &Ekos::MountModel::slotLoadAlignmentPoints);
103 connect(saveAlignB, &QPushButton::clicked, this, &Ekos::MountModel::slotSaveAlignmentPoints);
104 connect(clearAllAlignB, &QPushButton::clicked, this, &Ekos::MountModel::slotClearAllAlignPoints);
105 connect(removeAlignB, &QPushButton::clicked, this, &Ekos::MountModel::slotRemoveAlignPoint);
106 connect(addAlignB, &QPushButton::clicked, this, &Ekos::MountModel::slotAddAlignPoint);
107 connect(findAlignB, &QPushButton::clicked, this, &Ekos::MountModel::slotFindAlignObject);
108 connect(sortAlignB, &QPushButton::clicked, this, &Ekos::MountModel::slotSortAlignmentPoints);
109
110 connect(previewB, &QPushButton::clicked, this, &Ekos::MountModel::togglePreviewAlignPoints);
111 connect(stopAlignB, &QPushButton::clicked, this, &Ekos::MountModel::resetAlignmentProcedure);
112 connect(startAlignB, &QPushButton::clicked, this, &Ekos::MountModel::startStopAlignmentProcedure);
113
114 generateAlignStarList();
115
116}
117
118MountModel::~MountModel()
119{
120
121}
122
123void MountModel::generateAlignStarList()
124{
125 alignStars.clear();
126 starListBox->clear();
127 greekStarListBox->clear();
128
129 KStarsData *data = KStarsData::Instance();
131 listStars.append(data->skyComposite()->objectLists(SkyObject::STAR));
132 for (int i = 0; i < listStars.size(); i++)
133 {
135 const StarObject *star = dynamic_cast<const StarObject *>(pair.second);
136 if (star)
137 {
138 StarObject *alignStar = star->clone();
139 alignStar->updateCoords(data->updateNum(), true, data->geo()->lat(), data->lst(), false);
140 alignStars.append(alignStar);
141 }
142 }
143
146
147 for (int i = 0; i < alignStars.size(); i++)
148 {
149 const StarObject *star = alignStars.value(i);
150 if (star)
151 {
152 if (!isVisible(star))
153 {
154 alignStars.remove(i);
155 i--;
156 }
157 else
158 {
159 if (star->hasLatinName())
160 boxNames << star->name();
161 else
162 {
163 if (!star->gname().isEmpty())
164 greekBoxNames << star->gname().simplified();
165 }
166 }
167 }
168 }
169
171 boxNames.removeDuplicates();
172 greekBoxNames.removeDuplicates();
173 std::sort(greekBoxNames.begin(), greekBoxNames.end(), [](const QString & a, const QString & b)
174 {
175 QStringList aParts = a.split(' ');
176 QStringList bParts = b.split(' ');
177 if (aParts.length() < 2 || bParts.length() < 2)
178 return a < b; //This should not happen, they should all have 2 words in the string.
179 if (aParts[1] == bParts[1])
180 {
181 return aParts[0] < bParts[0]; //This compares the greek letter when the constellation is the same
182 }
183 else
184 return aParts[1] < bParts[1]; //This compares the constellation names
185 });
186
187 starListBox->addItem("Select one:");
188 greekStarListBox->addItem("Select one:");
189 for (int i = 0; i < boxNames.size(); i++)
190 starListBox->addItem(boxNames.at(i));
191 for (int i = 0; i < greekBoxNames.size(); i++)
192 greekStarListBox->addItem(greekBoxNames.at(i));
193}
194
195bool MountModel::isVisible(const SkyObject *so)
196{
197 return (getAltitude(so) > 30);
198}
199
200double MountModel::getAltitude(const SkyObject *so)
201{
202 KStarsData *data = KStarsData::Instance();
203 SkyPoint sp = so->recomputeCoords(data->ut(), data->geo());
204
205 //check altitude of object at this time.
206 sp.EquatorialToHorizontal(data->lst(), data->geo()->lat());
207
208 return sp.alt().Degrees();
209}
210
211void MountModel::togglePreviewAlignPoints()
212{
213 previewShowing = !previewShowing;
214 previewB->setChecked(previewShowing);
215 updatePreviewAlignPoints();
216}
217
218void MountModel::updatePreviewAlignPoints()
219{
220 FlagComponent *flags = KStarsData::Instance()->skyComposite()->flags();
221 for (int i = 0; i < flags->size(); i++)
222 {
223 if (flags->label(i).startsWith(QLatin1String("Align")))
224 {
225 flags->remove(i);
226 i--;
227 }
228 }
229 if (previewShowing)
230 {
231 for (int i = 0; i < alignTable->rowCount(); i++)
232 {
233 QTableWidgetItem *raCell = alignTable->item(i, 0);
234 QTableWidgetItem *deCell = alignTable->item(i, 1);
236
237 if (raCell && deCell && objNameCell)
238 {
239 QString raString = raCell->text();
240 QString deString = deCell->text();
243
244 QString objString = objNameCell->text();
245
247 flags->add(flagPoint, "J2000", "Default", "Align " + QString::number(i + 1) + ' ' + objString, "white");
248 }
249 }
250 }
251 KStars::Instance()->map()->forceUpdate(true);
252}
253
254void MountModel::slotLoadAlignmentPoints()
255{
256 QUrl fileURL = QFileDialog::getOpenFileUrl(this, i18nc("@title:window", "Open Ekos Alignment List"),
257 alignURL,
258 "Ekos AlignmentList (*.eal)");
259 if (fileURL.isEmpty())
260 return;
261
262 if (fileURL.isValid() == false)
263 {
264 QString message = i18n("Invalid URL: %1", fileURL.toLocalFile());
265 KSNotification::sorry(message, i18n("Invalid URL"));
266 return;
267 }
268
269 alignURL = fileURL;
270
271 loadAlignmentPoints(fileURL.toLocalFile());
272 if (previewShowing)
273 updatePreviewAlignPoints();
274}
275
276bool MountModel::loadAlignmentPoints(const QString &fileURL)
277{
278 QFile sFile;
279 sFile.setFileName(fileURL);
280
281 if (!sFile.open(QIODevice::ReadOnly))
282 {
283 QString message = i18n("Unable to open file %1", fileURL);
284 KSNotification::sorry(message, i18n("Could Not Open File"));
285 return false;
286 }
287
288 alignTable->setRowCount(0);
289
291
292 char errmsg[MAXRBUF];
293 XMLEle *root = nullptr;
294 char c;
295
296 while (sFile.getChar(&c))
297 {
298 root = readXMLEle(xmlParser, c, errmsg);
299
300 if (root)
301 {
302 double sqVersion = atof(findXMLAttValu(root, "version"));
303 if (sqVersion < AL_FORMAT_VERSION)
304 {
305 emit newLog(i18n("Deprecated sequence file format version %1. Please construct a new sequence file.",
306 sqVersion));
307 return false;
308 }
309
310 XMLEle *ep = nullptr;
311 XMLEle *subEP = nullptr;
312
313 int currentRow = 0;
314
315 for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0))
316 {
317 if (!strcmp(tagXMLEle(ep), "AlignmentPoint"))
318 {
319 alignTable->insertRow(currentRow);
320
321 subEP = findXMLEle(ep, "RA");
322 if (subEP)
323 {
325 RAReport->setText(pcdataXMLEle(subEP));
326 RAReport->setTextAlignment(Qt::AlignHCenter);
327 alignTable->setItem(currentRow, 0, RAReport);
328 }
329 else
330 return false;
331 subEP = findXMLEle(ep, "DE");
332 if (subEP)
333 {
335 DEReport->setText(pcdataXMLEle(subEP));
336 DEReport->setTextAlignment(Qt::AlignHCenter);
337 alignTable->setItem(currentRow, 1, DEReport);
338 }
339 else
340 return false;
341 subEP = findXMLEle(ep, "NAME");
342 if (subEP)
343 {
345 ObjReport->setText(pcdataXMLEle(subEP));
346 ObjReport->setTextAlignment(Qt::AlignHCenter);
347 alignTable->setItem(currentRow, 2, ObjReport);
348 }
349 else
350 return false;
351 }
352 currentRow++;
353 }
354 return true;
355 }
356 }
357 return false;
358}
359
360void MountModel::slotSaveAlignmentPoints()
361{
362 QUrl backupCurrent = alignURL;
363
364 if (alignURL.toLocalFile().startsWith(QLatin1String("/tmp/")) || alignURL.toLocalFile().contains("/Temp"))
365 alignURL.clear();
366
367 alignURL = QFileDialog::getSaveFileUrl(this, i18nc("@title:window", "Save Ekos Alignment List"), alignURL,
368 "Ekos Alignment List (*.eal)");
369 // if user presses cancel
370 if (alignURL.isEmpty())
371 {
372 alignURL = backupCurrent;
373 return;
374 }
375
376 if (alignURL.toLocalFile().endsWith(QLatin1String(".eal")) == false)
377 alignURL.setPath(alignURL.toLocalFile() + ".eal");
378
379
380 if (alignURL.isValid())
381 {
382 if ((saveAlignmentPoints(alignURL.toLocalFile())) == false)
383 {
384 KSNotification::error(i18n("Failed to save alignment list"), i18n("Save"));
385 return;
386 }
387 }
388 else
389 {
390 QString message = i18n("Invalid URL: %1", alignURL.url());
391 KSNotification::sorry(message, i18n("Invalid URL"));
392 }
393}
394
395bool MountModel::saveAlignmentPoints(const QString &path)
396{
397 QFile file;
398 file.setFileName(path);
399 if (!file.open(QIODevice::WriteOnly))
400 {
401 QString message = i18n("Unable to write to file %1", path);
402 KSNotification::sorry(message, i18n("Could Not Open File"));
403 return false;
404 }
405
406 QTextStream outstream(&file);
407
408 outstream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << Qt::endl;
409 outstream << "<AlignmentList version='" << AL_FORMAT_VERSION << "'>" << Qt::endl;
410
411 for (int i = 0; i < alignTable->rowCount(); i++)
412 {
413 QTableWidgetItem *raCell = alignTable->item(i, 0);
414 QTableWidgetItem *deCell = alignTable->item(i, 1);
416
417 if (!raCell || !deCell || !objNameCell)
418 return false;
419 QString raString = raCell->text();
420 QString deString = deCell->text();
421 QString objString = objNameCell->text();
422
423 outstream << "<AlignmentPoint>" << Qt::endl;
424 outstream << "<RA>" << raString << "</RA>" << Qt::endl;
425 outstream << "<DE>" << deString << "</DE>" << Qt::endl;
426 outstream << "<NAME>" << objString << "</NAME>" << Qt::endl;
427 outstream << "</AlignmentPoint>" << Qt::endl;
428 }
429 outstream << "</AlignmentList>" << Qt::endl;
430 emit newLog(i18n("Alignment List saved to %1", path));
431 file.close();
432 return true;
433}
434
435void MountModel::slotSortAlignmentPoints()
436{
437 int firstAlignmentPt = findClosestAlignmentPointToTelescope();
438 if (firstAlignmentPt != -1)
439 {
440 swapAlignPoints(firstAlignmentPt, 0);
441 }
442
443 for (int i = 0; i < alignTable->rowCount() - 1; i++)
444 {
445 int nextAlignmentPoint = findNextAlignmentPointAfter(i);
446 if (nextAlignmentPoint != -1)
447 {
448 swapAlignPoints(nextAlignmentPoint, i + 1);
449 }
450 }
451 if (previewShowing)
452 updatePreviewAlignPoints();
453}
454
455int MountModel::findClosestAlignmentPointToTelescope()
456{
457 dms bestDiff = dms(360);
458 double index = -1;
459
460 for (int i = 0; i < alignTable->rowCount(); i++)
461 {
462 QTableWidgetItem *raCell = alignTable->item(i, 0);
463 QTableWidgetItem *deCell = alignTable->item(i, 1);
464
465 if (raCell && deCell)
466 {
467 dms raDMS = dms::fromString(raCell->text(), false);
468 dms deDMS = dms::fromString(deCell->text(), true);
469
471 dms thisDiff = telescopeCoord.angularDistanceTo(&sk);
472 if (thisDiff.Degrees() < bestDiff.Degrees())
473 {
474 index = i;
476 }
477 }
478 }
479 return index;
480}
481
482int MountModel::findNextAlignmentPointAfter(int currentSpot)
483{
486
488 {
489 dms thisRADMS = dms::fromString(currentRACell->text(), false);
491
493
494 dms bestDiff = dms(360);
495 double index = -1;
496
497 for (int i = currentSpot + 1; i < alignTable->rowCount(); i++)
498 {
499 QTableWidgetItem *raCell = alignTable->item(i, 0);
500 QTableWidgetItem *deCell = alignTable->item(i, 1);
501
502 if (raCell && deCell)
503 {
504 dms raDMS = dms::fromString(raCell->text(), false);
505 dms deDMS = dms::fromString(deCell->text(), true);
506 SkyPoint point(raDMS, deDMS);
507 dms thisDiff = thisPt.angularDistanceTo(&point);
508
509 if (thisDiff.Degrees() < bestDiff.Degrees())
510 {
511 index = i;
513 }
514 }
515 }
516 return index;
517 }
518 else
519 return -1;
520}
521
522void MountModel::slotWizardAlignmentPoints()
523{
524 int points = alignPtNum->value();
525 if (points <
526 2) //The minimum is 2 because the wizard calculations require the calculation of an angle between points.
527 return; //It should not be less than 2 because the minimum in the spin box is 2.
528
529 int minAlt = minAltBox->value();
530 KStarsData *data = KStarsData::Instance();
531 GeoLocation *geo = data->geo();
532 double lat = geo->lat()->Degrees();
533
534 if (alignTypeBox->currentIndex() == OBJECT_FIXED_DEC)
535 {
536 double decAngle = alignDec->value();
537 //Dec that never rises.
538 if (lat > 0)
539 {
540 if (decAngle < lat - 90 + minAlt) //Min altitude possible at minAlt deg above horizon
541 {
542 KSNotification::sorry(i18n("DEC is below the altitude limit"));
543 return;
544 }
545 }
546 else
547 {
548 if (decAngle > lat + 90 - minAlt) //Max altitude possible at minAlt deg above horizon
549 {
550 KSNotification::sorry(i18n("DEC is below the altitude limit"));
551 return;
552 }
553 }
554 }
555
556 //If there are less than 6 points, keep them all in the same DEC,
557 //any more, set the num per row to be the sqrt of the points to evenly distribute in RA and DEC
558 int numRAperDEC = 5;
559 if (points > 5)
560 numRAperDEC = qSqrt(points);
561
562 //These calculations rely on modulus and int division counting beginning at 0, but the #s start at 1.
563 int decPoints = (points - 1) / numRAperDEC + 1;
564 int lastSetRAPoints = (points - 1) % numRAperDEC + 1;
565
566 double decIncrement = -1;
567 double initDEC = -1;
569
570 if (alignTypeBox->currentIndex() == OBJECT_FIXED_DEC)
571 {
572 decPoints = 1;
573 initDEC = alignDec->value();
574 decIncrement = 0;
575 }
576 else if (decPoints == 1)
577 {
578 decIncrement = 0;
579 spTest.setAlt(
580 minAlt); //The goal here is to get the point exactly West at the minAlt so that we can use that DEC
581 spTest.setAz(270);
582 spTest.HorizontalToEquatorial(KStars::Instance()->data()->lst(), KStars::Instance()->data()->geo()->lat());
583 initDEC = spTest.dec().Degrees();
584 }
585 else
586 {
587 spTest.setAlt(
588 minAlt +
589 10); //We don't want to be right at the minAlt because there would be only 1 point on the dec circle above the alt.
590 spTest.setAz(180);
591 spTest.HorizontalToEquatorial(KStars::Instance()->data()->lst(), KStars::Instance()->data()->geo()->lat());
592 initDEC = spTest.dec().Degrees();
593 if (lat > 0)
594 decIncrement = (80 - initDEC) / (decPoints); //Don't quite want to reach NCP
595 else
596 decIncrement = (initDEC - 80) / (decPoints); //Don't quite want to reach SCP
597 }
598
599 for (int d = 0; d < decPoints; d++)
600 {
601 double initRA = -1;
602 double raPoints = -1;
603 double raIncrement = -1;
604 double dec;
605
606 if (lat > 0)
607 dec = initDEC + d * decIncrement;
608 else
609 dec = initDEC - d * decIncrement;
610
611 if (alignTypeBox->currentIndex() == OBJECT_FIXED_DEC)
612 {
613 raPoints = points;
614 }
615 else if (d == decPoints - 1)
616 {
618 }
619 else
620 {
622 }
623
624 //This computes both the initRA and the raIncrement.
625 calculateAngleForRALine(raIncrement, initRA, dec, lat, raPoints, minAlt);
626
627 if (raIncrement == -1 || decIncrement == -1)
628 {
629 KSNotification::sorry(i18n("Point calculation error."));
630 return;
631 }
632
633 for (int i = 0; i < raPoints; i++)
634 {
635 double ra = initRA + i * raIncrement;
636
637 const SkyObject *original = getWizardAlignObject(ra, dec);
638
640
641 if (original)
642 {
643 SkyObject *o = original->clone();
644 o->updateCoords(data->updateNum(), true, data->geo()->lat(), data->lst(), false);
645 getFormattedCoords(o->ra0().Hours(), o->dec0().Degrees(), ra_report, dec_report);
646 name = o->longname();
647 }
648 else
649 {
650 getFormattedCoords(dms(ra).Hours(), dec, ra_report, dec_report);
651 name = i18n("Sky Point");
652 }
653
654 int currentRow = alignTable->rowCount();
655 alignTable->insertRow(currentRow);
656
658 RAReport->setText(ra_report);
659 RAReport->setTextAlignment(Qt::AlignHCenter);
660 alignTable->setItem(currentRow, 0, RAReport);
661
663 DECReport->setText(dec_report);
664 DECReport->setTextAlignment(Qt::AlignHCenter);
665 alignTable->setItem(currentRow, 1, DECReport);
666
668 ObjNameReport->setText(name);
669 ObjNameReport->setTextAlignment(Qt::AlignHCenter);
670 alignTable->setItem(currentRow, 2, ObjNameReport);
671
674 alignTable->setItem(currentRow, 3, disabledBox);
675 }
676 }
677 if (previewShowing)
678 updatePreviewAlignPoints();
679}
680
681void MountModel::calculateAngleForRALine(double &raIncrement, double &initRA, double initDEC, double lat, double raPoints,
682 double minAlt)
683{
686
687 //Circumpolar dec
688 if (fabs(initDEC) > (90 - fabs(lat) + minAlt))
689 {
690 if (raPoints > 1)
691 raIncrement = 360 / (raPoints - 1);
692 else
693 raIncrement = 0;
694 initRA = 0;
695 }
696 else
697 {
699 calculateAZPointsForDEC(dms(initDEC), dms(minAlt), AZEast, AZWest);
700
701 spEast.setAlt(minAlt);
702 spEast.setAz(AZEast.Degrees());
703 spEast.HorizontalToEquatorial(KStars::Instance()->data()->lst(), KStars::Instance()->data()->geo()->lat());
704
705 spWest.setAlt(minAlt);
706 spWest.setAz(AZWest.Degrees());
707 spWest.HorizontalToEquatorial(KStars::Instance()->data()->lst(), KStars::Instance()->data()->geo()->lat());
708
709 dms angleSep = spEast.ra().deltaAngle(spWest.ra());
710
711 initRA = spWest.ra().Degrees();
712 if (raPoints > 1)
713 raIncrement = fabs(angleSep.Degrees() / (raPoints - 1));
714 else
715 raIncrement = 0;
716 }
717}
718
719void MountModel::calculateAZPointsForDEC(dms dec, dms alt, dms &AZEast, dms &AZWest)
720{
721 KStarsData *data = KStarsData::Instance();
722 GeoLocation *geo = data->geo();
723 double AZRad;
724
725 double sindec, cosdec, sinlat, coslat;
726 double sinAlt, cosAlt;
727
728 geo->lat()->SinCos(sinlat, coslat);
729 dec.SinCos(sindec, cosdec);
730 alt.SinCos(sinAlt, cosAlt);
731
732 double arg = (sindec - sinlat * sinAlt) / (coslat * cosAlt);
733 AZRad = acos(arg);
734 AZEast.setRadians(AZRad);
735 AZWest.setRadians(2.0 * dms::PI - AZRad);
736}
737
738const SkyObject *MountModel::getWizardAlignObject(double ra, double dec)
739{
740 double maxSearch = 5.0;
741 switch (alignTypeBox->currentIndex())
742 {
743 case OBJECT_ANY_OBJECT:
744 return KStarsData::Instance()->skyComposite()->objectNearest(new SkyPoint(dms(ra), dms(dec)), maxSearch);
745 case OBJECT_FIXED_DEC:
746 case OBJECT_FIXED_GRID:
747 return nullptr;
748
749 case OBJECT_ANY_STAR:
750 return KStarsData::Instance()->skyComposite()->starNearest(new SkyPoint(dms(ra), dms(dec)), maxSearch);
751 }
752
753 //If they want named stars, then try to search for and return the closest Align Star to the requested location
754
755 dms bestDiff = dms(360);
756 double index = -1;
757 for (int i = 0; i < alignStars.size(); i++)
758 {
759 const StarObject *star = alignStars.value(i);
760 if (star)
761 {
762 if (star->hasName())
763 {
764 SkyPoint thisPt(ra / 15.0, dec);
765 dms thisDiff = thisPt.angularDistanceTo(star);
766 if (thisDiff.Degrees() < bestDiff.Degrees())
767 {
768 index = i;
770 }
771 }
772 }
773 }
774 if (index == -1)
775 return KStarsData::Instance()->skyComposite()->starNearest(new SkyPoint(dms(ra), dms(dec)), maxSearch);
776 return alignStars.value(index);
777}
778
779void MountModel::alignTypeChanged(int alignType)
780{
781 if (alignType == OBJECT_FIXED_DEC)
782 alignDec->setEnabled(true);
783 else
784 alignDec->setEnabled(false);
785}
786
787void MountModel::slotStarSelected(const QString selectedStar)
788{
789 for (int i = 0; i < alignStars.size(); i++)
790 {
791 const StarObject *star = alignStars.value(i);
792 if (star)
793 {
794 if (star->name() == selectedStar || star->gname().simplified() == selectedStar)
795 {
796 int currentRow = alignTable->rowCount();
797 alignTable->insertRow(currentRow);
798
800 getFormattedCoords(star->ra0().Hours(), star->dec0().Degrees(), ra_report, dec_report);
801
803 RAReport->setText(ra_report);
804 RAReport->setTextAlignment(Qt::AlignHCenter);
805 alignTable->setItem(currentRow, 0, RAReport);
806
808 DECReport->setText(dec_report);
809 DECReport->setTextAlignment(Qt::AlignHCenter);
810 alignTable->setItem(currentRow, 1, DECReport);
811
813 ObjNameReport->setText(star->longname());
814 ObjNameReport->setTextAlignment(Qt::AlignHCenter);
815 alignTable->setItem(currentRow, 2, ObjNameReport);
816
819 alignTable->setItem(currentRow, 3, disabledBox);
820
821 starListBox->setCurrentIndex(0);
822 greekStarListBox->setCurrentIndex(0);
823 return;
824 }
825 }
826 }
827 if (previewShowing)
828 updatePreviewAlignPoints();
829}
830
831
832void MountModel::getFormattedCoords(double ra, double dec, QString &ra_str, QString &dec_str)
833{
834 dms ra_s, dec_s;
835 ra_s.setH(ra);
836 dec_s.setD(dec);
837
838 ra_str = QString("%1:%2:%3")
839 .arg(ra_s.hour(), 2, 10, QChar('0'))
840 .arg(ra_s.minute(), 2, 10, QChar('0'))
841 .arg(ra_s.second(), 2, 10, QChar('0'));
842 if (dec_s.Degrees() < 0)
843 dec_str = QString("-%1:%2:%3")
844 .arg(abs(dec_s.degree()), 2, 10, QChar('0'))
845 .arg(abs(dec_s.arcmin()), 2, 10, QChar('0'))
846 .arg(dec_s.arcsec(), 2, 10, QChar('0'));
847 else
848 dec_str = QString("%1:%2:%3")
849 .arg(dec_s.degree(), 2, 10, QChar('0'))
850 .arg(dec_s.arcmin(), 2, 10, QChar('0'))
851 .arg(dec_s.arcsec(), 2, 10, QChar('0'));
852}
853
854void MountModel::slotClearAllAlignPoints()
855{
856 if (alignTable->rowCount() == 0)
857 return;
858
859 if (KMessageBox::questionYesNo(this, i18n("Are you sure you want to clear all the alignment points?"),
860 i18n("Clear Align Points")) == KMessageBox::Yes)
861 alignTable->setRowCount(0);
862
863 if (previewShowing)
864 updatePreviewAlignPoints();
865}
866
867void MountModel::slotRemoveAlignPoint()
868{
869 alignTable->removeRow(alignTable->currentRow());
870 if (previewShowing)
871 updatePreviewAlignPoints();
872}
873
874void MountModel::moveAlignPoint(int logicalIndex, int oldVisualIndex, int newVisualIndex)
875{
876 Q_UNUSED(logicalIndex)
877
878 for (int i = 0; i < alignTable->columnCount(); i++)
879 {
881 QTableWidgetItem *newItem = alignTable->takeItem(newVisualIndex, i);
882
883 alignTable->setItem(newVisualIndex, i, oldItem);
884 alignTable->setItem(oldVisualIndex, i, newItem);
885 }
886 alignTable->verticalHeader()->blockSignals(true);
887 alignTable->verticalHeader()->moveSection(newVisualIndex, oldVisualIndex);
888 alignTable->verticalHeader()->blockSignals(false);
889
890 if (previewShowing)
891 updatePreviewAlignPoints();
892}
893
894void MountModel::swapAlignPoints(int firstPt, int secondPt)
895{
896 for (int i = 0; i < alignTable->columnCount(); i++)
897 {
900
901 alignTable->setItem(firstPt, i, secondPtItem);
902 alignTable->setItem(secondPt, i, firstPtItem);
903 }
904}
905
906void MountModel::slotAddAlignPoint()
907{
908 int currentRow = alignTable->rowCount();
909 alignTable->insertRow(currentRow);
910
913 alignTable->setItem(currentRow, 3, disabledBox);
914}
915
916void MountModel::slotFindAlignObject()
917{
918 if (FindDialog::Instance()->execWithParent(this) == QDialog::Accepted)
919 {
920 SkyObject *object = FindDialog::Instance()->targetObject();
921 if (object != nullptr)
922 {
923 KStarsData * const data = KStarsData::Instance();
924
925 SkyObject *o = object->clone();
926 o->updateCoords(data->updateNum(), true, data->geo()->lat(), data->lst(), false);
927 int currentRow = alignTable->rowCount();
928 alignTable->insertRow(currentRow);
929
931 getFormattedCoords(o->ra0().Hours(), o->dec0().Degrees(), ra_report, dec_report);
932
934 RAReport->setText(ra_report);
935 RAReport->setTextAlignment(Qt::AlignHCenter);
936 alignTable->setItem(currentRow, 0, RAReport);
937
939 DECReport->setText(dec_report);
940 DECReport->setTextAlignment(Qt::AlignHCenter);
941 alignTable->setItem(currentRow, 1, DECReport);
942
944 ObjNameReport->setText(o->longname());
945 ObjNameReport->setTextAlignment(Qt::AlignHCenter);
946 alignTable->setItem(currentRow, 2, ObjNameReport);
947
950 alignTable->setItem(currentRow, 3, disabledBox);
951 }
952 }
953 if (previewShowing)
954 updatePreviewAlignPoints();
955}
956
957void MountModel::resetAlignmentProcedure()
958{
959 alignTable->setCellWidget(currentAlignmentPoint, 3, new QWidget());
962 statusReport->setIcon(QIcon(":/icons/AlignWarning.svg"));
963 alignTable->setItem(currentAlignmentPoint, 3, statusReport);
964
965 emit newLog(i18n("The Mount Model Tool is Reset."));
966 startAlignB->setIcon(
967 QIcon::fromTheme("media-playback-start"));
968 m_IsRunning = false;
969 currentAlignmentPoint = 0;
970 emit aborted();
971}
972
973bool MountModel::alignmentPointsAreBad()
974{
975 for (int i = 0; i < alignTable->rowCount(); i++)
976 {
977 QTableWidgetItem *raCell = alignTable->item(i, 0);
978 if (!raCell)
979 return true;
980 QString raString = raCell->text();
981 if (dms().setFromString(raString, false) == false)
982 return true;
983
984 QTableWidgetItem *decCell = alignTable->item(i, 1);
985 if (!decCell)
986 return true;
987 QString decString = decCell->text();
988 if (dms().setFromString(decString, true) == false)
989 return true;
990 }
991 return false;
992}
993
994void MountModel::startStopAlignmentProcedure()
995{
996 if (!m_IsRunning)
997 {
998 if (alignTable->rowCount() > 0)
999 {
1000 if (alignmentPointsAreBad())
1001 {
1002 KSNotification::error(i18n("Please Check the Alignment Points."));
1003 return;
1004 }
1005 if (m_AlignInstance->currentGOTOMode() == Align::GOTO_NOTHING)
1006 {
1008 nullptr,
1009 i18n("In the Align Module, \"Nothing\" is Selected for the Solver Action. This means that the "
1010 "mount model tool will not sync/align your mount but will only report the pointing model "
1011 "errors. Do you wish to continue?"),
1012 i18n("Pointing Model Report Only?"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(),
1013 "nothing_selected_warning");
1014 if (r == KMessageBox::Cancel)
1015 return;
1016 }
1017 if (currentAlignmentPoint == 0)
1018 {
1019 for (int row = 0; row < alignTable->rowCount(); row++)
1020 {
1022 statusReport->setIcon(QIcon());
1023 alignTable->setItem(row, 3, statusReport);
1024 }
1025 }
1026 startAlignB->setIcon(
1027 QIcon::fromTheme("media-playback-pause"));
1028 m_IsRunning = true;
1029 emit newLog(i18n("The Mount Model Tool is Starting."));
1030 startAlignmentPoint();
1031 }
1032 }
1033 else
1034 {
1035 startAlignB->setIcon(
1036 QIcon::fromTheme("media-playback-start"));
1037 alignTable->setCellWidget(currentAlignmentPoint, 3, new QWidget());
1038 emit newLog(i18n("The Mount Model Tool is Paused."));
1039 emit aborted();
1040 m_IsRunning = false;
1041
1044 statusReport->setIcon(QIcon(":/icons/AlignWarning.svg"));
1045 alignTable->setItem(currentAlignmentPoint, 3, statusReport);
1046 }
1047}
1048
1049void MountModel::startAlignmentPoint()
1050{
1051 if (m_IsRunning && currentAlignmentPoint >= 0 && currentAlignmentPoint < alignTable->rowCount())
1052 {
1053 QTableWidgetItem *raCell = alignTable->item(currentAlignmentPoint, 0);
1054 QString raString = raCell->text();
1056 double raDeg = raDMS.Degrees();
1057
1058 QTableWidgetItem *decCell = alignTable->item(currentAlignmentPoint, 1);
1059 QString decString = decCell->text();
1061 double dec = decDMS.Degrees();
1062
1064 alignTable->setCellWidget(currentAlignmentPoint, 3, alignIndicator);
1065 alignIndicator->startAnimation();
1066
1067 const SkyObject *target = getWizardAlignObject(raDeg, dec);
1068 m_AlignInstance->setTarget(*target);
1069 m_AlignInstance->Slew();
1070 }
1071}
1072
1073void MountModel::finishAlignmentPoint(bool solverSucceeded)
1074{
1075 if (m_IsRunning && currentAlignmentPoint >= 0 && currentAlignmentPoint < alignTable->rowCount())
1076 {
1077 alignTable->setCellWidget(currentAlignmentPoint, 3, new QWidget());
1080 if (solverSucceeded)
1081 statusReport->setIcon(QIcon(":/icons/AlignSuccess.svg"));
1082 else
1083 statusReport->setIcon(QIcon(":/icons/AlignFailure.svg"));
1084 alignTable->setItem(currentAlignmentPoint, 3, statusReport);
1085
1086 currentAlignmentPoint++;
1087
1089 {
1090 startAlignmentPoint();
1091 }
1092 else
1093 {
1094 m_IsRunning = false;
1095 startAlignB->setIcon(
1096 QIcon::fromTheme("media-playback-start"));
1097 emit newLog(i18n("The Mount Model Tool is Finished."));
1098 currentAlignmentPoint = 0;
1099 }
1100 }
1101}
1102
1103void MountModel::setAlignStatus(Ekos::AlignState state)
1104{
1105 switch (state)
1106 {
1107 case ALIGN_COMPLETE:
1108 if (m_IsRunning)
1109 finishAlignmentPoint(true);
1110 break;
1111
1112 case ALIGN_FAILED:
1113 if (m_IsRunning)
1114 finishAlignmentPoint(false);
1115 break;
1116 default:
1117 break;
1118 }
1119}
1120}
Represents a flag on the sky map.
int size()
Return the numbers of flags.
void remove(int index)
Remove a flag.
QString label(int index)
Get label.
void add(const SkyPoint &flagPoint, QString epoch, QString image, QString label, QColor labelColor)
Add a flag.
Contains all relevant information for specifying a location on Earth: City Name, State/Province name,...
Definition geolocation.h:28
const CachingDms * lat() const
Definition geolocation.h:70
KStarsData is the backbone of KStars.
Definition kstarsdata.h:72
CachingDms * lst()
Definition kstarsdata.h:224
const KStarsDateTime & ut() const
Definition kstarsdata.h:157
GeoLocation * geo()
Definition kstarsdata.h:230
SkyMapComposite * skyComposite()
Definition kstarsdata.h:166
static KStars * Instance()
Definition kstars.h:123
The QProgressIndicator class lets an application display a progress indicator to show that a long tas...
Provides all necessary information about an object in the sky: its coordinates, name(s),...
Definition skyobject.h:42
virtual SkyObject * clone() const
Create copy of object.
Definition skyobject.cpp:50
virtual QString longname(void) const
Definition skyobject.h:164
The sky coordinates of a point in the sky.
Definition skypoint.h:45
const CachingDms & ra0() const
Definition skypoint.h:251
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
virtual void updateCoords(const KSNumbers *num, bool includePlanets=true, const CachingDms *lat=nullptr, const CachingDms *LST=nullptr, bool forceRecompute=false)
Determine the current coordinates (RA, Dec) from the catalog coordinates (RA0, Dec0),...
Definition skypoint.cpp:582
const dms & alt() const
Definition skypoint.h:281
const CachingDms & dec0() const
Definition skypoint.h:257
This is a subclass of SkyObject.
Definition starobject.h:33
StarObject * clone() const override
Create copy of object.
QString name(void) const override
If star is unnamed return "star" otherwise return the name.
Definition starobject.h:130
QString longname(void) const override
If star is unnamed return "star" otherwise return the longname.
Definition starobject.h:133
bool hasLatinName() const
Definition starobject.h:123
bool hasName() const
Definition starobject.h:120
QString gname(bool useGreekChars=true) const
Returns the genetive name of the star.
An angle, stored as degrees, but expressible in many ways.
Definition dms.h:38
static dms fromString(const QString &s, bool deg)
Static function to create a DMS object from a QString.
Definition dms.cpp:429
void SinCos(double &s, double &c) const
Compute Sine and Cosine of the angle simultaneously.
Definition dms.h:447
static constexpr double PI
PI is a const static member; it's public so that it can be used anywhere, as long as dms....
Definition dms.h:385
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
Ekos is an advanced Astrophotography tool for Linux.
Definition align.cpp:78
AlignState
Definition ekos.h:145
@ ALIGN_FAILED
Alignment failed.
Definition ekos.h:148
@ ALIGN_COMPLETE
Alignment successfully completed.
Definition ekos.h:147
GeoCoordinates geo(const QVariant &location)
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
KGuiItem cont()
KGuiItem cancel()
QString name(StandardShortcut id)
void clicked(bool checked)
void currentIndexChanged(int index)
void currentTextChanged(const QString &text)
bool open(FILE *fh, OpenMode mode, FileHandleFlags handleFlags)
void setFileName(const QString &name)
virtual void close() override
QUrl getOpenFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, Options options, const QStringList &supportedSchemes)
QUrl getSaveFileUrl(QWidget *parent, const QString &caption, const QUrl &dir, const QString &filter, QString *selectedFilter, Options options, const QStringList &supportedSchemes)
QIcon fromTheme(const QString &name)
QString arg(Args &&... args) const const
bool isEmpty() const const
QString number(double n, char format, int precision)
QString simplified() const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
AlignHCenter
CaseInsensitive
ItemIsSelectable
WA_LayoutUsesWidgetRect
QTextStream & dec(QTextStream &stream)
QTextStream & endl(QTextStream &stream)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:19:02 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.