11#include "kstarsdata.h"
12#include "flagcomponent.h"
13#include "ksnotification.h"
16#include "starobject.h"
17#include "skymapcomposite.h"
19#include "starobject.h"
20#include "dialogs/finddialog.h"
21#include "QProgressIndicator.h"
23#include <ekos_align_debug.h>
25#define AL_FORMAT_VERSION 1.0
34MountModel::MountModel(Align *parent) :
QDialog(parent)
38 m_AlignInstance = parent;
40 setWindowTitle(
"Mount Model Tool");
42 alignTable->setColumnWidth(0, 70);
43 alignTable->setColumnWidth(1, 75);
44 alignTable->setColumnWidth(2, 130);
45 alignTable->setColumnWidth(3, 30);
47 wizardAlignB->setIcon(
51 clearAllAlignB->setIcon(
64 alignTable->verticalHeader()->setDragDropOverwriteMode(
false);
65 alignTable->verticalHeader()->setSectionsMovable(
true);
66 alignTable->verticalHeader()->setDragEnabled(
true);
68 connect(alignTable->verticalHeader(), SIGNAL(sectionMoved(
int,
int,
int)),
this,
69 SLOT(moveAlignPoint(
int,
int,
int)));
81 previewB->setCheckable(
true);
96 &Ekos::MountModel::alignTypeChanged);
99 &Ekos::MountModel::slotStarSelected);
102 &Ekos::MountModel::slotStarSelected);
116 generateAlignStarList();
120MountModel::~MountModel()
125void MountModel::generateAlignStarList()
128 starListBox->clear();
129 greekStarListBox->clear();
134 for (
int i = 0; i < listStars.
size(); i++)
136 QPair<QString, const SkyObject *> pair = listStars.
value(i);
142 alignStars.append(alignStar);
149 for (
int i = 0; i < alignStars.size(); i++)
154 if (!isVisible(star))
156 alignStars.remove(i);
162 boxNames << star->
name();
177 QStringList aParts = a.split(
' ');
178 QStringList bParts = b.split(
' ');
179 if (aParts.length() < 2 || bParts.length() < 2)
181 if (aParts[1] == bParts[1])
183 return aParts[0] < bParts[0];
186 return aParts[1] < bParts[1];
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));
197bool MountModel::isVisible(
const SkyObject *so)
199 return (getAltitude(so) > 30);
202double MountModel::getAltitude(
const SkyObject *so)
210 return sp.
alt().Degrees();
213void MountModel::togglePreviewAlignPoints()
215 previewShowing = !previewShowing;
216 previewB->setChecked(previewShowing);
217 updatePreviewAlignPoints();
220void MountModel::updatePreviewAlignPoints()
223 for (
int i = 0; i < flags->
size(); i++)
233 for (
int i = 0; i < alignTable->rowCount(); i++)
239 if (raCell && deCell && objNameCell)
249 flags->
add(flagPoint,
"J2000",
"Default",
"Align " +
QString::number(i + 1) +
' ' + objString,
"white");
256void MountModel::slotLoadAlignmentPoints()
260 "Ekos AlignmentList (*.eal)");
264 if (fileURL.
isValid() ==
false)
267 KSNotification::sorry(message,
i18n(
"Invalid URL"));
275 updatePreviewAlignPoints();
278bool MountModel::loadAlignmentPoints(
const QString &fileURL)
285 QString message =
i18n(
"Unable to open file %1", fileURL);
286 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
290 alignTable->setRowCount(0);
292 LilXML *xmlParser = newLilXML();
294 char errmsg[MAXRBUF];
295 XMLEle *root =
nullptr;
300 root = readXMLEle(xmlParser, c, errmsg);
304 double sqVersion = atof(findXMLAttValu(root,
"version"));
305 if (sqVersion < AL_FORMAT_VERSION)
307 emit newLog(
i18n(
"Deprecated sequence file format version %1. Please construct a new sequence file.",
312 XMLEle *ep =
nullptr;
313 XMLEle *subEP =
nullptr;
317 for (ep = nextXMLEle(root, 1); ep !=
nullptr; ep = nextXMLEle(root, 0))
319 if (!strcmp(tagXMLEle(ep),
"AlignmentPoint"))
321 alignTable->insertRow(currentRow);
323 subEP = findXMLEle(ep,
"RA");
327 RAReport->
setText(pcdataXMLEle(subEP));
329 alignTable->setItem(currentRow, 0, RAReport);
333 subEP = findXMLEle(ep,
"DE");
337 DEReport->
setText(pcdataXMLEle(subEP));
339 alignTable->setItem(currentRow, 1, DEReport);
343 subEP = findXMLEle(ep,
"NAME");
347 ObjReport->
setText(pcdataXMLEle(subEP));
349 alignTable->setItem(currentRow, 2, ObjReport);
362void MountModel::slotSaveAlignmentPoints()
364 QUrl backupCurrent = alignURL;
366 if (alignURL.toLocalFile().startsWith(
QLatin1String(
"/tmp/")) || alignURL.toLocalFile().contains(
"/Temp"))
370 "Ekos Alignment List (*.eal)");
372 if (alignURL.isEmpty())
374 alignURL = backupCurrent;
378 if (alignURL.toLocalFile().endsWith(
QLatin1String(
".eal")) ==
false)
379 alignURL.setPath(alignURL.toLocalFile() +
".eal");
382 if (alignURL.isValid())
384 if ((saveAlignmentPoints(alignURL.toLocalFile())) ==
false)
386 KSNotification::error(
i18n(
"Failed to save alignment list"),
i18n(
"Save"));
392 QString message =
i18n(
"Invalid URL: %1", alignURL.url());
393 KSNotification::sorry(message,
i18n(
"Invalid URL"));
397bool MountModel::saveAlignmentPoints(
const QString &path)
403 QString message =
i18n(
"Unable to write to file %1", path);
404 KSNotification::sorry(message,
i18n(
"Could Not Open File"));
410 outstream <<
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>" <<
Qt::endl;
411 outstream <<
"<AlignmentList version='" << AL_FORMAT_VERSION <<
"'>" <<
Qt::endl;
413 for (
int i = 0; i < alignTable->rowCount(); i++)
419 if (!raCell || !deCell || !objNameCell)
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;
431 outstream <<
"</AlignmentList>" <<
Qt::endl;
432 emit newLog(
i18n(
"Alignment List saved to %1", path));
437void MountModel::slotSortAlignmentPoints()
439 int firstAlignmentPt = findClosestAlignmentPointToTelescope();
440 if (firstAlignmentPt != -1)
442 swapAlignPoints(firstAlignmentPt, 0);
445 for (
int i = 0; i < alignTable->rowCount() - 1; i++)
447 int nextAlignmentPoint = findNextAlignmentPointAfter(i);
448 if (nextAlignmentPoint != -1)
450 swapAlignPoints(nextAlignmentPoint, i + 1);
454 updatePreviewAlignPoints();
457int MountModel::findClosestAlignmentPointToTelescope()
462 for (
int i = 0; i < alignTable->rowCount(); i++)
467 if (raCell && deCell)
473 dms thisDiff = telescopeCoord.angularDistanceTo(&sk);
484int MountModel::findNextAlignmentPointAfter(
int currentSpot)
489 if (currentRACell && currentDECell)
494 SkyPoint thisPt(thisRADMS, thisDEDMS);
499 for (
int i = currentSpot + 1; i < alignTable->rowCount(); i++)
504 if (raCell && deCell)
509 dms thisDiff = thisPt.angularDistanceTo(&point);
524void MountModel::slotWizardAlignmentPoints()
526 int points = alignPtNum->value();
531 int minAlt = minAltBox->value();
534 double lat =
geo->lat()->Degrees();
536 if (alignTypeBox->currentIndex() == OBJECT_FIXED_DEC)
538 double decAngle = alignDec->value();
542 if (decAngle < lat - 90 + minAlt)
544 KSNotification::sorry(
i18n(
"DEC is below the altitude limit"));
550 if (decAngle > lat + 90 - minAlt)
552 KSNotification::sorry(
i18n(
"DEC is below the altitude limit"));
562 numRAperDEC = qSqrt(points);
565 int decPoints = (points - 1) / numRAperDEC + 1;
566 int lastSetRAPoints = (points - 1) % numRAperDEC + 1;
568 double decIncrement = -1;
572 if (alignTypeBox->currentIndex() == OBJECT_FIXED_DEC)
575 initDEC = alignDec->value();
578 else if (decPoints == 1)
585 initDEC = spTest.
dec().Degrees();
594 initDEC = spTest.
dec().Degrees();
596 decIncrement = (80 - initDEC) / (decPoints);
598 decIncrement = (initDEC - 80) / (decPoints);
601 for (
int d = 0; d < decPoints; d++)
604 double raPoints = -1;
605 double raIncrement = -1;
609 dec = initDEC + d * decIncrement;
611 dec = initDEC - d * decIncrement;
613 if (alignTypeBox->currentIndex() == OBJECT_FIXED_DEC)
617 else if (d == decPoints - 1)
619 raPoints = lastSetRAPoints;
623 raPoints = numRAperDEC;
627 calculateAngleForRALine(raIncrement, initRA, dec, lat, raPoints, minAlt);
629 if (raIncrement == -1 || decIncrement == -1)
631 KSNotification::sorry(
i18n(
"Point calculation error."));
635 for (
int i = 0; i < raPoints; i++)
637 double ra = initRA + i * raIncrement;
639 const SkyObject *original = getWizardAlignObject(ra, dec);
647 getFormattedCoords(o->
ra0().Hours(), o->
dec0().Degrees(), ra_report, dec_report);
652 getFormattedCoords(
dms(ra).Hours(), dec, ra_report, dec_report);
656 int currentRow = alignTable->rowCount();
657 alignTable->insertRow(currentRow);
662 alignTable->setItem(currentRow, 0, RAReport);
665 DECReport->
setText(dec_report);
667 alignTable->setItem(currentRow, 1, DECReport);
672 alignTable->setItem(currentRow, 2, ObjNameReport);
676 alignTable->setItem(currentRow, 3, disabledBox);
680 updatePreviewAlignPoints();
683void MountModel::calculateAngleForRALine(
double &raIncrement,
double &initRA,
double initDEC,
double lat,
double raPoints,
690 if (fabs(initDEC) > (90 - fabs(lat) + minAlt))
693 raIncrement = 360 / (raPoints - 1);
701 calculateAZPointsForDEC(
dms(initDEC),
dms(minAlt), AZEast, AZWest);
711 dms angleSep = spEast.
ra().deltaAngle(spWest.
ra());
713 initRA = spWest.
ra().Degrees();
715 raIncrement = fabs(angleSep.
Degrees() / (raPoints - 1));
721void MountModel::calculateAZPointsForDEC(
dms dec,
dms alt,
dms &AZEast,
dms &AZWest)
727 double sindec, cosdec, sinlat, coslat;
728 double sinAlt, cosAlt;
730 geo->lat()->SinCos(sinlat, coslat);
731 dec.SinCos(sindec, cosdec);
732 alt.
SinCos(sinAlt, cosAlt);
734 double arg = (sindec - sinlat * sinAlt) / (coslat * cosAlt);
740const SkyObject *MountModel::getWizardAlignObject(
double ra,
double dec)
742 double maxSearch = 5.0;
743 switch (alignTypeBox->currentIndex())
745 case OBJECT_ANY_OBJECT:
747 case OBJECT_FIXED_DEC:
748 case OBJECT_FIXED_GRID:
751 case OBJECT_ANY_STAR:
759 for (
int i = 0; i < alignStars.size(); i++)
767 dms thisDiff = thisPt.angularDistanceTo(star);
778 return alignStars.value(index);
781void MountModel::alignTypeChanged(
int alignType)
783 if (alignType == OBJECT_FIXED_DEC)
784 alignDec->setEnabled(
true);
786 alignDec->setEnabled(
false);
789void MountModel::slotStarSelected(
const QString selectedStar)
791 for (
int i = 0; i < alignStars.size(); i++)
798 int currentRow = alignTable->rowCount();
799 alignTable->insertRow(currentRow);
802 getFormattedCoords(star->
ra0().Hours(), star->
dec0().Degrees(), ra_report, dec_report);
807 alignTable->setItem(currentRow, 0, RAReport);
810 DECReport->
setText(dec_report);
812 alignTable->setItem(currentRow, 1, DECReport);
817 alignTable->setItem(currentRow, 2, ObjNameReport);
821 alignTable->setItem(currentRow, 3, disabledBox);
823 starListBox->setCurrentIndex(0);
824 greekStarListBox->setCurrentIndex(0);
830 updatePreviewAlignPoints();
834void MountModel::getFormattedCoords(
double ra,
double dec,
QString &ra_str,
QString &dec_str)
856void MountModel::slotClearAllAlignPoints()
858 if (alignTable->rowCount() == 0)
863 alignTable->setRowCount(0);
866 updatePreviewAlignPoints();
869void MountModel::slotRemoveAlignPoint()
871 alignTable->removeRow(alignTable->currentRow());
873 updatePreviewAlignPoints();
876void MountModel::moveAlignPoint(
int logicalIndex,
int oldVisualIndex,
int newVisualIndex)
878 Q_UNUSED(logicalIndex)
880 for (
int i = 0; i < alignTable->columnCount(); i++)
885 alignTable->setItem(newVisualIndex, i, oldItem);
886 alignTable->setItem(oldVisualIndex, i, newItem);
888 alignTable->verticalHeader()->blockSignals(
true);
889 alignTable->verticalHeader()->moveSection(newVisualIndex, oldVisualIndex);
890 alignTable->verticalHeader()->blockSignals(
false);
893 updatePreviewAlignPoints();
896void MountModel::swapAlignPoints(
int firstPt,
int secondPt)
898 for (
int i = 0; i < alignTable->columnCount(); i++)
903 alignTable->setItem(firstPt, i, secondPtItem);
904 alignTable->setItem(secondPt, i, firstPtItem);
908void MountModel::slotAddAlignPoint()
910 int currentRow = alignTable->rowCount();
911 alignTable->insertRow(currentRow);
915 alignTable->setItem(currentRow, 3, disabledBox);
918void MountModel::slotFindAlignObject()
923 if (
object !=
nullptr)
925 KStarsData *
const data = KStarsData::Instance();
929 int currentRow = alignTable->rowCount();
930 alignTable->insertRow(currentRow);
933 getFormattedCoords(o->
ra0().Hours(), o->
dec0().Degrees(), ra_report, dec_report);
938 alignTable->setItem(currentRow, 0, RAReport);
941 DECReport->
setText(dec_report);
943 alignTable->setItem(currentRow, 1, DECReport);
948 alignTable->setItem(currentRow, 2, ObjNameReport);
952 alignTable->setItem(currentRow, 3, disabledBox);
956 updatePreviewAlignPoints();
959void MountModel::resetAlignmentProcedure()
961 alignTable->setCellWidget(currentAlignmentPoint, 3,
new QWidget());
964 statusReport->
setIcon(
QIcon(
":/icons/AlignWarning.svg"));
965 alignTable->setItem(currentAlignmentPoint, 3, statusReport);
967 emit newLog(
i18n(
"The Mount Model Tool is Reset."));
968 startAlignB->setIcon(
971 currentAlignmentPoint = 0;
975bool MountModel::alignmentPointsAreBad()
977 for (
int i = 0; i < alignTable->rowCount(); i++)
983 if (
dms().setFromString(raString,
false) ==
false)
990 if (
dms().setFromString(decString,
true) ==
false)
996void MountModel::startStopAlignmentProcedure()
1000 if (alignTable->rowCount() > 0)
1002 if (alignmentPointsAreBad())
1004 KSNotification::error(
i18n(
"Please Check the Alignment Points."));
1007 if (m_AlignInstance->currentGOTOMode() == Align::GOTO_NOTHING)
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?"),
1015 "nothing_selected_warning");
1019 if (currentAlignmentPoint == 0)
1021 for (
int row = 0; row < alignTable->rowCount(); row++)
1025 alignTable->setItem(row, 3, statusReport);
1028 startAlignB->setIcon(
1031 emit newLog(
i18n(
"The Mount Model Tool is Starting."));
1032 startAlignmentPoint();
1037 startAlignB->setIcon(
1039 alignTable->setCellWidget(currentAlignmentPoint, 3,
new QWidget());
1040 emit newLog(
i18n(
"The Mount Model Tool is Paused."));
1042 m_IsRunning =
false;
1046 statusReport->
setIcon(
QIcon(
":/icons/AlignWarning.svg"));
1047 alignTable->setItem(currentAlignmentPoint, 3, statusReport);
1051void MountModel::startAlignmentPoint()
1053 if (m_IsRunning && currentAlignmentPoint >= 0 && currentAlignmentPoint < alignTable->rowCount())
1058 double raDeg = raDMS.
Degrees();
1066 alignTable->setCellWidget(currentAlignmentPoint, 3, alignIndicator);
1069 const SkyObject *target = getWizardAlignObject(raDeg, dec);
1070 m_AlignInstance->setTarget(*target);
1071 m_AlignInstance->Slew();
1075void MountModel::finishAlignmentPoint(
bool solverSucceeded)
1077 if (m_IsRunning && currentAlignmentPoint >= 0 && currentAlignmentPoint < alignTable->rowCount())
1079 alignTable->setCellWidget(currentAlignmentPoint, 3,
new QWidget());
1082 if (solverSucceeded)
1083 statusReport->
setIcon(
QIcon(
":/icons/AlignSuccess.svg"));
1085 statusReport->
setIcon(
QIcon(
":/icons/AlignFailure.svg"));
1086 alignTable->setItem(currentAlignmentPoint, 3, statusReport);
1088 currentAlignmentPoint++;
1090 if (currentAlignmentPoint < alignTable->rowCount())
1092 startAlignmentPoint();
1096 m_IsRunning =
false;
1097 startAlignB->setIcon(
1099 emit newLog(
i18n(
"The Mount Model Tool is Finished."));
1100 currentAlignmentPoint = 0;
1111 finishAlignmentPoint(
true);
1116 finishAlignmentPoint(
false);
SkyObject * targetObject()
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,...
const CachingDms * lat() const
KStarsData is the backbone of KStars.
const KStarsDateTime & ut() const
SkyMapComposite * skyComposite()
static KStars * Instance()
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.
Provides all necessary information about an object in the sky: its coordinates, name(s),...
virtual SkyObject * clone() const
Create copy of object.
virtual QString longname(void) const
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.
const CachingDms & dec() const
const CachingDms & ra0() const
const CachingDms & ra() const
void EquatorialToHorizontal(const CachingDms *LST, const CachingDms *lat)
Determine the (Altitude, Azimuth) coordinates of the SkyPoint from its (RA, Dec) coordinates,...
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),...
void setAlt(dms alt)
Sets Alt, the Altitude.
void HorizontalToEquatorial(const dms *LST, const dms *lat)
Determine the (RA, Dec) coordinates of the SkyPoint from its (Altitude, Azimuth) coordinates,...
void setAz(dms az)
Sets Az, the Azimuth.
const CachingDms & dec0() const
This is a subclass of SkyObject.
StarObject * clone() const override
Create copy of object.
QString name(void) const override
If star is unnamed return "star" otherwise return the name.
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.
bool hasLatinName() const
QString gname(bool useGreekChars=true) const
Returns the genetive name of the star.
An angle, stored as degrees, but expressible in many ways.
static dms fromString(const QString &s, bool deg)
Static function to create a DMS object from a QString.
virtual void setH(const double &x)
Sets floating-point value of angle, in hours.
void SinCos(double &s, double &c) const
Compute Sine and Cosine of the angle simultaneously.
static constexpr double PI
PI is a const static member; it's public so that it can be used anywhere, as long as dms....
virtual void setRadians(const double &Rad)
Set angle according to the argument, in radians.
virtual void setD(const double &x)
Sets floating-point value of angle, in degrees.
const double & Degrees() const
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.
@ ALIGN_FAILED
Alignment failed.
@ ALIGN_COMPLETE
Alignment successfully completed.
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)
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)
void append(QList< T > &&value)
const_reference at(qsizetype i) const const
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)
QTextStream & dec(QTextStream &stream)
QTextStream & endl(QTextStream &stream)
void setText(const QString &text)
void setTextAlignment(Qt::Alignment alignment)
QString text() const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool isEmpty() const const
bool isValid() const const
QString toLocalFile() const const