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

KDE's Doxygen guidelines are available online.