File indexing completed on 2024-04-21 14:47:26

0001 /*
0002     Base class of KStars UI tests for meridian flip
0003 
0004     SPDX-FileCopyrightText: 2020 Wolfgang Reissenberger <sterne-jaeger@openfuture.de>
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 
0010 #include "test_ekos_meridianflip_base.h"
0011 
0012 #if defined(HAVE_INDI)
0013 
0014 #include <qdir.h>
0015 
0016 #include "kstars_ui_tests.h"
0017 #include "test_ekos.h"
0018 
0019 #include "auxiliary/dms.h"
0020 #include "ksutils.h"
0021 #include "indicom.h"
0022 #include "Options.h"
0023 #include "ekos/capture/capture.h"
0024 
0025 TestEkosMeridianFlipBase::TestEkosMeridianFlipBase(QObject *parent) :
0026     TestEkosMeridianFlipBase::TestEkosMeridianFlipBase("Internal", parent) {}
0027 
0028 TestEkosMeridianFlipBase::TestEkosMeridianFlipBase(QString guider, QObject *parent) : QObject(parent)
0029 {
0030     m_CaptureHelper = new TestEkosCaptureHelper(guider);
0031     m_CaptureHelper->m_FocuserDevice = "Focuser Simulator";
0032 }
0033 
0034 bool TestEkosMeridianFlipBase::startEkosProfile()
0035 {
0036     // use the helper to start the profile
0037     KWRAP_SUB(m_CaptureHelper->startEkosProfile());
0038     // prepare optical trains for testing
0039     m_CaptureHelper->prepareOpticalTrains();
0040     // prepare the mount module for testing (OAG guiding seems more robust)
0041     m_CaptureHelper->prepareMountModule(TestEkosHelper::SCOPE_FSQ85, TestEkosHelper::SCOPE_FSQ85);
0042     // prepare for focusing tests
0043     m_CaptureHelper->prepareFocusModule();
0044 
0045     // Everything completed successfully
0046     return true;
0047 }
0048 
0049 void TestEkosMeridianFlipBase::initTestCase()
0050 {
0051     // ensure EKOS is running
0052     KVERIFY_EKOS_IS_HIDDEN();
0053     KTRY_OPEN_EKOS();
0054     KVERIFY_EKOS_IS_OPENED();
0055     // Prepare PHD2 usage
0056     if (m_CaptureHelper->m_Guider == "PHD2")
0057         m_CaptureHelper->preparePHD2();
0058 
0059     // disable twilight warning
0060     KMessageBox::saveDontShowAgainYesNo("astronomical_twilight_warning", KMessageBox::ButtonCode::No);
0061 }
0062 
0063 bool TestEkosMeridianFlipBase::shutdownEkosProfile()
0064 {
0065     // cleanup the capture helper
0066     m_CaptureHelper->cleanup();
0067     // shutdown the profile
0068     return m_CaptureHelper->shutdownEkosProfile();
0069 }
0070 
0071 void TestEkosMeridianFlipBase::cleanupTestCase()
0072 {
0073     if (m_CaptureHelper->m_Guider == "PHD2")
0074         m_CaptureHelper->cleanupPHD2();
0075     KTRY_CLOSE_EKOS();
0076     KVERIFY_EKOS_IS_HIDDEN();
0077 }
0078 
0079 void TestEkosMeridianFlipBase::init()
0080 {
0081     // initialize the capture helper
0082     m_CaptureHelper->init();
0083 
0084     // disable by default
0085     refocus_checked   = false;
0086     use_aligning      = false;
0087     dithering_checked = false;
0088 
0089     // reset initial focuser position
0090     initialFocusPosition = -1;
0091 
0092     // set geo location
0093     KStarsData * const d = KStars::Instance()->data();
0094     QVERIFY(d != nullptr);
0095     QFETCH(QString, location);
0096     GeoLocation * const geo = d->locationNamed(location);
0097     QVERIFY(geo != nullptr);
0098     d->setLocation(*geo);
0099 
0100     // start the profile (depending on the selected guider)
0101     QVERIFY(startEkosProfile());
0102 
0103     // disable FITS viewer
0104     Options::setUseFITSViewer(false);
0105 
0106     // Eliminate the noise setting to ensure a working focusing and plate solving
0107     KTRY_INDI_PROPERTY("CCD Simulator", "Simulator Config", "SIMULATOR_SETTINGS", ccd_settings);
0108     INDI_E *noise_setting = ccd_settings->getElement("SIM_NOISE");
0109     QVERIFY(ccd_settings != nullptr);
0110     noise_setting->setValue(0.0);
0111     ccd_settings->processSetButton();
0112 
0113     // clear guiding calibration to ensure proper guiding
0114     // TestEkosHelper::prepareGuidingModule() stores a guiding calibration, override if necessary
0115     // KTRY_CLICK(Ekos::Manager::Instance()->guideModule(), clearCalibrationB);
0116 
0117     // switch to mount module
0118     KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(Ekos::Manager::Instance()->mountModule(), 1000);
0119 
0120     // activate meridian flip
0121     QVERIFY(enableMeridianFlip(0.0));
0122 
0123     // expected beginning of the meridian flip
0124     m_CaptureHelper->expectedMeridianFlipStates.enqueue(Ekos::MeridianFlipState::MOUNT_FLIP_PLANNED);
0125     m_CaptureHelper->expectedMeridianFlipStates.enqueue(Ekos::MeridianFlipState::MOUNT_FLIP_RUNNING);
0126 }
0127 
0128 void TestEkosMeridianFlipBase::cleanup()
0129 {
0130     // ensure that capturing, focusing and guiding is stopped
0131     QVERIFY(stopScheduler());
0132     QVERIFY(m_CaptureHelper->stopFocusing());
0133     QVERIFY(stopAligning());
0134     QVERIFY(stopCapturing());
0135     QVERIFY(m_CaptureHelper->stopGuiding());
0136 
0137     // clean up capture module
0138     Ekos::Manager::Instance()->captureModule()->clearSequenceQueue();
0139     KTRY_GADGET(Ekos::Manager::Instance()->captureModule(), QTableWidget, queueTable);
0140     QTRY_VERIFY_WITH_TIMEOUT(queueTable->rowCount() == 0, 2000);
0141 
0142     // cleanup the scheduler
0143     m_CaptureHelper->cleanupScheduler();
0144 
0145     // shutdown profile and local INDI server
0146     QVERIFY(shutdownEkosProfile());
0147 }
0148 
0149 
0150 
0151 /* *********************************************************************************
0152  *
0153  * Test data
0154  *
0155  * ********************************************************************************* */
0156 
0157 void TestEkosMeridianFlipBase::prepareTestData(double exptime, QList<QString> locationList, QList<bool> culminationList,
0158         QList<std::pair<QString, int> > filterList, QList<int> focusList, QList<bool> guideList, QList<bool> ditherList)
0159 {
0160 #if QT_VERSION < QT_VERSION_CHECK(5,9,0)
0161     QSKIP("Bypassing fixture test on old Qt");
0162     Q_UNUSED(exptime)
0163     Q_UNUSED(locationList)
0164     Q_UNUSED(culminationList)
0165     Q_UNUSED(filterList)
0166     Q_UNUSED(focusList)
0167     Q_UNUSED(guideList)
0168     Q_UNUSED(ditherList)
0169 #else
0170     QTest::addColumn<QString>("location"); /*!< locations the KStars test is running for */
0171     QTest::addColumn<bool>("culmination"); /*!< upper culmination? */
0172     QTest::addColumn<int>("count");        /*!< frame counts to capture */
0173     QTest::addColumn<double>("exptime");   /*!< exposure time of a single frame */
0174     QTest::addColumn<QString>("filters");  /*!< list of filters for the capture sequence */
0175     QTest::addColumn<int>("focus");       /*!< focusing: 0=no, 1=refocus, 2=HFR autofocus, 3=after meridian flip */
0176     QTest::addColumn<bool>("guide");       /*!< use guiding */
0177     QTest::addColumn<bool>("dither");      /*!< execute dithering after each capture */
0178 
0179     KStarsData * const d = KStars::Instance()->data();
0180     QVERIFY(d != nullptr);
0181 
0182     for (QString location : locationList)
0183         for (bool culmination : culminationList)
0184             for (int focus : focusList)
0185                 for (bool guide : guideList)
0186                     for (bool dither : ditherList)
0187                         for(std::pair<QString, int> filter :  filterList)
0188                         {
0189                             int count = filter.second;
0190                             // both guide==false && dither==true does not make sense
0191                             if (guide == true || dither == false)
0192                                 QTest::newRow(QString("%7: culm=%8, %1x%2, foc=%3, di=%4, guider=%5").arg(count).arg(filter.first)
0193                                               .arg(focus == 0 ? "no" : (focus == 1 ? "refocus" : (focus == 2 ? "auto" : "mf"))).arg(dither ? "yes" : "no")
0194                                               .arg(guide ? m_CaptureHelper->m_Guider : "none").arg(location)
0195                                               .arg(culmination ? "up" : "low").toLocal8Bit())
0196                                         << location << culmination << count << exptime << filter.first << focus << guide << dither;
0197                         }
0198 #endif
0199 }
0200 
0201 /* *********************************************************************************
0202  *
0203  * Helpers functions
0204  *
0205  * ********************************************************************************* */
0206 
0207 bool TestEkosMeridianFlipBase::positionMountForMF(int secsToMF, bool fast)
0208 {
0209 #if QT_VERSION < QT_VERSION_CHECK(5,9,0)
0210     QSKIP("Bypassing fixture test on old Qt");
0211     Q_UNUSED(secsToMF)
0212     Q_UNUSED(fast)
0213 #else
0214     Ekos::Mount *mount = Ekos::Manager::Instance()->mountModule();
0215 
0216     findMFTestTarget(secsToMF, fast);
0217 
0218     // now slew very close before the meridian
0219     mount->slew(target->ra().Hours(), target->dec().Degrees());
0220     // wait a certain time until the mount slews
0221     QTest::qWait(3000);
0222     // wait until the mount is tracking
0223     KTRY_VERIFY_WITH_TIMEOUT_SUB(Ekos::Manager::Instance()->mountModule()->status() == ISD::Mount::MOUNT_TRACKING, 60000);
0224     // check whether a meridian flip is announced
0225     const QString flipmsg = Ekos::Manager::Instance()->mountModule()->meridianFlipStatusWidget->getStatus();
0226     const int prefixlength = QString("Meridian flip in").length();
0227     KWRAP_SUB(QTRY_VERIFY_WITH_TIMEOUT(flipmsg.length() >= prefixlength
0228                                        && flipmsg.left(prefixlength).compare("Meridian flip in") == 0, 20000));
0229 #endif
0230     // all checks succeeded
0231     return true;
0232 }
0233 
0234 bool TestEkosMeridianFlipBase::prepareMFTestcase(bool guideDeviation, bool initialGuideDeviation)
0235 {
0236 #if QT_VERSION < QT_VERSION_CHECK(5,9,0)
0237     QSKIP("Bypassing fixture test on old Qt");
0238     Q_UNUSED(guideDeviation)
0239     Q_UNUSED(initialGuideDeviation)
0240 #else
0241     // switch to capture module
0242     KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(Ekos::Manager::Instance()->captureModule(), 1000));
0243 
0244     // set refocus after meridian flip
0245     QFETCH(int, focus);
0246     refocus_checked = (focus > 0);
0247 
0248     // regular refocusing
0249     KTRY_SET_CHECKBOX_SUB(Ekos::Manager::Instance()->captureModule(), limitRefocusS, (focus == 1));
0250     // HFR based refocusing
0251     KTRY_SET_CHECKBOX_SUB(Ekos::Manager::Instance()->captureModule(), limitFocusHFRS, (focus == 2));
0252     // focus after flip
0253     KTRY_SET_CHECKBOX_SUB(Ekos::Manager::Instance()->captureModule(), meridianRefocusS, (focus == 3));
0254 
0255     // set guide deviation guard
0256     KTRY_SET_CHECKBOX_SUB(Ekos::Manager::Instance()->captureModule(), startGuiderDriftS, initialGuideDeviation);
0257     KTRY_SET_CHECKBOX_SUB(Ekos::Manager::Instance()->captureModule(), limitGuideDeviationS, guideDeviation);
0258     KTRY_SET_DOUBLESPINBOX_SUB(Ekos::Manager::Instance()->captureModule(), startGuiderDriftN, 3.0);
0259     KTRY_SET_DOUBLESPINBOX_SUB(Ekos::Manager::Instance()->captureModule(), limitGuideDeviationN, 3.0);
0260 
0261     // create the capture directory
0262     QTemporaryDir destination;
0263     KWRAP_SUB(QVERIFY(destination.isValid()));
0264     KWRAP_SUB(QVERIFY(destination.autoRemove()));
0265     qCInfo(KSTARS_EKOS_TEST) << "FITS path: " << destination.path();
0266 
0267     // create capture sequence
0268     QFETCH(int, count);
0269     QFETCH(double, exptime);
0270     QFETCH(QString, filters);
0271 
0272     KWRAP_SUB(foreach(QString filter, filters.split(",")) KTRY_CAPTURE_ADD_LIGHT(exptime, count, 0, filter, "test",
0273               destination.path()));
0274 
0275     QFETCH(bool, guide);
0276     if (guide)
0277     {
0278         // slew roughly to the position to calibrate first
0279         KWRAP_SUB(QVERIFY(positionMountForMF(600, true)));
0280         qCInfo(KSTARS_EKOS_TEST) << "Slewed roughly to the position to calibrate first.";
0281         // calibrate guiding
0282         if (! m_CaptureHelper->startGuiding(2.0)) return false;
0283         if (! m_CaptureHelper->stopGuiding()) return false;
0284         qCInfo(KSTARS_EKOS_TEST) << "Guider calibration" << Options::serializedCalibration();
0285     }
0286 
0287     // set dithering due to test data set
0288     QFETCH(bool, dither);
0289     dithering_checked = dither;
0290     Options::setDitherEnabled(dither);
0291 
0292     // all steps succeeded
0293     return true;
0294 #endif
0295 }
0296 
0297 bool TestEkosMeridianFlipBase::prepareCaptureTestcase(int secsToMF, bool guideDeviation, bool initialGuideDeviation)
0298 {
0299 #if QT_VERSION < QT_VERSION_CHECK(5,9,0)
0300     QSKIP("Bypassing fixture test on old Qt");
0301     Q_UNUSED(secsToMF)
0302     Q_UNUSED(initialFocus)
0303     Q_UNUSED(guideDeviation)
0304     Q_UNUSED(initialGuideDeviation)
0305 #else
0306     // execute preparation steps
0307     KWRAP_SUB(QVERIFY(prepareMFTestcase(guideDeviation, initialGuideDeviation)));
0308 
0309     // slew close to the meridian
0310     QFETCH(bool, guide);
0311     KWRAP_SUB(QVERIFY(positionMountForMF(secsToMF, !guide)));
0312     qCInfo(KSTARS_EKOS_TEST) << "Slewed close to the meridian.";
0313 #endif
0314     // all checks succeeded
0315     return true;
0316 }
0317 
0318 bool TestEkosMeridianFlipBase::prepareSchedulerTestcase(int secsToMF, bool useAlign,
0319         Ekos::CompletionCondition completionCondition, int iterations)
0320 {
0321 #if QT_VERSION < QT_VERSION_CHECK(5,9,0)
0322     QSKIP("Bypassing fixture test on old Qt");
0323     Q_UNUSED(secsToMF)
0324     Q_UNUSED(algorithm)
0325     Q_UNUSED(completionCondition)
0326     Q_UNUSED(iterations)
0327 #else
0328     // execute all similar preparation steps for a capture test case except for target slew
0329     KVERIFY_SUB(prepareMFTestcase(false, false));
0330 
0331     // determine the target and sync close to it
0332     findMFTestTarget(secsToMF, true);
0333 
0334     // save current capture sequence to Ekos sequence file
0335     QString sequenceFile = m_CaptureHelper->destination->filePath("test.esq");
0336     qCInfo(KSTARS_EKOS_TEST) << "Sequence file" << sequenceFile << "created.";
0337     KVERIFY_SUB(Ekos::Manager::Instance()->captureModule()->saveSequenceQueue(sequenceFile));
0338 
0339     Ekos::Scheduler *scheduler = Ekos::Manager::Instance()->schedulerModule();
0340     KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(scheduler, 1000));
0341     // set sequence file
0342     scheduler->setSequence(sequenceFile);
0343     // set target
0344     KTRY_SET_LINEEDIT_SUB(scheduler, nameEdit, "test");
0345     KTRY_SET_LINEEDIT_SUB(scheduler, raBox, target->ra0().toHMSString());
0346     KTRY_SET_LINEEDIT_SUB(scheduler, decBox, target->dec0().toDMSString());
0347     // define step checks
0348     QFETCH(bool, guide);
0349     // disable all step checks
0350     KTRY_SET_CHECKBOX_SUB(scheduler, schedulerTrackStep, true);
0351     KTRY_SET_CHECKBOX_SUB(scheduler, schedulerFocusStep, false);
0352     KTRY_SET_CHECKBOX_SUB(scheduler, schedulerAlignStep, useAlign);
0353     KTRY_SET_CHECKBOX_SUB(scheduler, schedulerGuideStep, guide);
0354     // ignore twilight
0355     KTRY_SET_CHECKBOX_SUB(scheduler, schedulerTwilight, false);
0356     prepareTestData(18.0, {"Greenwich"}, {true}, {{"Luminance", 6}}, {0}, {false}, {false});
0357     // disable remember job progress
0358     Options::setRememberJobProgress(false);
0359     // disable INDI stopping after scheduler finished
0360     Options::setStopEkosAfterShutdown(false);
0361 
0362     // set the completion condition
0363     switch (completionCondition)
0364     {
0365         case Ekos::FINISH_REPEAT:
0366             // repeat the job for a fixed amount
0367             KTRY_SET_RADIOBUTTON_SUB(scheduler, schedulerRepeatSequences, true);
0368             KTRY_SET_SPINBOX_SUB(scheduler, schedulerExecutionSequencesLimit, iterations);
0369             break;
0370         case Ekos::FINISH_LOOP:
0371             KTRY_SET_RADIOBUTTON_SUB(scheduler, schedulerUntilTerminated, true);
0372             break;
0373         default:
0374             QWARN(QString("Unsupported completion condition %1!").arg(completionCondition).toStdString().c_str());
0375             return false;
0376     }
0377     // add scheduler job
0378     KTRY_CLICK_SUB(scheduler, addToQueueB);
0379 #endif
0380     // preparation successful
0381     return true;
0382 }
0383 
0384 bool TestEkosMeridianFlipBase::checkDithering()
0385 {
0386     // if dithering is not selected, to nothing
0387     if (! dithering_checked)
0388         return true;
0389 
0390     // reset the dithering flag
0391     m_CaptureHelper->dithered = false;
0392 
0393     // wait for 120s if dithering happens
0394     KTRY_VERIFY_WITH_TIMEOUT_SUB(m_CaptureHelper->dithered == true, 120000);
0395     // all checks succeeded
0396     return true;
0397 }
0398 
0399 
0400 bool TestEkosMeridianFlipBase::checkRefocusing()
0401 {
0402     if (refocus_checked)
0403     {
0404         // wait until focusing has started
0405         KTRY_VERIFY_WITH_TIMEOUT_SUB(m_CaptureHelper->getFocusStatus() == Ekos::FOCUS_PROGRESS, 60000);
0406         qCInfo(KSTARS_EKOS_TEST()) << "Focus progress detected.";
0407         KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT_SUB(m_CaptureHelper->expectedFocusStates, 60000);
0408         qCInfo(KSTARS_EKOS_TEST()) << "All expected focus states received.";
0409         // check if focus completion is reached (successful or not)
0410         KTRY_VERIFY_WITH_TIMEOUT_SUB(m_CaptureHelper->getFocusStatus() == Ekos::FOCUS_COMPLETE
0411                                      || m_CaptureHelper->getFocusStatus() == Ekos::FOCUS_FAILED ||
0412                                      m_CaptureHelper->getFocusStatus() == Ekos::FOCUS_ABORTED, 120000);
0413         qCInfo(KSTARS_EKOS_TEST()) << "Focusing completed.";
0414     }
0415     // focusing might have suspended guiding
0416     if (m_CaptureHelper->use_guiding)
0417         KTRY_VERIFY_WITH_TIMEOUT_SUB(m_CaptureHelper->getGuidingStatus() == Ekos::GUIDE_GUIDING, 20000);
0418     // all checks succeeded
0419     return true;
0420 }
0421 
0422 bool TestEkosMeridianFlipBase::executeAlignment(double expTime)
0423 {
0424     // mark alignment
0425     use_aligning = true;
0426     // prepare for alignment tests
0427     m_CaptureHelper->prepareAlignmentModule();
0428 
0429     m_CaptureHelper->expectedAlignStates.enqueue(Ekos::ALIGN_COMPLETE);
0430     KVERIFY_SUB(m_CaptureHelper->startAligning(expTime));
0431     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT_SUB(m_CaptureHelper->expectedAlignStates, 60000);
0432 
0433     // alignment succeeded
0434     return true;
0435 }
0436 
0437 bool TestEkosMeridianFlipBase::stopAligning()
0438 {
0439     // check whether alignment is already stopped
0440     if (m_CaptureHelper->getAlignStatus() == Ekos::ALIGN_IDLE || m_CaptureHelper->getAlignStatus() == Ekos::ALIGN_ABORTED ||
0441             m_CaptureHelper->getAlignStatus() == Ekos::ALIGN_FAILED || m_CaptureHelper->getAlignStatus() == Ekos::ALIGN_COMPLETE)
0442         return true;
0443 
0444     // switch to alignment module
0445     KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(Ekos::Manager::Instance()->alignModule(), 1000));
0446 
0447     // stop alignment
0448     KTRY_GADGET_SUB(Ekos::Manager::Instance()->alignModule(), QPushButton, stopB);
0449     KTRY_CLICK_SUB(Ekos::Manager::Instance()->alignModule(), stopB);
0450     KTRY_VERIFY_WITH_TIMEOUT_SUB(m_CaptureHelper->getAlignStatus() == Ekos::ALIGN_IDLE
0451                                  || m_CaptureHelper->getAlignStatus() == Ekos::ALIGN_ABORTED ||
0452                                  m_CaptureHelper->getAlignStatus() == Ekos::ALIGN_FAILED || m_CaptureHelper->getAlignStatus() == Ekos::ALIGN_COMPLETE, 5000);
0453     // all checks succeeded
0454     return true;
0455 }
0456 
0457 bool TestEkosMeridianFlipBase::startCapturing()
0458 {
0459 #if QT_VERSION < QT_VERSION_CHECK(5,9,0)
0460     QSKIP("Bypassing fixture test on old Qt");
0461 #else
0462     QFETCH(double, exptime);
0463     // Now we can estimate how many captures will happen before the meridian flip.
0464     int t2mf = std::max(secondsToMF(), 0);
0465 
0466     // Ensure that dithering takes place after the flip
0467     if (dithering_checked)
0468         Options::setDitherFrames(static_cast<uint>(1 + t2mf / exptime));
0469 
0470     // switch to the capture module
0471     KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(Ekos::Manager::Instance()->captureModule(), 1000));
0472 
0473     // check if capture is in a stopped state
0474     Ekos::CaptureState capturestate = m_CaptureHelper->getCaptureStatus();
0475     KWRAP_SUB(QVERIFY(capturestate == Ekos::CAPTURE_IDLE || capturestate == Ekos::CAPTURE_ABORTED ||
0476                       capturestate == Ekos::CAPTURE_SUSPENDED || capturestate == Ekos::CAPTURE_COMPLETE));
0477 
0478     // press start
0479     m_CaptureHelper->expectedCaptureStates.enqueue(Ekos::CAPTURE_CAPTURING);
0480     KTRY_GADGET_SUB(Ekos::Manager::Instance()->captureModule(), QPushButton, startB);
0481     KTRY_CLICK_SUB(Ekos::Manager::Instance()->captureModule(), startB);
0482 
0483     // check if capturing has started
0484     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT_SUB(m_CaptureHelper->expectedCaptureStates, 15000);
0485 #endif
0486     // all checks succeeded
0487     return true;
0488 }
0489 
0490 bool TestEkosMeridianFlipBase::stopCapturing()
0491 {
0492     // check if capture is in a stopped state
0493     if (m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_IDLE
0494             || m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_ABORTED ||
0495             m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_COMPLETE
0496             || m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_PAUSED ||
0497             m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_MERIDIAN_FLIP)
0498         return true;
0499 
0500     // switch to the capture module
0501     KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(Ekos::Manager::Instance()->captureModule(), 1000));
0502 
0503     // else press stop
0504     m_CaptureHelper->expectedCaptureStates.enqueue(Ekos::CAPTURE_ABORTED);
0505     KTRY_GADGET_SUB(Ekos::Manager::Instance()->captureModule(), QPushButton, startB);
0506     KTRY_CLICK_SUB(Ekos::Manager::Instance()->captureModule(), startB);
0507 
0508     // check if capturing has stopped
0509     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT_SUB(m_CaptureHelper->expectedCaptureStates, 5000);
0510     // all checks succeeded
0511     return true;
0512 }
0513 
0514 bool TestEkosMeridianFlipBase::startScheduler()
0515 {
0516     Ekos::Scheduler *scheduler = Ekos::Manager::Instance()->schedulerModule();
0517     // set expected states
0518     // slewing detection unsure since the position is close to the target
0519     // m_CaptureHelper->expectedMountStates.append(ISD::Mount::MOUNT_SLEWING);
0520     // m_CaptureHelper->expectedMountStates.append(ISD::Mount::MOUNT_TRACKING);
0521     if (m_CaptureHelper->use_guiding == true)
0522         m_CaptureHelper->expectedGuidingStates.append(Ekos::GUIDE_GUIDING);
0523     m_CaptureHelper->expectedCaptureStates.append(Ekos::CAPTURE_CAPTURING);
0524 
0525     // switch to the scheduler module and start
0526     KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(scheduler, 1000));
0527     KVERIFY_SUB(m_CaptureHelper->getSchedulerStatus() == Ekos::SCHEDULER_IDLE
0528                 || m_CaptureHelper->getSchedulerStatus() == Ekos::SCHEDULER_ABORTED);
0529     KTRY_CLICK_SUB(scheduler, startB);
0530 
0531     // check mount slew and tracking
0532     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT_SUB(m_CaptureHelper->expectedMountStates, 60000);
0533     // check guiding
0534     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT_SUB(m_CaptureHelper->expectedGuidingStates, 60000);
0535     // check capturing
0536     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT_SUB(m_CaptureHelper->expectedCaptureStates, 60000);
0537 
0538     // all checks succeeded
0539     return true;
0540 }
0541 
0542 bool TestEkosMeridianFlipBase::stopScheduler()
0543 {
0544     Ekos::Scheduler *scheduler = Ekos::Manager::Instance()->schedulerModule();
0545     // switch to the capture module
0546     KWRAP_SUB(KTRY_SWITCH_TO_MODULE_WITH_TIMEOUT(scheduler, 1000));
0547     if (m_CaptureHelper->getSchedulerStatus() == Ekos::SCHEDULER_RUNNING
0548             || m_CaptureHelper->getSchedulerStatus() == Ekos::SCHEDULER_PAUSED)
0549         KTRY_CLICK_SUB(scheduler, startB);
0550     // all checks succeeded
0551     return true;
0552 }
0553 
0554 bool TestEkosMeridianFlipBase::enableMeridianFlip(double delay)
0555 {
0556     Ekos::Manager * const ekos = Ekos::Manager::Instance();
0557     KTRY_SET_CHECKBOX_SUB(ekos->mountModule(), executeMeridianFlip, true);
0558     // set the delay in degrees
0559     KTRY_SET_DOUBLESPINBOX_SUB(ekos->mountModule(), meridianFlipOffsetDegrees, 15.0 * delay / 3600.0);
0560     // all checks succeeded
0561     return true;
0562 }
0563 
0564 
0565 bool TestEkosMeridianFlipBase::checkMFStarted(int startDelay)
0566 {
0567     // check if the meridian flip starts running
0568     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT_SUB(m_CaptureHelper->expectedMeridianFlipStates, startDelay * 1000);
0569     // all checks succeeded
0570     return true;
0571 }
0572 
0573 bool TestEkosMeridianFlipBase::checkMFExecuted(int startDelay)
0574 {
0575     // check if the meridian flip starts running
0576     KVERIFY_SUB(checkMFStarted(startDelay));
0577     // if dithering test required, set the dithering frequency to 1
0578     if (dithering_checked)
0579         Options::setDitherFrames(1);
0580     // check if the meridian flip is completed latest after one minute
0581     m_CaptureHelper->expectedMeridianFlipStates.enqueue(Ekos::MeridianFlipState::MOUNT_FLIP_COMPLETED);
0582     KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT_SUB(m_CaptureHelper->expectedMeridianFlipStates, 60000);
0583     // all checks succeeded
0584     return true;
0585 }
0586 
0587 bool TestEkosMeridianFlipBase::checkPostMFBehavior()
0588 {
0589     // check if alignment succeeded
0590     if (use_aligning)
0591     {
0592         m_CaptureHelper->expectedAlignStates.enqueue(Ekos::ALIGN_COMPLETE);
0593         KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT_SUB(m_CaptureHelper->expectedAlignStates, 120000);
0594     }
0595 
0596     // check if guiding is running
0597     if (m_CaptureHelper->use_guiding)
0598         KTRY_VERIFY_WITH_TIMEOUT_SUB(m_CaptureHelper->getGuidingStatus() == Ekos::GUIDE_GUIDING, 30000);
0599 
0600     // check refocusing, that should happen immediately after the guiding calibration
0601     KWRAP_SUB(QVERIFY(checkRefocusing()));
0602 
0603     // check if capturing has been started
0604     // dithering happen after first capture
0605     // otherwise it is sufficient to wait for start of capturing
0606     if (dithering_checked)
0607     {
0608         m_CaptureHelper->expectedCaptureStates.enqueue(Ekos::CAPTURE_IMAGE_RECEIVED);
0609         KVERIFY_EMPTY_QUEUE_WITH_TIMEOUT_SUB(m_CaptureHelper->expectedCaptureStates, 60000);
0610     }
0611     else
0612         KTRY_VERIFY_WITH_TIMEOUT_SUB(m_CaptureHelper->getCaptureStatus() == Ekos::CAPTURE_CAPTURING, 60000);
0613 
0614     // After the first capture dithering should take place
0615     KWRAP_SUB(QVERIFY(checkDithering()));
0616 
0617     qCInfo(KSTARS_EKOS_TEST()) << "Post meridian flip steps completed.";
0618     return true;
0619 }
0620 
0621 int TestEkosMeridianFlipBase::secondsToMF()
0622 {
0623     const QString flipmsg = Ekos::Manager::Instance()->mountModule()->meridianFlipStatusWidget->getStatus();
0624 
0625     return m_CaptureHelper->secondsToMF(flipmsg);
0626 }
0627 
0628 void TestEkosMeridianFlipBase::findMFTestTarget(int secsToMF, bool fast)
0629 {
0630     Ekos::Mount *mount = Ekos::Manager::Instance()->mountModule();
0631 
0632     // translate seconds into fractions of hours
0633     double delta = static_cast<double>(secsToMF) / 3600.0;
0634 
0635     dms lst = KStarsData::Instance()->geo()->GSTtoLST(KStarsData::Instance()->clock()->utc().gst());
0636 
0637     // upper or lower culmination?
0638     QFETCH(bool, culmination);
0639     double meridianRA = lst.Hours() + (culmination ? 0.0 : 12.0);
0640 
0641     // calculate a feasible declination depending to the location's latitude
0642     // for the upper culmination, we use an azimuth of 45 deg, for the lower culmination half way between pole and horizont
0643     double lat = KStarsData::Instance()->geo()->lat()->Degrees();
0644     target = new SkyPoint(range24(meridianRA + delta), culmination ? (lat - 45) : (90 - lat / 2));
0645     m_CaptureHelper->updateJ2000Coordinates(target);
0646     if (fast)
0647     {
0648         // reset mount model
0649         mount->resetModel();
0650         // sync to a point close before the meridian to speed up slewing
0651         mount->sync(range24(target->ra().Hours() + 0.002), target->dec().Degrees());
0652     }
0653 }
0654 
0655 
0656 #endif // HAVE_INDI