Warning, file /education/kstars/kstars/ekos/align/mountmodel.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* Ekos Mount MOdel 0002 SPDX-FileCopyrightText: 2018 Robert Lancaster 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "mountmodel.h" 0008 0009 #include "align.h" 0010 #include "kstars.h" 0011 #include "kstarsdata.h" 0012 #include "flagcomponent.h" 0013 #include "ksnotification.h" 0014 0015 #include "skymap.h" 0016 #include "starobject.h" 0017 #include "skymapcomposite.h" 0018 #include "skyobject.h" 0019 #include "starobject.h" 0020 #include "dialogs/finddialog.h" 0021 #include "QProgressIndicator.h" 0022 0023 #include <ekos_align_debug.h> 0024 0025 #define AL_FORMAT_VERSION 1.0 0026 0027 // Qt version calming 0028 #include <qtendl.h> 0029 0030 namespace Ekos 0031 { 0032 MountModel::MountModel(Align *parent) : QDialog(parent) 0033 { 0034 setupUi(this); 0035 0036 m_AlignInstance = parent; 0037 0038 setWindowTitle("Mount Model Tool"); 0039 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); 0040 alignTable->setColumnWidth(0, 70); 0041 alignTable->setColumnWidth(1, 75); 0042 alignTable->setColumnWidth(2, 130); 0043 alignTable->setColumnWidth(3, 30); 0044 0045 wizardAlignB->setIcon( 0046 QIcon::fromTheme("tools-wizard")); 0047 wizardAlignB->setAttribute(Qt::WA_LayoutUsesWidgetRect); 0048 0049 clearAllAlignB->setIcon( 0050 QIcon::fromTheme("application-exit")); 0051 clearAllAlignB->setAttribute(Qt::WA_LayoutUsesWidgetRect); 0052 0053 removeAlignB->setIcon(QIcon::fromTheme("list-remove")); 0054 removeAlignB->setAttribute(Qt::WA_LayoutUsesWidgetRect); 0055 0056 addAlignB->setIcon(QIcon::fromTheme("list-add")); 0057 addAlignB->setAttribute(Qt::WA_LayoutUsesWidgetRect); 0058 0059 findAlignB->setIcon(QIcon::fromTheme("edit-find")); 0060 findAlignB->setAttribute(Qt::WA_LayoutUsesWidgetRect); 0061 0062 alignTable->verticalHeader()->setDragDropOverwriteMode(false); 0063 alignTable->verticalHeader()->setSectionsMovable(true); 0064 alignTable->verticalHeader()->setDragEnabled(true); 0065 alignTable->verticalHeader()->setDragDropMode(QAbstractItemView::InternalMove); 0066 connect(alignTable->verticalHeader(), SIGNAL(sectionMoved(int, int, int)), this, 0067 SLOT(moveAlignPoint(int, int, int))); 0068 0069 loadAlignB->setIcon( 0070 QIcon::fromTheme("document-open")); 0071 loadAlignB->setAttribute(Qt::WA_LayoutUsesWidgetRect); 0072 0073 saveAlignB->setIcon( 0074 QIcon::fromTheme("document-save")); 0075 saveAlignB->setAttribute(Qt::WA_LayoutUsesWidgetRect); 0076 0077 previewB->setIcon(QIcon::fromTheme("kstars_grid")); 0078 previewB->setAttribute(Qt::WA_LayoutUsesWidgetRect); 0079 previewB->setCheckable(true); 0080 0081 sortAlignB->setIcon(QIcon::fromTheme("svn-update")); 0082 sortAlignB->setAttribute(Qt::WA_LayoutUsesWidgetRect); 0083 0084 stopAlignB->setIcon( 0085 QIcon::fromTheme("media-playback-stop")); 0086 stopAlignB->setAttribute(Qt::WA_LayoutUsesWidgetRect); 0087 0088 startAlignB->setIcon( 0089 QIcon::fromTheme("media-playback-start")); 0090 startAlignB->setAttribute(Qt::WA_LayoutUsesWidgetRect); 0091 0092 connect(wizardAlignB, &QPushButton::clicked, this, &Ekos::MountModel::slotWizardAlignmentPoints); 0093 connect(alignTypeBox, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, 0094 &Ekos::MountModel::alignTypeChanged); 0095 0096 connect(starListBox, static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentTextChanged), this, 0097 &Ekos::MountModel::slotStarSelected); 0098 connect(greekStarListBox, static_cast<void (QComboBox::*)(const QString &)>(&QComboBox::currentTextChanged), 0099 this, 0100 &Ekos::MountModel::slotStarSelected); 0101 0102 connect(loadAlignB, &QPushButton::clicked, this, &Ekos::MountModel::slotLoadAlignmentPoints); 0103 connect(saveAlignB, &QPushButton::clicked, this, &Ekos::MountModel::slotSaveAlignmentPoints); 0104 connect(clearAllAlignB, &QPushButton::clicked, this, &Ekos::MountModel::slotClearAllAlignPoints); 0105 connect(removeAlignB, &QPushButton::clicked, this, &Ekos::MountModel::slotRemoveAlignPoint); 0106 connect(addAlignB, &QPushButton::clicked, this, &Ekos::MountModel::slotAddAlignPoint); 0107 connect(findAlignB, &QPushButton::clicked, this, &Ekos::MountModel::slotFindAlignObject); 0108 connect(sortAlignB, &QPushButton::clicked, this, &Ekos::MountModel::slotSortAlignmentPoints); 0109 0110 connect(previewB, &QPushButton::clicked, this, &Ekos::MountModel::togglePreviewAlignPoints); 0111 connect(stopAlignB, &QPushButton::clicked, this, &Ekos::MountModel::resetAlignmentProcedure); 0112 connect(startAlignB, &QPushButton::clicked, this, &Ekos::MountModel::startStopAlignmentProcedure); 0113 0114 generateAlignStarList(); 0115 0116 } 0117 0118 MountModel::~MountModel() 0119 { 0120 0121 } 0122 0123 void MountModel::generateAlignStarList() 0124 { 0125 alignStars.clear(); 0126 starListBox->clear(); 0127 greekStarListBox->clear(); 0128 0129 KStarsData *data = KStarsData::Instance(); 0130 QVector<QPair<QString, const SkyObject *>> listStars; 0131 listStars.append(data->skyComposite()->objectLists(SkyObject::STAR)); 0132 for (int i = 0; i < listStars.size(); i++) 0133 { 0134 QPair<QString, const SkyObject *> pair = listStars.value(i); 0135 const StarObject *star = dynamic_cast<const StarObject *>(pair.second); 0136 if (star) 0137 { 0138 StarObject *alignStar = star->clone(); 0139 alignStar->updateCoords(data->updateNum(), true, data->geo()->lat(), data->lst(), false); 0140 alignStars.append(alignStar); 0141 } 0142 } 0143 0144 QStringList boxNames; 0145 QStringList greekBoxNames; 0146 0147 for (int i = 0; i < alignStars.size(); i++) 0148 { 0149 const StarObject *star = alignStars.value(i); 0150 if (star) 0151 { 0152 if (!isVisible(star)) 0153 { 0154 alignStars.remove(i); 0155 i--; 0156 } 0157 else 0158 { 0159 if (star->hasLatinName()) 0160 boxNames << star->name(); 0161 else 0162 { 0163 if (!star->gname().isEmpty()) 0164 greekBoxNames << star->gname().simplified(); 0165 } 0166 } 0167 } 0168 } 0169 0170 boxNames.sort(Qt::CaseInsensitive); 0171 boxNames.removeDuplicates(); 0172 greekBoxNames.removeDuplicates(); 0173 std::sort(greekBoxNames.begin(), greekBoxNames.end(), [](const QString & a, const QString & b) 0174 { 0175 QStringList aParts = a.split(' '); 0176 QStringList bParts = b.split(' '); 0177 if (aParts.length() < 2 || bParts.length() < 2) 0178 return a < b; //This should not happen, they should all have 2 words in the string. 0179 if (aParts[1] == bParts[1]) 0180 { 0181 return aParts[0] < bParts[0]; //This compares the greek letter when the constellation is the same 0182 } 0183 else 0184 return aParts[1] < bParts[1]; //This compares the constellation names 0185 }); 0186 0187 starListBox->addItem("Select one:"); 0188 greekStarListBox->addItem("Select one:"); 0189 for (int i = 0; i < boxNames.size(); i++) 0190 starListBox->addItem(boxNames.at(i)); 0191 for (int i = 0; i < greekBoxNames.size(); i++) 0192 greekStarListBox->addItem(greekBoxNames.at(i)); 0193 } 0194 0195 bool MountModel::isVisible(const SkyObject *so) 0196 { 0197 return (getAltitude(so) > 30); 0198 } 0199 0200 double MountModel::getAltitude(const SkyObject *so) 0201 { 0202 KStarsData *data = KStarsData::Instance(); 0203 SkyPoint sp = so->recomputeCoords(data->ut(), data->geo()); 0204 0205 //check altitude of object at this time. 0206 sp.EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0207 0208 return sp.alt().Degrees(); 0209 } 0210 0211 void MountModel::togglePreviewAlignPoints() 0212 { 0213 previewShowing = !previewShowing; 0214 previewB->setChecked(previewShowing); 0215 updatePreviewAlignPoints(); 0216 } 0217 0218 void MountModel::updatePreviewAlignPoints() 0219 { 0220 FlagComponent *flags = KStarsData::Instance()->skyComposite()->flags(); 0221 for (int i = 0; i < flags->size(); i++) 0222 { 0223 if (flags->label(i).startsWith(QLatin1String("Align"))) 0224 { 0225 flags->remove(i); 0226 i--; 0227 } 0228 } 0229 if (previewShowing) 0230 { 0231 for (int i = 0; i < alignTable->rowCount(); i++) 0232 { 0233 QTableWidgetItem *raCell = alignTable->item(i, 0); 0234 QTableWidgetItem *deCell = alignTable->item(i, 1); 0235 QTableWidgetItem *objNameCell = alignTable->item(i, 2); 0236 0237 if (raCell && deCell && objNameCell) 0238 { 0239 QString raString = raCell->text(); 0240 QString deString = deCell->text(); 0241 dms raDMS = dms::fromString(raString, false); 0242 dms decDMS = dms::fromString(deString, true); 0243 0244 QString objString = objNameCell->text(); 0245 0246 SkyPoint flagPoint(raDMS, decDMS); 0247 flags->add(flagPoint, "J2000", "Default", "Align " + QString::number(i + 1) + ' ' + objString, "white"); 0248 } 0249 } 0250 } 0251 KStars::Instance()->map()->forceUpdate(true); 0252 } 0253 0254 void MountModel::slotLoadAlignmentPoints() 0255 { 0256 QUrl fileURL = QFileDialog::getOpenFileUrl(this, i18nc("@title:window", "Open Ekos Alignment List"), 0257 alignURL, 0258 "Ekos AlignmentList (*.eal)"); 0259 if (fileURL.isEmpty()) 0260 return; 0261 0262 if (fileURL.isValid() == false) 0263 { 0264 QString message = i18n("Invalid URL: %1", fileURL.toLocalFile()); 0265 KSNotification::sorry(message, i18n("Invalid URL")); 0266 return; 0267 } 0268 0269 alignURL = fileURL; 0270 0271 loadAlignmentPoints(fileURL.toLocalFile()); 0272 if (previewShowing) 0273 updatePreviewAlignPoints(); 0274 } 0275 0276 bool MountModel::loadAlignmentPoints(const QString &fileURL) 0277 { 0278 QFile sFile; 0279 sFile.setFileName(fileURL); 0280 0281 if (!sFile.open(QIODevice::ReadOnly)) 0282 { 0283 QString message = i18n("Unable to open file %1", fileURL); 0284 KSNotification::sorry(message, i18n("Could Not Open File")); 0285 return false; 0286 } 0287 0288 alignTable->setRowCount(0); 0289 0290 LilXML *xmlParser = newLilXML(); 0291 0292 char errmsg[MAXRBUF]; 0293 XMLEle *root = nullptr; 0294 char c; 0295 0296 while (sFile.getChar(&c)) 0297 { 0298 root = readXMLEle(xmlParser, c, errmsg); 0299 0300 if (root) 0301 { 0302 double sqVersion = atof(findXMLAttValu(root, "version")); 0303 if (sqVersion < AL_FORMAT_VERSION) 0304 { 0305 emit newLog(i18n("Deprecated sequence file format version %1. Please construct a new sequence file.", 0306 sqVersion)); 0307 return false; 0308 } 0309 0310 XMLEle *ep = nullptr; 0311 XMLEle *subEP = nullptr; 0312 0313 int currentRow = 0; 0314 0315 for (ep = nextXMLEle(root, 1); ep != nullptr; ep = nextXMLEle(root, 0)) 0316 { 0317 if (!strcmp(tagXMLEle(ep), "AlignmentPoint")) 0318 { 0319 alignTable->insertRow(currentRow); 0320 0321 subEP = findXMLEle(ep, "RA"); 0322 if (subEP) 0323 { 0324 QTableWidgetItem *RAReport = new QTableWidgetItem(); 0325 RAReport->setText(pcdataXMLEle(subEP)); 0326 RAReport->setTextAlignment(Qt::AlignHCenter); 0327 alignTable->setItem(currentRow, 0, RAReport); 0328 } 0329 else 0330 return false; 0331 subEP = findXMLEle(ep, "DE"); 0332 if (subEP) 0333 { 0334 QTableWidgetItem *DEReport = new QTableWidgetItem(); 0335 DEReport->setText(pcdataXMLEle(subEP)); 0336 DEReport->setTextAlignment(Qt::AlignHCenter); 0337 alignTable->setItem(currentRow, 1, DEReport); 0338 } 0339 else 0340 return false; 0341 subEP = findXMLEle(ep, "NAME"); 0342 if (subEP) 0343 { 0344 QTableWidgetItem *ObjReport = new QTableWidgetItem(); 0345 ObjReport->setText(pcdataXMLEle(subEP)); 0346 ObjReport->setTextAlignment(Qt::AlignHCenter); 0347 alignTable->setItem(currentRow, 2, ObjReport); 0348 } 0349 else 0350 return false; 0351 } 0352 currentRow++; 0353 } 0354 return true; 0355 } 0356 } 0357 return false; 0358 } 0359 0360 void MountModel::slotSaveAlignmentPoints() 0361 { 0362 QUrl backupCurrent = alignURL; 0363 0364 if (alignURL.toLocalFile().startsWith(QLatin1String("/tmp/")) || alignURL.toLocalFile().contains("/Temp")) 0365 alignURL.clear(); 0366 0367 alignURL = QFileDialog::getSaveFileUrl(this, i18nc("@title:window", "Save Ekos Alignment List"), alignURL, 0368 "Ekos Alignment List (*.eal)"); 0369 // if user presses cancel 0370 if (alignURL.isEmpty()) 0371 { 0372 alignURL = backupCurrent; 0373 return; 0374 } 0375 0376 if (alignURL.toLocalFile().endsWith(QLatin1String(".eal")) == false) 0377 alignURL.setPath(alignURL.toLocalFile() + ".eal"); 0378 0379 0380 if (alignURL.isValid()) 0381 { 0382 if ((saveAlignmentPoints(alignURL.toLocalFile())) == false) 0383 { 0384 KSNotification::error(i18n("Failed to save alignment list"), i18n("Save")); 0385 return; 0386 } 0387 } 0388 else 0389 { 0390 QString message = i18n("Invalid URL: %1", alignURL.url()); 0391 KSNotification::sorry(message, i18n("Invalid URL")); 0392 } 0393 } 0394 0395 bool MountModel::saveAlignmentPoints(const QString &path) 0396 { 0397 QFile file; 0398 file.setFileName(path); 0399 if (!file.open(QIODevice::WriteOnly)) 0400 { 0401 QString message = i18n("Unable to write to file %1", path); 0402 KSNotification::sorry(message, i18n("Could Not Open File")); 0403 return false; 0404 } 0405 0406 QTextStream outstream(&file); 0407 0408 outstream << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" << Qt::endl; 0409 outstream << "<AlignmentList version='" << AL_FORMAT_VERSION << "'>" << Qt::endl; 0410 0411 for (int i = 0; i < alignTable->rowCount(); i++) 0412 { 0413 QTableWidgetItem *raCell = alignTable->item(i, 0); 0414 QTableWidgetItem *deCell = alignTable->item(i, 1); 0415 QTableWidgetItem *objNameCell = alignTable->item(i, 2); 0416 0417 if (!raCell || !deCell || !objNameCell) 0418 return false; 0419 QString raString = raCell->text(); 0420 QString deString = deCell->text(); 0421 QString objString = objNameCell->text(); 0422 0423 outstream << "<AlignmentPoint>" << Qt::endl; 0424 outstream << "<RA>" << raString << "</RA>" << Qt::endl; 0425 outstream << "<DE>" << deString << "</DE>" << Qt::endl; 0426 outstream << "<NAME>" << objString << "</NAME>" << Qt::endl; 0427 outstream << "</AlignmentPoint>" << Qt::endl; 0428 } 0429 outstream << "</AlignmentList>" << Qt::endl; 0430 emit newLog(i18n("Alignment List saved to %1", path)); 0431 file.close(); 0432 return true; 0433 } 0434 0435 void MountModel::slotSortAlignmentPoints() 0436 { 0437 int firstAlignmentPt = findClosestAlignmentPointToTelescope(); 0438 if (firstAlignmentPt != -1) 0439 { 0440 swapAlignPoints(firstAlignmentPt, 0); 0441 } 0442 0443 for (int i = 0; i < alignTable->rowCount() - 1; i++) 0444 { 0445 int nextAlignmentPoint = findNextAlignmentPointAfter(i); 0446 if (nextAlignmentPoint != -1) 0447 { 0448 swapAlignPoints(nextAlignmentPoint, i + 1); 0449 } 0450 } 0451 if (previewShowing) 0452 updatePreviewAlignPoints(); 0453 } 0454 0455 int MountModel::findClosestAlignmentPointToTelescope() 0456 { 0457 dms bestDiff = dms(360); 0458 double index = -1; 0459 0460 for (int i = 0; i < alignTable->rowCount(); i++) 0461 { 0462 QTableWidgetItem *raCell = alignTable->item(i, 0); 0463 QTableWidgetItem *deCell = alignTable->item(i, 1); 0464 0465 if (raCell && deCell) 0466 { 0467 dms raDMS = dms::fromString(raCell->text(), false); 0468 dms deDMS = dms::fromString(deCell->text(), true); 0469 0470 SkyPoint sk(raDMS, deDMS); 0471 dms thisDiff = telescopeCoord.angularDistanceTo(&sk); 0472 if (thisDiff.Degrees() < bestDiff.Degrees()) 0473 { 0474 index = i; 0475 bestDiff = thisDiff; 0476 } 0477 } 0478 } 0479 return index; 0480 } 0481 0482 int MountModel::findNextAlignmentPointAfter(int currentSpot) 0483 { 0484 QTableWidgetItem *currentRACell = alignTable->item(currentSpot, 0); 0485 QTableWidgetItem *currentDECell = alignTable->item(currentSpot, 1); 0486 0487 if (currentRACell && currentDECell) 0488 { 0489 dms thisRADMS = dms::fromString(currentRACell->text(), false); 0490 dms thisDEDMS = dms::fromString(currentDECell->text(), true); 0491 0492 SkyPoint thisPt(thisRADMS, thisDEDMS); 0493 0494 dms bestDiff = dms(360); 0495 double index = -1; 0496 0497 for (int i = currentSpot + 1; i < alignTable->rowCount(); i++) 0498 { 0499 QTableWidgetItem *raCell = alignTable->item(i, 0); 0500 QTableWidgetItem *deCell = alignTable->item(i, 1); 0501 0502 if (raCell && deCell) 0503 { 0504 dms raDMS = dms::fromString(raCell->text(), false); 0505 dms deDMS = dms::fromString(deCell->text(), true); 0506 SkyPoint point(raDMS, deDMS); 0507 dms thisDiff = thisPt.angularDistanceTo(&point); 0508 0509 if (thisDiff.Degrees() < bestDiff.Degrees()) 0510 { 0511 index = i; 0512 bestDiff = thisDiff; 0513 } 0514 } 0515 } 0516 return index; 0517 } 0518 else 0519 return -1; 0520 } 0521 0522 void MountModel::slotWizardAlignmentPoints() 0523 { 0524 int points = alignPtNum->value(); 0525 if (points < 0526 2) //The minimum is 2 because the wizard calculations require the calculation of an angle between points. 0527 return; //It should not be less than 2 because the minimum in the spin box is 2. 0528 0529 int minAlt = minAltBox->value(); 0530 KStarsData *data = KStarsData::Instance(); 0531 GeoLocation *geo = data->geo(); 0532 double lat = geo->lat()->Degrees(); 0533 0534 if (alignTypeBox->currentIndex() == OBJECT_FIXED_DEC) 0535 { 0536 double decAngle = alignDec->value(); 0537 //Dec that never rises. 0538 if (lat > 0) 0539 { 0540 if (decAngle < lat - 90 + minAlt) //Min altitude possible at minAlt deg above horizon 0541 { 0542 KSNotification::sorry(i18n("DEC is below the altitude limit")); 0543 return; 0544 } 0545 } 0546 else 0547 { 0548 if (decAngle > lat + 90 - minAlt) //Max altitude possible at minAlt deg above horizon 0549 { 0550 KSNotification::sorry(i18n("DEC is below the altitude limit")); 0551 return; 0552 } 0553 } 0554 } 0555 0556 //If there are less than 6 points, keep them all in the same DEC, 0557 //any more, set the num per row to be the sqrt of the points to evenly distribute in RA and DEC 0558 int numRAperDEC = 5; 0559 if (points > 5) 0560 numRAperDEC = qSqrt(points); 0561 0562 //These calculations rely on modulus and int division counting beginning at 0, but the #s start at 1. 0563 int decPoints = (points - 1) / numRAperDEC + 1; 0564 int lastSetRAPoints = (points - 1) % numRAperDEC + 1; 0565 0566 double decIncrement = -1; 0567 double initDEC = -1; 0568 SkyPoint spTest; 0569 0570 if (alignTypeBox->currentIndex() == OBJECT_FIXED_DEC) 0571 { 0572 decPoints = 1; 0573 initDEC = alignDec->value(); 0574 decIncrement = 0; 0575 } 0576 else if (decPoints == 1) 0577 { 0578 decIncrement = 0; 0579 spTest.setAlt( 0580 minAlt); //The goal here is to get the point exactly West at the minAlt so that we can use that DEC 0581 spTest.setAz(270); 0582 spTest.HorizontalToEquatorial(KStars::Instance()->data()->lst(), KStars::Instance()->data()->geo()->lat()); 0583 initDEC = spTest.dec().Degrees(); 0584 } 0585 else 0586 { 0587 spTest.setAlt( 0588 minAlt + 0589 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. 0590 spTest.setAz(180); 0591 spTest.HorizontalToEquatorial(KStars::Instance()->data()->lst(), KStars::Instance()->data()->geo()->lat()); 0592 initDEC = spTest.dec().Degrees(); 0593 if (lat > 0) 0594 decIncrement = (80 - initDEC) / (decPoints); //Don't quite want to reach NCP 0595 else 0596 decIncrement = (initDEC - 80) / (decPoints); //Don't quite want to reach SCP 0597 } 0598 0599 for (int d = 0; d < decPoints; d++) 0600 { 0601 double initRA = -1; 0602 double raPoints = -1; 0603 double raIncrement = -1; 0604 double dec; 0605 0606 if (lat > 0) 0607 dec = initDEC + d * decIncrement; 0608 else 0609 dec = initDEC - d * decIncrement; 0610 0611 if (alignTypeBox->currentIndex() == OBJECT_FIXED_DEC) 0612 { 0613 raPoints = points; 0614 } 0615 else if (d == decPoints - 1) 0616 { 0617 raPoints = lastSetRAPoints; 0618 } 0619 else 0620 { 0621 raPoints = numRAperDEC; 0622 } 0623 0624 //This computes both the initRA and the raIncrement. 0625 calculateAngleForRALine(raIncrement, initRA, dec, lat, raPoints, minAlt); 0626 0627 if (raIncrement == -1 || decIncrement == -1) 0628 { 0629 KSNotification::sorry(i18n("Point calculation error.")); 0630 return; 0631 } 0632 0633 for (int i = 0; i < raPoints; i++) 0634 { 0635 double ra = initRA + i * raIncrement; 0636 0637 const SkyObject *original = getWizardAlignObject(ra, dec); 0638 0639 QString ra_report, dec_report, name; 0640 0641 if (original) 0642 { 0643 SkyObject *o = original->clone(); 0644 o->updateCoords(data->updateNum(), true, data->geo()->lat(), data->lst(), false); 0645 getFormattedCoords(o->ra0().Hours(), o->dec0().Degrees(), ra_report, dec_report); 0646 name = o->longname(); 0647 } 0648 else 0649 { 0650 getFormattedCoords(dms(ra).Hours(), dec, ra_report, dec_report); 0651 name = i18n("Sky Point"); 0652 } 0653 0654 int currentRow = alignTable->rowCount(); 0655 alignTable->insertRow(currentRow); 0656 0657 QTableWidgetItem *RAReport = new QTableWidgetItem(); 0658 RAReport->setText(ra_report); 0659 RAReport->setTextAlignment(Qt::AlignHCenter); 0660 alignTable->setItem(currentRow, 0, RAReport); 0661 0662 QTableWidgetItem *DECReport = new QTableWidgetItem(); 0663 DECReport->setText(dec_report); 0664 DECReport->setTextAlignment(Qt::AlignHCenter); 0665 alignTable->setItem(currentRow, 1, DECReport); 0666 0667 QTableWidgetItem *ObjNameReport = new QTableWidgetItem(); 0668 ObjNameReport->setText(name); 0669 ObjNameReport->setTextAlignment(Qt::AlignHCenter); 0670 alignTable->setItem(currentRow, 2, ObjNameReport); 0671 0672 QTableWidgetItem *disabledBox = new QTableWidgetItem(); 0673 disabledBox->setFlags(Qt::ItemIsSelectable); 0674 alignTable->setItem(currentRow, 3, disabledBox); 0675 } 0676 } 0677 if (previewShowing) 0678 updatePreviewAlignPoints(); 0679 } 0680 0681 void MountModel::calculateAngleForRALine(double &raIncrement, double &initRA, double initDEC, double lat, double raPoints, 0682 double minAlt) 0683 { 0684 SkyPoint spEast; 0685 SkyPoint spWest; 0686 0687 //Circumpolar dec 0688 if (fabs(initDEC) > (90 - fabs(lat) + minAlt)) 0689 { 0690 if (raPoints > 1) 0691 raIncrement = 360 / (raPoints - 1); 0692 else 0693 raIncrement = 0; 0694 initRA = 0; 0695 } 0696 else 0697 { 0698 dms AZEast, AZWest; 0699 calculateAZPointsForDEC(dms(initDEC), dms(minAlt), AZEast, AZWest); 0700 0701 spEast.setAlt(minAlt); 0702 spEast.setAz(AZEast.Degrees()); 0703 spEast.HorizontalToEquatorial(KStars::Instance()->data()->lst(), KStars::Instance()->data()->geo()->lat()); 0704 0705 spWest.setAlt(minAlt); 0706 spWest.setAz(AZWest.Degrees()); 0707 spWest.HorizontalToEquatorial(KStars::Instance()->data()->lst(), KStars::Instance()->data()->geo()->lat()); 0708 0709 dms angleSep = spEast.ra().deltaAngle(spWest.ra()); 0710 0711 initRA = spWest.ra().Degrees(); 0712 if (raPoints > 1) 0713 raIncrement = fabs(angleSep.Degrees() / (raPoints - 1)); 0714 else 0715 raIncrement = 0; 0716 } 0717 } 0718 0719 void MountModel::calculateAZPointsForDEC(dms dec, dms alt, dms &AZEast, dms &AZWest) 0720 { 0721 KStarsData *data = KStarsData::Instance(); 0722 GeoLocation *geo = data->geo(); 0723 double AZRad; 0724 0725 double sindec, cosdec, sinlat, coslat; 0726 double sinAlt, cosAlt; 0727 0728 geo->lat()->SinCos(sinlat, coslat); 0729 dec.SinCos(sindec, cosdec); 0730 alt.SinCos(sinAlt, cosAlt); 0731 0732 double arg = (sindec - sinlat * sinAlt) / (coslat * cosAlt); 0733 AZRad = acos(arg); 0734 AZEast.setRadians(AZRad); 0735 AZWest.setRadians(2.0 * dms::PI - AZRad); 0736 } 0737 0738 const SkyObject *MountModel::getWizardAlignObject(double ra, double dec) 0739 { 0740 double maxSearch = 5.0; 0741 switch (alignTypeBox->currentIndex()) 0742 { 0743 case OBJECT_ANY_OBJECT: 0744 return KStarsData::Instance()->skyComposite()->objectNearest(new SkyPoint(dms(ra), dms(dec)), maxSearch); 0745 case OBJECT_FIXED_DEC: 0746 case OBJECT_FIXED_GRID: 0747 return nullptr; 0748 0749 case OBJECT_ANY_STAR: 0750 return KStarsData::Instance()->skyComposite()->starNearest(new SkyPoint(dms(ra), dms(dec)), maxSearch); 0751 } 0752 0753 //If they want named stars, then try to search for and return the closest Align Star to the requested location 0754 0755 dms bestDiff = dms(360); 0756 double index = -1; 0757 for (int i = 0; i < alignStars.size(); i++) 0758 { 0759 const StarObject *star = alignStars.value(i); 0760 if (star) 0761 { 0762 if (star->hasName()) 0763 { 0764 SkyPoint thisPt(ra / 15.0, dec); 0765 dms thisDiff = thisPt.angularDistanceTo(star); 0766 if (thisDiff.Degrees() < bestDiff.Degrees()) 0767 { 0768 index = i; 0769 bestDiff = thisDiff; 0770 } 0771 } 0772 } 0773 } 0774 if (index == -1) 0775 return KStarsData::Instance()->skyComposite()->starNearest(new SkyPoint(dms(ra), dms(dec)), maxSearch); 0776 return alignStars.value(index); 0777 } 0778 0779 void MountModel::alignTypeChanged(int alignType) 0780 { 0781 if (alignType == OBJECT_FIXED_DEC) 0782 alignDec->setEnabled(true); 0783 else 0784 alignDec->setEnabled(false); 0785 } 0786 0787 void MountModel::slotStarSelected(const QString selectedStar) 0788 { 0789 for (int i = 0; i < alignStars.size(); i++) 0790 { 0791 const StarObject *star = alignStars.value(i); 0792 if (star) 0793 { 0794 if (star->name() == selectedStar || star->gname().simplified() == selectedStar) 0795 { 0796 int currentRow = alignTable->rowCount(); 0797 alignTable->insertRow(currentRow); 0798 0799 QString ra_report, dec_report; 0800 getFormattedCoords(star->ra0().Hours(), star->dec0().Degrees(), ra_report, dec_report); 0801 0802 QTableWidgetItem *RAReport = new QTableWidgetItem(); 0803 RAReport->setText(ra_report); 0804 RAReport->setTextAlignment(Qt::AlignHCenter); 0805 alignTable->setItem(currentRow, 0, RAReport); 0806 0807 QTableWidgetItem *DECReport = new QTableWidgetItem(); 0808 DECReport->setText(dec_report); 0809 DECReport->setTextAlignment(Qt::AlignHCenter); 0810 alignTable->setItem(currentRow, 1, DECReport); 0811 0812 QTableWidgetItem *ObjNameReport = new QTableWidgetItem(); 0813 ObjNameReport->setText(star->longname()); 0814 ObjNameReport->setTextAlignment(Qt::AlignHCenter); 0815 alignTable->setItem(currentRow, 2, ObjNameReport); 0816 0817 QTableWidgetItem *disabledBox = new QTableWidgetItem(); 0818 disabledBox->setFlags(Qt::ItemIsSelectable); 0819 alignTable->setItem(currentRow, 3, disabledBox); 0820 0821 starListBox->setCurrentIndex(0); 0822 greekStarListBox->setCurrentIndex(0); 0823 return; 0824 } 0825 } 0826 } 0827 if (previewShowing) 0828 updatePreviewAlignPoints(); 0829 } 0830 0831 0832 void MountModel::getFormattedCoords(double ra, double dec, QString &ra_str, QString &dec_str) 0833 { 0834 dms ra_s, dec_s; 0835 ra_s.setH(ra); 0836 dec_s.setD(dec); 0837 0838 ra_str = QString("%1:%2:%3") 0839 .arg(ra_s.hour(), 2, 10, QChar('0')) 0840 .arg(ra_s.minute(), 2, 10, QChar('0')) 0841 .arg(ra_s.second(), 2, 10, QChar('0')); 0842 if (dec_s.Degrees() < 0) 0843 dec_str = QString("-%1:%2:%3") 0844 .arg(abs(dec_s.degree()), 2, 10, QChar('0')) 0845 .arg(abs(dec_s.arcmin()), 2, 10, QChar('0')) 0846 .arg(dec_s.arcsec(), 2, 10, QChar('0')); 0847 else 0848 dec_str = QString("%1:%2:%3") 0849 .arg(dec_s.degree(), 2, 10, QChar('0')) 0850 .arg(dec_s.arcmin(), 2, 10, QChar('0')) 0851 .arg(dec_s.arcsec(), 2, 10, QChar('0')); 0852 } 0853 0854 void MountModel::slotClearAllAlignPoints() 0855 { 0856 if (alignTable->rowCount() == 0) 0857 return; 0858 0859 if (KMessageBox::questionYesNo(this, i18n("Are you sure you want to clear all the alignment points?"), 0860 i18n("Clear Align Points")) == KMessageBox::Yes) 0861 alignTable->setRowCount(0); 0862 0863 if (previewShowing) 0864 updatePreviewAlignPoints(); 0865 } 0866 0867 void MountModel::slotRemoveAlignPoint() 0868 { 0869 alignTable->removeRow(alignTable->currentRow()); 0870 if (previewShowing) 0871 updatePreviewAlignPoints(); 0872 } 0873 0874 void MountModel::moveAlignPoint(int logicalIndex, int oldVisualIndex, int newVisualIndex) 0875 { 0876 Q_UNUSED(logicalIndex) 0877 0878 for (int i = 0; i < alignTable->columnCount(); i++) 0879 { 0880 QTableWidgetItem *oldItem = alignTable->takeItem(oldVisualIndex, i); 0881 QTableWidgetItem *newItem = alignTable->takeItem(newVisualIndex, i); 0882 0883 alignTable->setItem(newVisualIndex, i, oldItem); 0884 alignTable->setItem(oldVisualIndex, i, newItem); 0885 } 0886 alignTable->verticalHeader()->blockSignals(true); 0887 alignTable->verticalHeader()->moveSection(newVisualIndex, oldVisualIndex); 0888 alignTable->verticalHeader()->blockSignals(false); 0889 0890 if (previewShowing) 0891 updatePreviewAlignPoints(); 0892 } 0893 0894 void MountModel::swapAlignPoints(int firstPt, int secondPt) 0895 { 0896 for (int i = 0; i < alignTable->columnCount(); i++) 0897 { 0898 QTableWidgetItem *firstPtItem = alignTable->takeItem(firstPt, i); 0899 QTableWidgetItem *secondPtItem = alignTable->takeItem(secondPt, i); 0900 0901 alignTable->setItem(firstPt, i, secondPtItem); 0902 alignTable->setItem(secondPt, i, firstPtItem); 0903 } 0904 } 0905 0906 void MountModel::slotAddAlignPoint() 0907 { 0908 int currentRow = alignTable->rowCount(); 0909 alignTable->insertRow(currentRow); 0910 0911 QTableWidgetItem *disabledBox = new QTableWidgetItem(); 0912 disabledBox->setFlags(Qt::ItemIsSelectable); 0913 alignTable->setItem(currentRow, 3, disabledBox); 0914 } 0915 0916 void MountModel::slotFindAlignObject() 0917 { 0918 if (FindDialog::Instance()->execWithParent(this) == QDialog::Accepted) 0919 { 0920 SkyObject *object = FindDialog::Instance()->targetObject(); 0921 if (object != nullptr) 0922 { 0923 KStarsData * const data = KStarsData::Instance(); 0924 0925 SkyObject *o = object->clone(); 0926 o->updateCoords(data->updateNum(), true, data->geo()->lat(), data->lst(), false); 0927 int currentRow = alignTable->rowCount(); 0928 alignTable->insertRow(currentRow); 0929 0930 QString ra_report, dec_report; 0931 getFormattedCoords(o->ra0().Hours(), o->dec0().Degrees(), ra_report, dec_report); 0932 0933 QTableWidgetItem *RAReport = new QTableWidgetItem(); 0934 RAReport->setText(ra_report); 0935 RAReport->setTextAlignment(Qt::AlignHCenter); 0936 alignTable->setItem(currentRow, 0, RAReport); 0937 0938 QTableWidgetItem *DECReport = new QTableWidgetItem(); 0939 DECReport->setText(dec_report); 0940 DECReport->setTextAlignment(Qt::AlignHCenter); 0941 alignTable->setItem(currentRow, 1, DECReport); 0942 0943 QTableWidgetItem *ObjNameReport = new QTableWidgetItem(); 0944 ObjNameReport->setText(o->longname()); 0945 ObjNameReport->setTextAlignment(Qt::AlignHCenter); 0946 alignTable->setItem(currentRow, 2, ObjNameReport); 0947 0948 QTableWidgetItem *disabledBox = new QTableWidgetItem(); 0949 disabledBox->setFlags(Qt::ItemIsSelectable); 0950 alignTable->setItem(currentRow, 3, disabledBox); 0951 } 0952 } 0953 if (previewShowing) 0954 updatePreviewAlignPoints(); 0955 } 0956 0957 void MountModel::resetAlignmentProcedure() 0958 { 0959 alignTable->setCellWidget(currentAlignmentPoint, 3, new QWidget()); 0960 QTableWidgetItem *statusReport = new QTableWidgetItem(); 0961 statusReport->setFlags(Qt::ItemIsSelectable); 0962 statusReport->setIcon(QIcon(":/icons/AlignWarning.svg")); 0963 alignTable->setItem(currentAlignmentPoint, 3, statusReport); 0964 0965 emit newLog(i18n("The Mount Model Tool is Reset.")); 0966 startAlignB->setIcon( 0967 QIcon::fromTheme("media-playback-start")); 0968 m_IsRunning = false; 0969 currentAlignmentPoint = 0; 0970 emit aborted(); 0971 } 0972 0973 bool MountModel::alignmentPointsAreBad() 0974 { 0975 for (int i = 0; i < alignTable->rowCount(); i++) 0976 { 0977 QTableWidgetItem *raCell = alignTable->item(i, 0); 0978 if (!raCell) 0979 return true; 0980 QString raString = raCell->text(); 0981 if (dms().setFromString(raString, false) == false) 0982 return true; 0983 0984 QTableWidgetItem *decCell = alignTable->item(i, 1); 0985 if (!decCell) 0986 return true; 0987 QString decString = decCell->text(); 0988 if (dms().setFromString(decString, true) == false) 0989 return true; 0990 } 0991 return false; 0992 } 0993 0994 void MountModel::startStopAlignmentProcedure() 0995 { 0996 if (!m_IsRunning) 0997 { 0998 if (alignTable->rowCount() > 0) 0999 { 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 { 1007 int r = KMessageBox::warningContinueCancel( 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 { 1021 QTableWidgetItem *statusReport = new QTableWidgetItem(); 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 1042 QTableWidgetItem *statusReport = new QTableWidgetItem(); 1043 statusReport->setFlags(Qt::ItemIsSelectable); 1044 statusReport->setIcon(QIcon(":/icons/AlignWarning.svg")); 1045 alignTable->setItem(currentAlignmentPoint, 3, statusReport); 1046 } 1047 } 1048 1049 void 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(); 1055 dms raDMS = dms::fromString(raString, false); 1056 double raDeg = raDMS.Degrees(); 1057 1058 QTableWidgetItem *decCell = alignTable->item(currentAlignmentPoint, 1); 1059 QString decString = decCell->text(); 1060 dms decDMS = dms::fromString(decString, true); 1061 double dec = decDMS.Degrees(); 1062 1063 QProgressIndicator *alignIndicator = new QProgressIndicator(this); 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 1073 void MountModel::finishAlignmentPoint(bool solverSucceeded) 1074 { 1075 if (m_IsRunning && currentAlignmentPoint >= 0 && currentAlignmentPoint < alignTable->rowCount()) 1076 { 1077 alignTable->setCellWidget(currentAlignmentPoint, 3, new QWidget()); 1078 QTableWidgetItem *statusReport = new QTableWidgetItem(); 1079 statusReport->setFlags(Qt::ItemIsSelectable); 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 1088 if (currentAlignmentPoint < alignTable->rowCount()) 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 1103 void 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 }